]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Read-only transactions, as defined in SQL.
authorPeter Eisentraut <peter_e@gmx.net>
Fri, 10 Jan 2003 22:03:30 +0000 (22:03 +0000)
committerPeter Eisentraut <peter_e@gmx.net>
Fri, 10 Jan 2003 22:03:30 +0000 (22:03 +0000)
16 files changed:
doc/src/sgml/features.sgml
doc/src/sgml/ref/set_transaction.sgml
doc/src/sgml/ref/start_transaction.sgml
doc/src/sgml/release.sgml
src/backend/access/transam/xact.c
src/backend/catalog/namespace.c
src/backend/commands/copy.c
src/backend/executor/execMain.c
src/backend/parser/gram.y
src/backend/tcop/utility.c
src/backend/utils/misc/guc.c
src/bin/psql/tab-complete.c
src/include/access/xact.h
src/include/catalog/namespace.h
src/test/regress/expected/transactions.out
src/test/regress/sql/transactions.sql

index fd01cd3cce62515d9fa2be8253eca695827afbae..9c7cc471935504028dea7898d33c4a20d80c7430 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/features.sgml,v 2.13 2003/01/10 11:02:41 petere Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/features.sgml,v 2.14 2003/01/10 22:03:26 petere Exp $
 -->
 
 <appendix id="features">
@@ -642,6 +642,12 @@ $Header: /cvsroot/pgsql/doc/src/sgml/features.sgml,v 2.13 2003/01/10 11:02:41 pe
         <entry>ROLLBACK statement</entry>
         <entry></entry>
        </row>
+       <row>
+        <entry>E152</entry>
+        <entry>Core</entry>
+        <entry>Basic SET TRANSACTION statement</entry>
+        <entry></entry>
+       </row>
        <row>
         <entry>E152-01</entry>
         <entry>Core</entry>
@@ -649,6 +655,13 @@ $Header: /cvsroot/pgsql/doc/src/sgml/features.sgml,v 2.13 2003/01/10 11:02:41 pe
          clause</entry> 
         <entry></entry>
        </row>
+       <row>
+        <entry>E152-02</entry>
+        <entry>Core</entry>
+        <entry>SET TRANSACTION statement: READ ONLY and READ WRITE
+         clauses</entry> 
+        <entry></entry>
+       </row>
        <row>
         <entry>E161</entry>
         <entry>Core</entry>
@@ -1598,19 +1611,6 @@ $Header: /cvsroot/pgsql/doc/src/sgml/features.sgml,v 2.13 2003/01/10 11:02:41 pe
         <entry>WITH HOLD cursors</entry>
         <entry>Cursor to stay open across transactions</entry>
        </row>
-       <row>
-        <entry>E152</entry>
-        <entry>Core</entry>
-        <entry>Basic SET TRANSACTION statement</entry>
-        <entry></entry>
-       </row>
-       <row>
-        <entry>E152-02</entry>
-        <entry>Core</entry>
-        <entry>SET TRANSACTION statement: READ ONLY and READ WRITE
-         clauses</entry> 
-        <entry>Syntax accepted; READ ONLY not supported</entry>
-       </row>
        <row>
         <entry>E153</entry>
         <entry>Core</entry>
index 315a999330f12478930a5e5931df1fdb5123c55e..f39e0eb7fe622af532ede620f11c57711b6fd971 100644 (file)
@@ -1,4 +1,4 @@
-<!-- $Header: /cvsroot/pgsql/doc/src/sgml/ref/set_transaction.sgml,v 1.9 2002/08/04 04:31:44 momjian Exp $ -->
+<!-- $Header: /cvsroot/pgsql/doc/src/sgml/ref/set_transaction.sgml,v 1.10 2003/01/10 22:03:27 petere Exp $ -->
 <refentry id="SQL-SET-TRANSACTION">
  <docinfo>
   <date>2000-11-24</date>
 
  <refsynopsisdiv>
   <synopsis>
-SET TRANSACTION ISOLATION LEVEL { READ COMMITTED | SERIALIZABLE }
-SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL
-    { READ COMMITTED | SERIALIZABLE }
+SET TRANSACTION
+    [ ISOLATION LEVEL { READ COMMITTED | SERIALIZABLE } ] [ READ WRITE | READ ONLY ]
+SET SESSION CHARACTERISTICS AS TRANSACTION
+    [ ISOLATION LEVEL { READ COMMITTED | SERIALIZABLE } ] [ READ WRITE | READ ONLY ]
   </synopsis>
  </refsynopsisdiv>
 
@@ -26,17 +27,19 @@ SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL
   <title>Description</title>
 
   <para>
-   This command sets the transaction isolation level.  The
-   <command>SET TRANSACTION</command> command sets the characteristics
-   for the current SQL-transaction. It has no effect on any subsequent
-   transactions.  This command cannot be used after the first query or data-modification
-   statement (<command>SELECT</command>, <command>INSERT</command>,
-   <command>DELETE</command>, <command>UPDATE</command>,
-   <command>FETCH</command>, <command>COPY</command>) of a transaction
-   has been executed.  <command>SET SESSION CHARACTERISTICS</command>
-   sets the default transaction isolation level for each transaction
-   for a session.  <command>SET TRANSACTION</command> can override it
-   for an individual transaction.
+   The <command>SET TRANSACTION</command> command sets the transaction
+   characteristics of the current SQL-transaction. It has no effect on
+   any subsequent transactions.  <command>SET SESSION
+   CHARACTERISTICS</command> sets the default transaction
+   characteristics for each transaction of a session.  <command>SET
+   TRANSACTION</command> can override it for an individual
+   transaction.
+  </para>
+
+  <para>
+   The available transaction characteristics are the transaction
+   isolation level and the transaction access mode (read/write or
+   read-only).
   </para>
 
   <para>
@@ -45,7 +48,7 @@ SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL
 
    <variablelist>
     <varlistentry>
-     <term>READ COMMITTED</term>
+     <term><literal>READ COMMITTED<literal></term>
      <listitem>
       <para>
        A statement can only see rows committed before it began. This
@@ -55,7 +58,7 @@ SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL
     </varlistentry>
 
     <varlistentry>
-     <term>SERIALIZABLE</term>
+     <term><literal>SERIALIZABLE</literal></term>
      <listitem>
       <para>
        The current transaction can only see rows committed before
@@ -72,6 +75,28 @@ SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL
      </listitem>
     </varlistentry>
    </variablelist>
+
+   The transaction isolation level cannot be set after the first query
+   or data-modification statement (<command>SELECT</command>,
+   <command>INSERT</command>, <command>DELETE</command>,
+   <command>UPDATE</command>, <command>FETCH</command>,
+   <command>COPY</command>) of a transaction has been executed.
+  </para>
+
+  <para>
+   The transaction access mode determines whether the transaction is
+   read/write or read-only.  Read/write is the default.  When a
+   transaction is read-only, the following SQL commands are
+   disallowed: <literal>INSERT</literal>, <literal>UPDATE</literal>,
+   <literal>DELETE</literal>, and <literal>COPY TO</literal> if the
+   table they would write to is not a temporary table; all
+   <literal>CREATE</literal>, <literal>ALTER</literal>, and
+   <literal>DROP</literal> commands; <literal>COMMENT</literal>,
+   <literal>GRANT</literal>, <literal>REVOKE</literal>,
+   <literal>TRUNCATE</literal>; and <literal>EXPLAIN ANALYZE</literal>
+   and <literal>EXECUTE</literal> if the command they would execute is
+   among those listed.  This is a high-level notion of read-only that
+   does not prevent writes to disk.
   </para>
  </refsect1>
 
@@ -97,7 +122,7 @@ SET default_transaction_isolation = '<replaceable>value</replaceable>'
    <title>SQL92, SQL99</title>
 
    <para>
-    <option>SERIALIZABLE</option> is the default level in
+    <option>SERIALIZABLE</option> is the default transaction isolation level in
     <acronym>SQL</acronym>.  <productname>PostgreSQL</productname> does
     not provide the isolation levels <option>READ UNCOMMITTED</option> 
     and <option>REPEATABLE READ</option>. Because of multiversion
@@ -107,11 +132,10 @@ SET default_transaction_isolation = '<replaceable>value</replaceable>'
    </para>
 
    <para>
-    In <acronym>SQL</acronym> there are two other transaction
-    characteristics that can be set with these commands: whether the
-    transaction is read-only and the size of the diagnostics area.
-    Neither of these concepts are supported in
-    <productname>PostgreSQL</productname>. 
+    In <acronym>SQL</acronym> there is one other transaction
+    characteristic that can be set with these commands: the size of
+    the diagnostics area.  This concept is not supported in
+    <productname>PostgreSQL</productname>.
    </para>
   </refsect2>
  </refsect1>
index fb8dd1319322a6e2074583b8b3af49c9b9bb265c..087a0df17746822fbbec80b847ef30e9b33a9dd4 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/ref/start_transaction.sgml,v 1.3 2002/08/30 22:45:25 tgl Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/ref/start_transaction.sgml,v 1.4 2003/01/10 22:03:27 petere Exp $
 PostgreSQL documentation
 -->
 
@@ -20,7 +20,7 @@ PostgreSQL documentation
 
  <refsynopsisdiv>
   <synopsis>
-START TRANSACTION [ ISOLATION LEVEL { READ COMMITTED | SERIALIZABLE } ]
+START TRANSACTION [ ISOLATION LEVEL { READ COMMITTED | SERIALIZABLE } ] [ READ WRITE | READ ONLY ]
   </synopsis>
 
   <refsect2 id="R2-SQL-START-TRANSACTION-1">
@@ -77,52 +77,23 @@ WARNING:  BEGIN: already a transaction in progress
   <title>Description</title>
 
   <para>
-   This command begins a new transaction. If the isolation level is
-   specified, the new transaction has that isolation level. In all other
-   respects, the behavior of this command is identical to the
-   <xref linkend="sql-begin" endterm="sql-begin-title"> command.
+   This command begins a new transaction. If the isolation level or
+   read/write mode is specified, the new transaction has those
+   characteristics, as if <xref linkend="sql-set-transaction"
+   endterm="sql-set-transaction-title"> was executed. In all other
+   respects, the behavior of this command is identical to the <xref
+   linkend="sql-begin" endterm="sql-begin-title"> command.
   </para>
 
  </refsect1>
 
- <refsect1>
-  <title>Notes</title>
-
-  <para>
-   The isolation level of a transaction can also be set with the <xref
-   linkend="sql-set-transaction" endterm="sql-set-transaction-title">
-   command. If no isolation level is specified, the default isolation
-   level is used.
-  </para>
- </refsect1>
-
  <refsect1 id="R1-SQL-START-TRANSACTION-3">
   <title>Compatibility</title>
 
-  <refsect2 id="R2-SQL-START-TRANSACTION-4">
-   <title>SQL99</title>
-
-   <para>
-    <option>SERIALIZABLE</option> is the default isolation level in
-    <acronym>SQL99</acronym>, but it is not the usual default in
-    <productname>PostgreSQL</productname>: the factory default setting
-    is READ COMMITTED.
-    <productname>PostgreSQL</productname> 
-    does not provide the isolation levels <option>READ UNCOMMITTED</option>
-    and <option>REPEATABLE READ</option>. Because of lack of predicate
-    locking, the <option>SERIALIZABLE</option> level is
-    not truly serializable. See the <citetitle>User's Guide</citetitle>
-    for details.
-   </para>
-
-   <para>
-    In <acronym>SQL99</acronym> this statement can specify two other
-    properties of the new transaction: whether the transaction is
-    read-only and the size of the diagnostics area. Neither of these
-    concepts are currently supported in
-    <productname>PostgreSQL</productname>.
-   </para>
-  </refsect2>
+  <para>
+   SQL99; but see also the compatibility section of <xref
+   linkend="sql-set-transaction" endterm="sql-set-transaction-title">.
+  </para>
  </refsect1>
 </refentry>
 
index 55007a7d627e8ac8c0ae0a18f440b4d24eb31de6..fd9a5e698f51a0bd7f4ad03e0ffa4565a972dff9 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/release.sgml,v 1.176 2003/01/08 00:22:26 tgl Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/release.sgml,v 1.177 2003/01/10 22:03:26 petere Exp $
 -->
 
 <appendix id="release">
@@ -36,6 +36,7 @@ System can use either hash- or sort-based strategy for grouped aggregation
 ON COMMIT options for temp tables
 extra_float_digits option allows pg_dump to dump float data accurately
 Long options for psql and pg_dump are now available on all platforms
+Read-only transactions
 ]]></literallayout>
 
  </sect1>
index 0f30e13c84858cff1a27096d189f15d32be9d340..7150569a228950506d57399664772bbca46e9a89 100644 (file)
@@ -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
index 0c571ca2a4028ef7dd7a53bcd6e47f1da1709864..d58313a2ee073036311e1a1583c57981ce911799 100644 (file)
@@ -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 $
  *
  *-------------------------------------------------------------------------
  */
@@ -278,6 +278,30 @@ RelnameGetRelid(const char *relname)
        return InvalidOid;
 }
 
+/*
+ * 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
index fd8c6b83a82d3aa37f63cff00f1350db54ef6803..91386eeb2ccfa154e25c954926a839ebcffab6f9 100644 (file)
@@ -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);
index 613a62c3c372a83421fe6c735689e87d2c9630b0..a1a8134a6a713002d548b960837a5654f213dc1c 100644 (file)
@@ -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);
@@ -201,6 +202,14 @@ ExecutorRun(QueryDesc *queryDesc,
        operation = queryDesc->operation;
        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
         */
@@ -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
  *
index fd33601cc08c98655ee9ebf352e1ee6726171d33..2703ae8a061399659acb07b16fe331dddc886f13 100644 (file)
@@ -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:
index 68c02c5f62d6a36aaf8d438a1736fcd8b7370ec7..45da9ba1c1d547836c5ecd98572493ccbb70c4f6 100644 (file)
@@ -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;
 
index bd63313cd23306647f99730013ab4299069ad5ab..471d895ea72dab384a46e2fbfe6f9d73d9b897b7 100644 (file)
@@ -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);
 
        /*
index 2e45278426c1211926a8fe4e12bfc46568998c77..e6972537536dc5fe3fbf86bacd1e1677333a5470 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright 2000-2002 by PostgreSQL Global Development Group
  *
- * $Header: /cvsroot/pgsql/src/bin/psql/tab-complete.c,v 1.70 2002/12/13 05:36:24 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/bin/psql/tab-complete.c,v 1.71 2003/01/10 22:03:30 petere Exp $
  */
 
 /*----------------------------------------------------------------------
@@ -212,13 +212,7 @@ psql_completion(char *text, int start, int end)
                "CONSTRAINTS",
                "NAMES",
                "SESSION",
-               "TRANSACTION ISOLATION LEVEL",
-               /* these are treated in backend/commands/variable.c */
-               "DateStyle",
-               "TimeZone",
-               "client_encoding",
-               "server_encoding",
-               "seed",
+               "TRANSACTION",
 
                /*
                 * the rest should match USERSET entries in
@@ -229,12 +223,14 @@ psql_completion(char *text, int start, int end)
                "autocommit",
                "checkpoint_segments",
                "checkpoint_timeout",
+               "client_encoding",
                "client_min_messages",
                "commit_delay",
                "commit_siblings",
                "cpu_index_tuple_cost",
                "cpu_operator_cost",
                "cpu_tuple_cost",
+               "DateStyle",
                "db_user_namespace",
                "deadlock_timeout",
                "debug_pretty_print",
@@ -269,7 +265,7 @@ psql_completion(char *text, int start, int end)
                "lc_messages",
                "lc_monetary",
                "lc_numeric",
-               "lc_timeC",
+               "lc_time",
                "log_connections",
                "log_duration",
                "log_min_error_statement",
@@ -294,6 +290,8 @@ psql_completion(char *text, int start, int end)
                "log_planner_stats",
                "log_source_port",
                "log_statement_stats",
+               "seed",
+               "server_encoding",
                "silent_mode",
                "sort_mem",
                "sql_inheritance",
@@ -309,6 +307,7 @@ psql_completion(char *text, int start, int end)
                "syslog_facility",
                "syslog_ident",
                "tcpip_socket",
+               "TimeZone",
                "trace_notify",
                "transform_null_equals",
                "unix_socket_directory",
@@ -817,10 +816,18 @@ psql_completion(char *text, int start, int end)
                         strcasecmp(prev_wd, "RESET") == 0 ||
                         strcasecmp(prev_wd, "SHOW") == 0)
                COMPLETE_WITH_LIST(pgsql_variables);
-       /* Complete "SET TRANSACTION ISOLOLATION LEVEL" */
-       else if (strcasecmp(prev2_wd, "SET") == 0 &&
-                        strcasecmp(prev_wd, "TRANSACTION") == 0)
-               COMPLETE_WITH_CONST("ISOLATION");
+       /* Complete "SET TRANSACTION" */
+       else if ((strcasecmp(prev2_wd, "SET") == 0 &&
+                         strcasecmp(prev_wd, "TRANSACTION") == 0) ||
+                        (strcasecmp(prev4_wd, "SESSION") == 0 &&
+                         strcasecmp(prev3_wd, "CHARACTERISTICS") == 0 &&
+                         strcasecmp(prev2_wd, "AS") == 0 &&
+                         strcasecmp(prev_wd, "TRANSACTION") == 0))
+       {
+               char       *my_list[] = {"ISOLATION", "READ", NULL};
+
+               COMPLETE_WITH_LIST(my_list);
+       }
        else if (strcasecmp(prev3_wd, "SET") == 0 &&
                         strcasecmp(prev2_wd, "TRANSACTION") == 0 &&
                         strcasecmp(prev_wd, "ISOLATION") == 0)
@@ -840,6 +847,15 @@ psql_completion(char *text, int start, int end)
                         strcasecmp(prev2_wd, "LEVEL") == 0 &&
                         strcasecmp(prev_wd, "READ") == 0)
                COMPLETE_WITH_CONST("COMMITTED");
+       else if ((strcasecmp(prev3_wd, "SET") == 0 ||
+                         strcasecmp(prev3_wd, "AS") == 0) &&
+                        strcasecmp(prev2_wd, "TRANSACTION") == 0 &&
+                        strcasecmp(prev_wd, "READ") == 0)
+       {
+               char       *my_list[] = {"ONLY", "WRITE", NULL};
+
+               COMPLETE_WITH_LIST(my_list);
+       }
        /* Complete SET CONSTRAINTS <foo> with DEFERRED|IMMEDIATE */
        else if (strcasecmp(prev3_wd, "SET") == 0 &&
                         strcasecmp(prev2_wd, "CONSTRAINTS") == 0)
@@ -853,7 +869,7 @@ psql_completion(char *text, int start, int end)
                         strcasecmp(prev_wd, "SESSION") == 0)
        {
                char       *my_list[] = {"AUTHORIZATION",
-                       "CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL",
+                       "CHARACTERISTICS AS TRANSACTION",
                NULL};
 
                COMPLETE_WITH_LIST(my_list);
index fa30c3303b1b66618211dbdc8590a9dd75ec2a49..b3938c869e1f759a5df90b3ce6f7e16f37593935 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: xact.h,v 1.48 2002/11/18 01:17:39 tgl Exp $
+ * $Id: xact.h,v 1.49 2003/01/10 22:03:30 petere Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -29,6 +29,9 @@
 
 extern int     DefaultXactIsoLevel;
 extern int     XactIsoLevel;
+extern bool    DefaultXactReadOnly;
+extern bool    XactReadOnly;
+
 
 /* ----------------
  *             transaction state structure
index 8bcd48767083edf20eb8a1903ca1456513a83d26..efd5ae3278671cd614923ec9c8491c29c6e2e5df 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: namespace.h,v 1.24 2003/01/07 20:56:07 tgl Exp $
+ * $Id: namespace.h,v 1.25 2003/01/10 22:03:30 petere Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -51,6 +51,7 @@ typedef struct _OpclassCandidateList
 extern Oid     RangeVarGetRelid(const RangeVar *relation, bool failOK);
 extern Oid     RangeVarGetCreationNamespace(const RangeVar *newRelation);
 extern Oid     RelnameGetRelid(const char *relname);
+extern Oid     RelidGetNamespaceId(Oid relid);
 extern bool RelationIsVisible(Oid relid);
 
 extern Oid     TypenameGetTypid(const char *typname);
index 46afaa3aad4403dcfec365e62e392910a00ad152..b72ca5f36e58be9656adbd2f371b8f9c005059e7 100644 (file)
@@ -40,3 +40,31 @@ SELECT * FROM aggtest;
   42 |  324.78
 (4 rows)
 
+-- Read-only tests
+CREATE TABLE writetest (a int);
+CREATE TEMPORARY TABLE temptest (a int);
+SET SESSION CHARACTERISTICS AS TRANSACTION READ ONLY;
+DROP TABLE writetest; -- fail
+ERROR:  transaction is read-only
+INSERT INTO writetest VALUES (1); -- fail
+ERROR:  transaction is read-only
+SELECT * FROM writetest; -- ok
+ a 
+---
+(0 rows)
+
+DELETE FROM temptest; -- ok
+UPDATE temptest SET a = 0 WHERE a = 1 AND writetest.a = temptest.a; -- ok
+PREPARE test AS UPDATE writetest SET a = 0; -- ok
+EXECUTE test; -- fail
+ERROR:  transaction is read-only
+SELECT * FROM writetest, temptest; -- ok
+ a | a 
+---+---
+(0 rows)
+
+CREATE TABLE test AS SELECT * FROM writetest; -- fail
+ERROR:  transaction is read-only
+START TRANSACTION READ WRITE;
+DROP TABLE writetest; -- ok
+COMMIT;
index 6a84c6365ceb19efd5c3a16c1df6101ead2a335e..10ef759998bc610b05d0f70a2fbb8c3bca53de73 100644 (file)
@@ -33,3 +33,24 @@ SELECT oid FROM pg_class WHERE relname = 'disappear';
 -- should have members again 
 SELECT * FROM aggtest;
 
+
+-- Read-only tests
+
+CREATE TABLE writetest (a int);
+CREATE TEMPORARY TABLE temptest (a int);
+
+SET SESSION CHARACTERISTICS AS TRANSACTION READ ONLY;
+
+DROP TABLE writetest; -- fail
+INSERT INTO writetest VALUES (1); -- fail
+SELECT * FROM writetest; -- ok
+DELETE FROM temptest; -- ok
+UPDATE temptest SET a = 0 WHERE a = 1 AND writetest.a = temptest.a; -- ok
+PREPARE test AS UPDATE writetest SET a = 0; -- ok
+EXECUTE test; -- fail
+SELECT * FROM writetest, temptest; -- ok
+CREATE TABLE test AS SELECT * FROM writetest; -- fail
+
+START TRANSACTION READ WRITE;
+DROP TABLE writetest; -- ok
+COMMIT;