/* * Setup a ScanKey for a search in the relation 'rel' for a tuple 'key' that * is setup to match 'rel' (*NOT* idxrel!). * * Returns whether any column contains NULLs. * * This is not generic routine, it expects the idxrel to be replication * identity of a rel and meet all limitations associated with that. */ static bool build_replindex_scan_key(ScanKey skey, Relation rel, Relation idxrel, TupleTableSlot *searchslot) { int attoff; bool isnull; Datum indclassDatum; oidvector *opclass; int2vector *indkey = &idxrel->rd_index->indkey; bool hasnulls = false; Assert(RelationGetReplicaIndex(rel) == RelationGetRelid(idxrel)); indclassDatum = SysCacheGetAttr(INDEXRELID, idxrel->rd_indextuple, Anum_pg_index_indclass, &isnull); Assert(!isnull); opclass = (oidvector *) DatumGetPointer(indclassDatum); /* Build scankey for every attribute in the index. */ for (attoff = 0; attoff < IndexRelationGetNumberOfKeyAttributes(idxrel); attoff++) { Oid operator; Oid opfamily; RegProcedure regop; int pkattno = attoff + 1; int mainattno = indkey->values[attoff]; Oid optype = get_opclass_input_type(opclass->values[attoff]); /* * Load the operator info. We need this to get the equality operator * function for the scan key. */ opfamily = get_opclass_family(opclass->values[attoff]); operator = get_opfamily_member(opfamily, optype, optype, BTEqualStrategyNumber); if (!OidIsValid(operator)) elog(ERROR, "missing operator %d(%u,%u) in opfamily %u", BTEqualStrategyNumber, optype, optype, opfamily); regop = get_opcode(operator); /* Initialize the scankey. */ ScanKeyInit(&skey[attoff], pkattno, BTEqualStrategyNumber, regop, searchslot->tts_values[mainattno - 1]); /* Check for null value. */ if (searchslot->tts_isnull[mainattno - 1]) { hasnulls = true; skey[attoff].sk_flags |= SK_ISNULL; } } return hasnulls; }
/* * Check if command can be executed with current replica identity. */ void CheckCmdReplicaIdentity(Relation rel, CmdType cmd) { PublicationActions *pubactions; /* We only need to do checks for UPDATE and DELETE. */ if (cmd != CMD_UPDATE && cmd != CMD_DELETE) return; /* If relation has replica identity we are always good. */ if (rel->rd_rel->relreplident == REPLICA_IDENTITY_FULL || OidIsValid(RelationGetReplicaIndex(rel))) return; /* * This is either UPDATE OR DELETE and there is no replica identity. * * Check if the table publishes UPDATES or DELETES. */ pubactions = GetRelationPublicationActions(rel); if (cmd == CMD_UPDATE && pubactions->pubupdate) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("cannot update table \"%s\" because it does not have replica identity and publishes updates", RelationGetRelationName(rel)), errhint("To enable updating the table, set REPLICA IDENTITY using ALTER TABLE."))); else if (cmd == CMD_DELETE && pubactions->pubdelete) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("cannot delete from table \"%s\" because it does not have replica identity and publishes deletes", RelationGetRelationName(rel)), errhint("To enable deleting from the table, set REPLICA IDENTITY using ALTER TABLE."))); }
/* Returns the relation object for the index that we're going to use as key for a * particular table. (Indexes are relations too!) Returns null if the table is unkeyed. * The return value is opened with a shared lock; call relation_close() when finished. */ Relation table_key_index(Relation rel) { char replident = rel->rd_rel->relreplident; Oid repl_ident_oid; List *indexes; ListCell *index_oid; if (replident == REPLICA_IDENTITY_NOTHING) { return NULL; } if (replident == REPLICA_IDENTITY_INDEX) { repl_ident_oid = RelationGetReplicaIndex(rel); if (repl_ident_oid != InvalidOid) { return relation_open(repl_ident_oid, AccessShareLock); } } // There doesn't seem to be a convenient way of getting the primary key index for // a table, so we have to iterate over all the table's indexes. indexes = RelationGetIndexList(rel); foreach(index_oid, indexes) { Relation index_rel = relation_open(lfirst_oid(index_oid), AccessShareLock); Form_pg_index index = index_rel->rd_index; if (IndexIsValid(index) && IndexIsReady(index) && index->indisprimary) { list_free(indexes); return index_rel; } relation_close(index_rel, AccessShareLock); }
/* * Get replica identity index or if it is not defined a primary key. * * If neither is defined, returns InvalidOid */ static Oid GetRelationIdentityOrPK(Relation rel) { Oid idxoid; idxoid = RelationGetReplicaIndex(rel); if (!OidIsValid(idxoid)) idxoid = RelationGetPrimaryKeyIndex(rel); return idxoid; }