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!