diff --git a/NEWS b/NEWS index 8d6fc8d5444..5e07e3a53cf 100644 --- a/NEWS +++ b/NEWS @@ -28,6 +28,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..fa160bfb471 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"; @@ -386,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.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..c44f124a6cf --- /dev/null +++ b/ext/pdo_odbc/tests/autocommit_change_mode.phpt @@ -0,0 +1,135 @@ +--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); +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"; + +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) { + var_dump($db->getAttribute(PDO::ATTR_AUTOCOMMIT)); + echo $e->getMessage()."\n\n"; +} + +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"; +} + +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) { + var_dump($db->getAttribute(PDO::ATTR_AUTOCOMMIT)); + echo $e->getMessage()."\n\n"; +} + +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"; +} + +echo "end transaction\n"; +$db->rollback(); +echo "\n"; + +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 +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 + +done!