Adds the parsing of a CREATE SCHEMA statement - Mailing list pgsql-patches
| From | Fernando Nasser |
|---|---|
| Subject | Adds the parsing of a CREATE SCHEMA statement |
| Date | |
| Msg-id | 3C7F8A49.CC4EF0BE@redhat.com Whole thread Raw |
| Responses |
Re: Adds the parsing of a CREATE SCHEMA statement
Re: Adds the parsing of a CREATE SCHEMA statement |
| List | pgsql-patches |
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
pgsql-patches by date: