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
43 62 : 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 :
54 62 : rel = table_open(AccessMethodRelationId, RowExclusiveLock);
55 :
56 : /* Must be superuser */
57 62 : if (!superuser())
58 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 */
65 62 : amoid = GetSysCacheOid1(AMNAME, Anum_pg_am_oid,
66 : CStringGetDatum(stmt->amname));
67 62 : if (OidIsValid(amoid))
68 : {
69 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 : */
78 62 : amhandler = lookup_am_handler_func(stmt->handler_name, stmt->amtype);
79 :
80 : /*
81 : * Insert tuple into pg_am.
82 : */
83 38 : memset(values, 0, sizeof(values));
84 38 : memset(nulls, false, sizeof(nulls));
85 :
86 38 : amoid = GetNewOidWithIndex(rel, AmOidIndexId, Anum_pg_am_oid);
87 38 : values[Anum_pg_am_oid - 1] = ObjectIdGetDatum(amoid);
88 38 : values[Anum_pg_am_amname - 1] =
89 38 : DirectFunctionCall1(namein, CStringGetDatum(stmt->amname));
90 38 : values[Anum_pg_am_amhandler - 1] = ObjectIdGetDatum(amhandler);
91 38 : values[Anum_pg_am_amtype - 1] = CharGetDatum(stmt->amtype);
92 :
93 38 : tup = heap_form_tuple(RelationGetDescr(rel), values, nulls);
94 :
95 38 : CatalogTupleInsert(rel, tup);
96 38 : heap_freetuple(tup);
97 :
98 38 : myself.classId = AccessMethodRelationId;
99 38 : myself.objectId = amoid;
100 38 : myself.objectSubId = 0;
101 :
102 : /* Record dependency on handler function */
103 38 : referenced.classId = ProcedureRelationId;
104 38 : referenced.objectId = amhandler;
105 38 : referenced.objectSubId = 0;
106 :
107 38 : recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
108 :
109 38 : recordDependencyOnCurrentExtension(&myself, false);
110 :
111 38 : InvokeObjectPostCreateHook(AccessMethodRelationId, amoid, 0);
112 :
113 38 : table_close(rel, RowExclusiveLock);
114 :
115 38 : 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 41832 : get_am_type_oid(const char *amname, char amtype, bool missing_ok)
130 : {
131 : HeapTuple tup;
132 41832 : Oid oid = InvalidOid;
133 :
134 41832 : tup = SearchSysCache1(AMNAME, CStringGetDatum(amname));
135 41832 : if (HeapTupleIsValid(tup))
136 : {
137 41724 : Form_pg_am amform = (Form_pg_am) GETSTRUCT(tup);
138 :
139 41724 : if (amtype != '\0' &&
140 41678 : amform->amtype != amtype)
141 12 : 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 :
147 41712 : oid = amform->oid;
148 41712 : ReleaseSysCache(tup);
149 : }
150 :
151 41820 : if (!OidIsValid(oid) && !missing_ok)
152 96 : ereport(ERROR,
153 : (errcode(ERRCODE_UNDEFINED_OBJECT),
154 : errmsg("access method \"%s\" does not exist", amname)));
155 41724 : 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 10204 : get_index_am_oid(const char *amname, bool missing_ok)
164 : {
165 10204 : 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 a table AM.
171 : */
172 : Oid
173 31564 : get_table_am_oid(const char *amname, bool missing_ok)
174 : {
175 31564 : 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
183 64 : get_am_oid(const char *amname, bool missing_ok)
184 : {
185 64 : 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 84 : get_am_name(Oid amOid)
193 : {
194 : HeapTuple tup;
195 84 : char *result = NULL;
196 :
197 84 : tup = SearchSysCache1(AMOID, ObjectIdGetDatum(amOid));
198 84 : if (HeapTupleIsValid(tup))
199 : {
200 72 : Form_pg_am amform = (Form_pg_am) GETSTRUCT(tup);
201 :
202 72 : result = pstrdup(NameStr(amform->amname));
203 72 : ReleaseSysCache(tup);
204 : }
205 84 : return result;
206 : }
207 :
208 : /*
209 : * Convert single-character access method type into string for error reporting.
210 : */
211 : static const char *
212 12 : get_am_type_string(char amtype)
213 : {
214 12 : switch (amtype)
215 : {
216 0 : case AMTYPE_INDEX:
217 0 : return "INDEX";
218 12 : case AMTYPE_TABLE:
219 12 : return "TABLE";
220 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
234 62 : lookup_am_handler_func(List *handler_name, char amtype)
235 : {
236 : Oid handlerOid;
237 62 : Oid funcargtypes[1] = {INTERNALOID};
238 62 : Oid expectedType = InvalidOid;
239 :
240 62 : if (handler_name == NIL)
241 0 : ereport(ERROR,
242 : (errcode(ERRCODE_UNDEFINED_FUNCTION),
243 : errmsg("handler function is not specified")));
244 :
245 : /* handlers have one argument of type internal */
246 62 : handlerOid = LookupFuncName(handler_name, 1, funcargtypes, false);
247 :
248 : /* check that handler has the correct return type */
249 50 : switch (amtype)
250 : {
251 28 : case AMTYPE_INDEX:
252 28 : expectedType = INDEX_AM_HANDLEROID;
253 28 : break;
254 22 : case AMTYPE_TABLE:
255 22 : expectedType = TABLE_AM_HANDLEROID;
256 22 : break;
257 0 : default:
258 0 : elog(ERROR, "unrecognized access method type \"%c\"", amtype);
259 : }
260 :
261 50 : if (get_func_rettype(handlerOid) != expectedType)
262 12 : 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 :
268 38 : return handlerOid;
269 : }
|