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