aboutsummaryrefslogtreecommitdiff
path: root/src/backend/commands/command.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/commands/command.c')
-rw-r--r--src/backend/commands/command.c192
1 files changed, 188 insertions, 4 deletions
diff --git a/src/backend/commands/command.c b/src/backend/commands/command.c
index be24c696a7c..bcd0b4b2fb7 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 */
}
+