diff options
Diffstat (limited to 'src/backend')
-rw-r--r-- | src/backend/access/transam/xact.c | 6 | ||||
-rw-r--r-- | src/backend/catalog/namespace.c | 26 | ||||
-rw-r--r-- | src/backend/commands/copy.c | 6 | ||||
-rw-r--r-- | src/backend/executor/execMain.c | 50 | ||||
-rw-r--r-- | src/backend/parser/gram.y | 87 | ||||
-rw-r--r-- | src/backend/tcop/utility.c | 125 | ||||
-rw-r--r-- | src/backend/utils/misc/guc.c | 18 |
7 files changed, 267 insertions, 51 deletions
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c index 0f30e13c848..7150569a228 100644 --- a/src/backend/access/transam/xact.c +++ b/src/backend/access/transam/xact.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/access/transam/xact.c,v 1.140 2002/11/23 03:59:06 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/access/transam/xact.c,v 1.141 2003/01/10 22:03:27 petere Exp $ * * NOTES * Transaction aborts can now occur two ways: @@ -208,6 +208,9 @@ TransactionState CurrentTransactionState = &CurrentTransactionStateData; int DefaultXactIsoLevel = XACT_READ_COMMITTED; int XactIsoLevel; +bool DefaultXactReadOnly = false; +bool XactReadOnly; + bool autocommit = true; int CommitDelay = 0; /* precommit delay in microseconds */ @@ -848,6 +851,7 @@ StartTransaction(void) FreeXactSnapshot(); XactIsoLevel = DefaultXactIsoLevel; + XactReadOnly = DefaultXactReadOnly; /* * Check the current transaction state. If the transaction system is diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c index 0c571ca2a40..d58313a2ee0 100644 --- a/src/backend/catalog/namespace.c +++ b/src/backend/catalog/namespace.c @@ -13,7 +13,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/catalog/namespace.c,v 1.43 2003/01/07 20:56:06 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/catalog/namespace.c,v 1.44 2003/01/10 22:03:27 petere Exp $ * *------------------------------------------------------------------------- */ @@ -279,6 +279,30 @@ RelnameGetRelid(const char *relname) } /* + * RelidGetNamespaceId + * Given a relation OID, return the namespace OID. + */ +Oid +RelidGetNamespaceId(Oid relid) +{ + HeapTuple tuple; + Form_pg_class pg_class_form; + Oid result; + + tuple = SearchSysCache(RELOID, + ObjectIdGetDatum(relid), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for relation %u", relid); + pg_class_form = (Form_pg_class) GETSTRUCT(tuple); + + result = pg_class_form->relnamespace; + ReleaseSysCache(tuple); + return result; +} + + +/* * RelationIsVisible * Determine whether a relation (identified by OID) is visible in the * current search path. Visible means "would be found by searching diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index fd8c6b83a82..91386eeb2cc 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.187 2002/12/15 16:17:38 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.188 2003/01/10 22:03:27 petere Exp $ * *------------------------------------------------------------------------- */ @@ -348,6 +348,10 @@ DoCopy(const CopyStmt *stmt) */ rel = heap_openrv(relation, (is_from ? RowExclusiveLock : AccessShareLock)); + /* check read-only transaction */ + if (XactReadOnly && !is_from && !isTempNamespace(RelationGetNamespace(rel))) + elog(ERROR, "transaction is read-only"); + /* Check permissions. */ aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(), required_access); diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 613a62c3c37..a1a8134a6a7 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -26,7 +26,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.196 2003/01/08 23:32:29 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.197 2003/01/10 22:03:27 petere Exp $ * *------------------------------------------------------------------------- */ @@ -85,6 +85,7 @@ static void ExecUpdate(TupleTableSlot *slot, ItemPointer tupleid, static TupleTableSlot *EvalPlanQualNext(EState *estate); static void EndEvalPlanQual(EState *estate); static void ExecCheckRTEPerms(RangeTblEntry *rte, CmdType operation); +static void ExecCheckXactReadOnly(Query *parsetree, CmdType operation); static void EvalPlanQualStart(evalPlanQual *epq, EState *estate, evalPlanQual *priorepq); static void EvalPlanQualStop(evalPlanQual *epq); @@ -202,6 +203,14 @@ ExecutorRun(QueryDesc *queryDesc, dest = queryDesc->dest; /* + * If the transaction is read-only, we need to check if any writes + * are planned to non-temporary tables. This is done here at this + * rather late stage so that we can handle EXPLAIN vs. EXPLAIN + * ANALYZE easily. + */ + ExecCheckXactReadOnly(queryDesc->parsetree, operation); + + /* * startup tuple receiver */ estate->es_processed = 0; @@ -385,6 +394,45 @@ ExecCheckRTEPerms(RangeTblEntry *rte, CmdType operation) */ +static void +ExecCheckXactReadOnly(Query *parsetree, CmdType operation) +{ + if (!XactReadOnly) + return; + + /* CREATE TABLE AS or SELECT INTO */ + if (operation == CMD_SELECT && parsetree->into != NULL) + goto fail; + + if (operation == CMD_DELETE || operation == CMD_INSERT + || operation == CMD_UPDATE) + { + List *lp; + + foreach(lp, parsetree->rtable) + { + RangeTblEntry *rte = lfirst(lp); + + if (rte->rtekind != RTE_RELATION) + continue; + + if (!rte->checkForWrite) + continue; + + if (isTempNamespace(RelidGetNamespaceId(rte->relid))) + continue; + + goto fail; + } + } + + return; + +fail: + elog(ERROR, "transaction is read-only"); +} + + /* ---------------------------------------------------------------- * InitPlan * diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index fd33601cc08..2703ae8a061 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -11,7 +11,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.394 2003/01/10 21:08:13 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.395 2003/01/10 22:03:27 petere Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -162,7 +162,7 @@ static void doNegateFloat(Value *v); %type <defelt> createdb_opt_item copy_opt_item %type <ival> opt_lock lock_type cast_context -%type <boolean> opt_force opt_or_replace +%type <boolean> opt_force opt_or_replace transaction_access_mode %type <list> user_list @@ -215,7 +215,8 @@ static void doNegateFloat(Value *v); target_list update_target_list insert_column_list insert_target_list def_list opt_indirection group_clause TriggerFuncArgs select_limit - opt_select_limit opclass_item_list trans_options + opt_select_limit opclass_item_list transaction_mode_list + transaction_mode_list_or_empty TableFuncElementList prep_type_clause prep_type_list execute_param_clause @@ -863,18 +864,18 @@ set_rest: ColId TO var_list_or_default n->args = makeList1($3); $$ = n; } - | TRANSACTION ISOLATION LEVEL iso_level opt_mode + | TRANSACTION transaction_mode_list { VariableSetStmt *n = makeNode(VariableSetStmt); - n->name = "TRANSACTION ISOLATION LEVEL"; - n->args = makeList1(makeStringConst($4, NULL)); + n->name = "TRANSACTION"; + n->args = $2; $$ = n; } - | SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL iso_level + | SESSION CHARACTERISTICS AS TRANSACTION transaction_mode_list { VariableSetStmt *n = makeNode(VariableSetStmt); - n->name = "default_transaction_isolation"; - n->args = makeList1(makeStringConst($7, NULL)); + n->name = "SESSION CHARACTERISTICS"; + n->args = $5; $$ = n; } | NAMES opt_encoding @@ -922,16 +923,6 @@ iso_level: READ COMMITTED { $$ = "read committed"; } | SERIALIZABLE { $$ = "serializable"; } ; -opt_mode: READ WRITE - {} - | READ ONLY - { - elog(ERROR, "SET TRANSACTION/READ ONLY not yet supported"); - } - | /*EMPTY*/ - {} - ; - opt_boolean: TRUE_P { $$ = "true"; } | FALSE_P { $$ = "false"; } @@ -1020,7 +1011,7 @@ VariableShowStmt: | SHOW TRANSACTION ISOLATION LEVEL { VariableShowStmt *n = makeNode(VariableShowStmt); - n->name = "TRANSACTION ISOLATION LEVEL"; + n->name = "transaction_isolation"; $$ = (Node *) n; } | SHOW SESSION AUTHORIZATION @@ -1053,7 +1044,7 @@ VariableResetStmt: | RESET TRANSACTION ISOLATION LEVEL { VariableResetStmt *n = makeNode(VariableResetStmt); - n->name = "TRANSACTION ISOLATION LEVEL"; + n->name = "transaction_isolation"; $$ = (Node *) n; } | RESET SESSION AUTHORIZATION @@ -3500,42 +3491,42 @@ UnlistenStmt: *****************************************************************************/ TransactionStmt: - ABORT_TRANS opt_trans + ABORT_TRANS opt_transaction { TransactionStmt *n = makeNode(TransactionStmt); n->command = ROLLBACK; n->options = NIL; $$ = (Node *)n; } - | BEGIN_TRANS opt_trans + | BEGIN_TRANS opt_transaction { TransactionStmt *n = makeNode(TransactionStmt); n->command = BEGIN_TRANS; n->options = NIL; $$ = (Node *)n; } - | START TRANSACTION trans_options + | START TRANSACTION transaction_mode_list_or_empty { TransactionStmt *n = makeNode(TransactionStmt); n->command = START; n->options = $3; $$ = (Node *)n; } - | COMMIT opt_trans + | COMMIT opt_transaction { TransactionStmt *n = makeNode(TransactionStmt); n->command = COMMIT; n->options = NIL; $$ = (Node *)n; } - | END_TRANS opt_trans + | END_TRANS opt_transaction { TransactionStmt *n = makeNode(TransactionStmt); n->command = COMMIT; n->options = NIL; $$ = (Node *)n; } - | ROLLBACK opt_trans + | ROLLBACK opt_transaction { TransactionStmt *n = makeNode(TransactionStmt); n->command = ROLLBACK; @@ -3544,16 +3535,46 @@ TransactionStmt: } ; -trans_options: ISOLATION LEVEL iso_level - { $$ = makeList1(makeStringConst($3, NULL)); } - | /* EMPTY */ { $$ = NIL; } - ; - -opt_trans: WORK {} +opt_transaction: WORK {} | TRANSACTION {} | /*EMPTY*/ {} ; +transaction_mode_list: + ISOLATION LEVEL iso_level + { $$ = makeList1(makeDefElem("transaction_isolation", + makeStringConst($3, NULL))); } + | transaction_access_mode + { $$ = makeList1(makeDefElem("transaction_read_only", + makeIntConst($1))); } + | ISOLATION LEVEL iso_level transaction_access_mode + { + $$ = makeList2(makeDefElem("transaction_isolation", + makeStringConst($3, NULL)), + makeDefElem("transaction_read_only", + makeIntConst($4))); + } + | transaction_access_mode ISOLATION LEVEL iso_level + { + $$ = makeList2(makeDefElem("transaction_read_only", + makeIntConst($1)), + makeDefElem("transaction_isolation", + makeStringConst($4, NULL))); + } + ; + +transaction_mode_list_or_empty: + transaction_mode_list + | /* EMPTY */ + { $$ = NIL; } + ; + +transaction_access_mode: + READ ONLY { $$ = TRUE; } + | READ WRITE { $$ = FALSE; } + ; + + /***************************************************************************** * * QUERY: diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index 68c02c5f62d..45da9ba1c1d 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.188 2003/01/06 00:31:44 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.189 2003/01/10 22:03:28 petere Exp $ * *------------------------------------------------------------------------- */ @@ -168,6 +168,66 @@ CheckOwnership(RangeVar *rel, bool noCatalogs) } +static void +check_xact_readonly(Node *parsetree) +{ + if (!XactReadOnly) + return; + + /* + * Note: Commands that need to do more complicated checking are + * handled elsewhere. + */ + + switch (nodeTag(parsetree)) + { + case T_AlterDatabaseSetStmt: + case T_AlterDomainStmt: + case T_AlterGroupStmt: + case T_AlterTableStmt: + case T_RenameStmt: + case T_AlterUserStmt: + case T_AlterUserSetStmt: + case T_CommentStmt: + case T_DefineStmt: + case T_CreateCastStmt: + case T_CreateConversionStmt: + case T_CreatedbStmt: + case T_CreateDomainStmt: + case T_CreateFunctionStmt: + case T_CreateGroupStmt: + case T_IndexStmt: + case T_CreatePLangStmt: + case T_CreateOpClassStmt: + case T_RuleStmt: + case T_CreateSchemaStmt: + case T_CreateSeqStmt: + case T_CreateStmt: + case T_CreateTrigStmt: + case T_CompositeTypeStmt: + case T_CreateUserStmt: + case T_ViewStmt: + case T_RemoveAggrStmt: + case T_DropCastStmt: + case T_DropStmt: + case T_DropdbStmt: + case T_RemoveFuncStmt: + case T_DropGroupStmt: + case T_DropPLangStmt: + case T_RemoveOperStmt: + case T_RemoveOpClassStmt: + case T_DropPropertyStmt: + case T_DropUserStmt: + case T_GrantStmt: + case T_TruncateStmt: + elog(ERROR, "transaction is read-only"); + break; + default: + /*nothing*/; + } +} + + /* * ProcessUtility * general utility function invoker @@ -187,6 +247,8 @@ ProcessUtility(Node *parsetree, CommandDest dest, char *completionTag) { + check_xact_readonly(parsetree); + if (completionTag) completionTag[0] = '\0'; @@ -214,16 +276,21 @@ ProcessUtility(Node *parsetree, { BeginTransactionBlock(); - /* - * Currently, the only option that can be set - * by START TRANSACTION is the isolation - * level. - */ if (stmt->options) { - SetPGVariable("TRANSACTION ISOLATION LEVEL", - stmt->options, - false); + List *head; + + foreach(head, stmt->options) + { + DefElem *item = (DefElem *) lfirst(head); + + if (strcmp(item->defname, "transaction_isolation")==0) + SetPGVariable("transaction_isolation", + makeList1(item->arg), false); + else if (strcmp(item->defname, "transaction_read_only")==0) + SetPGVariable("transaction_read_only", + makeList1(item->arg), false); + } } } break; @@ -765,7 +832,45 @@ ProcessUtility(Node *parsetree, { VariableSetStmt *n = (VariableSetStmt *) parsetree; - SetPGVariable(n->name, n->args, n->is_local); + /* + * Special cases for special SQL syntax that + * effectively sets more than one variable per + * statement. + */ + if (strcmp(n->name, "TRANSACTION")==0) + { + List *head; + + foreach(head, n->args) + { + DefElem *item = (DefElem *) lfirst(head); + + if (strcmp(item->defname, "transaction_isolation")==0) + SetPGVariable("transaction_isolation", + makeList1(item->arg), n->is_local); + else if (strcmp(item->defname, "transaction_read_only")==0) + SetPGVariable("transaction_read_only", + makeList1(item->arg), n->is_local); + } + } + else if (strcmp(n->name, "SESSION CHARACTERISTICS")==0) + { + List *head; + + foreach(head, n->args) + { + DefElem *item = (DefElem *) lfirst(head); + + if (strcmp(item->defname, "transaction_isolation")==0) + SetPGVariable("default_transaction_isolation", + makeList1(item->arg), n->is_local); + else if (strcmp(item->defname, "transaction_read_only")==0) + SetPGVariable("default_transaction_read_only", + makeList1(item->arg), n->is_local); + } + } + else + SetPGVariable(n->name, n->args, n->is_local); } break; diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index bd63313cd23..471d895ea72 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -5,7 +5,7 @@ * command, configuration file, and command line options. * See src/backend/utils/misc/README for more information. * - * $Header: /cvsroot/pgsql/src/backend/utils/misc/guc.c,v 1.109 2002/12/27 14:06:34 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/misc/guc.c,v 1.110 2003/01/10 22:03:29 petere Exp $ * * Copyright 2000 by PostgreSQL Global Development Group * Written by Peter Eisentraut <peter_e@gmx.net>. @@ -516,6 +516,14 @@ static struct config_bool {"autocommit", PGC_USERSET}, &autocommit, true, NULL, NULL }, + { + {"default_transaction_read_only", PGC_USERSET}, &DefaultXactReadOnly, + false, NULL, NULL + }, + { + {"transaction_read_only", PGC_USERSET, GUC_NO_RESET_ALL}, &XactReadOnly, + false, NULL, NULL + }, { {NULL, 0}, NULL, false, NULL, NULL @@ -841,7 +849,7 @@ static struct config_string }, { - {"TRANSACTION ISOLATION LEVEL", PGC_USERSET, GUC_NO_RESET_ALL}, + {"transaction_isolation", PGC_USERSET, GUC_NO_RESET_ALL}, &XactIsoLevel_string, NULL, assign_XactIsoLevel, show_XactIsoLevel }, @@ -1157,10 +1165,12 @@ InitializeGUCOptions(void) guc_string_workspace = NULL; /* - * Prevent any attempt to override TRANSACTION ISOLATION LEVEL from + * Prevent any attempt to override the transaction modes from * non-interactive sources. */ - SetConfigOption("TRANSACTION ISOLATION LEVEL", "default", + SetConfigOption("transaction_isolation", "default", + PGC_POSTMASTER, PGC_S_OVERRIDE); + SetConfigOption("transaction_read_only", "no", PGC_POSTMASTER, PGC_S_OVERRIDE); /* |