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