diff --git a/src/backend/commands/command.c b/src/backend/commands/command.c index be24c696a7cb2328c7764dd0853fdf692f2242e2..bcd0b4b2fb76952ed8aa4737750972f2436ede2c 100644 --- a/src/backend/commands/command.c +++ b/src/backend/commands/command.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.89 2000/07/14 22:17:42 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.90 2000/07/15 12:37:14 momjian Exp $ * * NOTES * The PerformAddAttribute() code, like most of the relation @@ -33,6 +33,18 @@ #include "utils/acl.h" #include "utils/fmgroids.h" #include "commands/trigger.h" + +#include "parser/parse_expr.h" +#include "parser/parse_clause.h" +#include "parser/parse_relation.h" +#include "nodes/makefuncs.h" +#include "optimizer/planmain.h" +#include "optimizer/clauses.h" +#include "rewrite/rewriteSupport.h" +#include "commands/view.h" +#include "utils/temprel.h" +#include "executor/spi_priv.h" + #ifdef _DROP_COLUMN_HACK__ #include "catalog/pg_index.h" #include "parser/parse.h" @@ -1067,13 +1079,158 @@ void AlterTableAddConstraint(const char *relationName, bool inh, Node *newConstraint) { + char rulequery[41+NAMEDATALEN]; + void *qplan; + char nulls[1]=""; + if (newConstraint == NULL) elog(ERROR, "ALTER TABLE / ADD CONSTRAINT passed invalid constraint."); +#ifndef NO_SECURITY + if (!pg_ownercheck(UserName, relationName, RELNAME)) + elog(ERROR, "ALTER TABLE: permission denied"); +#endif + + /* check to see if the table to be constrained is a view. */ + sprintf(rulequery, "select * from pg_views where viewname='%s'", relationName); + if (SPI_connect()!=SPI_OK_CONNECT) + elog(ERROR, "ALTER TABLE: Unable to determine if %s is a view - SPI_connect failure..", relationName); + qplan=SPI_prepare(rulequery, 0, NULL); + if (!qplan) + elog(ERROR, "ALTER TABLE: Unable to determine if %s is a view - SPI_prepare failure.", relationName); + qplan=SPI_saveplan(qplan); + if (SPI_execp(qplan, NULL, nulls, 1)!=SPI_OK_SELECT) + elog(ERROR, "ALTER TABLE: Unable to determine if %s is a view - SPI_execp failure.", relationName); + if (SPI_processed != 0) + elog(ERROR, "ALTER TABLE: Cannot add constraints to views."); + if (SPI_finish() != SPI_OK_FINISH) + elog(NOTICE, "SPI_finish() failed in ALTER TABLE"); + switch (nodeTag(newConstraint)) { case T_Constraint: - elog(ERROR, "ALTER TABLE / ADD CONSTRAINT is not implemented"); + { + Constraint *constr=(Constraint *)newConstraint; + switch (constr->contype) { + case CONSTR_CHECK: + { + ParseState *pstate; + bool successful=TRUE; + HeapScanDesc scan; + ExprContext *econtext; + TupleTableSlot *slot = makeNode(TupleTableSlot); + HeapTuple tuple; + RangeTblEntry *rte = makeNode(RangeTblEntry); + List *rtlist; + List *qual; + List *constlist; + Relation rel; + Node *expr; + char *name; + if (constr->name) + name=constr->name; + else + name="<unnamed>"; + + rel = heap_openr(relationName, AccessExclusiveLock); + + /* + * Scan all of the rows, looking for a false match + */ + scan = heap_beginscan(rel, false, SnapshotNow, 0, NULL); + AssertState(scan != NULL); + + /* + *We need to make a parse state and range table to allow us + * to transformExpr and fix_opids to get a version of the + * expression we can pass to ExecQual + */ + pstate = make_parsestate(NULL); + makeRangeTable(pstate, NULL); + addRangeTableEntry(pstate, relationName, + makeAttr(relationName, NULL), false, true,true); + constlist=lcons(constr, NIL); + + /* Convert the A_EXPR in raw_expr into an EXPR */ + expr = transformExpr(pstate, constr->raw_expr, EXPR_COLUMN_FIRST); + + /* + * Make sure it yields a boolean result. + */ + if (exprType(expr) != BOOLOID) + elog(ERROR, "CHECK '%s' does not yield boolean result", + name); + + /* + * Make sure no outside relations are referred to. + */ + if (length(pstate->p_rtable) != 1) + elog(ERROR, "Only relation '%s' can be referenced in CHECK", + relationName); + + /* + * Might as well try to reduce any constant expressions. + */ + expr = eval_const_expressions(expr); + + /* And fix the opids */ + fix_opids(expr); + + qual = lcons(expr, NIL); + rte->relname = relationName; + rte->ref = makeNode(Attr); + rte->ref->relname = rte->relname; + rte->relid = RelationGetRelid(rel); + rtlist = lcons(rte, NIL); + + /* + * Scan through the rows now, making the necessary things for + * ExecQual, and then call it to evaluate the expression. + */ + while (HeapTupleIsValid(tuple = heap_getnext(scan, 0))) + { + slot->val = tuple; + slot->ttc_shouldFree = false; + slot->ttc_descIsNew = true; + slot->ttc_tupleDescriptor = rel->rd_att; + slot->ttc_buffer = InvalidBuffer; + slot->ttc_whichplan = -1; + + econtext = MakeExprContext(slot, CurrentMemoryContext); + econtext->ecxt_range_table = rtlist; /* range table */ + if (!ExecQual(qual, econtext, true)) { + successful=false; + break; + } + FreeExprContext(econtext); + } + + pfree(slot); + pfree(rtlist); + pfree(rte); + + heap_endscan(scan); + heap_close(rel, NoLock); + + if (!successful) + { + elog(ERROR, "AlterTableAddConstraint: rejected due to CHECK constraint %s", name); + } + /* + * Call AddRelationRawConstraints to do the real adding -- It duplicates some + * of the above, but does not check the validity of the constraint against + * tuples already in the table. + */ + AddRelationRawConstraints(rel, NIL, constlist); + pfree(constlist); + + break; + } + default: + elog(ERROR, "ALTER TABLE / ADD CONSTRAINT is not implemented for that constraint type."); + } + } + break; case T_FkConstraint: { FkConstraint *fkconstraint = (FkConstraint *) newConstraint; @@ -1084,6 +1241,26 @@ AlterTableAddConstraint(const char *relationName, List *list; int count; + if (get_temp_rel_by_username(fkconstraint->pktable_name)!=NULL && + get_temp_rel_by_username(relationName)==NULL) { + elog(ERROR, "ALTER TABLE / ADD CONSTRAINT: Unable to reference temporary table from permanent table constraint."); + } + + /* check to see if the referenced table is a view. */ + sprintf(rulequery, "select * from pg_views where viewname='%s'", fkconstraint->pktable_name); + if (SPI_connect()!=SPI_OK_CONNECT) + elog(ERROR, "ALTER TABLE: Unable to determine if %s is a view.", relationName); + qplan=SPI_prepare(rulequery, 0, NULL); + if (!qplan) + elog(ERROR, "ALTER TABLE: Unable to determine if %s is a view.", relationName); + qplan=SPI_saveplan(qplan); + if (SPI_execp(qplan, NULL, nulls, 1)!=SPI_OK_SELECT) + elog(ERROR, "ALTER TABLE: Unable to determine if %s is a view.", relationName); + if (SPI_processed != 0) + elog(ERROR, "ALTER TABLE: Cannot add constraints to views."); + if (SPI_finish() != SPI_OK_FINISH) + elog(NOTICE, "SPI_finish() failed in RI_FKey_check()"); + /* * Grab an exclusive lock on the pk table, so that someone * doesn't delete rows out from under us. @@ -1101,7 +1278,10 @@ AlterTableAddConstraint(const char *relationName, */ rel = heap_openr(relationName, AccessExclusiveLock); trig.tgoid = 0; - trig.tgname = "<unknown>"; + if (fkconstraint->constr_name) + trig.tgname = fkconstraint->constr_name; + else + trig.tgname = "<unknown>"; trig.tgfoid = 0; trig.tgtype = 0; trig.tgenabled = TRUE; @@ -1113,7 +1293,10 @@ AlterTableAddConstraint(const char *relationName, sizeof(char *) * (4 + length(fkconstraint->fk_attrs) + length(fkconstraint->pk_attrs))); - trig.tgargs[0] = "<unnamed>"; + if (fkconstraint->constr_name) + trig.tgargs[0] = fkconstraint->constr_name; + else + trig.tgargs[0] = "<unknown>"; trig.tgargs[1] = (char *) relationName; trig.tgargs[2] = fkconstraint->pktable_name; trig.tgargs[3] = fkconstraint->match_type; @@ -1446,3 +1629,4 @@ LockTableCommand(LockStmt *lockstmt) heap_close(rel, NoLock); /* close rel, keep lock */ } +