summaryrefslogtreecommitdiffstats
path: root/src/backend/catalog/pg_attrdef.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/catalog/pg_attrdef.c')
-rw-r--r--src/backend/catalog/pg_attrdef.c413
1 files changed, 413 insertions, 0 deletions
diff --git a/src/backend/catalog/pg_attrdef.c b/src/backend/catalog/pg_attrdef.c
new file mode 100644
index 0000000..c5d4a99
--- /dev/null
+++ b/src/backend/catalog/pg_attrdef.c
@@ -0,0 +1,413 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_attrdef.c
+ * routines to support manipulation of the pg_attrdef relation
+ *
+ * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/catalog/pg_attrdef.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/genam.h"
+#include "access/relation.h"
+#include "access/table.h"
+#include "catalog/catalog.h"
+#include "catalog/dependency.h"
+#include "catalog/indexing.h"
+#include "catalog/objectaccess.h"
+#include "catalog/pg_attrdef.h"
+#include "executor/executor.h"
+#include "optimizer/optimizer.h"
+#include "utils/array.h"
+#include "utils/builtins.h"
+#include "utils/fmgroids.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+
+/*
+ * Store a default expression for column attnum of relation rel.
+ *
+ * Returns the OID of the new pg_attrdef tuple.
+ *
+ * add_column_mode must be true if we are storing the default for a new
+ * attribute, and false if it's for an already existing attribute. The reason
+ * for this is that the missing value must never be updated after it is set,
+ * which can only be when a column is added to the table. Otherwise we would
+ * in effect be changing existing tuples.
+ */
+Oid
+StoreAttrDefault(Relation rel, AttrNumber attnum,
+ Node *expr, bool is_internal, bool add_column_mode)
+{
+ char *adbin;
+ Relation adrel;
+ HeapTuple tuple;
+ Datum values[4];
+ static bool nulls[4] = {false, false, false, false};
+ Relation attrrel;
+ HeapTuple atttup;
+ Form_pg_attribute attStruct;
+ char attgenerated;
+ Oid attrdefOid;
+ ObjectAddress colobject,
+ defobject;
+
+ adrel = table_open(AttrDefaultRelationId, RowExclusiveLock);
+
+ /*
+ * Flatten expression to string form for storage.
+ */
+ adbin = nodeToString(expr);
+
+ /*
+ * Make the pg_attrdef entry.
+ */
+ attrdefOid = GetNewOidWithIndex(adrel, AttrDefaultOidIndexId,
+ Anum_pg_attrdef_oid);
+ values[Anum_pg_attrdef_oid - 1] = ObjectIdGetDatum(attrdefOid);
+ values[Anum_pg_attrdef_adrelid - 1] = RelationGetRelid(rel);
+ values[Anum_pg_attrdef_adnum - 1] = attnum;
+ values[Anum_pg_attrdef_adbin - 1] = CStringGetTextDatum(adbin);
+
+ tuple = heap_form_tuple(adrel->rd_att, values, nulls);
+ CatalogTupleInsert(adrel, tuple);
+
+ defobject.classId = AttrDefaultRelationId;
+ defobject.objectId = attrdefOid;
+ defobject.objectSubId = 0;
+
+ table_close(adrel, RowExclusiveLock);
+
+ /* now can free some of the stuff allocated above */
+ pfree(DatumGetPointer(values[Anum_pg_attrdef_adbin - 1]));
+ heap_freetuple(tuple);
+ pfree(adbin);
+
+ /*
+ * Update the pg_attribute entry for the column to show that a default
+ * exists.
+ */
+ attrrel = table_open(AttributeRelationId, RowExclusiveLock);
+ atttup = SearchSysCacheCopy2(ATTNUM,
+ ObjectIdGetDatum(RelationGetRelid(rel)),
+ Int16GetDatum(attnum));
+ if (!HeapTupleIsValid(atttup))
+ elog(ERROR, "cache lookup failed for attribute %d of relation %u",
+ attnum, RelationGetRelid(rel));
+ attStruct = (Form_pg_attribute) GETSTRUCT(atttup);
+ attgenerated = attStruct->attgenerated;
+ if (!attStruct->atthasdef)
+ {
+ Form_pg_attribute defAttStruct;
+
+ ExprState *exprState;
+ Expr *expr2 = (Expr *) expr;
+ EState *estate = NULL;
+ ExprContext *econtext;
+ Datum valuesAtt[Natts_pg_attribute];
+ bool nullsAtt[Natts_pg_attribute];
+ bool replacesAtt[Natts_pg_attribute];
+ Datum missingval = (Datum) 0;
+ bool missingIsNull = true;
+
+ MemSet(valuesAtt, 0, sizeof(valuesAtt));
+ MemSet(nullsAtt, false, sizeof(nullsAtt));
+ MemSet(replacesAtt, false, sizeof(replacesAtt));
+ valuesAtt[Anum_pg_attribute_atthasdef - 1] = true;
+ replacesAtt[Anum_pg_attribute_atthasdef - 1] = true;
+
+ if (rel->rd_rel->relkind == RELKIND_RELATION && add_column_mode &&
+ !attgenerated)
+ {
+ expr2 = expression_planner(expr2);
+ estate = CreateExecutorState();
+ exprState = ExecPrepareExpr(expr2, estate);
+ econtext = GetPerTupleExprContext(estate);
+
+ missingval = ExecEvalExpr(exprState, econtext,
+ &missingIsNull);
+
+ FreeExecutorState(estate);
+
+ defAttStruct = TupleDescAttr(rel->rd_att, attnum - 1);
+
+ if (missingIsNull)
+ {
+ /* if the default evaluates to NULL, just store a NULL array */
+ missingval = (Datum) 0;
+ }
+ else
+ {
+ /* otherwise make a one-element array of the value */
+ missingval = PointerGetDatum(construct_array(&missingval,
+ 1,
+ defAttStruct->atttypid,
+ defAttStruct->attlen,
+ defAttStruct->attbyval,
+ defAttStruct->attalign));
+ }
+
+ valuesAtt[Anum_pg_attribute_atthasmissing - 1] = !missingIsNull;
+ replacesAtt[Anum_pg_attribute_atthasmissing - 1] = true;
+ valuesAtt[Anum_pg_attribute_attmissingval - 1] = missingval;
+ replacesAtt[Anum_pg_attribute_attmissingval - 1] = true;
+ nullsAtt[Anum_pg_attribute_attmissingval - 1] = missingIsNull;
+ }
+ atttup = heap_modify_tuple(atttup, RelationGetDescr(attrrel),
+ valuesAtt, nullsAtt, replacesAtt);
+
+ CatalogTupleUpdate(attrrel, &atttup->t_self, atttup);
+
+ if (!missingIsNull)
+ pfree(DatumGetPointer(missingval));
+ }
+ table_close(attrrel, RowExclusiveLock);
+ heap_freetuple(atttup);
+
+ /*
+ * Make a dependency so that the pg_attrdef entry goes away if the column
+ * (or whole table) is deleted. In the case of a generated column, make
+ * it an internal dependency to prevent the default expression from being
+ * deleted separately.
+ */
+ colobject.classId = RelationRelationId;
+ colobject.objectId = RelationGetRelid(rel);
+ colobject.objectSubId = attnum;
+
+ recordDependencyOn(&defobject, &colobject,
+ attgenerated ? DEPENDENCY_INTERNAL : DEPENDENCY_AUTO);
+
+ /*
+ * Record dependencies on objects used in the expression, too.
+ */
+ recordDependencyOnSingleRelExpr(&defobject, expr, RelationGetRelid(rel),
+ DEPENDENCY_NORMAL,
+ DEPENDENCY_NORMAL, false);
+
+ /*
+ * Post creation hook for attribute defaults.
+ *
+ * XXX. ALTER TABLE ALTER COLUMN SET/DROP DEFAULT is implemented with a
+ * couple of deletion/creation of the attribute's default entry, so the
+ * callee should check existence of an older version of this entry if it
+ * needs to distinguish.
+ */
+ InvokeObjectPostCreateHookArg(AttrDefaultRelationId,
+ RelationGetRelid(rel), attnum, is_internal);
+
+ return attrdefOid;
+}
+
+
+/*
+ * RemoveAttrDefault
+ *
+ * If the specified relation/attribute has a default, remove it.
+ * (If no default, raise error if complain is true, else return quietly.)
+ */
+void
+RemoveAttrDefault(Oid relid, AttrNumber attnum,
+ DropBehavior behavior, bool complain, bool internal)
+{
+ Relation attrdef_rel;
+ ScanKeyData scankeys[2];
+ SysScanDesc scan;
+ HeapTuple tuple;
+ bool found = false;
+
+ attrdef_rel = table_open(AttrDefaultRelationId, RowExclusiveLock);
+
+ ScanKeyInit(&scankeys[0],
+ Anum_pg_attrdef_adrelid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(relid));
+ ScanKeyInit(&scankeys[1],
+ Anum_pg_attrdef_adnum,
+ BTEqualStrategyNumber, F_INT2EQ,
+ Int16GetDatum(attnum));
+
+ scan = systable_beginscan(attrdef_rel, AttrDefaultIndexId, true,
+ NULL, 2, scankeys);
+
+ /* There should be at most one matching tuple, but we loop anyway */
+ while (HeapTupleIsValid(tuple = systable_getnext(scan)))
+ {
+ ObjectAddress object;
+ Form_pg_attrdef attrtuple = (Form_pg_attrdef) GETSTRUCT(tuple);
+
+ object.classId = AttrDefaultRelationId;
+ object.objectId = attrtuple->oid;
+ object.objectSubId = 0;
+
+ performDeletion(&object, behavior,
+ internal ? PERFORM_DELETION_INTERNAL : 0);
+
+ found = true;
+ }
+
+ systable_endscan(scan);
+ table_close(attrdef_rel, RowExclusiveLock);
+
+ if (complain && !found)
+ elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
+ relid, attnum);
+}
+
+/*
+ * RemoveAttrDefaultById
+ *
+ * Remove a pg_attrdef entry specified by OID. This is the guts of
+ * attribute-default removal. Note it should be called via performDeletion,
+ * not directly.
+ */
+void
+RemoveAttrDefaultById(Oid attrdefId)
+{
+ Relation attrdef_rel;
+ Relation attr_rel;
+ Relation myrel;
+ ScanKeyData scankeys[1];
+ SysScanDesc scan;
+ HeapTuple tuple;
+ Oid myrelid;
+ AttrNumber myattnum;
+
+ /* Grab an appropriate lock on the pg_attrdef relation */
+ attrdef_rel = table_open(AttrDefaultRelationId, RowExclusiveLock);
+
+ /* Find the pg_attrdef tuple */
+ ScanKeyInit(&scankeys[0],
+ Anum_pg_attrdef_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(attrdefId));
+
+ scan = systable_beginscan(attrdef_rel, AttrDefaultOidIndexId, true,
+ NULL, 1, scankeys);
+
+ tuple = systable_getnext(scan);
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "could not find tuple for attrdef %u", attrdefId);
+
+ myrelid = ((Form_pg_attrdef) GETSTRUCT(tuple))->adrelid;
+ myattnum = ((Form_pg_attrdef) GETSTRUCT(tuple))->adnum;
+
+ /* Get an exclusive lock on the relation owning the attribute */
+ myrel = relation_open(myrelid, AccessExclusiveLock);
+
+ /* Now we can delete the pg_attrdef row */
+ CatalogTupleDelete(attrdef_rel, &tuple->t_self);
+
+ systable_endscan(scan);
+ table_close(attrdef_rel, RowExclusiveLock);
+
+ /* Fix the pg_attribute row */
+ attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
+
+ tuple = SearchSysCacheCopy2(ATTNUM,
+ ObjectIdGetDatum(myrelid),
+ Int16GetDatum(myattnum));
+ if (!HeapTupleIsValid(tuple)) /* shouldn't happen */
+ elog(ERROR, "cache lookup failed for attribute %d of relation %u",
+ myattnum, myrelid);
+
+ ((Form_pg_attribute) GETSTRUCT(tuple))->atthasdef = false;
+
+ CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
+
+ /*
+ * Our update of the pg_attribute row will force a relcache rebuild, so
+ * there's nothing else to do here.
+ */
+ table_close(attr_rel, RowExclusiveLock);
+
+ /* Keep lock on attribute's rel until end of xact */
+ relation_close(myrel, NoLock);
+}
+
+
+/*
+ * Get the pg_attrdef OID of the default expression for a column
+ * identified by relation OID and and column number.
+ *
+ * Returns InvalidOid if there is no such pg_attrdef entry.
+ */
+Oid
+GetAttrDefaultOid(Oid relid, AttrNumber attnum)
+{
+ Oid result = InvalidOid;
+ Relation attrdef;
+ ScanKeyData keys[2];
+ SysScanDesc scan;
+ HeapTuple tup;
+
+ attrdef = table_open(AttrDefaultRelationId, AccessShareLock);
+ ScanKeyInit(&keys[0],
+ Anum_pg_attrdef_adrelid,
+ BTEqualStrategyNumber,
+ F_OIDEQ,
+ ObjectIdGetDatum(relid));
+ ScanKeyInit(&keys[1],
+ Anum_pg_attrdef_adnum,
+ BTEqualStrategyNumber,
+ F_INT2EQ,
+ Int16GetDatum(attnum));
+ scan = systable_beginscan(attrdef, AttrDefaultIndexId, true,
+ NULL, 2, keys);
+
+ if (HeapTupleIsValid(tup = systable_getnext(scan)))
+ {
+ Form_pg_attrdef atdform = (Form_pg_attrdef) GETSTRUCT(tup);
+
+ result = atdform->oid;
+ }
+
+ systable_endscan(scan);
+ table_close(attrdef, AccessShareLock);
+
+ return result;
+}
+
+/*
+ * Given a pg_attrdef OID, return the relation OID and column number of
+ * the owning column (represented as an ObjectAddress for convenience).
+ *
+ * Returns InvalidObjectAddress if there is no such pg_attrdef entry.
+ */
+ObjectAddress
+GetAttrDefaultColumnAddress(Oid attrdefoid)
+{
+ ObjectAddress result = InvalidObjectAddress;
+ Relation attrdef;
+ ScanKeyData skey[1];
+ SysScanDesc scan;
+ HeapTuple tup;
+
+ attrdef = table_open(AttrDefaultRelationId, AccessShareLock);
+ ScanKeyInit(&skey[0],
+ Anum_pg_attrdef_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(attrdefoid));
+ scan = systable_beginscan(attrdef, AttrDefaultOidIndexId, true,
+ NULL, 1, skey);
+
+ if (HeapTupleIsValid(tup = systable_getnext(scan)))
+ {
+ Form_pg_attrdef atdform = (Form_pg_attrdef) GETSTRUCT(tup);
+
+ result.classId = RelationRelationId;
+ result.objectId = atdform->adrelid;
+ result.objectSubId = atdform->adnum;
+ }
+
+ systable_endscan(scan);
+ table_close(attrdef, AccessShareLock);
+
+ return result;
+}