Commit b65cd562 authored by Peter Eisentraut's avatar Peter Eisentraut

Read-only transactions, as defined in SQL.

parent b7ca9bdf
<!-- <!--
$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"> <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 ...@@ -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>ROLLBACK statement</entry>
<entry></entry> <entry></entry>
</row> </row>
<row>
<entry>E152</entry>
<entry>Core</entry>
<entry>Basic SET TRANSACTION statement</entry>
<entry></entry>
</row>
<row> <row>
<entry>E152-01</entry> <entry>E152-01</entry>
<entry>Core</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 ...@@ -649,6 +655,13 @@ $Header: /cvsroot/pgsql/doc/src/sgml/features.sgml,v 2.13 2003/01/10 11:02:41 pe
clause</entry> clause</entry>
<entry></entry> <entry></entry>
</row> </row>
<row>
<entry>E152-02</entry>
<entry>Core</entry>
<entry>SET TRANSACTION statement: READ ONLY and READ WRITE
clauses</entry>
<entry></entry>
</row>
<row> <row>
<entry>E161</entry> <entry>E161</entry>
<entry>Core</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 ...@@ -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>WITH HOLD cursors</entry>
<entry>Cursor to stay open across transactions</entry> <entry>Cursor to stay open across transactions</entry>
</row> </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> <row>
<entry>E153</entry> <entry>E153</entry>
<entry>Core</entry> <entry>Core</entry>
......
<!-- $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"> <refentry id="SQL-SET-TRANSACTION">
<docinfo> <docinfo>
<date>2000-11-24</date> <date>2000-11-24</date>
...@@ -16,9 +16,10 @@ ...@@ -16,9 +16,10 @@
<refsynopsisdiv> <refsynopsisdiv>
<synopsis> <synopsis>
SET TRANSACTION ISOLATION LEVEL { READ COMMITTED | SERIALIZABLE } SET TRANSACTION
SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL [ ISOLATION LEVEL { READ COMMITTED | SERIALIZABLE } ] [ READ WRITE | READ ONLY ]
{ READ COMMITTED | SERIALIZABLE } SET SESSION CHARACTERISTICS AS TRANSACTION
[ ISOLATION LEVEL { READ COMMITTED | SERIALIZABLE } ] [ READ WRITE | READ ONLY ]
</synopsis> </synopsis>
</refsynopsisdiv> </refsynopsisdiv>
...@@ -26,17 +27,19 @@ SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL ...@@ -26,17 +27,19 @@ SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL
<title>Description</title> <title>Description</title>
<para> <para>
This command sets the transaction isolation level. The The <command>SET TRANSACTION</command> command sets the transaction
<command>SET TRANSACTION</command> command sets the characteristics characteristics of the current SQL-transaction. It has no effect on
for the current SQL-transaction. It has no effect on any subsequent any subsequent transactions. <command>SET SESSION
transactions. This command cannot be used after the first query or data-modification CHARACTERISTICS</command> sets the default transaction
statement (<command>SELECT</command>, <command>INSERT</command>, characteristics for each transaction of a session. <command>SET
<command>DELETE</command>, <command>UPDATE</command>, TRANSACTION</command> can override it for an individual
<command>FETCH</command>, <command>COPY</command>) of a transaction transaction.
has been executed. <command>SET SESSION CHARACTERISTICS</command> </para>
sets the default transaction isolation level for each transaction
for a session. <command>SET TRANSACTION</command> can override it <para>
for an individual transaction. The available transaction characteristics are the transaction
isolation level and the transaction access mode (read/write or
read-only).
</para> </para>
<para> <para>
...@@ -45,7 +48,7 @@ SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL ...@@ -45,7 +48,7 @@ SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL
<variablelist> <variablelist>
<varlistentry> <varlistentry>
<term>READ COMMITTED</term> <term><literal>READ COMMITTED<literal></term>
<listitem> <listitem>
<para> <para>
A statement can only see rows committed before it began. This A statement can only see rows committed before it began. This
...@@ -55,7 +58,7 @@ SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL ...@@ -55,7 +58,7 @@ SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>
<term>SERIALIZABLE</term> <term><literal>SERIALIZABLE</literal></term>
<listitem> <listitem>
<para> <para>
The current transaction can only see rows committed before The current transaction can only see rows committed before
...@@ -72,6 +75,28 @@ SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL ...@@ -72,6 +75,28 @@ SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL
</listitem> </listitem>
</varlistentry> </varlistentry>
</variablelist> </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> </para>
</refsect1> </refsect1>
...@@ -97,7 +122,7 @@ SET default_transaction_isolation = '<replaceable>value</replaceable>' ...@@ -97,7 +122,7 @@ SET default_transaction_isolation = '<replaceable>value</replaceable>'
<title>SQL92, SQL99</title> <title>SQL92, SQL99</title>
<para> <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 <acronym>SQL</acronym>. <productname>PostgreSQL</productname> does
not provide the isolation levels <option>READ UNCOMMITTED</option> not provide the isolation levels <option>READ UNCOMMITTED</option>
and <option>REPEATABLE READ</option>. Because of multiversion and <option>REPEATABLE READ</option>. Because of multiversion
...@@ -107,10 +132,9 @@ SET default_transaction_isolation = '<replaceable>value</replaceable>' ...@@ -107,10 +132,9 @@ SET default_transaction_isolation = '<replaceable>value</replaceable>'
</para> </para>
<para> <para>
In <acronym>SQL</acronym> there are two other transaction In <acronym>SQL</acronym> there is one other transaction
characteristics that can be set with these commands: whether the characteristic that can be set with these commands: the size of
transaction is read-only and the size of the diagnostics area. the diagnostics area. This concept is not supported in
Neither of these concepts are supported in
<productname>PostgreSQL</productname>. <productname>PostgreSQL</productname>.
</para> </para>
</refsect2> </refsect2>
......
<!-- <!--
$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 PostgreSQL documentation
--> -->
...@@ -20,7 +20,7 @@ PostgreSQL documentation ...@@ -20,7 +20,7 @@ PostgreSQL documentation
<refsynopsisdiv> <refsynopsisdiv>
<synopsis> <synopsis>
START TRANSACTION [ ISOLATION LEVEL { READ COMMITTED | SERIALIZABLE } ] START TRANSACTION [ ISOLATION LEVEL { READ COMMITTED | SERIALIZABLE } ] [ READ WRITE | READ ONLY ]
</synopsis> </synopsis>
<refsect2 id="R2-SQL-START-TRANSACTION-1"> <refsect2 id="R2-SQL-START-TRANSACTION-1">
...@@ -77,52 +77,23 @@ WARNING: BEGIN: already a transaction in progress ...@@ -77,52 +77,23 @@ WARNING: BEGIN: already a transaction in progress
<title>Description</title> <title>Description</title>
<para> <para>
This command begins a new transaction. If the isolation level is This command begins a new transaction. If the isolation level or
specified, the new transaction has that isolation level. In all other read/write mode is specified, the new transaction has those
respects, the behavior of this command is identical to the characteristics, as if <xref linkend="sql-set-transaction"
<xref linkend="sql-begin" endterm="sql-begin-title"> command. 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> </para>
</refsect1> </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"> <refsect1 id="R1-SQL-START-TRANSACTION-3">
<title>Compatibility</title> <title>Compatibility</title>
<refsect2 id="R2-SQL-START-TRANSACTION-4">
<title>SQL99</title>
<para> <para>
<option>SERIALIZABLE</option> is the default isolation level in SQL99; but see also the compatibility section of <xref
<acronym>SQL99</acronym>, but it is not the usual default in linkend="sql-set-transaction" endterm="sql-set-transaction-title">.
<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>
<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>
</refsect1> </refsect1>
</refentry> </refentry>
......
<!-- <!--
$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"> <appendix id="release">
...@@ -36,6 +36,7 @@ System can use either hash- or sort-based strategy for grouped aggregation ...@@ -36,6 +36,7 @@ System can use either hash- or sort-based strategy for grouped aggregation
ON COMMIT options for temp tables ON COMMIT options for temp tables
extra_float_digits option allows pg_dump to dump float data accurately 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 Long options for psql and pg_dump are now available on all platforms
Read-only transactions
]]></literallayout> ]]></literallayout>
</sect1> </sect1>
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 * NOTES
* Transaction aborts can now occur two ways: * Transaction aborts can now occur two ways:
...@@ -208,6 +208,9 @@ TransactionState CurrentTransactionState = &CurrentTransactionStateData; ...@@ -208,6 +208,9 @@ TransactionState CurrentTransactionState = &CurrentTransactionStateData;
int DefaultXactIsoLevel = XACT_READ_COMMITTED; int DefaultXactIsoLevel = XACT_READ_COMMITTED;
int XactIsoLevel; int XactIsoLevel;
bool DefaultXactReadOnly = false;
bool XactReadOnly;
bool autocommit = true; bool autocommit = true;
int CommitDelay = 0; /* precommit delay in microseconds */ int CommitDelay = 0; /* precommit delay in microseconds */
...@@ -848,6 +851,7 @@ StartTransaction(void) ...@@ -848,6 +851,7 @@ StartTransaction(void)
FreeXactSnapshot(); FreeXactSnapshot();
XactIsoLevel = DefaultXactIsoLevel; XactIsoLevel = DefaultXactIsoLevel;
XactReadOnly = DefaultXactReadOnly;
/* /*
* Check the current transaction state. If the transaction system is * Check the current transaction state. If the transaction system is
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * 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) ...@@ -278,6 +278,30 @@ RelnameGetRelid(const char *relname)
return InvalidOid; 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 * RelationIsVisible
* Determine whether a relation (identified by OID) is visible in the * Determine whether a relation (identified by OID) is visible in the
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -348,6 +348,10 @@ DoCopy(const CopyStmt *stmt)
*/ */
rel = heap_openrv(relation, (is_from ? RowExclusiveLock : AccessShareLock)); 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. */ /* Check permissions. */
aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(), aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(),
required_access); required_access);
......
...@@ -26,7 +26,7 @@ ...@@ -26,7 +26,7 @@
* *
* *
* IDENTIFICATION * 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, ...@@ -85,6 +85,7 @@ static void ExecUpdate(TupleTableSlot *slot, ItemPointer tupleid,
static TupleTableSlot *EvalPlanQualNext(EState *estate); static TupleTableSlot *EvalPlanQualNext(EState *estate);
static void EndEvalPlanQual(EState *estate); static void EndEvalPlanQual(EState *estate);
static void ExecCheckRTEPerms(RangeTblEntry *rte, CmdType operation); static void ExecCheckRTEPerms(RangeTblEntry *rte, CmdType operation);
static void ExecCheckXactReadOnly(Query *parsetree, CmdType operation);
static void EvalPlanQualStart(evalPlanQual *epq, EState *estate, static void EvalPlanQualStart(evalPlanQual *epq, EState *estate,
evalPlanQual *priorepq); evalPlanQual *priorepq);
static void EvalPlanQualStop(evalPlanQual *epq); static void EvalPlanQualStop(evalPlanQual *epq);
...@@ -201,6 +202,14 @@ ExecutorRun(QueryDesc *queryDesc, ...@@ -201,6 +202,14 @@ ExecutorRun(QueryDesc *queryDesc,
operation = queryDesc->operation; operation = queryDesc->operation;
dest = queryDesc->dest; 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 * startup tuple receiver
*/ */
...@@ -385,6 +394,45 @@ ExecCheckRTEPerms(RangeTblEntry *rte, CmdType operation) ...@@ -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 * InitPlan
* *
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
* *
* *
* IDENTIFICATION * 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 * HISTORY
* AUTHOR DATE MAJOR EVENT * AUTHOR DATE MAJOR EVENT
...@@ -162,7 +162,7 @@ static void doNegateFloat(Value *v); ...@@ -162,7 +162,7 @@ static void doNegateFloat(Value *v);
%type <defelt> createdb_opt_item copy_opt_item %type <defelt> createdb_opt_item copy_opt_item
%type <ival> opt_lock lock_type cast_context %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 %type <list> user_list
...@@ -215,7 +215,8 @@ static void doNegateFloat(Value *v); ...@@ -215,7 +215,8 @@ static void doNegateFloat(Value *v);
target_list update_target_list insert_column_list target_list update_target_list insert_column_list
insert_target_list def_list opt_indirection insert_target_list def_list opt_indirection
group_clause TriggerFuncArgs select_limit 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 TableFuncElementList
prep_type_clause prep_type_list prep_type_clause prep_type_list
execute_param_clause execute_param_clause
...@@ -863,18 +864,18 @@ set_rest: ColId TO var_list_or_default ...@@ -863,18 +864,18 @@ set_rest: ColId TO var_list_or_default
n->args = makeList1($3); n->args = makeList1($3);
$$ = n; $$ = n;
} }
| TRANSACTION ISOLATION LEVEL iso_level opt_mode | TRANSACTION transaction_mode_list
{ {
VariableSetStmt *n = makeNode(VariableSetStmt); VariableSetStmt *n = makeNode(VariableSetStmt);
n->name = "TRANSACTION ISOLATION LEVEL"; n->name = "TRANSACTION";
n->args = makeList1(makeStringConst($4, NULL)); n->args = $2;
$$ = n; $$ = n;
} }
| SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL iso_level | SESSION CHARACTERISTICS AS TRANSACTION transaction_mode_list
{ {
VariableSetStmt *n = makeNode(VariableSetStmt); VariableSetStmt *n = makeNode(VariableSetStmt);
n->name = "default_transaction_isolation"; n->name = "SESSION CHARACTERISTICS";
n->args = makeList1(makeStringConst($7, NULL)); n->args = $5;
$$ = n; $$ = n;
} }
| NAMES opt_encoding | NAMES opt_encoding
...@@ -922,16 +923,6 @@ iso_level: READ COMMITTED { $$ = "read committed"; } ...@@ -922,16 +923,6 @@ iso_level: READ COMMITTED { $$ = "read committed"; }
| SERIALIZABLE { $$ = "serializable"; } | SERIALIZABLE { $$ = "serializable"; }
; ;
opt_mode: READ WRITE
{}
| READ ONLY
{
elog(ERROR, "SET TRANSACTION/READ ONLY not yet supported");
}
| /*EMPTY*/
{}
;
opt_boolean: opt_boolean:
TRUE_P { $$ = "true"; } TRUE_P { $$ = "true"; }
| FALSE_P { $$ = "false"; } | FALSE_P { $$ = "false"; }
...@@ -1020,7 +1011,7 @@ VariableShowStmt: ...@@ -1020,7 +1011,7 @@ VariableShowStmt:
| SHOW TRANSACTION ISOLATION LEVEL | SHOW TRANSACTION ISOLATION LEVEL
{ {
VariableShowStmt *n = makeNode(VariableShowStmt); VariableShowStmt *n = makeNode(VariableShowStmt);
n->name = "TRANSACTION ISOLATION LEVEL"; n->name = "transaction_isolation";
$$ = (Node *) n; $$ = (Node *) n;
} }
| SHOW SESSION AUTHORIZATION | SHOW SESSION AUTHORIZATION
...@@ -1053,7 +1044,7 @@ VariableResetStmt: ...@@ -1053,7 +1044,7 @@ VariableResetStmt:
| RESET TRANSACTION ISOLATION LEVEL | RESET TRANSACTION ISOLATION LEVEL
{ {
VariableResetStmt *n = makeNode(VariableResetStmt); VariableResetStmt *n = makeNode(VariableResetStmt);
n->name = "TRANSACTION ISOLATION LEVEL"; n->name = "transaction_isolation";
$$ = (Node *) n; $$ = (Node *) n;
} }
| RESET SESSION AUTHORIZATION | RESET SESSION AUTHORIZATION
...@@ -3500,42 +3491,42 @@ UnlistenStmt: ...@@ -3500,42 +3491,42 @@ UnlistenStmt:
*****************************************************************************/ *****************************************************************************/
TransactionStmt: TransactionStmt:
ABORT_TRANS opt_trans ABORT_TRANS opt_transaction
{ {
TransactionStmt *n = makeNode(TransactionStmt); TransactionStmt *n = makeNode(TransactionStmt);
n->command = ROLLBACK; n->command = ROLLBACK;
n->options = NIL; n->options = NIL;
$$ = (Node *)n; $$ = (Node *)n;
} }
| BEGIN_TRANS opt_trans | BEGIN_TRANS opt_transaction
{ {
TransactionStmt *n = makeNode(TransactionStmt); TransactionStmt *n = makeNode(TransactionStmt);
n->command = BEGIN_TRANS; n->command = BEGIN_TRANS;
n->options = NIL; n->options = NIL;
$$ = (Node *)n; $$ = (Node *)n;
} }
| START TRANSACTION trans_options | START TRANSACTION transaction_mode_list_or_empty
{ {
TransactionStmt *n = makeNode(TransactionStmt); TransactionStmt *n = makeNode(TransactionStmt);
n->command = START; n->command = START;
n->options = $3; n->options = $3;
$$ = (Node *)n; $$ = (Node *)n;
} }
| COMMIT opt_trans | COMMIT opt_transaction
{ {
TransactionStmt *n = makeNode(TransactionStmt); TransactionStmt *n = makeNode(TransactionStmt);
n->command = COMMIT; n->command = COMMIT;
n->options = NIL; n->options = NIL;
$$ = (Node *)n; $$ = (Node *)n;
} }
| END_TRANS opt_trans | END_TRANS opt_transaction
{ {
TransactionStmt *n = makeNode(TransactionStmt); TransactionStmt *n = makeNode(TransactionStmt);
n->command = COMMIT; n->command = COMMIT;
n->options = NIL; n->options = NIL;
$$ = (Node *)n; $$ = (Node *)n;
} }
| ROLLBACK opt_trans | ROLLBACK opt_transaction
{ {
TransactionStmt *n = makeNode(TransactionStmt); TransactionStmt *n = makeNode(TransactionStmt);
n->command = ROLLBACK; n->command = ROLLBACK;
...@@ -3544,16 +3535,46 @@ TransactionStmt: ...@@ -3544,16 +3535,46 @@ TransactionStmt:
} }
; ;
trans_options: ISOLATION LEVEL iso_level opt_transaction: WORK {}
{ $$ = makeList1(makeStringConst($3, NULL)); }
| /* EMPTY */ { $$ = NIL; }
;
opt_trans: WORK {}
| TRANSACTION {} | TRANSACTION {}
| /*EMPTY*/ {} | /*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: * QUERY:
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -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 * ProcessUtility
* general utility function invoker * general utility function invoker
...@@ -187,6 +247,8 @@ ProcessUtility(Node *parsetree, ...@@ -187,6 +247,8 @@ ProcessUtility(Node *parsetree,
CommandDest dest, CommandDest dest,
char *completionTag) char *completionTag)
{ {
check_xact_readonly(parsetree);
if (completionTag) if (completionTag)
completionTag[0] = '\0'; completionTag[0] = '\0';
...@@ -214,16 +276,21 @@ ProcessUtility(Node *parsetree, ...@@ -214,16 +276,21 @@ ProcessUtility(Node *parsetree,
{ {
BeginTransactionBlock(); BeginTransactionBlock();
/*
* Currently, the only option that can be set
* by START TRANSACTION is the isolation
* level.
*/
if (stmt->options) if (stmt->options)
{ {
SetPGVariable("TRANSACTION ISOLATION LEVEL", List *head;
stmt->options,
false); 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; break;
...@@ -765,6 +832,44 @@ ProcessUtility(Node *parsetree, ...@@ -765,6 +832,44 @@ ProcessUtility(Node *parsetree,
{ {
VariableSetStmt *n = (VariableSetStmt *) parsetree; VariableSetStmt *n = (VariableSetStmt *) parsetree;
/*
* 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); SetPGVariable(n->name, n->args, n->is_local);
} }
break; break;
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
* command, configuration file, and command line options. * command, configuration file, and command line options.
* See src/backend/utils/misc/README for more information. * 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 * Copyright 2000 by PostgreSQL Global Development Group
* Written by Peter Eisentraut <peter_e@gmx.net>. * Written by Peter Eisentraut <peter_e@gmx.net>.
...@@ -516,6 +516,14 @@ static struct config_bool ...@@ -516,6 +516,14 @@ static struct config_bool
{"autocommit", PGC_USERSET}, &autocommit, {"autocommit", PGC_USERSET}, &autocommit,
true, NULL, NULL 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 {NULL, 0}, NULL, false, NULL, NULL
...@@ -841,7 +849,7 @@ static struct config_string ...@@ -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, &XactIsoLevel_string,
NULL, assign_XactIsoLevel, show_XactIsoLevel NULL, assign_XactIsoLevel, show_XactIsoLevel
}, },
...@@ -1157,10 +1165,12 @@ InitializeGUCOptions(void) ...@@ -1157,10 +1165,12 @@ InitializeGUCOptions(void)
guc_string_workspace = NULL; 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. * 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); PGC_POSTMASTER, PGC_S_OVERRIDE);
/* /*
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* *
* Copyright 2000-2002 by PostgreSQL Global Development Group * 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) ...@@ -212,13 +212,7 @@ psql_completion(char *text, int start, int end)
"CONSTRAINTS", "CONSTRAINTS",
"NAMES", "NAMES",
"SESSION", "SESSION",
"TRANSACTION ISOLATION LEVEL", "TRANSACTION",
/* these are treated in backend/commands/variable.c */
"DateStyle",
"TimeZone",
"client_encoding",
"server_encoding",
"seed",
/* /*
* the rest should match USERSET entries in * the rest should match USERSET entries in
...@@ -229,12 +223,14 @@ psql_completion(char *text, int start, int end) ...@@ -229,12 +223,14 @@ psql_completion(char *text, int start, int end)
"autocommit", "autocommit",
"checkpoint_segments", "checkpoint_segments",
"checkpoint_timeout", "checkpoint_timeout",
"client_encoding",
"client_min_messages", "client_min_messages",
"commit_delay", "commit_delay",
"commit_siblings", "commit_siblings",
"cpu_index_tuple_cost", "cpu_index_tuple_cost",
"cpu_operator_cost", "cpu_operator_cost",
"cpu_tuple_cost", "cpu_tuple_cost",
"DateStyle",
"db_user_namespace", "db_user_namespace",
"deadlock_timeout", "deadlock_timeout",
"debug_pretty_print", "debug_pretty_print",
...@@ -269,7 +265,7 @@ psql_completion(char *text, int start, int end) ...@@ -269,7 +265,7 @@ psql_completion(char *text, int start, int end)
"lc_messages", "lc_messages",
"lc_monetary", "lc_monetary",
"lc_numeric", "lc_numeric",
"lc_timeC", "lc_time",
"log_connections", "log_connections",
"log_duration", "log_duration",
"log_min_error_statement", "log_min_error_statement",
...@@ -294,6 +290,8 @@ psql_completion(char *text, int start, int end) ...@@ -294,6 +290,8 @@ psql_completion(char *text, int start, int end)
"log_planner_stats", "log_planner_stats",
"log_source_port", "log_source_port",
"log_statement_stats", "log_statement_stats",
"seed",
"server_encoding",
"silent_mode", "silent_mode",
"sort_mem", "sort_mem",
"sql_inheritance", "sql_inheritance",
...@@ -309,6 +307,7 @@ psql_completion(char *text, int start, int end) ...@@ -309,6 +307,7 @@ psql_completion(char *text, int start, int end)
"syslog_facility", "syslog_facility",
"syslog_ident", "syslog_ident",
"tcpip_socket", "tcpip_socket",
"TimeZone",
"trace_notify", "trace_notify",
"transform_null_equals", "transform_null_equals",
"unix_socket_directory", "unix_socket_directory",
...@@ -817,10 +816,18 @@ psql_completion(char *text, int start, int end) ...@@ -817,10 +816,18 @@ psql_completion(char *text, int start, int end)
strcasecmp(prev_wd, "RESET") == 0 || strcasecmp(prev_wd, "RESET") == 0 ||
strcasecmp(prev_wd, "SHOW") == 0) strcasecmp(prev_wd, "SHOW") == 0)
COMPLETE_WITH_LIST(pgsql_variables); COMPLETE_WITH_LIST(pgsql_variables);
/* Complete "SET TRANSACTION ISOLOLATION LEVEL" */ /* Complete "SET TRANSACTION" */
else if (strcasecmp(prev2_wd, "SET") == 0 && else if ((strcasecmp(prev2_wd, "SET") == 0 &&
strcasecmp(prev_wd, "TRANSACTION") == 0) strcasecmp(prev_wd, "TRANSACTION") == 0) ||
COMPLETE_WITH_CONST("ISOLATION"); (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 && else if (strcasecmp(prev3_wd, "SET") == 0 &&
strcasecmp(prev2_wd, "TRANSACTION") == 0 && strcasecmp(prev2_wd, "TRANSACTION") == 0 &&
strcasecmp(prev_wd, "ISOLATION") == 0) strcasecmp(prev_wd, "ISOLATION") == 0)
...@@ -840,6 +847,15 @@ psql_completion(char *text, int start, int end) ...@@ -840,6 +847,15 @@ psql_completion(char *text, int start, int end)
strcasecmp(prev2_wd, "LEVEL") == 0 && strcasecmp(prev2_wd, "LEVEL") == 0 &&
strcasecmp(prev_wd, "READ") == 0) strcasecmp(prev_wd, "READ") == 0)
COMPLETE_WITH_CONST("COMMITTED"); 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 */ /* Complete SET CONSTRAINTS <foo> with DEFERRED|IMMEDIATE */
else if (strcasecmp(prev3_wd, "SET") == 0 && else if (strcasecmp(prev3_wd, "SET") == 0 &&
strcasecmp(prev2_wd, "CONSTRAINTS") == 0) strcasecmp(prev2_wd, "CONSTRAINTS") == 0)
...@@ -853,7 +869,7 @@ psql_completion(char *text, int start, int end) ...@@ -853,7 +869,7 @@ psql_completion(char *text, int start, int end)
strcasecmp(prev_wd, "SESSION") == 0) strcasecmp(prev_wd, "SESSION") == 0)
{ {
char *my_list[] = {"AUTHORIZATION", char *my_list[] = {"AUTHORIZATION",
"CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL", "CHARACTERISTICS AS TRANSACTION",
NULL}; NULL};
COMPLETE_WITH_LIST(my_list); COMPLETE_WITH_LIST(my_list);
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 @@ ...@@ -29,6 +29,9 @@
extern int DefaultXactIsoLevel; extern int DefaultXactIsoLevel;
extern int XactIsoLevel; extern int XactIsoLevel;
extern bool DefaultXactReadOnly;
extern bool XactReadOnly;
/* ---------------- /* ----------------
* transaction state structure * transaction state structure
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 ...@@ -51,6 +51,7 @@ typedef struct _OpclassCandidateList
extern Oid RangeVarGetRelid(const RangeVar *relation, bool failOK); extern Oid RangeVarGetRelid(const RangeVar *relation, bool failOK);
extern Oid RangeVarGetCreationNamespace(const RangeVar *newRelation); extern Oid RangeVarGetCreationNamespace(const RangeVar *newRelation);
extern Oid RelnameGetRelid(const char *relname); extern Oid RelnameGetRelid(const char *relname);
extern Oid RelidGetNamespaceId(Oid relid);
extern bool RelationIsVisible(Oid relid); extern bool RelationIsVisible(Oid relid);
extern Oid TypenameGetTypid(const char *typname); extern Oid TypenameGetTypid(const char *typname);
......
...@@ -40,3 +40,31 @@ SELECT * FROM aggtest; ...@@ -40,3 +40,31 @@ SELECT * FROM aggtest;
42 | 324.78 42 | 324.78
(4 rows) (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;
...@@ -33,3 +33,24 @@ SELECT oid FROM pg_class WHERE relname = 'disappear'; ...@@ -33,3 +33,24 @@ SELECT oid FROM pg_class WHERE relname = 'disappear';
-- should have members again -- should have members again
SELECT * FROM aggtest; 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;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment