From 933dccb79b054ff5ff2d33ddf45c4d9ae89fdbfc Mon Sep 17 00:00:00 2001 From: SakiTakamachi Date: Sat, 25 Nov 2023 21:22:50 +0900 Subject: [PATCH 1/2] Fix GH-12767: Fixed to be able to change autocommit mode using setAttribute Signed-off-by: Gina Peter Banyard --- NEWS | 4 + ext/pdo_odbc/odbc_driver.c | 24 ++++ ext/pdo_odbc/tests/autocommit.phpt | 53 ++++++++ .../tests/autocommit_change_mode.phpt | 119 ++++++++++++++++++ 4 files changed, 200 insertions(+) create mode 100644 ext/pdo_odbc/tests/autocommit.phpt create mode 100644 ext/pdo_odbc/tests/autocommit_change_mode.phpt diff --git a/NEWS b/NEWS index a061e779884..96b5ca80dcd 100644 --- a/NEWS +++ b/NEWS @@ -20,6 +20,10 @@ PHP NEWS . Added workaround for SELinux mprotect execheap issue. See https://bugzilla.kernel.org/show_bug.cgi?id=218258. (ilutov) +- PDO_ODBC: + . Fixed bug GH-12767 (Unable to turn on autocommit mode with setAttribute()). + (SakiTakamachi) + - PHPDBG: . Fixed bug GH-12962 (Double free of init_file in phpdbg_prompt.c). (nielsdos) diff --git a/ext/pdo_odbc/odbc_driver.c b/ext/pdo_odbc/odbc_driver.c index a9d5befdac8..6ab6314e224 100644 --- a/ext/pdo_odbc/odbc_driver.c +++ b/ext/pdo_odbc/odbc_driver.c @@ -344,6 +344,30 @@ static bool odbc_handle_set_attr(pdo_dbh_t *dbh, zend_long attr, zval *val) } H->assume_utf8 = bval; return true; + case PDO_ATTR_AUTOCOMMIT: + if (!pdo_get_bool_param(&bval, val)) { + return false; + } + if (dbh->in_txn) { + pdo_raise_impl_error(dbh, NULL, "HY000", "Cannot change autocommit mode while a transaction is already open"); + return false; + } + if (dbh->auto_commit ^ bval) { + dbh->auto_commit = bval; + RETCODE rc = SQLSetConnectAttr( + H->dbc, + SQL_ATTR_AUTOCOMMIT, + dbh->auto_commit ? (SQLPOINTER) SQL_AUTOCOMMIT_ON : (SQLPOINTER) SQL_AUTOCOMMIT_OFF, + SQL_IS_INTEGER + ); + if (rc != SQL_SUCCESS) { + pdo_odbc_drv_error( + dbh->auto_commit ? "SQLSetConnectAttr AUTOCOMMIT = ON" : "SQLSetConnectAttr AUTOCOMMIT = OFF" + ); + return false; + } + } + return true; default: strcpy(H->einfo.last_err_msg, "Unknown Attribute"); H->einfo.what = "setAttribute"; diff --git a/ext/pdo_odbc/tests/autocommit.phpt b/ext/pdo_odbc/tests/autocommit.phpt new file mode 100644 index 00000000000..3e17a4ad2e9 --- /dev/null +++ b/ext/pdo_odbc/tests/autocommit.phpt @@ -0,0 +1,53 @@ +--TEST-- +PDO ODBC auto commit mode +--EXTENSIONS-- +pdo_odbc +--SKIPIF-- + +--XLEAK-- +A bug in msodbcsql causes a memory leak when reconnecting after closing. See GH-12306 +--FILE-- +exec("CREATE TABLE {$table} (id INT, name VARCHAR(255))"); +unset($db); + +$db = new PDO(getenv('PDOTEST_DSN'), getenv('PDOTEST_USER'), getenv('PDOTEST_PASS'), [ + PDO::ATTR_AUTOCOMMIT => 0, +]); + +$db->setAttribute(PDO::ATTR_AUTOCOMMIT, 1); +$db->query("INSERT INTO {$table} (id, name) VALUES (1, 'test')"); +unset($db); + +$db = new PDO(getenv('PDOTEST_DSN'), getenv('PDOTEST_USER'), getenv('PDOTEST_PASS')); + +$r = $db->query("SELECT * FROM {$table}"); +var_dump($r->fetchAll(PDO::FETCH_ASSOC)); + +echo "done!"; +?> +--CLEAN-- +exec("DROP TABLE IF EXISTS autocommit_pdo_odbc"); +?> +--EXPECT-- +array(1) { + [0]=> + array(2) { + ["id"]=> + string(1) "1" + ["name"]=> + string(4) "test" + } +} +done! diff --git a/ext/pdo_odbc/tests/autocommit_change_mode.phpt b/ext/pdo_odbc/tests/autocommit_change_mode.phpt new file mode 100644 index 00000000000..7b4dc21079c --- /dev/null +++ b/ext/pdo_odbc/tests/autocommit_change_mode.phpt @@ -0,0 +1,119 @@ +--TEST-- +PDO ODBC auto commit mode +--EXTENSIONS-- +pdo_odbc +--SKIPIF-- + +--FILE-- +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + +echo "========== not in transaction ==========\n"; + +echo "auto commit ON from ON\n"; +$db->setAttribute(PDO::ATTR_AUTOCOMMIT, true); +echo "Success\n\n"; + +echo "auto commit OFF from ON\n"; +$db->setAttribute(PDO::ATTR_AUTOCOMMIT, false); +echo "Success\n\n"; + +echo "auto commit OFF from OFF\n"; +$db->setAttribute(PDO::ATTR_AUTOCOMMIT, false); +echo "Success\n\n"; + +echo "auto commit ON from OFF\n"; +$db->setAttribute(PDO::ATTR_AUTOCOMMIT, true); +echo "Success\n\n"; + +echo "========== in transaction ==========\n"; + +echo "begin transaction\n"; +$db->beginTransaction(); +echo "\n"; + +echo "auto commit ON from ON, expect error\n"; +try { + $db->setAttribute(PDO::ATTR_AUTOCOMMIT, true); +} catch (PDOException $e) { + echo $e->getMessage()."\n\n"; +} + +echo "auto commit OFF from ON, expect error\n"; +try { + $db->setAttribute(PDO::ATTR_AUTOCOMMIT, false); +} catch (PDOException $e) { + echo $e->getMessage()."\n\n"; +} + +echo "end transaction\n"; +$db->rollback(); + +echo "auto commit OFF\n"; +$db->setAttribute(PDO::ATTR_AUTOCOMMIT, false); + +echo "begin transaction\n"; +$db->beginTransaction(); +echo "\n"; + +echo "auto commit ON from OFF, expect error\n"; +try { + $db->setAttribute(PDO::ATTR_AUTOCOMMIT, true); +} catch (PDOException $e) { + echo $e->getMessage()."\n\n"; +} + +echo "auto commit OFF from OFF, expect error\n"; +try { + $db->setAttribute(PDO::ATTR_AUTOCOMMIT, false); +} catch (PDOException $e) { + echo $e->getMessage()."\n\n"; +} + +echo "end transaction\n"; +$db->rollback(); +echo "\n"; + +echo "done!"; +?> +--EXPECT-- +========== not in transaction ========== +auto commit ON from ON +Success + +auto commit OFF from ON +Success + +auto commit OFF from OFF +Success + +auto commit ON from OFF +Success + +========== in transaction ========== +begin transaction + +auto commit ON from ON, expect error +SQLSTATE[HY000]: General error: Cannot change autocommit mode while a transaction is already open + +auto commit OFF from ON, expect error +SQLSTATE[HY000]: General error: Cannot change autocommit mode while a transaction is already open + +end transaction +auto commit OFF +begin transaction + +auto commit ON from OFF, expect error +SQLSTATE[HY000]: General error: Cannot change autocommit mode while a transaction is already open + +auto commit OFF from OFF, expect error +SQLSTATE[HY000]: General error: Cannot change autocommit mode while a transaction is already open + +end transaction + +done! From 2553ffeaa03bd82976f36b93f2e34618178ad361 Mon Sep 17 00:00:00 2001 From: SakiTakamachi Date: Sat, 2 Dec 2023 17:23:22 +0900 Subject: [PATCH 2/2] add PDO::ATTR_AUTOCOMMIT to getAttribute Signed-off-by: Gina Peter Banyard --- ext/pdo_odbc/odbc_driver.c | 4 +++- ext/pdo_odbc/tests/autocommit_change_mode.phpt | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/ext/pdo_odbc/odbc_driver.c b/ext/pdo_odbc/odbc_driver.c index 6ab6314e224..fa160bfb471 100644 --- a/ext/pdo_odbc/odbc_driver.c +++ b/ext/pdo_odbc/odbc_driver.c @@ -410,7 +410,9 @@ static int odbc_handle_get_attr(pdo_dbh_t *dbh, zend_long attr, zval *val) case PDO_ODBC_ATTR_ASSUME_UTF8: ZVAL_BOOL(val, H->assume_utf8 ? 1 : 0); return 1; - + case PDO_ATTR_AUTOCOMMIT: + ZVAL_BOOL(val, dbh->auto_commit); + return 1; } return 0; } diff --git a/ext/pdo_odbc/tests/autocommit_change_mode.phpt b/ext/pdo_odbc/tests/autocommit_change_mode.phpt index 7b4dc21079c..c44f124a6cf 100644 --- a/ext/pdo_odbc/tests/autocommit_change_mode.phpt +++ b/ext/pdo_odbc/tests/autocommit_change_mode.phpt @@ -17,18 +17,22 @@ echo "========== not in transaction ==========\n"; echo "auto commit ON from ON\n"; $db->setAttribute(PDO::ATTR_AUTOCOMMIT, true); +var_dump($db->getAttribute(PDO::ATTR_AUTOCOMMIT)); echo "Success\n\n"; echo "auto commit OFF from ON\n"; $db->setAttribute(PDO::ATTR_AUTOCOMMIT, false); +var_dump($db->getAttribute(PDO::ATTR_AUTOCOMMIT)); echo "Success\n\n"; echo "auto commit OFF from OFF\n"; $db->setAttribute(PDO::ATTR_AUTOCOMMIT, false); +var_dump($db->getAttribute(PDO::ATTR_AUTOCOMMIT)); echo "Success\n\n"; echo "auto commit ON from OFF\n"; $db->setAttribute(PDO::ATTR_AUTOCOMMIT, true); +var_dump($db->getAttribute(PDO::ATTR_AUTOCOMMIT)); echo "Success\n\n"; echo "========== in transaction ==========\n"; @@ -41,6 +45,7 @@ echo "auto commit ON from ON, expect error\n"; try { $db->setAttribute(PDO::ATTR_AUTOCOMMIT, true); } catch (PDOException $e) { + var_dump($db->getAttribute(PDO::ATTR_AUTOCOMMIT)); echo $e->getMessage()."\n\n"; } @@ -48,6 +53,7 @@ echo "auto commit OFF from ON, expect error\n"; try { $db->setAttribute(PDO::ATTR_AUTOCOMMIT, false); } catch (PDOException $e) { + var_dump($db->getAttribute(PDO::ATTR_AUTOCOMMIT)); echo $e->getMessage()."\n\n"; } @@ -65,6 +71,7 @@ echo "auto commit ON from OFF, expect error\n"; try { $db->setAttribute(PDO::ATTR_AUTOCOMMIT, true); } catch (PDOException $e) { + var_dump($db->getAttribute(PDO::ATTR_AUTOCOMMIT)); echo $e->getMessage()."\n\n"; } @@ -72,6 +79,7 @@ echo "auto commit OFF from OFF, expect error\n"; try { $db->setAttribute(PDO::ATTR_AUTOCOMMIT, false); } catch (PDOException $e) { + var_dump($db->getAttribute(PDO::ATTR_AUTOCOMMIT)); echo $e->getMessage()."\n\n"; } @@ -84,24 +92,30 @@ echo "done!"; --EXPECT-- ========== not in transaction ========== auto commit ON from ON +bool(true) Success auto commit OFF from ON +bool(false) Success auto commit OFF from OFF +bool(false) Success auto commit ON from OFF +bool(true) Success ========== in transaction ========== begin transaction auto commit ON from ON, expect error +bool(true) SQLSTATE[HY000]: General error: Cannot change autocommit mode while a transaction is already open auto commit OFF from ON, expect error +bool(true) SQLSTATE[HY000]: General error: Cannot change autocommit mode while a transaction is already open end transaction @@ -109,9 +123,11 @@ auto commit OFF begin transaction auto commit ON from OFF, expect error +bool(false) SQLSTATE[HY000]: General error: Cannot change autocommit mode while a transaction is already open auto commit OFF from OFF, expect error +bool(false) SQLSTATE[HY000]: General error: Cannot change autocommit mode while a transaction is already open end transaction