PostgreSQL Source Code  git master
amcmds.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * amcmds.c
4  * Routines for SQL commands that manipulate access methods.
5  *
6  * Portions Copyright (c) 1996-2020, 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  */
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  rel = table_open(AccessMethodRelationId, RowExclusiveLock);
55 
56  /* Must be super user */
57  if (!superuser())
58  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  amoid = GetSysCacheOid1(AMNAME, Anum_pg_am_oid,
66  CStringGetDatum(stmt->amname));
67  if (OidIsValid(amoid))
68  {
69  ereport(ERROR,
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  amhandler = lookup_am_handler_func(stmt->handler_name, stmt->amtype);
79 
80  /*
81  * Insert tuple into pg_am.
82  */
83  memset(values, 0, sizeof(values));
84  memset(nulls, false, sizeof(nulls));
85 
86  amoid = GetNewOidWithIndex(rel, AmOidIndexId, Anum_pg_am_oid);
87  values[Anum_pg_am_oid - 1] = ObjectIdGetDatum(amoid);
88  values[Anum_pg_am_amname - 1] =
90  values[Anum_pg_am_amhandler - 1] = ObjectIdGetDatum(amhandler);
91  values[Anum_pg_am_amtype - 1] = CharGetDatum(stmt->amtype);
92 
93  tup = heap_form_tuple(RelationGetDescr(rel), values, nulls);
94 
95  CatalogTupleInsert(rel, tup);
96  heap_freetuple(tup);
97 
98  myself.classId = AccessMethodRelationId;
99  myself.objectId = amoid;
100  myself.objectSubId = 0;
101 
102  /* Record dependency on handler function */
103  referenced.classId = ProcedureRelationId;
104  referenced.objectId = amhandler;
105  referenced.objectSubId = 0;
106 
107  recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
108 
109  recordDependencyOnCurrentExtension(&myself, false);
110 
111  InvokeObjectPostCreateHook(AccessMethodRelationId, amoid, 0);
112 
114 
115  return myself;
116 }
117 
118 /*
119  * Guts of access method deletion.
120  */
121 void
123 {
124  Relation relation;
125  HeapTuple tup;
126 
127  if (!superuser())
128  ereport(ERROR,
129  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
130  errmsg("must be superuser to drop access methods")));
131 
132  relation = table_open(AccessMethodRelationId, RowExclusiveLock);
133 
134  tup = SearchSysCache1(AMOID, ObjectIdGetDatum(amOid));
135  if (!HeapTupleIsValid(tup))
136  elog(ERROR, "cache lookup failed for access method %u", amOid);
137 
138  CatalogTupleDelete(relation, &tup->t_self);
139 
140  ReleaseSysCache(tup);
141 
142  table_close(relation, RowExclusiveLock);
143 }
144 
145 /*
146  * get_am_type_oid
147  * Worker for various get_am_*_oid variants
148  *
149  * If missing_ok is false, throw an error if access method not found. If
150  * true, just return InvalidOid.
151  *
152  * If amtype is not '\0', an error is raised if the AM found is not of the
153  * given type.
154  */
155 static Oid
156 get_am_type_oid(const char *amname, char amtype, bool missing_ok)
157 {
158  HeapTuple tup;
159  Oid oid = InvalidOid;
160 
161  tup = SearchSysCache1(AMNAME, CStringGetDatum(amname));
162  if (HeapTupleIsValid(tup))
163  {
164  Form_pg_am amform = (Form_pg_am) GETSTRUCT(tup);
165 
166  if (amtype != '\0' &&
167  amform->amtype != amtype)
168  ereport(ERROR,
169  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
170  errmsg("access method \"%s\" is not of type %s",
171  NameStr(amform->amname),
172  get_am_type_string(amtype))));
173 
174  oid = amform->oid;
175  ReleaseSysCache(tup);
176  }
177 
178  if (!OidIsValid(oid) && !missing_ok)
179  ereport(ERROR,
180  (errcode(ERRCODE_UNDEFINED_OBJECT),
181  errmsg("access method \"%s\" does not exist", amname)));
182  return oid;
183 }
184 
185 /*
186  * get_index_am_oid - given an access method name, look up its OID
187  * and verify it corresponds to an index AM.
188  */
189 Oid
190 get_index_am_oid(const char *amname, bool missing_ok)
191 {
192  return get_am_type_oid(amname, AMTYPE_INDEX, missing_ok);
193 }
194 
195 /*
196  * get_table_am_oid - given an access method name, look up its OID
197  * and verify it corresponds to an table AM.
198  */
199 Oid
200 get_table_am_oid(const char *amname, bool missing_ok)
201 {
202  return get_am_type_oid(amname, AMTYPE_TABLE, missing_ok);
203 }
204 
205 /*
206  * get_am_oid - given an access method name, look up its OID.
207  * The type is not checked.
208  */
209 Oid
210 get_am_oid(const char *amname, bool missing_ok)
211 {
212  return get_am_type_oid(amname, '\0', missing_ok);
213 }
214 
215 /*
216  * get_am_name - given an access method OID name and type, look up its name.
217  */
218 char *
220 {
221  HeapTuple tup;
222  char *result = NULL;
223 
224  tup = SearchSysCache1(AMOID, ObjectIdGetDatum(amOid));
225  if (HeapTupleIsValid(tup))
226  {
227  Form_pg_am amform = (Form_pg_am) GETSTRUCT(tup);
228 
229  result = pstrdup(NameStr(amform->amname));
230  ReleaseSysCache(tup);
231  }
232  return result;
233 }
234 
235 /*
236  * Convert single-character access method type into string for error reporting.
237  */
238 static const char *
239 get_am_type_string(char amtype)
240 {
241  switch (amtype)
242  {
243  case AMTYPE_INDEX:
244  return "INDEX";
245  case AMTYPE_TABLE:
246  return "TABLE";
247  default:
248  /* shouldn't happen */
249  elog(ERROR, "invalid access method type '%c'", amtype);
250  return NULL; /* keep compiler quiet */
251  }
252 }
253 
254 /*
255  * Convert a handler function name to an Oid. If the return type of the
256  * function doesn't match the given AM type, an error is raised.
257  *
258  * This function either return valid function Oid or throw an error.
259  */
260 static Oid
261 lookup_am_handler_func(List *handler_name, char amtype)
262 {
263  Oid handlerOid;
264  Oid funcargtypes[1] = {INTERNALOID};
265  Oid expectedType = InvalidOid;
266 
267  if (handler_name == NIL)
268  ereport(ERROR,
269  (errcode(ERRCODE_UNDEFINED_FUNCTION),
270  errmsg("handler function is not specified")));
271 
272  /* handlers have one argument of type internal */
273  handlerOid = LookupFuncName(handler_name, 1, funcargtypes, false);
274 
275  /* check that handler has the correct return type */
276  switch (amtype)
277  {
278  case AMTYPE_INDEX:
279  expectedType = INDEX_AM_HANDLEROID;
280  break;
281  case AMTYPE_TABLE:
282  expectedType = TABLE_AM_HANDLEROID;
283  break;
284  default:
285  elog(ERROR, "unrecognized access method type \"%c\"", amtype);
286  }
287 
288  if (get_func_rettype(handlerOid) != expectedType)
289  ereport(ERROR,
290  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
291  errmsg("function %s must return type %s",
292  get_func_name(handlerOid),
293  format_type_extended(expectedType, -1, 0))));
294 
295  return handlerOid;
296 }
#define NIL
Definition: pg_list.h:65
Oid GetNewOidWithIndex(Relation relation, Oid indexId, AttrNumber oidcolumn)
Definition: catalog.c:317
Datum namein(PG_FUNCTION_ARGS)
Definition: name.c:48
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:133
int errhint(const char *fmt,...)
Definition: elog.c:1071
#define GETSTRUCT(TUP)
Definition: htup_details.h:655
static const char * get_am_type_string(char amtype)
Definition: amcmds.c:239
static Oid lookup_am_handler_func(List *handler_name, char amtype)
Definition: amcmds.c:261
Oid get_am_oid(const char *amname, bool missing_ok)
Definition: amcmds.c:210
#define InvokeObjectPostCreateHook(classId, objectId, subId)
Definition: objectaccess.h:151
Definition: syscache.h:36
#define RelationGetDescr(relation)
Definition: rel.h:482
#define GetSysCacheOid1(cacheId, oidcol, key1)
Definition: syscache.h:192
char * pstrdup(const char *in)
Definition: mcxt.c:1186
static Oid get_am_type_oid(const char *amname, char amtype, bool missing_ok)
Definition: amcmds.c:156
int errcode(int sqlerrcode)
Definition: elog.c:610
bool superuser(void)
Definition: superuser.c:46
void CatalogTupleDelete(Relation heapRel, ItemPointer tid)
Definition: indexing.c:269
void recordDependencyOn(const ObjectAddress *depender, const ObjectAddress *referenced, DependencyType behavior)
Definition: pg_depend.c:43
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, Datum *values, bool *isnull)
Definition: heaptuple.c:1020
#define DirectFunctionCall1(func, arg1)
Definition: fmgr.h:624
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1338
unsigned int Oid
Definition: postgres_ext.h:31
#define OidIsValid(objectId)
Definition: c.h:644
void RemoveAccessMethodById(Oid amOid)
Definition: amcmds.c:122
Oid get_func_rettype(Oid funcid)
Definition: lsyscache.c:1518
#define ObjectIdGetDatum(X)
Definition: postgres.h:507
#define ERROR
Definition: elog.h:43
char * get_func_name(Oid funcid)
Definition: lsyscache.c:1471
Oid LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool missing_ok)
Definition: parse_func.c:2103
char * amname
Definition: parsenodes.h:2418
ItemPointerData t_self
Definition: htup.h:65
Oid get_index_am_oid(const char *amname, bool missing_ok)
Definition: amcmds.c:190
char * get_am_name(Oid amOid)
Definition: amcmds.c:219
#define RowExclusiveLock
Definition: lockdefs.h:38
#define CStringGetDatum(X)
Definition: postgres.h:578
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:1116
uintptr_t Datum
Definition: postgres.h:367
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:1164
List * handler_name
Definition: parsenodes.h:2419
#define InvalidOid
Definition: postgres_ext.h:36
#define ereport(elevel,...)
Definition: elog.h:144
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
Oid get_table_am_oid(const char *amname, bool missing_ok)
Definition: amcmds.c:200
void recordDependencyOnCurrentExtension(const ObjectAddress *object, bool isReplace)
Definition: pg_depend.c:138
char * format_type_extended(Oid type_oid, int32 typemod, bits16 flags)
Definition: format_type.c:108
#define CharGetDatum(X)
Definition: postgres.h:416
static Datum values[MAXATTR]
Definition: bootstrap.c:167
FormData_pg_am * Form_pg_am
Definition: pg_am.h:48
int errmsg(const char *fmt,...)
Definition: elog.c:824
#define elog(elevel,...)
Definition: elog.h:214
#define NameStr(name)
Definition: c.h:615
#define AmOidIndexId
Definition: indexing.h:74
ObjectAddress CreateAccessMethod(CreateAmStmt *stmt)
Definition: amcmds.c:43
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:39
#define ERRCODE_DUPLICATE_OBJECT
Definition: streamutil.c:31
Definition: pg_list.h:50
void CatalogTupleInsert(Relation heapRel, HeapTuple tup)
Definition: indexing.c:183