diff --git a/src/common/backend/utils/misc/guc/guc_storage.cpp b/src/common/backend/utils/misc/guc/guc_storage.cpp index 027976653..521a9523f 100755 --- a/src/common/backend/utils/misc/guc/guc_storage.cpp +++ b/src/common/backend/utils/misc/guc/guc_storage.cpp @@ -170,6 +170,7 @@ static bool check_and_assign_catalog_oids(List* elemlist); static const char* show_archive_command(void); bool check_enable_gtm_free(bool* newval, void** extra, GucSource source); static bool check_phony_autocommit(bool* newval, void** extra, GucSource source); +static void assign_phony_autocommit(bool newval, void* extra); static bool check_enable_data_replicate(bool* newval, void** extra, GucSource source); static bool check_adio_debug_guc(bool* newval, void** extra, GucSource source); static bool check_adio_function_guc(bool* newval, void** extra, GucSource source); @@ -521,13 +522,14 @@ static void InitStorageConfigureNamesBool() PGC_USERSET, NODE_ALL, CLIENT_CONN_STATEMENT, - gettext_noop("This parameter doesn't do anything."), - gettext_noop("It's just here so that we won't choke on SET AUTOCOMMIT TO ON from 7.3-vintage clients."), + gettext_noop("This parameter is only used in B compatibility."), + gettext_noop("Otherwise, it's just here so that we won't choke on SET AUTOCOMMIT TO ON from 7.3-vintage clients.\n" + "This parameter is used to control whether the transactions are committed automatically in B compatibility."), GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE}, &u_sess->attr.attr_storage.phony_autocommit, true, check_phony_autocommit, - NULL, + assign_phony_autocommit, NULL}, /* * security requirements: system/ordinary users can not set current transaction to read-only, @@ -4656,7 +4658,7 @@ void InitializeNumLwLockPartitions(void) static bool check_phony_autocommit(bool* newval, void** extra, GucSource source) { - if (!*newval) { + if (!*newval && (!OidIsValid(u_sess->proc_cxt.MyDatabaseId) || !DB_IS_CMPT(B_FORMAT))) { GUC_check_errcode(ERRCODE_FEATURE_NOT_SUPPORTED); GUC_check_errmsg("SET AUTOCOMMIT TO OFF is no longer supported"); return false; @@ -4665,6 +4667,18 @@ static bool check_phony_autocommit(bool* newval, void** extra, GucSource source) return true; } + +static void assign_phony_autocommit(bool newval, void* extra) +{ + /* change autocommit from false to on */ + if (newval && u_sess->attr.attr_storage.phony_autocommit != newval) { + if (!IsTransactionDefaultState() && !EndTransactionBlock()) { + ereport(ERROR, (errmsg("end transaction failed"))); + } + } + return; +} + static bool check_enable_data_replicate(bool* newval, void** extra, GucSource source) { /* Always disable data replication in multi standbys mode */ diff --git a/src/gausskernel/process/tcop/postgres.cpp b/src/gausskernel/process/tcop/postgres.cpp index a58079da8..32ab6d6a5 100755 --- a/src/gausskernel/process/tcop/postgres.cpp +++ b/src/gausskernel/process/tcop/postgres.cpp @@ -2740,6 +2740,9 @@ static void exec_simple_query(const char* query_string, MessageType messageType, ((ReindexStmt*)parsetree)->kind == OBJECT_DATABASE && IsPostmasterEnvironment) SetForceXidFromGTM(true); #endif + if (!u_sess->attr.attr_storage.phony_autocommit) { + BeginTxnForAutoCommitOff(); + } /* SQL bypass */ if (runOpfusionCheck && !IsRightRefState(plantree_list)) { (void)MemoryContextSwitchTo(oldcontext); @@ -9331,6 +9334,9 @@ int PostgresMain(int argc, char* argv[], const char* dbname, const char* usernam } } bool isQueryCompleted = false; + if (!u_sess->attr.attr_storage.phony_autocommit) { + BeginTxnForAutoCommitOff(); + } if (OpFusion::process(FUSION_EXECUTE, &input_message, completionTag, true, &isQueryCompleted)) { if(isQueryCompleted) { CommandCounterIncrement(); @@ -11249,6 +11255,9 @@ static void exec_batch_bind_execute(StringInfo input_message) * we are already in one. */ start_xact_command(); + if (!u_sess->attr.attr_storage.phony_autocommit) { + BeginTxnForAutoCommitOff(); + } if (ENABLE_WORKLOAD_CONTROL && SqlIsValid(t_thrd.postgres_cxt.debug_query_string) && (IS_PGXC_COORDINATOR || IS_SINGLE_NODE) && diff --git a/src/gausskernel/storage/access/transam/xact.cpp b/src/gausskernel/storage/access/transam/xact.cpp index 0773e66fb..6fdb04a52 100755 --- a/src/gausskernel/storage/access/transam/xact.cpp +++ b/src/gausskernel/storage/access/transam/xact.cpp @@ -8484,3 +8484,23 @@ static void AtEOXact_Proceed_PatchSeq() u_sess->opt_cxt.xact_modify_sql_patch = false; } } + +bool IsTransactionDefaultState() +{ + TransactionState s = CurrentTransactionState; + return s->blockState == TBLOCK_DEFAULT; +} + +bool IsTransactionInProgressState() +{ + TransactionState s = CurrentTransactionState; + return s->blockState == TBLOCK_INPROGRESS; +} + +void BeginTxnForAutoCommitOff() +{ + TransactionState s = CurrentTransactionState; + if (s->blockState == TBLOCK_STARTED) { + s->blockState = TBLOCK_INPROGRESS; + } +} diff --git a/src/include/access/xact.h b/src/include/access/xact.h index d98f8749d..0ff8a562d 100644 --- a/src/include/access/xact.h +++ b/src/include/access/xact.h @@ -464,4 +464,7 @@ extern void push_unlink_rel_to_hashtbl(ColFileNode *xnodes, int nrels); extern void XactCleanExceptionSubTransaction(SubTransactionId head); extern char* GetCurrentTransactionName(); extern List* GetTransactionList(List *head); +extern void BeginTxnForAutoCommitOff(); +extern bool IsTransactionInProgressState(); +extern bool IsTransactionDefaultState(); #endif /* XACT_H */ diff --git a/src/test/regress/expected/autocommit_test.out b/src/test/regress/expected/autocommit_test.out new file mode 100644 index 000000000..05ac4ec7f --- /dev/null +++ b/src/test/regress/expected/autocommit_test.out @@ -0,0 +1,243 @@ +CREATE DATABASE test_db DBCOMPATIBILITY 'B'; +\c test_db +SET autocommit = 1; +CREATE TABLE test_table (a text); +CREATE DATABASE test_drop; +INSERT INTO test_table values('aaaaa'); +SELECT * FROM test_table; + a +------- + aaaaa +(1 row) + +ROLLBACK; +NOTICE: there is no transaction in progress +SELECT * FROM test_table; + a +------- + aaaaa +(1 row) + +SET autocommit = 0; +-- DML +-- rollback the insert statement +INSERT INTO test_table values('bbbbb'); +SELECT * FROM test_table; + a +------- + aaaaa + bbbbb +(2 rows) + +ROLLBACK; +SELECT * FROM test_table; + a +------- + aaaaa +(1 row) + +-- commit the insert statement +INSERT INTO test_table values('ccccc'); +SELECT * FROM test_table; + a +------- + aaaaa + ccccc +(2 rows) + +COMMIT; +SELECT * FROM test_table; + a +------- + aaaaa + ccccc +(2 rows) + +-- commit the insert statement auto +INSERT INTO test_table values('ddddd'); +SELECT * FROM test_table; + a +------- + aaaaa + ccccc + ddddd +(3 rows) + +SET autocommit = 1; +SELECT * FROM test_table; + a +------- + aaaaa + ccccc + ddddd +(3 rows) + +SET autocommit = 0; +-- DDL +-- rollback the create table statement +CREATE TABLE test_a (a text); +INSERT INTO test_a values('aaaaa'); +SELECT * FROM test_a; + a +------- + aaaaa +(1 row) + +ROLLBACK; +SELECT * FROM test_a; +ERROR: relation "test_a" does not exist on datanode1 +LINE 1: SELECT * FROM test_a; + ^ +COMMIT; +-- commit the create table statement +CREATE TABLE test_b (a text); +INSERT INTO test_b values('aaaaa'); +SELECT * FROM test_b; + a +------- + aaaaa +(1 row) + +COMMIT; +SELECT * FROM test_b; + a +------- + aaaaa +(1 row) + +-- commit the create table statement auto +CREATE TABLE test_c (a text); +INSERT INTO test_c values('aaaaa'); +SELECT * FROM test_c; + a +------- + aaaaa +(1 row) + +SET autocommit = 1; +SELECT * FROM test_c; + a +------- + aaaaa +(1 row) + +-- prepare test +SET autocommit = 0; +INSERT INTO test_table values('eeeee'); +PREPARE TRANSACTION 'test_id'; +SET autocommit = 1; +SELECT * FROM test_table; + a +------- + aaaaa + ccccc + ddddd +(3 rows) + +COMMIT PREPARED 'test_id'; +SELECT * FROM test_table; + a +------- + aaaaa + ccccc + ddddd + eeeee +(4 rows) + +-- truncate the table test_table +TRUNCATE test_table; +SELECT * FROM test_table; + a +--- +(0 rows) + +ROLLBACK; +NOTICE: there is no transaction in progress +TRUNCATE test_table; +SELECT * FROM test_table; + a +--- +(0 rows) + +COMMIT; +WARNING: there is no transaction in progress +SELECT * FROM test_table; + a +--- +(0 rows) + +-- something statement could not execute in the transaction block +SET autocommit = 0; +START TRANSACTION; +WARNING: there is already a transaction in progress +BEGIN; +WARNING: there is already a transaction in progress +CREATE DATABASE test_error; +ERROR: CREATE DATABASE cannot run inside a transaction block +ROLLBACK; +VACUUM; +ERROR: VACUUM cannot run inside a transaction block +ROLLBACK; +DROP DATABASE test_drop; +ERROR: DROP DATABASE cannot run inside a transaction block +ROLLBACK; +CLUSTER test_table; +ERROR: CLUSTER cannot run inside a transaction block +ROLLBACK; +CREATE TABLESPACE gs_basebackup_tablespace relative LOCATION 'gs_basebackup_tablespace'; +ERROR: CREATE TABLESPACE cannot run inside a transaction block +ROLLBACK; +CREATE INDEX CONCURRENTLY ON test_table(a); +ERROR: CREATE INDEX CONCURRENTLY cannot run inside a transaction block +ROLLBACK; +REINDEX DATABASE test_db; +ERROR: REINDEX DATABASE cannot run inside a transaction block +ROLLBACK; +DROP DATABASE test_error; +ERROR: DROP DATABASE cannot run inside a transaction block +ROLLBACK; +DROP TABLESPACE test_space; +ERROR: DROP TABLESPACE cannot run inside a transaction block +ROLLBACK; +DROP INDEX test_index; +ERROR: index "test_index" does not exist +ROLLBACK; +REINDEX TABLE CONCURRENTLY test_table; +ERROR: REINDEX CONCURRENTLY cannot run inside a transaction block +-- test about set autocommit = 1 when the transaction is aborted +SET autocommit = 1; +ERROR: current transaction is aborted, commands ignored until end of transaction block, firstChar[Q] +ROLLBACK; +-- set autocommit = 0 in a transaction block +SET autocommit = 1; +TRUNCATE test_table; +BEGIN; +INSERT INTO test_table values('aaaaa'); +SET autocommit = 0; +SET autocommit = 1; +SHOW autocommit; + autocommit +------------ + on +(1 row) + +SELECT * FROM test_table; + a +------- + aaaaa +(1 row) + +-- only set autocommit = 1 cannot commit transaction +BEGIN; +INSERT INTO test_table values('bbbbb'); +SET autocommit = 1; +ROLLBACK; +SELECT * FROM test_table; + a +------- + aaaaa +(1 row) + +\c regression +DROP DATABASE test_db; +DROP DATABASE test_drop; diff --git a/src/test/regress/parallel_schedule0 b/src/test/regress/parallel_schedule0 index 23d77c7a0..e0132a4b1 100644 --- a/src/test/regress/parallel_schedule0 +++ b/src/test/regress/parallel_schedule0 @@ -190,7 +190,7 @@ test: single_node_select_implicit single_node_select_having test: single_node_union #test: single_node_case single_node_join single_node_aggregates #test: single_node_transactions -test: single_node_random transactions_test +test: single_node_random transactions_test autocommit_test #test: single_node_portals #test: single_node_arrays #test: single_node_btree_index single_node_hash_index single_node_update diff --git a/src/test/regress/sql/autocommit_test.sql b/src/test/regress/sql/autocommit_test.sql new file mode 100644 index 000000000..1d4d6e172 --- /dev/null +++ b/src/test/regress/sql/autocommit_test.sql @@ -0,0 +1,121 @@ +CREATE DATABASE test_db DBCOMPATIBILITY 'B'; +\c test_db +SET autocommit = 1; +CREATE TABLE test_table (a text); +CREATE DATABASE test_drop; +INSERT INTO test_table values('aaaaa'); +SELECT * FROM test_table; +ROLLBACK; +SELECT * FROM test_table; + +SET autocommit = 0; +-- DML +-- rollback the insert statement +INSERT INTO test_table values('bbbbb'); +SELECT * FROM test_table; +ROLLBACK; +SELECT * FROM test_table; + +-- commit the insert statement +INSERT INTO test_table values('ccccc'); +SELECT * FROM test_table; +COMMIT; +SELECT * FROM test_table; + +-- commit the insert statement auto +INSERT INTO test_table values('ddddd'); +SELECT * FROM test_table; +SET autocommit = 1; +SELECT * FROM test_table; + +SET autocommit = 0; +-- DDL +-- rollback the create table statement +CREATE TABLE test_a (a text); +INSERT INTO test_a values('aaaaa'); +SELECT * FROM test_a; +ROLLBACK; +SELECT * FROM test_a; +COMMIT; + +-- commit the create table statement +CREATE TABLE test_b (a text); +INSERT INTO test_b values('aaaaa'); +SELECT * FROM test_b; +COMMIT; +SELECT * FROM test_b; + +-- commit the create table statement auto +CREATE TABLE test_c (a text); +INSERT INTO test_c values('aaaaa'); +SELECT * FROM test_c; +SET autocommit = 1; +SELECT * FROM test_c; + +-- prepare test +SET autocommit = 0; +INSERT INTO test_table values('eeeee'); +PREPARE TRANSACTION 'test_id'; +SET autocommit = 1; +SELECT * FROM test_table; +COMMIT PREPARED 'test_id'; +SELECT * FROM test_table; + +-- truncate the table test_table +TRUNCATE test_table; +SELECT * FROM test_table; +ROLLBACK; +TRUNCATE test_table; +SELECT * FROM test_table; +COMMIT; +SELECT * FROM test_table; + +-- something statement could not execute in the transaction block +SET autocommit = 0; +START TRANSACTION; +BEGIN; +CREATE DATABASE test_error; +ROLLBACK; +VACUUM; +ROLLBACK; +DROP DATABASE test_drop; +ROLLBACK; +CLUSTER test_table; +ROLLBACK; +CREATE TABLESPACE gs_basebackup_tablespace relative LOCATION 'gs_basebackup_tablespace'; +ROLLBACK; +CREATE INDEX CONCURRENTLY ON test_table(a); +ROLLBACK; +REINDEX DATABASE test_db; +ROLLBACK; +DROP DATABASE test_error; +ROLLBACK; +DROP TABLESPACE test_space; +ROLLBACK; +DROP INDEX test_index; +ROLLBACK; +REINDEX TABLE CONCURRENTLY test_table; +-- test about set autocommit = 1 when the transaction is aborted +SET autocommit = 1; +ROLLBACK; + +-- set autocommit = 0 in a transaction block +SET autocommit = 1; +TRUNCATE test_table; +BEGIN; +INSERT INTO test_table values('aaaaa'); +SET autocommit = 0; +SET autocommit = 1; +SHOW autocommit; +SELECT * FROM test_table; + +-- only set autocommit = 1 cannot commit transaction +BEGIN; +INSERT INTO test_table values('bbbbb'); +SET autocommit = 1; +ROLLBACK; +SELECT * FROM test_table; + +\c regression +DROP DATABASE test_db; +DROP DATABASE test_drop;