diff --git a/ext/pdo_pgsql/pgsql_statement.c b/ext/pdo_pgsql/pgsql_statement.c index 15ecd8ce6f562..5f83eaeca5ea7 100644 --- a/ext/pdo_pgsql/pgsql_statement.c +++ b/ext/pdo_pgsql/pgsql_statement.c @@ -348,60 +348,104 @@ static int pgsql_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data * parameter = ¶m->parameter; } - if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB && - Z_TYPE_P(parameter) == IS_RESOURCE) { - php_stream *stm; - php_stream_from_zval_no_verify(stm, parameter); - if (stm) { - if (php_stream_is(stm, &pdo_pgsql_lob_stream_ops)) { - struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stm->abstract; - pdo_pgsql_bound_param *P = param->driver_data; - - if (P == NULL) { - P = ecalloc(1, sizeof(*P)); - param->driver_data = P; +exec_pre_retry: + switch (PDO_PARAM_TYPE(param->param_type)) { + case PDO_PARAM_NULL: + S->param_values[param->paramno] = NULL; + S->param_lengths[param->paramno] = 0; + break; + + case PDO_PARAM_STMT: + return 0; + + case PDO_PARAM_INT: + if (Z_TYPE_P(parameter) == IS_NULL) { + param->param_type = PDO_PARAM_NULL; + goto exec_pre_retry; + } + convert_to_long(parameter); + convert_to_string(parameter); + S->param_values[param->paramno] = Z_STRVAL_P(parameter); + S->param_lengths[param->paramno] = Z_STRLEN_P(parameter); + S->param_formats[param->paramno] = 0; +#if SIZEOF_ZEND_LONG >= 8 + S->param_types[param->paramno] = INT8OID; +#else + S->param_types[param->paramno] = INT4OID; +#endif + break; + + case PDO_PARAM_BOOL: + if (Z_TYPE_P(parameter) == IS_NULL) { + param->param_type = PDO_PARAM_NULL; + goto exec_pre_retry; + } + convert_to_boolean(parameter); + S->param_values[param->paramno] = Z_TYPE_P(parameter) == IS_TRUE ? "t" : "f"; + S->param_lengths[param->paramno] = 1; + S->param_formats[param->paramno] = 0; + break; + + case PDO_PARAM_LOB: + if (Z_TYPE_P(parameter) == IS_RESOURCE) { + php_stream *stm; + php_stream_from_zval_no_verify(stm, parameter); + if (stm) { + if (php_stream_is(stm, &pdo_pgsql_lob_stream_ops)) { + struct pdo_pgsql_lob_self *self = (struct pdo_pgsql_lob_self*)stm->abstract; + pdo_pgsql_bound_param *P = param->driver_data; + + if (P == NULL) { + P = ecalloc(1, sizeof(*P)); + param->driver_data = P; + } + P->oid = htonl(self->oid); + S->param_values[param->paramno] = (char*)&P->oid; + S->param_lengths[param->paramno] = sizeof(P->oid); + S->param_formats[param->paramno] = 1; + S->param_types[param->paramno] = OIDOID; + return 1; + } else { + zend_string *str = php_stream_copy_to_mem(stm, PHP_STREAM_COPY_ALL, 0); + if (str != NULL) { + ZVAL_STR(parameter, str); + } else { + ZVAL_EMPTY_STRING(parameter); + } + } + } else { + /* expected a stream resource */ + pdo_pgsql_error_stmt(stmt, PGRES_FATAL_ERROR, "HY105"); + return 0; } - P->oid = htonl(self->oid); - S->param_values[param->paramno] = (char*)&P->oid; - S->param_lengths[param->paramno] = sizeof(P->oid); - S->param_formats[param->paramno] = 1; - S->param_types[param->paramno] = OIDOID; - return 1; + } else if (Z_TYPE_P(parameter) == IS_NULL) { + param->param_type = PDO_PARAM_NULL; + goto exec_pre_retry; } else { - zend_string *str = php_stream_copy_to_mem(stm, PHP_STREAM_COPY_ALL, 0); - if (str != NULL) { - ZVAL_STR(parameter, str); - } else { - ZVAL_EMPTY_STRING(parameter); + // fallback + if (!try_convert_to_string(parameter)) { + return 0; } } - } else { - /* expected a stream resource */ - pdo_pgsql_error_stmt(stmt, PGRES_FATAL_ERROR, "HY105"); - return 0; - } - } - - if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_NULL || - Z_TYPE_P(parameter) == IS_NULL) { - S->param_values[param->paramno] = NULL; - S->param_lengths[param->paramno] = 0; - } else if (Z_TYPE_P(parameter) == IS_FALSE || Z_TYPE_P(parameter) == IS_TRUE) { - S->param_values[param->paramno] = Z_TYPE_P(parameter) == IS_TRUE ? "t" : "f"; - S->param_lengths[param->paramno] = 1; - S->param_formats[param->paramno] = 0; - } else { - convert_to_string(parameter); - S->param_values[param->paramno] = Z_STRVAL_P(parameter); - S->param_lengths[param->paramno] = Z_STRLEN_P(parameter); - S->param_formats[param->paramno] = 0; - } - - if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) { - S->param_types[param->paramno] = 0; - S->param_formats[param->paramno] = 1; - } else { - S->param_types[param->paramno] = 0; + S->param_values[param->paramno] = Z_STRVAL_P(parameter); + S->param_lengths[param->paramno] = Z_STRLEN_P(parameter); + S->param_types[param->paramno] = 0; + S->param_formats[param->paramno] = 1; + break; + + case PDO_PARAM_STR: + default: + if (Z_TYPE_P(parameter) == IS_NULL) { + param->param_type = PDO_PARAM_NULL; + goto exec_pre_retry; + } + if (!try_convert_to_string(parameter)) { + return 0; + } + S->param_values[param->paramno] = Z_STRVAL_P(parameter); + S->param_lengths[param->paramno] = Z_STRLEN_P(parameter); + S->param_formats[param->paramno] = 0; + break; } } break; @@ -409,6 +453,7 @@ static int pgsql_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data * } else if (param->is_param && event_type == PDO_PARAM_EVT_NORMALIZE) { /* We need to manually convert to a pg native boolean value */ if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_BOOL && + Z_TYPE_P(¶m->parameter) != IS_NULL && ((param->param_type & PDO_PARAM_INPUT_OUTPUT) != PDO_PARAM_INPUT_OUTPUT)) { const char *s = zend_is_true(¶m->parameter) ? "t" : "f"; param->param_type = PDO_PARAM_STR; diff --git a/ext/pdo_pgsql/tests/pdo_param.phpt b/ext/pdo_pgsql/tests/pdo_param.phpt new file mode 100644 index 0000000000000..c6d23d6871760 --- /dev/null +++ b/ext/pdo_pgsql/tests/pdo_param.phpt @@ -0,0 +1,126 @@ +--TEST-- +PDO PgSQL PDOStatement PDO::PARAM_XXX +--EXTENSIONS-- +pdo +pdo_pgsql +--SKIPIF-- + +--FILE-- +setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false); + +// The LOB test is omitted because it exists separately. +foreach ([ + 'PDO::PARAM_STR', + 'PDO::PARAM_INT', + 'PDO::PARAM_BOOL', + 'PDO::PARAM_NULL', +] as $param_type_const) { + foreach ([ + null, + 0, + 8, + '', + '0', + '10', + 'str', + true, + false, + ] as $val) { + foreach ([1, 0] as $emulate) { + printf('%s, var = %s, PDO::ATTR_EMULATE_PREPARES = %d: ', $param_type_const, var_export($val, true), $emulate); + $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, $emulate); + $stmt = $db->prepare('SELECT :val as val'); + $stmt->bindValue(':val', $val, constant($param_type_const)); + $stmt->execute(); + $row = $stmt->fetch(PDO::FETCH_ASSOC); + var_dump($row['val']); + } + } + if ($param_type_const !== 'PDO::PARAM_NULL') { + echo PHP_EOL; + } +} +?> +--EXPECT-- +PDO::PARAM_STR, var = NULL, PDO::ATTR_EMULATE_PREPARES = 1: NULL +PDO::PARAM_STR, var = NULL, PDO::ATTR_EMULATE_PREPARES = 0: NULL +PDO::PARAM_STR, var = 0, PDO::ATTR_EMULATE_PREPARES = 1: string(1) "0" +PDO::PARAM_STR, var = 0, PDO::ATTR_EMULATE_PREPARES = 0: string(1) "0" +PDO::PARAM_STR, var = 8, PDO::ATTR_EMULATE_PREPARES = 1: string(1) "8" +PDO::PARAM_STR, var = 8, PDO::ATTR_EMULATE_PREPARES = 0: string(1) "8" +PDO::PARAM_STR, var = '', PDO::ATTR_EMULATE_PREPARES = 1: string(0) "" +PDO::PARAM_STR, var = '', PDO::ATTR_EMULATE_PREPARES = 0: string(0) "" +PDO::PARAM_STR, var = '0', PDO::ATTR_EMULATE_PREPARES = 1: string(1) "0" +PDO::PARAM_STR, var = '0', PDO::ATTR_EMULATE_PREPARES = 0: string(1) "0" +PDO::PARAM_STR, var = '10', PDO::ATTR_EMULATE_PREPARES = 1: string(2) "10" +PDO::PARAM_STR, var = '10', PDO::ATTR_EMULATE_PREPARES = 0: string(2) "10" +PDO::PARAM_STR, var = 'str', PDO::ATTR_EMULATE_PREPARES = 1: string(3) "str" +PDO::PARAM_STR, var = 'str', PDO::ATTR_EMULATE_PREPARES = 0: string(3) "str" +PDO::PARAM_STR, var = true, PDO::ATTR_EMULATE_PREPARES = 1: string(1) "1" +PDO::PARAM_STR, var = true, PDO::ATTR_EMULATE_PREPARES = 0: string(1) "1" +PDO::PARAM_STR, var = false, PDO::ATTR_EMULATE_PREPARES = 1: string(0) "" +PDO::PARAM_STR, var = false, PDO::ATTR_EMULATE_PREPARES = 0: string(0) "" + +PDO::PARAM_INT, var = NULL, PDO::ATTR_EMULATE_PREPARES = 1: NULL +PDO::PARAM_INT, var = NULL, PDO::ATTR_EMULATE_PREPARES = 0: NULL +PDO::PARAM_INT, var = 0, PDO::ATTR_EMULATE_PREPARES = 1: int(0) +PDO::PARAM_INT, var = 0, PDO::ATTR_EMULATE_PREPARES = 0: int(0) +PDO::PARAM_INT, var = 8, PDO::ATTR_EMULATE_PREPARES = 1: int(8) +PDO::PARAM_INT, var = 8, PDO::ATTR_EMULATE_PREPARES = 0: int(8) +PDO::PARAM_INT, var = '', PDO::ATTR_EMULATE_PREPARES = 1: int(0) +PDO::PARAM_INT, var = '', PDO::ATTR_EMULATE_PREPARES = 0: int(0) +PDO::PARAM_INT, var = '0', PDO::ATTR_EMULATE_PREPARES = 1: int(0) +PDO::PARAM_INT, var = '0', PDO::ATTR_EMULATE_PREPARES = 0: int(0) +PDO::PARAM_INT, var = '10', PDO::ATTR_EMULATE_PREPARES = 1: int(10) +PDO::PARAM_INT, var = '10', PDO::ATTR_EMULATE_PREPARES = 0: int(10) +PDO::PARAM_INT, var = 'str', PDO::ATTR_EMULATE_PREPARES = 1: int(0) +PDO::PARAM_INT, var = 'str', PDO::ATTR_EMULATE_PREPARES = 0: int(0) +PDO::PARAM_INT, var = true, PDO::ATTR_EMULATE_PREPARES = 1: int(1) +PDO::PARAM_INT, var = true, PDO::ATTR_EMULATE_PREPARES = 0: int(1) +PDO::PARAM_INT, var = false, PDO::ATTR_EMULATE_PREPARES = 1: int(0) +PDO::PARAM_INT, var = false, PDO::ATTR_EMULATE_PREPARES = 0: int(0) + +PDO::PARAM_BOOL, var = NULL, PDO::ATTR_EMULATE_PREPARES = 1: NULL +PDO::PARAM_BOOL, var = NULL, PDO::ATTR_EMULATE_PREPARES = 0: NULL +PDO::PARAM_BOOL, var = 0, PDO::ATTR_EMULATE_PREPARES = 1: string(1) "f" +PDO::PARAM_BOOL, var = 0, PDO::ATTR_EMULATE_PREPARES = 0: string(1) "f" +PDO::PARAM_BOOL, var = 8, PDO::ATTR_EMULATE_PREPARES = 1: string(1) "t" +PDO::PARAM_BOOL, var = 8, PDO::ATTR_EMULATE_PREPARES = 0: string(1) "t" +PDO::PARAM_BOOL, var = '', PDO::ATTR_EMULATE_PREPARES = 1: string(1) "f" +PDO::PARAM_BOOL, var = '', PDO::ATTR_EMULATE_PREPARES = 0: string(1) "f" +PDO::PARAM_BOOL, var = '0', PDO::ATTR_EMULATE_PREPARES = 1: string(1) "f" +PDO::PARAM_BOOL, var = '0', PDO::ATTR_EMULATE_PREPARES = 0: string(1) "f" +PDO::PARAM_BOOL, var = '10', PDO::ATTR_EMULATE_PREPARES = 1: string(1) "t" +PDO::PARAM_BOOL, var = '10', PDO::ATTR_EMULATE_PREPARES = 0: string(1) "t" +PDO::PARAM_BOOL, var = 'str', PDO::ATTR_EMULATE_PREPARES = 1: string(1) "t" +PDO::PARAM_BOOL, var = 'str', PDO::ATTR_EMULATE_PREPARES = 0: string(1) "t" +PDO::PARAM_BOOL, var = true, PDO::ATTR_EMULATE_PREPARES = 1: string(1) "t" +PDO::PARAM_BOOL, var = true, PDO::ATTR_EMULATE_PREPARES = 0: string(1) "t" +PDO::PARAM_BOOL, var = false, PDO::ATTR_EMULATE_PREPARES = 1: string(1) "f" +PDO::PARAM_BOOL, var = false, PDO::ATTR_EMULATE_PREPARES = 0: string(1) "f" + +PDO::PARAM_NULL, var = NULL, PDO::ATTR_EMULATE_PREPARES = 1: NULL +PDO::PARAM_NULL, var = NULL, PDO::ATTR_EMULATE_PREPARES = 0: NULL +PDO::PARAM_NULL, var = 0, PDO::ATTR_EMULATE_PREPARES = 1: NULL +PDO::PARAM_NULL, var = 0, PDO::ATTR_EMULATE_PREPARES = 0: NULL +PDO::PARAM_NULL, var = 8, PDO::ATTR_EMULATE_PREPARES = 1: NULL +PDO::PARAM_NULL, var = 8, PDO::ATTR_EMULATE_PREPARES = 0: NULL +PDO::PARAM_NULL, var = '', PDO::ATTR_EMULATE_PREPARES = 1: NULL +PDO::PARAM_NULL, var = '', PDO::ATTR_EMULATE_PREPARES = 0: NULL +PDO::PARAM_NULL, var = '0', PDO::ATTR_EMULATE_PREPARES = 1: NULL +PDO::PARAM_NULL, var = '0', PDO::ATTR_EMULATE_PREPARES = 0: NULL +PDO::PARAM_NULL, var = '10', PDO::ATTR_EMULATE_PREPARES = 1: NULL +PDO::PARAM_NULL, var = '10', PDO::ATTR_EMULATE_PREPARES = 0: NULL +PDO::PARAM_NULL, var = 'str', PDO::ATTR_EMULATE_PREPARES = 1: NULL +PDO::PARAM_NULL, var = 'str', PDO::ATTR_EMULATE_PREPARES = 0: NULL +PDO::PARAM_NULL, var = true, PDO::ATTR_EMULATE_PREPARES = 1: NULL +PDO::PARAM_NULL, var = true, PDO::ATTR_EMULATE_PREPARES = 0: NULL +PDO::PARAM_NULL, var = false, PDO::ATTR_EMULATE_PREPARES = 1: NULL +PDO::PARAM_NULL, var = false, PDO::ATTR_EMULATE_PREPARES = 0: NULL