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