diff --git a/src/common/backend/catalog/pg_constraint.cpp b/src/common/backend/catalog/pg_constraint.cpp index 27af27d2a..9b362ba4c 100755 --- a/src/common/backend/catalog/pg_constraint.cpp +++ b/src/common/backend/catalog/pg_constraint.cpp @@ -164,6 +164,8 @@ Oid CreateConstraintEntry(const char* constraintName, Oid constraintNamespace, c values[Anum_pg_constraint_conopt - 1] = BoolGetDatum(inforConstraint->enableOpt); } + values[Anum_pg_constraint_conisenable - 1] = BoolGetDatum(true); + if (conkeyArray != NULL) values[Anum_pg_constraint_conkey - 1] = PointerGetDatum(conkeyArray); else @@ -528,7 +530,7 @@ void RemoveConstraintById(Oid conId) errmsg("cache lookup failed for relation %u", con->conrelid))); classForm = (Form_pg_class)GETSTRUCT(relTup); - if (classForm->relchecks == 0) /* should not happen */ + if (classForm->relchecks == 0 && con->conisenable) /* should not happen */ ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("relation \"%s\" has relchecks = 0", RelationGetRelationName(rel)))); diff --git a/src/common/backend/parser/gram.y b/src/common/backend/parser/gram.y index 28b2df012..615e5e97c 100755 --- a/src/common/backend/parser/gram.y +++ b/src/common/backend/parser/gram.y @@ -3267,6 +3267,22 @@ alter_table_cmd: n->def = (Node *)$1; $$ = (Node *) n; } + /* ALTER TABLE ENABLE CONSTRAINT */ + | ENABLE_P CONSTRAINT name + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_EnableConstraint; + n->name = $3; + $$ = (Node *)n; + } + /* ALTER TABLE DISABLE CONSTRAINT */ + | DISABLE_P CONSTRAINT name + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_DisableConstraint; + n->name = $3; + $$ = (Node *)n; + } /* PGXC_BEGIN */ /* ALTER TABLE DISTRIBUTE BY ... */ diff --git a/src/common/backend/utils/adt/ri_triggers.cpp b/src/common/backend/utils/adt/ri_triggers.cpp index cd798dfd8..d2e33dd04 100755 --- a/src/common/backend/utils/adt/ri_triggers.cpp +++ b/src/common/backend/utils/adt/ri_triggers.cpp @@ -117,6 +117,7 @@ typedef struct RI_ConstraintInfo { * PK) */ Oid ff_eq_oprs[RI_MAX_NUMKEYS]; /* equality operators (FK = * FK) */ + bool conisenable; /* constraint is enable or not */ } RI_ConstraintInfo; /* ---------- @@ -244,6 +245,11 @@ static Datum RI_FKey_check(PG_FUNCTION_ARGS) */ ri_FetchConstraintInfo(&riinfo, trigdata->tg_trigger, trigdata->tg_relation, false); + if (!riinfo.conisenable) + { + return PointerGetDatum(NULL); + } + if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event)) { new_row = trigdata->tg_newtuple; new_row_buf = trigdata->tg_newtuplebuf; @@ -2064,6 +2070,11 @@ bool RI_Initial_Check(Trigger* trigger, Relation fk_rel, Relation pk_rel) /* Fetch constraint info. */ ri_FetchConstraintInfo(&riinfo, trigger, fk_rel, false); + if (!riinfo.conisenable) + { + return true; + } + /* * Check to make sure current user has enough permissions to do the test * query. (If not, caller can fall back to the trigger method, which @@ -2483,15 +2494,33 @@ static void ri_FetchConstraintInfo(RI_ConstraintInfo* riinfo, Trigger* trigger, "Remove this referential integrity trigger and its mates, then do ALTER TABLE ADD CONSTRAINT."))); } /* OK, fetch the tuple */ - tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constraintOid)); - - // should not happen - if (!HeapTupleIsValid(tup)) { - ereport(ERROR, - (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for constraint %u", constraintOid))); + if (trigger->tuple) + { + tup = trigger->tuple; } + else + { + tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constraintOid)); + + // should not happen + if (!HeapTupleIsValid(tup)) { + ereport(ERROR, + (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for constraint %u", constraintOid))); + } + } + conForm = (Form_pg_constraint)GETSTRUCT(tup); + /* init conisenable here */ + riinfo->conisenable = true; + if (conForm->contype == CONSTRAINT_FOREIGN && !conForm->conisenable) + { + riinfo->nkeys = 0; + riinfo->conisenable = false; + ReleaseSysCache(tup); + return; + } + /* Do some easy cross-checks against the trigger call data */ if (rel_is_pk) { if (conForm->contype != CONSTRAINT_FOREIGN || conForm->conrelid != trigger->tgconstrrelid || @@ -2612,7 +2641,11 @@ static void ri_FetchConstraintInfo(RI_ConstraintInfo* riinfo, Trigger* trigger, if ((Pointer)arr != DatumGetPointer(adatum)) { pfree_ext(arr); /* free de-toasted copy, if any */ } - ReleaseSysCache(tup); + + if (!trigger->tuple) + { + ReleaseSysCache(tup); + } } /* diff --git a/src/common/backend/utils/cache/relcache.cpp b/src/common/backend/utils/cache/relcache.cpp index faa1ca222..0e3b41dfe 100644 --- a/src/common/backend/utils/cache/relcache.cpp +++ b/src/common/backend/utils/cache/relcache.cpp @@ -4651,7 +4651,7 @@ static void check_constraint_fetch(Relation relation) Form_pg_constraint conform = (Form_pg_constraint)GETSTRUCT(htup); /* We want check constraints only */ - if (conform->contype != CONSTRAINT_CHECK) + if (conform->contype != CONSTRAINT_CHECK || !conform->conisenable) continue; if (found >= ncheck) diff --git a/src/gausskernel/optimizer/commands/tablecmds.cpp b/src/gausskernel/optimizer/commands/tablecmds.cpp index a750b7e32..41fde16fe 100644 --- a/src/gausskernel/optimizer/commands/tablecmds.cpp +++ b/src/gausskernel/optimizer/commands/tablecmds.cpp @@ -395,7 +395,7 @@ static void checkFkeyPermissions(Relation rel, int16* attnums, int natts); static CoercionPathType findFkeyCast(Oid targetTypeId, Oid sourceTypeId, Oid* funcid); static void validateCheckConstraint(Relation rel, HeapTuple constrtup); static void validateCheckConstraintForBucket(Relation rel, Partition part, HeapTuple constrtup); -static void validateForeignKeyConstraint(char* conname, Relation rel, Relation pkrel, Oid pkindOid, Oid constraintOid); +static void validateForeignKeyConstraint(char* conname, Relation rel, Relation pkrel, Oid pkindOid, Oid constraintOid, HeapTuple constuple); static void createForeignKeyTriggers( Relation rel, Oid refRelOid, Constraint* fkconstraint, Oid constraintOid, Oid indexOid); static void ATController(Relation rel, List* cmds, bool recurse, LOCKMODE lockmode); @@ -476,6 +476,7 @@ static void ATAddCheckConstraint(List** wqueue, AlteredTableInfo* tab, Relation static void ATAddForeignKeyConstraint(AlteredTableInfo* tab, Relation rel, Constraint* fkconstraint, LOCKMODE lockmode); static void ATExecDropConstraint(Relation rel, const char* constrName, DropBehavior behavior, bool recurse, bool recursing, bool missing_ok, LOCKMODE lockmode); +static void ATExecEnableDisableConstraint(List** wqueue, AlteredTableInfo* tab, Relation rel, AlterTableCmd* cmd, LOCKMODE lockmode, bool isenable); static void ATPrepAlterColumnType(List** wqueue, AlteredTableInfo* tab, Relation rel, bool recurse, bool recursing, AlterTableCmd* cmd, LOCKMODE lockmode); static bool ATColumnChangeRequiresRewrite(Node* expr, AttrNumber varattno); @@ -5669,6 +5670,8 @@ LOCKMODE AlterTableGetLockLevel(List* cmds) case AT_AlterColumnGenericOptions: case AT_EnableRowMoveMent: case AT_DisableRowMoveMent: + case AT_EnableConstraint: + case AT_DisableConstraint: cmd_lockmode = AccessExclusiveLock; break; @@ -6195,6 +6198,8 @@ static void ATPrepCmd(List** wqueue, Relation rel, AlterTableCmd* cmd, bool recu case AT_DropInherit: /* NO INHERIT */ case AT_AddOf: /* OF */ case AT_DropOf: /* NOT OF */ + case AT_EnableConstraint: + case AT_DisableConstraint: ATSimplePermissions(rel, ATT_TABLE); /* These commands never recurse */ /* No command-specific prep needed */ @@ -6583,7 +6588,12 @@ static void ATExecCmd(List** wqueue, AlteredTableInfo* tab, Relation rel, AlterT case AT_SplitPartition: ATExecSplitPartition(rel, cmd); break; - + case AT_EnableConstraint: + ATExecEnableDisableConstraint(wqueue, tab, rel, cmd, lockmode, true); + break; + case AT_DisableConstraint: + ATExecEnableDisableConstraint(wqueue, tab, rel, cmd, lockmode, false); + break; #ifdef PGXC case AT_DistributeBy: AtExecDistributeBy(rel, (DistributeBy*)cmd->def); @@ -6823,7 +6833,7 @@ static void ATRewriteTables(List** wqueue, LOCKMODE lockmode) refrel = heap_open(con->refrelid, RowShareLock); - validateForeignKeyConstraint(fkconstraint->conname, rel, refrel, con->refindid, con->conid); + validateForeignKeyConstraint(fkconstraint->conname, rel, refrel, con->refindid, con->conid, NULL); /* * No need to mark the constraint row as validated, we did @@ -9689,7 +9699,7 @@ static void ATExecValidateConstraint(Relation rel, char* constrName, bool recurs */ refrel = heap_open(con->confrelid, RowShareLock); - validateForeignKeyConstraint(constrName, rel, refrel, con->conindid, conid); + validateForeignKeyConstraint(constrName, rel, refrel, con->conindid, conid, NULL); heap_close(refrel, NoLock); /* @@ -10170,7 +10180,7 @@ static void validateCheckConstraintForBucket(Relation rel, Partition part, HeapT * * Caller must have opened and locked both relations appropriately. */ -static void validateForeignKeyConstraint(char* conname, Relation rel, Relation pkrel, Oid pkindOid, Oid constraintOid) +static void validateForeignKeyConstraint(char* conname, Relation rel, Relation pkrel, Oid pkindOid, Oid constraintOid, HeapTuple constuple) { HeapScanDesc scan; HeapTuple tuple; @@ -10193,6 +10203,7 @@ static void validateForeignKeyConstraint(char* conname, Relation rel, Relation p trig.tgconstraint = constraintOid; trig.tgdeferrable = FALSE; trig.tginitdeferred = FALSE; + trig.tuple = constuple; /* we needn't fill in tgargs or tgqual */ /* * See if we can do it with a single LEFT JOIN query. A FALSE result @@ -10620,6 +10631,186 @@ static void ATExecDropConstraint(Relation rel, const char* constrName, DropBehav heap_close(conrel, RowExclusiveLock); } +static void +enable_disable_constraint_check(List** wqueue, AlteredTableInfo* tab, + Relation rel, HeapTuple tuple, bool isenable) +{ + Relation pgrel = NULL; + HeapTuple relTup = NULL; + Form_pg_class classForm = NULL; + Form_pg_constraint con = (Form_pg_constraint)GETSTRUCT(tuple); + + pgrel = heap_open(RelationRelationId, RowExclusiveLock); + relTup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(con->conrelid)); + if (!HeapTupleIsValid(relTup)) + ereport(ERROR, + (errcode(ERRCODE_CACHE_LOOKUP_FAILED), + errmsg("cache lookup failed for relation %u", con->conrelid))); + classForm = (Form_pg_class)GETSTRUCT(relTup); + + if (isenable) + { + classForm->relchecks++; + } + else + { + if (classForm->relchecks == 0) /* should not happen */ + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("relation \"%s\" has relchecks = 0", RelationGetRelationName(rel)))); + + classForm->relchecks--; + } + + simple_heap_update(pgrel, &relTup->t_self, relTup); + CatalogUpdateIndexes(pgrel, relTup); + CacheInvalidateRelcache(pgrel); + heap_freetuple_ext(relTup); + heap_close(pgrel, RowExclusiveLock); + + if (isenable) + { + validateCheckConstraint(rel, tuple); + } + + return; +} + +/** + * make primary enable/disable + */ +static void +enable_disable_constraint_primary(Form_pg_constraint con, bool isenable) +{ + Relation irel; + Oid indOid = InvalidOid; + + char persistence; + + if (!con) + { + return; + } + + indOid = con->conindid;//index oid of constraint, max one index + /* + * Obtain the current persistence of the existing index. We already hold + * lock on the index. + */ + irel = index_open(indOid, NoLock); + + if (isenable)//enable + { + //recreate index + AdaptMem mem_info; + + persistence = irel->rd_rel->relpersistence; + + //the index must be not used + index_close(irel, NoLock); + //recreate index + reindex_index(indOid, InvalidOid, false, &mem_info, false, persistence); + } + else// disable + { + //set index unuseable + ATExecUnusableIndex(irel); + index_close(irel, NoLock); + } + + return; +} + +static void +enable_disable_constraint_foreign(Relation rel, HeapTuple tuple, bool isenable) +{ + Form_pg_constraint con = (Form_pg_constraint)GETSTRUCT(tuple); + Relation refrel; + Oid constraintid = get_relation_constraint_oid(con->conrelid, NameStr(con->conname), false); + + if (isenable) + { + refrel = heap_open(con->confrelid, NoLock); + + validateForeignKeyConstraint(NameStr(con->conname), rel, refrel, con->conindid, constraintid, tuple); + + heap_close(refrel, NoLock); + } +} + +/* + * ALTER TABLE ENABLE/DISABLE CONSTRAINT + */ +static void ATExecEnableDisableConstraint(List** wqueue, AlteredTableInfo* tab, + Relation rel, AlterTableCmd* cmd, LOCKMODE lockmode, bool isenable) +{ + Relation conrel; + HeapTuple tuple, copy_tuple; + Form_pg_constraint con; + Oid owningRel = RelationGetRelid(rel); + char* consname = cmd->name; + Oid constraintid = InvalidOid; + + conrel = heap_open(ConstraintRelationId, RowExclusiveLock); + + constraintid = get_relation_constraint_oid(owningRel, consname, false); + tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constraintid)); + if (!HeapTupleIsValid(tuple)) { + ereport(ERROR, + (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for constraint %u", constraintid))); + } + + copy_tuple = heap_copytuple(tuple); + con = (Form_pg_constraint)GETSTRUCT(copy_tuple); + + if (con->conisenable != isenable) { + con->conisenable = BoolGetDatum(isenable); + + simple_heap_update(conrel, ©_tuple->t_self, copy_tuple); + + /* update the system catalog indexes */ + CatalogUpdateIndexes(conrel, copy_tuple); + + /* release system cache */ + CacheInvalidateRelcache(conrel); + + if (con)//routine check + { + switch (con->contype) + { + case CONSTRAINT_TRIGGER: //! 't' + case CONSTRAINT_CHECK: //! 'c' + enable_disable_constraint_check(wqueue, tab, rel, copy_tuple, isenable); + break; + case CONSTRAINT_PRIMARY: //! 'p' + case CONSTRAINT_UNIQUE: //! 'u' + enable_disable_constraint_primary(con, isenable); + break; + case CONSTRAINT_FOREIGN: //! 'f' + enable_disable_constraint_foreign(rel, copy_tuple, isenable); + break; + case CONSTRAINT_EXCLUSION: //! 'x' + case CONSTRAINT_CLUSTER: //! 's' + case CONSTRAINT_INVALID: //! 'i' + default: + ereport(WARNING, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("constraint \"%s\" of relation \"%s\" type %c unknown", + consname, + RelationGetRelationName(rel), + con->contype))); + break; + } + } + } + + heap_freetuple_ext(copy_tuple); + ReleaseSysCache(tuple); + heap_close(conrel, RowExclusiveLock); + + return; +} + /* * ALTER COLUMN TYPE */ diff --git a/src/gausskernel/optimizer/commands/trigger.cpp b/src/gausskernel/optimizer/commands/trigger.cpp index c406f6ed0..7d94322bc 100644 --- a/src/gausskernel/optimizer/commands/trigger.cpp +++ b/src/gausskernel/optimizer/commands/trigger.cpp @@ -1389,6 +1389,7 @@ void RelationBuildTriggers(Relation relation) build->tgdeferrable = pg_trigger->tgdeferrable; build->tginitdeferred = pg_trigger->tginitdeferred; build->tgnargs = pg_trigger->tgnargs; + build->tuple = NULL; /* tgattr is first var-width field, so OK to access directly */ build->tgnattr = pg_trigger->tgattr.dim1; if (build->tgnattr > 0) { @@ -1548,6 +1549,7 @@ TriggerDesc* CopyTriggerDesc(TriggerDesc* trigdesc) } if (trigger->tgqual) trigger->tgqual = pstrdup(trigger->tgqual); + trigger->tuple = NULL; trigger++; } diff --git a/src/gausskernel/process/tcop/utility.cpp b/src/gausskernel/process/tcop/utility.cpp index 18c707dc7..086462822 100644 --- a/src/gausskernel/process/tcop/utility.cpp +++ b/src/gausskernel/process/tcop/utility.cpp @@ -7747,6 +7747,12 @@ const char* CreateAlterTableCommandTag(const AlterTableType subtype) case AT_SET_COMPRESS: tag = "SET COMPRESS"; break; + case AT_EnableConstraint: + tag = "ENABLE CONSTRAINT"; + break; + case AT_DisableConstraint: + tag = "DISABLE CONSTRAINT"; + break; #ifdef PGXC case AT_DistributeBy: tag = "DISTRIBUTE BY"; diff --git a/src/include/catalog/pg_constraint.h b/src/include/catalog/pg_constraint.h index 305779598..2aa04e86c 100755 --- a/src/include/catalog/pg_constraint.h +++ b/src/include/catalog/pg_constraint.h @@ -95,6 +95,7 @@ CATALOG(pg_constraint,2606) BKI_SCHEMA_MACRO bool consoft; /* @hdfs show informational constraint. */ bool conopt; /* @hdfs decide whether to optimize plan by using informational constraint. */ + bool conisenable; /* enable = 1, disable = 0*/ #ifdef CATALOG_VARLEN /* variable-length fields start here */ @@ -162,7 +163,7 @@ typedef FormData_pg_constraint *Form_pg_constraint; * compiler constants for pg_constraint * ---------------- */ -#define Natts_pg_constraint 27 +#define Natts_pg_constraint 28 #define Anum_pg_constraint_conname 1 #define Anum_pg_constraint_connamespace 2 #define Anum_pg_constraint_contype 3 @@ -181,15 +182,16 @@ typedef FormData_pg_constraint *Form_pg_constraint; #define Anum_pg_constraint_connoinherit 16 #define Anum_pg_constraint_consoft 17 #define Anum_pg_constraint_conopt 18 -#define Anum_pg_constraint_conkey 19 -#define Anum_pg_constraint_confkey 20 -#define Anum_pg_constraint_conpfeqop 21 -#define Anum_pg_constraint_conppeqop 22 -#define Anum_pg_constraint_conffeqop 23 -#define Anum_pg_constraint_conexclop 24 -#define Anum_pg_constraint_conbin 25 -#define Anum_pg_constraint_consrc 26 -#define Anum_pg_constraint_conincluding 27 +#define Anum_pg_constraint_conisenable 19 +#define Anum_pg_constraint_conkey 20 +#define Anum_pg_constraint_confkey 21 +#define Anum_pg_constraint_conpfeqop 22 +#define Anum_pg_constraint_conppeqop 23 +#define Anum_pg_constraint_conffeqop 24 +#define Anum_pg_constraint_conexclop 25 +#define Anum_pg_constraint_conbin 26 +#define Anum_pg_constraint_consrc 27 +#define Anum_pg_constraint_conincluding 28 /* Valid values for contype */ diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 3499cde47..7e8f5c17f 100755 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -1567,6 +1567,8 @@ typedef enum AlterTableType { AT_ExchangePartition, /* ALTER TABLE EXCHANGE PARTITION WITH TABLE */ AT_MergePartition, /*MERGE PARTITION */ AT_SplitPartition, /* SPLIT PARTITION */ + AT_EnableConstraint, /* ENABLE CONSTRAINT name*/ + AT_DisableConstraint, /* DISABLE CONSTRAINT name */ /* this will be in a more natural position in 9.3: */ AT_ReAddConstraint /* internal to commands/tablecmds.c */ } AlterTableType; diff --git a/src/include/utils/reltrigger.h b/src/include/utils/reltrigger.h index 96a02f6fc..aa14da847 100644 --- a/src/include/utils/reltrigger.h +++ b/src/include/utils/reltrigger.h @@ -38,6 +38,7 @@ typedef struct Trigger { char** tgargs; char* tgqual; Oid tgowner; + HeapTuple tuple; } Trigger; typedef struct TriggerDesc {