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