Re: Adds the parsing of a CREATE SCHEMA statement - Mailing list pgsql-patches
From | Bruce Momjian |
---|---|
Subject | Re: Adds the parsing of a CREATE SCHEMA statement |
Date | |
Msg-id | 200203111858.g2BIwHZ01523@candle.pha.pa.us Whole thread Raw |
In response to | Adds the parsing of a CREATE SCHEMA statement (Fernando Nasser <fnasser@redhat.com>) |
Responses |
Re: Adds the parsing of a CREATE SCHEMA statement
|
List | pgsql-patches |
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
pgsql-patches by date: