diff --git a/NEWS b/NEWS index eea1ce0bf0f..d22eaf52224 100644 --- a/NEWS +++ b/NEWS @@ -34,6 +34,10 @@ PHP NEWS . Fixed bug #80783 (PDO ODBC truncates BLOB records at every 256th byte). (cmb) +- PDO_pgsql: + . Fixed bug #80892 (PDO::PARAM_INT is treated the same as PDO::PARAM_STR). + (Matteo) + - Session: . Fixed bug #80889 (Cannot set save handler when save_handler is invalid). (cmb) diff --git a/ext/pdo_pgsql/pgsql_statement.c b/ext/pdo_pgsql/pgsql_statement.c index 21f4c83807e..a7b4bc1d8b3 100644 --- a/ext/pdo_pgsql/pgsql_statement.c +++ b/ext/pdo_pgsql/pgsql_statement.c @@ -52,6 +52,19 @@ #define VARCHARLABEL "varchar" #define VARCHAROID 1043 +#define PG_INT32_MIN (-0x7FFFFFFF-1) +#define PG_INT32_MAX (0x7FFFFFFF) + +#if defined(_MSC_VER) +# define strtoll(s, f, b) _atoi64(s) +#elif !defined(HAVE_STRTOLL) +# if defined(HAVE_ATOLL) +# define strtoll(s, f, b) atoll(s) +# else +# define strtoll(s, f, b) strtol(s, f, b) +# endif +#endif + static int pgsql_stmt_dtor(pdo_stmt_t *stmt) @@ -388,7 +401,16 @@ static int pgsql_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_data * S->param_formats[param->paramno] = 0; } - if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) { + if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_INT) { + /* we need to check if the number requires bigints */ + long long val = strtoll(Z_STRVAL_P(parameter), NULL, 10); + + if (val > PG_INT32_MAX || val < PG_INT32_MIN) { + S->param_types[param->paramno] = INT8OID; + } else { + S->param_types[param->paramno] = INT4OID; + } + } else if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_LOB) { S->param_types[param->paramno] = 0; S->param_formats[param->paramno] = 1; } else { diff --git a/ext/pdo_pgsql/tests/bug_80892.phpt b/ext/pdo_pgsql/tests/bug_80892.phpt new file mode 100644 index 00000000000..8c48b169560 --- /dev/null +++ b/ext/pdo_pgsql/tests/bug_80892.phpt @@ -0,0 +1,77 @@ +--TEST-- +Bug #80892 PDO::PARAM_INT is treated the same as PDO::PARAM_STR +--SKIPIF-- + +--FILE-- +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); +$db->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC); + +$db->exec("CREATE OR REPLACE FUNCTION bug80892 (x bigint) RETURNS varchar AS $$ SELECT 'bigint' $$ LANGUAGE sql"); +$db->exec("CREATE OR REPLACE FUNCTION bug80892 (x int) RETURNS varchar AS $$ SELECT 'int' $$ LANGUAGE sql"); +$db->exec("CREATE OR REPLACE FUNCTION bug80892 (x text) RETURNS varchar AS $$ SELECT 'text' $$ LANGUAGE sql"); + +// Sanity check +var_dump($db->query("SELECT bug80892(2147483648)")->fetchColumn()); +var_dump($db->query("SELECT bug80892(1)")->fetchColumn()); +var_dump($db->query("SELECT bug80892('1')")->fetchColumn()); + +// No binding +$stmt = $db->prepare("SELECT bug80892(?)"); +$stmt->execute([1]); +var_dump($stmt->fetchColumn()); + +// Bind int value as string +$stmt = $db->prepare("SELECT bug80892(?)"); +$stmt->bindValue(1, 1, PDO::PARAM_STR); +$stmt->execute(); +var_dump($stmt->fetchColumn()); + +// Bind int value as int +$stmt = $db->prepare("SELECT bug80892(?)"); +$stmt->bindValue(1, 1, PDO::PARAM_INT); +$stmt->execute(); +var_dump($stmt->fetchColumn()); + +// Bind string value as int +$stmt = $db->prepare("SELECT bug80892(?)"); +$stmt->bindValue(1, '1', PDO::PARAM_INT); +$stmt->execute(); +var_dump($stmt->fetchColumn()); + +// Bind bigint string value as int +$stmt = $db->prepare("SELECT bug80892(?)"); +$stmt->bindValue(1, '2147483648', PDO::PARAM_INT); +$stmt->execute(); +var_dump($stmt->fetchColumn()); + +// Bind negative bigint (string on 32bit) as int +$stmt = $db->prepare("SELECT bug80892(?)"); +$stmt->bindValue(1, PHP_INT_SIZE == 4 ? '-33333333333' : -33333333333, PDO::PARAM_INT); +$stmt->execute(); +var_dump($stmt->fetchColumn()); + +$db->exec("DROP FUNCTION bug80892 (bigint)"); +$db->exec("DROP FUNCTION bug80892 (int)"); +$db->exec("DROP FUNCTION bug80892 (text)"); + +?> +--EXPECT-- +string(6) "bigint" +string(3) "int" +string(4) "text" +string(4) "text" +string(4) "text" +string(3) "int" +string(3) "int" +string(6) "bigint" +string(6) "bigint"