diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c index 4bce6ee914f9b24e6f9058c4ce2cec9780d844d0..f0e717445a6ba53eecfa860d938de048c9eeee08 100644 --- a/src/backend/access/common/tupdesc.c +++ b/src/backend/access/common/tupdesc.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/access/common/tupdesc.c,v 1.54 1999/07/17 20:16:36 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/access/common/tupdesc.c,v 1.55 1999/10/03 23:55:25 tgl Exp $ * * NOTES * some of the executor utility code such as "ExecTypeFromTL" should be @@ -160,8 +160,6 @@ CreateTupleDescCopyConstr(TupleDesc tupdesc) { if (constr->defval[i].adbin) cpy->defval[i].adbin = pstrdup(constr->defval[i].adbin); - if (constr->defval[i].adsrc) - cpy->defval[i].adsrc = pstrdup(constr->defval[i].adsrc); } } @@ -175,8 +173,6 @@ CreateTupleDescCopyConstr(TupleDesc tupdesc) cpy->check[i].ccname = pstrdup(constr->check[i].ccname); if (constr->check[i].ccbin) cpy->check[i].ccbin = pstrdup(constr->check[i].ccbin); - if (constr->check[i].ccsrc) - cpy->check[i].ccsrc = pstrdup(constr->check[i].ccsrc); } } @@ -206,8 +202,6 @@ FreeTupleDesc(TupleDesc tupdesc) { if (attrdef[i].adbin) pfree(attrdef[i].adbin); - if (attrdef[i].adsrc) - pfree(attrdef[i].adsrc); } pfree(attrdef); } @@ -221,8 +215,6 @@ FreeTupleDesc(TupleDesc tupdesc) pfree(check[i].ccname); if (check[i].ccbin) pfree(check[i].ccbin); - if (check[i].ccsrc) - pfree(check[i].ccsrc); } pfree(check); } @@ -438,7 +430,7 @@ BuildDescForRelation(List *schema, char *relname) AttrDefault *attrdef = NULL; TupleConstr *constr = (TupleConstr *) palloc(sizeof(TupleConstr)); char *attname; - char *typename; + char typename[NAMEDATALEN]; int32 atttypmod; int attdim; int ndef = 0; @@ -454,11 +446,9 @@ BuildDescForRelation(List *schema, char *relname) attnum = 0; - typename = palloc(NAMEDATALEN); - foreach(p, schema) { - ColumnDef *entry; + ColumnDef *entry = lfirst(p); List *arry; /* ---------------- @@ -469,7 +459,6 @@ BuildDescForRelation(List *schema, char *relname) */ attnum++; - entry = lfirst(p); attname = entry->colname; arry = entry->typename->arrayBounds; attisset = entry->typename->setof; @@ -513,13 +502,15 @@ BuildDescForRelation(List *schema, char *relname) constr->has_not_null = true; desc->attrs[attnum - 1]->attnotnull = entry->is_not_null; - if (entry->defval != NULL) + /* Note we copy only pre-cooked default expressions. + * Digestion of raw ones is someone else's problem. + */ + if (entry->cooked_default != NULL) { if (attrdef == NULL) attrdef = (AttrDefault *) palloc(natts * sizeof(AttrDefault)); attrdef[ndef].adnum = attnum; - attrdef[ndef].adbin = NULL; - attrdef[ndef].adsrc = entry->defval; + attrdef[ndef].adbin = pstrdup(entry->cooked_default); ndef++; desc->attrs[attnum - 1]->atthasdef = true; } @@ -539,7 +530,11 @@ BuildDescForRelation(List *schema, char *relname) constr->num_defval = ndef; } else + { + constr->defval = NULL; constr->num_defval = 0; + } + constr->check = NULL; constr->num_check = 0; } else diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index 5ad0f7c413b1965131588b5835c8843b20c0a5b7..5cb9da1cae1b633f840a665c36ff3810bd901f70 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.99 1999/09/29 16:05:56 wieck Exp $ + * $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.100 1999/10/03 23:55:26 tgl Exp $ * * * INTERFACE ROUTINES @@ -44,9 +44,14 @@ #include "catalog/pg_proc.h" #include "catalog/pg_relcheck.h" #include "commands/trigger.h" +#include "optimizer/clauses.h" +#include "optimizer/planmain.h" #include "optimizer/tlist.h" +#include "optimizer/var.h" +#include "parser/parse_clause.h" #include "parser/parse_coerce.h" #include "parser/parse_expr.h" +#include "parser/parse_relation.h" #include "rewrite/rewriteRemove.h" #include "storage/smgr.h" #include "tcop/tcopprot.h" @@ -68,6 +73,9 @@ static void RelationRemoveIndexes(Relation relation); static void RelationRemoveInheritance(Relation relation); static void RemoveFromNoNameRelList(Relation r); static void AddNewRelationType(char *typeName, Oid new_rel_oid); +static void StoreAttrDefault(Relation rel, AttrNumber attnum, char *adbin, + bool updatePgAttribute); +static void StoreRelCheck(Relation rel, char *ccname, char *ccbin); static void StoreConstraints(Relation rel); static void RemoveConstraints(Relation rel); @@ -1650,86 +1658,66 @@ DestroyNoNameRels(void) tempRels = NULL; } - +/* + * Store a default expression for column attnum of relation rel. + * The expression must be presented as a nodeToString() string. + * If updatePgAttribute is true, update the pg_attribute entry + * for the column to show that a default exists. + */ static void -StoreAttrDefault(Relation rel, AttrDefault *attrdef) +StoreAttrDefault(Relation rel, AttrNumber attnum, char *adbin, + bool updatePgAttribute) { - char str[MAX_PARSE_BUFFER]; - char cast[2 * NAMEDATALEN] = {0}; - Form_pg_attribute atp = rel->rd_att->attrs[attrdef->adnum - 1]; - List *queryTree_list; - List *planTree_list; - Query *query; - TargetEntry *te; - Resdom *resdom; + Form_pg_attribute atp = rel->rd_att->attrs[attnum - 1]; Node *expr; Oid type; - char *adbin; - MemoryContext oldcxt; + RangeTblEntry *rte; + char *adsrc; Relation adrel; Relation idescs[Num_pg_attrdef_indices]; HeapTuple tuple; Datum values[4]; - char nulls[4] = {' ', ' ', ' ', ' '}; - extern GlobalMemory CacheCxt; - -start: + static char nulls[4] = {' ', ' ', ' ', ' '}; + Relation attrrel; + Relation attridescs[Num_pg_attr_indices]; + HeapTuple atttup; + Form_pg_attribute attStruct; - /* - * Surround table name with double quotes to allow mixed-case and - * whitespaces in names. - BGA 1998-11-14 - */ - snprintf(str, MAX_PARSE_BUFFER, - "select %s%s from \"%.*s\"", attrdef->adsrc, cast, - NAMEDATALEN, rel->rd_rel->relname.data); - setheapoverride(true); - planTree_list = pg_parse_and_plan(str, NULL, 0, - &queryTree_list, None, FALSE); - setheapoverride(false); - query = (Query *) lfirst(queryTree_list); - - if (length(query->rtable) > 1 || - flatten_tlist(query->targetList) != NIL) - elog(ERROR, "Cannot use attribute(s) in DEFAULT clause"); - te = (TargetEntry *) lfirst(query->targetList); - resdom = te->resdom; - expr = te->expr; + expr = stringToNode(adbin); type = exprType(expr); if (type != atp->atttypid) { - if (IS_BINARY_COMPATIBLE(type, atp->atttypid)) - ; /* use without change */ - else if (can_coerce_type(1, &(type), &(atp->atttypid))) - expr = coerce_type(NULL, (Node *) expr, type, atp->atttypid, - atp->atttypmod); - else if (IsA(expr, Const)) - { - if (*cast != 0) - elog(ERROR, "DEFAULT clause const type '%s' mismatched with column type '%s'", - typeidTypeName(type), typeidTypeName(atp->atttypid)); - snprintf(cast, 2 * NAMEDATALEN, ":: %s", typeidTypeName(atp->atttypid)); - goto start; - } - else - elog(ERROR, "DEFAULT clause type '%s' mismatched with column type '%s'", - typeidTypeName(type), typeidTypeName(atp->atttypid)); + /* + * Check that it will be possible to coerce the expression + * to the column's type. We store the expression without + * coercion, however, to avoid premature coercion in cases like + * CREATE TABLE tbl (fld datetime DEFAULT 'now'); + */ + coerce_type(NULL, expr, type, atp->atttypid, atp->atttypmod); } - adbin = nodeToString(expr); - oldcxt = MemoryContextSwitchTo((MemoryContext) CacheCxt); - attrdef->adbin = pstrdup(adbin); - (void) MemoryContextSwitchTo(oldcxt); - pfree(adbin); + /* + * deparse_expression needs a RangeTblEntry list, so make one + */ + rte = makeNode(RangeTblEntry); + rte->relname = RelationGetRelationName(rel)->data; + rte->refname = RelationGetRelationName(rel)->data; + rte->relid = RelationGetRelid(rel); + rte->inh = false; + rte->inFromCl = true; + rte->skipAcl = false; + adsrc = deparse_expression(expr, lcons(lcons(rte, NIL), NIL), false); values[Anum_pg_attrdef_adrelid - 1] = rel->rd_id; - values[Anum_pg_attrdef_adnum - 1] = attrdef->adnum; - values[Anum_pg_attrdef_adbin - 1] = PointerGetDatum(textin(attrdef->adbin)); - values[Anum_pg_attrdef_adsrc - 1] = PointerGetDatum(textin(attrdef->adsrc)); + values[Anum_pg_attrdef_adnum - 1] = attnum; + values[Anum_pg_attrdef_adbin - 1] = PointerGetDatum(textin(adbin)); + values[Anum_pg_attrdef_adsrc - 1] = PointerGetDatum(textin(adsrc)); adrel = heap_openr(AttrDefaultRelationName, RowExclusiveLock); tuple = heap_formtuple(adrel->rd_att, values, nulls); - CatalogOpenIndices(Num_pg_attrdef_indices, Name_pg_attrdef_indices, idescs); heap_insert(adrel, tuple); + CatalogOpenIndices(Num_pg_attrdef_indices, Name_pg_attrdef_indices, + idescs); CatalogIndexInsert(idescs, Num_pg_attrdef_indices, adrel, tuple); CatalogCloseIndices(Num_pg_attrdef_indices, idescs); heap_close(adrel, RowExclusiveLock); @@ -1737,62 +1725,78 @@ start: pfree(DatumGetPointer(values[Anum_pg_attrdef_adbin - 1])); pfree(DatumGetPointer(values[Anum_pg_attrdef_adsrc - 1])); pfree(tuple); - + pfree(adsrc); + + if (! updatePgAttribute) + return; /* done if pg_attribute is OK */ + + attrrel = heap_openr(AttributeRelationName, RowExclusiveLock); + atttup = SearchSysCacheTupleCopy(ATTNUM, + ObjectIdGetDatum(rel->rd_id), + (Datum) attnum, 0, 0); + if (!HeapTupleIsValid(atttup)) + elog(ERROR, "cache lookup of attribute %d in relation %u failed", + attnum, rel->rd_id); + attStruct = (Form_pg_attribute) GETSTRUCT(atttup); + if (! attStruct->atthasdef) + { + attStruct->atthasdef = true; + heap_replace(attrrel, &atttup->t_self, atttup, NULL); + /* keep catalog indices current */ + CatalogOpenIndices(Num_pg_attr_indices, Name_pg_attr_indices, + attridescs); + CatalogIndexInsert(attridescs, Num_pg_attr_indices, attrrel, atttup); + CatalogCloseIndices(Num_pg_attr_indices, attridescs); + } + heap_close(attrrel, RowExclusiveLock); + pfree(atttup); } +/* + * Store a constraint expression for the given relation. + * The expression must be presented as a nodeToString() string. + * + * Caller is responsible for updating the count of constraints + * in the pg_class entry for the relation. + */ static void -StoreRelCheck(Relation rel, ConstrCheck *check) +StoreRelCheck(Relation rel, char *ccname, char *ccbin) { - char str[MAX_PARSE_BUFFER]; - List *queryTree_list; - List *planTree_list; - Query *query; - Plan *plan; - List *qual; - char *ccbin; - MemoryContext oldcxt; + Node *expr; + RangeTblEntry *rte; + char *ccsrc; Relation rcrel; Relation idescs[Num_pg_relcheck_indices]; HeapTuple tuple; Datum values[4]; - char nulls[4] = {' ', ' ', ' ', ' '}; - extern GlobalMemory CacheCxt; + static char nulls[4] = {' ', ' ', ' ', ' '}; /* - * Check for table's existance. Surround table name with double-quotes - * to allow mixed-case and whitespace names. - thomas 1998-11-12 + * Convert condition to a normal boolean expression tree. */ - snprintf(str, MAX_PARSE_BUFFER, - "select 1 from \"%.*s\" where %s", - NAMEDATALEN, rel->rd_rel->relname.data, check->ccsrc); - setheapoverride(true); - planTree_list = pg_parse_and_plan(str, NULL, 0, - &queryTree_list, None, FALSE); - setheapoverride(false); - query = (Query *) lfirst(queryTree_list); - - if (length(query->rtable) > 1) - elog(ERROR, "Only relation '%.*s' can be referenced", - NAMEDATALEN, rel->rd_rel->relname.data); - - plan = (Plan *) lfirst(planTree_list); - qual = plan->qual; - - ccbin = nodeToString(qual); - oldcxt = MemoryContextSwitchTo((MemoryContext) CacheCxt); - check->ccbin = (char *) palloc(strlen(ccbin) + 1); - strcpy(check->ccbin, ccbin); - (void) MemoryContextSwitchTo(oldcxt); - pfree(ccbin); + expr = stringToNode(ccbin); + expr = (Node *) make_ands_explicit((List *) expr); + /* + * deparse_expression needs a RangeTblEntry list, so make one + */ + rte = makeNode(RangeTblEntry); + rte->relname = RelationGetRelationName(rel)->data; + rte->refname = RelationGetRelationName(rel)->data; + rte->relid = RelationGetRelid(rel); + rte->inh = false; + rte->inFromCl = true; + rte->skipAcl = false; + ccsrc = deparse_expression(expr, lcons(lcons(rte, NIL), NIL), false); values[Anum_pg_relcheck_rcrelid - 1] = rel->rd_id; - values[Anum_pg_relcheck_rcname - 1] = PointerGetDatum(namein(check->ccname)); - values[Anum_pg_relcheck_rcbin - 1] = PointerGetDatum(textin(check->ccbin)); - values[Anum_pg_relcheck_rcsrc - 1] = PointerGetDatum(textin(check->ccsrc)); + values[Anum_pg_relcheck_rcname - 1] = PointerGetDatum(namein(ccname)); + values[Anum_pg_relcheck_rcbin - 1] = PointerGetDatum(textin(ccbin)); + values[Anum_pg_relcheck_rcsrc - 1] = PointerGetDatum(textin(ccsrc)); rcrel = heap_openr(RelCheckRelationName, RowExclusiveLock); tuple = heap_formtuple(rcrel->rd_att, values, nulls); - CatalogOpenIndices(Num_pg_relcheck_indices, Name_pg_relcheck_indices, idescs); heap_insert(rcrel, tuple); + CatalogOpenIndices(Num_pg_relcheck_indices, Name_pg_relcheck_indices, + idescs); CatalogIndexInsert(idescs, Num_pg_relcheck_indices, rcrel, tuple); CatalogCloseIndices(Num_pg_relcheck_indices, idescs); heap_close(rcrel, RowExclusiveLock); @@ -1801,8 +1805,19 @@ StoreRelCheck(Relation rel, ConstrCheck *check) pfree(DatumGetPointer(values[Anum_pg_relcheck_rcbin - 1])); pfree(DatumGetPointer(values[Anum_pg_relcheck_rcsrc - 1])); pfree(tuple); + pfree(ccsrc); } +/* + * Store defaults and constraints passed in via the tuple constraint struct. + * + * NOTE: only pre-cooked expressions will be passed this way, which is to + * say expressions inherited from an existing relation. Newly parsed + * expressions can be added later, by direct calls to StoreAttrDefault + * and StoreRelCheck (see AddRelationRawConstraints()). We assume that + * pg_attribute and pg_class entries for the relation were already set + * to reflect the existence of these defaults/constraints. + */ static void StoreConstraints(Relation rel) { @@ -1812,19 +1827,229 @@ StoreConstraints(Relation rel) if (!constr) return; - if (constr->num_defval > 0) + for (i = 0; i < constr->num_defval; i++) + StoreAttrDefault(rel, constr->defval[i].adnum, + constr->defval[i].adbin, false); + + for (i = 0; i < constr->num_check; i++) + StoreRelCheck(rel, constr->check[i].ccname, + constr->check[i].ccbin); +} + +/* + * AddRelationRawConstraints + * + * Add raw (not-yet-transformed) column default expressions and/or constraint + * check expressions to an existing relation. This is defined to do both + * for efficiency in DefineRelation, but of course you can do just one or + * the other by passing empty lists. + * + * rel: relation to be modified + * rawColDefaults: list of RawColumnDefault structures + * rawConstraints: list of Constraint nodes + * + * All entries in rawColDefaults will be processed. Entries in rawConstraints + * will be processed only if they are CONSTR_CHECK type and contain a "raw" + * expression. + * + * NB: caller should have opened rel with AccessExclusiveLock, and should + * hold that lock till end of transaction. + */ +void +AddRelationRawConstraints(Relation rel, + List *rawColDefaults, + List *rawConstraints) +{ + char *relname = RelationGetRelationName(rel)->data; + TupleDesc tupleDesc; + TupleConstr *oldconstr; + int numoldchecks; + ConstrCheck *oldchecks; + ParseState *pstate; + int numchecks; + List *listptr; + Relation relrel; + Relation relidescs[Num_pg_class_indices]; + HeapTuple reltup; + Form_pg_class relStruct; + + /* + * Get info about existing constraints. + */ + tupleDesc = RelationGetDescr(rel); + oldconstr = tupleDesc->constr; + if (oldconstr) + { + numoldchecks = oldconstr->num_check; + oldchecks = oldconstr->check; + } + else { - for (i = 0; i < constr->num_defval; i++) - StoreAttrDefault(rel, &(constr->defval[i])); + numoldchecks = 0; + oldchecks = NULL; } - if (constr->num_check > 0) + /* + * Create a dummy ParseState and insert the target relation as + * its sole rangetable entry. We need a ParseState for transformExpr. + */ + pstate = make_parsestate(NULL); + makeRangeTable(pstate, NULL, NULL); + addRangeTableEntry(pstate, relname, relname, false, true); + + /* + * Process column default expressions. + */ + foreach(listptr, rawColDefaults) { - for (i = 0; i < constr->num_check; i++) - StoreRelCheck(rel, &(constr->check[i])); + RawColumnDefault *colDef = (RawColumnDefault *) lfirst(listptr); + Node *expr; + + Assert(colDef->raw_default != NULL); + /* + * Transform raw parsetree to executable expression. + */ + expr = transformExpr(pstate, colDef->raw_default, EXPR_COLUMN_FIRST); + /* + * Make sure default expr does not refer to any vars. + */ + if (contain_var_clause(expr)) + elog(ERROR, "Cannot use attribute(s) in DEFAULT clause"); + /* + * Might as well try to reduce any constant expressions. + */ + expr = eval_const_expressions(expr); + /* + * Must fix opids, in case any operators remain... + */ + fix_opids(expr); + /* + * OK, store it. + */ + StoreAttrDefault(rel, colDef->attnum, nodeToString(expr), true); } - return; + /* + * Process constraint expressions. + */ + numchecks = numoldchecks; + foreach(listptr, rawConstraints) + { + Constraint *cdef = (Constraint *) lfirst(listptr); + char *ccname; + Node *expr; + + if (cdef->contype != CONSTR_CHECK || cdef->raw_expr == NULL) + continue; + Assert(cdef->cooked_expr == NULL); + + /* Check name uniqueness, or generate a new name */ + if (cdef->name != NULL) + { + int i; + List *listptr2; + + ccname = cdef->name; + /* Check against old constraints */ + for (i = 0; i < numoldchecks; i++) + { + if (strcmp(oldchecks[i].ccname, ccname) == 0) + elog(ERROR, "Duplicate CHECK constraint name: '%s'", + ccname); + } + /* Check against other new constraints */ + foreach(listptr2, rawConstraints) + { + Constraint *cdef2 = (Constraint *) lfirst(listptr2); + + if (cdef2 == cdef || + cdef2->contype != CONSTR_CHECK || + cdef2->raw_expr == NULL || + cdef2->name == NULL) + continue; + if (strcmp(cdef2->name, ccname) == 0) + elog(ERROR, "Duplicate CHECK constraint name: '%s'", + ccname); + } + } + else + { + ccname = (char *) palloc(NAMEDATALEN); + snprintf(ccname, NAMEDATALEN, "$%d", numchecks + 1); + } + /* + * Transform raw parsetree to executable expression. + */ + expr = transformExpr(pstate, cdef->raw_expr, EXPR_COLUMN_FIRST); + /* + * Make sure no outside relations are referred to. + */ + if (length(pstate->p_rtable) != 1) + elog(ERROR, "Only relation '%s' can be referenced in CHECK", + relname); + /* + * Might as well try to reduce any constant expressions. + */ + expr = eval_const_expressions(expr); + /* + * Constraints are evaluated with execQual, which expects an + * implicit-AND list, so convert expression to implicit-AND form. + * (We could go so far as to convert to CNF, but that's probably + * overkill...) + */ + expr = (Node *) make_ands_implicit((Expr *) expr); + /* + * Must fix opids in operator clauses. + */ + fix_opids(expr); + /* + * OK, store it. + */ + StoreRelCheck(rel, ccname, nodeToString(expr)); + + numchecks++; + } + + /* + * Update the count of constraints in the relation's pg_class tuple. + * We do this even if there was no change, in order to ensure that an + * SI update message is sent out for the pg_class tuple, which will + * force other backends to rebuild their relcache entries for the rel. + * (Of course, for a newly created rel there is no need for an SI message, + * but for ALTER TABLE ADD ATTRIBUTE this'd be important.) + */ + relrel = heap_openr(RelationRelationName, RowExclusiveLock); + reltup = SearchSysCacheTupleCopy(RELOID, + ObjectIdGetDatum(rel->rd_id), + 0, 0, 0); + if (!HeapTupleIsValid(reltup)) + elog(ERROR, "cache lookup of relation %u failed", rel->rd_id); + relStruct = (Form_pg_class) GETSTRUCT(reltup); + + relStruct->relchecks = numchecks; + + heap_replace(relrel, &reltup->t_self, reltup, NULL); + + /* keep catalog indices current */ + CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, + relidescs); + CatalogIndexInsert(relidescs, Num_pg_class_indices, relrel, reltup); + CatalogCloseIndices(Num_pg_class_indices, relidescs); + + heap_close(relrel, RowExclusiveLock); + pfree(reltup); + + /* + * Force rebuild of our own relcache entry, otherwise subsequent commands + * in this transaction won't see the new defaults/constraints. + * Must bump command counter or relcache rebuild won't see 'em either. + * + * (This might seem unnecessary, since we are sending out an SI message; + * but if the relation has just been created then relcache.c will ignore + * the SI message on the grounds that the rel is transaction-local...) + */ + CommandCounterIncrement(); + RelationRebuildRelation(rel); } static void diff --git a/src/backend/commands/command.c b/src/backend/commands/command.c index 8872bcdfac72ab79a8682c12256a87c8a0eeed60..3e71e7d1927f4977868586bb676c5d0ca2bb2afd 100644 --- a/src/backend/commands/command.c +++ b/src/backend/commands/command.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.54 1999/09/18 19:06:40 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.55 1999/10/03 23:55:27 tgl Exp $ * * NOTES * The PortalExecutorHeapMemory crap needs to be eliminated @@ -324,7 +324,7 @@ PerformAddAttribute(char *relationName, */ if (colDef->is_not_null) elog(ERROR, "Can't add a NOT NULL attribute to an existing relation"); - if (colDef->defval) + if (colDef->raw_default || colDef->cooked_default) elog(ERROR, "ADD ATTRIBUTE: DEFAULT not yet implemented"); /* @@ -457,7 +457,8 @@ PerformAddAttribute(char *relationName, attribute->attisset = (bool) (tform->typtype == 'c'); attribute->attalign = tform->typalign; attribute->attnotnull = false; - attribute->atthasdef = (colDef->defval != NULL); + attribute->atthasdef = (colDef->raw_default != NULL || + colDef->cooked_default != NULL); heap_insert(attrdesc, attributeTuple); if (hasindex) diff --git a/src/backend/commands/creatinh.c b/src/backend/commands/creatinh.c index c146afa1db4b600953f83e53a82639ea93d81c4e..0b2d2360b3c685f66ea696bbe99055fc8e492fb5 100644 --- a/src/backend/commands/creatinh.c +++ b/src/backend/commands/creatinh.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/Attic/creatinh.c,v 1.47 1999/09/23 17:02:40 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/Attic/creatinh.c,v 1.48 1999/10/03 23:55:27 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -45,15 +45,19 @@ DefineRelation(CreateStmt *stmt, char relkind) List *schema = stmt->tableElts; int numberOfAttributes; Oid relationId; - List *inheritList = NULL; + Relation rel; + List *inheritList; TupleDesc descriptor; - List *constraints; + List *old_constraints; + List *rawDefaults; + List *listptr; + int i; + AttrNumber attnum; if (strlen(stmt->relname) >= NAMEDATALEN) - elog(ERROR, "the relation name %s is >= %d characters long", stmt->relname, - NAMEDATALEN); - StrNCpy(relname, stmt->relname, NAMEDATALEN); /* make full length for - * copy */ + elog(ERROR, "the relation name %s is >= %d characters long", + stmt->relname, NAMEDATALEN); + StrNCpy(relname, stmt->relname, NAMEDATALEN); /* ---------------- * Handle parameters @@ -66,8 +70,7 @@ DefineRelation(CreateStmt *stmt, char relkind) * generate relation schema, including inherited attributes. * ---------------- */ - schema = MergeAttributes(schema, inheritList, &constraints); - constraints = nconc(constraints, stmt->constraints); + schema = MergeAttributes(schema, inheritList, &old_constraints); numberOfAttributes = length(schema); if (numberOfAttributes <= 0) @@ -78,53 +81,53 @@ DefineRelation(CreateStmt *stmt, char relkind) /* ---------------- * create a relation descriptor from the relation schema - * and create the relation. + * and create the relation. Note that in this stage only + * inherited (pre-cooked) defaults and constraints will be + * included into the new relation. (BuildDescForRelation + * takes care of the inherited defaults, but we have to copy + * inherited constraints here.) * ---------------- */ descriptor = BuildDescForRelation(schema, relname); - if (constraints != NIL) + if (old_constraints != NIL) { - List *entry; - int nconstr = length(constraints), - ncheck = 0, - i; - ConstrCheck *check = (ConstrCheck *) palloc(nconstr * sizeof(ConstrCheck)); + ConstrCheck *check = (ConstrCheck *) palloc(length(old_constraints) * + sizeof(ConstrCheck)); + int ncheck = 0; - foreach(entry, constraints) + foreach(listptr, old_constraints) { - Constraint *cdef = (Constraint *) lfirst(entry); + Constraint *cdef = (Constraint *) lfirst(listptr); - if (cdef->contype == CONSTR_CHECK) + if (cdef->contype != CONSTR_CHECK) + continue; + + if (cdef->name != NULL) { - if (cdef->name != NULL) - { - for (i = 0; i < ncheck; i++) - { - if (strcmp(check[i].ccname, cdef->name) == 0) - elog(ERROR, - "DefineRelation: name (%s) of CHECK constraint duplicated", - cdef->name); - } - check[ncheck].ccname = cdef->name; - } - else + for (i = 0; i < ncheck; i++) { - check[ncheck].ccname = (char *) palloc(NAMEDATALEN); - snprintf(check[ncheck].ccname, NAMEDATALEN, "$%d", ncheck + 1); + if (strcmp(check[i].ccname, cdef->name) == 0) + elog(ERROR, "Duplicate CHECK constraint name: '%s'", + cdef->name); } - check[ncheck].ccbin = NULL; - check[ncheck].ccsrc = (char *) cdef->def; - ncheck++; + check[ncheck].ccname = cdef->name; } + else + { + check[ncheck].ccname = (char *) palloc(NAMEDATALEN); + snprintf(check[ncheck].ccname, NAMEDATALEN, "$%d", ncheck + 1); + } + Assert(cdef->raw_expr == NULL && cdef->cooked_expr != NULL); + check[ncheck].ccbin = pstrdup(cdef->cooked_expr); + ncheck++; } if (ncheck > 0) { - if (ncheck < nconstr) - check = (ConstrCheck *) repalloc(check, ncheck * sizeof(ConstrCheck)); if (descriptor->constr == NULL) { descriptor->constr = (TupleConstr *) palloc(sizeof(TupleConstr)); + descriptor->constr->defval = NULL; descriptor->constr->num_defval = 0; descriptor->constr->has_not_null = false; } @@ -137,6 +140,61 @@ DefineRelation(CreateStmt *stmt, char relkind) relkind, stmt->istemp); StoreCatalogInheritance(relationId, inheritList); + + /* + * Now add any newly specified column default values + * and CHECK constraints to the new relation. These are passed + * to us in the form of raw parsetrees; we need to transform + * them to executable expression trees before they can be added. + * The most convenient way to do that is to apply the parser's + * transformExpr routine, but transformExpr doesn't work unless + * we have a pre-existing relation. So, the transformation has + * to be postponed to this final step of CREATE TABLE. + * + * First, scan schema to find new column defaults. + */ + rawDefaults = NIL; + attnum = 0; + + foreach(listptr, schema) + { + ColumnDef *colDef = lfirst(listptr); + RawColumnDefault *rawEnt; + + attnum++; + + if (colDef->raw_default == NULL) + continue; + Assert(colDef->cooked_default == NULL); + + rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault)); + rawEnt->attnum = attnum; + rawEnt->raw_default = colDef->raw_default; + rawDefaults = lappend(rawDefaults, rawEnt); + } + + /* If no raw defaults and no constraints, nothing to do. */ + if (rawDefaults == NIL && stmt->constraints == NIL) + return; + + /* + * We must bump the command counter to make the newly-created + * relation tuple visible for opening. + */ + CommandCounterIncrement(); + /* + * Open the new relation. + */ + rel = heap_openr(relname, AccessExclusiveLock); + /* + * Parse and add the defaults/constraints. + */ + AddRelationRawConstraints(rel, rawDefaults, stmt->constraints); + /* + * Clean up. We keep lock on new relation (although it shouldn't + * be visible to anyone else anyway, until commit). + */ + heap_close(rel, NoLock); } /* @@ -164,18 +222,14 @@ RemoveRelation(char *name) * Exceptions: * BadArg if name is invalid * - * * Note: * Rows are removed, indices are truncated and reconstructed. */ - void TruncateRelation(char *name) { - - AssertArg(name); - heap_truncate(name); - + AssertArg(name); + heap_truncate(name); } /* @@ -316,22 +370,25 @@ MergeAttributes(List *schema, List *supers, List **supconstr) typename->typmod = attribute->atttypmod; def->typename = typename; def->is_not_null = attribute->attnotnull; - def->defval = NULL; + def->raw_default = NULL; + def->cooked_default = NULL; if (attribute->atthasdef) { - AttrDefault *attrdef = constr->defval; + AttrDefault *attrdef; int i; - Assert(constr != NULL && constr->num_defval > 0); + Assert(constr != NULL); + attrdef = constr->defval; for (i = 0; i < constr->num_defval; i++) { - if (attrdef[i].adnum != attrno + 1) - continue; - def->defval = pstrdup(attrdef[i].adsrc); - break; + if (attrdef[i].adnum == attrno + 1) + { + def->cooked_default = pstrdup(attrdef[i].adbin); + break; + } } - Assert(def->defval != NULL); + Assert(def->cooked_default != NULL); } partialResult = lcons(def, partialResult); } @@ -343,14 +400,15 @@ MergeAttributes(List *schema, List *supers, List **supconstr) for (i = 0; i < constr->num_check; i++) { - Constraint *cdef = (Constraint *) makeNode(Constraint); + Constraint *cdef = makeNode(Constraint); cdef->contype = CONSTR_CHECK; if (check[i].ccname[0] == '$') cdef->name = NULL; else cdef->name = pstrdup(check[i].ccname); - cdef->def = (void *) pstrdup(check[i].ccsrc); + cdef->raw_expr = NULL; + cdef->cooked_expr = pstrdup(check[i].ccbin); constraints = lappend(constraints, cdef); } } diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index be47d32f9f9abbbb9615aff038de5f0a4227a19c..228aaba7990b3e81d82ef616991013621d29a618 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -96,7 +96,8 @@ DefineSequence(CreateSeqStmt *seq) typnam->typmod = -1; coldef = makeNode(ColumnDef); coldef->typename = typnam; - coldef->defval = NULL; + coldef->raw_default = NULL; + coldef->cooked_default = NULL; coldef->is_not_null = false; null[i - 1] = ' '; diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c index 5baf4eceac8cdc79a2e8c372a6ff991dd760d73e..a88b1840dd3100ab7db8e685993a4e3fc368aa30 100644 --- a/src/backend/commands/view.c +++ b/src/backend/commands/view.c @@ -5,7 +5,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: view.c,v 1.37 1999/07/17 20:16:54 momjian Exp $ + * $Id: view.c,v 1.38 1999/10/03 23:55:27 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -76,7 +76,8 @@ DefineVirtualRelation(char *relname, List *tlist) def->typename = typename; def->is_not_null = false; - def->defval = (char *) NULL; + def->raw_default = NULL; + def->cooked_default = NULL; attrList = lappend(attrList, def); } diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 13e32ed54fe330be8d1d887cecbefb9841e16f7d..6b1e560014fa46b30f4c2149e541e8000e5e9891 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -5,7 +5,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: outfuncs.c,v 1.95 1999/08/31 01:28:32 tgl Exp $ + * $Id: outfuncs.c,v 1.96 1999/10/03 23:55:29 tgl Exp $ * * NOTES * Every (plan) node in POSTGRES has an associated "out" routine which @@ -124,10 +124,12 @@ _outColumnDef(StringInfo str, ColumnDef *node) appendStringInfo(str, " COLUMNDEF :colname %s :typename ", stringStringInfo(node->colname)); _outNode(str, node->typename); - - appendStringInfo(str, " :is_not_null %s :defval %s :constraints ", + appendStringInfo(str, " :is_not_null %s :is_sequence %s :raw_default ", node->is_not_null ? "true" : "false", - stringStringInfo(node->defval)); + node->is_sequence ? "true" : "false"); + _outNode(str, node->raw_default); + appendStringInfo(str, " :cooked_default %s :constraints ", + stringStringInfo(node->cooked_default)); _outNode(str, node->constraints); } @@ -1216,11 +1218,17 @@ _outConstraint(StringInfo str, Constraint *node) break; case CONSTR_CHECK: - appendStringInfo(str, " CHECK %s", stringStringInfo(node->def)); + appendStringInfo(str, " CHECK :raw "); + _outNode(str, node->raw_expr); + appendStringInfo(str, " :cooked %s ", + stringStringInfo(node->cooked_expr)); break; case CONSTR_DEFAULT: - appendStringInfo(str, " DEFAULT %s", stringStringInfo(node->def)); + appendStringInfo(str, " DEFAULT :raw "); + _outNode(str, node->raw_expr); + appendStringInfo(str, " :cooked %s ", + stringStringInfo(node->cooked_expr)); break; case CONSTR_NOTNULL: @@ -1236,7 +1244,6 @@ _outConstraint(StringInfo str, Constraint *node) appendStringInfo(str, "<unrecognized constraint>"); break; } - return; } static void diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 276d28b3aa14e9474e887af207190ff0dac9daa8..4bcf79a72776ea36c1e50b75b075d99be06c60dd 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -5,7 +5,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: analyze.c,v 1.119 1999/09/18 19:07:12 tgl Exp $ + * $Id: analyze.c,v 1.120 1999/10/03 23:55:30 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -396,7 +396,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) if (namestrcmp(&(thisatt->attname), resnode->resname) == 0) break; } - if (tl != NIL) /* something given for this attr */ + if (tl != NIL) /* found TLE for this attr */ continue; /* * No user-supplied value, so add a targetentry with DEFAULT expr @@ -573,7 +573,6 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt) q = makeNode(Query); q->commandType = CMD_UTILITY; - elements = stmt->tableElts; constraints = stmt->constraints; columns = NIL; dlist = NIL; @@ -581,7 +580,7 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt) /* * Run through each primary element in the table creation clause */ - while (elements != NIL) + foreach(elements, stmt->tableElts) { element = lfirst(elements); switch (nodeTag(element)) @@ -594,19 +593,31 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt) if (column->is_sequence) { char *sname; - char *cstring; + char *qstring; + A_Const *snamenode; + FuncCall *funccallnode; CreateSeqStmt *sequence; sname = makeObjectName(stmt->relname, column->colname, "seq"); + /* + * Create an expression tree representing the function + * call nextval('"sequencename"') + */ + qstring = palloc(strlen(sname) + 2 + 1); + sprintf(qstring, "\"%s\"", sname); + snamenode = makeNode(A_Const); + snamenode->val.type = T_String; + snamenode->val.val.str = qstring; + funccallnode = makeNode(FuncCall); + funccallnode->funcname = "nextval"; + funccallnode->args = lcons(snamenode, NIL); + constraint = makeNode(Constraint); constraint->contype = CONSTR_DEFAULT; constraint->name = sname; - cstring = palloc(10 + strlen(constraint->name) + 3 + 1); - strcpy(cstring, "nextval('\""); - strcat(cstring, constraint->name); - strcat(cstring, "\"')"); - constraint->def = cstring; + constraint->raw_expr = (Node *) funccallnode; + constraint->cooked_expr = NULL; constraint->keys = NULL; column->constraints = lappend(column->constraints, constraint); @@ -628,69 +639,65 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt) blist = lcons(sequence, NIL); } - /* Check for column constraints, if any... */ - if (column->constraints != NIL) + /* Process column constraints, if any... */ + foreach(clist, column->constraints) { - clist = column->constraints; - while (clist != NIL) + constraint = lfirst(clist); + switch (constraint->contype) { - constraint = lfirst(clist); - switch (constraint->contype) - { - case CONSTR_NULL: - - /* - * We should mark this explicitly, so we - * can tell if NULL and NOT NULL are both - * specified - */ - if (column->is_not_null) - elog(ERROR, "CREATE TABLE/(NOT) NULL conflicting declaration" - " for '%s.%s'", stmt->relname, column->colname); - column->is_not_null = FALSE; - break; - - case CONSTR_NOTNULL: - if (column->is_not_null) - elog(ERROR, "CREATE TABLE/NOT NULL already specified" - " for '%s.%s'", stmt->relname, column->colname); - column->is_not_null = TRUE; - break; - - case CONSTR_DEFAULT: - if (column->defval != NULL) - elog(ERROR, "CREATE TABLE/DEFAULT multiple values specified" - " for '%s.%s'", stmt->relname, column->colname); - column->defval = constraint->def; - break; - - case CONSTR_PRIMARY: - if (constraint->name == NULL) - constraint->name = makeObjectName(stmt->relname, NULL, "pkey"); - if (constraint->keys == NIL) - constraint->keys = lappend(constraint->keys, column); - dlist = lappend(dlist, constraint); - break; - - case CONSTR_UNIQUE: - if (constraint->name == NULL) - constraint->name = makeObjectName(stmt->relname, column->colname, "key"); - if (constraint->keys == NIL) - constraint->keys = lappend(constraint->keys, column); - dlist = lappend(dlist, constraint); - break; - - case CONSTR_CHECK: - constraints = lappend(constraints, constraint); - if (constraint->name == NULL) - constraint->name = makeObjectName(stmt->relname, column->colname, NULL); - break; - - default: - elog(ERROR, "parser: unrecognized constraint (internal error)", NULL); - break; - } - clist = lnext(clist); + case CONSTR_NULL: + + /* + * We should mark this explicitly, so we + * can tell if NULL and NOT NULL are both + * specified + */ + if (column->is_not_null) + elog(ERROR, "CREATE TABLE/(NOT) NULL conflicting declaration" + " for '%s.%s'", stmt->relname, column->colname); + column->is_not_null = FALSE; + break; + + case CONSTR_NOTNULL: + if (column->is_not_null) + elog(ERROR, "CREATE TABLE/NOT NULL already specified" + " for '%s.%s'", stmt->relname, column->colname); + column->is_not_null = TRUE; + break; + + case CONSTR_DEFAULT: + if (column->raw_default != NULL) + elog(ERROR, "CREATE TABLE/DEFAULT multiple values specified" + " for '%s.%s'", stmt->relname, column->colname); + column->raw_default = constraint->raw_expr; + Assert(constraint->cooked_expr == NULL); + break; + + case CONSTR_PRIMARY: + if (constraint->name == NULL) + constraint->name = makeObjectName(stmt->relname, NULL, "pkey"); + if (constraint->keys == NIL) + constraint->keys = lappend(constraint->keys, column); + dlist = lappend(dlist, constraint); + break; + + case CONSTR_UNIQUE: + if (constraint->name == NULL) + constraint->name = makeObjectName(stmt->relname, column->colname, "key"); + if (constraint->keys == NIL) + constraint->keys = lappend(constraint->keys, column); + dlist = lappend(dlist, constraint); + break; + + case CONSTR_CHECK: + if (constraint->name == NULL) + constraint->name = makeObjectName(stmt->relname, column->colname, NULL); + constraints = lappend(constraints, constraint); + break; + + default: + elog(ERROR, "parser: unrecognized constraint (internal error)", NULL); + break; } } break; @@ -715,19 +722,17 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt) case CONSTR_NOTNULL: case CONSTR_DEFAULT: - elog(ERROR, "parser: illegal context for constraint (internal error)", NULL); + elog(ERROR, "parser: illegal context for constraint (internal error)"); break; default: - elog(ERROR, "parser: unrecognized constraint (internal error)", NULL); + elog(ERROR, "parser: unrecognized constraint (internal error)"); break; } break; default: - elog(ERROR, "parser: unrecognized node (internal error)", NULL); + elog(ERROR, "parser: unrecognized node (internal error)"); } - - elements = lnext(elements); } stmt->tableElts = columns; diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 96f480ea0ca27f5467401df138f5aab53715692b..87c82839df460c81b854443d144c3b0a4564f5e4 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.105 1999/10/02 21:33:21 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.106 1999/10/03 23:55:30 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -72,8 +72,6 @@ static char *xlateSqlType(char *); static Node *makeA_Expr(int oper, char *opname, Node *lexpr, Node *rexpr); static Node *makeRowExpr(char *opr, List *largs, List *rargs); static void mapTargetColumns(List *source, List *target); -static List *makeConstantList( A_Const *node); -static char *FlattenStringList(List *list); static char *fmtId(char *rawid); static void param_type_init(Oid *typev, int nargs); static Node *doNegate(Node *n); @@ -217,7 +215,7 @@ Oid param_type(int t); /* used in parse_expr.c */ %type <node> def_arg, columnElem, where_clause, a_expr, a_expr_or_null, b_expr, AexprConst, in_expr, having_clause -%type <list> row_descriptor, row_list, c_list, c_expr, in_expr_nodes +%type <list> row_descriptor, row_list, in_expr_nodes %type <node> row_expr %type <str> row_op %type <node> case_expr, case_arg, when_clause, case_default @@ -250,8 +248,6 @@ Oid param_type(int t); /* used in parse_expr.c */ %type <str> TypeId %type <node> TableConstraint -%type <list> constraint_list, constraint_expr -%type <list> default_list, default_expr %type <list> ColPrimaryKey, ColQualList, ColQualifier %type <node> ColConstraint, ColConstraintElem %type <list> key_actions, key_action @@ -716,7 +712,7 @@ alter_clause: ADD opt_column columnDef } | DROP opt_column ColId { elog(ERROR,"ALTER TABLE/DROP COLUMN not yet implemented"); } - | ALTER opt_column ColId SET DEFAULT default_expr + | ALTER opt_column ColId SET DEFAULT a_expr { elog(ERROR,"ALTER TABLE/ALTER COLUMN/SET DEFAULT not yet implemented"); } | ALTER opt_column ColId DROP DEFAULT { elog(ERROR,"ALTER TABLE/ALTER COLUMN/DROP DEFAULT not yet implemented"); } @@ -864,7 +860,8 @@ columnDef: ColId Typename ColQualifier ColumnDef *n = makeNode(ColumnDef); n->colname = $1; n->typename = $2; - n->defval = NULL; + n->raw_default = NULL; + n->cooked_default = NULL; n->is_not_null = FALSE; n->constraints = $3; $$ = (Node *)n; @@ -875,7 +872,8 @@ columnDef: ColId Typename ColQualifier n->colname = $1; n->typename = makeNode(TypeName); n->typename->name = xlateSqlType("integer"); - n->defval = NULL; + n->raw_default = NULL; + n->cooked_default = NULL; n->is_not_null = TRUE; n->is_sequence = TRUE; n->constraints = $3; @@ -909,7 +907,8 @@ ColPrimaryKey: PRIMARY KEY Constraint *n = makeNode(Constraint); n->contype = CONSTR_PRIMARY; n->name = NULL; - n->def = NULL; + n->raw_expr = NULL; + n->cooked_expr = NULL; n->keys = NULL; $$ = lcons((Node *)n, NIL); } @@ -928,7 +927,7 @@ ColConstraint: ; /* DEFAULT NULL is already the default for Postgres. - * Bue define it here and carry it forward into the system + * But define it here and carry it forward into the system * to make it explicit. * - thomas 1998-09-13 * WITH NULL and NULL are not SQL92-standard syntax elements, @@ -936,13 +935,17 @@ ColConstraint: * that a column may have that value. WITH NULL leads to * shift/reduce conflicts with WITH TIME ZONE anyway. * - thomas 1999-01-08 + * DEFAULT expression must be b_expr not a_expr to prevent shift/reduce + * conflict on NOT (since NOT might start a subsequent NOT NULL constraint, + * or be part of a_expr NOT LIKE or similar constructs). */ -ColConstraintElem: CHECK '(' constraint_expr ')' +ColConstraintElem: CHECK '(' a_expr ')' { Constraint *n = makeNode(Constraint); n->contype = CONSTR_CHECK; n->name = NULL; - n->def = FlattenStringList($3); + n->raw_expr = $3; + n->cooked_expr = NULL; n->keys = NULL; $$ = (Node *)n; } @@ -951,16 +954,18 @@ ColConstraintElem: CHECK '(' constraint_expr ')' Constraint *n = makeNode(Constraint); n->contype = CONSTR_DEFAULT; n->name = NULL; - n->def = NULL; + n->raw_expr = NULL; + n->cooked_expr = NULL; n->keys = NULL; $$ = (Node *)n; } - | DEFAULT default_expr + | DEFAULT b_expr { Constraint *n = makeNode(Constraint); n->contype = CONSTR_DEFAULT; n->name = NULL; - n->def = FlattenStringList($2); + n->raw_expr = $2; + n->cooked_expr = NULL; n->keys = NULL; $$ = (Node *)n; } @@ -969,7 +974,8 @@ ColConstraintElem: CHECK '(' constraint_expr ')' Constraint *n = makeNode(Constraint); n->contype = CONSTR_NOTNULL; n->name = NULL; - n->def = NULL; + n->raw_expr = NULL; + n->cooked_expr = NULL; n->keys = NULL; $$ = (Node *)n; } @@ -978,7 +984,8 @@ ColConstraintElem: CHECK '(' constraint_expr ')' Constraint *n = makeNode(Constraint); n->contype = CONSTR_UNIQUE; n->name = NULL; - n->def = NULL; + n->raw_expr = NULL; + n->cooked_expr = NULL; n->keys = NULL; $$ = (Node *)n; } @@ -987,7 +994,8 @@ ColConstraintElem: CHECK '(' constraint_expr ')' Constraint *n = makeNode(Constraint); n->contype = CONSTR_PRIMARY; n->name = NULL; - n->def = NULL; + n->raw_expr = NULL; + n->cooked_expr = NULL; n->keys = NULL; $$ = (Node *)n; } @@ -998,112 +1006,6 @@ ColConstraintElem: CHECK '(' constraint_expr ')' } ; -default_list: default_list ',' default_expr - { - $$ = lappend($1,makeString(",")); - $$ = nconc($$, $3); - } - | default_expr - { - $$ = $1; - } - ; - -/* The Postgres default column value is NULL. - * Rather than carrying DEFAULT NULL forward as a clause, - * let's just have it be a no-op. - | NULL_P - { $$ = lcons( makeString("NULL"), NIL); } - * - thomas 1998-09-13 - */ -default_expr: AexprConst - { $$ = makeConstantList((A_Const *) $1); } - | '-' default_expr %prec UMINUS - { $$ = lcons( makeString( "-"), $2); } - | default_expr '+' default_expr - { $$ = nconc( $1, lcons( makeString( "+"), $3)); } - | default_expr '-' default_expr - { $$ = nconc( $1, lcons( makeString( "-"), $3)); } - | default_expr '/' default_expr - { $$ = nconc( $1, lcons( makeString( "/"), $3)); } - | default_expr '%' default_expr - { $$ = nconc( $1, lcons( makeString( "%"), $3)); } - | default_expr '*' default_expr - { $$ = nconc( $1, lcons( makeString( "*"), $3)); } - | default_expr '^' default_expr - { $$ = nconc( $1, lcons( makeString( "^"), $3)); } - | default_expr '|' default_expr - { $$ = nconc( $1, lcons( makeString( "|"), $3)); } - | default_expr '=' default_expr - { elog(ERROR,"boolean expressions not supported in DEFAULT"); } - | default_expr '<' default_expr - { elog(ERROR,"boolean expressions not supported in DEFAULT"); } - | default_expr '>' default_expr - { elog(ERROR,"boolean expressions not supported in DEFAULT"); } - | ':' default_expr - { $$ = lcons( makeString( ":"), $2); } - | ';' default_expr - { $$ = lcons( makeString( ";"), $2); } - | '|' default_expr - { $$ = lcons( makeString( "|"), $2); } - | default_expr TYPECAST Typename - { - $3->name = fmtId($3->name); - $$ = nconc( lcons( makeString( "CAST"), $1), makeList( makeString("AS"), $3, -1)); - } - | CAST '(' default_expr AS Typename ')' - { - $5->name = fmtId($5->name); - $$ = nconc( lcons( makeString( "CAST"), $3), makeList( makeString("AS"), $5, -1)); - } - | '(' default_expr ')' - { $$ = lappend( lcons( makeString( "("), $2), makeString( ")")); } - | func_name '(' ')' - { - $$ = makeList( makeString($1), makeString("("), -1); - $$ = lappend( $$, makeString(")")); - } - | func_name '(' default_list ')' - { - $$ = makeList( makeString($1), makeString("("), -1); - $$ = nconc( $$, $3); - $$ = lappend( $$, makeString(")")); - } - | default_expr Op default_expr - { - if (!strcmp("<=", $2) || !strcmp(">=", $2)) - elog(ERROR,"boolean expressions not supported in DEFAULT"); - $$ = nconc( $1, lcons( makeString( $2), $3)); - } - | Op default_expr - { $$ = lcons( makeString( $1), $2); } - | default_expr Op - { $$ = lappend( $1, makeString( $2)); } - /* XXX - thomas 1997-10-07 v6.2 function-specific code to be changed */ - | CURRENT_DATE - { $$ = lcons( makeString( "date( 'current'::datetime + '0 sec')"), NIL); } - | CURRENT_TIME - { $$ = lcons( makeString( "'now'::time"), NIL); } - | CURRENT_TIME '(' Iconst ')' - { - if ($3 != 0) - elog(NOTICE,"CURRENT_TIME(%d) precision not implemented; zero used instead",$3); - $$ = lcons( makeString( "'now'::time"), NIL); - } - | CURRENT_TIMESTAMP - { $$ = lcons( makeString( "now()"), NIL); } - | CURRENT_TIMESTAMP '(' Iconst ')' - { - if ($3 != 0) - elog(NOTICE,"CURRENT_TIMESTAMP(%d) precision not implemented; zero used instead",$3); - $$ = lcons( makeString( "now()"), NIL); - } - | CURRENT_USER - { $$ = lcons( makeString( "CURRENT_USER"), NIL); } - | USER - { $$ = lcons( makeString( "USER"), NIL); } - ; - /* ConstraintElem specifies constraint syntax which is not embedded into * a column definition. ColConstraintElem specifies the embedded form. * - thomas 1997-12-03 @@ -1118,12 +1020,13 @@ TableConstraint: CONSTRAINT name ConstraintElem { $$ = $1; } ; -ConstraintElem: CHECK '(' constraint_expr ')' +ConstraintElem: CHECK '(' a_expr ')' { Constraint *n = makeNode(Constraint); n->contype = CONSTR_CHECK; n->name = NULL; - n->def = FlattenStringList($3); + n->raw_expr = $3; + n->cooked_expr = NULL; $$ = (Node *)n; } | UNIQUE '(' columnList ')' @@ -1131,7 +1034,8 @@ ConstraintElem: CHECK '(' constraint_expr ')' Constraint *n = makeNode(Constraint); n->contype = CONSTR_UNIQUE; n->name = NULL; - n->def = NULL; + n->raw_expr = NULL; + n->cooked_expr = NULL; n->keys = $3; $$ = (Node *)n; } @@ -1140,7 +1044,8 @@ ConstraintElem: CHECK '(' constraint_expr ')' Constraint *n = makeNode(Constraint); n->contype = CONSTR_PRIMARY; n->name = NULL; - n->def = NULL; + n->raw_expr = NULL; + n->cooked_expr = NULL; n->keys = $4; $$ = (Node *)n; } @@ -1151,153 +1056,6 @@ ConstraintElem: CHECK '(' constraint_expr ')' } ; -constraint_list: constraint_list ',' constraint_expr - { - $$ = lappend($1,makeString(",")); - $$ = nconc($$, $3); - } - | constraint_expr - { - $$ = $1; - } - ; - -constraint_expr: AexprConst - { $$ = makeConstantList((A_Const *) $1); } - | NULL_P - { $$ = lcons( makeString("NULL"), NIL); } - | ColId - { - $$ = lcons( makeString(fmtId($1)), NIL); - } - | '-' constraint_expr %prec UMINUS - { $$ = lcons( makeString( "-"), $2); } - | constraint_expr '+' constraint_expr - { $$ = nconc( $1, lcons( makeString( "+"), $3)); } - | constraint_expr '-' constraint_expr - { $$ = nconc( $1, lcons( makeString( "-"), $3)); } - | constraint_expr '/' constraint_expr - { $$ = nconc( $1, lcons( makeString( "/"), $3)); } - | constraint_expr '%' constraint_expr - { $$ = nconc( $1, lcons( makeString( "%"), $3)); } - | constraint_expr '*' constraint_expr - { $$ = nconc( $1, lcons( makeString( "*"), $3)); } - | constraint_expr '^' constraint_expr - { $$ = nconc( $1, lcons( makeString( "^"), $3)); } - | constraint_expr '|' constraint_expr - { $$ = nconc( $1, lcons( makeString( "|"), $3)); } - | constraint_expr '=' constraint_expr - { $$ = nconc( $1, lcons( makeString( "="), $3)); } - | constraint_expr '<' constraint_expr - { $$ = nconc( $1, lcons( makeString( "<"), $3)); } - | constraint_expr '>' constraint_expr - { $$ = nconc( $1, lcons( makeString( ">"), $3)); } - | ':' constraint_expr - { $$ = lcons( makeString( ":"), $2); } - | ';' constraint_expr - { $$ = lcons( makeString( ";"), $2); } - | '|' constraint_expr - { $$ = lcons( makeString( "|"), $2); } - | constraint_expr TYPECAST Typename - { - $3->name = fmtId($3->name); - $$ = nconc( lcons( makeString( "CAST"), $1), makeList( makeString("AS"), $3, -1)); - } - | CAST '(' constraint_expr AS Typename ')' - { - $5->name = fmtId($5->name); - $$ = nconc( lcons( makeString( "CAST"), $3), makeList( makeString("AS"), $5, -1)); - } - | '(' constraint_expr ')' - { $$ = lappend( lcons( makeString( "("), $2), makeString( ")")); } - | func_name '(' ')' - { - $$ = makeList( makeString($1), makeString("("), -1); - $$ = lappend( $$, makeString(")")); - } - | func_name '(' constraint_list ')' - { - $$ = makeList( makeString($1), makeString("("), -1); - $$ = nconc( $$, $3); - $$ = lappend( $$, makeString(")")); - } - | constraint_expr Op constraint_expr - { $$ = nconc( $1, lcons( makeString( $2), $3)); } - | constraint_expr LIKE constraint_expr - { $$ = nconc( $1, lcons( makeString( "LIKE"), $3)); } - | constraint_expr NOT LIKE constraint_expr - { $$ = nconc( $1, lcons( makeString( "NOT LIKE"), $4)); } - | constraint_expr AND constraint_expr - { $$ = nconc( $1, lcons( makeString( "AND"), $3)); } - | constraint_expr OR constraint_expr - { $$ = nconc( $1, lcons( makeString( "OR"), $3)); } - | NOT constraint_expr - { $$ = lcons( makeString( "NOT"), $2); } - | Op constraint_expr - { $$ = lcons( makeString( $1), $2); } - | constraint_expr Op - { $$ = lappend( $1, makeString( $2)); } - | constraint_expr ISNULL - { $$ = lappend( $1, makeString( "IS NULL")); } - | constraint_expr IS NULL_P - { $$ = lappend( $1, makeString( "IS NULL")); } - | constraint_expr NOTNULL - { $$ = lappend( $1, makeString( "IS NOT NULL")); } - | constraint_expr IS NOT NULL_P - { $$ = lappend( $1, makeString( "IS NOT NULL")); } - | constraint_expr IS TRUE_P - { $$ = lappend( $1, makeString( "IS TRUE")); } - | constraint_expr IS FALSE_P - { $$ = lappend( $1, makeString( "IS FALSE")); } - | constraint_expr IS NOT TRUE_P - { $$ = lappend( $1, makeString( "IS NOT TRUE")); } - | constraint_expr IS NOT FALSE_P - { $$ = lappend( $1, makeString( "IS NOT FALSE")); } - | constraint_expr IN '(' c_list ')' - { - $$ = lappend( $1, makeString("IN")); - $$ = lappend( $$, makeString("(")); - $$ = nconc( $$, $4); - $$ = lappend( $$, makeString(")")); - } - | constraint_expr NOT IN '(' c_list ')' - { - $$ = lappend( $1, makeString("NOT IN")); - $$ = lappend( $$, makeString("(")); - $$ = nconc( $$, $5); - $$ = lappend( $$, makeString(")")); - } - | constraint_expr BETWEEN c_expr AND c_expr - { - $$ = lappend( $1, makeString("BETWEEN")); - $$ = nconc( $$, $3); - $$ = lappend( $$, makeString("AND")); - $$ = nconc( $$, $5); - } - | constraint_expr NOT BETWEEN c_expr AND c_expr - { - $$ = lappend( $1, makeString("NOT BETWEEN")); - $$ = nconc( $$, $4); - $$ = lappend( $$, makeString("AND")); - $$ = nconc( $$, $6); - } - ; - -c_list: c_list ',' c_expr - { - $$ = lappend($1, makeString(",")); - $$ = nconc($$, $3); - } - | c_expr - { - $$ = $1; - } - ; - -c_expr: AexprConst - { $$ = makeConstantList((A_Const *) $1); } - ; - key_match: MATCH FULL { $$ = NULL; } | MATCH PARTIAL { $$ = NULL; } | /*EMPTY*/ { $$ = NULL; } @@ -1346,7 +1104,8 @@ CreateAsElement: ColId ColumnDef *n = makeNode(ColumnDef); n->colname = $1; n->typename = NULL; - n->defval = NULL; + n->raw_default = NULL; + n->cooked_default = NULL; n->is_not_null = FALSE; n->constraints = NULL; $$ = (Node *)n; @@ -5495,95 +5254,6 @@ void parser_init(Oid *typev, int nargs) } -/* FlattenStringList() - * Traverse list of string nodes and convert to a single string. - * Used for reconstructing string form of complex expressions. - * - * Allocate at least one byte for terminator. - */ -static char * -FlattenStringList(List *list) -{ - List *l; - Value *v; - char *s; - char *sp; - int nlist, len = 0; - - nlist = length(list); - l = list; - while(l != NIL) { - v = (Value *)lfirst(l); - sp = v->val.str; - l = lnext(l); - len += strlen(sp); - }; - len += nlist; - - s = (char*) palloc(len+1); - *s = '\0'; - - l = list; - while(l != NIL) { - v = (Value *)lfirst(l); - sp = v->val.str; - l = lnext(l); - strcat(s,sp); - if (l != NIL) strcat(s," "); - }; - *(s+len) = '\0'; - - return s; -} /* FlattenStringList() */ - - -/* makeConstantList() - * Convert constant value node into string node. - */ -static List * -makeConstantList( A_Const *n) -{ - List *result = NIL; - char *typval = NULL; - char *defval = NULL; - - if (nodeTag(n) != T_A_Const) { - elog(ERROR,"Cannot handle non-constant parameter"); - - } else if (n->val.type == T_Float) { - defval = (char*) palloc(20+1); - sprintf( defval, "%g", n->val.val.dval); - result = lcons( makeString(defval), NIL); - - } else if (n->val.type == T_Integer) { - defval = (char*) palloc(20+1); - sprintf( defval, "%ld", n->val.val.ival); - result = lcons( makeString(defval), NIL); - - } else if (n->val.type == T_String) { - defval = (char*) palloc(strlen( ((A_Const *) n)->val.val.str) + 3); - strcpy( defval, "'"); - strcat( defval, ((A_Const *) n)->val.val.str); - strcat( defval, "'"); - if (n->typename != NULL) - { - typval = (char*) palloc(strlen( n->typename->name) + 1); - strcpy(typval, n->typename->name); - result = lappend( lcons( makeString(typval), NIL), makeString(defval)); - } - else - { - result = lcons( makeString(defval), NIL); - } - - } else { - elog(ERROR,"Internal error in makeConstantList(): cannot encode node"); - }; - - return result; -} /* makeConstantList() */ - - /* fmtId() * Check input string for non-lowercase/non-numeric characters. * Returns either input string or input surrounded by double quotes. diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 59f3586d13f6636c1b80731fea26831d7bcda7b7..bcaa0f0d68c5a1ffd6156ca7c262cf84fc3bbd05 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -3,7 +3,7 @@ * out of it's tuple * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.26 1999/10/02 01:07:51 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.27 1999/10/03 23:55:31 tgl Exp $ * * This software is copyrighted by Jan Wieck - Hamburg. * @@ -39,25 +39,28 @@ #include <fcntl.h> #include "postgres.h" + #include "executor/spi.h" #include "lib/stringinfo.h" #include "optimizer/clauses.h" #include "optimizer/tlist.h" -#include "utils/lsyscache.h" #include "catalog/pg_index.h" #include "catalog/pg_operator.h" #include "catalog/pg_shadow.h" +#include "utils/builtins.h" +#include "utils/lsyscache.h" /* ---------- * Local data types * ---------- */ -typedef struct QryHier +typedef struct { - struct QryHier *parent; - Query *query; -} QryHier; + StringInfo buf; /* output buffer to append to */ + List *rangetables; /* List of List of RangeTblEntry */ + bool varprefix; /* TRUE to print prefixes on Vars */ +} deparse_context; typedef struct { Index rt_index; @@ -69,7 +72,7 @@ typedef struct { * Global data * ---------- */ -static char *rulename; +static char *rulename = NULL; static void *plan_getrule = NULL; static char *query_getrule = "SELECT * FROM pg_rewrite WHERE rulename = $1"; static void *plan_getview = NULL; @@ -80,16 +83,6 @@ static void *plan_getopclass = NULL; static char *query_getopclass = "SELECT * FROM pg_opclass WHERE oid = $1"; -/* ---------- - * Global functions - * ---------- - */ -text *pg_get_ruledef(NameData *rname); -text *pg_get_viewdef(NameData *rname); -text *pg_get_indexdef(Oid indexrelid); -NameData *pg_get_userbyid(int4 uid); - - /* ---------- * Local functions * @@ -100,21 +93,17 @@ NameData *pg_get_userbyid(int4 uid); */ static void make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc); static void make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc); -static void get_query_def(StringInfo buf, Query *query, QryHier *parentqh); -static void get_select_query_def(StringInfo buf, Query *query, QryHier *qh); -static void get_insert_query_def(StringInfo buf, Query *query, QryHier *qh); -static void get_update_query_def(StringInfo buf, Query *query, QryHier *qh); -static void get_delete_query_def(StringInfo buf, Query *query, QryHier *qh); -static RangeTblEntry *get_rte_for_var(Var *var, QryHier *qh); -static void get_rule_expr(StringInfo buf, QryHier *qh, int rt_index, - Node *node, bool varprefix); -static void get_func_expr(StringInfo buf, QryHier *qh, int rt_index, - Expr *expr, bool varprefix); -static void get_tle_expr(StringInfo buf, QryHier *qh, int rt_index, - TargetEntry *tle, bool varprefix); -static void get_const_expr(StringInfo buf, Const *constval); -static void get_sublink_expr(StringInfo buf, QryHier *qh, int rt_index, - Node *node, bool varprefix); +static void get_query_def(Query *query, StringInfo buf, List *parentrtables); +static void get_select_query_def(Query *query, deparse_context *context); +static void get_insert_query_def(Query *query, deparse_context *context); +static void get_update_query_def(Query *query, deparse_context *context); +static void get_delete_query_def(Query *query, deparse_context *context); +static RangeTblEntry *get_rte_for_var(Var *var, deparse_context *context); +static void get_rule_expr(Node *node, deparse_context *context); +static void get_func_expr(Expr *expr, deparse_context *context); +static void get_tle_expr(TargetEntry *tle, deparse_context *context); +static void get_const_expr(Const *constval, deparse_context *context); +static void get_sublink_expr(Node *node, deparse_context *context); static char *get_relation_name(Oid relid); static char *get_attribute_name(Oid relid, int2 attnum); static bool check_if_rte_used(Node *node, Index rt_index, int levelsup); @@ -554,7 +543,7 @@ pg_get_indexdef(Oid indexrelid) * ---------- */ NameData * -pg_get_userbyid(int4 uid) +pg_get_userbyid(int32 uid) { HeapTuple usertup; Form_pg_shadow user_rec; @@ -584,6 +573,43 @@ pg_get_userbyid(int4 uid) return result; } +/* ---------- + * deparse_expression - General utility for deparsing expressions + * + * expr is the node tree to be deparsed. It must be a transformed expression + * tree (ie, not the raw output of gram.y). + * + * rangetables is a List of Lists of RangeTblEntry nodes: first sublist is for + * varlevelsup = 0, next for varlevelsup = 1, etc. In each sublist the first + * item is for varno = 1, next varno = 2, etc. (Each sublist has the same + * format as the rtable list of a parsetree or query.) + * + * forceprefix is TRUE to force all Vars to be prefixed with their table names. + * Otherwise, a prefix is printed only if there's more than one table involved + * (and someday the code might try to print one only if there's ambiguity). + * + * The result is a palloc'd string. + * ---------- + */ +char * +deparse_expression(Node *expr, List *rangetables, bool forceprefix) +{ + StringInfoData buf; + deparse_context context; + + initStringInfo(&buf); + context.buf = &buf; + context.rangetables = rangetables; + context.varprefix = (forceprefix || + length(rangetables) != 1 || + length((List *) lfirst(rangetables)) != 1); + + rulename = ""; /* in case of errors */ + + get_rule_expr(expr, &context); + + return buf.data; +} /* ---------- * make_ruledef - reconstruct the CREATE RULE command @@ -672,15 +698,18 @@ make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc) { Node *qual; Query *query; - QryHier qh; + deparse_context context; + + appendStringInfo(buf, " WHERE "); qual = stringToNode(ev_qual); query = (Query *) lfirst(actions); - qh.parent = NULL; - qh.query = query; - appendStringInfo(buf, " WHERE "); - get_rule_expr(buf, &qh, 0, qual, TRUE); + context.buf = buf; + context.rangetables = lcons(query->rtable, NIL); + context.varprefix = (length(query->rtable) != 1); + + get_rule_expr(qual, &context); } appendStringInfo(buf, " DO "); @@ -699,7 +728,7 @@ make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc) foreach(action, actions) { query = (Query *) lfirst(action); - get_query_def(buf, query, NULL); + get_query_def(query, buf, NIL); appendStringInfo(buf, "; "); } appendStringInfo(buf, ");"); @@ -715,7 +744,7 @@ make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc) Query *query; query = (Query *) lfirst(actions); - get_query_def(buf, query, NULL); + get_query_def(query, buf, NIL); appendStringInfo(buf, ";"); } } @@ -779,7 +808,7 @@ make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc) return; } - get_query_def(buf, query, NULL); + get_query_def(query, buf, NIL); appendStringInfo(buf, ";"); } @@ -791,29 +820,31 @@ make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc) * ---------- */ static void -get_query_def(StringInfo buf, Query *query, QryHier *parentqh) +get_query_def(Query *query, StringInfo buf, List *parentrtables) { - QryHier qh; + deparse_context context; - qh.parent = parentqh; - qh.query = query; + context.buf = buf; + context.rangetables = lcons(query->rtable, parentrtables); + context.varprefix = (parentrtables != NIL || + length(query->rtable) != 1); switch (query->commandType) { case CMD_SELECT: - get_select_query_def(buf, query, &qh); + get_select_query_def(query, &context); break; case CMD_UPDATE: - get_update_query_def(buf, query, &qh); + get_update_query_def(query, &context); break; case CMD_INSERT: - get_insert_query_def(buf, query, &qh); + get_insert_query_def(query, &context); break; case CMD_DELETE: - get_delete_query_def(buf, query, &qh); + get_delete_query_def(query, &context); break; case CMD_NOTHING: @@ -833,8 +864,9 @@ get_query_def(StringInfo buf, Query *query, QryHier *parentqh) * ---------- */ static void -get_select_query_def(StringInfo buf, Query *query, QryHier *qh) +get_select_query_def(Query *query, deparse_context *context) { + StringInfo buf = context->buf; char *sep; TargetEntry *tle; RangeTblEntry *rte; @@ -904,7 +936,7 @@ get_select_query_def(StringInfo buf, Query *query, QryHier *qh) appendStringInfo(buf, sep); sep = ", "; - get_tle_expr(buf, qh, 0, tle, (rt_numused > 1)); + get_tle_expr(tle, context); /* Check if we must say AS ... */ if (! IsA(tle->expr, Var)) @@ -914,7 +946,7 @@ get_select_query_def(StringInfo buf, Query *query, QryHier *qh) Var *var = (Var *) (tle->expr); char *attname; - rte = get_rte_for_var(var, qh); + rte = get_rte_for_var(var, context); attname = get_attribute_name(rte->relid, var->varattno); if (strcmp(attname, tle->resdom->resname)) tell_as = TRUE; @@ -957,7 +989,7 @@ get_select_query_def(StringInfo buf, Query *query, QryHier *qh) if (query->qual != NULL) { appendStringInfo(buf, " WHERE "); - get_rule_expr(buf, qh, 0, query->qual, (rt_numused > 1)); + get_rule_expr(query->qual, context); } /* Add the GROUP BY CLAUSE */ @@ -973,7 +1005,7 @@ get_select_query_def(StringInfo buf, Query *query, QryHier *qh) groupexpr = get_sortgroupclause_expr(grp, query->targetList); appendStringInfo(buf, sep); - get_rule_expr(buf, qh, 0, groupexpr, (rt_numused > 1)); + get_rule_expr(groupexpr, context); sep = ", "; } } @@ -985,8 +1017,9 @@ get_select_query_def(StringInfo buf, Query *query, QryHier *qh) * ---------- */ static void -get_insert_query_def(StringInfo buf, Query *query, QryHier *qh) +get_insert_query_def(Query *query, deparse_context *context) { + StringInfo buf = context->buf; char *sep; TargetEntry *tle; RangeTblEntry *rte; @@ -1064,12 +1097,12 @@ get_insert_query_def(StringInfo buf, Query *query, QryHier *qh) appendStringInfo(buf, sep); sep = ", "; - get_tle_expr(buf, qh, 0, tle, (rt_numused > 1)); + get_tle_expr(tle, context); } appendStringInfo(buf, ")"); } else - get_select_query_def(buf, query, qh); + get_select_query_def(query, context); } @@ -1078,8 +1111,9 @@ get_insert_query_def(StringInfo buf, Query *query, QryHier *qh) * ---------- */ static void -get_update_query_def(StringInfo buf, Query *query, QryHier *qh) +get_update_query_def(Query *query, deparse_context *context) { + StringInfo buf = context->buf; char *sep; TargetEntry *tle; RangeTblEntry *rte; @@ -1101,14 +1135,14 @@ get_update_query_def(StringInfo buf, Query *query, QryHier *qh) appendStringInfo(buf, sep); sep = ", "; appendStringInfo(buf, "\"%s\" = ", tle->resdom->resname); - get_tle_expr(buf, qh, query->resultRelation, tle, TRUE); + get_tle_expr(tle, context); } /* Finally add a WHERE clause if given */ if (query->qual != NULL) { appendStringInfo(buf, " WHERE "); - get_rule_expr(buf, qh, query->resultRelation, query->qual, TRUE); + get_rule_expr(query->qual, context); } } @@ -1118,8 +1152,9 @@ get_update_query_def(StringInfo buf, Query *query, QryHier *qh) * ---------- */ static void -get_delete_query_def(StringInfo buf, Query *query, QryHier *qh) +get_delete_query_def(Query *query, deparse_context *context) { + StringInfo buf = context->buf; RangeTblEntry *rte; /* ---------- @@ -1133,7 +1168,7 @@ get_delete_query_def(StringInfo buf, Query *query, QryHier *qh) if (query->qual != NULL) { appendStringInfo(buf, " WHERE "); - get_rule_expr(buf, qh, 0, query->qual, FALSE); + get_rule_expr(query->qual, context); } } @@ -1141,14 +1176,15 @@ get_delete_query_def(StringInfo buf, Query *query, QryHier *qh) * Find the RTE referenced by a (possibly nonlocal) Var. */ static RangeTblEntry * -get_rte_for_var(Var *var, QryHier *qh) +get_rte_for_var(Var *var, deparse_context *context) { + List *rtlist = context->rangetables; int sup = var->varlevelsup; while (sup-- > 0) - qh = qh->parent; + rtlist = lnext(rtlist); - return (RangeTblEntry *) nth(var->varno - 1, qh->query->rtable); + return (RangeTblEntry *) nth(var->varno - 1, (List *) lfirst(rtlist)); } @@ -1157,9 +1193,10 @@ get_rte_for_var(Var *var, QryHier *qh) * ---------- */ static void -get_rule_expr(StringInfo buf, QryHier *qh, int rt_index, - Node *node, bool varprefix) +get_rule_expr(Node *node, deparse_context *context) { + StringInfo buf = context->buf; + if (node == NULL) return; @@ -1175,20 +1212,23 @@ get_rule_expr(StringInfo buf, QryHier *qh, int rt_index, switch (nodeTag(node)) { case T_Const: - get_const_expr(buf, (Const *) node); + get_const_expr((Const *) node, context); break; case T_Var: { Var *var = (Var *) node; - RangeTblEntry *rte = get_rte_for_var(var, qh); + RangeTblEntry *rte = get_rte_for_var(var, context); - if (!strcmp(rte->refname, "*NEW*")) - appendStringInfo(buf, "new."); - else if (!strcmp(rte->refname, "*CURRENT*")) - appendStringInfo(buf, "old."); - else - appendStringInfo(buf, "\"%s\".", rte->refname); + if (context->varprefix) + { + if (!strcmp(rte->refname, "*NEW*")) + appendStringInfo(buf, "new."); + else if (!strcmp(rte->refname, "*CURRENT*")) + appendStringInfo(buf, "old."); + else + appendStringInfo(buf, "\"%s\".", rte->refname); + } appendStringInfo(buf, "\"%s\"", get_attribute_name(rte->relid, var->varattno)); @@ -1211,14 +1251,10 @@ get_rule_expr(StringInfo buf, QryHier *qh, int rt_index, if (length(args) == 2) { /* binary operator */ - get_rule_expr(buf, qh, rt_index, - (Node *) lfirst(args), - varprefix); + get_rule_expr((Node *) lfirst(args), context); appendStringInfo(buf, " %s ", get_opname(((Oper *) expr->oper)->opno)); - get_rule_expr(buf, qh, rt_index, - (Node *) lsecond(args), - varprefix); + get_rule_expr((Node *) lsecond(args), context); } else { @@ -1237,14 +1273,12 @@ get_rule_expr(StringInfo buf, QryHier *qh, int rt_index, case 'l': appendStringInfo(buf, "%s ", get_opname(opno)); - get_rule_expr(buf, qh, rt_index, - (Node *) lfirst(args), - varprefix); + get_rule_expr((Node *) lfirst(args), + context); break; case 'r': - get_rule_expr(buf, qh, rt_index, - (Node *) lfirst(args), - varprefix); + get_rule_expr((Node *) lfirst(args), + context); appendStringInfo(buf, " %s", get_opname(opno)); break; @@ -1257,49 +1291,34 @@ get_rule_expr(StringInfo buf, QryHier *qh, int rt_index, case OR_EXPR: appendStringInfo(buf, "("); - get_rule_expr(buf, qh, rt_index, - (Node *) lfirst(args), - varprefix); - /* It's not clear that we can ever see N-argument - * OR/AND clauses here, but might as well cope... - */ + get_rule_expr((Node *) lfirst(args), context); while ((args = lnext(args)) != NIL) { appendStringInfo(buf, " OR "); - get_rule_expr(buf, qh, rt_index, - (Node *) lfirst(args), - varprefix); + get_rule_expr((Node *) lfirst(args), context); } appendStringInfo(buf, ")"); break; case AND_EXPR: appendStringInfo(buf, "("); - get_rule_expr(buf, qh, rt_index, - (Node *) lfirst(args), - varprefix); + get_rule_expr((Node *) lfirst(args), context); while ((args = lnext(args)) != NIL) { appendStringInfo(buf, " AND "); - get_rule_expr(buf, qh, rt_index, - (Node *) lfirst(args), - varprefix); + get_rule_expr((Node *) lfirst(args), context); } appendStringInfo(buf, ")"); break; case NOT_EXPR: appendStringInfo(buf, "(NOT "); - get_rule_expr(buf, qh, rt_index, - (Node *) lfirst(args), - varprefix); + get_rule_expr((Node *) lfirst(args), context); appendStringInfo(buf, ")"); break; case FUNC_EXPR: - get_func_expr(buf, qh, rt_index, - (Expr *) node, - varprefix); + get_func_expr((Expr *) node, context); break; default: @@ -1314,15 +1333,13 @@ get_rule_expr(StringInfo buf, QryHier *qh, int rt_index, Aggref *aggref = (Aggref *) node; appendStringInfo(buf, "\"%s\"(", aggref->aggname); - get_rule_expr(buf, qh, rt_index, - (Node *) (aggref->target), varprefix); + get_rule_expr(aggref->target, context); appendStringInfo(buf, ")"); } break; case T_Iter: - get_rule_expr(buf, qh, rt_index, - ((Iter *) node)->iterexpr, varprefix); + get_rule_expr(((Iter *) node)->iterexpr, context); break; case T_ArrayRef: @@ -1331,23 +1348,18 @@ get_rule_expr(StringInfo buf, QryHier *qh, int rt_index, List *lowlist; List *uplist; - get_rule_expr(buf, qh, rt_index, - aref->refexpr, varprefix); + get_rule_expr(aref->refexpr, context); lowlist = aref->reflowerindexpr; foreach(uplist, aref->refupperindexpr) { appendStringInfo(buf, "["); if (lowlist) { - get_rule_expr(buf, qh, rt_index, - (Node *) lfirst(lowlist), - varprefix); + get_rule_expr((Node *) lfirst(lowlist), context); appendStringInfo(buf, ":"); lowlist = lnext(lowlist); } - get_rule_expr(buf, qh, rt_index, - (Node *) lfirst(uplist), - varprefix); + get_rule_expr((Node *) lfirst(uplist), context); appendStringInfo(buf, "]"); } /* XXX need to do anything with refassgnexpr? */ @@ -1365,21 +1377,18 @@ get_rule_expr(StringInfo buf, QryHier *qh, int rt_index, CaseWhen *when = (CaseWhen *) lfirst(temp); appendStringInfo(buf, " WHEN "); - get_rule_expr(buf, qh, rt_index, - when->expr, varprefix); + get_rule_expr(when->expr, context); appendStringInfo(buf, " THEN "); - get_rule_expr(buf, qh, rt_index, - when->result, varprefix); + get_rule_expr(when->result, context); } appendStringInfo(buf, " ELSE "); - get_rule_expr(buf, qh, rt_index, - caseexpr->defresult, varprefix); + get_rule_expr(caseexpr->defresult, context); appendStringInfo(buf, " END"); } break; case T_SubLink: - get_sublink_expr(buf, qh, rt_index, node, varprefix); + get_sublink_expr(node, context); break; default: @@ -1396,9 +1405,9 @@ get_rule_expr(StringInfo buf, QryHier *qh, int rt_index, * ---------- */ static void -get_func_expr(StringInfo buf, QryHier *qh, int rt_index, - Expr *expr, bool varprefix) +get_func_expr(Expr *expr, deparse_context *context) { + StringInfo buf = context->buf; HeapTuple proctup; Form_pg_proc procStruct; List *l; @@ -1411,7 +1420,8 @@ get_func_expr(StringInfo buf, QryHier *qh, int rt_index, * ---------- */ proctup = SearchSysCacheTuple(PROOID, - ObjectIdGetDatum(func->funcid), 0, 0, 0); + ObjectIdGetDatum(func->funcid), + 0, 0, 0); if (!HeapTupleIsValid(proctup)) elog(ERROR, "cache lookup for proc %u failed", func->funcid); @@ -1426,14 +1436,14 @@ get_func_expr(StringInfo buf, QryHier *qh, int rt_index, if (!strcmp(proname, "nullvalue")) { appendStringInfo(buf, "("); - get_rule_expr(buf, qh, rt_index, lfirst(expr->args), varprefix); + get_rule_expr((Node *) lfirst(expr->args), context); appendStringInfo(buf, " ISNULL)"); return; } if (!strcmp(proname, "nonnullvalue")) { appendStringInfo(buf, "("); - get_rule_expr(buf, qh, rt_index, lfirst(expr->args), varprefix); + get_rule_expr((Node *) lfirst(expr->args), context); appendStringInfo(buf, " NOTNULL)"); return; } @@ -1449,7 +1459,7 @@ get_func_expr(StringInfo buf, QryHier *qh, int rt_index, { appendStringInfo(buf, sep); sep = ", "; - get_rule_expr(buf, qh, rt_index, lfirst(l), varprefix); + get_rule_expr((Node *) lfirst(l), context); } appendStringInfo(buf, ")"); } @@ -1466,8 +1476,7 @@ get_func_expr(StringInfo buf, QryHier *qh, int rt_index, * ---------- */ static void -get_tle_expr(StringInfo buf, QryHier *qh, int rt_index, - TargetEntry *tle, bool varprefix) +get_tle_expr(TargetEntry *tle, deparse_context *context) { Expr *expr = (Expr *) (tle->expr); Func *func; @@ -1485,7 +1494,7 @@ get_tle_expr(StringInfo buf, QryHier *qh, int rt_index, ! IsA(expr, Expr) || expr->opType != FUNC_EXPR) { - get_rule_expr(buf, qh, rt_index, tle->expr, varprefix); + get_rule_expr(tle->expr, context); return; } @@ -1511,7 +1520,7 @@ get_tle_expr(StringInfo buf, QryHier *qh, int rt_index, procStruct->prorettype != procStruct->proargtypes[0] || procStruct->proargtypes[1] != INT4OID) { - get_rule_expr(buf, qh, rt_index, tle->expr, varprefix); + get_rule_expr(tle->expr, context); return; } @@ -1529,7 +1538,7 @@ get_tle_expr(StringInfo buf, QryHier *qh, int rt_index, if (strncmp(procStruct->proname.data, typeStruct->typname.data, NAMEDATALEN) != 0) { - get_rule_expr(buf, qh, rt_index, tle->expr, varprefix); + get_rule_expr(tle->expr, context); return; } @@ -1542,7 +1551,7 @@ get_tle_expr(StringInfo buf, QryHier *qh, int rt_index, if (! IsA(second_arg, Const) || DatumGetInt32(second_arg->constvalue) != tle->resdom->restypmod) { - get_rule_expr(buf, qh, rt_index, tle->expr, varprefix); + get_rule_expr(tle->expr, context); return; } @@ -1550,7 +1559,7 @@ get_tle_expr(StringInfo buf, QryHier *qh, int rt_index, * Whow - got it. Now get rid of the padding function * ---------- */ - get_rule_expr(buf, qh, rt_index, lfirst(expr->args), varprefix); + get_rule_expr((Node *) lfirst(expr->args), context); } @@ -1561,8 +1570,9 @@ get_tle_expr(StringInfo buf, QryHier *qh, int rt_index, * ---------- */ static void -get_const_expr(StringInfo buf, Const *constval) +get_const_expr(Const *constval, deparse_context *context) { + StringInfo buf = context->buf; HeapTuple typetup; Form_pg_type typeStruct; FmgrInfo finfo_output; @@ -1624,9 +1634,9 @@ get_const_expr(StringInfo buf, Const *constval) * ---------- */ static void -get_sublink_expr(StringInfo buf, QryHier *qh, int rt_index, - Node *node, bool varprefix) +get_sublink_expr(Node *node, deparse_context *context) { + StringInfo buf = context->buf; SubLink *sublink = (SubLink *) node; Query *query = (Query *) (sublink->subselect); Oper *oper; @@ -1645,7 +1655,7 @@ get_sublink_expr(StringInfo buf, QryHier *qh, int rt_index, { appendStringInfo(buf, sep); sep = ", "; - get_rule_expr(buf, qh, rt_index, lfirst(l), varprefix); + get_rule_expr((Node *) lfirst(l), context); } if (length(sublink->lefthand) > 1) @@ -1682,7 +1692,7 @@ get_sublink_expr(StringInfo buf, QryHier *qh, int rt_index, } appendStringInfo(buf, "("); - get_query_def(buf, query, qh); + get_query_def(query, buf, context->rangetables); appendStringInfo(buf, "))"); } diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index afba41db108094e12a771688014257c7586f7ba6..fab04036d2ea029d1668faa8a75375ac0145cd78 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/cache/relcache.c,v 1.73 1999/09/18 19:07:55 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/cache/relcache.c,v 1.74 1999/10/03 23:55:33 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -19,7 +19,7 @@ * RelationIdGetRelation - get a reldesc by relation id * RelationNameGetRelation - get a reldesc by relation name * RelationClose - close an open relation - * RelationFlushRelation - flush relation information + * RelationRebuildRelation - rebuild relation information * * NOTES * This file is in the process of being cleaned up @@ -59,8 +59,9 @@ #include "utils/temprel.h" +static void RelationClearRelation(Relation relation, bool rebuildIt); static void RelationFlushRelation(Relation *relationPtr, - bool onlyFlushReferenceCountZero); + bool onlyFlushReferenceCountZero); static Relation RelationNameCacheGetRelation(char *relationName); static void RelationCacheAbortWalker(Relation *relationPtr, int dummy); @@ -247,34 +248,6 @@ static List *newlyCreatedRelns = NULL; */ -#if NOT_USED /* XXX This doesn't seem to be used - * anywhere */ -/* -------------------------------- - * BuildDescInfoError returns a string appropriate to - * the buildinfo passed to it - * -------------------------------- - */ -static char * -BuildDescInfoError(RelationBuildDescInfo buildinfo) -{ - static char errBuf[64]; - - MemSet(errBuf, 0, (int) sizeof(errBuf)); - switch (buildinfo.infotype) - { - case INFO_RELID: - sprintf(errBuf, "(relation id %u)", buildinfo.i.info_id); - break; - case INFO_RELNAME: - sprintf(errBuf, "(relation name %s)", buildinfo.i.info_name); - break; - } - - return errBuf; -} - -#endif - /* -------------------------------- * ScanPgRelation * @@ -403,7 +376,7 @@ scan_pg_rel_ind(RelationBuildDescInfo buildinfo) * * If 'relation' is NULL, allocate a new RelationData object. * If not, reuse the given object (that path is taken only when - * we have to rebuild a relcache entry during RelationFlushRelation). + * we have to rebuild a relcache entry during RelationClearRelation). * ---------------- */ static Relation @@ -578,11 +551,14 @@ build_tupdesc_ind(RelationBuildDescInfo buildinfo, if (attp->atthasdef) { if (attrdef == NULL) + { attrdef = (AttrDefault *) palloc(relation->rd_rel->relnatts * sizeof(AttrDefault)); + MemSet(attrdef, 0, + relation->rd_rel->relnatts * sizeof(AttrDefault)); + } attrdef[ndef].adnum = i; attrdef[ndef].adbin = NULL; - attrdef[ndef].adsrc = NULL; ndef++; } } @@ -1231,7 +1207,9 @@ RelationNameGetRelation(char *relationName) */ /* -------------------------------- - * RelationClose - close an open relation + * RelationClose - close an open relation + * + * Actually, we just decrement the refcount. * -------------------------------- */ void @@ -1242,17 +1220,18 @@ RelationClose(Relation relation) } /* -------------------------------- - * RelationFlushRelation + * RelationClearRelation * - * Actually blows away a relation cache entry... RelationFree doesn't do - * anything anymore. + * Physically blow away a relation cache entry, or reset it and rebuild + * it from scratch (that is, from catalog entries). The latter path is + * usually used when we are notified of a change to an open relation + * (one with refcount > 0). However, this routine just does whichever + * it's told to do; callers must determine which they want. * -------------------------------- */ static void -RelationFlushRelation(Relation *relationPtr, - bool onlyFlushReferenceCountZero) +RelationClearRelation(Relation relation, bool rebuildIt) { - Relation relation = *relationPtr; MemoryContext oldcxt; /* @@ -1261,19 +1240,18 @@ RelationFlushRelation(Relation *relationPtr, * if the relation is not deleted, the next smgr access should * reopen the files automatically. This ensures that the low-level * file access state is updated after, say, a vacuum truncation. + * * NOTE: this call is a no-op if the relation's smgr file is already * closed or unlinked. */ smgrclose(DEFAULT_SMGR, relation); - if (relation->rd_isnailed || relation->rd_myxactonly) - { - /* Do not flush relation cache entry if it is a nailed-in system - * relation or it is marked transaction-local. - * (To delete a local relation, caller must clear rd_myxactonly!) - */ + /* + * Never, never ever blow away a nailed-in system relation, + * because we'd be unable to recover. + */ + if (relation->rd_isnailed) return; - } oldcxt = MemoryContextSwitchTo((MemoryContext) CacheCxt); @@ -1293,19 +1271,21 @@ RelationFlushRelation(Relation *relationPtr, FreeTriggerDesc(relation); pfree(RelationGetForm(relation)); - /* If we're really done with the relcache entry, blow it away. + /* + * If we're really done with the relcache entry, blow it away. * But if someone is still using it, reconstruct the whole deal * without moving the physical RelationData record (so that the - * someone's pointer is still valid). Preserve ref count, too. + * someone's pointer is still valid). Must preserve ref count + * and myxactonly flag, too. */ - if (!onlyFlushReferenceCountZero || - RelationHasReferenceCountZero(relation)) + if (! rebuildIt) { pfree(relation); } else { uint16 old_refcnt = relation->rd_refcnt; + bool old_myxactonly = relation->rd_myxactonly; RelationBuildDescInfo buildinfo; buildinfo.infotype = INFO_RELID; @@ -1315,19 +1295,53 @@ RelationFlushRelation(Relation *relationPtr, { /* Should only get here if relation was deleted */ pfree(relation); - elog(ERROR, "RelationFlushRelation: relation %u deleted while still in use", + elog(ERROR, "RelationClearRelation: relation %u deleted while still in use", buildinfo.i.info_id); } RelationSetReferenceCount(relation, old_refcnt); + relation->rd_myxactonly = old_myxactonly; } MemoryContextSwitchTo(oldcxt); } /* -------------------------------- - * RelationForgetRelation - - * RelationFlushRelation + if the relation is myxactonly then - * get rid of the relation descriptor from the newly created + * RelationFlushRelation + * + * Rebuild the relation if it is open (refcount > 0), else blow it away. + * Setting onlyFlushReferenceCountZero to FALSE overrides refcount check. + * This is currently only used to process SI invalidation notifications. + * The peculiar calling convention (pointer to pointer to relation) + * is needed so that we can use this routine as a hash table walker. + * -------------------------------- + */ +static void +RelationFlushRelation(Relation *relationPtr, + bool onlyFlushReferenceCountZero) +{ + Relation relation = *relationPtr; + + /* + * Do nothing to transaction-local relations, since they cannot be + * subjects of SI notifications from other backends. + */ + if (relation->rd_myxactonly) + return; + + /* + * Zap it. Rebuild if it has nonzero ref count and we did not get + * the override flag. + */ + RelationClearRelation(relation, + (onlyFlushReferenceCountZero && + ! RelationHasReferenceCountZero(relation))); +} + +/* -------------------------------- + * RelationForgetRelation - + * + * RelationClearRelation + if the relation is myxactonly then + * remove the relation descriptor from the newly created * relation list. * -------------------------------- */ @@ -1368,12 +1382,24 @@ RelationForgetRelation(Oid rid) MemoryContextSwitchTo(oldcxt); } - relation->rd_myxactonly = false; /* so it can be flushed */ - - RelationFlushRelation(&relation, false); + /* Unconditionally destroy the relcache entry */ + RelationClearRelation(relation, false); } } +/* -------------------------------- + * RelationRebuildRelation - + * + * Force a relcache entry to be rebuilt from catalog entries. + * This is needed, eg, after modifying an attribute of the rel. + * -------------------------------- + */ +void +RelationRebuildRelation(Relation relation) +{ + RelationClearRelation(relation, true); +} + /* -------------------------------- * RelationIdInvalidateRelationCacheByRelationId * -------------------------------- @@ -1573,6 +1599,11 @@ RelationPurgeLocalRelation(bool xactCommitted) Assert(reln != NULL && reln->rd_myxactonly); + reln->rd_myxactonly = false; /* mark it not on list anymore */ + + newlyCreatedRelns = lnext(newlyCreatedRelns); + pfree(l); + if (!xactCommitted) { @@ -1592,13 +1623,8 @@ RelationPurgeLocalRelation(bool xactCommitted) smgrunlink(DEFAULT_SMGR, reln); } - reln->rd_myxactonly = false; /* so it can be flushed */ - if (!IsBootstrapProcessingMode()) - RelationFlushRelation(&reln, false); - - newlyCreatedRelns = lnext(newlyCreatedRelns); - pfree(l); + RelationClearRelation(reln, false); } MemoryContextSwitchTo(oldcxt); @@ -1717,7 +1743,7 @@ AttrDefaultFetch(Relation relation) { if (adform->adnum != attrdef[i].adnum) continue; - if (attrdef[i].adsrc != NULL) + if (attrdef[i].adbin != NULL) elog(ERROR, "AttrDefaultFetch: second record found for attr %s in rel %s", relation->rd_att->attrs[adform->adnum - 1]->attname.data, relation->rd_rel->relname.data); @@ -1730,14 +1756,6 @@ AttrDefaultFetch(Relation relation) relation->rd_att->attrs[adform->adnum - 1]->attname.data, relation->rd_rel->relname.data); attrdef[i].adbin = textout(val); - val = (struct varlena *) fastgetattr(&tuple, - Anum_pg_attrdef_adsrc, - adrel->rd_att, &isnull); - if (isnull) - elog(ERROR, "AttrDefaultFetch: adsrc IS NULL for attr %s in rel %s", - relation->rd_att->attrs[adform->adnum - 1]->attname.data, - relation->rd_rel->relname.data); - attrdef[i].adsrc = textout(val); break; } ReleaseBuffer(buffer); @@ -1816,13 +1834,6 @@ RelCheckFetch(Relation relation) elog(ERROR, "RelCheckFetch: rcbin IS NULL for rel %s", relation->rd_rel->relname.data); check[found].ccbin = textout(val); - val = (struct varlena *) fastgetattr(&tuple, - Anum_pg_relcheck_rcsrc, - rcrel->rd_att, &isnull); - if (isnull) - elog(ERROR, "RelCheckFetch: rcsrc IS NULL for rel %s", - relation->rd_rel->relname.data); - check[found].ccsrc = textout(val); found++; ReleaseBuffer(buffer); } diff --git a/src/include/access/tupdesc.h b/src/include/access/tupdesc.h index c1b30598b952ba7bf498be9b8b58354a61a5161a..3ff678d48f54554cb1095d91cfad6b52d1222b49 100644 --- a/src/include/access/tupdesc.h +++ b/src/include/access/tupdesc.h @@ -6,7 +6,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: tupdesc.h,v 1.24 1999/07/16 17:07:28 momjian Exp $ + * $Id: tupdesc.h,v 1.25 1999/10/03 23:55:34 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -21,22 +21,20 @@ typedef struct attrDefault { AttrNumber adnum; - char *adbin; - char *adsrc; + char *adbin; /* nodeToString representation of expr */ } AttrDefault; typedef struct constrCheck { char *ccname; - char *ccbin; - char *ccsrc; + char *ccbin; /* nodeToString representation of expr */ } ConstrCheck; /* This structure contains constraints of a tuple */ typedef struct tupleConstr { - AttrDefault *defval; - ConstrCheck *check; + AttrDefault *defval; /* array */ + ConstrCheck *check; /* array */ uint16 num_defval; uint16 num_check; bool has_not_null; diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h index c8c130da96b1620858f2a43938540d4c834f3ef5..faa708ff24bb803c8d96c71455689e69b8901808 100644 --- a/src/include/catalog/heap.h +++ b/src/include/catalog/heap.h @@ -6,7 +6,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: heap.h,v 1.21 1999/09/23 17:03:10 momjian Exp $ + * $Id: heap.h,v 1.22 1999/10/03 23:55:35 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -15,6 +15,12 @@ #include "utils/rel.h" +typedef struct RawColumnDefault +{ + AttrNumber attnum; /* attribute to attach default to */ + Node *raw_default; /* default value (untransformed parse tree) */ +} RawColumnDefault; + extern Oid RelnameFindRelid(char *relname); extern Relation heap_create(char *relname, TupleDesc att, bool isnoname, bool istemp); @@ -26,6 +32,10 @@ extern void heap_destroy_with_catalog(char *relname); extern void heap_truncate(char *relname); extern void heap_destroy(Relation rel); +extern void AddRelationRawConstraints(Relation rel, + List *rawColDefaults, + List *rawConstraints); + extern void InitNoNameRelList(void); extern void DestroyNoNameRels(void); diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 463ea1518e0d92407a4e300d0949da0df94bee7f..8ac9e3d76476cc7c3f469ad78318b24a94d21cb3 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -6,7 +6,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: parsenodes.h,v 1.82 1999/10/02 21:33:33 tgl Exp $ + * $Id: parsenodes.h,v 1.83 1999/10/03 23:55:36 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -140,25 +140,36 @@ typedef struct CreateStmt { NodeTag type; bool istemp; /* is this a temp table? */ - char *relname; /* the relation to create */ - List *tableElts; /* column definitions list of Column */ - List *inhRelnames; /* relations to inherit from list of Value - * (string) */ - List *constraints; /* list of constraints (ConstaintDef) */ + char *relname; /* name of relation to create */ + List *tableElts; /* column definitions (list of ColumnDef) */ + List *inhRelnames; /* relations to inherit from (list of + * T_String Values) */ + List *constraints; /* list of constraints (Constraint nodes) */ } CreateStmt; -typedef enum ConstrType /* type of constaints */ +typedef enum ConstrType /* types of constraints */ { - CONSTR_NULL, CONSTR_NOTNULL, CONSTR_DEFAULT, CONSTR_CHECK, CONSTR_PRIMARY, CONSTR_UNIQUE + CONSTR_NULL, CONSTR_NOTNULL, CONSTR_DEFAULT, CONSTR_CHECK, + CONSTR_PRIMARY, CONSTR_UNIQUE } ConstrType; +/* + * For constraints that use expressions (CONSTR_DEFAULT, CONSTR_CHECK) + * we may have the expression in either "raw" form (an untransformed + * parse tree) or "cooked" form (the nodeToString representation of + * an executable expression tree), depending on how this Constraint + * node was created (by parsing, or by inheritance from an existing + * relation). We should never have both in the same node! + */ + typedef struct Constraint { NodeTag type; ConstrType contype; char *name; /* name */ - void *def; /* definition */ - void *keys; /* list of primary keys */ + Node *raw_expr; /* untransformed parse tree */ + char *cooked_expr; /* nodeToString representation */ + List *keys; /* list of primary keys */ } Constraint; /* ---------------------- @@ -790,6 +801,18 @@ typedef struct CaseWhen /* * ColumnDef - column definition (used in various creates) + * + * If the column has a default value, we may have the value expression + * in either "raw" form (an untransformed parse tree) or "cooked" form + * (the nodeToString representation of an executable expression tree), + * depending on how this ColumnDef node was created (by parsing, or by + * inheritance from an existing relation). We should never have both + * in the same node! + * + * The constraints list may contain a CONSTR_DEFAULT item in a raw + * parsetree produced by gram.y, but transformCreateStmt will remove + * the item and set raw_default instead. CONSTR_DEFAULT items + * should not appear in any subsequent processing. */ typedef struct ColumnDef { @@ -798,8 +821,9 @@ typedef struct ColumnDef TypeName *typename; /* type of column */ bool is_not_null; /* flag to NOT NULL constraint */ bool is_sequence; /* is a sequence? */ - char *defval; /* default value of column */ - List *constraints; /* constraints on column */ + Node *raw_default; /* default value (untransformed parse tree) */ + char *cooked_default; /* nodeToString representation */ + List *constraints; /* other constraints on column */ } ColumnDef; /* diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index f1c7a25f9dd4f2ef2fb229b1e99f9c394d39b2ca..da09be6524353d973b8d8545d61b57456916b718 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -6,7 +6,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: builtins.h,v 1.87 1999/09/30 14:54:24 wieck Exp $ + * $Id: builtins.h,v 1.88 1999/10/03 23:55:37 tgl Exp $ * * NOTES * This should normally only be included by fmgr.h. @@ -372,6 +372,14 @@ extern Oid regproctooid(RegProcedure rp); /* define macro to replace mixed-case function call - tgl 97/04/27 */ #define RegprocToOid(rp) regproctooid(rp) +/* ruleutils.c */ +extern text *pg_get_ruledef(NameData *rname); +extern text *pg_get_viewdef(NameData *rname); +extern text *pg_get_indexdef(Oid indexrelid); +extern NameData *pg_get_userbyid(int32 uid); +extern char *deparse_expression(Node *expr, List *rangetables, + bool forceprefix); + /* selfuncs.c */ extern float64 eqsel(Oid opid, Oid relid, AttrNumber attno, Datum value, int32 flag); extern float64 neqsel(Oid opid, Oid relid, AttrNumber attno, Datum value, int32 flag); diff --git a/src/include/utils/relcache.h b/src/include/utils/relcache.h index ad96a0fa9ad4d3e946bd01df2adc89bdf6b180ba..95fab3b22aeb89f7f358f0d48327389a700c27ba 100644 --- a/src/include/utils/relcache.h +++ b/src/include/utils/relcache.h @@ -6,7 +6,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: relcache.h,v 1.14 1999/09/04 18:42:11 tgl Exp $ + * $Id: relcache.h,v 1.15 1999/10/03 23:55:38 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -24,6 +24,12 @@ extern Relation RelationNameGetRelation(char *relationName); extern void RelationClose(Relation relation); extern void RelationForgetRelation(Oid rid); + +/* + * Routines for flushing/rebuilding relcache entries in various scenarios + */ +extern void RelationRebuildRelation(Relation relation); + extern void RelationIdInvalidateRelationCacheByRelationId(Oid relationId); extern void RelationIdInvalidateRelationCacheByAccessMethodId(Oid accessMethodId); diff --git a/src/test/regress/input/constraints.source b/src/test/regress/input/constraints.source index a39cb8b0bcb0d3014efcdb5a5305a84423df0cd8..a4f02d59d7dc86bfc1a90fe6c6ac99e916867fa1 100644 --- a/src/test/regress/input/constraints.source +++ b/src/test/regress/input/constraints.source @@ -34,12 +34,17 @@ INSERT INTO DEFAULTEXPR_TBL (i2) VALUES (NULL); SELECT '' AS four, * FROM DEFAULTEXPR_TBL; --- errors --- test for: --- extraneous comma --- booleans not allowed +-- syntax errors +-- test for extraneous comma CREATE TABLE error_tbl (i int DEFAULT (100, )); +-- this will fail because gram.y uses b_expr not a_expr for defaults, +-- to avoid a shift/reduce conflict that arises from NOT NULL being +-- part of the column definition syntax: CREATE TABLE error_tbl (b1 bool DEFAULT 1 < 2); +-- this should work, however: +CREATE TABLE error_tbl (b1 bool DEFAULT (1 < 2)); + +DROP TABLE error_tbl; -- -- CHECK syntax diff --git a/src/test/regress/output/constraints.source b/src/test/regress/output/constraints.source index edacd7d93fa907e1d64e047c4e64d87fdfa9ebfb..6b905e7f42c34312fea63e78f0f7302168e60a92 100644 --- a/src/test/regress/output/constraints.source +++ b/src/test/regress/output/constraints.source @@ -34,7 +34,9 @@ four| i1|i2 QUERY: CREATE TABLE error_tbl (i int DEFAULT (100, )); ERROR: parser: parse error at or near "," QUERY: CREATE TABLE error_tbl (b1 bool DEFAULT 1 < 2); -ERROR: boolean expressions not supported in DEFAULT +ERROR: parser: parse error at or near "<" +QUERY: CREATE TABLE error_tbl (b1 bool DEFAULT (1 < 2)); +QUERY: DROP TABLE error_tbl; QUERY: CREATE TABLE CHECK_TBL (x int, CONSTRAINT CHECK_CON CHECK (x > 3)); QUERY: INSERT INTO CHECK_TBL VALUES (5);