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