ext/pdo: Fixed PDO::setAttribute() and PDO::getAttribute() (#12793)

This commit is contained in:
Saki Takamachi 2023-12-05 01:05:30 +09:00 committed by GitHub
parent e502aaf0c5
commit 866aa12bcd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 104 additions and 46 deletions

12
NEWS
View File

@ -34,6 +34,18 @@ Opcache:
. If JIT is enabled, PHP will now exit with a fatal error on startup in case
of JIT startup initialization issues. (danog)
PDO:
. Fixed setAttribute and getAttribute (SakiTakamachi)
PDO_DBLIB:
. Fixed setAttribute and getAttribute (SakiTakamachi)
PDO_FIREBIRD:
. Fixed setAttribute and getAttribute (SakiTakamachi)
PDO_MYSQL:
. Fixed setAttribute and getAttribute (SakiTakamachi)
PDO_PGSQL:
. Fixed GH-12423, DSN credentials being prioritized over the user/password
PDO constructor arguments. (SakiTakamachi)

View File

@ -26,6 +26,17 @@ PHP 8.4 UPGRADE NOTES
Consult sections 2. New Features and 6. New Functions for a list of
newly implemented methods and constants.
- PDO_DBLIB:
. setAttribute, DBLIB_ATTR_STRINGIFY_UNIQUEIDENTIFIER and DBLIB_ATTR_DATETIME_CONVERT
have been changed to set value as a bool.
- PDO_FIREBIRD:
. getAttribute, ATTR_AUTOCOMMIT has been changed to get the value as a bool.
- PDO_MYSQL:
. getAttribute, ATTR_AUTOCOMMIT, ATTR_EMULATE_PREPARES, MYSQL_ATTR_DIRECT_QUERY have
been changed to get values as bool.
- PDO_PGSQL:
. The DSN's credentials, when set, are given priority over their PDO
constructor counterparts, being closer to the documentation states.
@ -139,6 +150,16 @@ PHP 8.4 UPGRADE NOTES
. The behavior of mb_strcut is more consistent now on invalid UTF-8 and UTF-16
strings. (For valid UTF-8 and UTF-16 strings, there is no change.)
- PDO:
. getAttribute, enabled to get the value of ATTR_STRINGIFY_FETCHES.
- PDO_FIREBIRD:
. getAttribute, enabled to get values of FB_ATTR_DATE_FORMAT, FB_ATTR_TIME_FORMAT,
FB_ATTR_TIMESTAMP_FORMAT.
- PDO_MYSQL:
. getAttribute, enabled to get the value of ATTR_FETCH_TABLE_NAMES.
- PGSQL:
. pg_select, the conditions arguments accepts an empty array and is optional.

View File

@ -911,8 +911,13 @@ PHP_METHOD(PDO, getAttribute)
add_next_index_zval(return_value, &dbh->def_stmt_ctor_args);
}
return;
case PDO_ATTR_DEFAULT_FETCH_MODE:
RETURN_LONG(dbh->default_fetch_type);
case PDO_ATTR_STRINGIFY_FETCHES:
RETURN_BOOL(dbh->stringify);
default:
break;
}

View File

@ -290,10 +290,10 @@ static bool dblib_set_attr(pdo_dbh_t *dbh, zend_long attr, zval *val)
}
return SUCCEED == dbsettime(lval);
case PDO_DBLIB_ATTR_STRINGIFY_UNIQUEIDENTIFIER:
if (!pdo_get_long_param(&lval, val)) {
if (!pdo_get_bool_param(&bval, val)) {
return false;
}
H->stringify_uniqueidentifier = lval;
H->stringify_uniqueidentifier = bval;
return true;
case PDO_DBLIB_ATTR_SKIP_EMPTY_ROWSETS:
if (!pdo_get_bool_param(&bval, val)) {
@ -302,10 +302,10 @@ static bool dblib_set_attr(pdo_dbh_t *dbh, zend_long attr, zval *val)
H->skip_empty_rowsets = bval;
return true;
case PDO_DBLIB_ATTR_DATETIME_CONVERT:
if (!pdo_get_long_param(&lval, val)) {
if (!pdo_get_bool_param(&bval, val)) {
return false;
}
H->datetime_convert = lval;
H->datetime_convert = bval;
return true;
default:
return false;

View File

@ -1080,7 +1080,7 @@ static int pdo_firebird_get_attribute(pdo_dbh_t *dbh, zend_long attr, zval *val)
char tmp[INFO_BUF_LEN];
case PDO_ATTR_AUTOCOMMIT:
ZVAL_LONG(val,dbh->auto_commit);
ZVAL_BOOL(val,dbh->auto_commit);
return 1;
case PDO_ATTR_CONNECTION_STATUS:
@ -1124,6 +1124,18 @@ static int pdo_firebird_get_attribute(pdo_dbh_t *dbh, zend_long attr, zval *val)
case PDO_ATTR_FETCH_TABLE_NAMES:
ZVAL_BOOL(val, H->fetch_table_names);
return 1;
case PDO_FB_ATTR_DATE_FORMAT:
ZVAL_STRING(val, H->date_format);
return 1;
case PDO_FB_ATTR_TIME_FORMAT:
ZVAL_STRING(val, H->time_format);
return 1;
case PDO_FB_ATTR_TIMESTAMP_FORMAT:
ZVAL_STRING(val, H->timestamp_format);
return 1;
}
return 0;
}

View File

@ -91,30 +91,30 @@ echo "done!";
--EXPECT--
========== not in manually transaction ==========
auto commit ON from ON
int(1)
bool(true)
Success
auto commit OFF from ON
int(0)
bool(false)
Success
auto commit OFF from OFF
int(0)
bool(false)
Success
auto commit ON from OFF
int(1)
bool(true)
Success
========== in manually transaction ==========
begin transaction
auto commit ON from ON, expect error
int(1)
bool(true)
SQLSTATE[HY000]: General error: Cannot change autocommit mode while a transaction is already open
auto commit OFF from ON, expect error
int(1)
bool(true)
SQLSTATE[HY000]: General error: Cannot change autocommit mode while a transaction is already open
end transaction
@ -122,11 +122,11 @@ auto commit OFF
begin transaction
auto commit ON from OFF, expect error
int(0)
bool(false)
SQLSTATE[HY000]: General error: Cannot change autocommit mode while a transaction is already open
auto commit OFF from OFF, expect error
int(0)
bool(false)
SQLSTATE[HY000]: General error: Cannot change autocommit mode while a transaction is already open
end transaction

View File

@ -532,7 +532,7 @@ static int pdo_mysql_get_attribute(pdo_dbh_t *dbh, zend_long attr, zval *return_
break;
case PDO_ATTR_AUTOCOMMIT:
ZVAL_LONG(return_value, dbh->auto_commit);
ZVAL_BOOL(return_value, dbh->auto_commit);
break;
case PDO_ATTR_DEFAULT_STR_PARAM:
@ -545,7 +545,7 @@ static int pdo_mysql_get_attribute(pdo_dbh_t *dbh, zend_long attr, zval *return_
case PDO_ATTR_EMULATE_PREPARES:
case PDO_MYSQL_ATTR_DIRECT_QUERY:
ZVAL_LONG(return_value, H->emulate_prepare);
ZVAL_BOOL(return_value, H->emulate_prepare);
break;
#ifndef PDO_USE_MYSQLND
@ -576,6 +576,10 @@ static int pdo_mysql_get_attribute(pdo_dbh_t *dbh, zend_long attr, zval *return_
}
#endif
case PDO_ATTR_FETCH_TABLE_NAMES:
ZVAL_BOOL(return_value, H->fetch_table_names);
break;
default:
PDO_DBG_RETURN(0);
}

View File

@ -15,8 +15,8 @@ $pdo->setAttribute (\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
$attrs = [
// Extensive test: default value and set+get values
PDO::ATTR_EMULATE_PREPARES => array(null, 1, 0),
PDO::MYSQL_ATTR_DIRECT_QUERY => array(null, 0, 1),
PDO::ATTR_EMULATE_PREPARES => array(null, true, false),
PDO::MYSQL_ATTR_DIRECT_QUERY => array(null, false, true),
PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => array(null, false, true),
// Just test the default
@ -67,16 +67,16 @@ foreach ($attrs as $a => $vals) {
?>
--EXPECTF--
int(1)
bool(true)
OK
OK
int(0)
bool(false)
OK
OK
bool(true)
OK
OK
int(1)
bool(true)
ERR
ERR
int(2)
@ -93,9 +93,9 @@ array(1) {
[0]=>
string(12) "PDOStatement"
}
ERR
bool(false)
ERR
string(5) "mysql"
ERR
bool(false)
ERR
int(4)

View File

@ -54,15 +54,15 @@ MySQLPDOTest::skip();
$defaults = [
PDO::ATTR_PERSISTENT => false,
PDO::ATTR_AUTOCOMMIT => 1,
PDO::ATTR_AUTOCOMMIT => true,
/* TODO - why is this a valid option if getAttribute() does not support it?! */
PDO::ATTR_TIMEOUT => false,
PDO::ATTR_EMULATE_PREPARES => 1,
PDO::ATTR_EMULATE_PREPARES => true,
PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true,
/* TODO getAttribute() does not handle it */
PDO::MYSQL_ATTR_LOCAL_INFILE => false,
/* TODO getAttribute() does not handle it */
PDO::MYSQL_ATTR_DIRECT_QUERY => 1,
PDO::MYSQL_ATTR_DIRECT_QUERY => true,
PDO::MYSQL_ATTR_INIT_COMMAND => '',
];
@ -151,8 +151,8 @@ MySQLPDOTest::skip();
set_option_and_check(24, PDO::MYSQL_ATTR_INIT_COMMAND, '', 'PDO::MYSQL_ATTR_INIT_COMMAND');
set_option_and_check(25, PDO::MYSQL_ATTR_INIT_COMMAND, 'INSERT INTO nonexistent(invalid) VALUES (1)', 'PDO::MYSQL_ATTR_INIT_COMMAND');
set_option_and_check(33, PDO::MYSQL_ATTR_DIRECT_QUERY, 1, 'PDO::MYSQL_ATTR_DIRECT_QUERY');
set_option_and_check(34, PDO::MYSQL_ATTR_DIRECT_QUERY, 0, 'PDO::MYSQL_ATTR_DIRECT_QUERY');
set_option_and_check(33, PDO::MYSQL_ATTR_DIRECT_QUERY, true, 'PDO::MYSQL_ATTR_DIRECT_QUERY');
set_option_and_check(34, PDO::MYSQL_ATTR_DIRECT_QUERY, false, 'PDO::MYSQL_ATTR_DIRECT_QUERY');
if (defined('PDO::MYSQL_ATTR_LOCAL_INFILE_DIRECTORY')) {
set_option_and_check(35, PDO::MYSQL_ATTR_LOCAL_INFILE_DIRECTORY, null, 'PDO::MYSQL_ATTR_LOCAL_INFILE_DIRECTORY');

View File

@ -13,7 +13,7 @@ MySQLPDOTest::skip();
$db = MySQLPDOTest::factory();
// autocommit should be on by default
if (1 !== ($tmp = $db->getAttribute(PDO::ATTR_AUTOCOMMIT)))
if (true !== ($tmp = $db->getAttribute(PDO::ATTR_AUTOCOMMIT)))
printf("[001] Expecting int/1 got %s\n", var_export($tmp, true));
// lets see if the server agrees to that
@ -33,7 +33,7 @@ MySQLPDOTest::skip();
if (!$db->query('SET autocommit = 1'))
printf("[005] Cannot turn on server autocommit mode, %s\n", var_export($db->errorInfo(), true));
if (0 !== ($tmp = $db->getAttribute(PDO::ATTR_AUTOCOMMIT)))
if (false !== ($tmp = $db->getAttribute(PDO::ATTR_AUTOCOMMIT)))
printf("[006] Expecting int/0 got %s\n", var_export($tmp, true));
// off -> on
@ -47,7 +47,7 @@ MySQLPDOTest::skip();
if (!$row['_autocommit'])
printf("[009] Server autocommit mode should be on, got '%s'\n", var_export($row['_autocommit']));
if (1 !== ($tmp = $db->getAttribute(PDO::ATTR_AUTOCOMMIT)))
if (true !== ($tmp = $db->getAttribute(PDO::ATTR_AUTOCOMMIT)))
printf("[010] Expecting int/1 got %s\n", var_export($tmp, true));
$table = 'pdo_mysql_attr_autocommit';

View File

@ -15,12 +15,14 @@ MySQLPDOTest::skip();
$table = 'pdo_mysql_attr_fetch_table_names';
MySQLPDOTest::createTestTable($table, $db);
$db->setAttribute(PDO::ATTR_FETCH_TABLE_NAMES, 1);
$db->setAttribute(PDO::ATTR_FETCH_TABLE_NAMES, true);
var_dump($db->getAttribute(PDO::ATTR_FETCH_TABLE_NAMES));
$stmt = $db->query("SELECT label FROM {$table} LIMIT 1");
var_dump($stmt->fetchAll(PDO::FETCH_ASSOC));
$stmt->closeCursor();
$db->setAttribute(PDO::ATTR_FETCH_TABLE_NAMES, 0);
$db->setAttribute(PDO::ATTR_FETCH_TABLE_NAMES, false);
var_dump($db->getAttribute(PDO::ATTR_FETCH_TABLE_NAMES));
$stmt = $db->query("SELECT label FROM {$table} LIMIT 1");
var_dump($stmt->fetchAll(PDO::FETCH_ASSOC));
$stmt->closeCursor();
@ -34,6 +36,7 @@ $db = MySQLPDOTest::factory();
$db->query('DROP TABLE IF EXISTS pdo_mysql_attr_fetch_table_names');
?>
--EXPECT--
bool(true)
array(1) {
[0]=>
array(1) {
@ -41,6 +44,7 @@ array(1) {
string(1) "a"
}
}
bool(false)
array(1) {
[0]=>
array(1) {

View File

@ -16,14 +16,14 @@ MySQLPDOTest::skipNotTransactionalEngine();
MySQLPDOTest::createTestTable($table, $db, MySQLPDOTest::detect_transactional_mysql_engine($db));
if (1 !== $db->getAttribute(PDO::ATTR_AUTOCOMMIT))
if (true !== $db->getAttribute(PDO::ATTR_AUTOCOMMIT))
printf("[001] Autocommit should be on by default\n");
if (false == $db->beginTransaction())
printf("[002] Cannot start a transaction, [%s] [%s]\n",
$db->errorCode(), implode(' ', $db->errorInfo()));
if (1 !== $db->getAttribute(PDO::ATTR_AUTOCOMMIT))
if (true !== $db->getAttribute(PDO::ATTR_AUTOCOMMIT))
printf("[003] Autocommit should be on by default, beginTransaction() shall not impact it\n");
if (0 == $db->exec("DELETE FROM {$table}"))
@ -50,7 +50,7 @@ MySQLPDOTest::skipNotTransactionalEngine();
if (!$db->commit())
printf("[008] [%s] %s\n", $db->errorCode(), implode(' ', $db->errorInfo()));
if (1 !== $db->getAttribute(PDO::ATTR_AUTOCOMMIT))
if (true !== $db->getAttribute(PDO::ATTR_AUTOCOMMIT))
printf("[009] Autocommit should be on after commit()\n");
if (!($stmt = $db->query(sprintf('SELECT id, label FROM %s WHERE id = %d', $table, $row['id']))))
@ -91,7 +91,7 @@ MySQLPDOTest::skipNotTransactionalEngine();
if (!$db->rollback())
printf("[018] [%s] %s\n", $db->errorCode(), implode(' ', $db->errorInfo()));
if (1 !== $db->getAttribute(PDO::ATTR_AUTOCOMMIT))
if (true !== $db->getAttribute(PDO::ATTR_AUTOCOMMIT))
printf("[019] Autocommit should be on after rollback\n");
if (!($stmt = $db->query(sprintf('SELECT id, label FROM %s WHERE id = %d', $table, $row['id']))))
@ -132,7 +132,7 @@ MySQLPDOTest::skipNotTransactionalEngine();
// Turn off autocommit using a server variable
$db->exec('SET @@autocommit = 0');
if (1 === $db->getAttribute(PDO::ATTR_AUTOCOMMIT))
if (true === $db->getAttribute(PDO::ATTR_AUTOCOMMIT))
printf("[028] I'm confused, how can autocommit be on? Didn't I say I want to manually control transactions?\n");
if (!$db->beginTransaction())
@ -155,7 +155,7 @@ MySQLPDOTest::skipNotTransactionalEngine();
printf("[031] Cannot start a transaction, [%s] [%s]\n",
$db->errorCode(), implode(' ', $db->errorInfo()));
if (1 !== $db->getAttribute(PDO::ATTR_AUTOCOMMIT))
if (true !== $db->getAttribute(PDO::ATTR_AUTOCOMMIT))
printf("[032] Autocommit should be on my default, beginTransaction() should not change that\n");
if (0 == $db->exec("DELETE FROM {$table}"))

View File

@ -35,7 +35,7 @@ MySQLPDOTest::skipNotTransactionalEngine();
// pdo_transaction_transitions should check this as well...
// ... just to be sure the most basic stuff really works we check it again...
if (1 !== ($tmp = $db->getAttribute(PDO::ATTR_AUTOCOMMIT)))
if (true !== ($tmp = $db->getAttribute(PDO::ATTR_AUTOCOMMIT)))
printf("[003] According to the manual we should be back to autocommit mode, got %s/%s\n",
gettype($tmp), var_export($tmp, true));

View File

@ -43,7 +43,7 @@ MySQLPDOTest::skipNotTransactionalEngine();
return true;
}
set_and_get(1, $db, PDO::ATTR_AUTOCOMMIT, 1);
set_and_get(1, $db, PDO::ATTR_AUTOCOMMIT, true);
/*
set_and_get(2, $db, PDO::ATTR_AUTOCOMMIT, 0);
set_and_get(3, $db, PDO::ATTR_AUTOCOMMIT, -1);

View File

@ -93,8 +93,8 @@ $db->exec('DROP TABLE IF EXISTS pdo_mysql_rollback');
$db->exec('DROP TABLE IF EXISTS pdo_mysql_rollback_2');
?>
--EXPECT--
int(1)
int(0)
int(1)
int(0)
bool(true)
bool(false)
bool(true)
bool(false)
done!

View File

@ -384,7 +384,7 @@ static int odbc_handle_get_attr(pdo_dbh_t *dbh, zend_long attr, zval *val)
case PDO_ATTR_CONNECTION_STATUS:
break;
case PDO_ODBC_ATTR_ASSUME_UTF8:
ZVAL_BOOL(val, H->assume_utf8 ? 1 : 0);
ZVAL_BOOL(val, H->assume_utf8);
return 1;
}

View File

@ -95,6 +95,6 @@ array(1) {
ERR
ERR
string(5) "pgsql"
ERR
bool(true)
ERR
int(4)