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