/* * moveArrayTypeName * - try to reassign an array type name that the user wants to use. * * The given type name has been discovered to already exist (with the given * OID). If it is an autogenerated array type, change the array type's name * to not conflict. This allows the user to create type "foo" followed by * type "_foo" without problems. (Of course, there are race conditions if * two backends try to create similarly-named types concurrently, but the * worst that can happen is an unnecessary failure --- anything we do here * will be rolled back if the type creation fails due to conflicting names.) * * Note that this must be called *before* calling makeArrayTypeName to * determine the new type's own array type name; else the latter will * certainly pick the same name. * * Returns TRUE if successfully moved the type, FALSE if not. * * We also return TRUE if the given type is a shell type. In this case * the type has not been renamed out of the way, but nonetheless it can * be expected that TypeCreate will succeed. This behavior is convenient * for most callers --- those that need to distinguish the shell-type case * must do their own typisdefined test. */ bool moveArrayTypeName(Oid typeOid, const char *typeName, Oid typeNamespace) { Oid elemOid; char *newname; /* We need do nothing if it's a shell type. */ if (!get_typisdefined(typeOid)) return true; /* Can't change it if it's not an autogenerated array type. */ elemOid = get_element_type(typeOid); if (!OidIsValid(elemOid) || get_array_type(elemOid) != typeOid) return false; /* * OK, use makeArrayTypeName to pick an unused modification of the name. * Note that since makeArrayTypeName is an iterative process, this will * produce a name that it might have produced the first time, had the * conflicting type we are about to create already existed. */ newname = makeArrayTypeName(typeName, typeNamespace); /* Apply the rename */ RenameTypeInternal(typeOid, newname, typeNamespace); /* * We must bump the command counter so that any subsequent use of * makeArrayTypeName sees what we just did and doesn't pick the same name. */ CommandCounterIncrement(); pfree(newname); return true; }
/* * RenameTypeInternal * This renames a type, as well as any associated array type. * * Caller must have already checked privileges. * * Currently this is used for renaming table rowtypes and for * ALTER TYPE RENAME TO command. */ void RenameTypeInternal(Oid typeOid, const char *newTypeName, Oid typeNamespace) { Relation pg_type_desc; HeapTuple tuple; Form_pg_type typ; Oid arrayOid; Oid oldTypeOid; pg_type_desc = table_open(TypeRelationId, RowExclusiveLock); tuple = SearchSysCacheCopy1(TYPEOID, ObjectIdGetDatum(typeOid)); if (!HeapTupleIsValid(tuple)) elog(ERROR, "cache lookup failed for type %u", typeOid); typ = (Form_pg_type) GETSTRUCT(tuple); /* We are not supposed to be changing schemas here */ Assert(typeNamespace == typ->typnamespace); arrayOid = typ->typarray; /* Check for a conflicting type name. */ oldTypeOid = GetSysCacheOid2(TYPENAMENSP, Anum_pg_type_oid, CStringGetDatum(newTypeName), ObjectIdGetDatum(typeNamespace)); /* * If there is one, see if it's an autogenerated array type, and if so * rename it out of the way. (But we must skip that for a shell type * because moveArrayTypeName will do the wrong thing in that case.) * Otherwise, we can at least give a more friendly error than unique-index * violation. */ if (OidIsValid(oldTypeOid)) { if (get_typisdefined(oldTypeOid) && moveArrayTypeName(oldTypeOid, newTypeName, typeNamespace)) /* successfully dodged the problem */ ; else ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("type \"%s\" already exists", newTypeName))); } /* OK, do the rename --- tuple is a copy, so OK to scribble on it */ namestrcpy(&(typ->typname), newTypeName); CatalogTupleUpdate(pg_type_desc, &tuple->t_self, tuple); InvokeObjectPostAlterHook(TypeRelationId, typeOid, 0); heap_freetuple(tuple); table_close(pg_type_desc, RowExclusiveLock); /* * If the type has an array type, recurse to handle that. But we don't * need to do anything more if we already renamed that array type above * (which would happen when, eg, renaming "foo" to "_foo"). */ if (OidIsValid(arrayOid) && arrayOid != oldTypeOid) { char *arrname = makeArrayTypeName(newTypeName, typeNamespace); RenameTypeInternal(arrayOid, arrname, typeNamespace); pfree(arrname); } }
/* * Examine the RETURNS clause of the CREATE FUNCTION statement * and return information about it as *prorettype_p and *returnsSet. * * This is more complex than the average typename lookup because we want to * allow a shell type to be used, or even created if the specified return type * doesn't exist yet. (Without this, there's no way to define the I/O procs * for a new type.) But SQL function creation won't cope, so error out if * the target language is SQL. (We do this here, not in the SQL-function * validator, so as not to produce a NOTICE and then an ERROR for the same * condition.) */ static void compute_return_type(TypeName *returnType, Oid languageOid, Oid *prorettype_p, bool *returnsSet_p) { Oid rettype; rettype = LookupTypeName(NULL, returnType); if (OidIsValid(rettype)) { if (!get_typisdefined(rettype)) { if (languageOid == SQLlanguageId) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("SQL function cannot return shell type %s", TypeNameToString(returnType)))); else ereport(NOTICE, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("return type %s is only a shell", TypeNameToString(returnType)))); } } else { char *typnam = TypeNameToString(returnType); Oid namespaceId; AclResult aclresult; char *typname; /* * Only C-coded functions can be I/O functions. We enforce this * restriction here mainly to prevent littering the catalogs with * shell types due to simple typos in user-defined function * definitions. */ if (languageOid != INTERNALlanguageId && languageOid != ClanguageId) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("type \"%s\" does not exist", typnam))); /* Otherwise, go ahead and make a shell type */ ereport(NOTICE, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("type \"%s\" is not yet defined", typnam), errdetail("Creating a shell type definition."))); namespaceId = QualifiedNameGetCreationNamespace(returnType->names, &typname); aclresult = pg_namespace_aclcheck(namespaceId, GetUserId(), ACL_CREATE); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceId)); rettype = TypeShellMake(typname, namespaceId); Assert(OidIsValid(rettype)); } *prorettype_p = rettype; *returnsSet_p = returnType->setof; }
/* * Interpret the parameter list of the CREATE FUNCTION statement. * * Results are stored into output parameters. parameterTypes must always * be created, but the other arrays are set to NULL if not needed. * requiredResultType is set to InvalidOid if there are no OUT parameters, * else it is set to the OID of the implied result type. */ static void examine_parameter_list(List *parameters, Oid languageOid, oidvector **parameterTypes, ArrayType **allParameterTypes, ArrayType **parameterModes, ArrayType **parameterNames, Oid *requiredResultType) { int parameterCount = list_length(parameters); Oid *inTypes; int inCount = 0; Datum *allTypes; Datum *paramModes; Datum *paramNames; int outCount = 0; bool have_names = false; ListCell *x; int i; *requiredResultType = InvalidOid; /* default result */ inTypes = (Oid *) palloc(parameterCount * sizeof(Oid)); allTypes = (Datum *) palloc(parameterCount * sizeof(Datum)); paramModes = (Datum *) palloc(parameterCount * sizeof(Datum)); paramNames = (Datum *) palloc0(parameterCount * sizeof(Datum)); /* Scan the list and extract data into work arrays */ i = 0; foreach(x, parameters) { FunctionParameter *fp = (FunctionParameter *) lfirst(x); TypeName *t = fp->argType; Oid toid; toid = LookupTypeName(NULL, t); if (OidIsValid(toid)) { if (!get_typisdefined(toid)) { /* As above, hard error if language is SQL */ if (languageOid == SQLlanguageId) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("SQL function cannot accept shell type %s", TypeNameToString(t)))); else ereport(NOTICE, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("argument type %s is only a shell", TypeNameToString(t)))); } } else { ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("type %s does not exist", TypeNameToString(t)))); } if (t->setof) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("functions cannot accept set arguments"))); if (fp->mode != FUNC_PARAM_OUT) inTypes[inCount++] = toid; if (fp->mode != FUNC_PARAM_IN) { if (outCount == 0) /* save first OUT param's type */ *requiredResultType = toid; outCount++; } allTypes[i] = ObjectIdGetDatum(toid); paramModes[i] = CharGetDatum(fp->mode); if (fp->name && fp->name[0]) { paramNames[i] = DirectFunctionCall1(textin, CStringGetDatum(fp->name)); have_names = true; } i++; }