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