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 5392 : StoreAttrDefault(Relation rel, AttrNumber attnum,
38 : Node *expr, bool is_internal)
39 : {
40 : char *adbin;
41 : Relation adrel;
42 : HeapTuple tuple;
43 : Datum values[Natts_pg_attrdef];
44 : static bool nulls[Natts_pg_attrdef] = {false, false, false, false};
45 : Relation attrrel;
46 : HeapTuple atttup;
47 : Form_pg_attribute attStruct;
48 5392 : Datum valuesAtt[Natts_pg_attribute] = {0};
49 5392 : bool nullsAtt[Natts_pg_attribute] = {0};
50 5392 : bool replacesAtt[Natts_pg_attribute] = {0};
51 : char attgenerated;
52 : Oid attrdefOid;
53 : ObjectAddress colobject,
54 : defobject;
55 :
56 5392 : adrel = table_open(AttrDefaultRelationId, RowExclusiveLock);
57 :
58 : /*
59 : * Flatten expression to string form for storage.
60 : */
61 5392 : adbin = nodeToString(expr);
62 :
63 : /*
64 : * Make the pg_attrdef entry.
65 : */
66 5392 : attrdefOid = GetNewOidWithIndex(adrel, AttrDefaultOidIndexId,
67 : Anum_pg_attrdef_oid);
68 5392 : values[Anum_pg_attrdef_oid - 1] = ObjectIdGetDatum(attrdefOid);
69 5392 : values[Anum_pg_attrdef_adrelid - 1] = ObjectIdGetDatum(RelationGetRelid(rel));
70 5392 : values[Anum_pg_attrdef_adnum - 1] = Int16GetDatum(attnum);
71 5392 : values[Anum_pg_attrdef_adbin - 1] = CStringGetTextDatum(adbin);
72 :
73 5392 : tuple = heap_form_tuple(adrel->rd_att, values, nulls);
74 5392 : CatalogTupleInsert(adrel, tuple);
75 :
76 5392 : defobject.classId = AttrDefaultRelationId;
77 5392 : defobject.objectId = attrdefOid;
78 5392 : defobject.objectSubId = 0;
79 :
80 5392 : table_close(adrel, RowExclusiveLock);
81 :
82 : /* now can free some of the stuff allocated above */
83 5392 : pfree(DatumGetPointer(values[Anum_pg_attrdef_adbin - 1]));
84 5392 : heap_freetuple(tuple);
85 5392 : pfree(adbin);
86 :
87 : /*
88 : * Update the pg_attribute entry for the column to show that a default
89 : * exists.
90 : */
91 5392 : attrrel = table_open(AttributeRelationId, RowExclusiveLock);
92 5392 : atttup = SearchSysCacheCopy2(ATTNUM,
93 : ObjectIdGetDatum(RelationGetRelid(rel)),
94 : Int16GetDatum(attnum));
95 5392 : if (!HeapTupleIsValid(atttup))
96 0 : elog(ERROR, "cache lookup failed for attribute %d of relation %u",
97 : attnum, RelationGetRelid(rel));
98 5392 : attStruct = (Form_pg_attribute) GETSTRUCT(atttup);
99 5392 : attgenerated = attStruct->attgenerated;
100 :
101 5392 : valuesAtt[Anum_pg_attribute_atthasdef - 1] = BoolGetDatum(true);
102 5392 : replacesAtt[Anum_pg_attribute_atthasdef - 1] = true;
103 :
104 5392 : atttup = heap_modify_tuple(atttup, RelationGetDescr(attrrel),
105 : valuesAtt, nullsAtt, replacesAtt);
106 :
107 5392 : CatalogTupleUpdate(attrrel, &atttup->t_self, atttup);
108 :
109 5392 : table_close(attrrel, RowExclusiveLock);
110 5392 : 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 5392 : colobject.classId = RelationRelationId;
119 5392 : colobject.objectId = RelationGetRelid(rel);
120 5392 : colobject.objectSubId = attnum;
121 :
122 5392 : recordDependencyOn(&defobject, &colobject,
123 : attgenerated ? DEPENDENCY_INTERNAL : DEPENDENCY_AUTO);
124 :
125 : /*
126 : * Record dependencies on objects used in the expression, too.
127 : */
128 5392 : 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 5380 : InvokeObjectPostCreateHookArg(AttrDefaultRelationId,
141 : RelationGetRelid(rel), attnum, is_internal);
142 :
143 5380 : return attrdefOid;
144 : }
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 904 : RemoveAttrDefault(Oid relid, AttrNumber attnum,
155 : DropBehavior behavior, bool complain, bool internal)
156 : {
157 : Relation attrdef_rel;
158 : ScanKeyData scankeys[2];
159 : SysScanDesc scan;
160 : HeapTuple tuple;
161 904 : bool found = false;
162 :
163 904 : attrdef_rel = table_open(AttrDefaultRelationId, RowExclusiveLock);
164 :
165 904 : ScanKeyInit(&scankeys[0],
166 : Anum_pg_attrdef_adrelid,
167 : BTEqualStrategyNumber, F_OIDEQ,
168 : ObjectIdGetDatum(relid));
169 904 : ScanKeyInit(&scankeys[1],
170 : Anum_pg_attrdef_adnum,
171 : BTEqualStrategyNumber, F_INT2EQ,
172 : Int16GetDatum(attnum));
173 :
174 904 : scan = systable_beginscan(attrdef_rel, AttrDefaultIndexId, true,
175 : NULL, 2, scankeys);
176 :
177 : /* There should be at most one matching tuple, but we loop anyway */
178 1528 : while (HeapTupleIsValid(tuple = systable_getnext(scan)))
179 : {
180 : ObjectAddress object;
181 624 : Form_pg_attrdef attrtuple = (Form_pg_attrdef) GETSTRUCT(tuple);
182 :
183 624 : object.classId = AttrDefaultRelationId;
184 624 : object.objectId = attrtuple->oid;
185 624 : object.objectSubId = 0;
186 :
187 624 : performDeletion(&object, behavior,
188 : internal ? PERFORM_DELETION_INTERNAL : 0);
189 :
190 624 : found = true;
191 : }
192 :
193 904 : systable_endscan(scan);
194 904 : table_close(attrdef_rel, RowExclusiveLock);
195 :
196 904 : if (complain && !found)
197 0 : elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
198 : relid, attnum);
199 904 : }
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 3864 : RemoveAttrDefaultById(Oid attrdefId)
210 : {
211 : Relation attrdef_rel;
212 : Relation attr_rel;
213 : Relation myrel;
214 : ScanKeyData scankeys[1];
215 : SysScanDesc scan;
216 : HeapTuple tuple;
217 : Oid myrelid;
218 : AttrNumber myattnum;
219 :
220 : /* Grab an appropriate lock on the pg_attrdef relation */
221 3864 : attrdef_rel = table_open(AttrDefaultRelationId, RowExclusiveLock);
222 :
223 : /* Find the pg_attrdef tuple */
224 3864 : ScanKeyInit(&scankeys[0],
225 : Anum_pg_attrdef_oid,
226 : BTEqualStrategyNumber, F_OIDEQ,
227 : ObjectIdGetDatum(attrdefId));
228 :
229 3864 : scan = systable_beginscan(attrdef_rel, AttrDefaultOidIndexId, true,
230 : NULL, 1, scankeys);
231 :
232 3864 : tuple = systable_getnext(scan);
233 3864 : if (!HeapTupleIsValid(tuple))
234 0 : elog(ERROR, "could not find tuple for attrdef %u", attrdefId);
235 :
236 3864 : myrelid = ((Form_pg_attrdef) GETSTRUCT(tuple))->adrelid;
237 3864 : myattnum = ((Form_pg_attrdef) GETSTRUCT(tuple))->adnum;
238 :
239 : /* Get an exclusive lock on the relation owning the attribute */
240 3864 : myrel = relation_open(myrelid, AccessExclusiveLock);
241 :
242 : /* Now we can delete the pg_attrdef row */
243 3864 : CatalogTupleDelete(attrdef_rel, &tuple->t_self);
244 :
245 3864 : systable_endscan(scan);
246 3864 : table_close(attrdef_rel, RowExclusiveLock);
247 :
248 : /* Fix the pg_attribute row */
249 3864 : attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
250 :
251 3864 : tuple = SearchSysCacheCopy2(ATTNUM,
252 : ObjectIdGetDatum(myrelid),
253 : Int16GetDatum(myattnum));
254 3864 : if (!HeapTupleIsValid(tuple)) /* shouldn't happen */
255 0 : elog(ERROR, "cache lookup failed for attribute %d of relation %u",
256 : myattnum, myrelid);
257 :
258 3864 : ((Form_pg_attribute) GETSTRUCT(tuple))->atthasdef = false;
259 :
260 3864 : 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 3864 : table_close(attr_rel, RowExclusiveLock);
267 :
268 : /* Keep lock on attribute's rel until end of xact */
269 3864 : relation_close(myrel, NoLock);
270 3864 : }
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 262 : GetAttrDefaultOid(Oid relid, AttrNumber attnum)
281 : {
282 262 : Oid result = InvalidOid;
283 : Relation attrdef;
284 : ScanKeyData keys[2];
285 : SysScanDesc scan;
286 : HeapTuple tup;
287 :
288 262 : attrdef = table_open(AttrDefaultRelationId, AccessShareLock);
289 262 : ScanKeyInit(&keys[0],
290 : Anum_pg_attrdef_adrelid,
291 : BTEqualStrategyNumber,
292 : F_OIDEQ,
293 : ObjectIdGetDatum(relid));
294 262 : ScanKeyInit(&keys[1],
295 : Anum_pg_attrdef_adnum,
296 : BTEqualStrategyNumber,
297 : F_INT2EQ,
298 : Int16GetDatum(attnum));
299 262 : scan = systable_beginscan(attrdef, AttrDefaultIndexId, true,
300 : NULL, 2, keys);
301 :
302 262 : if (HeapTupleIsValid(tup = systable_getnext(scan)))
303 : {
304 262 : Form_pg_attrdef atdform = (Form_pg_attrdef) GETSTRUCT(tup);
305 :
306 262 : result = atdform->oid;
307 : }
308 :
309 262 : systable_endscan(scan);
310 262 : table_close(attrdef, AccessShareLock);
311 :
312 262 : return result;
313 : }
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 4350 : GetAttrDefaultColumnAddress(Oid attrdefoid)
323 : {
324 4350 : ObjectAddress result = InvalidObjectAddress;
325 : Relation attrdef;
326 : ScanKeyData skey[1];
327 : SysScanDesc scan;
328 : HeapTuple tup;
329 :
330 4350 : attrdef = table_open(AttrDefaultRelationId, AccessShareLock);
331 4350 : ScanKeyInit(&skey[0],
332 : Anum_pg_attrdef_oid,
333 : BTEqualStrategyNumber, F_OIDEQ,
334 : ObjectIdGetDatum(attrdefoid));
335 4350 : scan = systable_beginscan(attrdef, AttrDefaultOidIndexId, true,
336 : NULL, 1, skey);
337 :
338 4350 : if (HeapTupleIsValid(tup = systable_getnext(scan)))
339 : {
340 4332 : Form_pg_attrdef atdform = (Form_pg_attrdef) GETSTRUCT(tup);
341 :
342 4332 : result.classId = RelationRelationId;
343 4332 : result.objectId = atdform->adrelid;
344 4332 : result.objectSubId = atdform->adnum;
345 : }
346 :
347 4350 : systable_endscan(scan);
348 4350 : table_close(attrdef, AccessShareLock);
349 :
350 4350 : return result;
351 : }
|