Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * amcmds.c
4 : : * Routines for SQL commands that manipulate access methods.
5 : : *
6 : : * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
7 : : * Portions Copyright (c) 1994, Regents of the University of California
8 : : *
9 : : *
10 : : * IDENTIFICATION
11 : : * src/backend/commands/amcmds.c
12 : : *-------------------------------------------------------------------------
13 : : */
14 : : #include "postgres.h"
15 : :
16 : : #include "access/htup_details.h"
17 : : #include "access/table.h"
18 : : #include "catalog/catalog.h"
19 : : #include "catalog/dependency.h"
20 : : #include "catalog/indexing.h"
21 : : #include "catalog/objectaccess.h"
22 : : #include "catalog/pg_am.h"
23 : : #include "catalog/pg_proc.h"
24 : : #include "catalog/pg_type.h"
25 : : #include "commands/defrem.h"
26 : : #include "miscadmin.h"
27 : : #include "parser/parse_func.h"
28 : : #include "utils/builtins.h"
29 : : #include "utils/lsyscache.h"
30 : : #include "utils/rel.h"
31 : : #include "utils/syscache.h"
32 : :
33 : :
34 : : static Oid lookup_am_handler_func(List *handler_name, char amtype);
35 : : static const char *get_am_type_string(char amtype);
36 : :
37 : :
38 : : /*
39 : : * CreateAccessMethod
40 : : * Registers a new access method.
41 : : */
42 : : ObjectAddress
2944 alvherre@alvh.no-ip. 43 :CBC 30 : CreateAccessMethod(CreateAmStmt *stmt)
44 : : {
45 : : Relation rel;
46 : : ObjectAddress myself;
47 : : ObjectAddress referenced;
48 : : Oid amoid;
49 : : Oid amhandler;
50 : : bool nulls[Natts_pg_am];
51 : : Datum values[Natts_pg_am];
52 : : HeapTuple tup;
53 : :
1910 andres@anarazel.de 54 : 30 : rel = table_open(AccessMethodRelationId, RowExclusiveLock);
55 : :
56 : : /* Must be superuser */
2944 alvherre@alvh.no-ip. 57 [ - + ]: 30 : if (!superuser())
2944 alvherre@alvh.no-ip. 58 [ # # ]:UBC 0 : ereport(ERROR,
59 : : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
60 : : errmsg("permission denied to create access method \"%s\"",
61 : : stmt->amname),
62 : : errhint("Must be superuser to create an access method.")));
63 : :
64 : : /* Check if name is used */
1789 tgl@sss.pgh.pa.us 65 :CBC 30 : amoid = GetSysCacheOid1(AMNAME, Anum_pg_am_oid,
66 : : CStringGetDatum(stmt->amname));
2944 alvherre@alvh.no-ip. 67 [ - + ]: 30 : if (OidIsValid(amoid))
68 : : {
2944 alvherre@alvh.no-ip. 69 [ # # ]:UBC 0 : ereport(ERROR,
70 : : (errcode(ERRCODE_DUPLICATE_OBJECT),
71 : : errmsg("access method \"%s\" already exists",
72 : : stmt->amname)));
73 : : }
74 : :
75 : : /*
76 : : * Get the handler function oid, verifying the AM type while at it.
77 : : */
1866 andres@anarazel.de 78 :CBC 30 : amhandler = lookup_am_handler_func(stmt->handler_name, stmt->amtype);
79 : :
80 : : /*
81 : : * Insert tuple into pg_am.
82 : : */
2944 alvherre@alvh.no-ip. 83 : 18 : memset(values, 0, sizeof(values));
84 : 18 : memset(nulls, false, sizeof(nulls));
85 : :
1972 andres@anarazel.de 86 : 18 : amoid = GetNewOidWithIndex(rel, AmOidIndexId, Anum_pg_am_oid);
87 : 18 : values[Anum_pg_am_oid - 1] = ObjectIdGetDatum(amoid);
2944 alvherre@alvh.no-ip. 88 : 18 : values[Anum_pg_am_amname - 1] =
89 : 18 : DirectFunctionCall1(namein, CStringGetDatum(stmt->amname));
90 : 18 : values[Anum_pg_am_amhandler - 1] = ObjectIdGetDatum(amhandler);
91 : 18 : values[Anum_pg_am_amtype - 1] = CharGetDatum(stmt->amtype);
92 : :
93 : 18 : tup = heap_form_tuple(RelationGetDescr(rel), values, nulls);
94 : :
1972 andres@anarazel.de 95 : 18 : CatalogTupleInsert(rel, tup);
2944 alvherre@alvh.no-ip. 96 : 18 : heap_freetuple(tup);
97 : :
98 : 18 : myself.classId = AccessMethodRelationId;
99 : 18 : myself.objectId = amoid;
100 : 18 : myself.objectSubId = 0;
101 : :
102 : : /* Record dependency on handler function */
103 : 18 : referenced.classId = ProcedureRelationId;
104 : 18 : referenced.objectId = amhandler;
105 : 18 : referenced.objectSubId = 0;
106 : :
107 : 18 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
108 : :
109 : 18 : recordDependencyOnCurrentExtension(&myself, false);
110 : :
1422 michael@paquier.xyz 111 [ - + ]: 18 : InvokeObjectPostCreateHook(AccessMethodRelationId, amoid, 0);
112 : :
1910 andres@anarazel.de 113 : 18 : table_close(rel, RowExclusiveLock);
114 : :
2944 alvherre@alvh.no-ip. 115 : 18 : return myself;
116 : : }
117 : :
118 : : /*
119 : : * get_am_type_oid
120 : : * Worker for various get_am_*_oid variants
121 : : *
122 : : * If missing_ok is false, throw an error if access method not found. If
123 : : * true, just return InvalidOid.
124 : : *
125 : : * If amtype is not '\0', an error is raised if the AM found is not of the
126 : : * given type.
127 : : */
128 : : static Oid
129 : 20381 : get_am_type_oid(const char *amname, char amtype, bool missing_ok)
130 : : {
131 : : HeapTuple tup;
132 : 20381 : Oid oid = InvalidOid;
133 : :
134 : 20381 : tup = SearchSysCache1(AMNAME, CStringGetDatum(amname));
135 [ + + ]: 20381 : if (HeapTupleIsValid(tup))
136 : : {
137 : 20327 : Form_pg_am amform = (Form_pg_am) GETSTRUCT(tup);
138 : :
139 [ + + ]: 20327 : if (amtype != '\0' &&
140 [ + + ]: 20304 : amform->amtype != amtype)
141 [ + - ]: 6 : ereport(ERROR,
142 : : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
143 : : errmsg("access method \"%s\" is not of type %s",
144 : : NameStr(amform->amname),
145 : : get_am_type_string(amtype))));
146 : :
1972 andres@anarazel.de 147 : 20321 : oid = amform->oid;
2944 alvherre@alvh.no-ip. 148 : 20321 : ReleaseSysCache(tup);
149 : : }
150 : :
151 [ + + + + ]: 20375 : if (!OidIsValid(oid) && !missing_ok)
152 [ + - ]: 48 : ereport(ERROR,
153 : : (errcode(ERRCODE_UNDEFINED_OBJECT),
154 : : errmsg("access method \"%s\" does not exist", amname)));
155 : 20327 : return oid;
156 : : }
157 : :
158 : : /*
159 : : * get_index_am_oid - given an access method name, look up its OID
160 : : * and verify it corresponds to an index AM.
161 : : */
162 : : Oid
163 : 4448 : get_index_am_oid(const char *amname, bool missing_ok)
164 : : {
165 : 4448 : return get_am_type_oid(amname, AMTYPE_INDEX, missing_ok);
166 : : }
167 : :
168 : : /*
169 : : * get_table_am_oid - given an access method name, look up its OID
170 : : * and verify it corresponds to an table AM.
171 : : */
172 : : Oid
1837 andres@anarazel.de 173 : 15901 : get_table_am_oid(const char *amname, bool missing_ok)
174 : : {
175 : 15901 : return get_am_type_oid(amname, AMTYPE_TABLE, missing_ok);
176 : : }
177 : :
178 : : /*
179 : : * get_am_oid - given an access method name, look up its OID.
180 : : * The type is not checked.
181 : : */
182 : : Oid
2944 alvherre@alvh.no-ip. 183 : 32 : get_am_oid(const char *amname, bool missing_ok)
184 : : {
185 : 32 : return get_am_type_oid(amname, '\0', missing_ok);
186 : : }
187 : :
188 : : /*
189 : : * get_am_name - given an access method OID, look up its name.
190 : : */
191 : : char *
192 : 42 : get_am_name(Oid amOid)
193 : : {
194 : : HeapTuple tup;
195 : 42 : char *result = NULL;
196 : :
197 : 42 : tup = SearchSysCache1(AMOID, ObjectIdGetDatum(amOid));
198 [ + + ]: 42 : if (HeapTupleIsValid(tup))
199 : : {
200 : 36 : Form_pg_am amform = (Form_pg_am) GETSTRUCT(tup);
201 : :
202 : 36 : result = pstrdup(NameStr(amform->amname));
203 : 36 : ReleaseSysCache(tup);
204 : : }
205 : 42 : return result;
206 : : }
207 : :
208 : : /*
209 : : * Convert single-character access method type into string for error reporting.
210 : : */
211 : : static const char *
212 : 6 : get_am_type_string(char amtype)
213 : : {
214 [ - + - ]: 6 : switch (amtype)
215 : : {
2944 alvherre@alvh.no-ip. 216 :UBC 0 : case AMTYPE_INDEX:
217 : 0 : return "INDEX";
1866 andres@anarazel.de 218 :CBC 6 : case AMTYPE_TABLE:
219 : 6 : return "TABLE";
2944 alvherre@alvh.no-ip. 220 :UBC 0 : default:
221 : : /* shouldn't happen */
222 [ # # ]: 0 : elog(ERROR, "invalid access method type '%c'", amtype);
223 : : return NULL; /* keep compiler quiet */
224 : : }
225 : : }
226 : :
227 : : /*
228 : : * Convert a handler function name to an Oid. If the return type of the
229 : : * function doesn't match the given AM type, an error is raised.
230 : : *
231 : : * This function either return valid function Oid or throw an error.
232 : : */
233 : : static Oid
1866 andres@anarazel.de 234 :CBC 30 : lookup_am_handler_func(List *handler_name, char amtype)
235 : : {
236 : : Oid handlerOid;
237 : 30 : Oid funcargtypes[1] = {INTERNALOID};
238 : 30 : Oid expectedType = InvalidOid;
239 : :
2944 alvherre@alvh.no-ip. 240 [ - + ]: 30 : if (handler_name == NIL)
2944 alvherre@alvh.no-ip. 241 [ # # ]:UBC 0 : ereport(ERROR,
242 : : (errcode(ERRCODE_UNDEFINED_FUNCTION),
243 : : errmsg("handler function is not specified")));
244 : :
245 : : /* handlers have one argument of type internal */
2944 alvherre@alvh.no-ip. 246 :CBC 30 : handlerOid = LookupFuncName(handler_name, 1, funcargtypes, false);
247 : :
248 : : /* check that handler has the correct return type */
249 [ + + - ]: 24 : switch (amtype)
250 : : {
251 : 13 : case AMTYPE_INDEX:
1866 andres@anarazel.de 252 : 13 : expectedType = INDEX_AM_HANDLEROID;
253 : 13 : break;
254 : 11 : case AMTYPE_TABLE:
255 : 11 : expectedType = TABLE_AM_HANDLEROID;
2944 alvherre@alvh.no-ip. 256 : 11 : break;
2944 alvherre@alvh.no-ip. 257 :UBC 0 : default:
258 [ # # ]: 0 : elog(ERROR, "unrecognized access method type \"%c\"", amtype);
259 : : }
260 : :
1866 andres@anarazel.de 261 [ + + ]:CBC 24 : if (get_func_rettype(handlerOid) != expectedType)
262 [ + - ]: 6 : ereport(ERROR,
263 : : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
264 : : errmsg("function %s must return type %s",
265 : : get_func_name(handlerOid),
266 : : format_type_extended(expectedType, -1, 0))));
267 : :
2944 alvherre@alvh.no-ip. 268 : 18 : return handlerOid;
269 : : }
|