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