*** pgsql.orig/src/backend/commands/command.c Wed Jul 5 16:11:09 2000 --- pgsql/src/backend/commands/command.c Thu Jul 13 02:21:05 2000 *************** *** 33,38 **** --- 33,50 ---- #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,1079 **** AlterTableAddConstraint(const char *relationName, bool inh, Node *newConstraint) { if (newConstraint == NULL) elog(ERROR, "ALTER TABLE / ADD CONSTRAINT passed invalid constraint."); switch (nodeTag(newConstraint)) { case T_Constraint: ! elog(ERROR, "ALTER TABLE / ADD CONSTRAINT is not implemented"); case T_FkConstraint: { FkConstraint *fkconstraint = (FkConstraint *) newConstraint; --- 1079,1236 ---- 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: ! { ! 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=""; ! ! 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,1089 **** --- 1241,1266 ---- 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,1107 **** */ rel = heap_openr(relationName, AccessExclusiveLock); trig.tgoid = 0; ! trig.tgname = ""; trig.tgfoid = 0; trig.tgtype = 0; trig.tgenabled = TRUE; --- 1278,1287 ---- */ rel = heap_openr(relationName, AccessExclusiveLock); trig.tgoid = 0; ! if (fkconstraint->constr_name) ! trig.tgname = fkconstraint->constr_name; ! else ! trig.tgname = ""; trig.tgfoid = 0; trig.tgtype = 0; trig.tgenabled = TRUE; *************** *** 1113,1119 **** sizeof(char *) * (4 + length(fkconstraint->fk_attrs) + length(fkconstraint->pk_attrs))); ! trig.tgargs[0] = ""; trig.tgargs[1] = (char *) relationName; trig.tgargs[2] = fkconstraint->pktable_name; trig.tgargs[3] = fkconstraint->match_type; --- 1293,1302 ---- sizeof(char *) * (4 + length(fkconstraint->fk_attrs) + length(fkconstraint->pk_attrs))); ! if (fkconstraint->constr_name) ! trig.tgargs[0] = fkconstraint->constr_name; ! else ! trig.tgargs[0] = ""; trig.tgargs[1] = (char *) relationName; trig.tgargs[2] = fkconstraint->pktable_name; trig.tgargs[3] = fkconstraint->match_type; *************** *** 1437,1439 **** --- 1620,1623 ---- heap_close(rel, NoLock); /* close rel, keep lock */ } +