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-2025, 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/relation.h"
18 : : #include "access/table.h"
19 : : #include "catalog/dependency.h"
20 : : #include "catalog/indexing.h"
21 : : #include "catalog/objectaccess.h"
22 : : #include "catalog/pg_attrdef.h"
23 : : #include "utils/builtins.h"
24 : : #include "utils/fmgroids.h"
25 : : #include "utils/rel.h"
26 : : #include "utils/syscache.h"
27 : :
28 : :
29 : : /*
30 : : * Store a default expression for column attnum of relation rel.
31 : : *
32 : : * Returns the OID of the new pg_attrdef tuple.
33 : : */
34 : : Oid
1265 tgl@sss.pgh.pa.us 35 :CBC 2386 : StoreAttrDefault(Relation rel, AttrNumber attnum,
36 : : Node *expr, bool is_internal)
37 : : {
38 : : char *adbin;
39 : : Relation adrel;
40 : : HeapTuple tuple;
41 : : Datum values[Natts_pg_attrdef];
42 : : static bool nulls[Natts_pg_attrdef] = {false, false, false, false};
43 : : Relation attrrel;
44 : : HeapTuple atttup;
45 : : Form_pg_attribute attStruct;
187 46 : 2386 : Datum valuesAtt[Natts_pg_attribute] = {0};
47 : 2386 : bool nullsAtt[Natts_pg_attribute] = {0};
48 : 2386 : bool replacesAtt[Natts_pg_attribute] = {0};
49 : : char attgenerated;
50 : : Oid attrdefOid;
51 : : ObjectAddress colobject,
52 : : defobject;
53 : :
1265 54 : 2386 : adrel = table_open(AttrDefaultRelationId, RowExclusiveLock);
55 : :
56 : : /*
57 : : * Flatten expression to string form for storage.
58 : : */
59 : 2386 : adbin = nodeToString(expr);
60 : :
61 : : /*
62 : : * Make the pg_attrdef entry.
63 : : */
64 : 2386 : attrdefOid = GetNewOidWithIndex(adrel, AttrDefaultOidIndexId,
65 : : Anum_pg_attrdef_oid);
66 : 2386 : values[Anum_pg_attrdef_oid - 1] = ObjectIdGetDatum(attrdefOid);
187 67 : 2386 : values[Anum_pg_attrdef_adrelid - 1] = ObjectIdGetDatum(RelationGetRelid(rel));
68 : 2386 : values[Anum_pg_attrdef_adnum - 1] = Int16GetDatum(attnum);
1265 69 : 2386 : values[Anum_pg_attrdef_adbin - 1] = CStringGetTextDatum(adbin);
70 : :
71 : 2386 : tuple = heap_form_tuple(adrel->rd_att, values, nulls);
72 : 2386 : CatalogTupleInsert(adrel, tuple);
73 : :
74 : 2386 : defobject.classId = AttrDefaultRelationId;
75 : 2386 : defobject.objectId = attrdefOid;
76 : 2386 : defobject.objectSubId = 0;
77 : :
78 : 2386 : table_close(adrel, RowExclusiveLock);
79 : :
80 : : /* now can free some of the stuff allocated above */
81 : 2386 : pfree(DatumGetPointer(values[Anum_pg_attrdef_adbin - 1]));
82 : 2386 : heap_freetuple(tuple);
83 : 2386 : pfree(adbin);
84 : :
85 : : /*
86 : : * Update the pg_attribute entry for the column to show that a default
87 : : * exists.
88 : : */
89 : 2386 : attrrel = table_open(AttributeRelationId, RowExclusiveLock);
90 : 2386 : atttup = SearchSysCacheCopy2(ATTNUM,
91 : : ObjectIdGetDatum(RelationGetRelid(rel)),
92 : : Int16GetDatum(attnum));
93 [ - + ]: 2386 : if (!HeapTupleIsValid(atttup))
1265 tgl@sss.pgh.pa.us 94 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for attribute %d of relation %u",
95 : : attnum, RelationGetRelid(rel));
1265 tgl@sss.pgh.pa.us 96 :CBC 2386 : attStruct = (Form_pg_attribute) GETSTRUCT(atttup);
97 : 2386 : attgenerated = attStruct->attgenerated;
98 : :
187 99 : 2386 : valuesAtt[Anum_pg_attribute_atthasdef - 1] = BoolGetDatum(true);
100 : 2386 : replacesAtt[Anum_pg_attribute_atthasdef - 1] = true;
101 : :
102 : 2386 : atttup = heap_modify_tuple(atttup, RelationGetDescr(attrrel),
103 : : valuesAtt, nullsAtt, replacesAtt);
104 : :
105 : 2386 : CatalogTupleUpdate(attrrel, &atttup->t_self, atttup);
106 : :
1265 107 : 2386 : table_close(attrrel, RowExclusiveLock);
108 : 2386 : heap_freetuple(atttup);
109 : :
110 : : /*
111 : : * Make a dependency so that the pg_attrdef entry goes away if the column
112 : : * (or whole table) is deleted. In the case of a generated column, make
113 : : * it an internal dependency to prevent the default expression from being
114 : : * deleted separately.
115 : : */
116 : 2386 : colobject.classId = RelationRelationId;
117 : 2386 : colobject.objectId = RelationGetRelid(rel);
118 : 2386 : colobject.objectSubId = attnum;
119 : :
120 [ + + ]: 2386 : recordDependencyOn(&defobject, &colobject,
121 : : attgenerated ? DEPENDENCY_INTERNAL : DEPENDENCY_AUTO);
122 : :
123 : : /*
124 : : * Record dependencies on objects used in the expression, too.
125 : : */
126 : 2386 : recordDependencyOnSingleRelExpr(&defobject, expr, RelationGetRelid(rel),
127 : : DEPENDENCY_NORMAL,
128 : : DEPENDENCY_NORMAL, false);
129 : :
130 : : /*
131 : : * Post creation hook for attribute defaults.
132 : : *
133 : : * XXX. ALTER TABLE ALTER COLUMN SET/DROP DEFAULT is implemented with a
134 : : * couple of deletion/creation of the attribute's default entry, so the
135 : : * callee should check existence of an older version of this entry if it
136 : : * needs to distinguish.
137 : : */
138 [ - + ]: 2380 : InvokeObjectPostCreateHookArg(AttrDefaultRelationId,
139 : : RelationGetRelid(rel), attnum, is_internal);
140 : :
141 : 2380 : return attrdefOid;
142 : : }
143 : :
144 : :
145 : : /*
146 : : * RemoveAttrDefault
147 : : *
148 : : * If the specified relation/attribute has a default, remove it.
149 : : * (If no default, raise error if complain is true, else return quietly.)
150 : : */
151 : : void
152 : 448 : RemoveAttrDefault(Oid relid, AttrNumber attnum,
153 : : DropBehavior behavior, bool complain, bool internal)
154 : : {
155 : : Relation attrdef_rel;
156 : : ScanKeyData scankeys[2];
157 : : SysScanDesc scan;
158 : : HeapTuple tuple;
159 : 448 : bool found = false;
160 : :
161 : 448 : attrdef_rel = table_open(AttrDefaultRelationId, RowExclusiveLock);
162 : :
163 : 448 : ScanKeyInit(&scankeys[0],
164 : : Anum_pg_attrdef_adrelid,
165 : : BTEqualStrategyNumber, F_OIDEQ,
166 : : ObjectIdGetDatum(relid));
167 : 448 : ScanKeyInit(&scankeys[1],
168 : : Anum_pg_attrdef_adnum,
169 : : BTEqualStrategyNumber, F_INT2EQ,
170 : : Int16GetDatum(attnum));
171 : :
172 : 448 : scan = systable_beginscan(attrdef_rel, AttrDefaultIndexId, true,
173 : : NULL, 2, scankeys);
174 : :
175 : : /* There should be at most one matching tuple, but we loop anyway */
176 [ + + ]: 756 : while (HeapTupleIsValid(tuple = systable_getnext(scan)))
177 : : {
178 : : ObjectAddress object;
179 : 308 : Form_pg_attrdef attrtuple = (Form_pg_attrdef) GETSTRUCT(tuple);
180 : :
181 : 308 : object.classId = AttrDefaultRelationId;
182 : 308 : object.objectId = attrtuple->oid;
183 : 308 : object.objectSubId = 0;
184 : :
185 : 308 : performDeletion(&object, behavior,
186 : : internal ? PERFORM_DELETION_INTERNAL : 0);
187 : :
188 : 308 : found = true;
189 : : }
190 : :
191 : 448 : systable_endscan(scan);
192 : 448 : table_close(attrdef_rel, RowExclusiveLock);
193 : :
194 [ + + - + ]: 448 : if (complain && !found)
1265 tgl@sss.pgh.pa.us 195 [ # # ]:UBC 0 : elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
196 : : relid, attnum);
1265 tgl@sss.pgh.pa.us 197 :CBC 448 : }
198 : :
199 : : /*
200 : : * RemoveAttrDefaultById
201 : : *
202 : : * Remove a pg_attrdef entry specified by OID. This is the guts of
203 : : * attribute-default removal. Note it should be called via performDeletion,
204 : : * not directly.
205 : : */
206 : : void
207 : 1650 : RemoveAttrDefaultById(Oid attrdefId)
208 : : {
209 : : Relation attrdef_rel;
210 : : Relation attr_rel;
211 : : Relation myrel;
212 : : ScanKeyData scankeys[1];
213 : : SysScanDesc scan;
214 : : HeapTuple tuple;
215 : : Oid myrelid;
216 : : AttrNumber myattnum;
217 : :
218 : : /* Grab an appropriate lock on the pg_attrdef relation */
219 : 1650 : attrdef_rel = table_open(AttrDefaultRelationId, RowExclusiveLock);
220 : :
221 : : /* Find the pg_attrdef tuple */
222 : 1650 : ScanKeyInit(&scankeys[0],
223 : : Anum_pg_attrdef_oid,
224 : : BTEqualStrategyNumber, F_OIDEQ,
225 : : ObjectIdGetDatum(attrdefId));
226 : :
227 : 1650 : scan = systable_beginscan(attrdef_rel, AttrDefaultOidIndexId, true,
228 : : NULL, 1, scankeys);
229 : :
230 : 1650 : tuple = systable_getnext(scan);
231 [ - + ]: 1650 : if (!HeapTupleIsValid(tuple))
1265 tgl@sss.pgh.pa.us 232 [ # # ]:UBC 0 : elog(ERROR, "could not find tuple for attrdef %u", attrdefId);
233 : :
1265 tgl@sss.pgh.pa.us 234 :CBC 1650 : myrelid = ((Form_pg_attrdef) GETSTRUCT(tuple))->adrelid;
235 : 1650 : myattnum = ((Form_pg_attrdef) GETSTRUCT(tuple))->adnum;
236 : :
237 : : /* Get an exclusive lock on the relation owning the attribute */
238 : 1650 : myrel = relation_open(myrelid, AccessExclusiveLock);
239 : :
240 : : /* Now we can delete the pg_attrdef row */
241 : 1650 : CatalogTupleDelete(attrdef_rel, &tuple->t_self);
242 : :
243 : 1650 : systable_endscan(scan);
244 : 1650 : table_close(attrdef_rel, RowExclusiveLock);
245 : :
246 : : /* Fix the pg_attribute row */
247 : 1650 : attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
248 : :
249 : 1650 : tuple = SearchSysCacheCopy2(ATTNUM,
250 : : ObjectIdGetDatum(myrelid),
251 : : Int16GetDatum(myattnum));
252 [ - + ]: 1650 : if (!HeapTupleIsValid(tuple)) /* shouldn't happen */
1265 tgl@sss.pgh.pa.us 253 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for attribute %d of relation %u",
254 : : myattnum, myrelid);
255 : :
1265 tgl@sss.pgh.pa.us 256 :CBC 1650 : ((Form_pg_attribute) GETSTRUCT(tuple))->atthasdef = false;
257 : :
258 : 1650 : CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
259 : :
260 : : /*
261 : : * Our update of the pg_attribute row will force a relcache rebuild, so
262 : : * there's nothing else to do here.
263 : : */
264 : 1650 : table_close(attr_rel, RowExclusiveLock);
265 : :
266 : : /* Keep lock on attribute's rel until end of xact */
267 : 1650 : relation_close(myrel, NoLock);
268 : 1650 : }
269 : :
270 : :
271 : : /*
272 : : * Get the pg_attrdef OID of the default expression for a column
273 : : * identified by relation OID and column number.
274 : : *
275 : : * Returns InvalidOid if there is no such pg_attrdef entry.
276 : : */
277 : : Oid
278 : 127 : GetAttrDefaultOid(Oid relid, AttrNumber attnum)
279 : : {
280 : 127 : Oid result = InvalidOid;
281 : : Relation attrdef;
282 : : ScanKeyData keys[2];
283 : : SysScanDesc scan;
284 : : HeapTuple tup;
285 : :
286 : 127 : attrdef = table_open(AttrDefaultRelationId, AccessShareLock);
287 : 127 : ScanKeyInit(&keys[0],
288 : : Anum_pg_attrdef_adrelid,
289 : : BTEqualStrategyNumber,
290 : : F_OIDEQ,
291 : : ObjectIdGetDatum(relid));
292 : 127 : ScanKeyInit(&keys[1],
293 : : Anum_pg_attrdef_adnum,
294 : : BTEqualStrategyNumber,
295 : : F_INT2EQ,
296 : : Int16GetDatum(attnum));
297 : 127 : scan = systable_beginscan(attrdef, AttrDefaultIndexId, true,
298 : : NULL, 2, keys);
299 : :
300 [ + - ]: 127 : if (HeapTupleIsValid(tup = systable_getnext(scan)))
301 : : {
302 : 127 : Form_pg_attrdef atdform = (Form_pg_attrdef) GETSTRUCT(tup);
303 : :
304 : 127 : result = atdform->oid;
305 : : }
306 : :
307 : 127 : systable_endscan(scan);
308 : 127 : table_close(attrdef, AccessShareLock);
309 : :
310 : 127 : return result;
311 : : }
312 : :
313 : : /*
314 : : * Given a pg_attrdef OID, return the relation OID and column number of
315 : : * the owning column (represented as an ObjectAddress for convenience).
316 : : *
317 : : * Returns InvalidObjectAddress if there is no such pg_attrdef entry.
318 : : */
319 : : ObjectAddress
320 : 1623 : GetAttrDefaultColumnAddress(Oid attrdefoid)
321 : : {
322 : 1623 : ObjectAddress result = InvalidObjectAddress;
323 : : Relation attrdef;
324 : : ScanKeyData skey[1];
325 : : SysScanDesc scan;
326 : : HeapTuple tup;
327 : :
328 : 1623 : attrdef = table_open(AttrDefaultRelationId, AccessShareLock);
329 : 1623 : ScanKeyInit(&skey[0],
330 : : Anum_pg_attrdef_oid,
331 : : BTEqualStrategyNumber, F_OIDEQ,
332 : : ObjectIdGetDatum(attrdefoid));
333 : 1623 : scan = systable_beginscan(attrdef, AttrDefaultOidIndexId, true,
334 : : NULL, 1, skey);
335 : :
336 [ + + ]: 1623 : if (HeapTupleIsValid(tup = systable_getnext(scan)))
337 : : {
338 : 1614 : Form_pg_attrdef atdform = (Form_pg_attrdef) GETSTRUCT(tup);
339 : :
340 : 1614 : result.classId = RelationRelationId;
341 : 1614 : result.objectId = atdform->adrelid;
342 : 1614 : result.objectSubId = atdform->adnum;
343 : : }
344 : :
345 : 1623 : systable_endscan(scan);
346 : 1623 : table_close(attrdef, AccessShareLock);
347 : :
348 : 1623 : return result;
349 : : }
|