PostgreSQL Source Code  git master
pg_attrdef.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * pg_attrdef.c
4  * routines to support manipulation of the pg_attrdef relation
5  *
6  * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  * src/backend/catalog/pg_attrdef.c
12  *
13  *-------------------------------------------------------------------------
14  */
15 #include "postgres.h"
16 
17 #include "access/genam.h"
18 #include "access/relation.h"
19 #include "access/table.h"
20 #include "catalog/catalog.h"
21 #include "catalog/dependency.h"
22 #include "catalog/indexing.h"
23 #include "catalog/objectaccess.h"
24 #include "catalog/pg_attrdef.h"
25 #include "executor/executor.h"
26 #include "optimizer/optimizer.h"
27 #include "utils/array.h"
28 #include "utils/builtins.h"
29 #include "utils/fmgroids.h"
30 #include "utils/rel.h"
31 #include "utils/syscache.h"
32 
33 
34 /*
35  * Store a default expression for column attnum of relation rel.
36  *
37  * Returns the OID of the new pg_attrdef tuple.
38  *
39  * add_column_mode must be true if we are storing the default for a new
40  * attribute, and false if it's for an already existing attribute. The reason
41  * for this is that the missing value must never be updated after it is set,
42  * which can only be when a column is added to the table. Otherwise we would
43  * in effect be changing existing tuples.
44  */
45 Oid
47  Node *expr, bool is_internal, bool add_column_mode)
48 {
49  char *adbin;
50  Relation adrel;
51  HeapTuple tuple;
52  Datum values[4];
53  static bool nulls[4] = {false, false, false, false};
54  Relation attrrel;
55  HeapTuple atttup;
56  Form_pg_attribute attStruct;
57  char attgenerated;
58  Oid attrdefOid;
59  ObjectAddress colobject,
60  defobject;
61 
62  adrel = table_open(AttrDefaultRelationId, RowExclusiveLock);
63 
64  /*
65  * Flatten expression to string form for storage.
66  */
67  adbin = nodeToString(expr);
68 
69  /*
70  * Make the pg_attrdef entry.
71  */
72  attrdefOid = GetNewOidWithIndex(adrel, AttrDefaultOidIndexId,
73  Anum_pg_attrdef_oid);
74  values[Anum_pg_attrdef_oid - 1] = ObjectIdGetDatum(attrdefOid);
75  values[Anum_pg_attrdef_adrelid - 1] = RelationGetRelid(rel);
76  values[Anum_pg_attrdef_adnum - 1] = attnum;
77  values[Anum_pg_attrdef_adbin - 1] = CStringGetTextDatum(adbin);
78 
79  tuple = heap_form_tuple(adrel->rd_att, values, nulls);
80  CatalogTupleInsert(adrel, tuple);
81 
82  defobject.classId = AttrDefaultRelationId;
83  defobject.objectId = attrdefOid;
84  defobject.objectSubId = 0;
85 
87 
88  /* now can free some of the stuff allocated above */
89  pfree(DatumGetPointer(values[Anum_pg_attrdef_adbin - 1]));
90  heap_freetuple(tuple);
91  pfree(adbin);
92 
93  /*
94  * Update the pg_attribute entry for the column to show that a default
95  * exists.
96  */
97  attrrel = table_open(AttributeRelationId, RowExclusiveLock);
98  atttup = SearchSysCacheCopy2(ATTNUM,
101  if (!HeapTupleIsValid(atttup))
102  elog(ERROR, "cache lookup failed for attribute %d of relation %u",
103  attnum, RelationGetRelid(rel));
104  attStruct = (Form_pg_attribute) GETSTRUCT(atttup);
105  attgenerated = attStruct->attgenerated;
106  if (!attStruct->atthasdef)
107  {
108  Form_pg_attribute defAttStruct;
109 
110  ExprState *exprState;
111  Expr *expr2 = (Expr *) expr;
112  EState *estate = NULL;
113  ExprContext *econtext;
114  Datum valuesAtt[Natts_pg_attribute] = {0};
115  bool nullsAtt[Natts_pg_attribute] = {0};
116  bool replacesAtt[Natts_pg_attribute] = {0};
117  Datum missingval = (Datum) 0;
118  bool missingIsNull = true;
119 
120  valuesAtt[Anum_pg_attribute_atthasdef - 1] = true;
121  replacesAtt[Anum_pg_attribute_atthasdef - 1] = true;
122 
123  if (rel->rd_rel->relkind == RELKIND_RELATION && add_column_mode &&
124  !attgenerated)
125  {
126  expr2 = expression_planner(expr2);
127  estate = CreateExecutorState();
128  exprState = ExecPrepareExpr(expr2, estate);
129  econtext = GetPerTupleExprContext(estate);
130 
131  missingval = ExecEvalExpr(exprState, econtext,
132  &missingIsNull);
133 
134  FreeExecutorState(estate);
135 
136  defAttStruct = TupleDescAttr(rel->rd_att, attnum - 1);
137 
138  if (missingIsNull)
139  {
140  /* if the default evaluates to NULL, just store a NULL array */
141  missingval = (Datum) 0;
142  }
143  else
144  {
145  /* otherwise make a one-element array of the value */
146  missingval = PointerGetDatum(construct_array(&missingval,
147  1,
148  defAttStruct->atttypid,
149  defAttStruct->attlen,
150  defAttStruct->attbyval,
151  defAttStruct->attalign));
152  }
153 
154  valuesAtt[Anum_pg_attribute_atthasmissing - 1] = !missingIsNull;
155  replacesAtt[Anum_pg_attribute_atthasmissing - 1] = true;
156  valuesAtt[Anum_pg_attribute_attmissingval - 1] = missingval;
157  replacesAtt[Anum_pg_attribute_attmissingval - 1] = true;
158  nullsAtt[Anum_pg_attribute_attmissingval - 1] = missingIsNull;
159  }
160  atttup = heap_modify_tuple(atttup, RelationGetDescr(attrrel),
161  valuesAtt, nullsAtt, replacesAtt);
162 
163  CatalogTupleUpdate(attrrel, &atttup->t_self, atttup);
164 
165  if (!missingIsNull)
166  pfree(DatumGetPointer(missingval));
167  }
168  table_close(attrrel, RowExclusiveLock);
169  heap_freetuple(atttup);
170 
171  /*
172  * Make a dependency so that the pg_attrdef entry goes away if the column
173  * (or whole table) is deleted. In the case of a generated column, make
174  * it an internal dependency to prevent the default expression from being
175  * deleted separately.
176  */
177  colobject.classId = RelationRelationId;
178  colobject.objectId = RelationGetRelid(rel);
179  colobject.objectSubId = attnum;
180 
181  recordDependencyOn(&defobject, &colobject,
182  attgenerated ? DEPENDENCY_INTERNAL : DEPENDENCY_AUTO);
183 
184  /*
185  * Record dependencies on objects used in the expression, too.
186  */
187  recordDependencyOnSingleRelExpr(&defobject, expr, RelationGetRelid(rel),
189  DEPENDENCY_NORMAL, false);
190 
191  /*
192  * Post creation hook for attribute defaults.
193  *
194  * XXX. ALTER TABLE ALTER COLUMN SET/DROP DEFAULT is implemented with a
195  * couple of deletion/creation of the attribute's default entry, so the
196  * callee should check existence of an older version of this entry if it
197  * needs to distinguish.
198  */
199  InvokeObjectPostCreateHookArg(AttrDefaultRelationId,
200  RelationGetRelid(rel), attnum, is_internal);
201 
202  return attrdefOid;
203 }
204 
205 
206 /*
207  * RemoveAttrDefault
208  *
209  * If the specified relation/attribute has a default, remove it.
210  * (If no default, raise error if complain is true, else return quietly.)
211  */
212 void
214  DropBehavior behavior, bool complain, bool internal)
215 {
216  Relation attrdef_rel;
217  ScanKeyData scankeys[2];
218  SysScanDesc scan;
219  HeapTuple tuple;
220  bool found = false;
221 
222  attrdef_rel = table_open(AttrDefaultRelationId, RowExclusiveLock);
223 
224  ScanKeyInit(&scankeys[0],
225  Anum_pg_attrdef_adrelid,
226  BTEqualStrategyNumber, F_OIDEQ,
227  ObjectIdGetDatum(relid));
228  ScanKeyInit(&scankeys[1],
229  Anum_pg_attrdef_adnum,
230  BTEqualStrategyNumber, F_INT2EQ,
232 
233  scan = systable_beginscan(attrdef_rel, AttrDefaultIndexId, true,
234  NULL, 2, scankeys);
235 
236  /* There should be at most one matching tuple, but we loop anyway */
237  while (HeapTupleIsValid(tuple = systable_getnext(scan)))
238  {
239  ObjectAddress object;
240  Form_pg_attrdef attrtuple = (Form_pg_attrdef) GETSTRUCT(tuple);
241 
242  object.classId = AttrDefaultRelationId;
243  object.objectId = attrtuple->oid;
244  object.objectSubId = 0;
245 
246  performDeletion(&object, behavior,
247  internal ? PERFORM_DELETION_INTERNAL : 0);
248 
249  found = true;
250  }
251 
252  systable_endscan(scan);
253  table_close(attrdef_rel, RowExclusiveLock);
254 
255  if (complain && !found)
256  elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
257  relid, attnum);
258 }
259 
260 /*
261  * RemoveAttrDefaultById
262  *
263  * Remove a pg_attrdef entry specified by OID. This is the guts of
264  * attribute-default removal. Note it should be called via performDeletion,
265  * not directly.
266  */
267 void
269 {
270  Relation attrdef_rel;
271  Relation attr_rel;
272  Relation myrel;
273  ScanKeyData scankeys[1];
274  SysScanDesc scan;
275  HeapTuple tuple;
276  Oid myrelid;
277  AttrNumber myattnum;
278 
279  /* Grab an appropriate lock on the pg_attrdef relation */
280  attrdef_rel = table_open(AttrDefaultRelationId, RowExclusiveLock);
281 
282  /* Find the pg_attrdef tuple */
283  ScanKeyInit(&scankeys[0],
284  Anum_pg_attrdef_oid,
285  BTEqualStrategyNumber, F_OIDEQ,
286  ObjectIdGetDatum(attrdefId));
287 
288  scan = systable_beginscan(attrdef_rel, AttrDefaultOidIndexId, true,
289  NULL, 1, scankeys);
290 
291  tuple = systable_getnext(scan);
292  if (!HeapTupleIsValid(tuple))
293  elog(ERROR, "could not find tuple for attrdef %u", attrdefId);
294 
295  myrelid = ((Form_pg_attrdef) GETSTRUCT(tuple))->adrelid;
296  myattnum = ((Form_pg_attrdef) GETSTRUCT(tuple))->adnum;
297 
298  /* Get an exclusive lock on the relation owning the attribute */
299  myrel = relation_open(myrelid, AccessExclusiveLock);
300 
301  /* Now we can delete the pg_attrdef row */
302  CatalogTupleDelete(attrdef_rel, &tuple->t_self);
303 
304  systable_endscan(scan);
305  table_close(attrdef_rel, RowExclusiveLock);
306 
307  /* Fix the pg_attribute row */
308  attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
309 
310  tuple = SearchSysCacheCopy2(ATTNUM,
311  ObjectIdGetDatum(myrelid),
312  Int16GetDatum(myattnum));
313  if (!HeapTupleIsValid(tuple)) /* shouldn't happen */
314  elog(ERROR, "cache lookup failed for attribute %d of relation %u",
315  myattnum, myrelid);
316 
317  ((Form_pg_attribute) GETSTRUCT(tuple))->atthasdef = false;
318 
319  CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
320 
321  /*
322  * Our update of the pg_attribute row will force a relcache rebuild, so
323  * there's nothing else to do here.
324  */
325  table_close(attr_rel, RowExclusiveLock);
326 
327  /* Keep lock on attribute's rel until end of xact */
328  relation_close(myrel, NoLock);
329 }
330 
331 
332 /*
333  * Get the pg_attrdef OID of the default expression for a column
334  * identified by relation OID and column number.
335  *
336  * Returns InvalidOid if there is no such pg_attrdef entry.
337  */
338 Oid
340 {
341  Oid result = InvalidOid;
342  Relation attrdef;
343  ScanKeyData keys[2];
344  SysScanDesc scan;
345  HeapTuple tup;
346 
347  attrdef = table_open(AttrDefaultRelationId, AccessShareLock);
348  ScanKeyInit(&keys[0],
349  Anum_pg_attrdef_adrelid,
351  F_OIDEQ,
352  ObjectIdGetDatum(relid));
353  ScanKeyInit(&keys[1],
354  Anum_pg_attrdef_adnum,
356  F_INT2EQ,
358  scan = systable_beginscan(attrdef, AttrDefaultIndexId, true,
359  NULL, 2, keys);
360 
361  if (HeapTupleIsValid(tup = systable_getnext(scan)))
362  {
363  Form_pg_attrdef atdform = (Form_pg_attrdef) GETSTRUCT(tup);
364 
365  result = atdform->oid;
366  }
367 
368  systable_endscan(scan);
369  table_close(attrdef, AccessShareLock);
370 
371  return result;
372 }
373 
374 /*
375  * Given a pg_attrdef OID, return the relation OID and column number of
376  * the owning column (represented as an ObjectAddress for convenience).
377  *
378  * Returns InvalidObjectAddress if there is no such pg_attrdef entry.
379  */
382 {
384  Relation attrdef;
385  ScanKeyData skey[1];
386  SysScanDesc scan;
387  HeapTuple tup;
388 
389  attrdef = table_open(AttrDefaultRelationId, AccessShareLock);
390  ScanKeyInit(&skey[0],
391  Anum_pg_attrdef_oid,
392  BTEqualStrategyNumber, F_OIDEQ,
393  ObjectIdGetDatum(attrdefoid));
394  scan = systable_beginscan(attrdef, AttrDefaultOidIndexId, true,
395  NULL, 1, skey);
396 
397  if (HeapTupleIsValid(tup = systable_getnext(scan)))
398  {
399  Form_pg_attrdef atdform = (Form_pg_attrdef) GETSTRUCT(tup);
400 
401  result.classId = RelationRelationId;
402  result.objectId = atdform->adrelid;
403  result.objectSubId = atdform->adnum;
404  }
405 
406  systable_endscan(scan);
407  table_close(attrdef, AccessShareLock);
408 
409  return result;
410 }
ArrayType * construct_array(Datum *elems, int nelems, Oid elmtype, int elmlen, bool elmbyval, char elmalign)
Definition: arrayfuncs.c:3343
int16 AttrNumber
Definition: attnum.h:21
static Datum values[MAXATTR]
Definition: bootstrap.c:156
#define CStringGetTextDatum(s)
Definition: builtins.h:94
Oid GetNewOidWithIndex(Relation relation, Oid indexId, AttrNumber oidcolumn)
Definition: catalog.c:393
void performDeletion(const ObjectAddress *object, DropBehavior behavior, int flags)
Definition: dependency.c:328
void recordDependencyOnSingleRelExpr(const ObjectAddress *depender, Node *expr, Oid relId, DependencyType behavior, DependencyType self_behavior, bool reverse_self)
Definition: dependency.c:1645
@ DEPENDENCY_AUTO
Definition: dependency.h:34
@ DEPENDENCY_INTERNAL
Definition: dependency.h:35
@ DEPENDENCY_NORMAL
Definition: dependency.h:33
#define PERFORM_DELETION_INTERNAL
Definition: dependency.h:136
#define ERROR
Definition: elog.h:39
ExprState * ExecPrepareExpr(Expr *node, EState *estate)
Definition: execExpr.c:747
EState * CreateExecutorState(void)
Definition: execUtils.c:93
void FreeExecutorState(EState *estate)
Definition: execUtils.c:193
#define GetPerTupleExprContext(estate)
Definition: executor.h:538
static Datum ExecEvalExpr(ExprState *state, ExprContext *econtext, bool *isNull)
Definition: executor.h:321
void systable_endscan(SysScanDesc sysscan)
Definition: genam.c:599
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition: genam.c:506
SysScanDesc systable_beginscan(Relation heapRelation, Oid indexId, bool indexOK, Snapshot snapshot, int nkeys, ScanKey key)
Definition: genam.c:387
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, Datum *values, bool *isnull)
Definition: heaptuple.c:1020
HeapTuple heap_modify_tuple(HeapTuple tuple, TupleDesc tupleDesc, Datum *replValues, bool *replIsnull, bool *doReplace)
Definition: heaptuple.c:1113
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1338
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define GETSTRUCT(TUP)
Definition: htup_details.h:653
void CatalogTupleUpdate(Relation heapRel, ItemPointer otid, HeapTuple tup)
Definition: indexing.c:301
void CatalogTupleInsert(Relation heapRel, HeapTuple tup)
Definition: indexing.c:221
void CatalogTupleDelete(Relation heapRel, ItemPointer tid)
Definition: indexing.c:350
#define NoLock
Definition: lockdefs.h:34
#define AccessExclusiveLock
Definition: lockdefs.h:43
#define AccessShareLock
Definition: lockdefs.h:36
#define RowExclusiveLock
Definition: lockdefs.h:38
void pfree(void *pointer)
Definition: mcxt.c:1436
#define InvokeObjectPostCreateHookArg(classId, objectId, subId, is_internal)
Definition: objectaccess.h:175
const ObjectAddress InvalidObjectAddress
char * nodeToString(const void *obj)
Definition: outfuncs.c:877
DropBehavior
Definition: parsenodes.h:2041
void RemoveAttrDefaultById(Oid attrdefId)
Definition: pg_attrdef.c:268
Oid GetAttrDefaultOid(Oid relid, AttrNumber attnum)
Definition: pg_attrdef.c:339
Oid StoreAttrDefault(Relation rel, AttrNumber attnum, Node *expr, bool is_internal, bool add_column_mode)
Definition: pg_attrdef.c:46
ObjectAddress GetAttrDefaultColumnAddress(Oid attrdefoid)
Definition: pg_attrdef.c:381
void RemoveAttrDefault(Oid relid, AttrNumber attnum, DropBehavior behavior, bool complain, bool internal)
Definition: pg_attrdef.c:213
FormData_pg_attrdef * Form_pg_attrdef
Definition: pg_attrdef.h:49
int16 attnum
Definition: pg_attribute.h:83
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:207
void recordDependencyOn(const ObjectAddress *depender, const ObjectAddress *referenced, DependencyType behavior)
Definition: pg_depend.c:44
Expr * expression_planner(Expr *expr)
Definition: planner.c:6434
static Datum PointerGetDatum(const void *X)
Definition: postgres.h:322
uintptr_t Datum
Definition: postgres.h:64
static Datum Int16GetDatum(int16 X)
Definition: postgres.h:172
static Datum ObjectIdGetDatum(Oid X)
Definition: postgres.h:252
static Pointer DatumGetPointer(Datum X)
Definition: postgres.h:312
#define InvalidOid
Definition: postgres_ext.h:36
unsigned int Oid
Definition: postgres_ext.h:31
#define RelationGetRelid(relation)
Definition: rel.h:501
#define RelationGetDescr(relation)
Definition: rel.h:527
void ScanKeyInit(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, RegProcedure procedure, Datum argument)
Definition: scankey.c:76
void relation_close(Relation relation, LOCKMODE lockmode)
Definition: relation.c:206
Relation relation_open(Oid relationId, LOCKMODE lockmode)
Definition: relation.c:48
#define BTEqualStrategyNumber
Definition: stratnum.h:31
ItemPointerData t_self
Definition: htup.h:65
Definition: nodes.h:129
TupleDesc rd_att
Definition: rel.h:111
Form_pg_class rd_rel
Definition: rel.h:110
#define SearchSysCacheCopy2(cacheId, key1, key2)
Definition: syscache.h:181
@ ATTNUM
Definition: syscache.h:41
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:126
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:40
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:92