Thread: Adds the parsing of a CREATE SCHEMA statement
ChnageLog: Adds the parsing of a CREATE SCHEMA statement. It is still just syntactic sugar as we still have a single namespace. We also now accept qualified names for tables and views (where the reference is for tables, not for columns) but we do nothing with it. -- Fernando Nasser Red Hat Canada Ltd. E-Mail: fnasser@redhat.com 2323 Yonge Street, Suite #300 Toronto, Ontario M4P 2C9Index: src/backend/nodes/copyfuncs.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v retrieving revision 1.163 diff -c -p -r1.163 copyfuncs.c *** src/backend/nodes/copyfuncs.c 2002/02/26 22:47:05 1.163 --- src/backend/nodes/copyfuncs.c 2002/02/28 20:46:34 *************** _copyReindexStmt(ReindexStmt *from) *** 2508,2514 **** --- 2508,2528 ---- return newnode; } + static CreateSchemaStmt * + _copyCreateSchemaStmt(CreateSchemaStmt *from) + { + CreateSchemaStmt *newnode = makeNode(CreateSchemaStmt); + + newnode->schemaname = pstrdup(from->schemaname); + if (from->authid) + newnode->authid = pstrdup(from->authid); + Node_Copy(from, newnode, schemaElts); + newnode->in_progress = from->in_progress; + return newnode; + } + + /* **************************************************************** * pg_list.h copy functions * **************************************************************** *************** copyObject(void *from) *** 2905,2910 **** --- 2919,2927 ---- break; case T_CheckPointStmt: retval = (void *) makeNode(CheckPointStmt); + break; + case T_CreateSchemaStmt: + retval = _copyCreateSchemaStmt(from); break; case T_A_Expr: Index: src/backend/nodes/equalfuncs.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v retrieving revision 1.111 diff -c -p -r1.111 equalfuncs.c *** src/backend/nodes/equalfuncs.c 2002/02/26 22:47:05 1.111 --- src/backend/nodes/equalfuncs.c 2002/02/28 20:46:34 *************** _equalReindexStmt(ReindexStmt *a, Reinde *** 1369,1374 **** --- 1369,1389 ---- } static bool + _equalCreateSchemaStmt(CreateSchemaStmt *a, CreateSchemaStmt *b) + { + if (!equalstr(a->schemaname, b->schemaname)) + return false; + if (!equalstr(a->authid, b->authid)) + return false; + if (!equal(a->schemaElts, b->schemaElts)) + return false; + if (a->in_progress != b->in_progress) + return false; + + return true; + } + + static bool _equalAExpr(A_Expr *a, A_Expr *b) { if (a->oper != b->oper) *************** equal(void *a, void *b) *** 2050,2055 **** --- 2065,2073 ---- break; case T_CheckPointStmt: retval = true; + break; + case T_CreateSchemaStmt: + retval = _equalCreateSchemaStmt(a, b); break; case T_A_Expr: Index: src/backend/parser/analyze.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/parser/analyze.c,v retrieving revision 1.215 diff -c -p -r1.215 analyze.c *** src/backend/parser/analyze.c 2002/02/26 22:47:08 1.215 --- src/backend/parser/analyze.c 2002/02/28 20:46:34 *************** *** 43,48 **** --- 43,66 ---- #endif + /* State shared by transformCreateSchemaStmt and its subroutines */ + typedef struct + { + const char *stmtType; /* "CREATE TABLE" or "ALTER TABLE" */ + char *schemaname; /* name of schema */ + char *authid; /* owner of schema */ + List *tables; /* CREATE TABLE items */ + List *views; /* CREATE VIEW items */ + List *grants; /* GRANT items */ + List *fwconstraints; /* Forward referencing FOREIGN KEY constraints */ + List *alters; /* Generated ALTER items (from the above) */ + List *ixconstraints; /* index-creating constraints */ + List *blist; /* "before list" of things to do before + * creating the schema */ + List *alist; /* "after list" of things to do after + * creating the schema */ + } CreateSchemaStmtContext; + /* State shared by transformCreateStmt and its subroutines */ typedef struct { *************** static Query *transformSelectStmt(ParseS *** 76,81 **** --- 94,101 ---- static Query *transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt); static Node *transformSetOperationTree(ParseState *pstate, SelectStmt *stmt); static Query *transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt); + static Query *transformCreateSchemaStmt(ParseState *pstate, CreateSchemaStmt *stmt, + List **extras_before, List **extras_after); static Query *transformCreateStmt(ParseState *pstate, CreateStmt *stmt, List **extras_before, List **extras_after); static Query *transformAlterTableStmt(ParseState *pstate, AlterTableStmt *stmt, *************** parse_analyze(Node *parseTree, ParseStat *** 130,135 **** --- 150,159 ---- query = transformStmt(pstate, parseTree, &extras_before, &extras_after); release_pstate_resources(pstate); + + /* Check if we are done processing this parseTree */ + if (query == NULL) + return NIL; while (extras_before != NIL) { *************** transformStmt(ParseState *pstate, Node * *** 186,191 **** --- 210,225 ---- /* * Non-optimizable statements */ + case T_CreateSchemaStmt: + /* + * transformCreateSchemaStmt sets the parseTree nodeTag to + * T_Invalid when its done; otherwise the parseTree + * is modified to contain what still has to be done + */ + result = transformCreateSchemaStmt(pstate, (CreateSchemaStmt *) parseTree, + extras_before, extras_after); + break; + case T_CreateStmt: result = transformCreateStmt(pstate, (CreateStmt *) parseTree, extras_before, extras_after); *************** CreateIndexName(char *table_name, char * *** 671,676 **** --- 705,797 ---- } return iname; + } + + /* + * transformCreateSchemaStmt - + * transforms the "create schema" statement + * + * Split the schema element list into individual commands and place + * them in the extras_after list in an order such that there is no + * forward references (e.g. GRANT to a table created after in the list). + * SQL92also allows constraints to make forward references, so thumb through + * the table columns and move forward references to a posterior alter table. + */ + static Query * + transformCreateSchemaStmt(ParseState *pstate, CreateSchemaStmt *stmt, + List **extras_before, List **extras_after) + { + CreateSchemaStmtContext cxt; + Query *q; + List *elements; + + cxt.stmtType = "CREATE SCHEMA"; + cxt.schemaname = stmt->schemaname; + cxt.authid = stmt->authid; + cxt.tables = NIL; + cxt.views = NIL; + cxt.grants = NIL; + cxt.fwconstraints = NIL; + cxt.alters = NIL; + cxt.blist = NIL; + cxt.alist = NIL; + + /* + * Run through each schema element in the schema element list. + * Separate statements by type, and do preliminary analysis. + */ + foreach(elements, stmt->schemaElts) + { + Node *element = lfirst(elements); + + switch (nodeTag(element)) + { + case T_CreateStmt: + cxt.tables = lappend(cxt.tables, element); + break; + + case T_ViewStmt: + cxt.views = lappend(cxt.views, element); + break; + + case T_GrantStmt: + cxt.grants = lappend(cxt.grants, element); + break; + + default: + elog(ERROR, "parser: unrecognized schema node (internal error)"); + } + } + + if (cxt.tables != NIL) + { + cxt.alist = nconc(cxt.alist, cxt.tables); + stmt->schemaElts = cxt.views; + stmt->schemaElts = nconc(stmt->schemaElts, cxt.grants); + } else if (cxt.views != NIL) + { + /* Views have to be processed one at a time */ + cxt.alist = makeList1(lfirst(cxt.views)); + stmt->schemaElts = nconc(lnext(cxt.views), cxt.grants); + } else if (cxt.grants != NIL) + { + cxt.alist = nconc(cxt.alist, cxt.grants); + stmt->schemaElts = NIL; + } else if (stmt->in_progress) + /* ProcessUtility marks the T_CreateSchemaStmt as in progress so + we do not try to create the schema again */ + return NULL; + + /* + * Output results. + */ + q = makeNode(Query); + q->commandType = CMD_UTILITY; + q->utilityStmt = (Node *) stmt; + *extras_before = nconc (*extras_before, cxt.blist); + *extras_after = nconc (cxt.alist, *extras_after); + + return q; } /* Index: src/backend/parser/gram.y =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/parser/gram.y,v retrieving revision 2.281 diff -c -p -r2.281 gram.y *** src/backend/parser/gram.y 2002/02/25 03:37:14 2.281 --- src/backend/parser/gram.y 2002/02/28 20:46:36 *************** static void doNegateFloat(Value *v); *** 129,135 **** InsertStmt *istmt; } ! %type <node> stmt, AlterGroupStmt, AlterSchemaStmt, AlterTableStmt, AlterUserStmt, AnalyzeStmt, ClosePortalStmt, ClusterStmt, CommentStmt, ConstraintsSetStmt, --- 129,135 ---- InsertStmt *istmt; } ! %type <node> stmt, schema_stmt, nonschema_stmt, AlterGroupStmt, AlterSchemaStmt, AlterTableStmt, AlterUserStmt, AnalyzeStmt, ClosePortalStmt, ClusterStmt, CommentStmt, ConstraintsSetStmt, *************** static void doNegateFloat(Value *v); *** 166,171 **** --- 166,174 ---- %type <list> OptUserList %type <defelt> OptUserElem + %type <str> OptSchemaName + %type <list> OptSchemaEltList + %type <boolean> TriggerActionTime, TriggerForSpec, opt_trusted, opt_procedural %type <str> opt_lancompiler *************** static void doNegateFloat(Value *v); *** 176,182 **** %type <str> relation_name, copy_file_name, copy_delimiter, copy_null, database_name, access_method_clause, access_method, attr_name, ! class, index_name, name, func_name, file_name %type <str> opt_id, all_Op, MathOp, opt_name, --- 179,185 ---- %type <str> relation_name, copy_file_name, copy_delimiter, copy_null, database_name, access_method_clause, access_method, attr_name, ! class, index_name, name, func_name, file_name, qualified_name %type <str> opt_id, all_Op, MathOp, opt_name, *************** stmtmulti: stmtmulti ';' stmt *** 435,448 **** $$ = NIL; } ; ! stmt : AlterSchemaStmt | AlterTableStmt | AlterGroupStmt | AlterUserStmt | ClosePortalStmt | CopyStmt - | CreateStmt | CreateAsStmt | CreateSchemaStmt | CreateGroupStmt --- 438,462 ---- $$ = NIL; } ; + + /* + * schema_stmt are the ones that can show up inside a CREATE SCHEMA + * statement (in addition to by themselves). + */ + stmt: schema_stmt | nonschema_stmt + ; ! schema_stmt : CreateStmt ! | GrantStmt ! | ViewStmt ! ; ! ! nonschema_stmt : AlterSchemaStmt | AlterTableStmt | AlterGroupStmt | AlterUserStmt | ClosePortalStmt | CopyStmt | CreateAsStmt | CreateSchemaStmt | CreateGroupStmt *************** stmt : AlterSchemaStmt *** 462,468 **** | DropUserStmt | ExplainStmt | FetchStmt - | GrantStmt | IndexStmt | ListenStmt | UnlistenStmt --- 476,481 ---- *************** stmt : AlterSchemaStmt *** 478,484 **** | OptimizableStmt | RuleStmt | TransactionStmt - | ViewStmt | LoadStmt | CreatedbStmt | DropdbStmt --- 491,496 ---- *************** DropGroupStmt: DROP GROUP UserId *** 729,743 **** * *****************************************************************************/ ! CreateSchemaStmt: CREATE SCHEMA UserId { ! /* for now, just make this the same as CREATE DATABASE */ ! CreatedbStmt *n = makeNode(CreatedbStmt); ! n->dbname = $3; ! n->dbowner = NULL; ! n->dbpath = NULL; ! n->dbtemplate = NULL; ! n->encoding = -1; $$ = (Node *)n; } ; --- 741,767 ---- * *****************************************************************************/ ! CreateSchemaStmt: CREATE SCHEMA OptSchemaName AUTHORIZATION UserId OptSchemaEltList { ! CreateSchemaStmt *n = makeNode(CreateSchemaStmt); ! /* One can omit the schema name or the authorization id... */ ! if ($3 != NULL) ! n->schemaname = $3; ! else ! n->schemaname = $5; ! n->authid = $5; ! n->schemaElts = $6; ! n->in_progress = false; ! $$ = (Node *)n; ! } ! | CREATE SCHEMA ColId OptSchemaEltList ! { ! CreateSchemaStmt *n = makeNode(CreateSchemaStmt); ! /* ...but not both */ ! n->schemaname = $3; ! n->authid = NULL; ! n->schemaElts = $4; ! n->in_progress = false; $$ = (Node *)n; } ; *************** AlterSchemaStmt: ALTER SCHEMA UserId *** 750,760 **** DropSchemaStmt: DROP SCHEMA UserId { ! DropdbStmt *n = makeNode(DropdbStmt); ! n->dbname = $3; ! $$ = (Node *)n; } /***************************************************************************** * --- 774,794 ---- DropSchemaStmt: DROP SCHEMA UserId { ! elog(ERROR, "DROP SCHEMA not yet supported"); } + OptSchemaName: ColId { $$ = $1 } + | /* EMPTY */ { $$ = NULL; } + ; + + OptSchemaEltList: OptSchemaEltList schema_stmt + { if ($1 == NIL) + $$ = makeList1($2); + else + $$ = lappend($1, $2); + } + | /* EMPTY */ { $$ = NIL; } + ; /***************************************************************************** * *************** copy_null: WITH NULL_P AS Sconst *** 1253,1259 **** * *****************************************************************************/ ! CreateStmt: CREATE OptTemp TABLE relation_name '(' OptTableElementList ')' OptInherit OptWithOids { CreateStmt *n = makeNode(CreateStmt); n->relname = $4; --- 1287,1293 ---- * *****************************************************************************/ ! CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')' OptInherit OptWithOids { CreateStmt *n = makeNode(CreateStmt); n->relname = $4; *************** OptWithOids: WITH OIDS { $$ = TRUE *** 1616,1622 **** * SELECT ... INTO. */ ! CreateAsStmt: CREATE OptTemp TABLE relation_name OptCreateAs AS SelectStmt { /* * When the SelectStmt is a set-operation tree, we must --- 1650,1656 ---- * SELECT ... INTO. */ ! CreateAsStmt: CREATE OptTemp TABLE qualified_name OptCreateAs AS SelectStmt { /* * When the SelectStmt is a set-operation tree, we must *************** opt_chain: AND NO CHAIN *** 3010,3016 **** * *****************************************************************************/ ! ViewStmt: CREATE VIEW name opt_column_list AS SelectStmt { ViewStmt *n = makeNode(ViewStmt); n->viewname = $3; --- 3044,3050 ---- * *****************************************************************************/ ! ViewStmt: CREATE VIEW qualified_name opt_column_list AS SelectStmt { ViewStmt *n = makeNode(ViewStmt); n->viewname = $3; *************** join_qual: USING '(' name_list ')' { *** 4049,4055 **** ; ! relation_expr: relation_name { /* default inheritance */ $$ = makeNode(RangeVar); --- 4083,4089 ---- ; ! relation_expr: qualified_name { /* default inheritance */ $$ = makeNode(RangeVar); *************** relation_expr: relation_name *** 4057,4063 **** $$->inhOpt = INH_DEFAULT; $$->name = NULL; } ! | relation_name '*' { /* inheritance query */ $$ = makeNode(RangeVar); --- 4091,4097 ---- $$->inhOpt = INH_DEFAULT; $$->name = NULL; } ! | qualified_name '*' { /* inheritance query */ $$ = makeNode(RangeVar); *************** relation_expr: relation_name *** 4065,4071 **** $$->inhOpt = INH_YES; $$->name = NULL; } ! | ONLY relation_name { /* no inheritance */ $$ = makeNode(RangeVar); --- 4099,4105 ---- $$->inhOpt = INH_YES; $$->name = NULL; } ! | ONLY qualified_name { /* no inheritance */ $$ = makeNode(RangeVar); *************** relation_name: SpecialRuleRelation *** 5611,5616 **** --- 5645,5660 ---- } ; + qualified_name: ColId + { + $$ = $1; + } + | ColId '.' ColId + { + $$ = $3; + } + ; + name: ColId { $$ = $1; }; database_name: ColId { $$ = $1; }; access_method: ColId { $$ = $1; }; *************** unreserved_keyword: *** 5791,5797 **** | AGGREGATE { $$ = "aggregate"; } | ALTER { $$ = "alter"; } | AT { $$ = "at"; } - | AUTHORIZATION { $$ = "authorization"; } | BACKWARD { $$ = "backward"; } | BEFORE { $$ = "before"; } | BEGIN_TRANS { $$ = "begin"; } --- 5835,5840 ---- *************** unreserved_keyword: *** 5808,5814 **** | COMMITTED { $$ = "committed"; } | CONSTRAINTS { $$ = "constraints"; } | COPY { $$ = "copy"; } - | CREATE { $$ = "create"; } | CREATEDB { $$ = "createdb"; } | CREATEUSER { $$ = "createuser"; } | CURSOR { $$ = "cursor"; } --- 5851,5856 ---- *************** unreserved_keyword: *** 5833,5839 **** | FORWARD { $$ = "forward"; } | FUNCTION { $$ = "function"; } | GLOBAL { $$ = "global"; } - | GRANT { $$ = "grant"; } | HANDLER { $$ = "handler"; } | HOUR_P { $$ = "hour"; } | IMMEDIATE { $$ = "immediate"; } --- 5875,5880 ---- *************** unreserved_keyword: *** 5911,5917 **** | STDIN { $$ = "stdin"; } | STDOUT { $$ = "stdout"; } | SYSID { $$ = "sysid"; } - | TEMP { $$ = "temp"; } | TEMPLATE { $$ = "template"; } | TEMPORARY { $$ = "temporary"; } | TOAST { $$ = "toast"; } --- 5952,5957 ---- *************** col_name_keyword: *** 5967,5972 **** --- 6007,6013 ---- | POSITION { $$ = "position"; } | SETOF { $$ = "setof"; } | SUBSTRING { $$ = "substring"; } + | TEMP { $$ = "temp"; } | TIME { $$ = "time"; } | TIMESTAMP { $$ = "timestamp"; } | TRIM { $$ = "trim"; } *************** col_name_keyword: *** 5984,5990 **** * - thomas 2000-11-28 */ func_name_keyword: ! BETWEEN { $$ = "between"; } | BINARY { $$ = "binary"; } | CROSS { $$ = "cross"; } | FREEZE { $$ = "freeze"; } --- 6025,6032 ---- * - thomas 2000-11-28 */ func_name_keyword: ! AUTHORIZATION { $$ = "authorization"; } ! | BETWEEN { $$ = "between"; } | BINARY { $$ = "binary"; } | CROSS { $$ = "cross"; } | FREEZE { $$ = "freeze"; } *************** reserved_keyword: *** 6027,6032 **** --- 6069,6075 ---- | COLLATE { $$ = "collate"; } | COLUMN { $$ = "column"; } | CONSTRAINT { $$ = "constraint"; } + | CREATE { $$ = "create"; } | CURRENT_DATE { $$ = "current_date"; } | CURRENT_TIME { $$ = "current_time"; } | CURRENT_TIMESTAMP { $$ = "current_timestamp"; } *************** reserved_keyword: *** 6043,6048 **** --- 6086,6092 ---- | FOR { $$ = "for"; } | FOREIGN { $$ = "foreign"; } | FROM { $$ = "from"; } + | GRANT { $$ = "grant"; } | GROUP { $$ = "group"; } | HAVING { $$ = "having"; } | INITIALLY { $$ = "initially"; } Index: src/backend/tcop/utility.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/tcop/utility.c,v retrieving revision 1.127 diff -c -p -r1.127 utility.c *** src/backend/tcop/utility.c 2002/02/26 22:47:09 1.127 --- src/backend/tcop/utility.c 2002/02/28 20:46:36 *************** ProcessUtility(Node *parsetree, *** 220,225 **** --- 220,240 ---- * manipulation ******************************** * */ + case T_CreateSchemaStmt: + { + CreateSchemaStmt *stmt = (CreateSchemaStmt *) parsetree; + if (stmt->in_progress) + break; /* Has created the schema already in a previous pass */ + stmt->in_progress = true; + elog(NOTICE, "Schema %s owned by %s will be created", + stmt->schemaname, stmt->authid); + /* + * Let commands in the schema-element-list know about the schema + */ + CommandCounterIncrement(); + } + break; + case T_CreateStmt: DefineRelation((CreateStmt *) parsetree, RELKIND_RELATION); Index: src/include/nodes/parsenodes.h =================================================================== RCS file: /projects/cvsroot/pgsql/src/include/nodes/parsenodes.h,v retrieving revision 1.154 diff -c -p -r1.154 parsenodes.h *** src/include/nodes/parsenodes.h 2002/02/26 22:47:10 1.154 --- src/include/nodes/parsenodes.h 2002/02/28 20:46:38 *************** typedef enum InhOption *** 110,115 **** --- 110,132 ---- *****************************************************************************/ /* ---------------------- + * Create Schema Statement + * + * NOTE: The schemaElts list contain several of the other nodes before + * parse analysis. These become independent nodes after that and the + * list is empty. + * ---------------------- + */ + typedef struct CreateSchemaStmt + { + NodeTag type; + char *schemaname; /* the name of the schema to create */ + char *authid; /* the owner of the created schema */ + List *schemaElts; /* column definitions (list of ColumnDef) */ + bool in_progress; /* used by analyze */ + } CreateSchemaStmt; + + /* ---------------------- * Alter Table * * The fields are used in different ways by the different variants of
This self-modification of the CreateSchema statement makes my head hurt ... isn't there a cleaner way? I would really like to see us move towards a processing pipeline in which parse analysis, rewrite, planning, and execution steps each take their input data structures as *read only*. I know it's not like that today, but it needs to be so. If I were to enumerate the bugs we've had in the past because of violations of that rule, I'd still be composing this message at dinnertime. (And I still have a very long to-fix list of kluges, workarounds, and memory leaks that are traceable to the lack of read-only data structures.) I really really don't want to see any new work introducing new violations of the rule. regards, tom lane
Tom Lane wrote: > > This self-modification of the CreateSchema statement makes my head hurt > ... isn't there a cleaner way? > > I would really like to see us move towards a processing pipeline in > which parse analysis, rewrite, planning, and execution steps each take > their input data structures as *read only*. I know it's not like that > today, but it needs to be so. If I were to enumerate the bugs we've had > in the past because of violations of that rule, I'd still be composing > this message at dinnertime. (And I still have a very long to-fix list > of kluges, workarounds, and memory leaks that are traceable to the lack > of read-only data structures.) I really really don't want to see any > new work introducing new violations of the rule. > OK, I will tell you what the problem is and maybe you can help finding a solution. Here it is: The CREATE SCHEMA statement is actually a collection of object creation and privilege statements. What happens is that the creation of some objects, or the assignment of privileges, may depend on the existence of previous ones. Following your suggestion, I grouped them in classes and ordered them so most of the dependencies are solved. So far so good. But then VIEWs came into play. We can have: CREATE SCHEMA AUTHORIZATION HU CREATE TABLE STAFF (EMPNUM CHAR(3) NOT NULL UNIQUE, EMPNAME CHAR(20), GRADE DECIMAL(4), CITY CHAR(15)) CREATE VIEW STAFFV1 AS SELECT * FROM STAFF WHERE GRADE >= 12 CREATE VIEW STAFFV2 AS SELECT * FROM STAFF WHERE GRADE >= 12 The creation of the STAFFV1 view requires that the table STAFF has been created already, i.e., that the CREATE TABLE has been executed before it can be analyzed (I will explain why below, and that is where you may be able to help). Furthermore, the view STAFFV2 requires that the CREATE VIEW STAFFV1 has been executed already. That is why I had to iterate in the analyze-execute cycle. Here is where you can help: the reason I need the tables (or views) which are used to create a view to be created before I can analyze a new view is that it contains a SELECT statement and the analyze of a SELECT statement _opens_ the relation to do some checking on columns etc. If we could avoid that, I could just analyze the whole CREATE SCHEMA and then execute the (reordered) resulting commands as a whole. Here is where it happens: transformStmt() when called with T_ViewStmt calls itself for the SELECT statement that defines the view. transformSelectStmt() is then called, which calls transformFromClause(). transformFromClause() loops calling transformFromClauseItem() and it calls transformTableEntry(). transformTableEntry() calls addRangeTableEntry(), which is where the problem is. addRangeTableEntry() calls heap_open() on the relation we have not created yet (if we don't iterate as I did) and... BOOM!!! "relation xxxx does not exist", where xxxx is the relation previously appearing in the CREATE SCHEMA statement. addRangeTableEntry() has the following comment at the top: /* * Add an entry for a relation to the pstate's range table (p_rtable). * * If pstate is NULL, we just build an RTE and return it without adding it * to an rtable list. * * Note: formerly this checked for refname conflicts, but that's wrong. * Caller is responsible for checking for conflicts in the appropriate scope. */ Is there any way we can parse views without doing this? If we can, we can avoid the iteration between analyze-execute. Another thing: can this happen again with other objects when we add them to the list of things that can go into a create schema? I.e., can the analyze phase of an object depend on another from the same create schema statement to be created (statement executed) already? I felt sort of funny about keeping the state on the CreateSchemaStmt. But this statement is a really weird one and the other possibilities seemed worse. Everything considered, it was still the most logical place. I guess the only way to avoid this is to get rid of all heap_open() calls in the analyze phase, at least for statements that can appear in a create schema statement. Regards, Fernando -- Fernando Nasser Red Hat Canada Ltd. E-Mail: fnasser@redhat.com 2323 Yonge Street, Suite #300 Toronto, Ontario M4P 2C9
I assume you do _not_ want this applied as is is part of Tom's schema work, right? --------------------------------------------------------------------------- Fernando Nasser wrote: > ChnageLog: > > Adds the parsing of a CREATE SCHEMA statement. It is > still just syntactic sugar as we still have a single namespace. > We also now accept qualified names for tables and views (where > the > reference is for tables, not for columns) but we do nothing with > it. > > > -- > Fernando Nasser > Red Hat Canada Ltd. E-Mail: fnasser@redhat.com > 2323 Yonge Street, Suite #300 > Toronto, Ontario M4P 2C9 > Index: src/backend/nodes/copyfuncs.c > =================================================================== > RCS file: /projects/cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v > retrieving revision 1.163 > diff -c -p -r1.163 copyfuncs.c > *** src/backend/nodes/copyfuncs.c 2002/02/26 22:47:05 1.163 > --- src/backend/nodes/copyfuncs.c 2002/02/28 20:46:34 > *************** _copyReindexStmt(ReindexStmt *from) > *** 2508,2514 **** > --- 2508,2528 ---- > return newnode; > } > > + static CreateSchemaStmt * > + _copyCreateSchemaStmt(CreateSchemaStmt *from) > + { > + CreateSchemaStmt *newnode = makeNode(CreateSchemaStmt); > + > + newnode->schemaname = pstrdup(from->schemaname); > + if (from->authid) > + newnode->authid = pstrdup(from->authid); > + Node_Copy(from, newnode, schemaElts); > + newnode->in_progress = from->in_progress; > > + return newnode; > + } > + > + > /* **************************************************************** > * pg_list.h copy functions > * **************************************************************** > *************** copyObject(void *from) > *** 2905,2910 **** > --- 2919,2927 ---- > break; > case T_CheckPointStmt: > retval = (void *) makeNode(CheckPointStmt); > + break; > + case T_CreateSchemaStmt: > + retval = _copyCreateSchemaStmt(from); > break; > > case T_A_Expr: > Index: src/backend/nodes/equalfuncs.c > =================================================================== > RCS file: /projects/cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v > retrieving revision 1.111 > diff -c -p -r1.111 equalfuncs.c > *** src/backend/nodes/equalfuncs.c 2002/02/26 22:47:05 1.111 > --- src/backend/nodes/equalfuncs.c 2002/02/28 20:46:34 > *************** _equalReindexStmt(ReindexStmt *a, Reinde > *** 1369,1374 **** > --- 1369,1389 ---- > } > > static bool > + _equalCreateSchemaStmt(CreateSchemaStmt *a, CreateSchemaStmt *b) > + { > + if (!equalstr(a->schemaname, b->schemaname)) > + return false; > + if (!equalstr(a->authid, b->authid)) > + return false; > + if (!equal(a->schemaElts, b->schemaElts)) > + return false; > + if (a->in_progress != b->in_progress) > + return false; > + > + return true; > + } > + > + static bool > _equalAExpr(A_Expr *a, A_Expr *b) > { > if (a->oper != b->oper) > *************** equal(void *a, void *b) > *** 2050,2055 **** > --- 2065,2073 ---- > break; > case T_CheckPointStmt: > retval = true; > + break; > + case T_CreateSchemaStmt: > + retval = _equalCreateSchemaStmt(a, b); > break; > > case T_A_Expr: > Index: src/backend/parser/analyze.c > =================================================================== > RCS file: /projects/cvsroot/pgsql/src/backend/parser/analyze.c,v > retrieving revision 1.215 > diff -c -p -r1.215 analyze.c > *** src/backend/parser/analyze.c 2002/02/26 22:47:08 1.215 > --- src/backend/parser/analyze.c 2002/02/28 20:46:34 > *************** > *** 43,48 **** > --- 43,66 ---- > #endif > > > + /* State shared by transformCreateSchemaStmt and its subroutines */ > + typedef struct > + { > + const char *stmtType; /* "CREATE TABLE" or "ALTER TABLE" */ > + char *schemaname; /* name of schema */ > + char *authid; /* owner of schema */ > + List *tables; /* CREATE TABLE items */ > + List *views; /* CREATE VIEW items */ > + List *grants; /* GRANT items */ > + List *fwconstraints; /* Forward referencing FOREIGN KEY constraints */ > + List *alters; /* Generated ALTER items (from the above) */ > + List *ixconstraints; /* index-creating constraints */ > + List *blist; /* "before list" of things to do before > + * creating the schema */ > + List *alist; /* "after list" of things to do after > + * creating the schema */ > + } CreateSchemaStmtContext; > + > /* State shared by transformCreateStmt and its subroutines */ > typedef struct > { > *************** static Query *transformSelectStmt(ParseS > *** 76,81 **** > --- 94,101 ---- > static Query *transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt); > static Node *transformSetOperationTree(ParseState *pstate, SelectStmt *stmt); > static Query *transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt); > + static Query *transformCreateSchemaStmt(ParseState *pstate, CreateSchemaStmt *stmt, > + List **extras_before, List **extras_after); > static Query *transformCreateStmt(ParseState *pstate, CreateStmt *stmt, > List **extras_before, List **extras_after); > static Query *transformAlterTableStmt(ParseState *pstate, AlterTableStmt *stmt, > *************** parse_analyze(Node *parseTree, ParseStat > *** 130,135 **** > --- 150,159 ---- > > query = transformStmt(pstate, parseTree, &extras_before, &extras_after); > release_pstate_resources(pstate); > + > + /* Check if we are done processing this parseTree */ > + if (query == NULL) > + return NIL; > > while (extras_before != NIL) > { > *************** transformStmt(ParseState *pstate, Node * > *** 186,191 **** > --- 210,225 ---- > /* > * Non-optimizable statements > */ > + case T_CreateSchemaStmt: > + /* > + * transformCreateSchemaStmt sets the parseTree nodeTag to > + * T_Invalid when its done; otherwise the parseTree > + * is modified to contain what still has to be done > + */ > + result = transformCreateSchemaStmt(pstate, (CreateSchemaStmt *) parseTree, > + extras_before, extras_after); > + break; > + > case T_CreateStmt: > result = transformCreateStmt(pstate, (CreateStmt *) parseTree, > extras_before, extras_after); > *************** CreateIndexName(char *table_name, char * > *** 671,676 **** > --- 705,797 ---- > } > > return iname; > + } > + > + /* > + * transformCreateSchemaStmt - > + * transforms the "create schema" statement > + * > + * Split the schema element list into individual commands and place > + * them in the extras_after list in an order such that there is no > + * forward references (e.g. GRANT to a table created after in the list). > + * SQL92also allows constraints to make forward references, so thumb through > + * the table columns and move forward references to a posterior alter table. > + */ > + static Query * > + transformCreateSchemaStmt(ParseState *pstate, CreateSchemaStmt *stmt, > + List **extras_before, List **extras_after) > + { > + CreateSchemaStmtContext cxt; > + Query *q; > + List *elements; > + > + cxt.stmtType = "CREATE SCHEMA"; > + cxt.schemaname = stmt->schemaname; > + cxt.authid = stmt->authid; > + cxt.tables = NIL; > + cxt.views = NIL; > + cxt.grants = NIL; > + cxt.fwconstraints = NIL; > + cxt.alters = NIL; > + cxt.blist = NIL; > + cxt.alist = NIL; > + > + /* > + * Run through each schema element in the schema element list. > + * Separate statements by type, and do preliminary analysis. > + */ > + foreach(elements, stmt->schemaElts) > + { > + Node *element = lfirst(elements); > + > + switch (nodeTag(element)) > + { > + case T_CreateStmt: > + cxt.tables = lappend(cxt.tables, element); > + break; > + > + case T_ViewStmt: > + cxt.views = lappend(cxt.views, element); > + break; > + > + case T_GrantStmt: > + cxt.grants = lappend(cxt.grants, element); > + break; > + > + default: > + elog(ERROR, "parser: unrecognized schema node (internal error)"); > + } > + } > + > + if (cxt.tables != NIL) > + { > + cxt.alist = nconc(cxt.alist, cxt.tables); > + stmt->schemaElts = cxt.views; > + stmt->schemaElts = nconc(stmt->schemaElts, cxt.grants); > + } else if (cxt.views != NIL) > + { > + /* Views have to be processed one at a time */ > + cxt.alist = makeList1(lfirst(cxt.views)); > + stmt->schemaElts = nconc(lnext(cxt.views), cxt.grants); > + } else if (cxt.grants != NIL) > + { > + cxt.alist = nconc(cxt.alist, cxt.grants); > + stmt->schemaElts = NIL; > + } else if (stmt->in_progress) > + /* ProcessUtility marks the T_CreateSchemaStmt as in progress so > + we do not try to create the schema again */ > + return NULL; > + > + /* > + * Output results. > + */ > + q = makeNode(Query); > + q->commandType = CMD_UTILITY; > + q->utilityStmt = (Node *) stmt; > + *extras_before = nconc (*extras_before, cxt.blist); > + *extras_after = nconc (cxt.alist, *extras_after); > + > + return q; > } > > /* > Index: src/backend/parser/gram.y > =================================================================== > RCS file: /projects/cvsroot/pgsql/src/backend/parser/gram.y,v > retrieving revision 2.281 > diff -c -p -r2.281 gram.y > *** src/backend/parser/gram.y 2002/02/25 03:37:14 2.281 > --- src/backend/parser/gram.y 2002/02/28 20:46:36 > *************** static void doNegateFloat(Value *v); > *** 129,135 **** > InsertStmt *istmt; > } > > ! %type <node> stmt, > AlterGroupStmt, AlterSchemaStmt, AlterTableStmt, AlterUserStmt, > AnalyzeStmt, > ClosePortalStmt, ClusterStmt, CommentStmt, ConstraintsSetStmt, > --- 129,135 ---- > InsertStmt *istmt; > } > > ! %type <node> stmt, schema_stmt, nonschema_stmt, > AlterGroupStmt, AlterSchemaStmt, AlterTableStmt, AlterUserStmt, > AnalyzeStmt, > ClosePortalStmt, ClusterStmt, CommentStmt, ConstraintsSetStmt, > *************** static void doNegateFloat(Value *v); > *** 166,171 **** > --- 166,174 ---- > %type <list> OptUserList > %type <defelt> OptUserElem > > + %type <str> OptSchemaName > + %type <list> OptSchemaEltList > + > %type <boolean> TriggerActionTime, TriggerForSpec, opt_trusted, opt_procedural > %type <str> opt_lancompiler > > *************** static void doNegateFloat(Value *v); > *** 176,182 **** > > %type <str> relation_name, copy_file_name, copy_delimiter, copy_null, > database_name, access_method_clause, access_method, attr_name, > ! class, index_name, name, func_name, file_name > > %type <str> opt_id, > all_Op, MathOp, opt_name, > --- 179,185 ---- > > %type <str> relation_name, copy_file_name, copy_delimiter, copy_null, > database_name, access_method_clause, access_method, attr_name, > ! class, index_name, name, func_name, file_name, qualified_name > > %type <str> opt_id, > all_Op, MathOp, opt_name, > *************** stmtmulti: stmtmulti ';' stmt > *** 435,448 **** > $$ = NIL; > } > ; > > ! stmt : AlterSchemaStmt > | AlterTableStmt > | AlterGroupStmt > | AlterUserStmt > | ClosePortalStmt > | CopyStmt > - | CreateStmt > | CreateAsStmt > | CreateSchemaStmt > | CreateGroupStmt > --- 438,462 ---- > $$ = NIL; > } > ; > + > + /* > + * schema_stmt are the ones that can show up inside a CREATE SCHEMA > + * statement (in addition to by themselves). > + */ > + stmt: schema_stmt | nonschema_stmt > + ; > > ! schema_stmt : CreateStmt > ! | GrantStmt > ! | ViewStmt > ! ; > ! > ! nonschema_stmt : AlterSchemaStmt > | AlterTableStmt > | AlterGroupStmt > | AlterUserStmt > | ClosePortalStmt > | CopyStmt > | CreateAsStmt > | CreateSchemaStmt > | CreateGroupStmt > *************** stmt : AlterSchemaStmt > *** 462,468 **** > | DropUserStmt > | ExplainStmt > | FetchStmt > - | GrantStmt > | IndexStmt > | ListenStmt > | UnlistenStmt > --- 476,481 ---- > *************** stmt : AlterSchemaStmt > *** 478,484 **** > | OptimizableStmt > | RuleStmt > | TransactionStmt > - | ViewStmt > | LoadStmt > | CreatedbStmt > | DropdbStmt > --- 491,496 ---- > *************** DropGroupStmt: DROP GROUP UserId > *** 729,743 **** > * > *****************************************************************************/ > > ! CreateSchemaStmt: CREATE SCHEMA UserId > { > ! /* for now, just make this the same as CREATE DATABASE */ > ! CreatedbStmt *n = makeNode(CreatedbStmt); > ! n->dbname = $3; > ! n->dbowner = NULL; > ! n->dbpath = NULL; > ! n->dbtemplate = NULL; > ! n->encoding = -1; > $$ = (Node *)n; > } > ; > --- 741,767 ---- > * > *****************************************************************************/ > > ! CreateSchemaStmt: CREATE SCHEMA OptSchemaName AUTHORIZATION UserId OptSchemaEltList > { > ! CreateSchemaStmt *n = makeNode(CreateSchemaStmt); > ! /* One can omit the schema name or the authorization id... */ > ! if ($3 != NULL) > ! n->schemaname = $3; > ! else > ! n->schemaname = $5; > ! n->authid = $5; > ! n->schemaElts = $6; > ! n->in_progress = false; > ! $$ = (Node *)n; > ! } > ! | CREATE SCHEMA ColId OptSchemaEltList > ! { > ! CreateSchemaStmt *n = makeNode(CreateSchemaStmt); > ! /* ...but not both */ > ! n->schemaname = $3; > ! n->authid = NULL; > ! n->schemaElts = $4; > ! n->in_progress = false; > $$ = (Node *)n; > } > ; > *************** AlterSchemaStmt: ALTER SCHEMA UserId > *** 750,760 **** > > DropSchemaStmt: DROP SCHEMA UserId > { > ! DropdbStmt *n = makeNode(DropdbStmt); > ! n->dbname = $3; > ! $$ = (Node *)n; > } > > > /***************************************************************************** > * > --- 774,794 ---- > > DropSchemaStmt: DROP SCHEMA UserId > { > ! elog(ERROR, "DROP SCHEMA not yet supported"); > } > > + OptSchemaName: ColId { $$ = $1 } > + | /* EMPTY */ { $$ = NULL; } > + ; > + > + OptSchemaEltList: OptSchemaEltList schema_stmt > + { if ($1 == NIL) > + $$ = makeList1($2); > + else > + $$ = lappend($1, $2); > + } > + | /* EMPTY */ { $$ = NIL; } > + ; > > /***************************************************************************** > * > *************** copy_null: WITH NULL_P AS Sconst > *** 1253,1259 **** > * > *****************************************************************************/ > > ! CreateStmt: CREATE OptTemp TABLE relation_name '(' OptTableElementList ')' OptInherit OptWithOids > { > CreateStmt *n = makeNode(CreateStmt); > n->relname = $4; > --- 1287,1293 ---- > * > *****************************************************************************/ > > ! CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')' OptInherit OptWithOids > { > CreateStmt *n = makeNode(CreateStmt); > n->relname = $4; > *************** OptWithOids: WITH OIDS { $$ = TRUE > *** 1616,1622 **** > * SELECT ... INTO. > */ > > ! CreateAsStmt: CREATE OptTemp TABLE relation_name OptCreateAs AS SelectStmt > { > /* > * When the SelectStmt is a set-operation tree, we must > --- 1650,1656 ---- > * SELECT ... INTO. > */ > > ! CreateAsStmt: CREATE OptTemp TABLE qualified_name OptCreateAs AS SelectStmt > { > /* > * When the SelectStmt is a set-operation tree, we must > *************** opt_chain: AND NO CHAIN > *** 3010,3016 **** > * > *****************************************************************************/ > > ! ViewStmt: CREATE VIEW name opt_column_list AS SelectStmt > { > ViewStmt *n = makeNode(ViewStmt); > n->viewname = $3; > --- 3044,3050 ---- > * > *****************************************************************************/ > > ! ViewStmt: CREATE VIEW qualified_name opt_column_list AS SelectStmt > { > ViewStmt *n = makeNode(ViewStmt); > n->viewname = $3; > *************** join_qual: USING '(' name_list ')' { > *** 4049,4055 **** > ; > > > ! relation_expr: relation_name > { > /* default inheritance */ > $$ = makeNode(RangeVar); > --- 4083,4089 ---- > ; > > > ! relation_expr: qualified_name > { > /* default inheritance */ > $$ = makeNode(RangeVar); > *************** relation_expr: relation_name > *** 4057,4063 **** > $$->inhOpt = INH_DEFAULT; > $$->name = NULL; > } > ! | relation_name '*' > { > /* inheritance query */ > $$ = makeNode(RangeVar); > --- 4091,4097 ---- > $$->inhOpt = INH_DEFAULT; > $$->name = NULL; > } > ! | qualified_name '*' > { > /* inheritance query */ > $$ = makeNode(RangeVar); > *************** relation_expr: relation_name > *** 4065,4071 **** > $$->inhOpt = INH_YES; > $$->name = NULL; > } > ! | ONLY relation_name > { > /* no inheritance */ > $$ = makeNode(RangeVar); > --- 4099,4105 ---- > $$->inhOpt = INH_YES; > $$->name = NULL; > } > ! | ONLY qualified_name > { > /* no inheritance */ > $$ = makeNode(RangeVar); > *************** relation_name: SpecialRuleRelation > *** 5611,5616 **** > --- 5645,5660 ---- > } > ; > > + qualified_name: ColId > + { > + $$ = $1; > + } > + | ColId '.' ColId > + { > + $$ = $3; > + } > + ; > + > name: ColId { $$ = $1; }; > database_name: ColId { $$ = $1; }; > access_method: ColId { $$ = $1; }; > *************** unreserved_keyword: > *** 5791,5797 **** > | AGGREGATE { $$ = "aggregate"; } > | ALTER { $$ = "alter"; } > | AT { $$ = "at"; } > - | AUTHORIZATION { $$ = "authorization"; } > | BACKWARD { $$ = "backward"; } > | BEFORE { $$ = "before"; } > | BEGIN_TRANS { $$ = "begin"; } > --- 5835,5840 ---- > *************** unreserved_keyword: > *** 5808,5814 **** > | COMMITTED { $$ = "committed"; } > | CONSTRAINTS { $$ = "constraints"; } > | COPY { $$ = "copy"; } > - | CREATE { $$ = "create"; } > | CREATEDB { $$ = "createdb"; } > | CREATEUSER { $$ = "createuser"; } > | CURSOR { $$ = "cursor"; } > --- 5851,5856 ---- > *************** unreserved_keyword: > *** 5833,5839 **** > | FORWARD { $$ = "forward"; } > | FUNCTION { $$ = "function"; } > | GLOBAL { $$ = "global"; } > - | GRANT { $$ = "grant"; } > | HANDLER { $$ = "handler"; } > | HOUR_P { $$ = "hour"; } > | IMMEDIATE { $$ = "immediate"; } > --- 5875,5880 ---- > *************** unreserved_keyword: > *** 5911,5917 **** > | STDIN { $$ = "stdin"; } > | STDOUT { $$ = "stdout"; } > | SYSID { $$ = "sysid"; } > - | TEMP { $$ = "temp"; } > | TEMPLATE { $$ = "template"; } > | TEMPORARY { $$ = "temporary"; } > | TOAST { $$ = "toast"; } > --- 5952,5957 ---- > *************** col_name_keyword: > *** 5967,5972 **** > --- 6007,6013 ---- > | POSITION { $$ = "position"; } > | SETOF { $$ = "setof"; } > | SUBSTRING { $$ = "substring"; } > + | TEMP { $$ = "temp"; } > | TIME { $$ = "time"; } > | TIMESTAMP { $$ = "timestamp"; } > | TRIM { $$ = "trim"; } > *************** col_name_keyword: > *** 5984,5990 **** > * - thomas 2000-11-28 > */ > func_name_keyword: > ! BETWEEN { $$ = "between"; } > | BINARY { $$ = "binary"; } > | CROSS { $$ = "cross"; } > | FREEZE { $$ = "freeze"; } > --- 6025,6032 ---- > * - thomas 2000-11-28 > */ > func_name_keyword: > ! AUTHORIZATION { $$ = "authorization"; } > ! | BETWEEN { $$ = "between"; } > | BINARY { $$ = "binary"; } > | CROSS { $$ = "cross"; } > | FREEZE { $$ = "freeze"; } > *************** reserved_keyword: > *** 6027,6032 **** > --- 6069,6075 ---- > | COLLATE { $$ = "collate"; } > | COLUMN { $$ = "column"; } > | CONSTRAINT { $$ = "constraint"; } > + | CREATE { $$ = "create"; } > | CURRENT_DATE { $$ = "current_date"; } > | CURRENT_TIME { $$ = "current_time"; } > | CURRENT_TIMESTAMP { $$ = "current_timestamp"; } > *************** reserved_keyword: > *** 6043,6048 **** > --- 6086,6092 ---- > | FOR { $$ = "for"; } > | FOREIGN { $$ = "foreign"; } > | FROM { $$ = "from"; } > + | GRANT { $$ = "grant"; } > | GROUP { $$ = "group"; } > | HAVING { $$ = "having"; } > | INITIALLY { $$ = "initially"; } > Index: src/backend/tcop/utility.c > =================================================================== > RCS file: /projects/cvsroot/pgsql/src/backend/tcop/utility.c,v > retrieving revision 1.127 > diff -c -p -r1.127 utility.c > *** src/backend/tcop/utility.c 2002/02/26 22:47:09 1.127 > --- src/backend/tcop/utility.c 2002/02/28 20:46:36 > *************** ProcessUtility(Node *parsetree, > *** 220,225 **** > --- 220,240 ---- > * manipulation ******************************** > * > */ > + case T_CreateSchemaStmt: > + { > + CreateSchemaStmt *stmt = (CreateSchemaStmt *) parsetree; > + if (stmt->in_progress) > + break; /* Has created the schema already in a previous pass */ > + stmt->in_progress = true; > + elog(NOTICE, "Schema %s owned by %s will be created", > + stmt->schemaname, stmt->authid); > + /* > + * Let commands in the schema-element-list know about the schema > + */ > + CommandCounterIncrement(); > + } > + break; > + > case T_CreateStmt: > DefineRelation((CreateStmt *) parsetree, RELKIND_RELATION); > > Index: src/include/nodes/parsenodes.h > =================================================================== > RCS file: /projects/cvsroot/pgsql/src/include/nodes/parsenodes.h,v > retrieving revision 1.154 > diff -c -p -r1.154 parsenodes.h > *** src/include/nodes/parsenodes.h 2002/02/26 22:47:10 1.154 > --- src/include/nodes/parsenodes.h 2002/02/28 20:46:38 > *************** typedef enum InhOption > *** 110,115 **** > --- 110,132 ---- > *****************************************************************************/ > > /* ---------------------- > + * Create Schema Statement > + * > + * NOTE: The schemaElts list contain several of the other nodes before > + * parse analysis. These become independent nodes after that and the > + * list is empty. > + * ---------------------- > + */ > + typedef struct CreateSchemaStmt > + { > + NodeTag type; > + char *schemaname; /* the name of the schema to create */ > + char *authid; /* the owner of the created schema */ > + List *schemaElts; /* column definitions (list of ColumnDef) */ > + bool in_progress; /* used by analyze */ > + } CreateSchemaStmt; > + > + /* ---------------------- > * Alter Table > * > * The fields are used in different ways by the different variants of > > ---------------------------(end of broadcast)--------------------------- > TIP 1: subscribe and unsubscribe commands go to majordomo@postgresql.org -- Bruce Momjian | http://candle.pha.pa.us pgman@candle.pha.pa.us | (610) 853-3000 + If your life is a hard drive, | 830 Blythe Avenue + Christ can be your backup. | Drexel Hill, Pennsylvania 19026
Bruce Momjian <pgman@candle.pha.pa.us> writes: > I assume you do _not_ want this applied as is is part of Tom's schema > work, right? Might as well hold off. The schema stuff is currently happening in a private CVS branch at Red Hat, and we're gonna have to figure out how to merge with the project CVS when it's far enough along to justify it. Fernando has been trying to send in bite-size pieces to keep the branches from getting far apart, but I'm not sure if it's worth the trouble ... regards, tom lane