Arrange that no database accesses are attempted during parser() --- this

took some rejiggering of typename and ACL parsing, as well as moving
parse_analyze call out of parser().  Restructure postgres.c processing
so that parse analysis and rewrite are skipped when in abort-transaction
state.  Only COMMIT and ABORT statements will be processed beyond the raw
parser() phase.  This addresses problem of parser failing with database access
errors while in aborted state (see pghackers discussions around 7/28/00).
Also fix some bugs with COMMIT/ABORT statements appearing in the middle of
a single query input string.
Function, operator, and aggregate arguments/results can now use full
TypeName production, in particular foo[] for array types.
DROP OPERATOR and COMMENT ON OPERATOR were broken for unary operators.
Allow CREATE AGGREGATE to accept unquoted numeric constants for initcond.
This commit is contained in:
Tom Lane 2000-10-07 00:58:23 +00:00
parent 4837270be9
commit fbd26d6984
20 changed files with 773 additions and 755 deletions

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/catalog/pg_proc.c,v 1.48 2000/08/21 20:55:31 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/catalog/pg_proc.c,v 1.49 2000/10/07 00:58:15 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -93,35 +93,34 @@ ProcedureCreate(char *procedureName,
MemSet(typev, 0, FUNC_MAX_ARGS * sizeof(Oid));
foreach(x, argList)
{
Value *t = lfirst(x);
TypeName *t = (TypeName *) lfirst(x);
char *typnam = TypeNameToInternalName(t);
if (parameterCount >= FUNC_MAX_ARGS)
elog(ERROR, "Procedures cannot take more than %d arguments",
FUNC_MAX_ARGS);
if (strcmp(strVal(t), "opaque") == 0)
if (strcmp(typnam, "opaque") == 0)
{
if (languageObjectId == SQLlanguageId)
elog(ERROR, "ProcedureCreate: sql functions cannot take type \"opaque\"");
toid = 0;
toid = InvalidOid;
}
else
{
toid = TypeGet(strVal(t), &defined);
toid = TypeGet(typnam, &defined);
if (!OidIsValid(toid))
{
elog(ERROR, "ProcedureCreate: arg type '%s' is not defined",
strVal(t));
}
typnam);
if (!defined)
{
elog(NOTICE, "ProcedureCreate: arg type '%s' is only a shell",
strVal(t));
}
typnam);
}
if (t->setof)
elog(ERROR, "ProcedureCreate: functions cannot accept set arguments");
typev[parameterCount++] = toid;
}
@ -178,7 +177,7 @@ ProcedureCreate(char *procedureName,
{
if (languageObjectId == SQLlanguageId)
elog(ERROR, "ProcedureCreate: sql functions cannot return type \"opaque\"");
typeObjectId = 0;
typeObjectId = InvalidOid;
}
else
{
@ -194,10 +193,8 @@ ProcedureCreate(char *procedureName,
returnTypeName);
}
else if (!defined)
{
elog(NOTICE, "ProcedureCreate: return type '%s' is only a shell",
returnTypeName);
}
}
/*
@ -212,7 +209,6 @@ ProcedureCreate(char *procedureName,
elog(ERROR, "method %s already an attribute of type %s",
procedureName, strVal(lfirst(argList)));
/*
* If this is a postquel procedure, we parse it here in order to be
* sure that it contains no syntax errors. We should store the plan

View File

@ -25,6 +25,8 @@
#include "commands/comment.h"
#include "miscadmin.h"
#include "parser/parse.h"
#include "parser/parse_expr.h"
#include "parser/parse_func.h"
#include "rewrite/rewriteRemove.h"
#include "utils/acl.h"
#include "utils/fmgroids.h"
@ -46,7 +48,7 @@ static void CommentAttribute(char *relation, char *attrib, char *comment);
static void CommentDatabase(char *database, char *comment);
static void CommentRewrite(char *rule, char *comment);
static void CommentType(char *type, char *comment);
static void CommentAggregate(char *aggregate, char *aggtype, char *comment);
static void CommentAggregate(char *aggregate, List *arguments, char *comment);
static void CommentProc(char *function, List *arguments, char *comment);
static void CommentOperator(char *opname, List *arguments, char *comment);
static void CommentTrigger(char *trigger, char *relation, char *comments);
@ -92,7 +94,7 @@ CommentObject(int objtype, char *objname, char *objproperty,
CommentType(objname, comment);
break;
case (AGGREGATE):
CommentAggregate(objname, objproperty, comment);
CommentAggregate(objname, objlist, comment);
break;
case (FUNCTION):
CommentProc(objname, objlist, comment);
@ -544,9 +546,10 @@ CommentType(char *type, char *comment)
*/
static void
CommentAggregate(char *aggregate, char *argument, char *comment)
CommentAggregate(char *aggregate, List *arguments, char *comment)
{
TypeName *aggtype = (TypeName *) lfirst(arguments);
char *aggtypename = NULL;
HeapTuple aggtuple;
Oid baseoid,
oid;
@ -554,11 +557,12 @@ CommentAggregate(char *aggregate, char *argument, char *comment)
/*** First, attempt to determine the base aggregate oid ***/
if (argument)
if (aggtype)
{
baseoid = TypeGet(argument, &defined);
aggtypename = TypeNameToInternalName(aggtype);
baseoid = TypeGet(aggtypename, &defined);
if (!OidIsValid(baseoid))
elog(ERROR, "aggregate type '%s' does not exist", argument);
elog(ERROR, "type '%s' does not exist", aggtypename);
}
else
baseoid = 0;
@ -568,10 +572,10 @@ CommentAggregate(char *aggregate, char *argument, char *comment)
#ifndef NO_SECURITY
if (!pg_aggr_ownercheck(GetUserId(), aggregate, baseoid))
{
if (argument)
if (aggtypename)
{
elog(ERROR, "you are not permitted to comment on aggregate '%s' %s '%s'",
aggregate, "with type", argument);
aggregate, "with type", aggtypename);
}
else
{
@ -587,10 +591,10 @@ CommentAggregate(char *aggregate, char *argument, char *comment)
ObjectIdGetDatum(baseoid), 0, 0);
if (!HeapTupleIsValid(aggtuple))
{
if (argument)
if (aggtypename)
{
elog(ERROR, "aggregate type '%s' does not exist for aggregate '%s'",
argument, aggregate);
aggtypename, aggregate);
}
else
elog(ERROR, "aggregate '%s' does not exist", aggregate);
@ -622,7 +626,6 @@ CommentProc(char *function, List *arguments, char *comment)
functuple;
Oid oid,
argoids[FUNC_MAX_ARGS];
char *argument;
int i,
argcount;
@ -635,18 +638,20 @@ CommentProc(char *function, List *arguments, char *comment)
FUNC_MAX_ARGS);
for (i = 0; i < argcount; i++)
{
argument = strVal(lfirst(arguments));
TypeName *t = (TypeName *) lfirst(arguments);
char *typnam = TypeNameToInternalName(t);
arguments = lnext(arguments);
if (strcmp(argument, "opaque") == 0)
argoids[i] = 0;
if (strcmp(typnam, "opaque") == 0)
argoids[i] = InvalidOid;
else
{
argtuple = SearchSysCacheTuple(TYPENAME,
PointerGetDatum(argument),
PointerGetDatum(typnam),
0, 0, 0);
if (!HeapTupleIsValid(argtuple))
elog(ERROR, "function argument type '%s' does not exist",
argument);
elog(ERROR, "CommentProc: type '%s' not found", typnam);
argoids[i] = argtuple->t_data->t_oid;
}
}
@ -664,10 +669,9 @@ CommentProc(char *function, List *arguments, char *comment)
functuple = SearchSysCacheTuple(PROCNAME, PointerGetDatum(function),
Int32GetDatum(argcount),
PointerGetDatum(argoids), 0);
if (!HeapTupleIsValid(functuple))
elog(ERROR, "function '%s' with the supplied %s does not exist",
function, "argument list");
func_error("CommentProc", function, argcount, argoids, NULL);
oid = functuple->t_data->t_oid;
/*** Call CreateComments() to create/drop the comments ***/
@ -691,23 +695,24 @@ CommentProc(char *function, List *arguments, char *comment)
static void
CommentOperator(char *opername, List *arguments, char *comment)
{
TypeName *typenode1 = (TypeName *) lfirst(arguments);
TypeName *typenode2 = (TypeName *) lsecond(arguments);
char oprtype = 0,
*lefttype = NULL,
*righttype = NULL;
Form_pg_operator data;
HeapTuple optuple;
Oid oid,
leftoid = InvalidOid,
rightoid = InvalidOid;
bool defined;
char oprtype = 0,
*lefttype = NULL,
*righttype = NULL;
/*** Initialize our left and right argument types ***/
if (lfirst(arguments) != NULL)
lefttype = strVal(lfirst(arguments));
if (lsecond(arguments) != NULL)
righttype = strVal(lsecond(arguments));
if (typenode1 != NULL)
lefttype = TypeNameToInternalName(typenode1);
if (typenode2 != NULL)
righttype = TypeNameToInternalName(typenode2);
/*** Attempt to fetch the left oid, if specified ***/
@ -732,9 +737,9 @@ CommentOperator(char *opername, List *arguments, char *comment)
if (OidIsValid(leftoid) && (OidIsValid(rightoid)))
oprtype = 'b';
else if (OidIsValid(leftoid))
oprtype = 'l';
else if (OidIsValid(rightoid))
oprtype = 'r';
else if (OidIsValid(rightoid))
oprtype = 'l';
else
elog(ERROR, "operator '%s' is of an illegal type'", opername);
@ -769,7 +774,6 @@ CommentOperator(char *opername, List *arguments, char *comment)
/*** Call CreateComments() to create/drop the comments ***/
CreateComments(oid, comment);
}
/*------------------------------------------------------------------

View File

@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/define.c,v 1.46 2000/07/22 03:34:26 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/commands/define.c,v 1.47 2000/10/07 00:58:16 tgl Exp $
*
* DESCRIPTION
* The "DefineFoo" routines take the parse tree and pick out the
@ -49,6 +49,7 @@
#include "commands/defrem.h"
#include "fmgr.h"
#include "optimizer/cost.h"
#include "parser/parse_expr.h"
#include "tcop/dest.h"
#include "utils/builtins.h"
#include "utils/syscache.h"
@ -83,27 +84,15 @@ case_translate_language_name(const char *input, char *output)
static void
compute_return_type(const Node *returnType,
compute_return_type(TypeName *returnType,
char **prorettype_p, bool *returnsSet_p)
{
/*---------------------------------------------------------------------------
Examine the "returns" clause returnType of the CREATE FUNCTION statement
and return information about it as **prorettype_p and **returnsSet.
and return information about it as *prorettype_p and *returnsSet.
----------------------------------------------------------------------------*/
if (nodeTag(returnType) == T_TypeName)
{
/* a set of values */
TypeName *setType = (TypeName *) returnType;
*prorettype_p = setType->name;
*returnsSet_p = setType->setof;
}
else
{
/* singleton */
*prorettype_p = strVal(returnType);
*returnsSet_p = false;
}
*prorettype_p = TypeNameToInternalName(returnType);
*returnsSet_p = returnType->setof;
}
@ -214,7 +203,7 @@ interpret_AS_clause(const char *languageName, const List *as,
*prosrc_str_p = strVal(lfirst(as));
*probin_str_p = "-";
if (lnext(as) != NULL)
if (lnext(as) != NIL)
elog(ERROR, "CREATE FUNCTION: only one AS item needed for %s language",
languageName);
}
@ -231,27 +220,23 @@ void
CreateFunction(ProcedureStmt *stmt, CommandDest dest)
{
char *probin_str;
/* pathname of executable file that executes this function, if any */
char *prosrc_str;
/* SQL that executes this function, if any */
char *prorettype;
/* Type of return value (or member of set of values) from function */
char languageName[NAMEDATALEN];
char languageName[NAMEDATALEN];
/*
* name of language of function, with case adjusted: "C", "newC",
* "internal", "newinternal", "sql", etc.
*/
bool returnsSet;
/* The function returns a set of values, as opposed to a singleton. */
bool lanisPL = false;
/*
* The following are optional user-supplied attributes of the
* function.
@ -263,8 +248,12 @@ CreateFunction(ProcedureStmt *stmt, CommandDest dest)
bool canCache,
isStrict;
/* Convert language name to canonical case */
case_translate_language_name(stmt->language, languageName);
/*
* Apply appropriate security checks depending on language.
*/
if (strcmp(languageName, "C") == 0 ||
strcmp(languageName, "newC") == 0 ||
strcmp(languageName, "internal") == 0 ||
@ -301,7 +290,7 @@ CreateFunction(ProcedureStmt *stmt, CommandDest dest)
/* Check that this language is a PL */
languageStruct = (Form_pg_language) GETSTRUCT(languageTuple);
if (!(languageStruct->lanispl))
if (!languageStruct->lanispl)
elog(ERROR,
"Language '%s' isn't defined as PL", languageName);
@ -316,11 +305,15 @@ CreateFunction(ProcedureStmt *stmt, CommandDest dest)
"language.",
languageName);
}
lanisPL = true;
}
compute_return_type(stmt->returnType, &prorettype, &returnsSet);
/*
* Convert remaining parameters of CREATE to form wanted by
* ProcedureCreate.
*/
Assert(IsA(stmt->returnType, TypeName));
compute_return_type((TypeName *) stmt->returnType,
&prorettype, &returnsSet);
compute_full_attributes(stmt->withClause,
&byte_pct, &perbyte_cpu, &percall_cpu,
@ -345,7 +338,7 @@ CreateFunction(ProcedureStmt *stmt, CommandDest dest)
perbyte_cpu,
percall_cpu,
outin_ratio,
stmt->defArgs,
stmt->argTypes,
dest);
}
@ -389,49 +382,43 @@ DefineOperator(char *oprName,
{
DefElem *defel = (DefElem *) lfirst(pl);
if (!strcasecmp(defel->defname, "leftarg"))
if (strcasecmp(defel->defname, "leftarg") == 0)
{
if ((nodeTag(defel->arg) == T_TypeName)
&& (((TypeName *) defel->arg)->setof))
elog(ERROR, "setof type not implemented for leftarg");
typeName1 = defGetString(defel);
if (typeName1 == NULL)
elog(ERROR, "type for leftarg is malformed.");
if (IsA(defel->arg, TypeName)
&& ((TypeName *) defel->arg)->setof)
elog(ERROR, "setof type not implemented for leftarg");
}
else if (!strcasecmp(defel->defname, "rightarg"))
else if (strcasecmp(defel->defname, "rightarg") == 0)
{
if ((nodeTag(defel->arg) == T_TypeName)
&& (((TypeName *) defel->arg)->setof))
elog(ERROR, "setof type not implemented for rightarg");
typeName2 = defGetString(defel);
if (typeName2 == NULL)
elog(ERROR, "type for rightarg is malformed.");
if (IsA(defel->arg, TypeName)
&& ((TypeName *) defel->arg)->setof)
elog(ERROR, "setof type not implemented for rightarg");
}
else if (!strcasecmp(defel->defname, "procedure"))
else if (strcasecmp(defel->defname, "procedure") == 0)
functionName = defGetString(defel);
else if (!strcasecmp(defel->defname, "precedence"))
else if (strcasecmp(defel->defname, "precedence") == 0)
{
/* NOT IMPLEMENTED (never worked in v4.2) */
elog(NOTICE, "CREATE OPERATOR: precedence not implemented");
}
else if (!strcasecmp(defel->defname, "associativity"))
else if (strcasecmp(defel->defname, "associativity") == 0)
{
/* NOT IMPLEMENTED (never worked in v4.2) */
elog(NOTICE, "CREATE OPERATOR: associativity not implemented");
}
else if (!strcasecmp(defel->defname, "commutator"))
else if (strcasecmp(defel->defname, "commutator") == 0)
commutatorName = defGetString(defel);
else if (!strcasecmp(defel->defname, "negator"))
else if (strcasecmp(defel->defname, "negator") == 0)
negatorName = defGetString(defel);
else if (!strcasecmp(defel->defname, "restrict"))
else if (strcasecmp(defel->defname, "restrict") == 0)
restrictionName = defGetString(defel);
else if (!strcasecmp(defel->defname, "join"))
else if (strcasecmp(defel->defname, "join") == 0)
joinName = defGetString(defel);
else if (!strcasecmp(defel->defname, "hashes"))
else if (strcasecmp(defel->defname, "hashes") == 0)
canHash = TRUE;
else if (!strcasecmp(defel->defname, "sort1"))
else if (strcasecmp(defel->defname, "sort1") == 0)
{
/* ----------------
* XXX ( ... [ , sort1 = oprname ] [ , sort2 = oprname ] ... )
@ -441,7 +428,7 @@ DefineOperator(char *oprName,
*/
sortName1 = defGetString(defel);
}
else if (!strcasecmp(defel->defname, "sort2"))
else if (strcasecmp(defel->defname, "sort2") == 0)
sortName2 = defGetString(defel);
else
{
@ -562,72 +549,73 @@ DefineType(char *typeName, List *parameters)
char delimiter = DEFAULT_TYPDELIM;
char *shadow_type;
List *pl;
char alignment = 'i';/* default alignment */
char storage = 'p'; /* default storage in TOAST */
char alignment = 'i'; /* default alignment */
char storage = 'p'; /* default storage in TOAST */
/*
* Type names can only be 15 characters long, so that the shadow type
* can be created using the 16th character as necessary.
* Type names must be one character shorter than other names,
* allowing room to create the corresponding array type name with
* prepended "_".
*/
if (strlen(typeName) >= (NAMEDATALEN - 1))
if (strlen(typeName) > (NAMEDATALEN - 2))
{
elog(ERROR, "DefineType: type names must be %d characters or less",
NAMEDATALEN - 1);
NAMEDATALEN - 2);
}
foreach(pl, parameters)
{
DefElem *defel = (DefElem *) lfirst(pl);
if (!strcasecmp(defel->defname, "internallength"))
if (strcasecmp(defel->defname, "internallength") == 0)
internalLength = defGetTypeLength(defel);
else if (!strcasecmp(defel->defname, "externallength"))
else if (strcasecmp(defel->defname, "externallength") == 0)
externalLength = defGetTypeLength(defel);
else if (!strcasecmp(defel->defname, "input"))
else if (strcasecmp(defel->defname, "input") == 0)
inputName = defGetString(defel);
else if (!strcasecmp(defel->defname, "output"))
else if (strcasecmp(defel->defname, "output") == 0)
outputName = defGetString(defel);
else if (!strcasecmp(defel->defname, "send"))
else if (strcasecmp(defel->defname, "send") == 0)
sendName = defGetString(defel);
else if (!strcasecmp(defel->defname, "delimiter"))
else if (strcasecmp(defel->defname, "delimiter") == 0)
{
char *p = defGetString(defel);
delimiter = p[0];
}
else if (!strcasecmp(defel->defname, "receive"))
else if (strcasecmp(defel->defname, "receive") == 0)
receiveName = defGetString(defel);
else if (!strcasecmp(defel->defname, "element"))
else if (strcasecmp(defel->defname, "element") == 0)
elemName = defGetString(defel);
else if (!strcasecmp(defel->defname, "default"))
else if (strcasecmp(defel->defname, "default") == 0)
defaultValue = defGetString(defel);
else if (!strcasecmp(defel->defname, "passedbyvalue"))
else if (strcasecmp(defel->defname, "passedbyvalue") == 0)
byValue = true;
else if (!strcasecmp(defel->defname, "alignment"))
else if (strcasecmp(defel->defname, "alignment") == 0)
{
char *a = defGetString(defel);
if (!strcasecmp(a, "double"))
if (strcasecmp(a, "double") == 0)
alignment = 'd';
else if (!strcasecmp(a, "int4"))
else if (strcasecmp(a, "int4") == 0)
alignment = 'i';
else
{
elog(ERROR, "DefineType: \"%s\" alignment not recognized",
elog(ERROR, "DefineType: \"%s\" alignment not recognized",
a);
}
}
else if (!strcasecmp(defel->defname, "storage"))
else if (strcasecmp(defel->defname, "storage") == 0)
{
char *a = defGetString(defel);
if (!strcasecmp(a, "plain"))
if (strcasecmp(a, "plain") == 0)
storage = 'p';
else if (!strcasecmp(a, "external"))
else if (strcasecmp(a, "external") == 0)
storage = 'e';
else if (!strcasecmp(a, "extended"))
else if (strcasecmp(a, "extended") == 0)
storage = 'x';
else if (!strcasecmp(a, "main"))
else if (strcasecmp(a, "main") == 0)
storage = 'm';
else
{
@ -674,8 +662,8 @@ DefineType(char *typeName, List *parameters)
storage); /* TOAST strategy */
/* ----------------
* When we create a true type (as opposed to a complex type)
* we need to have an shadow array entry for it in pg_type as well.
* When we create a base type (as opposed to a complex type)
* we need to have an array entry for it in pg_type as well.
* ----------------
*/
shadow_type = makeArrayTypeName(typeName);
@ -702,19 +690,32 @@ DefineType(char *typeName, List *parameters)
static char *
defGetString(DefElem *def)
{
char *string;
if (def->arg == NULL)
elog(ERROR, "Define: \"%s\" requires a parameter",
def->defname);
switch (nodeTag(def->arg))
{
case T_Integer:
{
char *str = palloc(32);
if (nodeTag(def->arg) == T_String)
string = strVal(def->arg);
else if (nodeTag(def->arg) == T_TypeName)
string = ((TypeName *) def->arg)->name;
else
string = NULL;
#if 0
elog(ERROR, "Define: \"%s\" = what?", def->defname);
#endif
return string;
snprintf(str, 32, "%ld", (long) intVal(def->arg));
return str;
}
case T_Float:
/* T_Float values are kept in string form, so this type cheat
* works (and doesn't risk losing precision)
*/
return strVal(def->arg);
case T_String:
return strVal(def->arg);
case T_TypeName:
return TypeNameToInternalName((TypeName *) def->arg);
default:
elog(ERROR, "Define: cannot interpret argument of \"%s\"",
def->defname);
}
return NULL; /* keep compiler quiet */
}
static double
@ -739,15 +740,32 @@ defGetNumeric(DefElem *def)
static int
defGetTypeLength(DefElem *def)
{
if (nodeTag(def->arg) == T_Integer)
return intVal(def->arg);
else if (nodeTag(def->arg) == T_String &&
!strcasecmp(strVal(def->arg), "variable"))
return -1; /* variable length */
else if (nodeTag(def->arg) == T_TypeName &&
!strcasecmp(((TypeName *)(def->arg))->name, "variable"))
return -1;
elog(ERROR, "Define: \"%s\" = what?", def->defname);
return -1;
if (def->arg == NULL)
elog(ERROR, "Define: \"%s\" requires a parameter",
def->defname);
switch (nodeTag(def->arg))
{
case T_Integer:
return intVal(def->arg);
case T_Float:
elog(ERROR, "Define: \"%s\" requires an integral value",
def->defname);
break;
case T_String:
if (strcasecmp(strVal(def->arg), "variable") == 0)
return -1; /* variable length */
break;
case T_TypeName:
/* cope if grammar chooses to believe "variable" is a typename */
if (strcasecmp(TypeNameToInternalName((TypeName *) def->arg),
"variable") == 0)
return -1; /* variable length */
break;
default:
elog(ERROR, "Define: cannot interpret argument of \"%s\"",
def->defname);
}
elog(ERROR, "Define: invalid argument for \"%s\"",
def->defname);
return 0; /* keep compiler quiet */
}

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/Attic/remove.c,v 1.52 2000/09/12 16:48:55 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/commands/Attic/remove.c,v 1.53 2000/10/07 00:58:16 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -22,6 +22,7 @@
#include "commands/comment.h"
#include "commands/defrem.h"
#include "miscadmin.h"
#include "parser/parse_expr.h"
#include "parser/parse_func.h"
#include "utils/acl.h"
#include "utils/syscache.h"
@ -39,8 +40,8 @@
*/
void
RemoveOperator(char *operatorName, /* operator name */
char *typeName1, /* first type name */
char *typeName2) /* optional second type name */
char *typeName1, /* left argument type name */
char *typeName2) /* right argument type name */
{
Relation relation;
HeapTuple tup;
@ -53,28 +54,22 @@ RemoveOperator(char *operatorName, /* operator name */
{
typeId1 = TypeGet(typeName1, &defined);
if (!OidIsValid(typeId1))
{
elog(ERROR, "RemoveOperator: type '%s' does not exist", typeName1);
return;
}
}
if (typeName2)
{
typeId2 = TypeGet(typeName2, &defined);
if (!OidIsValid(typeId2))
{
elog(ERROR, "RemoveOperator: type '%s' does not exist", typeName2);
return;
}
}
if (OidIsValid(typeId1) && OidIsValid(typeId2))
oprtype = 'b';
else if (OidIsValid(typeId1))
oprtype = 'l';
else
oprtype = 'r';
else
oprtype = 'l';
relation = heap_openr(OperatorRelationName, RowExclusiveLock);
@ -94,7 +89,6 @@ RemoveOperator(char *operatorName, /* operator name */
operatorName);
#endif
/*** Delete any comments associated with this operator ***/
DeleteComments(tup->t_data->t_oid);
@ -308,13 +302,12 @@ RemoveType(char *typeName) /* type name to be removed */
*/
void
RemoveFunction(char *functionName, /* function name to be removed */
int nargs,
List *argNameList /* list of TypeNames */ )
List *argTypes) /* list of TypeName nodes */
{
int nargs = length(argTypes);
Relation relation;
HeapTuple tup;
Oid argList[FUNC_MAX_ARGS];
char *typename;
int i;
if (nargs > FUNC_MAX_ARGS)
@ -323,19 +316,20 @@ RemoveFunction(char *functionName, /* function name to be removed */
MemSet(argList, 0, FUNC_MAX_ARGS * sizeof(Oid));
for (i = 0; i < nargs; i++)
{
typename = strVal(lfirst(argNameList));
argNameList = lnext(argNameList);
TypeName *t = (TypeName *) lfirst(argTypes);
char *typnam = TypeNameToInternalName(t);
if (strcmp(typename, "opaque") == 0)
argList[i] = 0;
argTypes = lnext(argTypes);
if (strcmp(typnam, "opaque") == 0)
argList[i] = InvalidOid;
else
{
tup = SearchSysCacheTuple(TYPENAME,
PointerGetDatum(typename),
PointerGetDatum(typnam),
0, 0, 0);
if (!HeapTupleIsValid(tup))
elog(ERROR, "RemoveFunction: type '%s' not found", typename);
elog(ERROR, "RemoveFunction: type '%s' not found", typnam);
argList[i] = tup->t_data->t_oid;
}
}

View File

@ -15,7 +15,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.124 2000/10/05 19:11:27 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.125 2000/10/07 00:58:16 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -1854,13 +1854,9 @@ _copyChangeACLStmt(ChangeACLStmt *from)
{
ChangeACLStmt *newnode = makeNode(ChangeACLStmt);
if (from->aclitem)
{
newnode->aclitem = (struct AclItem *) palloc(sizeof(struct AclItem));
memcpy(newnode->aclitem, from->aclitem, sizeof(struct AclItem));
}
newnode->modechg = from->modechg;
Node_Copy(from, newnode, relNames);
if (from->aclString)
newnode->aclString = pstrdup(from->aclString);
return newnode;
}
@ -2033,7 +2029,7 @@ _copyProcedureStmt(ProcedureStmt *from)
ProcedureStmt *newnode = makeNode(ProcedureStmt);
newnode->funcname = pstrdup(from->funcname);
Node_Copy(from, newnode, defArgs);
Node_Copy(from, newnode, argTypes);
Node_Copy(from, newnode, returnType);
Node_Copy(from, newnode, withClause);
Node_Copy(from, newnode, as);
@ -2048,7 +2044,7 @@ _copyRemoveAggrStmt(RemoveAggrStmt *from)
RemoveAggrStmt *newnode = makeNode(RemoveAggrStmt);
newnode->aggname = pstrdup(from->aggname);
newnode->aggtype = pstrdup(from->aggtype);
Node_Copy(from, newnode, aggtype);
return newnode;
}

View File

@ -20,7 +20,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.75 2000/10/05 19:11:27 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.76 2000/10/07 00:58:16 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -753,24 +753,10 @@ _equalAlterTableStmt(AlterTableStmt *a, AlterTableStmt *b)
static bool
_equalChangeACLStmt(ChangeACLStmt *a, ChangeACLStmt *b)
{
if (a->aclitem && b->aclitem)
{
if (a->aclitem->ai_id != b->aclitem->ai_id)
return false;
if (a->aclitem->ai_idtype != b->aclitem->ai_idtype)
return false;
if (a->aclitem->ai_mode != b->aclitem->ai_mode)
return false;
}
else
{
if (a->aclitem != b->aclitem)
return false; /* one NULL, one not */
}
if (a->modechg != b->modechg)
return false;
if (!equal(a->relNames, b->relNames))
return false;
if (!equalstr(a->aclString, b->aclString))
return false;
return true;
}
@ -956,7 +942,7 @@ _equalProcedureStmt(ProcedureStmt *a, ProcedureStmt *b)
{
if (!equalstr(a->funcname, b->funcname))
return false;
if (!equal(a->defArgs, b->defArgs))
if (!equal(a->argTypes, b->argTypes))
return false;
if (!equal(a->returnType, b->returnType))
return false;
@ -975,7 +961,7 @@ _equalRemoveAggrStmt(RemoveAggrStmt *a, RemoveAggrStmt *b)
{
if (!equalstr(a->aggname, b->aggname))
return false;
if (!equalstr(a->aggtype, b->aggtype))
if (!equal(a->aggtype, b->aggtype))
return false;
return true;

View File

@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: analyze.c,v 1.160 2000/10/05 19:11:33 tgl Exp $
* $Id: analyze.c,v 1.161 2000/10/07 00:58:17 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -69,49 +69,45 @@ static List *extras_after;
/*
* parse_analyze -
* analyze a list of parse trees and transform them if necessary.
*
* Returns a list of transformed parse trees. Optimizable statements are
* all transformed to Query while the rest stays the same.
* analyze a raw parse tree and transform it to Query form.
*
* The result is a List of Query nodes (we need a list since some commands
* produce multiple Queries). Optimizable statements require considerable
* transformation, while many utility-type statements are simply hung off
* a dummy CMD_UTILITY Query node.
*/
List *
parse_analyze(List *pl, ParseState *parentParseState)
parse_analyze(Node *parseTree, ParseState *parentParseState)
{
List *result = NIL;
ParseState *pstate = make_parsestate(parentParseState);
Query *query;
while (pl != NIL)
extras_before = extras_after = NIL;
query = transformStmt(pstate, parseTree);
release_pstate_resources(pstate);
while (extras_before != NIL)
{
ParseState *pstate = make_parsestate(parentParseState);
Query *parsetree;
extras_before = extras_after = NIL;
parsetree = transformStmt(pstate, lfirst(pl));
result = lappend(result,
transformStmt(pstate, lfirst(extras_before)));
release_pstate_resources(pstate);
while (extras_before != NIL)
{
result = lappend(result,
transformStmt(pstate, lfirst(extras_before)));
release_pstate_resources(pstate);
extras_before = lnext(extras_before);
}
result = lappend(result, parsetree);
while (extras_after != NIL)
{
result = lappend(result,
transformStmt(pstate, lfirst(extras_after)));
release_pstate_resources(pstate);
extras_after = lnext(extras_after);
}
pfree(pstate);
pl = lnext(pl);
extras_before = lnext(extras_before);
}
result = lappend(result, query);
while (extras_after != NIL)
{
result = lappend(result,
transformStmt(pstate, lfirst(extras_after)));
release_pstate_resources(pstate);
extras_after = lnext(extras_after);
}
pfree(pstate);
return result;
}
@ -126,8 +122,7 @@ release_pstate_resources(ParseState *pstate)
/*
* transformStmt -
* transform a Parse tree. If it is an optimizable statement, turn it
* into a Query tree.
* transform a Parse tree into a Query tree.
*/
static Query *
transformStmt(ParseState *pstate, Node *parseTree)
@ -353,7 +348,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
* from a stand-alone SELECT. (Indeed, Postgres up through 6.5 had
* bugs of just that nature...)
*/
selectList = parse_analyze(makeList1(stmt->selectStmt), pstate);
selectList = parse_analyze(stmt->selectStmt, pstate);
Assert(length(selectList) == 1);
selectQuery = (Query *) lfirst(selectList);
@ -1933,7 +1928,7 @@ transformSetOperationTree(ParseState *pstate, Node *node)
*/
save_rtable = pstate->p_rtable;
pstate->p_rtable = NIL;
selectList = parse_analyze(makeList1(stmt), pstate);
selectList = parse_analyze((Node *) stmt, pstate);
pstate->p_rtable = save_rtable;
Assert(length(selectList) == 1);
@ -2752,6 +2747,7 @@ makeFromExpr(List *fromlist, Node *quals)
static void
transformColumnType(ParseState *pstate, ColumnDef *column)
{
TypeName *typename = column->typename;
/*
* If the column doesn't have an explicitly specified typmod, check to
@ -2760,18 +2756,33 @@ transformColumnType(ParseState *pstate, ColumnDef *column)
* Note that we deliberately do NOT look at array or set information
* here; "numeric[]" needs the same default typmod as "numeric".
*/
if (column->typename->typmod == -1)
if (typename->typmod == -1)
{
switch (typeTypeId(typenameType(column->typename->name)))
switch (typeTypeId(typenameType(typename->name)))
{
case BPCHAROID:
case BPCHAROID:
/* "char" -> "char(1)" */
column->typename->typmod = VARHDRSZ + 1;
typename->typmod = VARHDRSZ + 1;
break;
case NUMERICOID:
column->typename->typmod = VARHDRSZ +
typename->typmod = VARHDRSZ +
((NUMERIC_DEFAULT_PRECISION << 16) | NUMERIC_DEFAULT_SCALE);
break;
}
}
/*
* Is this the name of a complex type? If so, implement
* it as a set.
*
* XXX this is a hangover from ancient Berkeley code that probably
* doesn't work anymore anyway.
*/
if (typeTypeRelid(typenameType(typename->name)) != InvalidOid)
{
/* (Eventually add in here that the set can only
* contain one element.)
*/
typename->setof = true;
}
}

View File

@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.194 2000/10/05 19:11:33 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.195 2000/10/07 00:58:17 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@ -59,7 +59,6 @@
extern List *parsetree; /* final parse result is delivered here */
static char saved_relname[NAMEDATALEN]; /* need this for complex attributes */
static bool QueryIsRule = FALSE;
static Oid *param_type_info;
static int pfunc_num_args;
@ -161,7 +160,7 @@ static void doNegateFloat(Value *v);
%type <str> relation_name, copy_file_name, copy_delimiter, copy_null, def_name,
database_name, access_method_clause, access_method, attr_name,
class, index_name, name, func_name, file_name, aggr_argtype
class, index_name, name, func_name, file_name
%type <str> opt_id,
all_Op, MathOp, opt_name,
@ -183,7 +182,7 @@ static void doNegateFloat(Value *v);
def_list, opt_indirection, group_clause, TriggerFuncArgs,
opt_select_limit
%type <typnam> func_arg, func_return
%type <typnam> func_arg, func_return, aggr_argtype
%type <boolean> opt_arg, TriggerForOpt, TriggerForType, OptTemp
@ -1541,7 +1540,7 @@ CreateAsStmt: CREATE OptTemp TABLE relation_name OptUnder OptCreateAs AS Select
n->istemp = $2;
n->into = $4;
if ($5 != NIL)
yyerror("CREATE TABLE/AS SELECT does not support UNDER");
elog(ERROR,"CREATE TABLE/AS SELECT does not support UNDER");
if ($6 != NIL)
mapTargetColumns($6, n->targetList);
$$ = $8;
@ -2009,8 +2008,8 @@ CommentStmt: COMMENT ON comment_type name IS comment_text
CommentStmt *n = makeNode(CommentStmt);
n->objtype = $3;
n->objname = $4;
n->objproperty = $5;
n->objlist = NULL;
n->objproperty = NULL;
n->objlist = makeList1($5);
n->comment = $7;
$$ = (Node *) n;
}
@ -2309,7 +2308,7 @@ grantee: PUBLIC
opt_with_grant: WITH GRANT OPTION
{
yyerror("WITH GRANT OPTION is not supported. Only relation owners can set privileges");
elog(ERROR,"WITH GRANT OPTION is not supported. Only relation owners can set privileges");
}
| /*EMPTY*/
;
@ -2471,7 +2470,7 @@ ProcedureStmt: CREATE FUNCTION func_name func_args
{
ProcedureStmt *n = makeNode(ProcedureStmt);
n->funcname = $3;
n->defArgs = $4;
n->argTypes = $4;
n->returnType = (Node *)$6;
n->withClause = $11;
n->as = $8;
@ -2488,29 +2487,12 @@ func_args: '(' func_args_list ')' { $$ = $2; }
;
func_args_list: func_arg
{ $$ = makeList1(makeString($1->name)); }
{ $$ = makeList1($1); }
| func_args_list ',' func_arg
{ $$ = lappend($1, makeString($3->name)); }
{ $$ = lappend($1, $3); }
;
/* Would be nice to use the full Typename production for these fields,
* but that one sometimes dives into the catalogs looking for valid types.
* Arguments like "opaque" are valid when defining functions,
* so that won't work here. The only thing we give up is array notation,
* which isn't meaningful in this context anyway.
* - thomas 2000-03-25
* The following productions are difficult, since it is difficult to
* distinguish between TokenId and SimpleTypename:
opt_arg TokenId SimpleTypename
{
$$ = $3;
}
| TokenId SimpleTypename
{
$$ = $2;
}
*/
func_arg: opt_arg SimpleTypename
func_arg: opt_arg Typename
{
/* We can catch over-specified arguments here if we want to,
* but for now better to silently swallow typmod, etc.
@ -2518,7 +2500,7 @@ func_arg: opt_arg SimpleTypename
*/
$$ = $2;
}
| SimpleTypename
| Typename
{
$$ = $1;
}
@ -2546,7 +2528,7 @@ func_as: Sconst
{ $$ = makeList2(makeString($1), makeString($3)); }
;
func_return: SimpleTypename
func_return: Typename
{
/* We can catch over-specified arguments here if we want to,
* but for now better to silently swallow typmod, etc.
@ -2554,11 +2536,6 @@ func_return: SimpleTypename
*/
$$ = $1;
}
| SETOF SimpleTypename
{
$$ = $2;
$$->setof = TRUE;
}
;
@ -2599,12 +2576,12 @@ RemoveAggrStmt: DROP AGGREGATE name aggr_argtype
{
RemoveAggrStmt *n = makeNode(RemoveAggrStmt);
n->aggname = $3;
n->aggtype = $4;
n->aggtype = (Node *) $4;
$$ = (Node *)n;
}
;
aggr_argtype: name { $$ = $1; }
aggr_argtype: Typename { $$ = $1; }
| '*' { $$ = NULL; }
;
@ -2628,16 +2605,16 @@ RemoveOperStmt: DROP OPERATOR all_Op '(' oper_argtypes ')'
}
;
oper_argtypes: name
oper_argtypes: Typename
{
elog(ERROR,"parser: argument type missing (use NONE for unary operators)");
}
| name ',' name
{ $$ = makeList2(makeString($1), makeString($3)); }
| NONE ',' name /* left unary */
{ $$ = makeList2(NULL, makeString($3)); }
| name ',' NONE /* right unary */
{ $$ = makeList2(makeString($1), NULL); }
| Typename ',' Typename
{ $$ = makeList2($1, $3); }
| NONE ',' Typename /* left unary */
{ $$ = makeList2(NULL, $3); }
| Typename ',' NONE /* right unary */
{ $$ = makeList2($1, NULL); }
;
@ -3832,23 +3809,6 @@ Typename: SimpleTypename opt_array_bounds
{
$$ = $1;
$$->arrayBounds = $2;
/* Is this the name of a complex type? If so, implement
* it as a set.
*/
if (strcmp(saved_relname, $$->name) == 0)
/* This attr is the same type as the relation
* being defined. The classic example: create
* emp(name=text,mgr=emp)
*/
$$->setof = TRUE;
else if (typeTypeRelid(typenameType($$->name)) != InvalidOid)
/* (Eventually add in here that the set can only
* contain one element.)
*/
$$->setof = TRUE;
else
$$->setof = FALSE;
}
| SETOF SimpleTypename
{
@ -3909,7 +3869,7 @@ Numeric: FLOAT opt_float
| DECIMAL opt_decimal
{
$$ = makeNode(TypeName);
$$->name = xlateSqlType("numeric");
$$->name = xlateSqlType("decimal");
$$->typmod = $2;
}
| DEC opt_decimal
@ -5245,17 +5205,15 @@ update_target_el: ColId opt_indirection '=' a_expr
relation_name: SpecialRuleRelation
{
$$ = $1;
StrNCpy(saved_relname, $1, NAMEDATALEN);
}
| ColId
{
/* disallow refs to variable system tables */
if (strcmp(LogRelationName, $1) == 0
|| strcmp(VariableRelationName, $1) == 0)
|| strcmp(VariableRelationName, $1) == 0)
elog(ERROR,"%s cannot be accessed by users",$1);
else
$$ = $1;
StrNCpy(saved_relname, $1, NAMEDATALEN);
}
;
@ -5298,7 +5256,7 @@ AexprConst: Iconst
n->val.val.str = $1;
$$ = (Node *)n;
}
/* The SimpleTypename rule formerly used Typename,
/* This rule formerly used Typename,
* but that causes reduce conflicts with subscripted column names.
* Now, separate into ConstTypename and ConstInterval,
* to allow implementing the SQL92 syntax for INTERVAL literals.
@ -5383,6 +5341,7 @@ ColId: generic { $$ = $1; }
| TokenId { $$ = $1; }
| INTERVAL { $$ = "interval"; }
| NATIONAL { $$ = "national"; }
| NONE { $$ = "none"; }
| PATH_P { $$ = "path"; }
| SERIAL { $$ = "serial"; }
| TIME { $$ = "time"; }
@ -5595,7 +5554,6 @@ ColLabel: ColId { $$ = $1; }
| NATURAL { $$ = "natural"; }
| NCHAR { $$ = "nchar"; }
| NEW { $$ = "new"; }
| NONE { $$ = "none"; }
| NOT { $$ = "not"; }
| NOTNULL { $$ = "notnull"; }
| NULLIF { $$ = "nullif"; }
@ -5851,7 +5809,6 @@ xlateSqlType(char *name)
void parser_init(Oid *typev, int nargs)
{
saved_relname[0] = '\0';
QueryIsRule = FALSE;
/*
* Keep enough information around to fill out the type of param nodes

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.69 2000/10/05 19:11:33 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.70 2000/10/07 00:58:17 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -375,7 +375,7 @@ transformRangeSubselect(ParseState *pstate, RangeSubselect *r)
save_joinlist = pstate->p_joinlist;
pstate->p_rtable = NIL;
pstate->p_joinlist = NIL;
parsetrees = parse_analyze(makeList1(r->subquery), pstate);
parsetrees = parse_analyze(r->subquery, pstate);
pstate->p_rtable = save_rtable;
pstate->p_joinlist = save_joinlist;

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.85 2000/10/05 19:11:33 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.86 2000/10/07 00:58:17 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -294,8 +294,7 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
break;
}
pstate->p_hasSubLinks = true;
qtrees = parse_analyze(makeList1(sublink->subselect),
pstate);
qtrees = parse_analyze(sublink->subselect, pstate);
if (length(qtrees) != 1)
elog(ERROR, "Bad query in subselect");
qtree = (Query *) lfirst(qtrees);
@ -821,19 +820,21 @@ exprIsLengthCoercion(Node *expr, int32 *coercedTypmod)
static Node *
parser_typecast_constant(Value *expr, TypeName *typename)
{
Const *con;
Type tp;
Datum datum;
Const *con;
char *const_string = NULL;
bool string_palloced = false;
bool isNull = false;
tp = typenameType(TypeNameToInternalName(typename));
switch (nodeTag(expr))
{
case T_Integer:
string_palloced = true;
const_string = DatumGetCString(DirectFunctionCall1(int4out,
Int32GetDatum(expr->val.ival)));
string_palloced = true;
break;
case T_Float:
case T_String:
@ -844,19 +845,9 @@ parser_typecast_constant(Value *expr, TypeName *typename)
break;
default:
elog(ERROR, "Cannot cast this expression to type '%s'",
typename->name);
typeTypeName(tp));
}
if (typename->arrayBounds != NIL)
{
char type_string[NAMEDATALEN + 2];
sprintf(type_string, "_%s", typename->name);
tp = (Type) typenameType(type_string);
}
else
tp = (Type) typenameType(typename->name);
if (isNull)
datum = (Datum) NULL;
else
@ -892,15 +883,7 @@ parser_typecast_expression(ParseState *pstate,
Type tp;
Oid targetType;
if (typename->arrayBounds != NIL)
{
char type_string[NAMEDATALEN + 2];
sprintf(type_string, "_%s", typename->name);
tp = (Type) typenameType(type_string);
}
else
tp = (Type) typenameType(typename->name);
tp = typenameType(TypeNameToInternalName(typename));
targetType = typeTypeId(tp);
if (inputType == InvalidOid)
@ -925,3 +908,26 @@ parser_typecast_expression(ParseState *pstate,
return expr;
}
/*
* Given a TypeName node as returned by the grammar, generate the internal
* name of the corresponding type. Note this does NOT check if the type
* exists or not.
*/
char *
TypeNameToInternalName(TypeName *typename)
{
if (typename->arrayBounds != NIL)
{
/*
* By convention, the name of an array type is the name of its
* element type with "_" prepended.
*/
char *arrayname = palloc(strlen(typename->name) + 2);
sprintf(arrayname, "_%s", typename->name);
return arrayname;
}
else
return typename->name;
}

View File

@ -1,14 +1,20 @@
/*-------------------------------------------------------------------------
*
* parser.c
* Main entry point/driver for PostgreSQL parser
* Main entry point/driver for PostgreSQL grammar
*
* Note that the grammar is not allowed to perform any table access
* (since we need to be able to do basic parsing even while inside an
* aborted transaction). Therefore, the data structures returned by
* the grammar are "raw" parsetrees that still need to be analyzed by
* parse_analyze.
*
*
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parser.c,v 1.46 2000/09/12 21:07:02 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parser.c,v 1.47 2000/10/07 00:58:17 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -16,8 +22,6 @@
#include "postgres.h"
#include "nodes/parsenodes.h"
#include "nodes/pg_list.h"
#include "parser/analyze.h"
#include "parser/gramparse.h"
#include "parser/parse.h"
#include "parser/parser.h"
@ -35,19 +39,17 @@ List *parsetree; /* result of parsing is left here */
static int lookahead_token; /* one-token lookahead */
static bool have_lookahead; /* lookahead_token set? */
#ifdef SETS_FIXED
static void fixupsets();
static void define_sets();
#endif
/*
* parser-- returns a list of parse trees
* parser
* Given a query in string form, and optionally info about
* parameter types, do lexical and syntactic analysis.
*
* Returns a list of raw (un-analyzed) parse trees.
*/
List *
parser(char *str, Oid *typev, int nargs)
{
List *queryList;
int yyresult;
parseString = str;
@ -67,28 +69,9 @@ parser(char *str, Oid *typev, int nargs)
clearerr(stdin);
if (yyresult) /* error */
return (List *) NULL;
return NIL;
queryList = parse_analyze(parsetree, NULL);
#ifdef SETS_FIXED
/*
* Fixing up sets calls the parser, so it reassigns the global
* variable parsetree. So save the real parsetree.
*/
savetree = parsetree;
foreach(parse, savetree)
{ /* savetree is really a list of parses */
/* find set definitions embedded in query */
fixupsets((Query *) lfirst(parse));
}
return savetree;
#endif
return queryList;
return parsetree;
}
@ -135,83 +118,3 @@ yylex(void)
return cur_token;
}
#ifdef SETS_FIXED
static void
fixupsets(Query *parse)
{
if (parse == NULL)
return;
if (parse->commandType == CMD_UTILITY) /* utility */
return;
if (parse->commandType != CMD_INSERT)
return;
define_sets(parse);
}
/* Recursively find all of the Consts in the parsetree. Some of
* these may represent a set. The value of the Const will be the
* query (a string) which defines the set. Call SetDefine to define
* the set, and store the OID of the new set in the Const instead.
*/
static void
define_sets(Node *clause)
{
Oid setoid;
Type t = typeidType(OIDOID);
Oid typeoid = typeTypeId(t);
Size oidsize = typeLen(t);
bool oidbyval = typeByVal(t);
if (clause == NULL)
return;
else if (IsA(clause, LispList))
{
define_sets(lfirst(clause));
define_sets(lnext(clause));
}
else if (IsA(clause, Const))
{
if (get_constisnull((Const) clause) ||
!get_constisset((Const) clause))
return;
setoid = SetDefine(((Const *) clause)->constvalue,
typeidTypeName(((Const *) clause)->consttype));
set_constvalue((Const) clause, setoid);
set_consttype((Const) clause, typeoid);
set_constlen((Const) clause, oidsize);
set_constypeByVal((Const) clause, oidbyval);
}
else if (IsA(clause, Iter))
define_sets(((Iter *) clause)->iterexpr);
else if (single_node(clause))
return;
else if (or_clause(clause) || and_clause(clause))
{
List *temp;
/* mapcan */
foreach(temp, ((Expr *) clause)->args)
define_sets(lfirst(temp));
}
else if (is_funcclause(clause))
{
List *temp;
/* mapcan */
foreach(temp, ((Expr *) clause)->args)
define_sets(lfirst(temp));
}
else if (IsA(clause, ArrayRef))
define_sets(((ArrayRef *) clause)->refassgnexpr);
else if (not_clause(clause))
define_sets(get_notclausearg(clause));
else if (is_opclause(clause))
{
define_sets(get_leftop(clause));
define_sets(get_rightop(clause));
}
}
#endif

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.177 2000/10/03 03:11:19 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.178 2000/10/07 00:58:18 tgl Exp $
*
* NOTES
* this is the "main" module of the postgres backend and
@ -47,6 +47,8 @@
#include "nodes/print.h"
#include "optimizer/cost.h"
#include "optimizer/planner.h"
#include "parser/analyze.h"
#include "parser/parse.h"
#include "parser/parser.h"
#include "rewrite/rewriteHandler.h"
#include "tcop/fastpath.h"
@ -90,8 +92,6 @@ extern char ControlFilePath[];
static bool dontExecute = false;
static bool IsEmptyQuery = false;
/* note: these declarations had better match tcopprot.h */
DLLIMPORT sigjmp_buf Warn_restart;
@ -129,6 +129,10 @@ int XfuncMode = 0;
static int InteractiveBackend(StringInfo inBuf);
static int SocketBackend(StringInfo inBuf);
static int ReadCommand(StringInfo inBuf);
static List *pg_parse_query(char *query_string, Oid *typev, int nargs);
static List *pg_analyze_and_rewrite(Node *parsetree);
static void start_xact_command(void);
static void finish_xact_command(void);
static void SigHupHandler(SIGNAL_ARGS);
static void FloatExceptionHandler(SIGNAL_ARGS);
static void quickdie(SIGNAL_ARGS);
@ -341,28 +345,66 @@ ReadCommand(StringInfo inBuf)
*
* A list of Query nodes is returned, since the string might contain
* multiple queries and/or the rewriter might expand one query to several.
*
* NOTE: this routine is no longer used for processing interactive queries,
* but it is still needed for parsing of SQL function bodies.
*/
List *
pg_parse_and_rewrite(char *query_string, /* string to execute */
Oid *typev, /* parameter types */
int nargs) /* number of parameters */
{
List *raw_parsetree_list;
List *querytree_list;
List *querytree_list_item;
Query *querytree;
List *new_list;
List *list_item;
/* ----------------
* (1) parse the request string into a list of raw parse trees.
* ----------------
*/
raw_parsetree_list = pg_parse_query(query_string, typev, nargs);
/* ----------------
* (2) Do parse analysis and rule rewrite.
* ----------------
*/
querytree_list = NIL;
foreach(list_item, raw_parsetree_list)
{
Node *parsetree = (Node *) lfirst(list_item);
querytree_list = nconc(querytree_list,
pg_analyze_and_rewrite(parsetree));
}
return querytree_list;
}
/*
* Do raw parsing (only).
*
* A list of parsetrees is returned, since there might be multiple
* commands in the given string.
*
* NOTE: for interactive queries, it is important to keep this routine
* separate from the analysis & rewrite stages. Analysis and rewriting
* cannot be done in an aborted transaction, since they require access to
* database tables. So, we rely on the raw parser to determine whether
* we've seen a COMMIT or ABORT command; when we are in abort state, other
* commands are not processed any further than the raw parse stage.
*/
static List *
pg_parse_query(char *query_string, Oid *typev, int nargs)
{
List *raw_parsetree_list;
if (Debug_print_query)
elog(DEBUG, "query: %s", query_string);
/* ----------------
* (1) parse the request string into a list of parse trees
* ----------------
*/
if (Show_parser_stats)
ResetUsage();
querytree_list = parser(query_string, typev, nargs);
raw_parsetree_list = parser(query_string, typev, nargs);
if (Show_parser_stats)
{
@ -370,17 +412,53 @@ pg_parse_and_rewrite(char *query_string, /* string to execute */
ShowUsage();
}
return raw_parsetree_list;
}
/*
* Given a raw parsetree (gram.y output), perform parse analysis and
* rule rewriting.
*
* A list of Query nodes is returned, since either the analyzer or the
* rewriter might expand one query to several.
*
* NOTE: for reasons mentioned above, this must be separate from raw parsing.
*/
static List *
pg_analyze_and_rewrite(Node *parsetree)
{
List *querytree_list;
List *list_item;
Query *querytree;
List *new_list;
/* ----------------
* (2) rewrite the queries, as necessary
* (1) Perform parse analysis.
* ----------------
*/
if (Show_parser_stats)
ResetUsage();
querytree_list = parse_analyze(parsetree, NULL);
if (Show_parser_stats)
{
fprintf(StatFp, "PARSE ANALYSIS STATISTICS\n");
ShowUsage();
ResetUsage();
}
/* ----------------
* (2) Rewrite the queries, as necessary
*
* rewritten queries are collected in new_list. Note there may be
* more or fewer than in the original list.
* ----------------
*/
new_list = NIL;
foreach(querytree_list_item, querytree_list)
foreach(list_item, querytree_list)
{
querytree = (Query *) lfirst(querytree_list_item);
querytree = (Query *) lfirst(list_item);
if (Debug_print_parse)
{
@ -409,19 +487,18 @@ pg_parse_and_rewrite(char *query_string, /* string to execute */
querytree_list = new_list;
if (Show_parser_stats)
{
fprintf(StatFp, "REWRITER STATISTICS\n");
ShowUsage();
}
#ifdef COPY_PARSE_PLAN_TREES
/* Optional debugging check: pass parsetree output through copyObject() */
/*
* Note: we run this test after rewrite, not before, because copyObject()
* does not handle most kinds of nodes that are used only in raw parse
* trees. The present (bizarre) implementation of UNION/INTERSECT/EXCEPT
* doesn't run analysis of the second and later subqueries until rewrite,
* so we'd get false failures on these queries if we did it beforehand.
*/
/* Optional debugging check: pass querytree output through copyObject() */
new_list = (List *) copyObject(querytree_list);
/* This checks both copyObject() and the equal() routines... */
if (! equal(new_list, querytree_list))
elog(NOTICE, "pg_parse_and_rewrite: copyObject failed on parse tree");
elog(NOTICE, "pg_analyze_and_rewrite: copyObject failed on parse tree");
else
querytree_list = new_list;
#endif
@ -431,9 +508,9 @@ pg_parse_and_rewrite(char *query_string, /* string to execute */
if (Debug_pretty_print)
{
elog(DEBUG, "rewritten parse tree:");
foreach(querytree_list_item, querytree_list)
foreach(list_item, querytree_list)
{
querytree = (Query *) lfirst(querytree_list_item);
querytree = (Query *) lfirst(list_item);
nodeDisplay(querytree);
printf("\n");
}
@ -441,10 +518,9 @@ pg_parse_and_rewrite(char *query_string, /* string to execute */
else
{
elog(DEBUG, "rewritten parse tree:");
foreach(querytree_list_item, querytree_list)
foreach(list_item, querytree_list)
{
querytree = (Query *) lfirst(querytree_list_item);
querytree = (Query *) lfirst(list_item);
elog(DEBUG, "%s", nodeToString(querytree));
}
}
@ -514,7 +590,7 @@ pg_plan_query(Query *querytree)
/* ----------------------------------------------------------------
* pg_exec_query_dest()
* pg_exec_query_string()
*
* Takes a querystring, runs the parser/utilities or
* parser/planner/executor over it as necessary.
@ -545,21 +621,31 @@ pg_plan_query(Query *querytree)
*/
void
pg_exec_query_dest(char *query_string, /* string to execute */
CommandDest dest, /* where results should go */
MemoryContext parse_context) /* context for parsetrees */
pg_exec_query_string(char *query_string, /* string to execute */
CommandDest dest, /* where results should go */
MemoryContext parse_context) /* context for parsetrees */
{
bool xact_started;
MemoryContext oldcontext;
List *querytree_list,
*querytree_item;
List *parsetree_list,
*parsetree_item;
/*
* If you called this routine with parse_context = CurrentMemoryContext,
* you blew it. They *must* be different, else the context reset
* at the bottom of the loop will destroy the querytree list.
* (We really ought to check that parse_context isn't a child of
* CurrentMemoryContext either, but that would take more cycles than
* it's likely to be worth.)
* Start up a transaction command. All queries generated by the
* query_string will be in this same command block, *unless* we find
* a BEGIN/COMMIT/ABORT statement; we have to force a new xact command
* after one of those, else bad things will happen in xact.c.
* (Note that this will possibly change execution memory context.)
*/
start_xact_command();
xact_started = true;
/*
* parse_context *must* be different from the execution memory context,
* else the context reset at the bottom of the loop will destroy the
* parsetree list. (We really ought to check that parse_context isn't a
* child of CurrentMemoryContext either, but that would take more cycles
* than it's likely to be worth.)
*/
Assert(parse_context != CurrentMemoryContext);
@ -569,48 +655,57 @@ pg_exec_query_dest(char *query_string, /* string to execute */
oldcontext = MemoryContextSwitchTo(parse_context);
/*
* Parse and rewrite the query or queries.
* Do basic parsing of the query or queries (this should be safe
* even if we are in aborted transaction state!)
*/
querytree_list = pg_parse_and_rewrite(query_string, NULL, 0);
parsetree_list = pg_parse_query(query_string, NULL, 0);
/*
* Switch back to execution context for planning and execution.
* Switch back to execution context to enter the loop.
*/
MemoryContextSwitchTo(oldcontext);
/*
* Run through the query or queries and execute each one.
* Run through the parsetree(s) and process each one.
*/
foreach(querytree_item, querytree_list)
foreach(parsetree_item, parsetree_list)
{
Query *querytree = (Query *) lfirst(querytree_item);
Node *parsetree = (Node *) lfirst(parsetree_item);
bool isTransactionStmt;
List *querytree_list,
*querytree_item;
/* if we got a cancel signal in parsing or prior command, quit */
if (QueryCancel)
CancelQuery();
/* Transaction control statements need some special handling */
isTransactionStmt = IsA(parsetree, TransactionStmt);
if (querytree->commandType == CMD_UTILITY)
/*
* If we are in an aborted transaction, ignore all commands except
* COMMIT/ABORT. It is important that this test occur before we
* try to do parse analysis, rewrite, or planning, since all those
* phases try to do database accesses, which may fail in abort state.
* (It might be safe to allow some additional utility commands in
* this state, but not many...)
*/
if (IsAbortedTransactionBlockState())
{
/* ----------------
* process utility functions (create, destroy, etc..)
*
* Note: we do not check for the transaction aborted state
* because that is done in ProcessUtility.
* ----------------
*/
if (Debug_print_query)
elog(DEBUG, "ProcessUtility: %s", query_string);
else if (DebugLvl > 1)
elog(DEBUG, "ProcessUtility");
bool allowit = false;
ProcessUtility(querytree->utilityStmt, dest);
}
else
{
Plan *plan;
if (isTransactionStmt)
{
TransactionStmt *stmt = (TransactionStmt *) parsetree;
/* If aborted transaction, skip planning and execution */
if (IsAbortedTransactionBlockState())
switch (stmt->command)
{
case COMMIT:
case ROLLBACK:
allowit = true;
break;
default:
break;
}
}
if (! allowit)
{
/* ----------------
* the EndCommand() stuff is to tell the frontend
@ -631,58 +726,180 @@ pg_exec_query_dest(char *query_string, /* string to execute */
*/
continue;
}
}
plan = pg_plan_query(querytree);
/* Make sure we are in a transaction command */
if (! xact_started)
{
start_xact_command();
xact_started = true;
}
/* if we got a cancel signal whilst planning, quit */
/* If we got a cancel signal in parsing or prior command, quit */
if (QueryCancel)
CancelQuery();
/*
* OK to analyze and rewrite this query.
*
* Switch to appropriate context for constructing querytrees
* (again, these must outlive the execution context).
*/
oldcontext = MemoryContextSwitchTo(parse_context);
querytree_list = pg_analyze_and_rewrite(parsetree);
/*
* Switch back to execution context for planning and execution.
*/
MemoryContextSwitchTo(oldcontext);
/*
* Inner loop handles the individual queries generated from a
* single parsetree by analysis and rewrite.
*/
foreach(querytree_item, querytree_list)
{
Query *querytree = (Query *) lfirst(querytree_item);
/* Make sure we are in a transaction command */
if (! xact_started)
{
start_xact_command();
xact_started = true;
}
/* If we got a cancel signal in analysis or prior command, quit */
if (QueryCancel)
CancelQuery();
/* Initialize snapshot state for query */
SetQuerySnapshot();
/*
* execute the plan
*/
if (Show_executor_stats)
ResetUsage();
if (dontExecute)
if (querytree->commandType == CMD_UTILITY)
{
/* don't execute it, just show the query plan */
print_plan(plan, querytree);
/* ----------------
* process utility functions (create, destroy, etc..)
* ----------------
*/
if (Debug_print_query)
elog(DEBUG, "ProcessUtility: %s", query_string);
else if (DebugLvl > 1)
elog(DEBUG, "ProcessUtility");
ProcessUtility(querytree->utilityStmt, dest);
}
else
{
if (DebugLvl > 1)
elog(DEBUG, "ProcessQuery");
ProcessQuery(querytree, plan, dest);
/* ----------------
* process a plannable query.
* ----------------
*/
Plan *plan;
plan = pg_plan_query(querytree);
/* if we got a cancel signal whilst planning, quit */
if (QueryCancel)
CancelQuery();
/* Initialize snapshot state for query */
SetQuerySnapshot();
/*
* execute the plan
*/
if (Show_executor_stats)
ResetUsage();
if (dontExecute)
{
/* don't execute it, just show the query plan */
print_plan(plan, querytree);
}
else
{
if (DebugLvl > 1)
elog(DEBUG, "ProcessQuery");
ProcessQuery(querytree, plan, dest);
}
if (Show_executor_stats)
{
fprintf(stderr, "EXECUTOR STATISTICS\n");
ShowUsage();
}
}
if (Show_executor_stats)
/*
* In a query block, we want to increment the command counter
* between queries so that the effects of early queries are
* visible to subsequent ones. In particular we'd better
* do so before checking constraints.
*/
if (!isTransactionStmt)
CommandCounterIncrement();
/*
* Invoke IMMEDIATE constraint triggers
*/
DeferredTriggerEndQuery();
/*
* Clear the execution context to recover temporary
* memory used by the query. NOTE: if query string contains
* BEGIN/COMMIT transaction commands, execution context may
* now be different from what we were originally passed;
* so be careful to clear current context not "oldcontext".
*/
Assert(parse_context != CurrentMemoryContext);
MemoryContextResetAndDeleteChildren(CurrentMemoryContext);
/*
* If this was a transaction control statement, commit it
* and arrange to start a new xact command for the next
* command (if any).
*/
if (isTransactionStmt)
{
fprintf(stderr, "EXECUTOR STATISTICS\n");
ShowUsage();
finish_xact_command();
xact_started = false;
}
}
/*
* In a query block, we want to increment the command counter
* between queries so that the effects of early queries are
* visible to subsequent ones.
*/
CommandCounterIncrement();
/*
* Also, clear the execution context to recover temporary
* memory used by the query. NOTE: if query string contains
* BEGIN/COMMIT transaction commands, execution context may
* now be different from what we were originally passed;
* so be careful to clear current context not "oldcontext".
*/
MemoryContextResetAndDeleteChildren(CurrentMemoryContext);
}
} /* end loop over queries generated from a parsetree */
} /* end loop over parsetrees */
/*
* Close down transaction statement, if one is open.
*/
if (xact_started)
finish_xact_command();
}
/*
* Convenience routines for starting/committing a single command.
*/
static void
start_xact_command(void)
{
if (DebugLvl >= 1)
elog(DEBUG, "StartTransactionCommand");
StartTransactionCommand();
}
static void
finish_xact_command(void)
{
if (DebugLvl >= 1)
elog(DEBUG, "CommitTransactionCommand");
set_ps_display("commit"); /* XXX probably the wrong place to do this */
CommitTransactionCommand();
#ifdef SHOW_MEMORY_STATS
/* print mem stats at each commit for leak tracking */
if (ShowStats)
MemoryContextStats(TopMemoryContext);
#endif
}
/* --------------------------------
* signal handler routines used in PostgresMain()
*
@ -1397,7 +1614,7 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[], const cha
if (!IsUnderPostmaster)
{
puts("\nPOSTGRES backend interactive interface ");
puts("$Revision: 1.177 $ $Date: 2000/10/03 03:11:19 $\n");
puts("$Revision: 1.178 $ $Date: 2000/10/07 00:58:18 $\n");
}
/*
@ -1524,22 +1741,20 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[], const cha
{
/* ----------------
* 'F' indicates a fastpath call.
* XXX HandleFunctionRequest
* ----------------
*/
case 'F':
IsEmptyQuery = false;
/* start an xact for this function invocation */
if (DebugLvl >= 1)
elog(DEBUG, "StartTransactionCommand");
StartTransactionCommand();
start_xact_command();
if (HandleFunctionRequest() == EOF)
{
/* lost frontend connection during F message input */
goto normalexit;
}
/* commit the function-invocation transaction */
finish_xact_command();
break;
/* ----------------
@ -1551,35 +1766,28 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[], const cha
{
/* ----------------
* if there is nothing in the input buffer, don't bother
* trying to parse and execute anything..
* trying to parse and execute anything; just send
* back a quick NullCommand response.
* ----------------
*/
IsEmptyQuery = true;
if (IsUnderPostmaster)
NullCommand(Remote);
}
else
{
/* ----------------
* otherwise, process the input string.
*
* Note: transaction command start/end is now done
* within pg_exec_query_string(), not here.
* ----------------
*/
IsEmptyQuery = false;
if (Show_query_stats)
ResetUsage();
/* start an xact for this query */
if (DebugLvl >= 1)
elog(DEBUG, "StartTransactionCommand");
StartTransactionCommand();
pg_exec_query_dest(parser_input->data,
whereToSendOutput,
QueryContext);
/*
* Invoke IMMEDIATE constraint triggers
*
*/
DeferredTriggerEndQuery();
pg_exec_query_string(parser_input->data,
whereToSendOutput,
QueryContext);
if (Show_query_stats)
{
@ -1603,39 +1811,14 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[], const cha
elog(ERROR, "unknown frontend message was received");
}
/* ----------------
* (6) commit the current transaction
*
* Note: if we had an empty input buffer, then we didn't
* call pg_exec_query_dest, so we don't bother to commit
* this transaction.
* ----------------
*/
if (!IsEmptyQuery)
{
if (DebugLvl >= 1)
elog(DEBUG, "CommitTransactionCommand");
set_ps_display("commit");
CommitTransactionCommand();
#ifdef SHOW_MEMORY_STATS
/* print global-context stats at each commit for leak tracking */
if (ShowStats)
MemoryContextStats(TopMemoryContext);
#endif
}
else
{
if (IsUnderPostmaster)
NullCommand(Remote);
}
#ifdef MEMORY_CONTEXT_CHECKING
/*
* Check all memory after each backend loop
* Check all memory after each backend loop. This is a rather
* weird place to do it, perhaps.
*/
MemoryContextCheck(TopMemoryContext);
#endif
} /* end of main loop */
} /* end of input-reading loop */
normalexit:
ExitAfterAbort = true; /* ensure we will exit if elog during abort */

View File

@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.94 2000/09/12 05:09:45 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.95 2000/10/07 00:58:18 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -37,6 +37,7 @@
#include "commands/view.h"
#include "miscadmin.h"
#include "parser/parse.h"
#include "parser/parse_expr.h"
#include "rewrite/rewriteDefine.h"
#include "rewrite/rewriteRemove.h"
#include "tcop/utility.h"
@ -45,24 +46,6 @@
#include "utils/syscache.h"
/* ----------------
* CHECK_IF_ABORTED() is used to avoid doing unnecessary
* processing within an aborted transaction block.
* ----------------
*/
/* we have to use IF because of the 'break' */
#define CHECK_IF_ABORTED() \
if (1) \
{ \
if (IsAbortedTransactionBlockState()) \
{ \
elog(NOTICE, "current transaction is aborted, " \
"queries ignored until end of transaction block"); \
commandTag = "*ABORT STATE*"; \
break; \
} \
} else
/* ----------------
* general utility function invoker
* ----------------
@ -90,7 +73,6 @@ ProcessUtility(Node *parsetree,
{
case BEGIN_TRANS:
set_ps_display(commandTag = "BEGIN");
CHECK_IF_ABORTED();
BeginTransactionBlock();
break;
@ -116,7 +98,6 @@ ProcessUtility(Node *parsetree,
ClosePortalStmt *stmt = (ClosePortalStmt *) parsetree;
set_ps_display(commandTag = "CLOSE");
CHECK_IF_ABORTED();
PerformPortalClose(stmt->portalname, dest);
}
@ -130,7 +111,6 @@ ProcessUtility(Node *parsetree,
int count;
set_ps_display(commandTag = (stmt->ismove) ? "MOVE" : "FETCH");
CHECK_IF_ABORTED();
SetQuerySnapshot();
@ -153,7 +133,6 @@ ProcessUtility(Node *parsetree,
*/
case T_CreateStmt:
set_ps_display(commandTag = "CREATE");
CHECK_IF_ABORTED();
DefineRelation((CreateStmt *) parsetree, RELKIND_RELATION);
@ -174,7 +153,6 @@ ProcessUtility(Node *parsetree,
List *arg;
set_ps_display(commandTag = "DROP");
CHECK_IF_ABORTED();
/* check as much as we can before we start dropping ... */
foreach(arg, args)
@ -216,7 +194,6 @@ ProcessUtility(Node *parsetree,
Relation rel;
set_ps_display(commandTag = "TRUNCATE");
CHECK_IF_ABORTED();
relname = ((TruncateStmt *) parsetree)->relName;
if (!allowSystemTableMods && IsSystemRelationName(relname))
@ -243,27 +220,23 @@ ProcessUtility(Node *parsetree,
case T_CommentStmt:
{
CommentStmt *statement;
statement = ((CommentStmt *) parsetree);
set_ps_display(commandTag = "COMMENT");
CHECK_IF_ABORTED();
CommentObject(statement->objtype, statement->objname,
statement->objproperty, statement->objlist,
statement->comment);
}
break;
case T_CopyStmt:
{
CopyStmt *stmt = (CopyStmt *) parsetree;
set_ps_display(commandTag = "COPY");
CHECK_IF_ABORTED();
if (stmt->direction != FROM)
SetQuerySnapshot();
@ -292,7 +265,6 @@ ProcessUtility(Node *parsetree,
RenameStmt *stmt = (RenameStmt *) parsetree;
set_ps_display(commandTag = "ALTER");
CHECK_IF_ABORTED();
relname = stmt->relname;
if (!allowSystemTableMods && IsSystemRelationName(relname))
@ -345,7 +317,6 @@ ProcessUtility(Node *parsetree,
AlterTableStmt *stmt = (AlterTableStmt *) parsetree;
set_ps_display(commandTag = "ALTER");
CHECK_IF_ABORTED();
/*
* Some or all of these functions are recursive to cover
@ -385,34 +356,10 @@ ProcessUtility(Node *parsetree,
case T_ChangeACLStmt:
{
ChangeACLStmt *stmt = (ChangeACLStmt *) parsetree;
List *i;
AclItem *aip;
unsigned modechg;
set_ps_display(commandTag = "CHANGE");
CHECK_IF_ABORTED();
aip = stmt->aclitem;
modechg = stmt->modechg;
foreach(i, stmt->relNames)
{
Relation rel;
relname = strVal(lfirst(i));
rel = heap_openr(relname, AccessExclusiveLock);
if (rel && rel->rd_rel->relkind == RELKIND_INDEX)
elog(ERROR, "\"%s\" is an index relation",
relname);
/* close rel, but keep lock until end of xact */
heap_close(rel, NoLock);
#ifndef NO_SECURITY
if (!pg_ownercheck(GetUserId(), relname, RELNAME))
elog(ERROR, "you do not own class \"%s\"",
relname);
#endif
ChangeAcl(relname, aip, modechg);
}
ExecuteChangeACLStmt(stmt);
}
break;
@ -426,7 +373,6 @@ ProcessUtility(Node *parsetree,
DefineStmt *stmt = (DefineStmt *) parsetree;
set_ps_display(commandTag = "CREATE");
CHECK_IF_ABORTED();
switch (stmt->defType)
{
@ -450,14 +396,14 @@ ProcessUtility(Node *parsetree,
ViewStmt *stmt = (ViewStmt *) parsetree;
set_ps_display(commandTag = "CREATE");
CHECK_IF_ABORTED();
DefineView(stmt->viewname, stmt->query); /* retrieve parsetree */
}
break;
case T_ProcedureStmt: /* CREATE FUNCTION */
set_ps_display(commandTag = "CREATE");
CHECK_IF_ABORTED();
CreateFunction((ProcedureStmt *) parsetree, dest); /* everything */
break;
@ -466,7 +412,7 @@ ProcessUtility(Node *parsetree,
IndexStmt *stmt = (IndexStmt *) parsetree;
set_ps_display(commandTag = "CREATE");
CHECK_IF_ABORTED();
DefineIndex(stmt->relname, /* relation name */
stmt->idxname, /* index name */
stmt->accessMethod, /* am name */
@ -491,14 +437,13 @@ ProcessUtility(Node *parsetree,
elog(ERROR, "%s: %s", relname, aclcheck_error_strings[aclcheck_result]);
#endif
set_ps_display(commandTag = "CREATE");
CHECK_IF_ABORTED();
DefineQueryRewrite(stmt);
}
break;
case T_CreateSeqStmt:
set_ps_display(commandTag = "CREATE");
CHECK_IF_ABORTED();
DefineSequence((CreateSeqStmt *) parsetree);
break;
@ -508,7 +453,6 @@ ProcessUtility(Node *parsetree,
ExtendStmt *stmt = (ExtendStmt *) parsetree;
set_ps_display(commandTag = "EXTEND");
CHECK_IF_ABORTED();
ExtendIndex(stmt->idxname, /* index name */
(Expr *) stmt->whereClause, /* where */
@ -521,7 +465,6 @@ ProcessUtility(Node *parsetree,
RemoveStmt *stmt = (RemoveStmt *) parsetree;
set_ps_display(commandTag = "DROP");
CHECK_IF_ABORTED();
switch (stmt->removeType)
{
@ -581,10 +524,14 @@ ProcessUtility(Node *parsetree,
case T_RemoveAggrStmt:
{
RemoveAggrStmt *stmt = (RemoveAggrStmt *) parsetree;
char *typename = (char *) NULL;
set_ps_display(commandTag = "DROP");
CHECK_IF_ABORTED();
RemoveAggregate(stmt->aggname, stmt->aggtype);
if (stmt->aggtype != NULL)
typename = TypeNameToInternalName((TypeName *) stmt->aggtype);
RemoveAggregate(stmt->aggname, typename);
}
break;
@ -593,27 +540,27 @@ ProcessUtility(Node *parsetree,
RemoveFuncStmt *stmt = (RemoveFuncStmt *) parsetree;
set_ps_display(commandTag = "DROP");
CHECK_IF_ABORTED();
RemoveFunction(stmt->funcname,
length(stmt->args),
stmt->args);
RemoveFunction(stmt->funcname, stmt->args);
}
break;
case T_RemoveOperStmt:
{
RemoveOperStmt *stmt = (RemoveOperStmt *) parsetree;
char *type1 = (char *) NULL;
char *type2 = (char *) NULL;
TypeName *typenode1 = (TypeName *) lfirst(stmt->args);
TypeName *typenode2 = (TypeName *) lsecond(stmt->args);
char *typename1 = (char *) NULL;
char *typename2 = (char *) NULL;
set_ps_display(commandTag = "DROP");
CHECK_IF_ABORTED();
if (lfirst(stmt->args) != NULL)
type1 = strVal(lfirst(stmt->args));
if (lsecond(stmt->args) != NULL)
type2 = strVal(lsecond(stmt->args));
RemoveOperator(stmt->opname, type1, type2);
if (typenode1 != NULL)
typename1 = TypeNameToInternalName(typenode1);
if (typenode2 != NULL)
typename2 = TypeNameToInternalName(typenode2);
RemoveOperator(stmt->opname, typename1, typename2);
}
break;
@ -626,7 +573,7 @@ ProcessUtility(Node *parsetree,
CreatedbStmt *stmt = (CreatedbStmt *) parsetree;
set_ps_display(commandTag = "CREATE DATABASE");
CHECK_IF_ABORTED();
createdb(stmt->dbname, stmt->dbpath, stmt->encoding);
}
break;
@ -636,7 +583,7 @@ ProcessUtility(Node *parsetree,
DropdbStmt *stmt = (DropdbStmt *) parsetree;
set_ps_display(commandTag = "DROP DATABASE");
CHECK_IF_ABORTED();
dropdb(stmt->dbname);
}
break;
@ -647,7 +594,6 @@ ProcessUtility(Node *parsetree,
NotifyStmt *stmt = (NotifyStmt *) parsetree;
set_ps_display(commandTag = "NOTIFY");
CHECK_IF_ABORTED();
Async_Notify(stmt->relname);
}
@ -658,7 +604,6 @@ ProcessUtility(Node *parsetree,
ListenStmt *stmt = (ListenStmt *) parsetree;
set_ps_display(commandTag = "LISTEN");
CHECK_IF_ABORTED();
Async_Listen(stmt->relname, MyProcPid);
}
@ -669,7 +614,6 @@ ProcessUtility(Node *parsetree,
UnlistenStmt *stmt = (UnlistenStmt *) parsetree;
set_ps_display(commandTag = "UNLISTEN");
CHECK_IF_ABORTED();
Async_Unlisten(stmt->relname, MyProcPid);
}
@ -684,7 +628,6 @@ ProcessUtility(Node *parsetree,
LoadStmt *stmt = (LoadStmt *) parsetree;
set_ps_display(commandTag = "LOAD");
CHECK_IF_ABORTED();
closeAllVfds(); /* probably not necessary... */
load_file(stmt->filename);
@ -696,7 +639,6 @@ ProcessUtility(Node *parsetree,
ClusterStmt *stmt = (ClusterStmt *) parsetree;
set_ps_display(commandTag = "CLUSTER");
CHECK_IF_ABORTED();
cluster(stmt->relname, stmt->indexname);
}
@ -704,7 +646,7 @@ ProcessUtility(Node *parsetree,
case T_VacuumStmt:
set_ps_display(commandTag = "VACUUM");
CHECK_IF_ABORTED();
vacuum(((VacuumStmt *) parsetree)->vacrel,
((VacuumStmt *) parsetree)->verbose,
((VacuumStmt *) parsetree)->analyze,
@ -716,7 +658,6 @@ ProcessUtility(Node *parsetree,
ExplainStmt *stmt = (ExplainStmt *) parsetree;
set_ps_display(commandTag = "EXPLAIN");
CHECK_IF_ABORTED();
ExplainQuery(stmt->query, stmt->verbose, dest);
}
@ -732,7 +673,7 @@ ProcessUtility(Node *parsetree,
RecipeStmt *stmt = (RecipeStmt *) parsetree;
set_ps_display(commandTag = "EXECUTE RECIPE");
CHECK_IF_ABORTED();
beginRecipe(stmt);
}
break;
@ -773,14 +714,12 @@ ProcessUtility(Node *parsetree,
*/
case T_CreateTrigStmt:
set_ps_display(commandTag = "CREATE");
CHECK_IF_ABORTED();
CreateTrigger((CreateTrigStmt *) parsetree);
break;
case T_DropTrigStmt:
set_ps_display(commandTag = "DROP");
CHECK_IF_ABORTED();
DropTrigger((DropTrigStmt *) parsetree);
break;
@ -790,14 +729,12 @@ ProcessUtility(Node *parsetree,
*/
case T_CreatePLangStmt:
set_ps_display(commandTag = "CREATE");
CHECK_IF_ABORTED();
CreateProceduralLanguage((CreatePLangStmt *) parsetree);
break;
case T_DropPLangStmt:
set_ps_display(commandTag = "DROP");
CHECK_IF_ABORTED();
DropProceduralLanguage((DropPLangStmt *) parsetree);
break;
@ -808,56 +745,48 @@ ProcessUtility(Node *parsetree,
*/
case T_CreateUserStmt:
set_ps_display(commandTag = "CREATE USER");
CHECK_IF_ABORTED();
CreateUser((CreateUserStmt *) parsetree);
break;
case T_AlterUserStmt:
set_ps_display(commandTag = "ALTER USER");
CHECK_IF_ABORTED();
AlterUser((AlterUserStmt *) parsetree);
break;
case T_DropUserStmt:
set_ps_display(commandTag = "DROP USER");
CHECK_IF_ABORTED();
DropUser((DropUserStmt *) parsetree);
break;
case T_LockStmt:
set_ps_display(commandTag = "LOCK TABLE");
CHECK_IF_ABORTED();
LockTableCommand((LockStmt *) parsetree);
break;
case T_ConstraintsSetStmt:
set_ps_display(commandTag = "SET CONSTRAINTS");
CHECK_IF_ABORTED();
DeferredTriggerSetState((ConstraintsSetStmt *) parsetree);
break;
case T_CreateGroupStmt:
set_ps_display(commandTag = "CREATE GROUP");
CHECK_IF_ABORTED();
CreateGroup((CreateGroupStmt *) parsetree);
break;
case T_AlterGroupStmt:
set_ps_display(commandTag = "ALTER GROUP");
CHECK_IF_ABORTED();
AlterGroup((AlterGroupStmt *) parsetree, "ALTER GROUP");
break;
case T_DropGroupStmt:
set_ps_display(commandTag = "DROP GROUP");
CHECK_IF_ABORTED();
DropGroup((DropGroupStmt *) parsetree);
break;
@ -867,7 +796,6 @@ ProcessUtility(Node *parsetree,
ReindexStmt *stmt = (ReindexStmt *) parsetree;
set_ps_display(commandTag = "REINDEX");
CHECK_IF_ABORTED();
switch (stmt->reindexType)
{

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/adt/acl.c,v 1.49 2000/10/02 04:49:27 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/adt/acl.c,v 1.50 2000/10/07 00:58:19 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -16,10 +16,12 @@
#include "postgres.h"
#include "access/heapam.h"
#include "catalog/catalog.h"
#include "catalog/pg_shadow.h"
#include "catalog/pg_type.h"
#include "lib/stringinfo.h"
#include "miscadmin.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/memutils.h"
@ -561,7 +563,52 @@ aclcontains(PG_FUNCTION_ARGS)
PG_RETURN_BOOL(false);
}
/* parser support routines */
/*
* ExecuteChangeACLStmt
* Called to execute the utility command type ChangeACLStmt
*/
void
ExecuteChangeACLStmt(ChangeACLStmt *stmt)
{
AclItem aclitem;
unsigned modechg;
List *i;
/* see comment in pg_type.h */
Assert(ACLITEMSIZE == sizeof(AclItem));
/* Convert string ACL spec into internal form */
aclparse(stmt->aclString, &aclitem, &modechg);
foreach(i, stmt->relNames)
{
char *relname = strVal(lfirst(i));
Relation rel;
rel = heap_openr(relname, AccessExclusiveLock);
if (rel && rel->rd_rel->relkind == RELKIND_INDEX)
elog(ERROR, "\"%s\" is an index relation",
relname);
#ifndef NO_SECURITY
if (!pg_ownercheck(GetUserId(), relname, RELNAME))
elog(ERROR, "you do not own class \"%s\"",
relname);
#endif
ChangeAcl(relname, &aclitem, modechg);
/* close rel, but keep lock until end of xact */
heap_close(rel, NoLock);
}
}
/*
* Parser support routines for ACL-related statements.
*
* XXX CAUTION: these are called from gram.y, which is not allowed to
* do any table accesses. Therefore, it is not kosher to do things
* like trying to translate usernames to user IDs here. Keep it all
* in string form until statement execution time.
*/
/*
* aclmakepriv
@ -569,9 +616,7 @@ aclcontains(PG_FUNCTION_ARGS)
* and a new privilege
*
* does not add duplicate privileges
*
*/
char *
aclmakepriv(char *old_privlist, char new_priv)
{
@ -619,12 +664,9 @@ aclmakepriv(char *old_privlist, char new_priv)
* "G" - group
* "U" - user
*
* concatenates the two strings together with a space in between
*
* this routine is used in the parser
*
* Just concatenates the two strings together with a space in between.
* Per above comments, we can't try to resolve a user or group name here.
*/
char *
aclmakeuser(char *user_type, char *user)
{
@ -635,20 +677,16 @@ aclmakeuser(char *user_type, char *user)
return user_list;
}
/*
* makeAclStmt:
* this is a helper routine called by the parser
* create a ChangeAclStmt
* we take in the privilegs, relation_name_list, and grantee
* as well as a single character '+' or '-' to indicate grant or revoke
* create a ChangeACLStmt at parse time.
* we take in the privileges, relation_name_list, and grantee
* as well as a single character '+' or '-' to indicate grant or revoke
*
* returns a new ChangeACLStmt*
*
* this routines works by creating a old-style changle acl string and
* then calling aclparse;
* We convert the information to the same external form recognized by
* aclitemin (see aclparse), and save that string in the ChangeACLStmt.
* Conversion to internal form happens when the statement is executed.
*/
ChangeACLStmt *
makeAclStmt(char *privileges, List *rel_list, char *grantee,
char grant_or_revoke)
@ -658,11 +696,6 @@ makeAclStmt(char *privileges, List *rel_list, char *grantee,
initStringInfo(&str);
/* see comment in pg_type.h */
Assert(ACLITEMSIZE == sizeof(AclItem));
n->aclitem = (AclItem *) palloc(sizeof(AclItem));
/* the grantee string is "G <group_name>", "U <user_name>", or "ALL" */
if (grantee[0] == 'G') /* group permissions */
{
@ -683,7 +716,8 @@ makeAclStmt(char *privileges, List *rel_list, char *grantee,
grant_or_revoke, privileges);
}
n->relNames = rel_list;
aclparse(str.data, n->aclitem, (unsigned *) &n->modechg);
n->aclString = pstrdup(str.data);
pfree(str.data);
return n;
}

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: defrem.h,v 1.20 2000/08/24 03:29:09 tgl Exp $
* $Id: defrem.h,v 1.21 2000/10/07 00:58:20 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -48,7 +48,7 @@ extern void DefineType(char *name, List *parameters);
/*
* prototypes in remove.c
*/
extern void RemoveFunction(char *functionName, int nargs, List *argNameList);
extern void RemoveFunction(char *functionName, List *argTypes);
extern void RemoveOperator(char *operatorName,
char *typeName1, char *typeName2);
extern void RemoveType(char *typeName);

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: parsenodes.h,v 1.115 2000/10/05 19:11:36 tgl Exp $
* $Id: parsenodes.h,v 1.116 2000/10/07 00:58:21 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -114,9 +114,8 @@ typedef struct AlterTableStmt
typedef struct ChangeACLStmt
{
NodeTag type;
struct AclItem *aclitem;
unsigned modechg;
List *relNames;
char *aclString;
} ChangeACLStmt;
/* ----------------------
@ -488,10 +487,8 @@ typedef struct ProcedureStmt
{
NodeTag type;
char *funcname; /* name of function to create */
List *defArgs; /* list of definitions a list of strings
* (as Value *) */
Node *returnType; /* the return type (as a string or a
* TypeName (ie.setof) */
List *argTypes; /* list of argument types (TypeName nodes) */
Node *returnType; /* the return type (a TypeName node) */
List *withClause; /* a list of DefElem */
List *as; /* definition of function body */
char *language; /* C, SQL, etc */
@ -505,7 +502,7 @@ typedef struct RemoveAggrStmt
{
NodeTag type;
char *aggname; /* aggregate to drop */
char *aggtype; /* for this type */
Node *aggtype; /* TypeName for input datatype, or NULL */
} RemoveAggrStmt;
/* ----------------------

View File

@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: analyze.h,v 1.11 2000/10/05 19:11:38 tgl Exp $
* $Id: analyze.h,v 1.12 2000/10/07 00:58:21 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -15,6 +15,6 @@
#include "parser/parse_node.h"
extern List *parse_analyze(List *pl, ParseState *parentParseState);
extern List *parse_analyze(Node *parseTree, ParseState *parentParseState);
#endif /* ANALYZE_H */

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: parse_expr.h,v 1.19 2000/06/15 03:32:55 momjian Exp $
* $Id: parse_expr.h,v 1.20 2000/10/07 00:58:21 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -26,5 +26,6 @@ extern Oid exprType(Node *expr);
extern int32 exprTypmod(Node *expr);
extern bool exprIsLengthCoercion(Node *expr, int32 *coercedTypmod);
extern void parse_expr_init(void);
extern char *TypeNameToInternalName(TypeName *typename);
#endif /* PARSE_EXPR_H */

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: tcopprot.h,v 1.34 2000/09/06 14:15:28 petere Exp $
* $Id: tcopprot.h,v 1.35 2000/10/07 00:58:23 tgl Exp $
*
* OLD COMMENTS
* This file was created so that other c files could get the two
@ -35,9 +35,9 @@ extern bool ShowPortNumber;
extern List *pg_parse_and_rewrite(char *query_string,
Oid *typev, int nargs);
extern Plan *pg_plan_query(Query *querytree);
extern void pg_exec_query_dest(char *query_string,
CommandDest dest,
MemoryContext parse_context);
extern void pg_exec_query_string(char *query_string,
CommandDest dest,
MemoryContext parse_context);
#endif /* BOOTSTRAP_INCLUDE */

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: acl.h,v 1.28 2000/10/02 04:49:27 tgl Exp $
* $Id: acl.h,v 1.29 2000/10/07 00:58:23 tgl Exp $
*
* NOTES
* For backward-compatibility purposes we have to allow there
@ -167,12 +167,15 @@ extern char *aclcheck_error_strings[];
/*#define ACLDEBUG_TRACE*/
/*
* routines used internally (parser, etc.)
* routines used internally
*/
extern Acl *acldefault(char *relname, AclId ownerid);
extern Acl *aclinsert3(Acl *old_acl, AclItem *mod_aip, unsigned modechg);
/*
* routines used by the parser
*/
extern char *aclmakepriv(char *old_privlist, char new_priv);
extern char *aclmakeuser(char *user_type, char *user);
extern ChangeACLStmt *makeAclStmt(char *privs, List *rel_list, char *grantee,
@ -187,6 +190,7 @@ extern Datum aclitemout(PG_FUNCTION_ARGS);
extern Datum aclinsert(PG_FUNCTION_ARGS);
extern Datum aclremove(PG_FUNCTION_ARGS);
extern Datum aclcontains(PG_FUNCTION_ARGS);
extern void ExecuteChangeACLStmt(ChangeACLStmt *stmt);
/*
* prototypes for functions in aclchk.c