88 *
99 *
1010 * IDENTIFICATION
11- * $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.89 2000/07/14 22:17:42 tgl Exp $
11+ * $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.90 2000/07/15 12:37:14 momjian Exp $
1212 *
1313 * NOTES
1414 * The PerformAddAttribute() code, like most of the relation
3333#include "utils/acl.h"
3434#include "utils/fmgroids.h"
3535#include "commands/trigger.h"
36+
37+ #include "parser/parse_expr.h"
38+ #include "parser/parse_clause.h"
39+ #include "parser/parse_relation.h"
40+ #include "nodes/makefuncs.h"
41+ #include "optimizer/planmain.h"
42+ #include "optimizer/clauses.h"
43+ #include "rewrite/rewriteSupport.h"
44+ #include "commands/view.h"
45+ #include "utils/temprel.h"
46+ #include "executor/spi_priv.h"
47+
3648#ifdef _DROP_COLUMN_HACK__
3749#include "catalog/pg_index.h"
3850#include "parser/parse.h"
@@ -1067,13 +1079,158 @@ void
10671079AlterTableAddConstraint (const char * relationName ,
10681080 bool inh , Node * newConstraint )
10691081{
1082+ char rulequery [41 + NAMEDATALEN ];
1083+ void * qplan ;
1084+ char nulls [1 ]= "" ;
1085+
10701086 if (newConstraint == NULL )
10711087 elog (ERROR , "ALTER TABLE / ADD CONSTRAINT passed invalid constraint." );
10721088
1089+ #ifndef NO_SECURITY
1090+ if (!pg_ownercheck (UserName , relationName , RELNAME ))
1091+ elog (ERROR , "ALTER TABLE: permission denied" );
1092+ #endif
1093+
1094+ /* check to see if the table to be constrained is a view. */
1095+ sprintf (rulequery , "select * from pg_views where viewname='%s'" , relationName );
1096+ if (SPI_connect ()!= SPI_OK_CONNECT )
1097+ elog (ERROR , "ALTER TABLE: Unable to determine if %s is a view - SPI_connect failure.." , relationName );
1098+ qplan = SPI_prepare (rulequery , 0 , NULL );
1099+ if (!qplan )
1100+ elog (ERROR , "ALTER TABLE: Unable to determine if %s is a view - SPI_prepare failure." , relationName );
1101+ qplan = SPI_saveplan (qplan );
1102+ if (SPI_execp (qplan , NULL , nulls , 1 )!= SPI_OK_SELECT )
1103+ elog (ERROR , "ALTER TABLE: Unable to determine if %s is a view - SPI_execp failure." , relationName );
1104+ if (SPI_processed != 0 )
1105+ elog (ERROR , "ALTER TABLE: Cannot add constraints to views." );
1106+ if (SPI_finish () != SPI_OK_FINISH )
1107+ elog (NOTICE , "SPI_finish() failed in ALTER TABLE" );
1108+
10731109 switch (nodeTag (newConstraint ))
10741110 {
10751111 case T_Constraint :
1076- elog (ERROR , "ALTER TABLE / ADD CONSTRAINT is not implemented" );
1112+ {
1113+ Constraint * constr = (Constraint * )newConstraint ;
1114+ switch (constr -> contype ) {
1115+ case CONSTR_CHECK :
1116+ {
1117+ ParseState * pstate ;
1118+ bool successful = TRUE;
1119+ HeapScanDesc scan ;
1120+ ExprContext * econtext ;
1121+ TupleTableSlot * slot = makeNode (TupleTableSlot );
1122+ HeapTuple tuple ;
1123+ RangeTblEntry * rte = makeNode (RangeTblEntry );
1124+ List * rtlist ;
1125+ List * qual ;
1126+ List * constlist ;
1127+ Relation rel ;
1128+ Node * expr ;
1129+ char * name ;
1130+ if (constr -> name )
1131+ name = constr -> name ;
1132+ else
1133+ name = "<unnamed>" ;
1134+
1135+ rel = heap_openr (relationName , AccessExclusiveLock );
1136+
1137+ /*
1138+ * Scan all of the rows, looking for a false match
1139+ */
1140+ scan = heap_beginscan (rel , false, SnapshotNow , 0 , NULL );
1141+ AssertState (scan != NULL );
1142+
1143+ /*
1144+ *We need to make a parse state and range table to allow us
1145+ * to transformExpr and fix_opids to get a version of the
1146+ * expression we can pass to ExecQual
1147+ */
1148+ pstate = make_parsestate (NULL );
1149+ makeRangeTable (pstate , NULL );
1150+ addRangeTableEntry (pstate , relationName ,
1151+ makeAttr (relationName , NULL ), false, true,true);
1152+ constlist = lcons (constr , NIL );
1153+
1154+ /* Convert the A_EXPR in raw_expr into an EXPR */
1155+ expr = transformExpr (pstate , constr -> raw_expr , EXPR_COLUMN_FIRST );
1156+
1157+ /*
1158+ * Make sure it yields a boolean result.
1159+ */
1160+ if (exprType (expr ) != BOOLOID )
1161+ elog (ERROR , "CHECK '%s' does not yield boolean result" ,
1162+ name );
1163+
1164+ /*
1165+ * Make sure no outside relations are referred to.
1166+ */
1167+ if (length (pstate -> p_rtable ) != 1 )
1168+ elog (ERROR , "Only relation '%s' can be referenced in CHECK" ,
1169+ relationName );
1170+
1171+ /*
1172+ * Might as well try to reduce any constant expressions.
1173+ */
1174+ expr = eval_const_expressions (expr );
1175+
1176+ /* And fix the opids */
1177+ fix_opids (expr );
1178+
1179+ qual = lcons (expr , NIL );
1180+ rte -> relname = relationName ;
1181+ rte -> ref = makeNode (Attr );
1182+ rte -> ref -> relname = rte -> relname ;
1183+ rte -> relid = RelationGetRelid (rel );
1184+ rtlist = lcons (rte , NIL );
1185+
1186+ /*
1187+ * Scan through the rows now, making the necessary things for
1188+ * ExecQual, and then call it to evaluate the expression.
1189+ */
1190+ while (HeapTupleIsValid (tuple = heap_getnext (scan , 0 )))
1191+ {
1192+ slot -> val = tuple ;
1193+ slot -> ttc_shouldFree = false;
1194+ slot -> ttc_descIsNew = true;
1195+ slot -> ttc_tupleDescriptor = rel -> rd_att ;
1196+ slot -> ttc_buffer = InvalidBuffer ;
1197+ slot -> ttc_whichplan = -1 ;
1198+
1199+ econtext = MakeExprContext (slot , CurrentMemoryContext );
1200+ econtext -> ecxt_range_table = rtlist ; /* range table */
1201+ if (!ExecQual (qual , econtext , true)) {
1202+ successful = false;
1203+ break ;
1204+ }
1205+ FreeExprContext (econtext );
1206+ }
1207+
1208+ pfree (slot );
1209+ pfree (rtlist );
1210+ pfree (rte );
1211+
1212+ heap_endscan (scan );
1213+ heap_close (rel , NoLock );
1214+
1215+ if (!successful )
1216+ {
1217+ elog (ERROR , "AlterTableAddConstraint: rejected due to CHECK constraint %s" , name );
1218+ }
1219+ /*
1220+ * Call AddRelationRawConstraints to do the real adding -- It duplicates some
1221+ * of the above, but does not check the validity of the constraint against
1222+ * tuples already in the table.
1223+ */
1224+ AddRelationRawConstraints (rel , NIL , constlist );
1225+ pfree (constlist );
1226+
1227+ break ;
1228+ }
1229+ default :
1230+ elog (ERROR , "ALTER TABLE / ADD CONSTRAINT is not implemented for that constraint type." );
1231+ }
1232+ }
1233+ break ;
10771234 case T_FkConstraint :
10781235 {
10791236 FkConstraint * fkconstraint = (FkConstraint * ) newConstraint ;
@@ -1084,6 +1241,26 @@ AlterTableAddConstraint(const char *relationName,
10841241 List * list ;
10851242 int count ;
10861243
1244+ if (get_temp_rel_by_username (fkconstraint -> pktable_name )!= NULL &&
1245+ get_temp_rel_by_username (relationName )== NULL ) {
1246+ elog (ERROR , "ALTER TABLE / ADD CONSTRAINT: Unable to reference temporary table from permanent table constraint." );
1247+ }
1248+
1249+ /* check to see if the referenced table is a view. */
1250+ sprintf (rulequery , "select * from pg_views where viewname='%s'" , fkconstraint -> pktable_name );
1251+ if (SPI_connect ()!= SPI_OK_CONNECT )
1252+ elog (ERROR , "ALTER TABLE: Unable to determine if %s is a view." , relationName );
1253+ qplan = SPI_prepare (rulequery , 0 , NULL );
1254+ if (!qplan )
1255+ elog (ERROR , "ALTER TABLE: Unable to determine if %s is a view." , relationName );
1256+ qplan = SPI_saveplan (qplan );
1257+ if (SPI_execp (qplan , NULL , nulls , 1 )!= SPI_OK_SELECT )
1258+ elog (ERROR , "ALTER TABLE: Unable to determine if %s is a view." , relationName );
1259+ if (SPI_processed != 0 )
1260+ elog (ERROR , "ALTER TABLE: Cannot add constraints to views." );
1261+ if (SPI_finish () != SPI_OK_FINISH )
1262+ elog (NOTICE , "SPI_finish() failed in RI_FKey_check()" );
1263+
10871264 /*
10881265 * Grab an exclusive lock on the pk table, so that someone
10891266 * doesn't delete rows out from under us.
@@ -1101,7 +1278,10 @@ AlterTableAddConstraint(const char *relationName,
11011278 */
11021279 rel = heap_openr (relationName , AccessExclusiveLock );
11031280 trig .tgoid = 0 ;
1104- trig .tgname = "<unknown>" ;
1281+ if (fkconstraint -> constr_name )
1282+ trig .tgname = fkconstraint -> constr_name ;
1283+ else
1284+ trig .tgname = "<unknown>" ;
11051285 trig .tgfoid = 0 ;
11061286 trig .tgtype = 0 ;
11071287 trig .tgenabled = TRUE;
@@ -1113,7 +1293,10 @@ AlterTableAddConstraint(const char *relationName,
11131293 sizeof (char * ) * (4 + length (fkconstraint -> fk_attrs )
11141294 + length (fkconstraint -> pk_attrs )));
11151295
1116- trig .tgargs [0 ] = "<unnamed>" ;
1296+ if (fkconstraint -> constr_name )
1297+ trig .tgargs [0 ] = fkconstraint -> constr_name ;
1298+ else
1299+ trig .tgargs [0 ] = "<unknown>" ;
11171300 trig .tgargs [1 ] = (char * ) relationName ;
11181301 trig .tgargs [2 ] = fkconstraint -> pktable_name ;
11191302 trig .tgargs [3 ] = fkconstraint -> match_type ;
@@ -1446,3 +1629,4 @@ LockTableCommand(LockStmt *lockstmt)
14461629
14471630 heap_close (rel , NoLock ); /* close rel, keep lock */
14481631}
1632+
0 commit comments