aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/access/common/tupdesc.c29
-rw-r--r--src/backend/catalog/heap.c441
-rw-r--r--src/backend/commands/command.c7
-rw-r--r--src/backend/commands/creatinh.c166
-rw-r--r--src/backend/commands/sequence.c3
-rw-r--r--src/backend/commands/view.c5
-rw-r--r--src/backend/nodes/outfuncs.c21
-rw-r--r--src/backend/parser/analyze.c157
-rw-r--r--src/backend/parser/gram.y402
-rw-r--r--src/backend/utils/adt/ruleutils.c300
-rw-r--r--src/backend/utils/cache/relcache.c171
-rw-r--r--src/include/access/tupdesc.h12
-rw-r--r--src/include/catalog/heap.h12
-rw-r--r--src/include/nodes/parsenodes.h48
-rw-r--r--src/include/utils/builtins.h10
-rw-r--r--src/include/utils/relcache.h8
-rw-r--r--src/test/regress/input/constraints.source13
-rw-r--r--src/test/regress/output/constraints.source4
18 files changed, 923 insertions, 886 deletions
diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c
index 4bce6ee914f..f0e717445a6 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 5ad0f7c413b..5cb9da1cae1 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 8872bcdfac7..3e71e7d1927 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 c146afa1db4..0b2d2360b3c 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 be47d32f9f9..228aaba7990 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 5baf4eceac8..a88b1840dd3 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 13e32ed54fe..6b1e560014f 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 276d28b3aa1..4bcf79a7277 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 96f480ea0ca..87c82839df4 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 59f3586d13f..bcaa0f0d68c 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;
@@ -81,16 +84,6 @@ 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
*
* Most of these functions used to use fixed-size buffers to build their
@@ -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 afba41db108..fab04036d2e 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,13 +1382,25 @@ 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 c1b30598b95..3ff678d48f5 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 c8c130da96b..faa708ff24b 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 463ea1518e0..8ac9e3d7647 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 f1c7a25f9dd..da09be65243 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 ad96a0fa9ad..95fab3b22ae 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 a39cb8b0bcb..a4f02d59d7d 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 edacd7d93fa..6b905e7f42c 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);