Age Owner TLA Line data Source code
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
384 tgl 46 CBC 1717 : StoreAttrDefault(Relation rel, AttrNumber attnum,
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 1717 : adrel = table_open(AttrDefaultRelationId, RowExclusiveLock);
63 :
64 : /*
65 : * Flatten expression to string form for storage.
66 : */
67 1717 : adbin = nodeToString(expr);
68 :
69 : /*
70 : * Make the pg_attrdef entry.
71 : */
72 1717 : attrdefOid = GetNewOidWithIndex(adrel, AttrDefaultOidIndexId,
73 : Anum_pg_attrdef_oid);
74 1717 : values[Anum_pg_attrdef_oid - 1] = ObjectIdGetDatum(attrdefOid);
75 1717 : values[Anum_pg_attrdef_adrelid - 1] = RelationGetRelid(rel);
76 1717 : values[Anum_pg_attrdef_adnum - 1] = attnum;
77 1717 : values[Anum_pg_attrdef_adbin - 1] = CStringGetTextDatum(adbin);
78 :
79 1717 : tuple = heap_form_tuple(adrel->rd_att, values, nulls);
80 1717 : CatalogTupleInsert(adrel, tuple);
81 :
82 1717 : defobject.classId = AttrDefaultRelationId;
83 1717 : defobject.objectId = attrdefOid;
84 1717 : defobject.objectSubId = 0;
85 :
86 1717 : table_close(adrel, RowExclusiveLock);
87 :
88 : /* now can free some of the stuff allocated above */
89 1717 : pfree(DatumGetPointer(values[Anum_pg_attrdef_adbin - 1]));
90 1717 : heap_freetuple(tuple);
91 1717 : pfree(adbin);
92 :
93 : /*
94 : * Update the pg_attribute entry for the column to show that a default
95 : * exists.
96 : */
97 1717 : attrrel = table_open(AttributeRelationId, RowExclusiveLock);
98 1717 : atttup = SearchSysCacheCopy2(ATTNUM,
99 : ObjectIdGetDatum(RelationGetRelid(rel)),
100 : Int16GetDatum(attnum));
101 1717 : if (!HeapTupleIsValid(atttup))
384 tgl 102 UBC 0 : elog(ERROR, "cache lookup failed for attribute %d of relation %u",
103 : attnum, RelationGetRelid(rel));
384 tgl 104 CBC 1717 : attStruct = (Form_pg_attribute) GETSTRUCT(atttup);
105 1717 : attgenerated = attStruct->attgenerated;
106 1717 : if (!attStruct->atthasdef)
107 : {
108 : Form_pg_attribute defAttStruct;
109 :
110 : ExprState *exprState;
111 1717 : Expr *expr2 = (Expr *) expr;
112 1717 : EState *estate = NULL;
113 : ExprContext *econtext;
267 peter 114 GNC 1717 : Datum valuesAtt[Natts_pg_attribute] = {0};
115 1717 : bool nullsAtt[Natts_pg_attribute] = {0};
116 1717 : bool replacesAtt[Natts_pg_attribute] = {0};
384 tgl 117 CBC 1717 : Datum missingval = (Datum) 0;
118 1717 : bool missingIsNull = true;
119 :
120 1717 : valuesAtt[Anum_pg_attribute_atthasdef - 1] = true;
384 tgl 121 GIC 1717 : replacesAtt[Anum_pg_attribute_atthasdef - 1] = true;
122 :
384 tgl 123 CBC 1717 : if (rel->rd_rel->relkind == RELKIND_RELATION && add_column_mode &&
384 tgl 124 ECB : !attgenerated)
125 : {
384 tgl 126 CBC 220 : expr2 = expression_planner(expr2);
384 tgl 127 GIC 220 : estate = CreateExecutorState();
384 tgl 128 CBC 220 : exprState = ExecPrepareExpr(expr2, estate);
384 tgl 129 GIC 220 : econtext = GetPerTupleExprContext(estate);
130 :
384 tgl 131 CBC 220 : missingval = ExecEvalExpr(exprState, econtext,
132 : &missingIsNull);
384 tgl 133 ECB :
384 tgl 134 GIC 220 : FreeExecutorState(estate);
384 tgl 135 ECB :
384 tgl 136 GIC 220 : defAttStruct = TupleDescAttr(rel->rd_att, attnum - 1);
137 :
384 tgl 138 GBC 220 : if (missingIsNull)
139 : {
140 : /* if the default evaluates to NULL, just store a NULL array */
384 tgl 141 UIC 0 : missingval = (Datum) 0;
142 : }
384 tgl 143 ECB : else
144 : {
145 : /* otherwise make a one-element array of the value */
384 tgl 146 CBC 220 : missingval = PointerGetDatum(construct_array(&missingval,
384 tgl 147 ECB : 1,
148 : defAttStruct->atttypid,
384 tgl 149 GIC 220 : defAttStruct->attlen,
150 220 : defAttStruct->attbyval,
384 tgl 151 CBC 220 : defAttStruct->attalign));
384 tgl 152 ECB : }
153 :
384 tgl 154 CBC 220 : valuesAtt[Anum_pg_attribute_atthasmissing - 1] = !missingIsNull;
155 220 : replacesAtt[Anum_pg_attribute_atthasmissing - 1] = true;
384 tgl 156 GIC 220 : valuesAtt[Anum_pg_attribute_attmissingval - 1] = missingval;
384 tgl 157 CBC 220 : replacesAtt[Anum_pg_attribute_attmissingval - 1] = true;
384 tgl 158 GIC 220 : nullsAtt[Anum_pg_attribute_attmissingval - 1] = missingIsNull;
159 : }
384 tgl 160 CBC 1717 : atttup = heap_modify_tuple(atttup, RelationGetDescr(attrrel),
161 : valuesAtt, nullsAtt, replacesAtt);
384 tgl 162 ECB :
384 tgl 163 CBC 1717 : CatalogTupleUpdate(attrrel, &atttup->t_self, atttup);
164 :
165 1717 : if (!missingIsNull)
166 220 : pfree(DatumGetPointer(missingval));
167 : }
384 tgl 168 GIC 1717 : table_close(attrrel, RowExclusiveLock);
169 1717 : 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
384 tgl 174 ECB : * it an internal dependency to prevent the default expression from being
175 : * deleted separately.
176 : */
384 tgl 177 GIC 1717 : colobject.classId = RelationRelationId;
384 tgl 178 CBC 1717 : colobject.objectId = RelationGetRelid(rel);
384 tgl 179 GIC 1717 : colobject.objectSubId = attnum;
180 :
181 1717 : recordDependencyOn(&defobject, &colobject,
182 : attgenerated ? DEPENDENCY_INTERNAL : DEPENDENCY_AUTO);
183 :
384 tgl 184 ECB : /*
185 : * Record dependencies on objects used in the expression, too.
186 : */
384 tgl 187 GIC 1717 : recordDependencyOnSingleRelExpr(&defobject, expr, RelationGetRelid(rel),
188 : DEPENDENCY_NORMAL,
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
384 tgl 196 ECB : * callee should check existence of an older version of this entry if it
197 : * needs to distinguish.
198 : */
384 tgl 199 CBC 1717 : InvokeObjectPostCreateHookArg(AttrDefaultRelationId,
200 : RelationGetRelid(rel), attnum, is_internal);
201 :
384 tgl 202 GIC 1717 : return attrdefOid;
203 : }
204 :
205 :
206 : /*
207 : * RemoveAttrDefault
208 : *
209 : * If the specified relation/attribute has a default, remove it.
384 tgl 210 ECB : * (If no default, raise error if complain is true, else return quietly.)
211 : */
212 : void
384 tgl 213 GIC 308 : RemoveAttrDefault(Oid relid, AttrNumber attnum,
214 : DropBehavior behavior, bool complain, bool internal)
215 : {
216 : Relation attrdef_rel;
384 tgl 217 ECB : ScanKeyData scankeys[2];
218 : SysScanDesc scan;
219 : HeapTuple tuple;
384 tgl 220 GIC 308 : bool found = false;
384 tgl 221 ECB :
384 tgl 222 GIC 308 : attrdef_rel = table_open(AttrDefaultRelationId, RowExclusiveLock);
223 :
224 308 : ScanKeyInit(&scankeys[0],
384 tgl 225 ECB : Anum_pg_attrdef_adrelid,
226 : BTEqualStrategyNumber, F_OIDEQ,
227 : ObjectIdGetDatum(relid));
384 tgl 228 GIC 308 : ScanKeyInit(&scankeys[1],
229 : Anum_pg_attrdef_adnum,
384 tgl 230 ECB : BTEqualStrategyNumber, F_INT2EQ,
231 : Int16GetDatum(attnum));
232 :
384 tgl 233 GIC 308 : scan = systable_beginscan(attrdef_rel, AttrDefaultIndexId, true,
384 tgl 234 ECB : NULL, 2, scankeys);
235 :
236 : /* There should be at most one matching tuple, but we loop anyway */
384 tgl 237 CBC 505 : while (HeapTupleIsValid(tuple = systable_getnext(scan)))
238 : {
384 tgl 239 ECB : ObjectAddress object;
384 tgl 240 CBC 197 : Form_pg_attrdef attrtuple = (Form_pg_attrdef) GETSTRUCT(tuple);
384 tgl 241 ECB :
384 tgl 242 GIC 197 : object.classId = AttrDefaultRelationId;
384 tgl 243 CBC 197 : object.objectId = attrtuple->oid;
384 tgl 244 GIC 197 : object.objectSubId = 0;
245 :
384 tgl 246 CBC 197 : performDeletion(&object, behavior,
247 : internal ? PERFORM_DELETION_INTERNAL : 0);
248 :
249 197 : found = true;
384 tgl 250 ECB : }
251 :
384 tgl 252 CBC 308 : systable_endscan(scan);
384 tgl 253 GBC 308 : table_close(attrdef_rel, RowExclusiveLock);
254 :
384 tgl 255 CBC 308 : if (complain && !found)
384 tgl 256 UIC 0 : elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
257 : relid, attnum);
384 tgl 258 GIC 308 : }
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,
384 tgl 265 ECB : * not directly.
266 : */
267 : void
384 tgl 268 GIC 1296 : RemoveAttrDefaultById(Oid attrdefId)
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;
384 tgl 277 ECB : AttrNumber myattnum;
278 :
279 : /* Grab an appropriate lock on the pg_attrdef relation */
384 tgl 280 CBC 1296 : attrdef_rel = table_open(AttrDefaultRelationId, RowExclusiveLock);
281 :
282 : /* Find the pg_attrdef tuple */
384 tgl 283 GIC 1296 : ScanKeyInit(&scankeys[0],
284 : Anum_pg_attrdef_oid,
384 tgl 285 ECB : BTEqualStrategyNumber, F_OIDEQ,
286 : ObjectIdGetDatum(attrdefId));
287 :
384 tgl 288 CBC 1296 : scan = systable_beginscan(attrdef_rel, AttrDefaultOidIndexId, true,
384 tgl 289 ECB : NULL, 1, scankeys);
384 tgl 290 EUB :
384 tgl 291 GIC 1296 : tuple = systable_getnext(scan);
384 tgl 292 CBC 1296 : if (!HeapTupleIsValid(tuple))
384 tgl 293 LBC 0 : elog(ERROR, "could not find tuple for attrdef %u", attrdefId);
294 :
384 tgl 295 GIC 1296 : myrelid = ((Form_pg_attrdef) GETSTRUCT(tuple))->adrelid;
384 tgl 296 CBC 1296 : myattnum = ((Form_pg_attrdef) GETSTRUCT(tuple))->adnum;
297 :
298 : /* Get an exclusive lock on the relation owning the attribute */
299 1296 : myrel = relation_open(myrelid, AccessExclusiveLock);
300 :
384 tgl 301 ECB : /* Now we can delete the pg_attrdef row */
384 tgl 302 CBC 1296 : CatalogTupleDelete(attrdef_rel, &tuple->t_self);
303 :
384 tgl 304 GIC 1296 : systable_endscan(scan);
384 tgl 305 CBC 1296 : table_close(attrdef_rel, RowExclusiveLock);
306 :
384 tgl 307 ECB : /* Fix the pg_attribute row */
384 tgl 308 GIC 1296 : attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
309 :
384 tgl 310 CBC 1296 : tuple = SearchSysCacheCopy2(ATTNUM,
384 tgl 311 EUB : ObjectIdGetDatum(myrelid),
312 : Int16GetDatum(myattnum));
384 tgl 313 GIC 1296 : if (!HeapTupleIsValid(tuple)) /* shouldn't happen */
384 tgl 314 LBC 0 : elog(ERROR, "cache lookup failed for attribute %d of relation %u",
315 : myattnum, myrelid);
384 tgl 316 ECB :
384 tgl 317 GIC 1296 : ((Form_pg_attribute) GETSTRUCT(tuple))->atthasdef = false;
318 :
319 1296 : CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
320 :
321 : /*
384 tgl 322 ECB : * Our update of the pg_attribute row will force a relcache rebuild, so
323 : * there's nothing else to do here.
324 : */
384 tgl 325 CBC 1296 : table_close(attr_rel, RowExclusiveLock);
384 tgl 326 ECB :
327 : /* Keep lock on attribute's rel until end of xact */
384 tgl 328 GIC 1296 : relation_close(myrel, NoLock);
329 1296 : }
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 : *
384 tgl 336 ECB : * Returns InvalidOid if there is no such pg_attrdef entry.
337 : */
338 : Oid
384 tgl 339 GIC 19 : GetAttrDefaultOid(Oid relid, AttrNumber attnum)
340 : {
341 19 : Oid result = InvalidOid;
342 : Relation attrdef;
343 : ScanKeyData keys[2];
384 tgl 344 ECB : SysScanDesc scan;
345 : HeapTuple tup;
346 :
384 tgl 347 GIC 19 : attrdef = table_open(AttrDefaultRelationId, AccessShareLock);
348 19 : ScanKeyInit(&keys[0],
349 : Anum_pg_attrdef_adrelid,
384 tgl 350 ECB : BTEqualStrategyNumber,
351 : F_OIDEQ,
352 : ObjectIdGetDatum(relid));
384 tgl 353 GIC 19 : ScanKeyInit(&keys[1],
354 : Anum_pg_attrdef_adnum,
384 tgl 355 ECB : BTEqualStrategyNumber,
356 : F_INT2EQ,
357 : Int16GetDatum(attnum));
384 tgl 358 CBC 19 : scan = systable_beginscan(attrdef, AttrDefaultIndexId, true,
359 : NULL, 2, keys);
384 tgl 360 ECB :
384 tgl 361 GIC 19 : if (HeapTupleIsValid(tup = systable_getnext(scan)))
384 tgl 362 ECB : {
384 tgl 363 GIC 19 : Form_pg_attrdef atdform = (Form_pg_attrdef) GETSTRUCT(tup);
364 :
384 tgl 365 CBC 19 : result = atdform->oid;
384 tgl 366 ECB : }
367 :
384 tgl 368 CBC 19 : systable_endscan(scan);
384 tgl 369 GIC 19 : table_close(attrdef, AccessShareLock);
370 :
371 19 : 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 : *
384 tgl 378 ECB : * Returns InvalidObjectAddress if there is no such pg_attrdef entry.
379 : */
380 : ObjectAddress
384 tgl 381 GIC 1284 : GetAttrDefaultColumnAddress(Oid attrdefoid)
382 : {
383 1284 : ObjectAddress result = InvalidObjectAddress;
384 : Relation attrdef;
385 : ScanKeyData skey[1];
384 tgl 386 ECB : SysScanDesc scan;
387 : HeapTuple tup;
388 :
384 tgl 389 GIC 1284 : attrdef = table_open(AttrDefaultRelationId, AccessShareLock);
390 1284 : ScanKeyInit(&skey[0],
384 tgl 391 ECB : Anum_pg_attrdef_oid,
392 : BTEqualStrategyNumber, F_OIDEQ,
393 : ObjectIdGetDatum(attrdefoid));
384 tgl 394 CBC 1284 : scan = systable_beginscan(attrdef, AttrDefaultOidIndexId, true,
395 : NULL, 1, skey);
384 tgl 396 ECB :
384 tgl 397 GIC 1284 : if (HeapTupleIsValid(tup = systable_getnext(scan)))
384 tgl 398 ECB : {
384 tgl 399 CBC 1275 : Form_pg_attrdef atdform = (Form_pg_attrdef) GETSTRUCT(tup);
384 tgl 400 ECB :
384 tgl 401 GIC 1275 : result.classId = RelationRelationId;
402 1275 : result.objectId = atdform->adrelid;
384 tgl 403 CBC 1275 : result.objectSubId = atdform->adnum;
384 tgl 404 ECB : }
405 :
384 tgl 406 CBC 1284 : systable_endscan(scan);
384 tgl 407 GIC 1284 : table_close(attrdef, AccessShareLock);
408 :
409 1284 : return result;
410 : }
|