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 stmt, AlterGroupStmt, AlterSchemaStmt, AlterTableStmt, AlterUserStmt, AnalyzeStmt, ClosePortalStmt, ClusterStmt, CommentStmt, ConstraintsSetStmt, --- 129,135 ---- InsertStmt *istmt; } ! %type stmt, schema_stmt, nonschema_stmt, AlterGroupStmt, AlterSchemaStmt, AlterTableStmt, AlterUserStmt, AnalyzeStmt, ClosePortalStmt, ClusterStmt, CommentStmt, ConstraintsSetStmt, *************** static void doNegateFloat(Value *v); *** 166,171 **** --- 166,174 ---- %type OptUserList %type OptUserElem + %type OptSchemaName + %type OptSchemaEltList + %type TriggerActionTime, TriggerForSpec, opt_trusted, opt_procedural %type opt_lancompiler *************** static void doNegateFloat(Value *v); *** 176,182 **** %type 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 opt_id, all_Op, MathOp, opt_name, --- 179,185 ---- %type 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 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