* typecmds.c
* Routines for SQL commands that manipulate types (and domains).
*
- * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
#include "catalog/catalog.h"
#include "catalog/heap.h"
#include "catalog/objectaccess.h"
+#include "catalog/pg_am.h"
#include "catalog/pg_authid.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_constraint.h"
+#include "catalog/pg_constraint_fn.h"
#include "catalog/pg_depend.h"
#include "catalog/pg_enum.h"
#include "catalog/pg_language.h"
{
/* backwards-compatibility hack */
ereport(WARNING,
- (errmsg("changing return type of function %s from \"opaque\" to %s",
- NameListToString(inputName), typeName)));
+ (errmsg("changing return type of function %s from \"%s\" to \"%s\"",
+ NameListToString(inputName), "opaque", typeName)));
SetFunctionReturnType(inputOid, typoid);
}
else
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
- errmsg("type input function %s must return type %s",
+ errmsg("type input function %s must return type \"%s\"",
NameListToString(inputName), typeName)));
}
resulttype = get_func_rettype(outputOid);
{
/* backwards-compatibility hack */
ereport(WARNING,
- (errmsg("changing return type of function %s from \"opaque\" to \"cstring\"",
- NameListToString(outputName))));
+ (errmsg("changing return type of function %s from \"%s\" to \"%s\"",
+ NameListToString(outputName), "opaque", "cstring")));
SetFunctionReturnType(outputOid, CSTRINGOID);
}
else
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
- errmsg("type output function %s must return type \"cstring\"",
- NameListToString(outputName))));
+ errmsg("type output function %s must return type \"%s\"",
+ NameListToString(outputName), "cstring")));
}
if (receiveOid)
{
if (resulttype != typoid)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
- errmsg("type receive function %s must return type %s",
+ errmsg("type receive function %s must return type \"%s\"",
NameListToString(receiveName), typeName)));
}
if (sendOid)
if (resulttype != BYTEAOID)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
- errmsg("type send function %s must return type \"bytea\"",
- NameListToString(sendName))));
+ errmsg("type send function %s must return type \"%s\"",
+ NameListToString(sendName), "bytea")));
}
/*
if (typmodinOid && func_volatile(typmodinOid) == PROVOLATILE_VOLATILE)
ereport(WARNING,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
- errmsg("type modifier input function %s should not be volatile",
- NameListToString(typmodinName))));
+ errmsg("type modifier input function %s should not be volatile",
+ NameListToString(typmodinName))));
if (typmodoutOid && func_volatile(typmodoutOid) == PROVOLATILE_VOLATILE)
ereport(WARNING,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
- errmsg("type modifier output function %s should not be volatile",
- NameListToString(typmodoutName))));
+ errmsg("type modifier output function %s should not be volatile",
+ NameListToString(typmodoutName))));
/*
* OK, we're done checking, time to make the type. We must assign the
array_type, /* type name */
typeNamespace, /* namespace */
InvalidOid, /* relation oid (n/a here) */
- 0, /* relation kind (ditto) */
- GetUserId(), /* owner's ID */
- -1, /* internal size (always varlena) */
+ 0, /* relation kind (ditto) */
+ GetUserId(), /* owner's ID */
+ -1, /* internal size (always varlena) */
TYPTYPE_BASE, /* type-type (base type) */
TYPCATEGORY_ARRAY, /* type-category (array) */
- false, /* array types are never preferred */
+ false, /* array types are never preferred */
delimiter, /* array element delimiter */
F_ARRAY_IN, /* input procedure */
- F_ARRAY_OUT, /* output procedure */
+ F_ARRAY_OUT, /* output procedure */
F_ARRAY_RECV, /* receive procedure */
F_ARRAY_SEND, /* send procedure */
- typmodinOid, /* typmodin procedure */
+ typmodinOid, /* typmodin procedure */
typmodoutOid, /* typmodout procedure */
F_ARRAY_TYPANALYZE, /* analyze procedure */
- typoid, /* element type ID */
- true, /* yes this is an array type */
+ typoid, /* element type ID */
+ true, /* yes this is an array type */
InvalidOid, /* no further array type */
InvalidOid, /* base type ID */
- NULL, /* never a default type value */
- NULL, /* binary default isn't sent either */
- false, /* never passed by value */
+ NULL, /* never a default type value */
+ NULL, /* binary default isn't sent either */
+ false, /* never passed by value */
alignment, /* see above */
- 'x', /* ARRAY is always toastable */
- -1, /* typMod (Domains only) */
- 0, /* Array dimensions of typbasetype */
- false, /* Type NOT NULL */
+ 'x', /* ARRAY is always toastable */
+ -1, /* typMod (Domains only) */
+ 0, /* Array dimensions of typbasetype */
+ false, /* Type NOT NULL */
collation); /* type's collation */
pfree(array_type);
false, /* leakproof */
false, /* isStrict */
PROVOLATILE_IMMUTABLE, /* volatility */
+ PROPARALLEL_SAFE, /* parallel safety */
constructorArgTypesVector, /* parameterTypes */
PointerGetDatum(NULL), /* allParameterTypes */
PointerGetDatum(NULL), /* parameterModes */
PointerGetDatum(NULL), /* parameterNames */
NIL, /* parameterDefaults */
+ PointerGetDatum(NULL), /* trftypes */
PointerGetDatum(NULL), /* proconfig */
1.0, /* procost */
0.0); /* prorows */
if (get_func_rettype(procOid) != INT4OID)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
- errmsg("typmod_in function %s must return type \"integer\"",
- NameListToString(procname))));
+ errmsg("typmod_in function %s must return type \"%s\"",
+ NameListToString(procname), "integer")));
return procOid;
}
if (get_func_rettype(procOid) != CSTRINGOID)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
- errmsg("typmod_out function %s must return type \"cstring\"",
- NameListToString(procname))));
+ errmsg("typmod_out function %s must return type \"%s\"",
+ NameListToString(procname), "cstring")));
return procOid;
}
if (get_func_rettype(procOid) != BOOLOID)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
- errmsg("type analyze function %s must return type \"boolean\"",
- NameListToString(procname))));
+ errmsg("type analyze function %s must return type \"%s\"",
+ NameListToString(procname), "boolean")));
return procOid;
}
if (get_func_rettype(procOid) != FLOAT8OID)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
- errmsg("range subtype diff function %s must return type double precision",
- func_signature_string(procname, 2, NIL, argList))));
+ errmsg("range subtype diff function %s must return type \"%s\"",
+ func_signature_string(procname, 2, NIL, argList),
+ "double precision")));
if (func_volatile(procOid) != PROVOLATILE_IMMUTABLE)
ereport(ERROR,
get_namespace_name(typTup->typnamespace));
}
- /*
- * If it's a composite type, invoke ATExecChangeOwner so that we fix
- * up the pg_class entry properly. That will call back to
- * AlterTypeOwnerInternal to take care of the pg_type entry(s).
- */
- if (typTup->typtype == TYPTYPE_COMPOSITE)
- ATExecChangeOwner(typTup->typrelid, newOwnerId, true, AccessExclusiveLock);
- else
- {
- Datum repl_val[Natts_pg_type];
- bool repl_null[Natts_pg_type];
- bool repl_repl[Natts_pg_type];
- Acl *newAcl;
- Datum aclDatum;
- bool isNull;
-
- memset(repl_null, false, sizeof(repl_null));
- memset(repl_repl, false, sizeof(repl_repl));
-
- repl_repl[Anum_pg_type_typowner - 1] = true;
- repl_val[Anum_pg_type_typowner - 1] = ObjectIdGetDatum(newOwnerId);
-
- aclDatum = heap_getattr(tup,
- Anum_pg_type_typacl,
- RelationGetDescr(rel),
- &isNull);
- /* Null ACLs do not require changes */
- if (!isNull)
- {
- newAcl = aclnewowner(DatumGetAclP(aclDatum),
- typTup->typowner, newOwnerId);
- repl_repl[Anum_pg_type_typacl - 1] = true;
- repl_val[Anum_pg_type_typacl - 1] = PointerGetDatum(newAcl);
- }
-
- tup = heap_modify_tuple(tup, RelationGetDescr(rel), repl_val, repl_null,
- repl_repl);
-
- simple_heap_update(rel, &tup->t_self, tup);
-
- CatalogUpdateIndexes(rel, tup);
-
- /* Update owner dependency reference */
- changeDependencyOnOwner(TypeRelationId, typeOid, newOwnerId);
-
- InvokeObjectPostAlterHook(TypeRelationId, typeOid, 0);
-
- /* If it has an array type, update that too */
- if (OidIsValid(typTup->typarray))
- AlterTypeOwnerInternal(typTup->typarray, newOwnerId, false);
- }
+ AlterTypeOwner_oid(typeOid, newOwnerId, true);
}
ObjectAddressSet(address, TypeRelationId, typeOid);
}
/*
- * AlterTypeOwnerInternal - change type owner unconditionally
+ * AlterTypeOwner_oid - change type owner unconditionally
*
- * This is currently only used to propagate ALTER TABLE/TYPE OWNER to a
- * table's rowtype or an array type, and to implement REASSIGN OWNED BY.
- * It assumes the caller has done all needed checks. The function will
- * automatically recurse to an array type if the type has one.
+ * This function recurses to handle a pg_class entry, if necessary. It
+ * invokes any necessary access object hooks. If hasDependEntry is TRUE, this
+ * function modifies the pg_shdepend entry appropriately (this should be
+ * passed as FALSE only for table rowtypes and array types).
*
- * hasDependEntry should be TRUE if type is expected to have a pg_shdepend
- * entry (ie, it's not a table rowtype nor an array type).
- * is_primary_ops should be TRUE if this function is invoked with user's
- * direct operation (e.g, shdepReassignOwned). Elsewhere,
+ * This is used by ALTER TABLE/TYPE OWNER commands, as well as by REASSIGN
+ * OWNED BY. It assumes the caller has done all needed check.
*/
void
-AlterTypeOwnerInternal(Oid typeOid, Oid newOwnerId,
- bool hasDependEntry)
+AlterTypeOwner_oid(Oid typeOid, Oid newOwnerId, bool hasDependEntry)
+{
+ Relation rel;
+ HeapTuple tup;
+ Form_pg_type typTup;
+
+ rel = heap_open(TypeRelationId, RowExclusiveLock);
+
+ tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typeOid));
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for type %u", typeOid);
+ typTup = (Form_pg_type) GETSTRUCT(tup);
+
+ /*
+ * If it's a composite type, invoke ATExecChangeOwner so that we fix up the
+ * pg_class entry properly. That will call back to AlterTypeOwnerInternal
+ * to take care of the pg_type entry(s).
+ */
+ if (typTup->typtype == TYPTYPE_COMPOSITE)
+ ATExecChangeOwner(typTup->typrelid, newOwnerId, true, AccessExclusiveLock);
+ else
+ AlterTypeOwnerInternal(typeOid, newOwnerId);
+
+ /* Update owner dependency reference */
+ if (hasDependEntry)
+ changeDependencyOnOwner(TypeRelationId, typeOid, newOwnerId);
+
+ InvokeObjectPostAlterHook(TypeRelationId, typeOid, 0);
+
+ ReleaseSysCache(tup);
+ heap_close(rel, RowExclusiveLock);
+}
+
+/*
+ * AlterTypeOwnerInternal - bare-bones type owner change.
+ *
+ * This routine simply modifies the owner of a pg_type entry, and recurses
+ * to handle a possible array type.
+ */
+void
+AlterTypeOwnerInternal(Oid typeOid, Oid newOwnerId)
{
Relation rel;
HeapTuple tup;
CatalogUpdateIndexes(rel, tup);
- /* Update owner dependency reference, if it has one */
- if (hasDependEntry)
- changeDependencyOnOwner(TypeRelationId, typeOid, newOwnerId);
-
/* If it has an array type, update that too */
if (OidIsValid(typTup->typarray))
- AlterTypeOwnerInternal(typTup->typarray, newOwnerId, false);
-
- InvokeObjectPostAlterHook(TypeRelationId, typeOid, 0);
+ AlterTypeOwnerInternal(typTup->typarray, newOwnerId);
/* Clean up */
heap_close(rel, RowExclusiveLock);
oldNspOid = typform->typnamespace;
arrayOid = typform->typarray;
- /* common checks on switching namespaces */
- CheckSetNamespace(oldNspOid, nspOid, TypeRelationId, typeOid);
+ /* If the type is already there, we scan skip these next few checks. */
+ if (oldNspOid != nspOid)
+ {
+ /* common checks on switching namespaces */
+ CheckSetNamespace(oldNspOid, nspOid);
- /* check for duplicate name (more friendly than unique-index failure) */
- if (SearchSysCacheExists2(TYPENAMENSP,
- CStringGetDatum(NameStr(typform->typname)),
- ObjectIdGetDatum(nspOid)))
- ereport(ERROR,
- (errcode(ERRCODE_DUPLICATE_OBJECT),
- errmsg("type \"%s\" already exists in schema \"%s\"",
- NameStr(typform->typname),
- get_namespace_name(nspOid))));
+ /* check for duplicate name (more friendly than unique-index failure) */
+ if (SearchSysCacheExists2(TYPENAMENSP,
+ CStringGetDatum(NameStr(typform->typname)),
+ ObjectIdGetDatum(nspOid)))
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("type \"%s\" already exists in schema \"%s\"",
+ NameStr(typform->typname),
+ get_namespace_name(nspOid))));
+ }
/* Detect whether type is a composite type (but not a table rowtype) */
isCompositeType =
format_type_be(typeOid)),
errhint("Use ALTER TABLE instead.")));
- /* OK, modify the pg_type row */
+ if (oldNspOid != nspOid)
+ {
+ /* OK, modify the pg_type row */
- /* tup is a copy, so we can scribble directly on it */
- typform->typnamespace = nspOid;
+ /* tup is a copy, so we can scribble directly on it */
+ typform->typnamespace = nspOid;
- simple_heap_update(rel, &tup->t_self, tup);
- CatalogUpdateIndexes(rel, tup);
+ simple_heap_update(rel, &tup->t_self, tup);
+ CatalogUpdateIndexes(rel, tup);
+ }
/*
* Composite types have pg_class entries.
* Update dependency on schema, if any --- a table rowtype has not got
* one, and neither does an implicit array.
*/
- if ((isCompositeType || typform->typtype != TYPTYPE_COMPOSITE) &&
+ if (oldNspOid != nspOid &&
+ (isCompositeType || typform->typtype != TYPTYPE_COMPOSITE) &&
!isImplicitArray)
if (changeDependencyFor(TypeRelationId, typeOid,
NamespaceRelationId, oldNspOid, nspOid) != 1)