1996-07-09 08:22:35 +02:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
1999-02-14 00:22:53 +01:00
|
|
|
* utility.c
|
1997-09-07 07:04:48 +02:00
|
|
|
* Contains functions which control the execution of the POSTGRES utility
|
|
|
|
* commands. At one time acted as an interface between the Lisp and C
|
|
|
|
* systems.
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
2012-01-02 00:01:58 +01:00
|
|
|
* Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
|
2000-01-26 06:58:53 +01:00
|
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
2010-09-20 22:08:53 +02:00
|
|
|
* src/backend/tcop/utility.c
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
#include "postgres.h"
|
1999-07-16 07:00:38 +02:00
|
|
|
|
2009-02-02 20:31:40 +01:00
|
|
|
#include "access/reloptions.h"
|
2005-06-18 00:32:51 +02:00
|
|
|
#include "access/twophase.h"
|
2006-07-13 18:49:20 +02:00
|
|
|
#include "access/xact.h"
|
1996-07-09 08:22:35 +02:00
|
|
|
#include "catalog/catalog.h"
|
2002-03-26 20:17:02 +01:00
|
|
|
#include "catalog/namespace.h"
|
2006-07-31 03:16:38 +02:00
|
|
|
#include "catalog/toasting.h"
|
2003-06-27 16:45:32 +02:00
|
|
|
#include "commands/alter.h"
|
1996-07-09 08:22:35 +02:00
|
|
|
#include "commands/async.h"
|
|
|
|
#include "commands/cluster.h"
|
1999-10-26 05:12:39 +02:00
|
|
|
#include "commands/comment.h"
|
2011-02-12 14:54:13 +01:00
|
|
|
#include "commands/collationcmds.h"
|
2002-07-11 09:39:28 +02:00
|
|
|
#include "commands/conversioncmds.h"
|
2006-07-11 20:26:11 +02:00
|
|
|
#include "commands/copy.h"
|
1997-11-24 06:32:56 +01:00
|
|
|
#include "commands/dbcommands.h"
|
1996-07-09 08:22:35 +02:00
|
|
|
#include "commands/defrem.h"
|
2007-04-26 18:13:15 +02:00
|
|
|
#include "commands/discard.h"
|
1996-07-09 08:22:35 +02:00
|
|
|
#include "commands/explain.h"
|
2011-02-08 22:08:41 +01:00
|
|
|
#include "commands/extension.h"
|
2002-04-15 07:22:04 +02:00
|
|
|
#include "commands/lockcmds.h"
|
|
|
|
#include "commands/portalcmds.h"
|
2002-08-27 06:55:12 +02:00
|
|
|
#include "commands/prepare.h"
|
1997-10-28 16:11:45 +01:00
|
|
|
#include "commands/proclang.h"
|
2002-04-15 07:22:04 +02:00
|
|
|
#include "commands/schemacmds.h"
|
2010-09-28 02:55:27 +02:00
|
|
|
#include "commands/seclabel.h"
|
1999-07-16 07:00:38 +02:00
|
|
|
#include "commands/sequence.h"
|
2002-04-15 07:22:04 +02:00
|
|
|
#include "commands/tablecmds.h"
|
2004-06-18 08:14:31 +02:00
|
|
|
#include "commands/tablespace.h"
|
1999-07-16 07:00:38 +02:00
|
|
|
#include "commands/trigger.h"
|
2002-12-06 06:00:34 +01:00
|
|
|
#include "commands/typecmds.h"
|
1999-12-20 02:19:58 +01:00
|
|
|
#include "commands/user.h"
|
1999-07-16 07:00:38 +02:00
|
|
|
#include "commands/vacuum.h"
|
|
|
|
#include "commands/view.h"
|
|
|
|
#include "miscadmin.h"
|
2007-06-24 00:12:52 +02:00
|
|
|
#include "parser/parse_utilcmd.h"
|
2004-05-30 00:48:23 +02:00
|
|
|
#include "postmaster/bgwriter.h"
|
1996-07-09 08:22:35 +02:00
|
|
|
#include "rewrite/rewriteDefine.h"
|
1999-07-16 07:00:38 +02:00
|
|
|
#include "rewrite/rewriteRemove.h"
|
2004-02-10 02:55:27 +01:00
|
|
|
#include "storage/fd.h"
|
2003-05-06 22:26:28 +02:00
|
|
|
#include "tcop/pquery.h"
|
1996-11-11 05:54:54 +01:00
|
|
|
#include "tcop/utility.h"
|
1996-07-09 08:22:35 +02:00
|
|
|
#include "utils/acl.h"
|
2002-05-17 03:19:19 +02:00
|
|
|
#include "utils/guc.h"
|
1996-07-09 08:22:35 +02:00
|
|
|
#include "utils/syscache.h"
|
2004-05-30 00:48:23 +02:00
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
|
2009-12-15 21:04:49 +01:00
|
|
|
/* Hook for plugins to get control in ProcessUtility() */
|
|
|
|
ProcessUtility_hook_type ProcessUtility_hook = NULL;
|
|
|
|
|
|
|
|
|
2003-07-22 21:00:12 +02:00
|
|
|
/*
|
|
|
|
* Verify user has ownership of specified relation, else ereport.
|
|
|
|
*
|
|
|
|
* If noCatalogs is true then we also deny access to system catalogs,
|
|
|
|
* except when allowSystemTableMods is true.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
CheckRelationOwnership(RangeVar *rel, bool noCatalogs)
|
2002-03-22 00:27:25 +01:00
|
|
|
{
|
2002-03-26 20:17:02 +01:00
|
|
|
Oid relOid;
|
2002-03-22 00:27:25 +01:00
|
|
|
HeapTuple tuple;
|
|
|
|
|
2011-07-09 04:19:30 +02:00
|
|
|
/*
|
|
|
|
* XXX: This is unsafe in the presence of concurrent DDL, since it is
|
|
|
|
* called before acquiring any lock on the target relation. However,
|
|
|
|
* locking the target relation (especially using something like
|
|
|
|
* AccessExclusiveLock) before verifying that the user has permissions
|
|
|
|
* is not appealing either.
|
|
|
|
*/
|
Improve table locking behavior in the face of current DDL.
In the previous coding, callers were faced with an awkward choice:
look up the name, do permissions checks, and then lock the table; or
look up the name, lock the table, and then do permissions checks.
The first choice was wrong because the results of the name lookup
and permissions checks might be out-of-date by the time the table
lock was acquired, while the second allowed a user with no privileges
to interfere with access to a table by users who do have privileges
(e.g. if a malicious backend queues up for an AccessExclusiveLock on
a table on which AccessShareLock is already held, further attempts
to access the table will be blocked until the AccessExclusiveLock
is obtained and the malicious backend's transaction rolls back).
To fix, allow callers of RangeVarGetRelid() to pass a callback which
gets executed after performing the name lookup but before acquiring
the relation lock. If the name lookup is retried (because
invalidation messages are received), the callback will be re-executed
as well, so we get the best of both worlds. RangeVarGetRelid() is
renamed to RangeVarGetRelidExtended(); callers not wishing to supply
a callback can continue to invoke it as RangeVarGetRelid(), which is
now a macro. Since the only one caller that uses nowait = true now
passes a callback anyway, the RangeVarGetRelid() macro defaults nowait
as well. The callback can also be used for supplemental locking - for
example, REINDEX INDEX needs to acquire the table lock before the index
lock to reduce deadlock possibilities.
There's a lot more work to be done here to fix all the cases where this
can be a problem, but this commit provides the general infrastructure
and fixes the following specific cases: REINDEX INDEX, REINDEX TABLE,
LOCK TABLE, and and DROP TABLE/INDEX/SEQUENCE/VIEW/FOREIGN TABLE.
Per discussion with Noah Misch and Alvaro Herrera.
2011-11-30 16:12:27 +01:00
|
|
|
relOid = RangeVarGetRelid(rel, NoLock, false);
|
2011-07-09 04:19:30 +02:00
|
|
|
|
2010-02-14 19:42:19 +01:00
|
|
|
tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
|
2003-08-04 02:43:34 +02:00
|
|
|
if (!HeapTupleIsValid(tuple)) /* should not happen */
|
2003-07-22 21:00:12 +02:00
|
|
|
elog(ERROR, "cache lookup failed for relation %u", relOid);
|
2002-03-22 00:27:25 +01:00
|
|
|
|
2002-03-26 20:17:02 +01:00
|
|
|
if (!pg_class_ownercheck(relOid, GetUserId()))
|
2003-08-01 02:15:26 +02:00
|
|
|
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
|
|
|
|
rel->relname);
|
2002-03-22 00:27:25 +01:00
|
|
|
|
|
|
|
if (noCatalogs)
|
|
|
|
{
|
2002-04-12 22:38:31 +02:00
|
|
|
if (!allowSystemTableMods &&
|
|
|
|
IsSystemClass((Form_pg_class) GETSTRUCT(tuple)))
|
2003-07-22 21:00:12 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
2005-10-15 04:49:52 +02:00
|
|
|
errmsg("permission denied: \"%s\" is a system catalog",
|
|
|
|
rel->relname)));
|
2002-03-22 00:27:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
ReleaseSysCache(tuple);
|
|
|
|
}
|
|
|
|
|
2000-10-23 01:32:48 +02:00
|
|
|
|
2004-09-13 22:10:13 +02:00
|
|
|
/*
|
2007-02-20 18:32:18 +01:00
|
|
|
* CommandIsReadOnly: is an executable query read-only?
|
2004-09-13 22:10:13 +02:00
|
|
|
*
|
|
|
|
* This is a much stricter test than we apply for XactReadOnly mode;
|
|
|
|
* the query must be *in truth* read-only, because the caller wishes
|
|
|
|
* not to do CommandCounterIncrement for it.
|
2007-02-20 18:32:18 +01:00
|
|
|
*
|
|
|
|
* Note: currently no need to support Query nodes here
|
2004-09-13 22:10:13 +02:00
|
|
|
*/
|
|
|
|
bool
|
2007-02-20 18:32:18 +01:00
|
|
|
CommandIsReadOnly(Node *parsetree)
|
2004-09-13 22:10:13 +02:00
|
|
|
{
|
2007-02-20 18:32:18 +01:00
|
|
|
if (IsA(parsetree, PlannedStmt))
|
2004-09-13 22:10:13 +02:00
|
|
|
{
|
2007-02-20 18:32:18 +01:00
|
|
|
PlannedStmt *stmt = (PlannedStmt *) parsetree;
|
|
|
|
|
|
|
|
switch (stmt->commandType)
|
|
|
|
{
|
|
|
|
case CMD_SELECT:
|
2007-04-28 00:05:49 +02:00
|
|
|
if (stmt->intoClause != NULL)
|
2007-11-15 22:14:46 +01:00
|
|
|
return false; /* SELECT INTO */
|
2007-02-20 18:32:18 +01:00
|
|
|
else if (stmt->rowMarks != NIL)
|
2007-11-15 22:14:46 +01:00
|
|
|
return false; /* SELECT FOR UPDATE/SHARE */
|
2011-02-26 00:56:23 +01:00
|
|
|
else if (stmt->hasModifyingCTE)
|
|
|
|
return false; /* data-modifying CTE */
|
2007-02-20 18:32:18 +01:00
|
|
|
else
|
|
|
|
return true;
|
|
|
|
case CMD_UPDATE:
|
|
|
|
case CMD_INSERT:
|
|
|
|
case CMD_DELETE:
|
|
|
|
return false;
|
|
|
|
default:
|
|
|
|
elog(WARNING, "unrecognized commandType: %d",
|
|
|
|
(int) stmt->commandType);
|
|
|
|
break;
|
|
|
|
}
|
2004-09-13 22:10:13 +02:00
|
|
|
}
|
2007-02-20 18:32:18 +01:00
|
|
|
/* For now, treat all utility commands as read/write */
|
2004-09-13 22:10:13 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* check_xact_readonly: is a utility command read-only?
|
|
|
|
*
|
|
|
|
* Here we use the loose rules of XactReadOnly mode: no permanent effects
|
|
|
|
* on the database are allowed.
|
|
|
|
*/
|
2003-01-10 23:03:30 +01:00
|
|
|
static void
|
|
|
|
check_xact_readonly(Node *parsetree)
|
|
|
|
{
|
|
|
|
if (!XactReadOnly)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* Note: Commands that need to do more complicated checking are handled
|
2009-06-11 16:49:15 +02:00
|
|
|
* elsewhere, in particular COPY and plannable statements do their own
|
2010-02-20 22:24:02 +01:00
|
|
|
* checking. However they should all call PreventCommandIfReadOnly to
|
|
|
|
* actually throw the error.
|
2003-01-10 23:03:30 +01:00
|
|
|
*/
|
|
|
|
|
|
|
|
switch (nodeTag(parsetree))
|
|
|
|
{
|
2005-07-31 19:19:22 +02:00
|
|
|
case T_AlterDatabaseStmt:
|
2003-01-10 23:03:30 +01:00
|
|
|
case T_AlterDatabaseSetStmt:
|
|
|
|
case T_AlterDomainStmt:
|
2005-03-14 01:19:37 +01:00
|
|
|
case T_AlterFunctionStmt:
|
2005-06-28 07:09:14 +02:00
|
|
|
case T_AlterRoleStmt:
|
|
|
|
case T_AlterRoleSetStmt:
|
2005-08-01 06:03:59 +02:00
|
|
|
case T_AlterObjectSchemaStmt:
|
2004-06-25 23:55:59 +02:00
|
|
|
case T_AlterOwnerStmt:
|
2003-03-20 08:02:11 +01:00
|
|
|
case T_AlterSeqStmt:
|
2003-01-10 23:03:30 +01:00
|
|
|
case T_AlterTableStmt:
|
2004-06-25 23:55:59 +02:00
|
|
|
case T_RenameStmt:
|
2003-01-10 23:03:30 +01:00
|
|
|
case T_CommentStmt:
|
|
|
|
case T_DefineStmt:
|
|
|
|
case T_CreateCastStmt:
|
|
|
|
case T_CreateConversionStmt:
|
|
|
|
case T_CreatedbStmt:
|
|
|
|
case T_CreateDomainStmt:
|
|
|
|
case T_CreateFunctionStmt:
|
2005-06-28 07:09:14 +02:00
|
|
|
case T_CreateRoleStmt:
|
2003-01-10 23:03:30 +01:00
|
|
|
case T_IndexStmt:
|
|
|
|
case T_CreatePLangStmt:
|
|
|
|
case T_CreateOpClassStmt:
|
2007-01-23 06:07:18 +01:00
|
|
|
case T_CreateOpFamilyStmt:
|
|
|
|
case T_AlterOpFamilyStmt:
|
2003-01-10 23:03:30 +01:00
|
|
|
case T_RuleStmt:
|
|
|
|
case T_CreateSchemaStmt:
|
|
|
|
case T_CreateSeqStmt:
|
|
|
|
case T_CreateStmt:
|
2004-06-18 08:14:31 +02:00
|
|
|
case T_CreateTableSpaceStmt:
|
2003-01-10 23:03:30 +01:00
|
|
|
case T_CreateTrigStmt:
|
|
|
|
case T_CompositeTypeStmt:
|
2007-04-02 05:49:42 +02:00
|
|
|
case T_CreateEnumStmt:
|
2011-11-03 12:16:28 +01:00
|
|
|
case T_CreateRangeStmt:
|
2010-10-25 05:04:37 +02:00
|
|
|
case T_AlterEnumStmt:
|
2003-01-10 23:03:30 +01:00
|
|
|
case T_ViewStmt:
|
|
|
|
case T_DropStmt:
|
|
|
|
case T_DropdbStmt:
|
2004-06-18 08:14:31 +02:00
|
|
|
case T_DropTableSpaceStmt:
|
2005-06-28 07:09:14 +02:00
|
|
|
case T_DropRoleStmt:
|
2003-01-10 23:03:30 +01:00
|
|
|
case T_GrantStmt:
|
2005-06-28 07:09:14 +02:00
|
|
|
case T_GrantRoleStmt:
|
2009-10-05 21:24:49 +02:00
|
|
|
case T_AlterDefaultPrivilegesStmt:
|
2003-01-10 23:03:30 +01:00
|
|
|
case T_TruncateStmt:
|
2005-11-21 13:49:33 +01:00
|
|
|
case T_DropOwnedStmt:
|
|
|
|
case T_ReassignOwnedStmt:
|
2007-08-21 03:11:32 +02:00
|
|
|
case T_AlterTSDictionaryStmt:
|
|
|
|
case T_AlterTSConfigurationStmt:
|
2011-02-08 22:08:41 +01:00
|
|
|
case T_CreateExtensionStmt:
|
2011-02-12 03:25:20 +01:00
|
|
|
case T_AlterExtensionStmt:
|
2011-02-10 23:36:44 +01:00
|
|
|
case T_AlterExtensionContentsStmt:
|
2008-12-19 17:25:19 +01:00
|
|
|
case T_CreateFdwStmt:
|
|
|
|
case T_AlterFdwStmt:
|
|
|
|
case T_CreateForeignServerStmt:
|
|
|
|
case T_AlterForeignServerStmt:
|
|
|
|
case T_CreateUserMappingStmt:
|
|
|
|
case T_AlterUserMappingStmt:
|
|
|
|
case T_DropUserMappingStmt:
|
2010-01-05 22:54:00 +01:00
|
|
|
case T_AlterTableSpaceOptionsStmt:
|
2011-01-02 05:48:11 +01:00
|
|
|
case T_CreateForeignTableStmt:
|
2010-09-28 02:55:27 +02:00
|
|
|
case T_SecLabelStmt:
|
2010-02-20 22:24:02 +01:00
|
|
|
PreventCommandIfReadOnly(CreateCommandTag(parsetree));
|
2003-01-10 23:03:30 +01:00
|
|
|
break;
|
|
|
|
default:
|
2003-07-22 21:00:12 +02:00
|
|
|
/* do nothing */
|
|
|
|
break;
|
2003-01-10 23:03:30 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-02-20 22:24:02 +01:00
|
|
|
/*
|
|
|
|
* PreventCommandIfReadOnly: throw error if XactReadOnly
|
|
|
|
*
|
|
|
|
* This is useful mainly to ensure consistency of the error message wording;
|
|
|
|
* most callers have checked XactReadOnly for themselves.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
PreventCommandIfReadOnly(const char *cmdname)
|
|
|
|
{
|
|
|
|
if (XactReadOnly)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION),
|
2010-02-26 03:01:40 +01:00
|
|
|
/* translator: %s is name of a SQL command, eg CREATE */
|
2010-02-20 22:24:02 +01:00
|
|
|
errmsg("cannot execute %s in a read-only transaction",
|
|
|
|
cmdname)));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* PreventCommandDuringRecovery: throw error if RecoveryInProgress
|
|
|
|
*
|
|
|
|
* The majority of operations that are unsafe in a Hot Standby slave
|
2010-02-26 03:01:40 +01:00
|
|
|
* will be rejected by XactReadOnly tests. However there are a few
|
2010-02-20 22:24:02 +01:00
|
|
|
* commands that are allowed in "read-only" xacts but cannot be allowed
|
|
|
|
* in Hot Standby mode. Those commands should call this function.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
PreventCommandDuringRecovery(const char *cmdname)
|
|
|
|
{
|
|
|
|
if (RecoveryInProgress())
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION),
|
2010-02-26 03:01:40 +01:00
|
|
|
/* translator: %s is name of a SQL command, eg CREATE */
|
2010-02-20 22:24:02 +01:00
|
|
|
errmsg("cannot execute %s during recovery",
|
|
|
|
cmdname)));
|
|
|
|
}
|
2003-01-10 23:03:30 +01:00
|
|
|
|
Prevent indirect security attacks via changing session-local state within
an allegedly immutable index function. It was previously recognized that
we had to prevent such a function from executing SET/RESET ROLE/SESSION
AUTHORIZATION, or it could trivially obtain the privileges of the session
user. However, since there is in general no privilege checking for changes
of session-local state, it is also possible for such a function to change
settings in a way that might subvert later operations in the same session.
Examples include changing search_path to cause an unexpected function to
be called, or replacing an existing prepared statement with another one
that will execute a function of the attacker's choosing.
The present patch secures VACUUM, ANALYZE, and CREATE INDEX/REINDEX against
these threats, which are the same places previously deemed to need protection
against the SET ROLE issue. GUC changes are still allowed, since there are
many useful cases for that, but we prevent security problems by forcing a
rollback of any GUC change after completing the operation. Other cases are
handled by throwing an error if any change is attempted; these include temp
table creation, closing a cursor, and creating or deleting a prepared
statement. (In 7.4, the infrastructure to roll back GUC changes doesn't
exist, so we settle for rejecting changes of "search_path" in these contexts.)
Original report and patch by Gurjeet Singh, additional analysis by
Tom Lane.
Security: CVE-2009-4136
2009-12-09 22:57:51 +01:00
|
|
|
/*
|
|
|
|
* CheckRestrictedOperation: throw error for hazardous command if we're
|
|
|
|
* inside a security restriction context.
|
|
|
|
*
|
|
|
|
* This is needed to protect session-local state for which there is not any
|
|
|
|
* better-defined protection mechanism, such as ownership.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
CheckRestrictedOperation(const char *cmdname)
|
|
|
|
{
|
|
|
|
if (InSecurityRestrictedOperation())
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
2010-02-26 03:01:40 +01:00
|
|
|
/* translator: %s is name of a SQL command, eg PREPARE */
|
|
|
|
errmsg("cannot execute %s within security-restricted operation",
|
|
|
|
cmdname)));
|
Prevent indirect security attacks via changing session-local state within
an allegedly immutable index function. It was previously recognized that
we had to prevent such a function from executing SET/RESET ROLE/SESSION
AUTHORIZATION, or it could trivially obtain the privileges of the session
user. However, since there is in general no privilege checking for changes
of session-local state, it is also possible for such a function to change
settings in a way that might subvert later operations in the same session.
Examples include changing search_path to cause an unexpected function to
be called, or replacing an existing prepared statement with another one
that will execute a function of the attacker's choosing.
The present patch secures VACUUM, ANALYZE, and CREATE INDEX/REINDEX against
these threats, which are the same places previously deemed to need protection
against the SET ROLE issue. GUC changes are still allowed, since there are
many useful cases for that, but we prevent security problems by forcing a
rollback of any GUC change after completing the operation. Other cases are
handled by throwing an error if any change is attempted; these include temp
table creation, closing a cursor, and creating or deleting a prepared
statement. (In 7.4, the infrastructure to roll back GUC changes doesn't
exist, so we settle for rejecting changes of "search_path" in these contexts.)
Original report and patch by Gurjeet Singh, additional analysis by
Tom Lane.
Security: CVE-2009-4136
2009-12-09 22:57:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-02-26 23:47:12 +01:00
|
|
|
/*
|
|
|
|
* ProcessUtility
|
1997-09-07 07:04:48 +02:00
|
|
|
* general utility function invoker
|
2002-02-26 23:47:12 +01:00
|
|
|
*
|
|
|
|
* parsetree: the parse tree for the utility statement
|
Adjust things so that the query_string of a cached plan and the sourceText of
a portal are never NULL, but reliably provide the source text of the query.
It turns out that there was only one place that was really taking a short-cut,
which was the 'EXECUTE' utility statement. That doesn't seem like a
sufficiently critical performance hotspot to justify not offering a guarantee
of validity of the portal source text. Fix it to copy the source text over
from the cached plan. Add Asserts in the places that set up cached plans and
portals to reject null source strings, and simplify a bunch of places that
formerly needed to guard against nulls.
There may be a few places that cons up statements for execution without
having any source text at all; I found one such in ConvertTriggerToFK().
It seems sufficient to inject a phony source string in such a case,
for instance
ProcessUtility((Node *) atstmt,
"(generated ALTER TABLE ADD FOREIGN KEY command)",
NULL, false, None_Receiver, NULL);
We should take a second look at the usage of debug_query_string,
particularly the recently added current_query() SQL function.
ITAGAKI Takahiro and Tom Lane
2008-07-18 22:26:06 +02:00
|
|
|
* queryString: original source text of command
|
2005-11-29 02:25:50 +01:00
|
|
|
* params: parameters to use during execution
|
2007-03-13 01:33:44 +01:00
|
|
|
* isTopLevel: true if executing a "top level" (interactively issued) command
|
2002-02-26 23:47:12 +01:00
|
|
|
* dest: where to send results
|
|
|
|
* completionTag: points to a buffer of size COMPLETION_TAG_BUFSIZE
|
|
|
|
* in which to store a command completion status string.
|
|
|
|
*
|
Adjust things so that the query_string of a cached plan and the sourceText of
a portal are never NULL, but reliably provide the source text of the query.
It turns out that there was only one place that was really taking a short-cut,
which was the 'EXECUTE' utility statement. That doesn't seem like a
sufficiently critical performance hotspot to justify not offering a guarantee
of validity of the portal source text. Fix it to copy the source text over
from the cached plan. Add Asserts in the places that set up cached plans and
portals to reject null source strings, and simplify a bunch of places that
formerly needed to guard against nulls.
There may be a few places that cons up statements for execution without
having any source text at all; I found one such in ConvertTriggerToFK().
It seems sufficient to inject a phony source string in such a case,
for instance
ProcessUtility((Node *) atstmt,
"(generated ALTER TABLE ADD FOREIGN KEY command)",
NULL, false, None_Receiver, NULL);
We should take a second look at the usage of debug_query_string,
particularly the recently added current_query() SQL function.
ITAGAKI Takahiro and Tom Lane
2008-07-18 22:26:06 +02:00
|
|
|
* Notes: as of PG 8.4, caller MUST supply a queryString; it is not
|
|
|
|
* allowed anymore to pass NULL. (If you really don't have source text,
|
|
|
|
* you can pass a constant string, perhaps "(query not available)".)
|
|
|
|
*
|
2004-09-13 22:10:13 +02:00
|
|
|
* completionTag is only set nonempty if we want to return a nondefault status.
|
2002-02-26 23:47:12 +01:00
|
|
|
*
|
|
|
|
* completionTag may be NULL if caller doesn't want a status string.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
|
|
|
void
|
1998-02-26 05:46:47 +01:00
|
|
|
ProcessUtility(Node *parsetree,
|
2007-03-13 01:33:44 +01:00
|
|
|
const char *queryString,
|
2004-08-02 03:30:51 +02:00
|
|
|
ParamListInfo params,
|
2007-03-13 01:33:44 +01:00
|
|
|
bool isTopLevel,
|
2003-05-06 22:26:28 +02:00
|
|
|
DestReceiver *dest,
|
2002-02-26 23:47:12 +01:00
|
|
|
char *completionTag)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2009-06-11 16:49:15 +02:00
|
|
|
Assert(queryString != NULL); /* required as of 8.4 */
|
Adjust things so that the query_string of a cached plan and the sourceText of
a portal are never NULL, but reliably provide the source text of the query.
It turns out that there was only one place that was really taking a short-cut,
which was the 'EXECUTE' utility statement. That doesn't seem like a
sufficiently critical performance hotspot to justify not offering a guarantee
of validity of the portal source text. Fix it to copy the source text over
from the cached plan. Add Asserts in the places that set up cached plans and
portals to reject null source strings, and simplify a bunch of places that
formerly needed to guard against nulls.
There may be a few places that cons up statements for execution without
having any source text at all; I found one such in ConvertTriggerToFK().
It seems sufficient to inject a phony source string in such a case,
for instance
ProcessUtility((Node *) atstmt,
"(generated ALTER TABLE ADD FOREIGN KEY command)",
NULL, false, None_Receiver, NULL);
We should take a second look at the usage of debug_query_string,
particularly the recently added current_query() SQL function.
ITAGAKI Takahiro and Tom Lane
2008-07-18 22:26:06 +02:00
|
|
|
|
2009-12-15 21:04:49 +01:00
|
|
|
/*
|
2010-02-26 03:01:40 +01:00
|
|
|
* We provide a function hook variable that lets loadable plugins get
|
|
|
|
* control when ProcessUtility is called. Such a plugin would normally
|
|
|
|
* call standard_ProcessUtility().
|
2009-12-15 21:04:49 +01:00
|
|
|
*/
|
|
|
|
if (ProcessUtility_hook)
|
|
|
|
(*ProcessUtility_hook) (parsetree, queryString, params,
|
|
|
|
isTopLevel, dest, completionTag);
|
|
|
|
else
|
|
|
|
standard_ProcessUtility(parsetree, queryString, params,
|
|
|
|
isTopLevel, dest, completionTag);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
standard_ProcessUtility(Node *parsetree,
|
|
|
|
const char *queryString,
|
|
|
|
ParamListInfo params,
|
|
|
|
bool isTopLevel,
|
|
|
|
DestReceiver *dest,
|
|
|
|
char *completionTag)
|
|
|
|
{
|
2003-01-10 23:03:30 +01:00
|
|
|
check_xact_readonly(parsetree);
|
|
|
|
|
2002-02-26 23:47:12 +01:00
|
|
|
if (completionTag)
|
|
|
|
completionTag[0] = '\0';
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
switch (nodeTag(parsetree))
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
/*
|
2003-02-19 04:59:02 +01:00
|
|
|
* ******************** transactions ********************
|
1997-09-08 04:41:22 +02:00
|
|
|
*/
|
|
|
|
case T_TransactionStmt:
|
|
|
|
{
|
|
|
|
TransactionStmt *stmt = (TransactionStmt *) parsetree;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2003-02-10 05:44:47 +01:00
|
|
|
switch (stmt->kind)
|
1997-09-08 04:41:22 +02:00
|
|
|
{
|
2004-08-29 07:07:03 +02:00
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* START TRANSACTION, as defined by SQL99: Identical
|
|
|
|
* to BEGIN. Same code for both.
|
2004-08-29 07:07:03 +02:00
|
|
|
*/
|
2003-02-10 05:44:47 +01:00
|
|
|
case TRANS_STMT_BEGIN:
|
|
|
|
case TRANS_STMT_START:
|
2002-08-04 06:31:44 +02:00
|
|
|
{
|
2004-08-12 23:00:34 +02:00
|
|
|
ListCell *lc;
|
2002-08-04 06:31:44 +02:00
|
|
|
|
2004-08-12 23:00:34 +02:00
|
|
|
BeginTransactionBlock();
|
|
|
|
foreach(lc, stmt->options)
|
2002-08-04 06:31:44 +02:00
|
|
|
{
|
2004-08-12 23:00:34 +02:00
|
|
|
DefElem *item = (DefElem *) lfirst(lc);
|
|
|
|
|
|
|
|
if (strcmp(item->defname, "transaction_isolation") == 0)
|
|
|
|
SetPGVariable("transaction_isolation",
|
|
|
|
list_make1(item->arg),
|
2005-06-18 00:32:51 +02:00
|
|
|
true);
|
2004-08-12 23:00:34 +02:00
|
|
|
else if (strcmp(item->defname, "transaction_read_only") == 0)
|
|
|
|
SetPGVariable("transaction_read_only",
|
|
|
|
list_make1(item->arg),
|
2005-06-18 00:32:51 +02:00
|
|
|
true);
|
Implement genuine serializable isolation level.
Until now, our Serializable mode has in fact been what's called Snapshot
Isolation, which allows some anomalies that could not occur in any
serialized ordering of the transactions. This patch fixes that using a
method called Serializable Snapshot Isolation, based on research papers by
Michael J. Cahill (see README-SSI for full references). In Serializable
Snapshot Isolation, transactions run like they do in Snapshot Isolation,
but a predicate lock manager observes the reads and writes performed and
aborts transactions if it detects that an anomaly might occur. This method
produces some false positives, ie. it sometimes aborts transactions even
though there is no anomaly.
To track reads we implement predicate locking, see storage/lmgr/predicate.c.
Whenever a tuple is read, a predicate lock is acquired on the tuple. Shared
memory is finite, so when a transaction takes many tuple-level locks on a
page, the locks are promoted to a single page-level lock, and further to a
single relation level lock if necessary. To lock key values with no matching
tuple, a sequential scan always takes a relation-level lock, and an index
scan acquires a page-level lock that covers the search key, whether or not
there are any matching keys at the moment.
A predicate lock doesn't conflict with any regular locks or with another
predicate locks in the normal sense. They're only used by the predicate lock
manager to detect the danger of anomalies. Only serializable transactions
participate in predicate locking, so there should be no extra overhead for
for other transactions.
Predicate locks can't be released at commit, but must be remembered until
all the transactions that overlapped with it have completed. That means that
we need to remember an unbounded amount of predicate locks, so we apply a
lossy but conservative method of tracking locks for committed transactions.
If we run short of shared memory, we overflow to a new "pg_serial" SLRU
pool.
We don't currently allow Serializable transactions in Hot Standby mode.
That would be hard, because even read-only transactions can cause anomalies
that wouldn't otherwise occur.
Serializable isolation mode now means the new fully serializable level.
Repeatable Read gives you the old Snapshot Isolation level that we have
always had.
Kevin Grittner and Dan Ports, reviewed by Jeff Davis, Heikki Linnakangas and
Anssi Kääriäinen
2011-02-07 22:46:51 +01:00
|
|
|
else if (strcmp(item->defname, "transaction_deferrable") == 0)
|
|
|
|
SetPGVariable("transaction_deferrable",
|
|
|
|
list_make1(item->arg),
|
|
|
|
true);
|
2002-08-04 06:31:44 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2003-02-10 05:44:47 +01:00
|
|
|
case TRANS_STMT_COMMIT:
|
2004-07-27 07:11:48 +02:00
|
|
|
if (!EndTransactionBlock())
|
|
|
|
{
|
|
|
|
/* report unsuccessful commit in completionTag */
|
|
|
|
if (completionTag)
|
|
|
|
strcpy(completionTag, "ROLLBACK");
|
|
|
|
}
|
1997-09-08 04:41:22 +02:00
|
|
|
break;
|
|
|
|
|
2005-06-18 00:32:51 +02:00
|
|
|
case TRANS_STMT_PREPARE:
|
2010-02-20 22:24:02 +01:00
|
|
|
PreventCommandDuringRecovery("PREPARE TRANSACTION");
|
2005-06-18 00:32:51 +02:00
|
|
|
if (!PrepareTransactionBlock(stmt->gid))
|
|
|
|
{
|
|
|
|
/* report unsuccessful commit in completionTag */
|
|
|
|
if (completionTag)
|
|
|
|
strcpy(completionTag, "ROLLBACK");
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TRANS_STMT_COMMIT_PREPARED:
|
2007-03-13 01:33:44 +01:00
|
|
|
PreventTransactionChain(isTopLevel, "COMMIT PREPARED");
|
2010-02-20 22:24:02 +01:00
|
|
|
PreventCommandDuringRecovery("COMMIT PREPARED");
|
2005-06-18 00:32:51 +02:00
|
|
|
FinishPreparedTransaction(stmt->gid, true);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TRANS_STMT_ROLLBACK_PREPARED:
|
2007-03-13 01:33:44 +01:00
|
|
|
PreventTransactionChain(isTopLevel, "ROLLBACK PREPARED");
|
2010-02-20 22:24:02 +01:00
|
|
|
PreventCommandDuringRecovery("ROLLBACK PREPARED");
|
2005-06-18 00:32:51 +02:00
|
|
|
FinishPreparedTransaction(stmt->gid, false);
|
|
|
|
break;
|
|
|
|
|
2003-02-10 05:44:47 +01:00
|
|
|
case TRANS_STMT_ROLLBACK:
|
1997-09-08 04:41:22 +02:00
|
|
|
UserAbortTransactionBlock();
|
|
|
|
break;
|
2004-07-27 07:11:48 +02:00
|
|
|
|
|
|
|
case TRANS_STMT_SAVEPOINT:
|
|
|
|
{
|
2004-08-29 07:07:03 +02:00
|
|
|
ListCell *cell;
|
|
|
|
char *name = NULL;
|
2004-07-27 07:11:48 +02:00
|
|
|
|
2007-03-13 01:33:44 +01:00
|
|
|
RequireTransactionChain(isTopLevel, "SAVEPOINT");
|
2004-07-27 07:11:48 +02:00
|
|
|
|
2004-08-29 07:07:03 +02:00
|
|
|
foreach(cell, stmt->options)
|
2004-07-27 07:11:48 +02:00
|
|
|
{
|
2004-08-29 07:07:03 +02:00
|
|
|
DefElem *elem = lfirst(cell);
|
|
|
|
|
2004-07-27 07:11:48 +02:00
|
|
|
if (strcmp(elem->defname, "savepoint_name") == 0)
|
|
|
|
name = strVal(elem->arg);
|
|
|
|
}
|
|
|
|
|
|
|
|
Assert(PointerIsValid(name));
|
|
|
|
|
|
|
|
DefineSavepoint(name);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TRANS_STMT_RELEASE:
|
2007-03-13 01:33:44 +01:00
|
|
|
RequireTransactionChain(isTopLevel, "RELEASE SAVEPOINT");
|
2004-07-27 07:11:48 +02:00
|
|
|
ReleaseSavepoint(stmt->options);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TRANS_STMT_ROLLBACK_TO:
|
2007-03-13 01:33:44 +01:00
|
|
|
RequireTransactionChain(isTopLevel, "ROLLBACK TO SAVEPOINT");
|
2004-07-27 07:11:48 +02:00
|
|
|
RollbackToSavepoint(stmt->options);
|
2004-08-29 07:07:03 +02:00
|
|
|
|
2004-07-27 07:11:48 +02:00
|
|
|
/*
|
2004-08-29 07:07:03 +02:00
|
|
|
* CommitTransactionCommand is in charge of
|
|
|
|
* re-defining the savepoint again
|
2004-07-27 07:11:48 +02:00
|
|
|
*/
|
|
|
|
break;
|
1997-09-08 04:41:22 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
/*
|
2003-03-10 04:53:52 +01:00
|
|
|
* Portal (cursor) manipulation
|
2007-04-28 00:05:49 +02:00
|
|
|
*
|
|
|
|
* Note: DECLARE CURSOR is processed mostly as a SELECT, and
|
|
|
|
* therefore what we will get here is a PlannedStmt not a bare
|
|
|
|
* DeclareCursorStmt.
|
1997-09-08 04:41:22 +02:00
|
|
|
*/
|
2007-04-28 00:05:49 +02:00
|
|
|
case T_PlannedStmt:
|
|
|
|
{
|
|
|
|
PlannedStmt *stmt = (PlannedStmt *) parsetree;
|
|
|
|
|
|
|
|
if (stmt->utilityStmt == NULL ||
|
|
|
|
!IsA(stmt->utilityStmt, DeclareCursorStmt))
|
|
|
|
elog(ERROR, "non-DECLARE CURSOR PlannedStmt passed to ProcessUtility");
|
|
|
|
PerformCursorOpen(stmt, params, queryString, isTopLevel);
|
|
|
|
}
|
2003-03-10 04:53:52 +01:00
|
|
|
break;
|
|
|
|
|
1997-09-08 04:41:22 +02:00
|
|
|
case T_ClosePortalStmt:
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
ClosePortalStmt *stmt = (ClosePortalStmt *) parsetree;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
Prevent indirect security attacks via changing session-local state within
an allegedly immutable index function. It was previously recognized that
we had to prevent such a function from executing SET/RESET ROLE/SESSION
AUTHORIZATION, or it could trivially obtain the privileges of the session
user. However, since there is in general no privilege checking for changes
of session-local state, it is also possible for such a function to change
settings in a way that might subvert later operations in the same session.
Examples include changing search_path to cause an unexpected function to
be called, or replacing an existing prepared statement with another one
that will execute a function of the attacker's choosing.
The present patch secures VACUUM, ANALYZE, and CREATE INDEX/REINDEX against
these threats, which are the same places previously deemed to need protection
against the SET ROLE issue. GUC changes are still allowed, since there are
many useful cases for that, but we prevent security problems by forcing a
rollback of any GUC change after completing the operation. Other cases are
handled by throwing an error if any change is attempted; these include temp
table creation, closing a cursor, and creating or deleting a prepared
statement. (In 7.4, the infrastructure to roll back GUC changes doesn't
exist, so we settle for rejecting changes of "search_path" in these contexts.)
Original report and patch by Gurjeet Singh, additional analysis by
Tom Lane.
Security: CVE-2009-4136
2009-12-09 22:57:51 +01:00
|
|
|
CheckRestrictedOperation("CLOSE");
|
2003-03-10 04:53:52 +01:00
|
|
|
PerformPortalClose(stmt->portalname);
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
1997-09-08 04:41:22 +02:00
|
|
|
break;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1997-09-08 04:41:22 +02:00
|
|
|
case T_FetchStmt:
|
2003-03-11 20:40:24 +01:00
|
|
|
PerformPortalFetch((FetchStmt *) parsetree, dest,
|
|
|
|
completionTag);
|
1997-09-08 04:41:22 +02:00
|
|
|
break;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/*
|
2002-03-19 03:58:20 +01:00
|
|
|
* relation and attribute manipulation
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
2002-03-21 17:02:16 +01:00
|
|
|
case T_CreateSchemaStmt:
|
2007-03-13 01:33:44 +01:00
|
|
|
CreateSchemaCommand((CreateSchemaStmt *) parsetree,
|
|
|
|
queryString);
|
2002-03-21 17:02:16 +01:00
|
|
|
break;
|
|
|
|
|
1997-09-08 04:41:22 +02:00
|
|
|
case T_CreateStmt:
|
2011-01-02 05:48:11 +01:00
|
|
|
case T_CreateForeignTableStmt:
|
2002-03-26 20:17:02 +01:00
|
|
|
{
|
2007-11-15 22:14:46 +01:00
|
|
|
List *stmts;
|
|
|
|
ListCell *l;
|
2002-03-26 20:17:02 +01:00
|
|
|
Oid relOid;
|
2000-07-05 14:45:31 +02:00
|
|
|
|
2007-06-24 00:12:52 +02:00
|
|
|
/* Run parse analysis ... */
|
|
|
|
stmts = transformCreateStmt((CreateStmt *) parsetree,
|
|
|
|
queryString);
|
2002-03-26 20:17:02 +01:00
|
|
|
|
2007-06-24 00:12:52 +02:00
|
|
|
/* ... and do it */
|
|
|
|
foreach(l, stmts)
|
|
|
|
{
|
2007-11-15 22:14:46 +01:00
|
|
|
Node *stmt = (Node *) lfirst(l);
|
2007-06-24 00:12:52 +02:00
|
|
|
|
|
|
|
if (IsA(stmt, CreateStmt))
|
|
|
|
{
|
2009-06-11 16:49:15 +02:00
|
|
|
Datum toast_options;
|
|
|
|
static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
|
2009-02-02 20:31:40 +01:00
|
|
|
|
2007-06-24 00:12:52 +02:00
|
|
|
/* Create the table itself */
|
|
|
|
relOid = DefineRelation((CreateStmt *) stmt,
|
2010-08-18 20:35:21 +02:00
|
|
|
RELKIND_RELATION,
|
|
|
|
InvalidOid);
|
2007-06-24 00:12:52 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Let AlterTableCreateToastTable decide if this one
|
|
|
|
* needs a secondary relation too.
|
|
|
|
*/
|
|
|
|
CommandCounterIncrement();
|
2009-02-02 20:31:40 +01:00
|
|
|
|
|
|
|
/* parse and validate reloptions for the toast table */
|
|
|
|
toast_options = transformRelOptions((Datum) 0,
|
2009-06-11 16:49:15 +02:00
|
|
|
((CreateStmt *) stmt)->options,
|
2009-02-02 20:31:40 +01:00
|
|
|
"toast",
|
|
|
|
validnsps,
|
|
|
|
true, false);
|
2010-01-06 04:04:03 +01:00
|
|
|
(void) heap_reloptions(RELKIND_TOASTVALUE, toast_options,
|
2009-02-02 20:31:40 +01:00
|
|
|
true);
|
|
|
|
|
2010-01-06 04:04:03 +01:00
|
|
|
AlterTableCreateToastTable(relOid, toast_options);
|
2007-06-24 00:12:52 +02:00
|
|
|
}
|
2011-01-02 05:48:11 +01:00
|
|
|
else if (IsA(stmt, CreateForeignTableStmt))
|
|
|
|
{
|
|
|
|
/* Create the table itself */
|
|
|
|
relOid = DefineRelation((CreateStmt *) stmt,
|
|
|
|
RELKIND_FOREIGN_TABLE,
|
|
|
|
InvalidOid);
|
2011-04-25 22:55:11 +02:00
|
|
|
CreateForeignTable((CreateForeignTableStmt *) stmt,
|
|
|
|
relOid);
|
2011-01-02 05:48:11 +01:00
|
|
|
}
|
2007-06-24 00:12:52 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Recurse for anything else */
|
|
|
|
ProcessUtility(stmt,
|
|
|
|
queryString,
|
|
|
|
params,
|
|
|
|
false,
|
|
|
|
None_Receiver,
|
|
|
|
NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Need CCI between commands */
|
|
|
|
if (lnext(l) != NULL)
|
|
|
|
CommandCounterIncrement();
|
|
|
|
}
|
2002-03-26 20:17:02 +01:00
|
|
|
}
|
1997-09-08 04:41:22 +02:00
|
|
|
break;
|
|
|
|
|
2004-06-18 08:14:31 +02:00
|
|
|
case T_CreateTableSpaceStmt:
|
2007-03-13 01:33:44 +01:00
|
|
|
PreventTransactionChain(isTopLevel, "CREATE TABLESPACE");
|
2004-06-18 08:14:31 +02:00
|
|
|
CreateTableSpace((CreateTableSpaceStmt *) parsetree);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_DropTableSpaceStmt:
|
2007-03-13 01:33:44 +01:00
|
|
|
PreventTransactionChain(isTopLevel, "DROP TABLESPACE");
|
2004-06-18 08:14:31 +02:00
|
|
|
DropTableSpace((DropTableSpaceStmt *) parsetree);
|
|
|
|
break;
|
|
|
|
|
2010-01-05 22:54:00 +01:00
|
|
|
case T_AlterTableSpaceOptionsStmt:
|
|
|
|
AlterTableSpaceOptions((AlterTableSpaceOptionsStmt *) parsetree);
|
|
|
|
break;
|
|
|
|
|
2011-02-08 22:08:41 +01:00
|
|
|
case T_CreateExtensionStmt:
|
|
|
|
CreateExtension((CreateExtensionStmt *) parsetree);
|
|
|
|
break;
|
|
|
|
|
2011-02-12 03:25:20 +01:00
|
|
|
case T_AlterExtensionStmt:
|
|
|
|
ExecAlterExtensionStmt((AlterExtensionStmt *) parsetree);
|
|
|
|
break;
|
|
|
|
|
2011-02-10 23:36:44 +01:00
|
|
|
case T_AlterExtensionContentsStmt:
|
|
|
|
ExecAlterExtensionContentsStmt((AlterExtensionContentsStmt *) parsetree);
|
2011-02-09 17:55:32 +01:00
|
|
|
break;
|
|
|
|
|
2008-12-19 17:25:19 +01:00
|
|
|
case T_CreateFdwStmt:
|
|
|
|
CreateForeignDataWrapper((CreateFdwStmt *) parsetree);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_AlterFdwStmt:
|
|
|
|
AlterForeignDataWrapper((AlterFdwStmt *) parsetree);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_CreateForeignServerStmt:
|
|
|
|
CreateForeignServer((CreateForeignServerStmt *) parsetree);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_AlterForeignServerStmt:
|
|
|
|
AlterForeignServer((AlterForeignServerStmt *) parsetree);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_CreateUserMappingStmt:
|
|
|
|
CreateUserMapping((CreateUserMappingStmt *) parsetree);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_AlterUserMappingStmt:
|
|
|
|
AlterUserMapping((AlterUserMappingStmt *) parsetree);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_DropUserMappingStmt:
|
|
|
|
RemoveUserMapping((DropUserMappingStmt *) parsetree);
|
|
|
|
break;
|
|
|
|
|
1999-12-10 04:56:14 +01:00
|
|
|
case T_DropStmt:
|
2011-10-20 05:25:20 +02:00
|
|
|
switch (((DropStmt *) parsetree)->removeType)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2011-10-20 05:25:20 +02:00
|
|
|
case OBJECT_TABLE:
|
|
|
|
case OBJECT_SEQUENCE:
|
|
|
|
case OBJECT_VIEW:
|
|
|
|
case OBJECT_INDEX:
|
|
|
|
case OBJECT_FOREIGN_TABLE:
|
|
|
|
RemoveRelations((DropStmt *) parsetree);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
RemoveObjects((DropStmt *) parsetree);
|
|
|
|
break;
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
1997-09-08 04:41:22 +02:00
|
|
|
break;
|
|
|
|
|
1999-09-30 03:12:36 +02:00
|
|
|
case T_TruncateStmt:
|
2006-03-03 04:30:54 +01:00
|
|
|
ExecuteTruncate((TruncateStmt *) parsetree);
|
1999-09-23 19:03:39 +02:00
|
|
|
break;
|
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
case T_CommentStmt:
|
2002-04-09 22:35:55 +02:00
|
|
|
CommentObject((CommentStmt *) parsetree);
|
2000-04-12 19:17:23 +02:00
|
|
|
break;
|
|
|
|
|
2010-09-28 02:55:27 +02:00
|
|
|
case T_SecLabelStmt:
|
|
|
|
ExecSecLabelStmt((SecLabelStmt *) parsetree);
|
|
|
|
break;
|
|
|
|
|
1997-09-08 04:41:22 +02:00
|
|
|
case T_CopyStmt:
|
2006-03-03 20:54:10 +01:00
|
|
|
{
|
2007-03-13 01:33:44 +01:00
|
|
|
uint64 processed;
|
2006-03-03 20:54:10 +01:00
|
|
|
|
2007-03-13 01:33:44 +01:00
|
|
|
processed = DoCopy((CopyStmt *) parsetree, queryString);
|
2006-03-03 20:54:10 +01:00
|
|
|
if (completionTag)
|
|
|
|
snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
|
|
|
|
"COPY " UINT64_FORMAT, processed);
|
|
|
|
}
|
1997-09-08 04:41:22 +02:00
|
|
|
break;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2002-08-27 06:55:12 +02:00
|
|
|
case T_PrepareStmt:
|
Prevent indirect security attacks via changing session-local state within
an allegedly immutable index function. It was previously recognized that
we had to prevent such a function from executing SET/RESET ROLE/SESSION
AUTHORIZATION, or it could trivially obtain the privileges of the session
user. However, since there is in general no privilege checking for changes
of session-local state, it is also possible for such a function to change
settings in a way that might subvert later operations in the same session.
Examples include changing search_path to cause an unexpected function to
be called, or replacing an existing prepared statement with another one
that will execute a function of the attacker's choosing.
The present patch secures VACUUM, ANALYZE, and CREATE INDEX/REINDEX against
these threats, which are the same places previously deemed to need protection
against the SET ROLE issue. GUC changes are still allowed, since there are
many useful cases for that, but we prevent security problems by forcing a
rollback of any GUC change after completing the operation. Other cases are
handled by throwing an error if any change is attempted; these include temp
table creation, closing a cursor, and creating or deleting a prepared
statement. (In 7.4, the infrastructure to roll back GUC changes doesn't
exist, so we settle for rejecting changes of "search_path" in these contexts.)
Original report and patch by Gurjeet Singh, additional analysis by
Tom Lane.
Security: CVE-2009-4136
2009-12-09 22:57:51 +01:00
|
|
|
CheckRestrictedOperation("PREPARE");
|
2007-03-13 01:33:44 +01:00
|
|
|
PrepareQuery((PrepareStmt *) parsetree, queryString);
|
2002-08-27 06:55:12 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case T_ExecuteStmt:
|
2007-03-13 01:33:44 +01:00
|
|
|
ExecuteQuery((ExecuteStmt *) parsetree, queryString, params,
|
2005-11-29 02:25:50 +01:00
|
|
|
dest, completionTag);
|
2002-08-27 06:55:12 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case T_DeallocateStmt:
|
Prevent indirect security attacks via changing session-local state within
an allegedly immutable index function. It was previously recognized that
we had to prevent such a function from executing SET/RESET ROLE/SESSION
AUTHORIZATION, or it could trivially obtain the privileges of the session
user. However, since there is in general no privilege checking for changes
of session-local state, it is also possible for such a function to change
settings in a way that might subvert later operations in the same session.
Examples include changing search_path to cause an unexpected function to
be called, or replacing an existing prepared statement with another one
that will execute a function of the attacker's choosing.
The present patch secures VACUUM, ANALYZE, and CREATE INDEX/REINDEX against
these threats, which are the same places previously deemed to need protection
against the SET ROLE issue. GUC changes are still allowed, since there are
many useful cases for that, but we prevent security problems by forcing a
rollback of any GUC change after completing the operation. Other cases are
handled by throwing an error if any change is attempted; these include temp
table creation, closing a cursor, and creating or deleting a prepared
statement. (In 7.4, the infrastructure to roll back GUC changes doesn't
exist, so we settle for rejecting changes of "search_path" in these contexts.)
Original report and patch by Gurjeet Singh, additional analysis by
Tom Lane.
Security: CVE-2009-4136
2009-12-09 22:57:51 +01:00
|
|
|
CheckRestrictedOperation("DEALLOCATE");
|
2002-08-27 06:55:12 +02:00
|
|
|
DeallocateQuery((DeallocateStmt *) parsetree);
|
|
|
|
break;
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/*
|
1997-09-08 04:41:22 +02:00
|
|
|
* schema
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
1997-09-08 04:41:22 +02:00
|
|
|
case T_RenameStmt:
|
2003-06-27 16:45:32 +02:00
|
|
|
ExecRenameStmt((RenameStmt *) parsetree);
|
1997-09-08 04:41:22 +02:00
|
|
|
break;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2005-08-01 06:03:59 +02:00
|
|
|
case T_AlterObjectSchemaStmt:
|
|
|
|
ExecAlterObjectSchemaStmt((AlterObjectSchemaStmt *) parsetree);
|
|
|
|
break;
|
|
|
|
|
2004-06-25 23:55:59 +02:00
|
|
|
case T_AlterOwnerStmt:
|
|
|
|
ExecAlterOwnerStmt((AlterOwnerStmt *) parsetree);
|
|
|
|
break;
|
|
|
|
|
2000-01-16 21:05:00 +01:00
|
|
|
case T_AlterTableStmt:
|
2007-06-24 00:12:52 +02:00
|
|
|
{
|
2012-01-07 04:42:26 +01:00
|
|
|
AlterTableStmt *atstmt = (AlterTableStmt *) parsetree;
|
|
|
|
Oid relid;
|
2007-11-15 22:14:46 +01:00
|
|
|
List *stmts;
|
|
|
|
ListCell *l;
|
2012-01-07 04:42:26 +01:00
|
|
|
LOCKMODE lockmode;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Figure out lock mode, and acquire lock. This also does
|
|
|
|
* basic permissions checks, so that we won't wait for a lock
|
|
|
|
* on (for example) a relation on which we have no
|
|
|
|
* permissions.
|
|
|
|
*/
|
|
|
|
lockmode = AlterTableGetLockLevel(atstmt->cmds);
|
|
|
|
relid = AlterTableLookupRelation(atstmt, lockmode);
|
2007-06-24 00:12:52 +02:00
|
|
|
|
|
|
|
/* Run parse analysis ... */
|
2012-01-07 04:42:26 +01:00
|
|
|
stmts = transformAlterTableStmt(atstmt, queryString);
|
2007-06-24 00:12:52 +02:00
|
|
|
|
|
|
|
/* ... and do it */
|
|
|
|
foreach(l, stmts)
|
|
|
|
{
|
2007-11-15 22:14:46 +01:00
|
|
|
Node *stmt = (Node *) lfirst(l);
|
2007-06-24 00:12:52 +02:00
|
|
|
|
|
|
|
if (IsA(stmt, AlterTableStmt))
|
|
|
|
{
|
|
|
|
/* Do the table alteration proper */
|
2012-01-07 04:42:26 +01:00
|
|
|
AlterTable(relid, lockmode, (AlterTableStmt *) stmt);
|
2007-06-24 00:12:52 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Recurse for anything else */
|
|
|
|
ProcessUtility(stmt,
|
|
|
|
queryString,
|
|
|
|
params,
|
|
|
|
false,
|
|
|
|
None_Receiver,
|
|
|
|
NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Need CCI between commands */
|
|
|
|
if (lnext(l) != NULL)
|
|
|
|
CommandCounterIncrement();
|
|
|
|
}
|
|
|
|
}
|
2000-04-12 19:17:23 +02:00
|
|
|
break;
|
2000-01-16 21:05:00 +01:00
|
|
|
|
2002-12-06 06:00:34 +01:00
|
|
|
case T_AlterDomainStmt:
|
|
|
|
{
|
|
|
|
AlterDomainStmt *stmt = (AlterDomainStmt *) parsetree;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Some or all of these functions are recursive to cover
|
|
|
|
* inherited things, so permission checks are done there.
|
|
|
|
*/
|
|
|
|
switch (stmt->subtype)
|
|
|
|
{
|
2003-09-10 01:22:21 +02:00
|
|
|
case 'T': /* ALTER DOMAIN DEFAULT */
|
2002-12-06 06:00:34 +01:00
|
|
|
|
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* Recursively alter column default for table and, if
|
|
|
|
* requested, for descendants
|
2002-12-06 06:00:34 +01:00
|
|
|
*/
|
2009-07-16 08:33:46 +02:00
|
|
|
AlterDomainDefault(stmt->typeName,
|
2002-12-06 06:00:34 +01:00
|
|
|
stmt->def);
|
|
|
|
break;
|
2003-09-10 01:22:21 +02:00
|
|
|
case 'N': /* ALTER DOMAIN DROP NOT NULL */
|
2009-07-16 08:33:46 +02:00
|
|
|
AlterDomainNotNull(stmt->typeName,
|
2002-12-06 06:00:34 +01:00
|
|
|
false);
|
|
|
|
break;
|
2003-09-10 01:22:21 +02:00
|
|
|
case 'O': /* ALTER DOMAIN SET NOT NULL */
|
2009-07-16 08:33:46 +02:00
|
|
|
AlterDomainNotNull(stmt->typeName,
|
2002-12-06 06:00:34 +01:00
|
|
|
true);
|
|
|
|
break;
|
|
|
|
case 'C': /* ADD CONSTRAINT */
|
2009-07-16 08:33:46 +02:00
|
|
|
AlterDomainAddConstraint(stmt->typeName,
|
2002-12-06 06:00:34 +01:00
|
|
|
stmt->def);
|
|
|
|
break;
|
|
|
|
case 'X': /* DROP CONSTRAINT */
|
2009-07-16 08:33:46 +02:00
|
|
|
AlterDomainDropConstraint(stmt->typeName,
|
2002-12-06 06:00:34 +01:00
|
|
|
stmt->name,
|
2012-01-05 18:48:55 +01:00
|
|
|
stmt->behavior,
|
|
|
|
stmt->missing_ok);
|
2002-12-06 06:00:34 +01:00
|
|
|
break;
|
2011-06-02 00:43:50 +02:00
|
|
|
case 'V': /* VALIDATE CONSTRAINT */
|
|
|
|
AlterDomainValidateConstraint(stmt->typeName,
|
|
|
|
stmt->name);
|
|
|
|
break;
|
2002-12-06 06:00:34 +01:00
|
|
|
default: /* oops */
|
2003-07-22 21:00:12 +02:00
|
|
|
elog(ERROR, "unrecognized alter domain type: %d",
|
|
|
|
(int) stmt->subtype);
|
2002-12-06 06:00:34 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2001-06-10 01:21:55 +02:00
|
|
|
case T_GrantStmt:
|
2002-08-27 06:55:12 +02:00
|
|
|
ExecuteGrantStmt((GrantStmt *) parsetree);
|
1997-09-08 04:41:22 +02:00
|
|
|
break;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
case T_GrantRoleStmt:
|
|
|
|
GrantRole((GrantRoleStmt *) parsetree);
|
|
|
|
break;
|
|
|
|
|
2009-10-05 21:24:49 +02:00
|
|
|
case T_AlterDefaultPrivilegesStmt:
|
|
|
|
ExecAlterDefaultPrivilegesStmt((AlterDefaultPrivilegesStmt *) parsetree);
|
|
|
|
break;
|
|
|
|
|
1997-09-08 04:41:22 +02:00
|
|
|
/*
|
2006-04-15 19:45:46 +02:00
|
|
|
* **************** object creation / destruction *****************
|
1997-09-08 04:41:22 +02:00
|
|
|
*/
|
|
|
|
case T_DefineStmt:
|
|
|
|
{
|
|
|
|
DefineStmt *stmt = (DefineStmt *) parsetree;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2003-02-10 05:44:47 +01:00
|
|
|
switch (stmt->kind)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2003-06-27 16:45:32 +02:00
|
|
|
case OBJECT_AGGREGATE:
|
2006-04-15 19:45:46 +02:00
|
|
|
DefineAggregate(stmt->defnames, stmt->args,
|
|
|
|
stmt->oldstyle, stmt->definition);
|
2003-02-10 05:44:47 +01:00
|
|
|
break;
|
2003-06-27 16:45:32 +02:00
|
|
|
case OBJECT_OPERATOR:
|
2006-04-15 19:45:46 +02:00
|
|
|
Assert(stmt->args == NIL);
|
2002-03-29 20:06:29 +01:00
|
|
|
DefineOperator(stmt->defnames, stmt->definition);
|
1997-09-08 04:41:22 +02:00
|
|
|
break;
|
2003-06-27 16:45:32 +02:00
|
|
|
case OBJECT_TYPE:
|
2006-04-15 19:45:46 +02:00
|
|
|
Assert(stmt->args == NIL);
|
2002-03-29 20:06:29 +01:00
|
|
|
DefineType(stmt->defnames, stmt->definition);
|
1997-09-08 04:41:22 +02:00
|
|
|
break;
|
2007-08-21 03:11:32 +02:00
|
|
|
case OBJECT_TSPARSER:
|
|
|
|
Assert(stmt->args == NIL);
|
|
|
|
DefineTSParser(stmt->defnames, stmt->definition);
|
|
|
|
break;
|
|
|
|
case OBJECT_TSDICTIONARY:
|
|
|
|
Assert(stmt->args == NIL);
|
|
|
|
DefineTSDictionary(stmt->defnames, stmt->definition);
|
|
|
|
break;
|
|
|
|
case OBJECT_TSTEMPLATE:
|
|
|
|
Assert(stmt->args == NIL);
|
|
|
|
DefineTSTemplate(stmt->defnames, stmt->definition);
|
|
|
|
break;
|
|
|
|
case OBJECT_TSCONFIGURATION:
|
|
|
|
Assert(stmt->args == NIL);
|
|
|
|
DefineTSConfiguration(stmt->defnames, stmt->definition);
|
|
|
|
break;
|
2011-02-12 14:54:13 +01:00
|
|
|
case OBJECT_COLLATION:
|
|
|
|
Assert(stmt->args == NIL);
|
|
|
|
DefineCollation(stmt->defnames, stmt->definition);
|
|
|
|
break;
|
2003-06-27 16:45:32 +02:00
|
|
|
default:
|
2003-07-22 21:00:12 +02:00
|
|
|
elog(ERROR, "unrecognized define stmt type: %d",
|
|
|
|
(int) stmt->kind);
|
|
|
|
break;
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
}
|
1997-09-08 04:41:22 +02:00
|
|
|
break;
|
|
|
|
|
2002-08-15 18:36:08 +02:00
|
|
|
case T_CompositeTypeStmt: /* CREATE TYPE (composite) */
|
|
|
|
{
|
2002-09-04 22:31:48 +02:00
|
|
|
CompositeTypeStmt *stmt = (CompositeTypeStmt *) parsetree;
|
2002-08-15 18:36:08 +02:00
|
|
|
|
2002-08-29 02:17:06 +02:00
|
|
|
DefineCompositeType(stmt->typevar, stmt->coldeflist);
|
2002-08-15 18:36:08 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2011-11-21 05:50:27 +01:00
|
|
|
case T_CreateEnumStmt: /* CREATE TYPE AS ENUM */
|
2007-04-02 05:49:42 +02:00
|
|
|
DefineEnum((CreateEnumStmt *) parsetree);
|
|
|
|
break;
|
|
|
|
|
2011-11-21 05:50:27 +01:00
|
|
|
case T_CreateRangeStmt: /* CREATE TYPE AS RANGE */
|
2011-11-03 12:16:28 +01:00
|
|
|
DefineRange((CreateRangeStmt *) parsetree);
|
|
|
|
break;
|
|
|
|
|
2010-10-25 05:04:37 +02:00
|
|
|
case T_AlterEnumStmt: /* ALTER TYPE (enum) */
|
2011-04-10 17:42:00 +02:00
|
|
|
|
2010-10-25 05:04:37 +02:00
|
|
|
/*
|
|
|
|
* We disallow this in transaction blocks, because we can't cope
|
|
|
|
* with enum OID values getting into indexes and then having their
|
|
|
|
* defining pg_enum entries go away.
|
|
|
|
*/
|
|
|
|
PreventTransactionChain(isTopLevel, "ALTER TYPE ... ADD");
|
|
|
|
AlterEnum((AlterEnumStmt *) parsetree);
|
|
|
|
break;
|
|
|
|
|
2007-11-15 22:14:46 +01:00
|
|
|
case T_ViewStmt: /* CREATE VIEW */
|
2007-03-13 01:33:44 +01:00
|
|
|
DefineView((ViewStmt *) parsetree, queryString);
|
1997-09-08 04:41:22 +02:00
|
|
|
break;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2002-09-04 22:31:48 +02:00
|
|
|
case T_CreateFunctionStmt: /* CREATE FUNCTION */
|
2008-12-04 18:51:28 +01:00
|
|
|
CreateFunction((CreateFunctionStmt *) parsetree, queryString);
|
1997-09-08 04:41:22 +02:00
|
|
|
break;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2005-10-15 04:49:52 +02:00
|
|
|
case T_AlterFunctionStmt: /* ALTER FUNCTION */
|
2005-03-14 01:19:37 +01:00
|
|
|
AlterFunction((AlterFunctionStmt *) parsetree);
|
|
|
|
break;
|
|
|
|
|
1997-09-08 04:41:22 +02:00
|
|
|
case T_IndexStmt: /* CREATE INDEX */
|
|
|
|
{
|
|
|
|
IndexStmt *stmt = (IndexStmt *) parsetree;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2006-08-25 06:06:58 +02:00
|
|
|
if (stmt->concurrent)
|
2007-03-13 01:33:44 +01:00
|
|
|
PreventTransactionChain(isTopLevel,
|
|
|
|
"CREATE INDEX CONCURRENTLY");
|
2006-08-25 06:06:58 +02:00
|
|
|
|
2003-07-22 21:00:12 +02:00
|
|
|
CheckRelationOwnership(stmt->relation, true);
|
2002-01-04 00:21:32 +01:00
|
|
|
|
2007-03-13 01:33:44 +01:00
|
|
|
/* Run parse analysis ... */
|
2007-06-24 00:12:52 +02:00
|
|
|
stmt = transformIndexStmt(stmt, queryString);
|
2007-03-13 01:33:44 +01:00
|
|
|
|
|
|
|
/* ... and do it */
|
2002-09-04 22:31:48 +02:00
|
|
|
DefineIndex(stmt->relation, /* relation */
|
|
|
|
stmt->idxname, /* index name */
|
2005-10-15 04:49:52 +02:00
|
|
|
InvalidOid, /* no predefined OID */
|
2011-07-18 17:02:48 +02:00
|
|
|
InvalidOid, /* no previous storage */
|
2002-09-04 22:31:48 +02:00
|
|
|
stmt->accessMethod, /* am name */
|
2004-06-18 08:14:31 +02:00
|
|
|
stmt->tableSpace,
|
2002-09-04 22:31:48 +02:00
|
|
|
stmt->indexParams, /* parameters */
|
2004-05-05 06:48:48 +02:00
|
|
|
(Expr *) stmt->whereClause,
|
2006-07-02 04:23:23 +02:00
|
|
|
stmt->options,
|
2009-12-07 06:22:23 +01:00
|
|
|
stmt->excludeOpNames,
|
1997-09-08 04:41:22 +02:00
|
|
|
stmt->unique,
|
1999-01-26 15:38:52 +01:00
|
|
|
stmt->primary,
|
2002-07-12 20:43:19 +02:00
|
|
|
stmt->isconstraint,
|
2009-07-29 22:56:21 +02:00
|
|
|
stmt->deferrable,
|
|
|
|
stmt->initdeferred,
|
2006-10-04 02:30:14 +02:00
|
|
|
false, /* is_alter_table */
|
|
|
|
true, /* check_rights */
|
|
|
|
false, /* skip_build */
|
|
|
|
false, /* quiet */
|
2006-08-25 06:06:58 +02:00
|
|
|
stmt->concurrent); /* concurrent */
|
1997-09-08 04:41:22 +02:00
|
|
|
}
|
|
|
|
break;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1997-09-08 04:41:22 +02:00
|
|
|
case T_RuleStmt: /* CREATE RULE */
|
2007-03-13 01:33:44 +01:00
|
|
|
DefineRule((RuleStmt *) parsetree, queryString);
|
1997-09-08 04:41:22 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case T_CreateSeqStmt:
|
|
|
|
DefineSequence((CreateSeqStmt *) parsetree);
|
|
|
|
break;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2003-03-20 08:02:11 +01:00
|
|
|
case T_AlterSeqStmt:
|
|
|
|
AlterSequence((AlterSeqStmt *) parsetree);
|
|
|
|
break;
|
|
|
|
|
2009-09-23 01:43:43 +02:00
|
|
|
case T_DoStmt:
|
|
|
|
ExecuteDoStmt((DoStmt *) parsetree);
|
|
|
|
break;
|
|
|
|
|
1997-09-08 04:41:22 +02:00
|
|
|
case T_CreatedbStmt:
|
2007-03-13 01:33:44 +01:00
|
|
|
PreventTransactionChain(isTopLevel, "CREATE DATABASE");
|
2002-07-01 17:27:56 +02:00
|
|
|
createdb((CreatedbStmt *) parsetree);
|
1997-09-08 04:41:22 +02:00
|
|
|
break;
|
|
|
|
|
2005-07-31 19:19:22 +02:00
|
|
|
case T_AlterDatabaseStmt:
|
2008-11-07 19:25:07 +01:00
|
|
|
AlterDatabase((AlterDatabaseStmt *) parsetree, isTopLevel);
|
2005-07-31 19:19:22 +02:00
|
|
|
break;
|
|
|
|
|
2002-03-01 23:45:19 +01:00
|
|
|
case T_AlterDatabaseSetStmt:
|
2002-07-01 17:27:56 +02:00
|
|
|
AlterDatabaseSet((AlterDatabaseSetStmt *) parsetree);
|
2002-03-01 23:45:19 +01:00
|
|
|
break;
|
|
|
|
|
1999-12-10 04:56:14 +01:00
|
|
|
case T_DropdbStmt:
|
1997-09-08 04:41:22 +02:00
|
|
|
{
|
1999-12-10 04:56:14 +01:00
|
|
|
DropdbStmt *stmt = (DropdbStmt *) parsetree;
|
1997-04-02 20:24:52 +02:00
|
|
|
|
2007-03-13 01:33:44 +01:00
|
|
|
PreventTransactionChain(isTopLevel, "DROP DATABASE");
|
2005-11-22 16:24:18 +01:00
|
|
|
dropdb(stmt->dbname, stmt->missing_ok);
|
1997-09-08 04:41:22 +02:00
|
|
|
}
|
|
|
|
break;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1997-09-08 04:41:22 +02:00
|
|
|
/* Query-level asynchronous notification */
|
|
|
|
case T_NotifyStmt:
|
|
|
|
{
|
|
|
|
NotifyStmt *stmt = (NotifyStmt *) parsetree;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2010-02-20 22:24:02 +01:00
|
|
|
PreventCommandDuringRecovery("NOTIFY");
|
2010-02-16 23:34:57 +01:00
|
|
|
Async_Notify(stmt->conditionname, stmt->payload);
|
1997-09-08 04:41:22 +02:00
|
|
|
}
|
|
|
|
break;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1997-09-08 04:41:22 +02:00
|
|
|
case T_ListenStmt:
|
|
|
|
{
|
|
|
|
ListenStmt *stmt = (ListenStmt *) parsetree;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2010-02-20 22:24:02 +01:00
|
|
|
PreventCommandDuringRecovery("LISTEN");
|
Prevent indirect security attacks via changing session-local state within
an allegedly immutable index function. It was previously recognized that
we had to prevent such a function from executing SET/RESET ROLE/SESSION
AUTHORIZATION, or it could trivially obtain the privileges of the session
user. However, since there is in general no privilege checking for changes
of session-local state, it is also possible for such a function to change
settings in a way that might subvert later operations in the same session.
Examples include changing search_path to cause an unexpected function to
be called, or replacing an existing prepared statement with another one
that will execute a function of the attacker's choosing.
The present patch secures VACUUM, ANALYZE, and CREATE INDEX/REINDEX against
these threats, which are the same places previously deemed to need protection
against the SET ROLE issue. GUC changes are still allowed, since there are
many useful cases for that, but we prevent security problems by forcing a
rollback of any GUC change after completing the operation. Other cases are
handled by throwing an error if any change is attempted; these include temp
table creation, closing a cursor, and creating or deleting a prepared
statement. (In 7.4, the infrastructure to roll back GUC changes doesn't
exist, so we settle for rejecting changes of "search_path" in these contexts.)
Original report and patch by Gurjeet Singh, additional analysis by
Tom Lane.
Security: CVE-2009-4136
2009-12-09 22:57:51 +01:00
|
|
|
CheckRestrictedOperation("LISTEN");
|
2008-09-01 22:42:46 +02:00
|
|
|
Async_Listen(stmt->conditionname);
|
1997-09-08 04:41:22 +02:00
|
|
|
}
|
|
|
|
break;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1998-08-25 23:37:08 +02:00
|
|
|
case T_UnlistenStmt:
|
|
|
|
{
|
|
|
|
UnlistenStmt *stmt = (UnlistenStmt *) parsetree;
|
|
|
|
|
2010-02-20 22:24:02 +01:00
|
|
|
PreventCommandDuringRecovery("UNLISTEN");
|
Prevent indirect security attacks via changing session-local state within
an allegedly immutable index function. It was previously recognized that
we had to prevent such a function from executing SET/RESET ROLE/SESSION
AUTHORIZATION, or it could trivially obtain the privileges of the session
user. However, since there is in general no privilege checking for changes
of session-local state, it is also possible for such a function to change
settings in a way that might subvert later operations in the same session.
Examples include changing search_path to cause an unexpected function to
be called, or replacing an existing prepared statement with another one
that will execute a function of the attacker's choosing.
The present patch secures VACUUM, ANALYZE, and CREATE INDEX/REINDEX against
these threats, which are the same places previously deemed to need protection
against the SET ROLE issue. GUC changes are still allowed, since there are
many useful cases for that, but we prevent security problems by forcing a
rollback of any GUC change after completing the operation. Other cases are
handled by throwing an error if any change is attempted; these include temp
table creation, closing a cursor, and creating or deleting a prepared
statement. (In 7.4, the infrastructure to roll back GUC changes doesn't
exist, so we settle for rejecting changes of "search_path" in these contexts.)
Original report and patch by Gurjeet Singh, additional analysis by
Tom Lane.
Security: CVE-2009-4136
2009-12-09 22:57:51 +01:00
|
|
|
CheckRestrictedOperation("UNLISTEN");
|
2008-09-01 22:42:46 +02:00
|
|
|
if (stmt->conditionname)
|
|
|
|
Async_Unlisten(stmt->conditionname);
|
2008-08-30 03:39:14 +02:00
|
|
|
else
|
|
|
|
Async_UnlistenAll();
|
1998-08-25 23:37:08 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
1997-09-08 04:41:22 +02:00
|
|
|
case T_LoadStmt:
|
|
|
|
{
|
|
|
|
LoadStmt *stmt = (LoadStmt *) parsetree;
|
|
|
|
|
1999-05-25 18:15:34 +02:00
|
|
|
closeAllVfds(); /* probably not necessary... */
|
2006-08-15 20:26:59 +02:00
|
|
|
/* Allowed names are restricted if you're not superuser */
|
|
|
|
load_file(stmt->filename, !superuser());
|
1997-09-08 04:41:22 +02:00
|
|
|
}
|
|
|
|
break;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1997-09-08 04:41:22 +02:00
|
|
|
case T_ClusterStmt:
|
2010-02-20 22:24:02 +01:00
|
|
|
/* we choose to allow this during "read only" transactions */
|
|
|
|
PreventCommandDuringRecovery("CLUSTER");
|
2007-03-13 01:33:44 +01:00
|
|
|
cluster((ClusterStmt *) parsetree, isTopLevel);
|
1997-09-08 04:41:22 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case T_VacuumStmt:
|
2010-02-20 22:24:02 +01:00
|
|
|
/* we choose to allow this during "read only" transactions */
|
|
|
|
PreventCommandDuringRecovery("VACUUM");
|
2008-08-13 02:07:50 +02:00
|
|
|
vacuum((VacuumStmt *) parsetree, InvalidOid, true, NULL, false,
|
2008-06-05 17:47:32 +02:00
|
|
|
isTopLevel);
|
1997-09-08 04:41:22 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case T_ExplainStmt:
|
2007-03-13 01:33:44 +01:00
|
|
|
ExplainQuery((ExplainStmt *) parsetree, queryString, params, dest);
|
1997-09-08 04:41:22 +02:00
|
|
|
break;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1997-09-08 04:41:22 +02:00
|
|
|
case T_VariableSetStmt:
|
2007-09-03 20:46:30 +02:00
|
|
|
ExecSetVariableStmt((VariableSetStmt *) parsetree);
|
1997-09-08 04:41:22 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case T_VariableShowStmt:
|
|
|
|
{
|
|
|
|
VariableShowStmt *n = (VariableShowStmt *) parsetree;
|
|
|
|
|
2003-05-06 22:26:28 +02:00
|
|
|
GetPGVariable(n->name, dest);
|
1997-09-08 04:41:22 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2007-04-26 18:13:15 +02:00
|
|
|
case T_DiscardStmt:
|
Prevent indirect security attacks via changing session-local state within
an allegedly immutable index function. It was previously recognized that
we had to prevent such a function from executing SET/RESET ROLE/SESSION
AUTHORIZATION, or it could trivially obtain the privileges of the session
user. However, since there is in general no privilege checking for changes
of session-local state, it is also possible for such a function to change
settings in a way that might subvert later operations in the same session.
Examples include changing search_path to cause an unexpected function to
be called, or replacing an existing prepared statement with another one
that will execute a function of the attacker's choosing.
The present patch secures VACUUM, ANALYZE, and CREATE INDEX/REINDEX against
these threats, which are the same places previously deemed to need protection
against the SET ROLE issue. GUC changes are still allowed, since there are
many useful cases for that, but we prevent security problems by forcing a
rollback of any GUC change after completing the operation. Other cases are
handled by throwing an error if any change is attempted; these include temp
table creation, closing a cursor, and creating or deleting a prepared
statement. (In 7.4, the infrastructure to roll back GUC changes doesn't
exist, so we settle for rejecting changes of "search_path" in these contexts.)
Original report and patch by Gurjeet Singh, additional analysis by
Tom Lane.
Security: CVE-2009-4136
2009-12-09 22:57:51 +01:00
|
|
|
/* should we allow DISCARD PLANS? */
|
|
|
|
CheckRestrictedOperation("DISCARD");
|
2007-04-26 18:13:15 +02:00
|
|
|
DiscardCommand((DiscardStmt *) parsetree, isTopLevel);
|
|
|
|
break;
|
|
|
|
|
1997-09-08 04:41:22 +02:00
|
|
|
case T_CreateTrigStmt:
|
2010-01-17 23:56:23 +01:00
|
|
|
(void) CreateTrigger((CreateTrigStmt *) parsetree, queryString,
|
|
|
|
InvalidOid, InvalidOid, false);
|
1997-09-08 04:41:22 +02:00
|
|
|
break;
|
|
|
|
|
1997-10-28 16:11:45 +01:00
|
|
|
case T_CreatePLangStmt:
|
|
|
|
CreateProceduralLanguage((CreatePLangStmt *) parsetree);
|
|
|
|
break;
|
|
|
|
|
2002-03-19 03:18:25 +01:00
|
|
|
/*
|
|
|
|
* ******************************** DOMAIN statements ****
|
|
|
|
*/
|
|
|
|
case T_CreateDomainStmt:
|
|
|
|
DefineDomain((CreateDomainStmt *) parsetree);
|
|
|
|
break;
|
|
|
|
|
|
|
|
/*
|
2005-06-28 07:09:14 +02:00
|
|
|
* ******************************** ROLE statements ****
|
2002-03-19 03:18:25 +01:00
|
|
|
*/
|
2005-06-28 07:09:14 +02:00
|
|
|
case T_CreateRoleStmt:
|
|
|
|
CreateRole((CreateRoleStmt *) parsetree);
|
1998-02-26 05:46:47 +01:00
|
|
|
break;
|
1997-12-04 01:28:15 +01:00
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
case T_AlterRoleStmt:
|
|
|
|
AlterRole((AlterRoleStmt *) parsetree);
|
1998-02-26 05:46:47 +01:00
|
|
|
break;
|
1997-12-04 01:28:15 +01:00
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
case T_AlterRoleSetStmt:
|
|
|
|
AlterRoleSet((AlterRoleSetStmt *) parsetree);
|
2002-03-01 23:45:19 +01:00
|
|
|
break;
|
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
case T_DropRoleStmt:
|
|
|
|
DropRole((DropRoleStmt *) parsetree);
|
1998-02-26 05:46:47 +01:00
|
|
|
break;
|
1997-12-04 01:28:15 +01:00
|
|
|
|
2005-11-21 13:49:33 +01:00
|
|
|
case T_DropOwnedStmt:
|
|
|
|
DropOwnedObjects((DropOwnedStmt *) parsetree);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_ReassignOwnedStmt:
|
|
|
|
ReassignOwnedObjects((ReassignOwnedStmt *) parsetree);
|
|
|
|
break;
|
|
|
|
|
1998-12-18 10:10:39 +01:00
|
|
|
case T_LockStmt:
|
2009-06-11 16:49:15 +02:00
|
|
|
|
2008-11-04 01:57:19 +01:00
|
|
|
/*
|
|
|
|
* Since the lock would just get dropped immediately, LOCK TABLE
|
|
|
|
* outside a transaction block is presumed to be user error.
|
|
|
|
*/
|
|
|
|
RequireTransactionChain(isTopLevel, "LOCK TABLE");
|
1998-12-18 10:10:39 +01:00
|
|
|
LockTableCommand((LockStmt *) parsetree);
|
|
|
|
break;
|
|
|
|
|
1999-09-29 18:06:40 +02:00
|
|
|
case T_ConstraintsSetStmt:
|
2004-09-10 20:40:09 +02:00
|
|
|
AfterTriggerSetState((ConstraintsSetStmt *) parsetree);
|
1999-09-29 18:06:40 +02:00
|
|
|
break;
|
|
|
|
|
2000-11-05 23:50:21 +01:00
|
|
|
case T_CheckPointStmt:
|
2002-09-27 00:58:34 +02:00
|
|
|
if (!superuser())
|
2003-07-22 21:00:12 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
2003-08-01 02:15:26 +02:00
|
|
|
errmsg("must be superuser to do CHECKPOINT")));
|
2010-02-26 03:01:40 +01:00
|
|
|
|
Allow read only connections during recovery, known as Hot Standby.
Enabled by recovery_connections = on (default) and forcing archive recovery using a recovery.conf. Recovery processing now emulates the original transactions as they are replayed, providing full locking and MVCC behaviour for read only queries. Recovery must enter consistent state before connections are allowed, so there is a delay, typically short, before connections succeed. Replay of recovering transactions can conflict and in some cases deadlock with queries during recovery; these result in query cancellation after max_standby_delay seconds have expired. Infrastructure changes have minor effects on normal running, though introduce four new types of WAL record.
New test mode "make standbycheck" allows regression tests of static command behaviour on a standby server while in recovery. Typical and extreme dynamic behaviours have been checked via code inspection and manual testing. Few port specific behaviours have been utilised, though primary testing has been on Linux only so far.
This commit is the basic patch. Additional changes will follow in this release to enhance some aspects of behaviour, notably improved handling of conflicts, deadlock detection and query cancellation. Changes to VACUUM FULL are also required.
Simon Riggs, with significant and lengthy review by Heikki Linnakangas, including streamlined redesign of snapshot creation and two-phase commit.
Important contributions from Florian Pflug, Mark Kirkwood, Merlin Moncure, Greg Stark, Gianni Ciolli, Gabriele Bartolini, Hannu Krosing, Robert Haas, Tatsuo Ishii, Hiroyuki Yamada plus support and feedback from many other community members.
2009-12-19 02:32:45 +01:00
|
|
|
/*
|
|
|
|
* You might think we should have a PreventCommandDuringRecovery()
|
2010-02-26 03:01:40 +01:00
|
|
|
* here, but we interpret a CHECKPOINT command during recovery as
|
|
|
|
* a request for a restartpoint instead. We allow this since it
|
|
|
|
* can be a useful way of reducing switchover time when using
|
|
|
|
* various forms of replication.
|
Allow read only connections during recovery, known as Hot Standby.
Enabled by recovery_connections = on (default) and forcing archive recovery using a recovery.conf. Recovery processing now emulates the original transactions as they are replayed, providing full locking and MVCC behaviour for read only queries. Recovery must enter consistent state before connections are allowed, so there is a delay, typically short, before connections succeed. Replay of recovering transactions can conflict and in some cases deadlock with queries during recovery; these result in query cancellation after max_standby_delay seconds have expired. Infrastructure changes have minor effects on normal running, though introduce four new types of WAL record.
New test mode "make standbycheck" allows regression tests of static command behaviour on a standby server while in recovery. Typical and extreme dynamic behaviours have been checked via code inspection and manual testing. Few port specific behaviours have been utilised, though primary testing has been on Linux only so far.
This commit is the basic patch. Additional changes will follow in this release to enhance some aspects of behaviour, notably improved handling of conflicts, deadlock detection and query cancellation. Changes to VACUUM FULL are also required.
Simon Riggs, with significant and lengthy review by Heikki Linnakangas, including streamlined redesign of snapshot creation and two-phase commit.
Important contributions from Florian Pflug, Mark Kirkwood, Merlin Moncure, Greg Stark, Gianni Ciolli, Gabriele Bartolini, Hannu Krosing, Robert Haas, Tatsuo Ishii, Hiroyuki Yamada plus support and feedback from many other community members.
2009-12-19 02:32:45 +01:00
|
|
|
*/
|
|
|
|
RequestCheckpoint(CHECKPOINT_IMMEDIATE | CHECKPOINT_WAIT |
|
2010-02-20 22:24:02 +01:00
|
|
|
(RecoveryInProgress() ? 0 : CHECKPOINT_FORCE));
|
2000-11-05 23:50:21 +01:00
|
|
|
break;
|
|
|
|
|
2000-02-18 10:30:20 +01:00
|
|
|
case T_ReindexStmt:
|
|
|
|
{
|
|
|
|
ReindexStmt *stmt = (ReindexStmt *) parsetree;
|
|
|
|
|
2010-02-20 22:24:02 +01:00
|
|
|
/* we choose to allow this during "read only" transactions */
|
|
|
|
PreventCommandDuringRecovery("REINDEX");
|
2003-02-10 05:44:47 +01:00
|
|
|
switch (stmt->kind)
|
2000-02-18 10:30:20 +01:00
|
|
|
{
|
2003-06-27 16:45:32 +02:00
|
|
|
case OBJECT_INDEX:
|
2005-06-22 23:14:31 +02:00
|
|
|
ReindexIndex(stmt->relation);
|
2000-02-18 10:30:20 +01:00
|
|
|
break;
|
2003-06-27 16:45:32 +02:00
|
|
|
case OBJECT_TABLE:
|
2005-06-22 23:14:31 +02:00
|
|
|
ReindexTable(stmt->relation);
|
2000-02-18 10:30:20 +01:00
|
|
|
break;
|
2003-06-27 16:45:32 +02:00
|
|
|
case OBJECT_DATABASE:
|
2007-11-15 22:14:46 +01:00
|
|
|
|
2007-03-13 01:33:44 +01:00
|
|
|
/*
|
2007-11-15 22:14:46 +01:00
|
|
|
* This cannot run inside a user transaction block; if
|
|
|
|
* we were inside a transaction, then its commit- and
|
|
|
|
* start-transaction-command calls would not have the
|
|
|
|
* intended effect!
|
2007-03-13 01:33:44 +01:00
|
|
|
*/
|
|
|
|
PreventTransactionChain(isTopLevel,
|
|
|
|
"REINDEX DATABASE");
|
2005-06-22 23:14:31 +02:00
|
|
|
ReindexDatabase(stmt->name,
|
|
|
|
stmt->do_system, stmt->do_user);
|
2000-02-18 10:30:20 +01:00
|
|
|
break;
|
2003-06-27 16:45:32 +02:00
|
|
|
default:
|
2003-07-22 21:00:12 +02:00
|
|
|
elog(ERROR, "unrecognized object type: %d",
|
|
|
|
(int) stmt->kind);
|
|
|
|
break;
|
2000-02-18 10:30:20 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
2000-04-12 19:17:23 +02:00
|
|
|
|
2002-07-11 09:39:28 +02:00
|
|
|
case T_CreateConversionStmt:
|
2002-08-27 06:55:12 +02:00
|
|
|
CreateConversionCommand((CreateConversionStmt *) parsetree);
|
2002-07-11 09:39:28 +02:00
|
|
|
break;
|
|
|
|
|
2002-07-19 01:11:32 +02:00
|
|
|
case T_CreateCastStmt:
|
|
|
|
CreateCast((CreateCastStmt *) parsetree);
|
|
|
|
break;
|
|
|
|
|
2002-07-30 00:14:11 +02:00
|
|
|
case T_CreateOpClassStmt:
|
|
|
|
DefineOpClass((CreateOpClassStmt *) parsetree);
|
|
|
|
break;
|
|
|
|
|
2007-01-23 06:07:18 +01:00
|
|
|
case T_CreateOpFamilyStmt:
|
|
|
|
DefineOpFamily((CreateOpFamilyStmt *) parsetree);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_AlterOpFamilyStmt:
|
|
|
|
AlterOpFamily((AlterOpFamilyStmt *) parsetree);
|
|
|
|
break;
|
|
|
|
|
2007-08-21 03:11:32 +02:00
|
|
|
case T_AlterTSDictionaryStmt:
|
|
|
|
AlterTSDictionary((AlterTSDictionaryStmt *) parsetree);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_AlterTSConfigurationStmt:
|
|
|
|
AlterTSConfiguration((AlterTSConfigurationStmt *) parsetree);
|
|
|
|
break;
|
|
|
|
|
1997-09-08 04:41:22 +02:00
|
|
|
default:
|
2003-07-22 21:00:12 +02:00
|
|
|
elog(ERROR, "unrecognized node type: %d",
|
|
|
|
(int) nodeTag(parsetree));
|
1997-09-08 04:41:22 +02:00
|
|
|
break;
|
1997-04-02 20:24:52 +02:00
|
|
|
}
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
2003-05-02 22:54:36 +02:00
|
|
|
|
2003-05-06 22:26:28 +02:00
|
|
|
/*
|
|
|
|
* UtilityReturnsTuples
|
|
|
|
* Return "true" if this utility statement will send output to the
|
|
|
|
* destination.
|
|
|
|
*
|
|
|
|
* Generally, there should be a case here for each case in ProcessUtility
|
|
|
|
* where "dest" is passed on.
|
|
|
|
*/
|
|
|
|
bool
|
|
|
|
UtilityReturnsTuples(Node *parsetree)
|
|
|
|
{
|
|
|
|
switch (nodeTag(parsetree))
|
|
|
|
{
|
|
|
|
case T_FetchStmt:
|
|
|
|
{
|
2003-08-04 02:43:34 +02:00
|
|
|
FetchStmt *stmt = (FetchStmt *) parsetree;
|
|
|
|
Portal portal;
|
2003-05-06 22:26:28 +02:00
|
|
|
|
|
|
|
if (stmt->ismove)
|
|
|
|
return false;
|
|
|
|
portal = GetPortalByName(stmt->portalname);
|
|
|
|
if (!PortalIsValid(portal))
|
2003-08-04 02:43:34 +02:00
|
|
|
return false; /* not our business to raise error */
|
2003-05-06 22:26:28 +02:00
|
|
|
return portal->tupDesc ? true : false;
|
|
|
|
}
|
|
|
|
|
|
|
|
case T_ExecuteStmt:
|
|
|
|
{
|
|
|
|
ExecuteStmt *stmt = (ExecuteStmt *) parsetree;
|
|
|
|
PreparedStatement *entry;
|
|
|
|
|
|
|
|
if (stmt->into)
|
|
|
|
return false;
|
|
|
|
entry = FetchPreparedStatement(stmt->name, false);
|
|
|
|
if (!entry)
|
2003-08-04 02:43:34 +02:00
|
|
|
return false; /* not our business to raise error */
|
2007-03-13 01:33:44 +01:00
|
|
|
if (entry->plansource->resultDesc)
|
|
|
|
return true;
|
2003-05-06 22:26:28 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
case T_ExplainStmt:
|
|
|
|
return true;
|
|
|
|
|
|
|
|
case T_VariableShowStmt:
|
|
|
|
return true;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* UtilityTupleDescriptor
|
|
|
|
* Fetch the actual output tuple descriptor for a utility statement
|
|
|
|
* for which UtilityReturnsTuples() previously returned "true".
|
|
|
|
*
|
|
|
|
* The returned descriptor is created in (or copied into) the current memory
|
|
|
|
* context.
|
|
|
|
*/
|
|
|
|
TupleDesc
|
|
|
|
UtilityTupleDescriptor(Node *parsetree)
|
|
|
|
{
|
|
|
|
switch (nodeTag(parsetree))
|
|
|
|
{
|
|
|
|
case T_FetchStmt:
|
|
|
|
{
|
2003-08-04 02:43:34 +02:00
|
|
|
FetchStmt *stmt = (FetchStmt *) parsetree;
|
|
|
|
Portal portal;
|
2003-05-06 22:26:28 +02:00
|
|
|
|
|
|
|
if (stmt->ismove)
|
|
|
|
return NULL;
|
|
|
|
portal = GetPortalByName(stmt->portalname);
|
|
|
|
if (!PortalIsValid(portal))
|
2003-08-04 02:43:34 +02:00
|
|
|
return NULL; /* not our business to raise error */
|
2003-05-06 22:26:28 +02:00
|
|
|
return CreateTupleDescCopy(portal->tupDesc);
|
|
|
|
}
|
|
|
|
|
|
|
|
case T_ExecuteStmt:
|
|
|
|
{
|
|
|
|
ExecuteStmt *stmt = (ExecuteStmt *) parsetree;
|
|
|
|
PreparedStatement *entry;
|
|
|
|
|
|
|
|
if (stmt->into)
|
|
|
|
return NULL;
|
|
|
|
entry = FetchPreparedStatement(stmt->name, false);
|
|
|
|
if (!entry)
|
2003-08-04 02:43:34 +02:00
|
|
|
return NULL; /* not our business to raise error */
|
2003-05-06 23:51:42 +02:00
|
|
|
return FetchPreparedStatementResultDesc(entry);
|
2003-05-06 22:26:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
case T_ExplainStmt:
|
|
|
|
return ExplainResultDesc((ExplainStmt *) parsetree);
|
|
|
|
|
|
|
|
case T_VariableShowStmt:
|
|
|
|
{
|
|
|
|
VariableShowStmt *n = (VariableShowStmt *) parsetree;
|
|
|
|
|
|
|
|
return GetPGVariableResultDesc(n->name);
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-05-02 22:54:36 +02:00
|
|
|
|
2006-08-12 22:05:56 +02:00
|
|
|
/*
|
|
|
|
* QueryReturnsTuples
|
|
|
|
* Return "true" if this Query will send output to the destination.
|
|
|
|
*/
|
2007-02-20 18:32:18 +01:00
|
|
|
#ifdef NOT_USED
|
2006-08-12 22:05:56 +02:00
|
|
|
bool
|
|
|
|
QueryReturnsTuples(Query *parsetree)
|
|
|
|
{
|
|
|
|
switch (parsetree->commandType)
|
|
|
|
{
|
|
|
|
case CMD_SELECT:
|
2007-04-28 00:05:49 +02:00
|
|
|
/* returns tuples ... unless it's DECLARE CURSOR or SELECT INTO */
|
|
|
|
if (parsetree->utilityStmt == NULL &&
|
|
|
|
parsetree->intoClause == NULL)
|
2006-08-12 22:05:56 +02:00
|
|
|
return true;
|
|
|
|
break;
|
|
|
|
case CMD_INSERT:
|
|
|
|
case CMD_UPDATE:
|
|
|
|
case CMD_DELETE:
|
|
|
|
/* the forms with RETURNING return tuples */
|
|
|
|
if (parsetree->returningList)
|
|
|
|
return true;
|
|
|
|
break;
|
|
|
|
case CMD_UTILITY:
|
|
|
|
return UtilityReturnsTuples(parsetree->utilityStmt);
|
|
|
|
case CMD_UNKNOWN:
|
|
|
|
case CMD_NOTHING:
|
|
|
|
/* probably shouldn't get here */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return false; /* default */
|
|
|
|
}
|
2007-02-20 18:32:18 +01:00
|
|
|
#endif
|
2006-08-12 22:05:56 +02:00
|
|
|
|
|
|
|
|
2011-02-09 17:55:32 +01:00
|
|
|
/*
|
|
|
|
* AlterObjectTypeCommandTag
|
|
|
|
* helper function for CreateCommandTag
|
|
|
|
*
|
|
|
|
* This covers most cases where ALTER is used with an ObjectType enum.
|
|
|
|
*/
|
|
|
|
static const char *
|
|
|
|
AlterObjectTypeCommandTag(ObjectType objtype)
|
|
|
|
{
|
|
|
|
const char *tag;
|
|
|
|
|
|
|
|
switch (objtype)
|
|
|
|
{
|
|
|
|
case OBJECT_AGGREGATE:
|
|
|
|
tag = "ALTER AGGREGATE";
|
|
|
|
break;
|
|
|
|
case OBJECT_ATTRIBUTE:
|
|
|
|
tag = "ALTER TYPE";
|
|
|
|
break;
|
|
|
|
case OBJECT_CAST:
|
|
|
|
tag = "ALTER CAST";
|
|
|
|
break;
|
2011-02-12 14:54:13 +01:00
|
|
|
case OBJECT_COLLATION:
|
|
|
|
tag = "ALTER COLLATION";
|
|
|
|
break;
|
2011-02-09 17:55:32 +01:00
|
|
|
case OBJECT_COLUMN:
|
|
|
|
tag = "ALTER TABLE";
|
|
|
|
break;
|
|
|
|
case OBJECT_CONSTRAINT:
|
|
|
|
tag = "ALTER TABLE";
|
|
|
|
break;
|
|
|
|
case OBJECT_CONVERSION:
|
|
|
|
tag = "ALTER CONVERSION";
|
|
|
|
break;
|
|
|
|
case OBJECT_DATABASE:
|
|
|
|
tag = "ALTER DATABASE";
|
|
|
|
break;
|
|
|
|
case OBJECT_DOMAIN:
|
|
|
|
tag = "ALTER DOMAIN";
|
|
|
|
break;
|
|
|
|
case OBJECT_EXTENSION:
|
|
|
|
tag = "ALTER EXTENSION";
|
|
|
|
break;
|
|
|
|
case OBJECT_FDW:
|
|
|
|
tag = "ALTER FOREIGN DATA WRAPPER";
|
|
|
|
break;
|
|
|
|
case OBJECT_FOREIGN_SERVER:
|
|
|
|
tag = "ALTER SERVER";
|
|
|
|
break;
|
|
|
|
case OBJECT_FOREIGN_TABLE:
|
|
|
|
tag = "ALTER FOREIGN TABLE";
|
|
|
|
break;
|
|
|
|
case OBJECT_FUNCTION:
|
|
|
|
tag = "ALTER FUNCTION";
|
|
|
|
break;
|
|
|
|
case OBJECT_INDEX:
|
|
|
|
tag = "ALTER INDEX";
|
|
|
|
break;
|
|
|
|
case OBJECT_LANGUAGE:
|
|
|
|
tag = "ALTER LANGUAGE";
|
|
|
|
break;
|
|
|
|
case OBJECT_LARGEOBJECT:
|
|
|
|
tag = "ALTER LARGE OBJECT";
|
|
|
|
break;
|
|
|
|
case OBJECT_OPCLASS:
|
|
|
|
tag = "ALTER OPERATOR CLASS";
|
|
|
|
break;
|
|
|
|
case OBJECT_OPERATOR:
|
|
|
|
tag = "ALTER OPERATOR";
|
|
|
|
break;
|
|
|
|
case OBJECT_OPFAMILY:
|
|
|
|
tag = "ALTER OPERATOR FAMILY";
|
|
|
|
break;
|
|
|
|
case OBJECT_ROLE:
|
|
|
|
tag = "ALTER ROLE";
|
|
|
|
break;
|
|
|
|
case OBJECT_RULE:
|
|
|
|
tag = "ALTER RULE";
|
|
|
|
break;
|
|
|
|
case OBJECT_SCHEMA:
|
|
|
|
tag = "ALTER SCHEMA";
|
|
|
|
break;
|
|
|
|
case OBJECT_SEQUENCE:
|
|
|
|
tag = "ALTER SEQUENCE";
|
|
|
|
break;
|
|
|
|
case OBJECT_TABLE:
|
|
|
|
tag = "ALTER TABLE";
|
|
|
|
break;
|
|
|
|
case OBJECT_TABLESPACE:
|
|
|
|
tag = "ALTER TABLESPACE";
|
|
|
|
break;
|
|
|
|
case OBJECT_TRIGGER:
|
|
|
|
tag = "ALTER TRIGGER";
|
|
|
|
break;
|
|
|
|
case OBJECT_TSCONFIGURATION:
|
|
|
|
tag = "ALTER TEXT SEARCH CONFIGURATION";
|
|
|
|
break;
|
|
|
|
case OBJECT_TSDICTIONARY:
|
|
|
|
tag = "ALTER TEXT SEARCH DICTIONARY";
|
|
|
|
break;
|
|
|
|
case OBJECT_TSPARSER:
|
|
|
|
tag = "ALTER TEXT SEARCH PARSER";
|
|
|
|
break;
|
|
|
|
case OBJECT_TSTEMPLATE:
|
|
|
|
tag = "ALTER TEXT SEARCH TEMPLATE";
|
|
|
|
break;
|
|
|
|
case OBJECT_TYPE:
|
|
|
|
tag = "ALTER TYPE";
|
|
|
|
break;
|
|
|
|
case OBJECT_VIEW:
|
|
|
|
tag = "ALTER VIEW";
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
tag = "???";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return tag;
|
|
|
|
}
|
|
|
|
|
2003-05-02 22:54:36 +02:00
|
|
|
/*
|
|
|
|
* CreateCommandTag
|
2007-02-20 18:32:18 +01:00
|
|
|
* utility to get a string representation of the command operation,
|
|
|
|
* given either a raw (un-analyzed) parsetree or a planned query.
|
2003-05-02 22:54:36 +02:00
|
|
|
*
|
2007-02-20 18:32:18 +01:00
|
|
|
* This must handle all command types, but since the vast majority
|
2003-05-02 22:54:36 +02:00
|
|
|
* of 'em are utility commands, it seems sensible to keep it here.
|
|
|
|
*
|
|
|
|
* NB: all result strings must be shorter than COMPLETION_TAG_BUFSIZE.
|
|
|
|
* Also, the result must point at a true constant (permanent storage).
|
|
|
|
*/
|
|
|
|
const char *
|
|
|
|
CreateCommandTag(Node *parsetree)
|
|
|
|
{
|
|
|
|
const char *tag;
|
|
|
|
|
|
|
|
switch (nodeTag(parsetree))
|
|
|
|
{
|
2007-11-15 22:14:46 +01:00
|
|
|
/* raw plannable queries */
|
2003-05-02 22:54:36 +02:00
|
|
|
case T_InsertStmt:
|
|
|
|
tag = "INSERT";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_DeleteStmt:
|
|
|
|
tag = "DELETE";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_UpdateStmt:
|
|
|
|
tag = "UPDATE";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_SelectStmt:
|
|
|
|
tag = "SELECT";
|
|
|
|
break;
|
|
|
|
|
2007-11-15 22:14:46 +01:00
|
|
|
/* utility statements --- same whether raw or cooked */
|
2003-05-02 22:54:36 +02:00
|
|
|
case T_TransactionStmt:
|
|
|
|
{
|
|
|
|
TransactionStmt *stmt = (TransactionStmt *) parsetree;
|
|
|
|
|
|
|
|
switch (stmt->kind)
|
|
|
|
{
|
|
|
|
case TRANS_STMT_BEGIN:
|
|
|
|
tag = "BEGIN";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TRANS_STMT_START:
|
|
|
|
tag = "START TRANSACTION";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TRANS_STMT_COMMIT:
|
|
|
|
tag = "COMMIT";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TRANS_STMT_ROLLBACK:
|
2004-07-27 07:11:48 +02:00
|
|
|
case TRANS_STMT_ROLLBACK_TO:
|
2003-05-02 22:54:36 +02:00
|
|
|
tag = "ROLLBACK";
|
|
|
|
break;
|
|
|
|
|
2004-07-27 07:11:48 +02:00
|
|
|
case TRANS_STMT_SAVEPOINT:
|
|
|
|
tag = "SAVEPOINT";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TRANS_STMT_RELEASE:
|
|
|
|
tag = "RELEASE";
|
|
|
|
break;
|
|
|
|
|
2005-06-18 00:32:51 +02:00
|
|
|
case TRANS_STMT_PREPARE:
|
|
|
|
tag = "PREPARE TRANSACTION";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TRANS_STMT_COMMIT_PREPARED:
|
|
|
|
tag = "COMMIT PREPARED";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TRANS_STMT_ROLLBACK_PREPARED:
|
|
|
|
tag = "ROLLBACK PREPARED";
|
|
|
|
break;
|
|
|
|
|
2003-05-02 22:54:36 +02:00
|
|
|
default:
|
|
|
|
tag = "???";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_DeclareCursorStmt:
|
|
|
|
tag = "DECLARE CURSOR";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_ClosePortalStmt:
|
2007-04-12 08:53:49 +02:00
|
|
|
{
|
|
|
|
ClosePortalStmt *stmt = (ClosePortalStmt *) parsetree;
|
2007-11-15 22:14:46 +01:00
|
|
|
|
2007-04-12 08:53:49 +02:00
|
|
|
if (stmt->portalname == NULL)
|
|
|
|
tag = "CLOSE CURSOR ALL";
|
|
|
|
else
|
|
|
|
tag = "CLOSE CURSOR";
|
|
|
|
}
|
2003-05-02 22:54:36 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case T_FetchStmt:
|
|
|
|
{
|
|
|
|
FetchStmt *stmt = (FetchStmt *) parsetree;
|
|
|
|
|
|
|
|
tag = (stmt->ismove) ? "MOVE" : "FETCH";
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_CreateDomainStmt:
|
|
|
|
tag = "CREATE DOMAIN";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_CreateSchemaStmt:
|
|
|
|
tag = "CREATE SCHEMA";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_CreateStmt:
|
|
|
|
tag = "CREATE TABLE";
|
|
|
|
break;
|
|
|
|
|
2004-06-18 08:14:31 +02:00
|
|
|
case T_CreateTableSpaceStmt:
|
|
|
|
tag = "CREATE TABLESPACE";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_DropTableSpaceStmt:
|
|
|
|
tag = "DROP TABLESPACE";
|
|
|
|
break;
|
|
|
|
|
2010-01-05 22:54:00 +01:00
|
|
|
case T_AlterTableSpaceOptionsStmt:
|
|
|
|
tag = "ALTER TABLESPACE";
|
|
|
|
break;
|
|
|
|
|
2011-02-08 22:08:41 +01:00
|
|
|
case T_CreateExtensionStmt:
|
|
|
|
tag = "CREATE EXTENSION";
|
|
|
|
break;
|
|
|
|
|
2011-02-12 03:25:20 +01:00
|
|
|
case T_AlterExtensionStmt:
|
|
|
|
tag = "ALTER EXTENSION";
|
|
|
|
break;
|
|
|
|
|
2011-02-10 23:36:44 +01:00
|
|
|
case T_AlterExtensionContentsStmt:
|
2011-02-09 17:55:32 +01:00
|
|
|
tag = "ALTER EXTENSION";
|
|
|
|
break;
|
|
|
|
|
2008-12-19 17:25:19 +01:00
|
|
|
case T_CreateFdwStmt:
|
|
|
|
tag = "CREATE FOREIGN DATA WRAPPER";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_AlterFdwStmt:
|
|
|
|
tag = "ALTER FOREIGN DATA WRAPPER";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_CreateForeignServerStmt:
|
|
|
|
tag = "CREATE SERVER";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_AlterForeignServerStmt:
|
|
|
|
tag = "ALTER SERVER";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_CreateUserMappingStmt:
|
|
|
|
tag = "CREATE USER MAPPING";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_AlterUserMappingStmt:
|
|
|
|
tag = "ALTER USER MAPPING";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_DropUserMappingStmt:
|
|
|
|
tag = "DROP USER MAPPING";
|
|
|
|
break;
|
|
|
|
|
2011-01-02 05:48:11 +01:00
|
|
|
case T_CreateForeignTableStmt:
|
|
|
|
tag = "CREATE FOREIGN TABLE";
|
|
|
|
break;
|
|
|
|
|
2003-05-02 22:54:36 +02:00
|
|
|
case T_DropStmt:
|
|
|
|
switch (((DropStmt *) parsetree)->removeType)
|
|
|
|
{
|
2003-06-27 16:45:32 +02:00
|
|
|
case OBJECT_TABLE:
|
2003-05-02 22:54:36 +02:00
|
|
|
tag = "DROP TABLE";
|
|
|
|
break;
|
2003-06-27 16:45:32 +02:00
|
|
|
case OBJECT_SEQUENCE:
|
2003-05-02 22:54:36 +02:00
|
|
|
tag = "DROP SEQUENCE";
|
|
|
|
break;
|
2003-06-27 16:45:32 +02:00
|
|
|
case OBJECT_VIEW:
|
2003-05-02 22:54:36 +02:00
|
|
|
tag = "DROP VIEW";
|
|
|
|
break;
|
2003-06-27 16:45:32 +02:00
|
|
|
case OBJECT_INDEX:
|
2003-05-02 22:54:36 +02:00
|
|
|
tag = "DROP INDEX";
|
|
|
|
break;
|
2003-06-27 16:45:32 +02:00
|
|
|
case OBJECT_TYPE:
|
2003-05-02 22:54:36 +02:00
|
|
|
tag = "DROP TYPE";
|
|
|
|
break;
|
2003-06-27 16:45:32 +02:00
|
|
|
case OBJECT_DOMAIN:
|
2003-05-02 22:54:36 +02:00
|
|
|
tag = "DROP DOMAIN";
|
|
|
|
break;
|
2011-02-12 14:54:13 +01:00
|
|
|
case OBJECT_COLLATION:
|
|
|
|
tag = "DROP COLLATION";
|
|
|
|
break;
|
2003-06-27 16:45:32 +02:00
|
|
|
case OBJECT_CONVERSION:
|
2003-05-02 22:54:36 +02:00
|
|
|
tag = "DROP CONVERSION";
|
|
|
|
break;
|
2003-06-27 16:45:32 +02:00
|
|
|
case OBJECT_SCHEMA:
|
2003-05-02 22:54:36 +02:00
|
|
|
tag = "DROP SCHEMA";
|
|
|
|
break;
|
2007-08-21 03:11:32 +02:00
|
|
|
case OBJECT_TSPARSER:
|
|
|
|
tag = "DROP TEXT SEARCH PARSER";
|
|
|
|
break;
|
|
|
|
case OBJECT_TSDICTIONARY:
|
|
|
|
tag = "DROP TEXT SEARCH DICTIONARY";
|
|
|
|
break;
|
|
|
|
case OBJECT_TSTEMPLATE:
|
|
|
|
tag = "DROP TEXT SEARCH TEMPLATE";
|
|
|
|
break;
|
|
|
|
case OBJECT_TSCONFIGURATION:
|
|
|
|
tag = "DROP TEXT SEARCH CONFIGURATION";
|
|
|
|
break;
|
2011-01-02 05:48:11 +01:00
|
|
|
case OBJECT_FOREIGN_TABLE:
|
|
|
|
tag = "DROP FOREIGN TABLE";
|
|
|
|
break;
|
2011-02-08 22:08:41 +01:00
|
|
|
case OBJECT_EXTENSION:
|
|
|
|
tag = "DROP EXTENSION";
|
|
|
|
break;
|
2011-11-18 03:31:29 +01:00
|
|
|
case OBJECT_FUNCTION:
|
|
|
|
tag = "DROP FUNCTION";
|
|
|
|
break;
|
|
|
|
case OBJECT_AGGREGATE:
|
|
|
|
tag = "DROP AGGREGATE";
|
|
|
|
break;
|
|
|
|
case OBJECT_OPERATOR:
|
|
|
|
tag = "DROP OPERATOR";
|
|
|
|
break;
|
|
|
|
case OBJECT_LANGUAGE:
|
|
|
|
tag = "DROP LANGUAGE";
|
|
|
|
break;
|
|
|
|
case OBJECT_CAST:
|
|
|
|
tag = "DROP CAST";
|
|
|
|
break;
|
|
|
|
case OBJECT_TRIGGER:
|
|
|
|
tag = "DROP TRIGGER";
|
|
|
|
break;
|
|
|
|
case OBJECT_RULE:
|
|
|
|
tag = "DROP RULE";
|
|
|
|
break;
|
|
|
|
case OBJECT_FDW:
|
|
|
|
tag = "DROP FOREIGN DATA WRAPPER";
|
|
|
|
break;
|
|
|
|
case OBJECT_FOREIGN_SERVER:
|
|
|
|
tag = "DROP SERVER";
|
|
|
|
break;
|
|
|
|
case OBJECT_OPCLASS:
|
|
|
|
tag = "DROP OPERATOR CLASS";
|
|
|
|
break;
|
|
|
|
case OBJECT_OPFAMILY:
|
|
|
|
tag = "DROP OPERATOR FAMILY";
|
|
|
|
break;
|
2003-05-02 22:54:36 +02:00
|
|
|
default:
|
|
|
|
tag = "???";
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_TruncateStmt:
|
|
|
|
tag = "TRUNCATE TABLE";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_CommentStmt:
|
|
|
|
tag = "COMMENT";
|
|
|
|
break;
|
|
|
|
|
2010-09-28 02:55:27 +02:00
|
|
|
case T_SecLabelStmt:
|
|
|
|
tag = "SECURITY LABEL";
|
|
|
|
break;
|
|
|
|
|
2003-05-02 22:54:36 +02:00
|
|
|
case T_CopyStmt:
|
|
|
|
tag = "COPY";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_RenameStmt:
|
2011-02-09 17:55:32 +01:00
|
|
|
tag = AlterObjectTypeCommandTag(((RenameStmt *) parsetree)->renameType);
|
2003-05-02 22:54:36 +02:00
|
|
|
break;
|
|
|
|
|
2005-08-01 06:03:59 +02:00
|
|
|
case T_AlterObjectSchemaStmt:
|
2011-02-09 17:55:32 +01:00
|
|
|
tag = AlterObjectTypeCommandTag(((AlterObjectSchemaStmt *) parsetree)->objectType);
|
2005-08-01 06:03:59 +02:00
|
|
|
break;
|
|
|
|
|
2004-06-25 23:55:59 +02:00
|
|
|
case T_AlterOwnerStmt:
|
2011-02-09 17:55:32 +01:00
|
|
|
tag = AlterObjectTypeCommandTag(((AlterOwnerStmt *) parsetree)->objectType);
|
2004-06-25 23:55:59 +02:00
|
|
|
break;
|
|
|
|
|
2003-05-02 22:54:36 +02:00
|
|
|
case T_AlterTableStmt:
|
2011-02-09 17:55:32 +01:00
|
|
|
tag = AlterObjectTypeCommandTag(((AlterTableStmt *) parsetree)->relkind);
|
2004-08-20 06:29:33 +02:00
|
|
|
break;
|
2005-03-14 01:19:37 +01:00
|
|
|
|
2003-05-02 22:54:36 +02:00
|
|
|
case T_AlterDomainStmt:
|
|
|
|
tag = "ALTER DOMAIN";
|
|
|
|
break;
|
|
|
|
|
2005-03-14 01:19:37 +01:00
|
|
|
case T_AlterFunctionStmt:
|
|
|
|
tag = "ALTER FUNCTION";
|
|
|
|
break;
|
|
|
|
|
2003-05-02 22:54:36 +02:00
|
|
|
case T_GrantStmt:
|
|
|
|
{
|
|
|
|
GrantStmt *stmt = (GrantStmt *) parsetree;
|
|
|
|
|
|
|
|
tag = (stmt->is_grant) ? "GRANT" : "REVOKE";
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
case T_GrantRoleStmt:
|
|
|
|
{
|
2005-10-15 04:49:52 +02:00
|
|
|
GrantRoleStmt *stmt = (GrantRoleStmt *) parsetree;
|
2005-06-28 07:09:14 +02:00
|
|
|
|
|
|
|
tag = (stmt->is_grant) ? "GRANT ROLE" : "REVOKE ROLE";
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2009-10-05 21:24:49 +02:00
|
|
|
case T_AlterDefaultPrivilegesStmt:
|
|
|
|
tag = "ALTER DEFAULT PRIVILEGES";
|
|
|
|
break;
|
|
|
|
|
2003-05-02 22:54:36 +02:00
|
|
|
case T_DefineStmt:
|
|
|
|
switch (((DefineStmt *) parsetree)->kind)
|
|
|
|
{
|
2003-06-27 16:45:32 +02:00
|
|
|
case OBJECT_AGGREGATE:
|
2003-05-02 22:54:36 +02:00
|
|
|
tag = "CREATE AGGREGATE";
|
|
|
|
break;
|
2003-06-27 16:45:32 +02:00
|
|
|
case OBJECT_OPERATOR:
|
2003-05-02 22:54:36 +02:00
|
|
|
tag = "CREATE OPERATOR";
|
|
|
|
break;
|
2003-06-27 16:45:32 +02:00
|
|
|
case OBJECT_TYPE:
|
2003-05-02 22:54:36 +02:00
|
|
|
tag = "CREATE TYPE";
|
|
|
|
break;
|
2007-08-21 03:11:32 +02:00
|
|
|
case OBJECT_TSPARSER:
|
|
|
|
tag = "CREATE TEXT SEARCH PARSER";
|
|
|
|
break;
|
|
|
|
case OBJECT_TSDICTIONARY:
|
|
|
|
tag = "CREATE TEXT SEARCH DICTIONARY";
|
|
|
|
break;
|
|
|
|
case OBJECT_TSTEMPLATE:
|
|
|
|
tag = "CREATE TEXT SEARCH TEMPLATE";
|
|
|
|
break;
|
|
|
|
case OBJECT_TSCONFIGURATION:
|
|
|
|
tag = "CREATE TEXT SEARCH CONFIGURATION";
|
|
|
|
break;
|
2011-02-12 14:54:13 +01:00
|
|
|
case OBJECT_COLLATION:
|
|
|
|
tag = "CREATE COLLATION";
|
|
|
|
break;
|
2003-05-02 22:54:36 +02:00
|
|
|
default:
|
|
|
|
tag = "???";
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_CompositeTypeStmt:
|
|
|
|
tag = "CREATE TYPE";
|
|
|
|
break;
|
|
|
|
|
2007-04-02 05:49:42 +02:00
|
|
|
case T_CreateEnumStmt:
|
|
|
|
tag = "CREATE TYPE";
|
|
|
|
break;
|
|
|
|
|
2011-11-03 12:16:28 +01:00
|
|
|
case T_CreateRangeStmt:
|
|
|
|
tag = "CREATE TYPE";
|
|
|
|
break;
|
|
|
|
|
2010-10-25 05:04:37 +02:00
|
|
|
case T_AlterEnumStmt:
|
|
|
|
tag = "ALTER TYPE";
|
|
|
|
break;
|
|
|
|
|
2003-05-02 22:54:36 +02:00
|
|
|
case T_ViewStmt:
|
|
|
|
tag = "CREATE VIEW";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_CreateFunctionStmt:
|
|
|
|
tag = "CREATE FUNCTION";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_IndexStmt:
|
|
|
|
tag = "CREATE INDEX";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_RuleStmt:
|
|
|
|
tag = "CREATE RULE";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_CreateSeqStmt:
|
|
|
|
tag = "CREATE SEQUENCE";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_AlterSeqStmt:
|
|
|
|
tag = "ALTER SEQUENCE";
|
|
|
|
break;
|
|
|
|
|
2009-09-23 01:43:43 +02:00
|
|
|
case T_DoStmt:
|
|
|
|
tag = "DO";
|
|
|
|
break;
|
|
|
|
|
2003-05-02 22:54:36 +02:00
|
|
|
case T_CreatedbStmt:
|
|
|
|
tag = "CREATE DATABASE";
|
|
|
|
break;
|
|
|
|
|
2005-07-31 19:19:22 +02:00
|
|
|
case T_AlterDatabaseStmt:
|
|
|
|
tag = "ALTER DATABASE";
|
|
|
|
break;
|
|
|
|
|
2003-05-02 22:54:36 +02:00
|
|
|
case T_AlterDatabaseSetStmt:
|
|
|
|
tag = "ALTER DATABASE";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_DropdbStmt:
|
|
|
|
tag = "DROP DATABASE";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_NotifyStmt:
|
|
|
|
tag = "NOTIFY";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_ListenStmt:
|
|
|
|
tag = "LISTEN";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_UnlistenStmt:
|
|
|
|
tag = "UNLISTEN";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_LoadStmt:
|
|
|
|
tag = "LOAD";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_ClusterStmt:
|
|
|
|
tag = "CLUSTER";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_VacuumStmt:
|
2009-11-16 22:32:07 +01:00
|
|
|
if (((VacuumStmt *) parsetree)->options & VACOPT_VACUUM)
|
2003-05-02 22:54:36 +02:00
|
|
|
tag = "VACUUM";
|
|
|
|
else
|
|
|
|
tag = "ANALYZE";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_ExplainStmt:
|
|
|
|
tag = "EXPLAIN";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_VariableSetStmt:
|
2007-09-03 20:46:30 +02:00
|
|
|
switch (((VariableSetStmt *) parsetree)->kind)
|
|
|
|
{
|
|
|
|
case VAR_SET_VALUE:
|
|
|
|
case VAR_SET_CURRENT:
|
|
|
|
case VAR_SET_DEFAULT:
|
|
|
|
case VAR_SET_MULTI:
|
|
|
|
tag = "SET";
|
|
|
|
break;
|
|
|
|
case VAR_RESET:
|
|
|
|
case VAR_RESET_ALL:
|
|
|
|
tag = "RESET";
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
tag = "???";
|
|
|
|
}
|
2003-05-02 22:54:36 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case T_VariableShowStmt:
|
|
|
|
tag = "SHOW";
|
|
|
|
break;
|
|
|
|
|
2007-04-26 18:13:15 +02:00
|
|
|
case T_DiscardStmt:
|
2007-09-03 20:46:30 +02:00
|
|
|
switch (((DiscardStmt *) parsetree)->target)
|
|
|
|
{
|
2007-04-26 18:13:15 +02:00
|
|
|
case DISCARD_ALL:
|
|
|
|
tag = "DISCARD ALL";
|
|
|
|
break;
|
|
|
|
case DISCARD_PLANS:
|
|
|
|
tag = "DISCARD PLANS";
|
|
|
|
break;
|
|
|
|
case DISCARD_TEMP:
|
|
|
|
tag = "DISCARD TEMP";
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
tag = "???";
|
2007-04-12 08:53:49 +02:00
|
|
|
}
|
2003-05-02 22:54:36 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case T_CreateTrigStmt:
|
|
|
|
tag = "CREATE TRIGGER";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_CreatePLangStmt:
|
|
|
|
tag = "CREATE LANGUAGE";
|
|
|
|
break;
|
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
case T_CreateRoleStmt:
|
|
|
|
tag = "CREATE ROLE";
|
2003-05-02 22:54:36 +02:00
|
|
|
break;
|
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
case T_AlterRoleStmt:
|
|
|
|
tag = "ALTER ROLE";
|
2003-05-02 22:54:36 +02:00
|
|
|
break;
|
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
case T_AlterRoleSetStmt:
|
|
|
|
tag = "ALTER ROLE";
|
2003-05-02 22:54:36 +02:00
|
|
|
break;
|
|
|
|
|
2005-06-28 07:09:14 +02:00
|
|
|
case T_DropRoleStmt:
|
|
|
|
tag = "DROP ROLE";
|
2003-05-02 22:54:36 +02:00
|
|
|
break;
|
|
|
|
|
2005-11-21 13:49:33 +01:00
|
|
|
case T_DropOwnedStmt:
|
|
|
|
tag = "DROP OWNED";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_ReassignOwnedStmt:
|
|
|
|
tag = "REASSIGN OWNED";
|
|
|
|
break;
|
|
|
|
|
2003-05-02 22:54:36 +02:00
|
|
|
case T_LockStmt:
|
|
|
|
tag = "LOCK TABLE";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_ConstraintsSetStmt:
|
|
|
|
tag = "SET CONSTRAINTS";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_CheckPointStmt:
|
|
|
|
tag = "CHECKPOINT";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_ReindexStmt:
|
|
|
|
tag = "REINDEX";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_CreateConversionStmt:
|
|
|
|
tag = "CREATE CONVERSION";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_CreateCastStmt:
|
|
|
|
tag = "CREATE CAST";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_CreateOpClassStmt:
|
|
|
|
tag = "CREATE OPERATOR CLASS";
|
|
|
|
break;
|
|
|
|
|
2007-01-23 06:07:18 +01:00
|
|
|
case T_CreateOpFamilyStmt:
|
|
|
|
tag = "CREATE OPERATOR FAMILY";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_AlterOpFamilyStmt:
|
|
|
|
tag = "ALTER OPERATOR FAMILY";
|
|
|
|
break;
|
|
|
|
|
2007-08-21 03:11:32 +02:00
|
|
|
case T_AlterTSDictionaryStmt:
|
|
|
|
tag = "ALTER TEXT SEARCH DICTIONARY";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_AlterTSConfigurationStmt:
|
|
|
|
tag = "ALTER TEXT SEARCH CONFIGURATION";
|
|
|
|
break;
|
|
|
|
|
2003-05-02 22:54:36 +02:00
|
|
|
case T_PrepareStmt:
|
|
|
|
tag = "PREPARE";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_ExecuteStmt:
|
|
|
|
tag = "EXECUTE";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_DeallocateStmt:
|
2007-04-12 08:53:49 +02:00
|
|
|
{
|
|
|
|
DeallocateStmt *stmt = (DeallocateStmt *) parsetree;
|
2007-11-15 22:14:46 +01:00
|
|
|
|
2007-04-12 08:53:49 +02:00
|
|
|
if (stmt->name == NULL)
|
|
|
|
tag = "DEALLOCATE ALL";
|
|
|
|
else
|
|
|
|
tag = "DEALLOCATE";
|
|
|
|
}
|
2003-05-02 22:54:36 +02:00
|
|
|
break;
|
|
|
|
|
2007-11-15 22:14:46 +01:00
|
|
|
/* already-planned queries */
|
2007-02-20 18:32:18 +01:00
|
|
|
case T_PlannedStmt:
|
|
|
|
{
|
|
|
|
PlannedStmt *stmt = (PlannedStmt *) parsetree;
|
2006-09-08 00:52:01 +02:00
|
|
|
|
2007-02-20 18:32:18 +01:00
|
|
|
switch (stmt->commandType)
|
|
|
|
{
|
|
|
|
case CMD_SELECT:
|
2007-11-15 22:14:46 +01:00
|
|
|
|
2007-02-20 18:32:18 +01:00
|
|
|
/*
|
|
|
|
* We take a little extra care here so that the result
|
|
|
|
* will be useful for complaints about read-only
|
|
|
|
* statements
|
|
|
|
*/
|
2007-04-28 00:05:49 +02:00
|
|
|
if (stmt->utilityStmt != NULL)
|
|
|
|
{
|
|
|
|
Assert(IsA(stmt->utilityStmt, DeclareCursorStmt));
|
|
|
|
tag = "DECLARE CURSOR";
|
|
|
|
}
|
|
|
|
else if (stmt->intoClause != NULL)
|
2007-02-20 18:32:18 +01:00
|
|
|
tag = "SELECT INTO";
|
|
|
|
else if (stmt->rowMarks != NIL)
|
|
|
|
{
|
Re-implement EvalPlanQual processing to improve its performance and eliminate
a lot of strange behaviors that occurred in join cases. We now identify the
"current" row for every joined relation in UPDATE, DELETE, and SELECT FOR
UPDATE/SHARE queries. If an EvalPlanQual recheck is necessary, we jam the
appropriate row into each scan node in the rechecking plan, forcing it to emit
only that one row. The former behavior could rescan the whole of each joined
relation for each recheck, which was terrible for performance, and what's much
worse could result in duplicated output tuples.
Also, the original implementation of EvalPlanQual could not re-use the recheck
execution tree --- it had to go through a full executor init and shutdown for
every row to be tested. To avoid this overhead, I've associated a special
runtime Param with each LockRows or ModifyTable plan node, and arranged to
make every scan node below such a node depend on that Param. Thus, by
signaling a change in that Param, the EPQ machinery can just rescan the
already-built test plan.
This patch also adds a prohibition on set-returning functions in the
targetlist of SELECT FOR UPDATE/SHARE. This is needed to avoid the
duplicate-output-tuple problem. It seems fairly reasonable since the
other restrictions on SELECT FOR UPDATE are meant to ensure that there
is a unique correspondence between source tuples and result tuples,
which an output SRF destroys as much as anything else does.
2009-10-26 03:26:45 +01:00
|
|
|
/* not 100% but probably close enough */
|
|
|
|
if (((PlanRowMark *) linitial(stmt->rowMarks))->markType == ROW_MARK_EXCLUSIVE)
|
2007-02-20 18:32:18 +01:00
|
|
|
tag = "SELECT FOR UPDATE";
|
|
|
|
else
|
|
|
|
tag = "SELECT FOR SHARE";
|
|
|
|
}
|
|
|
|
else
|
|
|
|
tag = "SELECT";
|
|
|
|
break;
|
|
|
|
case CMD_UPDATE:
|
|
|
|
tag = "UPDATE";
|
|
|
|
break;
|
|
|
|
case CMD_INSERT:
|
|
|
|
tag = "INSERT";
|
|
|
|
break;
|
|
|
|
case CMD_DELETE:
|
|
|
|
tag = "DELETE";
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
elog(WARNING, "unrecognized commandType: %d",
|
|
|
|
(int) stmt->commandType);
|
|
|
|
tag = "???";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
2005-10-15 04:49:52 +02:00
|
|
|
|
2007-11-15 22:14:46 +01:00
|
|
|
/* parsed-and-rewritten-but-not-planned queries */
|
2007-02-20 18:32:18 +01:00
|
|
|
case T_Query:
|
2005-04-28 23:47:18 +02:00
|
|
|
{
|
2007-11-15 22:14:46 +01:00
|
|
|
Query *stmt = (Query *) parsetree;
|
2007-02-20 18:32:18 +01:00
|
|
|
|
|
|
|
switch (stmt->commandType)
|
|
|
|
{
|
|
|
|
case CMD_SELECT:
|
2007-11-15 22:14:46 +01:00
|
|
|
|
2007-02-20 18:32:18 +01:00
|
|
|
/*
|
|
|
|
* We take a little extra care here so that the result
|
|
|
|
* will be useful for complaints about read-only
|
|
|
|
* statements
|
|
|
|
*/
|
2007-04-28 00:05:49 +02:00
|
|
|
if (stmt->utilityStmt != NULL)
|
|
|
|
{
|
|
|
|
Assert(IsA(stmt->utilityStmt, DeclareCursorStmt));
|
|
|
|
tag = "DECLARE CURSOR";
|
|
|
|
}
|
|
|
|
else if (stmt->intoClause != NULL)
|
2007-02-20 18:32:18 +01:00
|
|
|
tag = "SELECT INTO";
|
|
|
|
else if (stmt->rowMarks != NIL)
|
|
|
|
{
|
Re-implement EvalPlanQual processing to improve its performance and eliminate
a lot of strange behaviors that occurred in join cases. We now identify the
"current" row for every joined relation in UPDATE, DELETE, and SELECT FOR
UPDATE/SHARE queries. If an EvalPlanQual recheck is necessary, we jam the
appropriate row into each scan node in the rechecking plan, forcing it to emit
only that one row. The former behavior could rescan the whole of each joined
relation for each recheck, which was terrible for performance, and what's much
worse could result in duplicated output tuples.
Also, the original implementation of EvalPlanQual could not re-use the recheck
execution tree --- it had to go through a full executor init and shutdown for
every row to be tested. To avoid this overhead, I've associated a special
runtime Param with each LockRows or ModifyTable plan node, and arranged to
make every scan node below such a node depend on that Param. Thus, by
signaling a change in that Param, the EPQ machinery can just rescan the
already-built test plan.
This patch also adds a prohibition on set-returning functions in the
targetlist of SELECT FOR UPDATE/SHARE. This is needed to avoid the
duplicate-output-tuple problem. It seems fairly reasonable since the
other restrictions on SELECT FOR UPDATE are meant to ensure that there
is a unique correspondence between source tuples and result tuples,
which an output SRF destroys as much as anything else does.
2009-10-26 03:26:45 +01:00
|
|
|
/* not 100% but probably close enough */
|
2007-02-20 18:32:18 +01:00
|
|
|
if (((RowMarkClause *) linitial(stmt->rowMarks))->forUpdate)
|
|
|
|
tag = "SELECT FOR UPDATE";
|
|
|
|
else
|
|
|
|
tag = "SELECT FOR SHARE";
|
|
|
|
}
|
|
|
|
else
|
|
|
|
tag = "SELECT";
|
|
|
|
break;
|
|
|
|
case CMD_UPDATE:
|
|
|
|
tag = "UPDATE";
|
|
|
|
break;
|
|
|
|
case CMD_INSERT:
|
|
|
|
tag = "INSERT";
|
|
|
|
break;
|
|
|
|
case CMD_DELETE:
|
|
|
|
tag = "DELETE";
|
|
|
|
break;
|
|
|
|
case CMD_UTILITY:
|
|
|
|
tag = CreateCommandTag(stmt->utilityStmt);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
elog(WARNING, "unrecognized commandType: %d",
|
|
|
|
(int) stmt->commandType);
|
|
|
|
tag = "???";
|
|
|
|
break;
|
|
|
|
}
|
2005-04-28 23:47:18 +02:00
|
|
|
}
|
2004-09-13 22:10:13 +02:00
|
|
|
break;
|
2007-02-20 18:32:18 +01:00
|
|
|
|
2004-09-13 22:10:13 +02:00
|
|
|
default:
|
2007-02-20 18:32:18 +01:00
|
|
|
elog(WARNING, "unrecognized node type: %d",
|
|
|
|
(int) nodeTag(parsetree));
|
2004-09-13 22:10:13 +02:00
|
|
|
tag = "???";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return tag;
|
|
|
|
}
|
2006-09-08 00:52:01 +02:00
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* GetCommandLogLevel
|
|
|
|
* utility to get the minimum log_statement level for a command,
|
2007-02-20 18:32:18 +01:00
|
|
|
* given either a raw (un-analyzed) parsetree or a planned query.
|
2006-09-08 00:52:01 +02:00
|
|
|
*
|
2007-02-20 18:32:18 +01:00
|
|
|
* This must handle all command types, but since the vast majority
|
2006-09-08 00:52:01 +02:00
|
|
|
* of 'em are utility commands, it seems sensible to keep it here.
|
|
|
|
*/
|
|
|
|
LogStmtLevel
|
|
|
|
GetCommandLogLevel(Node *parsetree)
|
|
|
|
{
|
|
|
|
LogStmtLevel lev;
|
|
|
|
|
|
|
|
switch (nodeTag(parsetree))
|
|
|
|
{
|
2007-11-15 22:14:46 +01:00
|
|
|
/* raw plannable queries */
|
2006-09-08 00:52:01 +02:00
|
|
|
case T_InsertStmt:
|
|
|
|
case T_DeleteStmt:
|
|
|
|
case T_UpdateStmt:
|
|
|
|
lev = LOGSTMT_MOD;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_SelectStmt:
|
2007-04-28 00:05:49 +02:00
|
|
|
if (((SelectStmt *) parsetree)->intoClause)
|
2006-10-04 02:30:14 +02:00
|
|
|
lev = LOGSTMT_DDL; /* CREATE AS, SELECT INTO */
|
2006-09-08 00:52:01 +02:00
|
|
|
else
|
|
|
|
lev = LOGSTMT_ALL;
|
|
|
|
break;
|
|
|
|
|
2007-11-15 22:14:46 +01:00
|
|
|
/* utility statements --- same whether raw or cooked */
|
2006-09-08 00:52:01 +02:00
|
|
|
case T_TransactionStmt:
|
|
|
|
lev = LOGSTMT_ALL;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_DeclareCursorStmt:
|
|
|
|
lev = LOGSTMT_ALL;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_ClosePortalStmt:
|
|
|
|
lev = LOGSTMT_ALL;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_FetchStmt:
|
|
|
|
lev = LOGSTMT_ALL;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_CreateSchemaStmt:
|
|
|
|
lev = LOGSTMT_DDL;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_CreateStmt:
|
2011-01-02 05:48:11 +01:00
|
|
|
case T_CreateForeignTableStmt:
|
2006-09-08 00:52:01 +02:00
|
|
|
lev = LOGSTMT_DDL;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_CreateTableSpaceStmt:
|
|
|
|
case T_DropTableSpaceStmt:
|
2010-01-05 22:54:00 +01:00
|
|
|
case T_AlterTableSpaceOptionsStmt:
|
|
|
|
lev = LOGSTMT_DDL;
|
|
|
|
break;
|
|
|
|
|
2011-02-08 22:08:41 +01:00
|
|
|
case T_CreateExtensionStmt:
|
2011-02-12 03:25:20 +01:00
|
|
|
case T_AlterExtensionStmt:
|
2011-02-10 23:36:44 +01:00
|
|
|
case T_AlterExtensionContentsStmt:
|
2011-02-08 22:08:41 +01:00
|
|
|
lev = LOGSTMT_DDL;
|
|
|
|
break;
|
|
|
|
|
2008-12-19 17:25:19 +01:00
|
|
|
case T_CreateFdwStmt:
|
|
|
|
case T_AlterFdwStmt:
|
|
|
|
case T_CreateForeignServerStmt:
|
|
|
|
case T_AlterForeignServerStmt:
|
|
|
|
case T_CreateUserMappingStmt:
|
|
|
|
case T_AlterUserMappingStmt:
|
|
|
|
case T_DropUserMappingStmt:
|
|
|
|
lev = LOGSTMT_DDL;
|
|
|
|
break;
|
|
|
|
|
2006-09-08 00:52:01 +02:00
|
|
|
case T_DropStmt:
|
|
|
|
lev = LOGSTMT_DDL;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_TruncateStmt:
|
|
|
|
lev = LOGSTMT_MOD;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_CommentStmt:
|
|
|
|
lev = LOGSTMT_DDL;
|
|
|
|
break;
|
2010-09-28 02:55:27 +02:00
|
|
|
|
|
|
|
case T_SecLabelStmt:
|
|
|
|
lev = LOGSTMT_DDL;
|
|
|
|
break;
|
2006-09-08 00:52:01 +02:00
|
|
|
|
|
|
|
case T_CopyStmt:
|
|
|
|
if (((CopyStmt *) parsetree)->is_from)
|
|
|
|
lev = LOGSTMT_MOD;
|
|
|
|
else
|
|
|
|
lev = LOGSTMT_ALL;
|
|
|
|
break;
|
|
|
|
|
2008-10-10 15:48:05 +02:00
|
|
|
case T_PrepareStmt:
|
|
|
|
{
|
|
|
|
PrepareStmt *stmt = (PrepareStmt *) parsetree;
|
|
|
|
|
|
|
|
/* Look through a PREPARE to the contained stmt */
|
|
|
|
lev = GetCommandLogLevel(stmt->query);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_ExecuteStmt:
|
|
|
|
{
|
|
|
|
ExecuteStmt *stmt = (ExecuteStmt *) parsetree;
|
|
|
|
PreparedStatement *ps;
|
|
|
|
|
|
|
|
/* Look through an EXECUTE to the referenced stmt */
|
|
|
|
ps = FetchPreparedStatement(stmt->name, false);
|
|
|
|
if (ps)
|
|
|
|
lev = GetCommandLogLevel(ps->plansource->raw_parse_tree);
|
|
|
|
else
|
|
|
|
lev = LOGSTMT_ALL;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_DeallocateStmt:
|
|
|
|
lev = LOGSTMT_ALL;
|
|
|
|
break;
|
|
|
|
|
2006-09-08 00:52:01 +02:00
|
|
|
case T_RenameStmt:
|
|
|
|
lev = LOGSTMT_DDL;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_AlterObjectSchemaStmt:
|
|
|
|
lev = LOGSTMT_DDL;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_AlterOwnerStmt:
|
|
|
|
lev = LOGSTMT_DDL;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_AlterTableStmt:
|
|
|
|
lev = LOGSTMT_DDL;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_AlterDomainStmt:
|
|
|
|
lev = LOGSTMT_DDL;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_GrantStmt:
|
|
|
|
lev = LOGSTMT_DDL;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_GrantRoleStmt:
|
|
|
|
lev = LOGSTMT_DDL;
|
|
|
|
break;
|
|
|
|
|
2009-10-05 21:24:49 +02:00
|
|
|
case T_AlterDefaultPrivilegesStmt:
|
|
|
|
lev = LOGSTMT_DDL;
|
|
|
|
break;
|
|
|
|
|
2006-09-08 00:52:01 +02:00
|
|
|
case T_DefineStmt:
|
|
|
|
lev = LOGSTMT_DDL;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_CompositeTypeStmt:
|
|
|
|
lev = LOGSTMT_DDL;
|
|
|
|
break;
|
|
|
|
|
2007-04-02 05:49:42 +02:00
|
|
|
case T_CreateEnumStmt:
|
|
|
|
lev = LOGSTMT_DDL;
|
|
|
|
break;
|
|
|
|
|
2011-11-03 12:16:28 +01:00
|
|
|
case T_CreateRangeStmt:
|
|
|
|
lev = LOGSTMT_DDL;
|
|
|
|
break;
|
|
|
|
|
2010-10-25 05:04:37 +02:00
|
|
|
case T_AlterEnumStmt:
|
|
|
|
lev = LOGSTMT_DDL;
|
|
|
|
break;
|
|
|
|
|
2006-09-08 00:52:01 +02:00
|
|
|
case T_ViewStmt:
|
|
|
|
lev = LOGSTMT_DDL;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_CreateFunctionStmt:
|
|
|
|
lev = LOGSTMT_DDL;
|
|
|
|
break;
|
|
|
|
|
2008-10-10 15:48:05 +02:00
|
|
|
case T_AlterFunctionStmt:
|
|
|
|
lev = LOGSTMT_DDL;
|
|
|
|
break;
|
|
|
|
|
2006-09-08 00:52:01 +02:00
|
|
|
case T_IndexStmt:
|
|
|
|
lev = LOGSTMT_DDL;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_RuleStmt:
|
|
|
|
lev = LOGSTMT_DDL;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_CreateSeqStmt:
|
|
|
|
lev = LOGSTMT_DDL;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_AlterSeqStmt:
|
|
|
|
lev = LOGSTMT_DDL;
|
|
|
|
break;
|
|
|
|
|
2009-09-23 01:43:43 +02:00
|
|
|
case T_DoStmt:
|
|
|
|
lev = LOGSTMT_ALL;
|
|
|
|
break;
|
|
|
|
|
2006-09-08 00:52:01 +02:00
|
|
|
case T_CreatedbStmt:
|
|
|
|
lev = LOGSTMT_DDL;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_AlterDatabaseStmt:
|
|
|
|
lev = LOGSTMT_DDL;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_AlterDatabaseSetStmt:
|
|
|
|
lev = LOGSTMT_DDL;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_DropdbStmt:
|
|
|
|
lev = LOGSTMT_DDL;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_NotifyStmt:
|
|
|
|
lev = LOGSTMT_ALL;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_ListenStmt:
|
|
|
|
lev = LOGSTMT_ALL;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_UnlistenStmt:
|
|
|
|
lev = LOGSTMT_ALL;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_LoadStmt:
|
|
|
|
lev = LOGSTMT_ALL;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_ClusterStmt:
|
|
|
|
lev = LOGSTMT_DDL;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_VacuumStmt:
|
|
|
|
lev = LOGSTMT_ALL;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_ExplainStmt:
|
|
|
|
{
|
2006-10-04 02:30:14 +02:00
|
|
|
ExplainStmt *stmt = (ExplainStmt *) parsetree;
|
2010-02-26 03:01:40 +01:00
|
|
|
bool analyze = false;
|
|
|
|
ListCell *lc;
|
2006-09-08 00:52:01 +02:00
|
|
|
|
|
|
|
/* Look through an EXPLAIN ANALYZE to the contained stmt */
|
2009-07-27 01:34:18 +02:00
|
|
|
foreach(lc, stmt->options)
|
|
|
|
{
|
2010-02-26 03:01:40 +01:00
|
|
|
DefElem *opt = (DefElem *) lfirst(lc);
|
2009-07-27 01:34:18 +02:00
|
|
|
|
|
|
|
if (strcmp(opt->defname, "analyze") == 0)
|
|
|
|
analyze = defGetBoolean(opt);
|
2010-01-15 23:36:35 +01:00
|
|
|
/* don't "break", as explain.c will use the last value */
|
2009-07-27 01:34:18 +02:00
|
|
|
}
|
|
|
|
if (analyze)
|
2007-03-13 01:33:44 +01:00
|
|
|
return GetCommandLogLevel(stmt->query);
|
2009-07-27 01:34:18 +02:00
|
|
|
|
2006-09-08 00:52:01 +02:00
|
|
|
/* Plain EXPLAIN isn't so interesting */
|
|
|
|
lev = LOGSTMT_ALL;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_VariableSetStmt:
|
|
|
|
lev = LOGSTMT_ALL;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_VariableShowStmt:
|
|
|
|
lev = LOGSTMT_ALL;
|
|
|
|
break;
|
|
|
|
|
2008-10-10 15:48:05 +02:00
|
|
|
case T_DiscardStmt:
|
|
|
|
lev = LOGSTMT_ALL;
|
|
|
|
break;
|
|
|
|
|
2006-09-08 00:52:01 +02:00
|
|
|
case T_CreateTrigStmt:
|
|
|
|
lev = LOGSTMT_DDL;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_CreatePLangStmt:
|
|
|
|
lev = LOGSTMT_DDL;
|
|
|
|
break;
|
|
|
|
|
2008-10-10 15:48:05 +02:00
|
|
|
case T_CreateDomainStmt:
|
|
|
|
lev = LOGSTMT_DDL;
|
|
|
|
break;
|
|
|
|
|
2006-09-08 00:52:01 +02:00
|
|
|
case T_CreateRoleStmt:
|
|
|
|
lev = LOGSTMT_DDL;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_AlterRoleStmt:
|
|
|
|
lev = LOGSTMT_DDL;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_AlterRoleSetStmt:
|
|
|
|
lev = LOGSTMT_DDL;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_DropRoleStmt:
|
|
|
|
lev = LOGSTMT_DDL;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_DropOwnedStmt:
|
|
|
|
lev = LOGSTMT_DDL;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_ReassignOwnedStmt:
|
|
|
|
lev = LOGSTMT_DDL;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_LockStmt:
|
|
|
|
lev = LOGSTMT_ALL;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_ConstraintsSetStmt:
|
|
|
|
lev = LOGSTMT_ALL;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_CheckPointStmt:
|
|
|
|
lev = LOGSTMT_ALL;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_ReindexStmt:
|
2006-10-04 02:30:14 +02:00
|
|
|
lev = LOGSTMT_ALL; /* should this be DDL? */
|
2006-09-08 00:52:01 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case T_CreateConversionStmt:
|
|
|
|
lev = LOGSTMT_DDL;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_CreateCastStmt:
|
|
|
|
lev = LOGSTMT_DDL;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_CreateOpClassStmt:
|
|
|
|
lev = LOGSTMT_DDL;
|
|
|
|
break;
|
|
|
|
|
2007-01-23 06:07:18 +01:00
|
|
|
case T_CreateOpFamilyStmt:
|
|
|
|
lev = LOGSTMT_DDL;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_AlterOpFamilyStmt:
|
|
|
|
lev = LOGSTMT_DDL;
|
|
|
|
break;
|
|
|
|
|
2007-08-21 03:11:32 +02:00
|
|
|
case T_AlterTSDictionaryStmt:
|
|
|
|
lev = LOGSTMT_DDL;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_AlterTSConfigurationStmt:
|
|
|
|
lev = LOGSTMT_DDL;
|
|
|
|
break;
|
2006-09-08 00:52:01 +02:00
|
|
|
|
2007-11-15 22:14:46 +01:00
|
|
|
/* already-planned queries */
|
2007-02-20 18:32:18 +01:00
|
|
|
case T_PlannedStmt:
|
|
|
|
{
|
|
|
|
PlannedStmt *stmt = (PlannedStmt *) parsetree;
|
2006-10-04 02:30:14 +02:00
|
|
|
|
2007-02-20 18:32:18 +01:00
|
|
|
switch (stmt->commandType)
|
|
|
|
{
|
|
|
|
case CMD_SELECT:
|
2007-04-28 00:05:49 +02:00
|
|
|
if (stmt->intoClause != NULL)
|
2007-02-20 18:32:18 +01:00
|
|
|
lev = LOGSTMT_DDL; /* CREATE AS, SELECT INTO */
|
|
|
|
else
|
2007-04-28 00:05:49 +02:00
|
|
|
lev = LOGSTMT_ALL; /* SELECT or DECLARE CURSOR */
|
2007-02-20 18:32:18 +01:00
|
|
|
break;
|
2006-09-08 00:52:01 +02:00
|
|
|
|
2007-02-20 18:32:18 +01:00
|
|
|
case CMD_UPDATE:
|
|
|
|
case CMD_INSERT:
|
|
|
|
case CMD_DELETE:
|
|
|
|
lev = LOGSTMT_MOD;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
elog(WARNING, "unrecognized commandType: %d",
|
|
|
|
(int) stmt->commandType);
|
|
|
|
lev = LOGSTMT_ALL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2006-09-08 00:52:01 +02:00
|
|
|
break;
|
|
|
|
|
2007-11-15 22:14:46 +01:00
|
|
|
/* parsed-and-rewritten-but-not-planned queries */
|
2007-02-20 18:32:18 +01:00
|
|
|
case T_Query:
|
|
|
|
{
|
2007-11-15 22:14:46 +01:00
|
|
|
Query *stmt = (Query *) parsetree;
|
2006-09-08 00:52:01 +02:00
|
|
|
|
2007-02-20 18:32:18 +01:00
|
|
|
switch (stmt->commandType)
|
|
|
|
{
|
|
|
|
case CMD_SELECT:
|
2007-04-28 00:05:49 +02:00
|
|
|
if (stmt->intoClause != NULL)
|
2007-02-20 18:32:18 +01:00
|
|
|
lev = LOGSTMT_DDL; /* CREATE AS, SELECT INTO */
|
|
|
|
else
|
2007-04-28 00:05:49 +02:00
|
|
|
lev = LOGSTMT_ALL; /* SELECT or DECLARE CURSOR */
|
2007-02-20 18:32:18 +01:00
|
|
|
break;
|
2006-09-08 00:52:01 +02:00
|
|
|
|
2007-02-20 18:32:18 +01:00
|
|
|
case CMD_UPDATE:
|
|
|
|
case CMD_INSERT:
|
|
|
|
case CMD_DELETE:
|
|
|
|
lev = LOGSTMT_MOD;
|
|
|
|
break;
|
2006-09-08 00:52:01 +02:00
|
|
|
|
2007-02-20 18:32:18 +01:00
|
|
|
case CMD_UTILITY:
|
|
|
|
lev = GetCommandLogLevel(stmt->utilityStmt);
|
|
|
|
break;
|
2006-09-08 00:52:01 +02:00
|
|
|
|
2007-02-20 18:32:18 +01:00
|
|
|
default:
|
|
|
|
elog(WARNING, "unrecognized commandType: %d",
|
|
|
|
(int) stmt->commandType);
|
|
|
|
lev = LOGSTMT_ALL;
|
|
|
|
break;
|
|
|
|
}
|
2006-09-08 00:52:01 +02:00
|
|
|
|
2007-02-20 18:32:18 +01:00
|
|
|
}
|
2006-09-08 00:52:01 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2007-02-20 18:32:18 +01:00
|
|
|
elog(WARNING, "unrecognized node type: %d",
|
|
|
|
(int) nodeTag(parsetree));
|
2006-09-08 00:52:01 +02:00
|
|
|
lev = LOGSTMT_ALL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return lev;
|
|
|
|
}
|