Reimplement parsing and storage of default expressions and constraint

expressions in CREATE TABLE.  There is no longer an emasculated expression
syntax for these things; it's full a_expr for constraints, and b_expr
for defaults (unfortunately the fact that NOT NULL is a part of the
column constraint syntax causes a shift/reduce conflict if you try a_expr.
Oh well --- at least parenthesized boolean expressions work now).  Also,
stored expression for a column default is not pre-coerced to the column
type; we rely on transformInsertStatement to do that when the default is
actually used.  This means "f1 datetime default 'now'" behaves the way
people usually expect it to.
BTW, all the support code is now there to implement ALTER TABLE ADD
CONSTRAINT and ALTER TABLE ADD COLUMN with a default value.  I didn't
actually teach ALTER TABLE to call it, but it wouldn't be much work.
This commit is contained in:
Tom Lane 1999-10-03 23:55:40 +00:00
parent f29ccc8270
commit eabc714a91
18 changed files with 915 additions and 878 deletions

View File

@ -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

View File

@ -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;
static char nulls[4] = {' ', ' ', ' ', ' '};
Relation attrrel;
Relation attridescs[Num_pg_attr_indices];
HeapTuple atttup;
Form_pg_attribute attStruct;
start:
/*
* 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)
{
for (i = 0; i < constr->num_defval; i++)
StoreAttrDefault(rel, &(constr->defval[i]));
numoldchecks = oldconstr->num_check;
oldchecks = oldconstr->check;
}
else
{
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

View File

@ -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)

View File

@ -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++)
{
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;
if (strcmp(check[i].ccname, cdef->name) == 0)
elog(ERROR, "Duplicate CHECK constraint name: '%s'",
cdef->name);
}
else
{
check[ncheck].ccname = (char *) palloc(NAMEDATALEN);
snprintf(check[ncheck].ccname, NAMEDATALEN, "$%d", ncheck + 1);
}
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);
}
}

View File

@ -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] = ' ';

View File

@ -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);
}

View File

@ -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

View File

@ -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:
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;
/*
* 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_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_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_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_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;
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;
}
clist = lnext(clist);
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;

View File

@ -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.

View File

@ -3,7 +3,7 @@
* out of it's tuple
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.26 1999/10/02 01:07:51 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.27 1999/10/03 23:55:31 tgl Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
@ -39,25 +39,28 @@
#include <fcntl.h>
#include "postgres.h"
#include "executor/spi.h"
#include "lib/stringinfo.h"
#include "optimizer/clauses.h"
#include "optimizer/tlist.h"
#include "utils/lsyscache.h"
#include "catalog/pg_index.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_shadow.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
/* ----------
* Local data types
* ----------
*/
typedef struct QryHier
typedef struct
{
struct QryHier *parent;
Query *query;
} QryHier;
StringInfo buf; /* output buffer to append to */
List *rangetables; /* List of List of RangeTblEntry */
bool varprefix; /* TRUE to print prefixes on Vars */
} deparse_context;
typedef struct {
Index rt_index;
@ -69,7 +72,7 @@ typedef struct {
* Global data
* ----------
*/
static char *rulename;
static char *rulename = NULL;
static void *plan_getrule = NULL;
static char *query_getrule = "SELECT * FROM pg_rewrite WHERE rulename = $1";
static void *plan_getview = NULL;
@ -80,16 +83,6 @@ static void *plan_getopclass = NULL;
static char *query_getopclass = "SELECT * FROM pg_opclass WHERE oid = $1";
/* ----------
* Global functions
* ----------
*/
text *pg_get_ruledef(NameData *rname);
text *pg_get_viewdef(NameData *rname);
text *pg_get_indexdef(Oid indexrelid);
NameData *pg_get_userbyid(int4 uid);
/* ----------
* Local functions
*
@ -100,21 +93,17 @@ NameData *pg_get_userbyid(int4 uid);
*/
static void make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc);
static void make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc);
static void get_query_def(StringInfo buf, Query *query, QryHier *parentqh);
static void get_select_query_def(StringInfo buf, Query *query, QryHier *qh);
static void get_insert_query_def(StringInfo buf, Query *query, QryHier *qh);
static void get_update_query_def(StringInfo buf, Query *query, QryHier *qh);
static void get_delete_query_def(StringInfo buf, Query *query, QryHier *qh);
static RangeTblEntry *get_rte_for_var(Var *var, QryHier *qh);
static void get_rule_expr(StringInfo buf, QryHier *qh, int rt_index,
Node *node, bool varprefix);
static void get_func_expr(StringInfo buf, QryHier *qh, int rt_index,
Expr *expr, bool varprefix);
static void get_tle_expr(StringInfo buf, QryHier *qh, int rt_index,
TargetEntry *tle, bool varprefix);
static void get_const_expr(StringInfo buf, Const *constval);
static void get_sublink_expr(StringInfo buf, QryHier *qh, int rt_index,
Node *node, bool varprefix);
static void get_query_def(Query *query, StringInfo buf, List *parentrtables);
static void get_select_query_def(Query *query, deparse_context *context);
static void get_insert_query_def(Query *query, deparse_context *context);
static void get_update_query_def(Query *query, deparse_context *context);
static void get_delete_query_def(Query *query, deparse_context *context);
static RangeTblEntry *get_rte_for_var(Var *var, deparse_context *context);
static void get_rule_expr(Node *node, deparse_context *context);
static void get_func_expr(Expr *expr, deparse_context *context);
static void get_tle_expr(TargetEntry *tle, deparse_context *context);
static void get_const_expr(Const *constval, deparse_context *context);
static void get_sublink_expr(Node *node, deparse_context *context);
static char *get_relation_name(Oid relid);
static char *get_attribute_name(Oid relid, int2 attnum);
static bool check_if_rte_used(Node *node, Index rt_index, int levelsup);
@ -554,7 +543,7 @@ pg_get_indexdef(Oid indexrelid)
* ----------
*/
NameData *
pg_get_userbyid(int4 uid)
pg_get_userbyid(int32 uid)
{
HeapTuple usertup;
Form_pg_shadow user_rec;
@ -584,6 +573,43 @@ pg_get_userbyid(int4 uid)
return result;
}
/* ----------
* deparse_expression - General utility for deparsing expressions
*
* expr is the node tree to be deparsed. It must be a transformed expression
* tree (ie, not the raw output of gram.y).
*
* rangetables is a List of Lists of RangeTblEntry nodes: first sublist is for
* varlevelsup = 0, next for varlevelsup = 1, etc. In each sublist the first
* item is for varno = 1, next varno = 2, etc. (Each sublist has the same
* format as the rtable list of a parsetree or query.)
*
* forceprefix is TRUE to force all Vars to be prefixed with their table names.
* Otherwise, a prefix is printed only if there's more than one table involved
* (and someday the code might try to print one only if there's ambiguity).
*
* The result is a palloc'd string.
* ----------
*/
char *
deparse_expression(Node *expr, List *rangetables, bool forceprefix)
{
StringInfoData buf;
deparse_context context;
initStringInfo(&buf);
context.buf = &buf;
context.rangetables = rangetables;
context.varprefix = (forceprefix ||
length(rangetables) != 1 ||
length((List *) lfirst(rangetables)) != 1);
rulename = ""; /* in case of errors */
get_rule_expr(expr, &context);
return buf.data;
}
/* ----------
* make_ruledef - reconstruct the CREATE RULE command
@ -672,15 +698,18 @@ make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc)
{
Node *qual;
Query *query;
QryHier qh;
deparse_context context;
appendStringInfo(buf, " WHERE ");
qual = stringToNode(ev_qual);
query = (Query *) lfirst(actions);
qh.parent = NULL;
qh.query = query;
appendStringInfo(buf, " WHERE ");
get_rule_expr(buf, &qh, 0, qual, TRUE);
context.buf = buf;
context.rangetables = lcons(query->rtable, NIL);
context.varprefix = (length(query->rtable) != 1);
get_rule_expr(qual, &context);
}
appendStringInfo(buf, " DO ");
@ -699,7 +728,7 @@ make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc)
foreach(action, actions)
{
query = (Query *) lfirst(action);
get_query_def(buf, query, NULL);
get_query_def(query, buf, NIL);
appendStringInfo(buf, "; ");
}
appendStringInfo(buf, ");");
@ -715,7 +744,7 @@ make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc)
Query *query;
query = (Query *) lfirst(actions);
get_query_def(buf, query, NULL);
get_query_def(query, buf, NIL);
appendStringInfo(buf, ";");
}
}
@ -779,7 +808,7 @@ make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc)
return;
}
get_query_def(buf, query, NULL);
get_query_def(query, buf, NIL);
appendStringInfo(buf, ";");
}
@ -791,29 +820,31 @@ make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc)
* ----------
*/
static void
get_query_def(StringInfo buf, Query *query, QryHier *parentqh)
get_query_def(Query *query, StringInfo buf, List *parentrtables)
{
QryHier qh;
deparse_context context;
qh.parent = parentqh;
qh.query = query;
context.buf = buf;
context.rangetables = lcons(query->rtable, parentrtables);
context.varprefix = (parentrtables != NIL ||
length(query->rtable) != 1);
switch (query->commandType)
{
case CMD_SELECT:
get_select_query_def(buf, query, &qh);
get_select_query_def(query, &context);
break;
case CMD_UPDATE:
get_update_query_def(buf, query, &qh);
get_update_query_def(query, &context);
break;
case CMD_INSERT:
get_insert_query_def(buf, query, &qh);
get_insert_query_def(query, &context);
break;
case CMD_DELETE:
get_delete_query_def(buf, query, &qh);
get_delete_query_def(query, &context);
break;
case CMD_NOTHING:
@ -833,8 +864,9 @@ get_query_def(StringInfo buf, Query *query, QryHier *parentqh)
* ----------
*/
static void
get_select_query_def(StringInfo buf, Query *query, QryHier *qh)
get_select_query_def(Query *query, deparse_context *context)
{
StringInfo buf = context->buf;
char *sep;
TargetEntry *tle;
RangeTblEntry *rte;
@ -904,7 +936,7 @@ get_select_query_def(StringInfo buf, Query *query, QryHier *qh)
appendStringInfo(buf, sep);
sep = ", ";
get_tle_expr(buf, qh, 0, tle, (rt_numused > 1));
get_tle_expr(tle, context);
/* Check if we must say AS ... */
if (! IsA(tle->expr, Var))
@ -914,7 +946,7 @@ get_select_query_def(StringInfo buf, Query *query, QryHier *qh)
Var *var = (Var *) (tle->expr);
char *attname;
rte = get_rte_for_var(var, qh);
rte = get_rte_for_var(var, context);
attname = get_attribute_name(rte->relid, var->varattno);
if (strcmp(attname, tle->resdom->resname))
tell_as = TRUE;
@ -957,7 +989,7 @@ get_select_query_def(StringInfo buf, Query *query, QryHier *qh)
if (query->qual != NULL)
{
appendStringInfo(buf, " WHERE ");
get_rule_expr(buf, qh, 0, query->qual, (rt_numused > 1));
get_rule_expr(query->qual, context);
}
/* Add the GROUP BY CLAUSE */
@ -973,7 +1005,7 @@ get_select_query_def(StringInfo buf, Query *query, QryHier *qh)
groupexpr = get_sortgroupclause_expr(grp,
query->targetList);
appendStringInfo(buf, sep);
get_rule_expr(buf, qh, 0, groupexpr, (rt_numused > 1));
get_rule_expr(groupexpr, context);
sep = ", ";
}
}
@ -985,8 +1017,9 @@ get_select_query_def(StringInfo buf, Query *query, QryHier *qh)
* ----------
*/
static void
get_insert_query_def(StringInfo buf, Query *query, QryHier *qh)
get_insert_query_def(Query *query, deparse_context *context)
{
StringInfo buf = context->buf;
char *sep;
TargetEntry *tle;
RangeTblEntry *rte;
@ -1064,12 +1097,12 @@ get_insert_query_def(StringInfo buf, Query *query, QryHier *qh)
appendStringInfo(buf, sep);
sep = ", ";
get_tle_expr(buf, qh, 0, tle, (rt_numused > 1));
get_tle_expr(tle, context);
}
appendStringInfo(buf, ")");
}
else
get_select_query_def(buf, query, qh);
get_select_query_def(query, context);
}
@ -1078,8 +1111,9 @@ get_insert_query_def(StringInfo buf, Query *query, QryHier *qh)
* ----------
*/
static void
get_update_query_def(StringInfo buf, Query *query, QryHier *qh)
get_update_query_def(Query *query, deparse_context *context)
{
StringInfo buf = context->buf;
char *sep;
TargetEntry *tle;
RangeTblEntry *rte;
@ -1101,14 +1135,14 @@ get_update_query_def(StringInfo buf, Query *query, QryHier *qh)
appendStringInfo(buf, sep);
sep = ", ";
appendStringInfo(buf, "\"%s\" = ", tle->resdom->resname);
get_tle_expr(buf, qh, query->resultRelation, tle, TRUE);
get_tle_expr(tle, context);
}
/* Finally add a WHERE clause if given */
if (query->qual != NULL)
{
appendStringInfo(buf, " WHERE ");
get_rule_expr(buf, qh, query->resultRelation, query->qual, TRUE);
get_rule_expr(query->qual, context);
}
}
@ -1118,8 +1152,9 @@ get_update_query_def(StringInfo buf, Query *query, QryHier *qh)
* ----------
*/
static void
get_delete_query_def(StringInfo buf, Query *query, QryHier *qh)
get_delete_query_def(Query *query, deparse_context *context)
{
StringInfo buf = context->buf;
RangeTblEntry *rte;
/* ----------
@ -1133,7 +1168,7 @@ get_delete_query_def(StringInfo buf, Query *query, QryHier *qh)
if (query->qual != NULL)
{
appendStringInfo(buf, " WHERE ");
get_rule_expr(buf, qh, 0, query->qual, FALSE);
get_rule_expr(query->qual, context);
}
}
@ -1141,14 +1176,15 @@ get_delete_query_def(StringInfo buf, Query *query, QryHier *qh)
* Find the RTE referenced by a (possibly nonlocal) Var.
*/
static RangeTblEntry *
get_rte_for_var(Var *var, QryHier *qh)
get_rte_for_var(Var *var, deparse_context *context)
{
List *rtlist = context->rangetables;
int sup = var->varlevelsup;
while (sup-- > 0)
qh = qh->parent;
rtlist = lnext(rtlist);
return (RangeTblEntry *) nth(var->varno - 1, qh->query->rtable);
return (RangeTblEntry *) nth(var->varno - 1, (List *) lfirst(rtlist));
}
@ -1157,9 +1193,10 @@ get_rte_for_var(Var *var, QryHier *qh)
* ----------
*/
static void
get_rule_expr(StringInfo buf, QryHier *qh, int rt_index,
Node *node, bool varprefix)
get_rule_expr(Node *node, deparse_context *context)
{
StringInfo buf = context->buf;
if (node == NULL)
return;
@ -1175,20 +1212,23 @@ get_rule_expr(StringInfo buf, QryHier *qh, int rt_index,
switch (nodeTag(node))
{
case T_Const:
get_const_expr(buf, (Const *) node);
get_const_expr((Const *) node, context);
break;
case T_Var:
{
Var *var = (Var *) node;
RangeTblEntry *rte = get_rte_for_var(var, qh);
RangeTblEntry *rte = get_rte_for_var(var, context);
if (!strcmp(rte->refname, "*NEW*"))
appendStringInfo(buf, "new.");
else if (!strcmp(rte->refname, "*CURRENT*"))
appendStringInfo(buf, "old.");
else
appendStringInfo(buf, "\"%s\".", rte->refname);
if (context->varprefix)
{
if (!strcmp(rte->refname, "*NEW*"))
appendStringInfo(buf, "new.");
else if (!strcmp(rte->refname, "*CURRENT*"))
appendStringInfo(buf, "old.");
else
appendStringInfo(buf, "\"%s\".", rte->refname);
}
appendStringInfo(buf, "\"%s\"",
get_attribute_name(rte->relid,
var->varattno));
@ -1211,14 +1251,10 @@ get_rule_expr(StringInfo buf, QryHier *qh, int rt_index,
if (length(args) == 2)
{
/* binary operator */
get_rule_expr(buf, qh, rt_index,
(Node *) lfirst(args),
varprefix);
get_rule_expr((Node *) lfirst(args), context);
appendStringInfo(buf, " %s ",
get_opname(((Oper *) expr->oper)->opno));
get_rule_expr(buf, qh, rt_index,
(Node *) lsecond(args),
varprefix);
get_rule_expr((Node *) lsecond(args), context);
}
else
{
@ -1237,14 +1273,12 @@ get_rule_expr(StringInfo buf, QryHier *qh, int rt_index,
case 'l':
appendStringInfo(buf, "%s ",
get_opname(opno));
get_rule_expr(buf, qh, rt_index,
(Node *) lfirst(args),
varprefix);
get_rule_expr((Node *) lfirst(args),
context);
break;
case 'r':
get_rule_expr(buf, qh, rt_index,
(Node *) lfirst(args),
varprefix);
get_rule_expr((Node *) lfirst(args),
context);
appendStringInfo(buf, " %s",
get_opname(opno));
break;
@ -1257,49 +1291,34 @@ get_rule_expr(StringInfo buf, QryHier *qh, int rt_index,
case OR_EXPR:
appendStringInfo(buf, "(");
get_rule_expr(buf, qh, rt_index,
(Node *) lfirst(args),
varprefix);
/* It's not clear that we can ever see N-argument
* OR/AND clauses here, but might as well cope...
*/
get_rule_expr((Node *) lfirst(args), context);
while ((args = lnext(args)) != NIL)
{
appendStringInfo(buf, " OR ");
get_rule_expr(buf, qh, rt_index,
(Node *) lfirst(args),
varprefix);
get_rule_expr((Node *) lfirst(args), context);
}
appendStringInfo(buf, ")");
break;
case AND_EXPR:
appendStringInfo(buf, "(");
get_rule_expr(buf, qh, rt_index,
(Node *) lfirst(args),
varprefix);
get_rule_expr((Node *) lfirst(args), context);
while ((args = lnext(args)) != NIL)
{
appendStringInfo(buf, " AND ");
get_rule_expr(buf, qh, rt_index,
(Node *) lfirst(args),
varprefix);
get_rule_expr((Node *) lfirst(args), context);
}
appendStringInfo(buf, ")");
break;
case NOT_EXPR:
appendStringInfo(buf, "(NOT ");
get_rule_expr(buf, qh, rt_index,
(Node *) lfirst(args),
varprefix);
get_rule_expr((Node *) lfirst(args), context);
appendStringInfo(buf, ")");
break;
case FUNC_EXPR:
get_func_expr(buf, qh, rt_index,
(Expr *) node,
varprefix);
get_func_expr((Expr *) node, context);
break;
default:
@ -1314,15 +1333,13 @@ get_rule_expr(StringInfo buf, QryHier *qh, int rt_index,
Aggref *aggref = (Aggref *) node;
appendStringInfo(buf, "\"%s\"(", aggref->aggname);
get_rule_expr(buf, qh, rt_index,
(Node *) (aggref->target), varprefix);
get_rule_expr(aggref->target, context);
appendStringInfo(buf, ")");
}
break;
case T_Iter:
get_rule_expr(buf, qh, rt_index,
((Iter *) node)->iterexpr, varprefix);
get_rule_expr(((Iter *) node)->iterexpr, context);
break;
case T_ArrayRef:
@ -1331,23 +1348,18 @@ get_rule_expr(StringInfo buf, QryHier *qh, int rt_index,
List *lowlist;
List *uplist;
get_rule_expr(buf, qh, rt_index,
aref->refexpr, varprefix);
get_rule_expr(aref->refexpr, context);
lowlist = aref->reflowerindexpr;
foreach(uplist, aref->refupperindexpr)
{
appendStringInfo(buf, "[");
if (lowlist)
{
get_rule_expr(buf, qh, rt_index,
(Node *) lfirst(lowlist),
varprefix);
get_rule_expr((Node *) lfirst(lowlist), context);
appendStringInfo(buf, ":");
lowlist = lnext(lowlist);
}
get_rule_expr(buf, qh, rt_index,
(Node *) lfirst(uplist),
varprefix);
get_rule_expr((Node *) lfirst(uplist), context);
appendStringInfo(buf, "]");
}
/* XXX need to do anything with refassgnexpr? */
@ -1365,21 +1377,18 @@ get_rule_expr(StringInfo buf, QryHier *qh, int rt_index,
CaseWhen *when = (CaseWhen *) lfirst(temp);
appendStringInfo(buf, " WHEN ");
get_rule_expr(buf, qh, rt_index,
when->expr, varprefix);
get_rule_expr(when->expr, context);
appendStringInfo(buf, " THEN ");
get_rule_expr(buf, qh, rt_index,
when->result, varprefix);
get_rule_expr(when->result, context);
}
appendStringInfo(buf, " ELSE ");
get_rule_expr(buf, qh, rt_index,
caseexpr->defresult, varprefix);
get_rule_expr(caseexpr->defresult, context);
appendStringInfo(buf, " END");
}
break;
case T_SubLink:
get_sublink_expr(buf, qh, rt_index, node, varprefix);
get_sublink_expr(node, context);
break;
default:
@ -1396,9 +1405,9 @@ get_rule_expr(StringInfo buf, QryHier *qh, int rt_index,
* ----------
*/
static void
get_func_expr(StringInfo buf, QryHier *qh, int rt_index,
Expr *expr, bool varprefix)
get_func_expr(Expr *expr, deparse_context *context)
{
StringInfo buf = context->buf;
HeapTuple proctup;
Form_pg_proc procStruct;
List *l;
@ -1411,7 +1420,8 @@ get_func_expr(StringInfo buf, QryHier *qh, int rt_index,
* ----------
*/
proctup = SearchSysCacheTuple(PROOID,
ObjectIdGetDatum(func->funcid), 0, 0, 0);
ObjectIdGetDatum(func->funcid),
0, 0, 0);
if (!HeapTupleIsValid(proctup))
elog(ERROR, "cache lookup for proc %u failed", func->funcid);
@ -1426,14 +1436,14 @@ get_func_expr(StringInfo buf, QryHier *qh, int rt_index,
if (!strcmp(proname, "nullvalue"))
{
appendStringInfo(buf, "(");
get_rule_expr(buf, qh, rt_index, lfirst(expr->args), varprefix);
get_rule_expr((Node *) lfirst(expr->args), context);
appendStringInfo(buf, " ISNULL)");
return;
}
if (!strcmp(proname, "nonnullvalue"))
{
appendStringInfo(buf, "(");
get_rule_expr(buf, qh, rt_index, lfirst(expr->args), varprefix);
get_rule_expr((Node *) lfirst(expr->args), context);
appendStringInfo(buf, " NOTNULL)");
return;
}
@ -1449,7 +1459,7 @@ get_func_expr(StringInfo buf, QryHier *qh, int rt_index,
{
appendStringInfo(buf, sep);
sep = ", ";
get_rule_expr(buf, qh, rt_index, lfirst(l), varprefix);
get_rule_expr((Node *) lfirst(l), context);
}
appendStringInfo(buf, ")");
}
@ -1466,8 +1476,7 @@ get_func_expr(StringInfo buf, QryHier *qh, int rt_index,
* ----------
*/
static void
get_tle_expr(StringInfo buf, QryHier *qh, int rt_index,
TargetEntry *tle, bool varprefix)
get_tle_expr(TargetEntry *tle, deparse_context *context)
{
Expr *expr = (Expr *) (tle->expr);
Func *func;
@ -1485,7 +1494,7 @@ get_tle_expr(StringInfo buf, QryHier *qh, int rt_index,
! IsA(expr, Expr) ||
expr->opType != FUNC_EXPR)
{
get_rule_expr(buf, qh, rt_index, tle->expr, varprefix);
get_rule_expr(tle->expr, context);
return;
}
@ -1511,7 +1520,7 @@ get_tle_expr(StringInfo buf, QryHier *qh, int rt_index,
procStruct->prorettype != procStruct->proargtypes[0] ||
procStruct->proargtypes[1] != INT4OID)
{
get_rule_expr(buf, qh, rt_index, tle->expr, varprefix);
get_rule_expr(tle->expr, context);
return;
}
@ -1529,7 +1538,7 @@ get_tle_expr(StringInfo buf, QryHier *qh, int rt_index,
if (strncmp(procStruct->proname.data, typeStruct->typname.data,
NAMEDATALEN) != 0)
{
get_rule_expr(buf, qh, rt_index, tle->expr, varprefix);
get_rule_expr(tle->expr, context);
return;
}
@ -1542,7 +1551,7 @@ get_tle_expr(StringInfo buf, QryHier *qh, int rt_index,
if (! IsA(second_arg, Const) ||
DatumGetInt32(second_arg->constvalue) != tle->resdom->restypmod)
{
get_rule_expr(buf, qh, rt_index, tle->expr, varprefix);
get_rule_expr(tle->expr, context);
return;
}
@ -1550,7 +1559,7 @@ get_tle_expr(StringInfo buf, QryHier *qh, int rt_index,
* Whow - got it. Now get rid of the padding function
* ----------
*/
get_rule_expr(buf, qh, rt_index, lfirst(expr->args), varprefix);
get_rule_expr((Node *) lfirst(expr->args), context);
}
@ -1561,8 +1570,9 @@ get_tle_expr(StringInfo buf, QryHier *qh, int rt_index,
* ----------
*/
static void
get_const_expr(StringInfo buf, Const *constval)
get_const_expr(Const *constval, deparse_context *context)
{
StringInfo buf = context->buf;
HeapTuple typetup;
Form_pg_type typeStruct;
FmgrInfo finfo_output;
@ -1624,9 +1634,9 @@ get_const_expr(StringInfo buf, Const *constval)
* ----------
*/
static void
get_sublink_expr(StringInfo buf, QryHier *qh, int rt_index,
Node *node, bool varprefix)
get_sublink_expr(Node *node, deparse_context *context)
{
StringInfo buf = context->buf;
SubLink *sublink = (SubLink *) node;
Query *query = (Query *) (sublink->subselect);
Oper *oper;
@ -1645,7 +1655,7 @@ get_sublink_expr(StringInfo buf, QryHier *qh, int rt_index,
{
appendStringInfo(buf, sep);
sep = ", ";
get_rule_expr(buf, qh, rt_index, lfirst(l), varprefix);
get_rule_expr((Node *) lfirst(l), context);
}
if (length(sublink->lefthand) > 1)
@ -1682,7 +1692,7 @@ get_sublink_expr(StringInfo buf, QryHier *qh, int rt_index,
}
appendStringInfo(buf, "(");
get_query_def(buf, query, qh);
get_query_def(query, buf, context->rangetables);
appendStringInfo(buf, "))");
}

View File

@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/cache/relcache.c,v 1.73 1999/09/18 19:07:55 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/cache/relcache.c,v 1.74 1999/10/03 23:55:33 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -19,7 +19,7 @@
* RelationIdGetRelation - get a reldesc by relation id
* RelationNameGetRelation - get a reldesc by relation name
* RelationClose - close an open relation
* RelationFlushRelation - flush relation information
* RelationRebuildRelation - rebuild relation information
*
* NOTES
* This file is in the process of being cleaned up
@ -59,8 +59,9 @@
#include "utils/temprel.h"
static void RelationClearRelation(Relation relation, bool rebuildIt);
static void RelationFlushRelation(Relation *relationPtr,
bool onlyFlushReferenceCountZero);
bool onlyFlushReferenceCountZero);
static Relation RelationNameCacheGetRelation(char *relationName);
static void RelationCacheAbortWalker(Relation *relationPtr,
int dummy);
@ -247,34 +248,6 @@ static List *newlyCreatedRelns = NULL;
*/
#if NOT_USED /* XXX This doesn't seem to be used
* anywhere */
/* --------------------------------
* BuildDescInfoError returns a string appropriate to
* the buildinfo passed to it
* --------------------------------
*/
static char *
BuildDescInfoError(RelationBuildDescInfo buildinfo)
{
static char errBuf[64];
MemSet(errBuf, 0, (int) sizeof(errBuf));
switch (buildinfo.infotype)
{
case INFO_RELID:
sprintf(errBuf, "(relation id %u)", buildinfo.i.info_id);
break;
case INFO_RELNAME:
sprintf(errBuf, "(relation name %s)", buildinfo.i.info_name);
break;
}
return errBuf;
}
#endif
/* --------------------------------
* ScanPgRelation
*
@ -403,7 +376,7 @@ scan_pg_rel_ind(RelationBuildDescInfo buildinfo)
*
* If 'relation' is NULL, allocate a new RelationData object.
* If not, reuse the given object (that path is taken only when
* we have to rebuild a relcache entry during RelationFlushRelation).
* we have to rebuild a relcache entry during RelationClearRelation).
* ----------------
*/
static Relation
@ -578,11 +551,14 @@ build_tupdesc_ind(RelationBuildDescInfo buildinfo,
if (attp->atthasdef)
{
if (attrdef == NULL)
{
attrdef = (AttrDefault *) palloc(relation->rd_rel->relnatts *
sizeof(AttrDefault));
MemSet(attrdef, 0,
relation->rd_rel->relnatts * sizeof(AttrDefault));
}
attrdef[ndef].adnum = i;
attrdef[ndef].adbin = NULL;
attrdef[ndef].adsrc = NULL;
ndef++;
}
}
@ -1231,7 +1207,9 @@ RelationNameGetRelation(char *relationName)
*/
/* --------------------------------
* RelationClose - close an open relation
* RelationClose - close an open relation
*
* Actually, we just decrement the refcount.
* --------------------------------
*/
void
@ -1242,17 +1220,18 @@ RelationClose(Relation relation)
}
/* --------------------------------
* RelationFlushRelation
* RelationClearRelation
*
* Actually blows away a relation cache entry... RelationFree doesn't do
* anything anymore.
* Physically blow away a relation cache entry, or reset it and rebuild
* it from scratch (that is, from catalog entries). The latter path is
* usually used when we are notified of a change to an open relation
* (one with refcount > 0). However, this routine just does whichever
* it's told to do; callers must determine which they want.
* --------------------------------
*/
static void
RelationFlushRelation(Relation *relationPtr,
bool onlyFlushReferenceCountZero)
RelationClearRelation(Relation relation, bool rebuildIt)
{
Relation relation = *relationPtr;
MemoryContext oldcxt;
/*
@ -1261,19 +1240,18 @@ RelationFlushRelation(Relation *relationPtr,
* if the relation is not deleted, the next smgr access should
* reopen the files automatically. This ensures that the low-level
* file access state is updated after, say, a vacuum truncation.
*
* NOTE: this call is a no-op if the relation's smgr file is already
* closed or unlinked.
*/
smgrclose(DEFAULT_SMGR, relation);
if (relation->rd_isnailed || relation->rd_myxactonly)
{
/* Do not flush relation cache entry if it is a nailed-in system
* relation or it is marked transaction-local.
* (To delete a local relation, caller must clear rd_myxactonly!)
*/
/*
* Never, never ever blow away a nailed-in system relation,
* because we'd be unable to recover.
*/
if (relation->rd_isnailed)
return;
}
oldcxt = MemoryContextSwitchTo((MemoryContext) CacheCxt);
@ -1293,19 +1271,21 @@ RelationFlushRelation(Relation *relationPtr,
FreeTriggerDesc(relation);
pfree(RelationGetForm(relation));
/* If we're really done with the relcache entry, blow it away.
/*
* If we're really done with the relcache entry, blow it away.
* But if someone is still using it, reconstruct the whole deal
* without moving the physical RelationData record (so that the
* someone's pointer is still valid). Preserve ref count, too.
* someone's pointer is still valid). Must preserve ref count
* and myxactonly flag, too.
*/
if (!onlyFlushReferenceCountZero ||
RelationHasReferenceCountZero(relation))
if (! rebuildIt)
{
pfree(relation);
}
else
{
uint16 old_refcnt = relation->rd_refcnt;
bool old_myxactonly = relation->rd_myxactonly;
RelationBuildDescInfo buildinfo;
buildinfo.infotype = INFO_RELID;
@ -1315,19 +1295,53 @@ RelationFlushRelation(Relation *relationPtr,
{
/* Should only get here if relation was deleted */
pfree(relation);
elog(ERROR, "RelationFlushRelation: relation %u deleted while still in use",
elog(ERROR, "RelationClearRelation: relation %u deleted while still in use",
buildinfo.i.info_id);
}
RelationSetReferenceCount(relation, old_refcnt);
relation->rd_myxactonly = old_myxactonly;
}
MemoryContextSwitchTo(oldcxt);
}
/* --------------------------------
* RelationForgetRelation -
* RelationFlushRelation + if the relation is myxactonly then
* get rid of the relation descriptor from the newly created
* RelationFlushRelation
*
* Rebuild the relation if it is open (refcount > 0), else blow it away.
* Setting onlyFlushReferenceCountZero to FALSE overrides refcount check.
* This is currently only used to process SI invalidation notifications.
* The peculiar calling convention (pointer to pointer to relation)
* is needed so that we can use this routine as a hash table walker.
* --------------------------------
*/
static void
RelationFlushRelation(Relation *relationPtr,
bool onlyFlushReferenceCountZero)
{
Relation relation = *relationPtr;
/*
* Do nothing to transaction-local relations, since they cannot be
* subjects of SI notifications from other backends.
*/
if (relation->rd_myxactonly)
return;
/*
* Zap it. Rebuild if it has nonzero ref count and we did not get
* the override flag.
*/
RelationClearRelation(relation,
(onlyFlushReferenceCountZero &&
! RelationHasReferenceCountZero(relation)));
}
/* --------------------------------
* RelationForgetRelation -
*
* RelationClearRelation + if the relation is myxactonly then
* remove the relation descriptor from the newly created
* relation list.
* --------------------------------
*/
@ -1368,12 +1382,24 @@ RelationForgetRelation(Oid rid)
MemoryContextSwitchTo(oldcxt);
}
relation->rd_myxactonly = false; /* so it can be flushed */
RelationFlushRelation(&relation, false);
/* Unconditionally destroy the relcache entry */
RelationClearRelation(relation, false);
}
}
/* --------------------------------
* RelationRebuildRelation -
*
* Force a relcache entry to be rebuilt from catalog entries.
* This is needed, eg, after modifying an attribute of the rel.
* --------------------------------
*/
void
RelationRebuildRelation(Relation relation)
{
RelationClearRelation(relation, true);
}
/* --------------------------------
* RelationIdInvalidateRelationCacheByRelationId
* --------------------------------
@ -1573,6 +1599,11 @@ RelationPurgeLocalRelation(bool xactCommitted)
Assert(reln != NULL && reln->rd_myxactonly);
reln->rd_myxactonly = false; /* mark it not on list anymore */
newlyCreatedRelns = lnext(newlyCreatedRelns);
pfree(l);
if (!xactCommitted)
{
@ -1592,13 +1623,8 @@ RelationPurgeLocalRelation(bool xactCommitted)
smgrunlink(DEFAULT_SMGR, reln);
}
reln->rd_myxactonly = false; /* so it can be flushed */
if (!IsBootstrapProcessingMode())
RelationFlushRelation(&reln, false);
newlyCreatedRelns = lnext(newlyCreatedRelns);
pfree(l);
RelationClearRelation(reln, false);
}
MemoryContextSwitchTo(oldcxt);
@ -1717,7 +1743,7 @@ AttrDefaultFetch(Relation relation)
{
if (adform->adnum != attrdef[i].adnum)
continue;
if (attrdef[i].adsrc != NULL)
if (attrdef[i].adbin != NULL)
elog(ERROR, "AttrDefaultFetch: second record found for attr %s in rel %s",
relation->rd_att->attrs[adform->adnum - 1]->attname.data,
relation->rd_rel->relname.data);
@ -1730,14 +1756,6 @@ AttrDefaultFetch(Relation relation)
relation->rd_att->attrs[adform->adnum - 1]->attname.data,
relation->rd_rel->relname.data);
attrdef[i].adbin = textout(val);
val = (struct varlena *) fastgetattr(&tuple,
Anum_pg_attrdef_adsrc,
adrel->rd_att, &isnull);
if (isnull)
elog(ERROR, "AttrDefaultFetch: adsrc IS NULL for attr %s in rel %s",
relation->rd_att->attrs[adform->adnum - 1]->attname.data,
relation->rd_rel->relname.data);
attrdef[i].adsrc = textout(val);
break;
}
ReleaseBuffer(buffer);
@ -1816,13 +1834,6 @@ RelCheckFetch(Relation relation)
elog(ERROR, "RelCheckFetch: rcbin IS NULL for rel %s",
relation->rd_rel->relname.data);
check[found].ccbin = textout(val);
val = (struct varlena *) fastgetattr(&tuple,
Anum_pg_relcheck_rcsrc,
rcrel->rd_att, &isnull);
if (isnull)
elog(ERROR, "RelCheckFetch: rcsrc IS NULL for rel %s",
relation->rd_rel->relname.data);
check[found].ccsrc = textout(val);
found++;
ReleaseBuffer(buffer);
}

View File

@ -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;

View File

@ -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);

View File

@ -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;
/*

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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);