diff options
Diffstat (limited to 'src/backend')
-rw-r--r-- | src/backend/catalog/README | 8 | ||||
-rw-r--r-- | src/backend/catalog/heap.c | 141 | ||||
-rw-r--r-- | src/backend/catalog/pg_shdepend.c | 4 | ||||
-rw-r--r-- | src/backend/catalog/pg_type.c | 102 | ||||
-rw-r--r-- | src/backend/commands/tablecmds.c | 33 | ||||
-rw-r--r-- | src/backend/commands/typecmds.c | 218 | ||||
-rw-r--r-- | src/backend/parser/parse_type.c | 10 | ||||
-rw-r--r-- | src/backend/utils/cache/lsyscache.c | 36 |
8 files changed, 373 insertions, 179 deletions
diff --git a/src/backend/catalog/README b/src/backend/catalog/README index 3cbcc3d45ee..89c6e0305ae 100644 --- a/src/backend/catalog/README +++ b/src/backend/catalog/README @@ -1,4 +1,4 @@ -$PostgreSQL: pgsql/src/backend/catalog/README,v 1.10 2006/07/31 01:16:36 tgl Exp $ +$PostgreSQL: pgsql/src/backend/catalog/README,v 1.11 2007/05/11 17:57:11 tgl Exp $ This directory contains .c files that manipulate the system catalogs; src/include/catalog contains the .h files that define the structure @@ -86,9 +86,9 @@ general) assumes that the fixed-length portions of all system catalog tuples are in fact present, because it maps C struct declarations onto them. Thus, the variable-length fields must all be at the end, and only the variable-length fields of a catalog tuple are permitted to be -NULL. For example, if you set pg_type.typdelim to be NULL, a -piece of code will likely perform "typetup->typdelim" (or, worse, -"typetyp->typelem", which follows typdelim). This will result in +NULL. For example, if you set pg_type.typrelid to be NULL, a +piece of code will likely perform "typetup->typrelid" (or, worse, +"typetyp->typelem", which follows typrelid). This will result in random errors or even segmentation violations. Hence, do NOT insert catalog tuples that contain NULL attributes except in their variable-length portions! (The bootstrapping code is fairly good about diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index e6404ecd0b5..8b34d685d85 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.318 2007/04/02 03:49:37 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.319 2007/05/11 17:57:11 tgl Exp $ * * * INTERFACE ROUTINES @@ -45,6 +45,7 @@ #include "catalog/pg_statistic.h" #include "catalog/pg_type.h" #include "commands/tablecmds.h" +#include "commands/typecmds.h" #include "miscadmin.h" #include "optimizer/clauses.h" #include "optimizer/var.h" @@ -69,7 +70,8 @@ static void AddNewRelationTuple(Relation pg_class_desc, static Oid AddNewRelationType(const char *typeName, Oid typeNamespace, Oid new_rel_oid, - char new_rel_kind); + char new_rel_kind, + Oid new_array_type); static void RelationRemoveInheritance(Oid relid); static void StoreRelCheck(Relation rel, char *ccname, char *ccbin); static void StoreConstraints(Relation rel, TupleDesc tupdesc); @@ -401,26 +403,55 @@ CheckAttributeType(const char *attname, Oid atttypid) { char att_typtype = get_typtype(atttypid); - /* - * Warn user, but don't fail, if column to be created has UNKNOWN type - * (usually as a result of a 'retrieve into' - jolly) - * - * Refuse any attempt to create a pseudo-type column. - */ if (atttypid == UNKNOWNOID) + { + /* + * Warn user, but don't fail, if column to be created has UNKNOWN type + * (usually as a result of a 'retrieve into' - jolly) + */ ereport(WARNING, (errcode(ERRCODE_INVALID_TABLE_DEFINITION), errmsg("column \"%s\" has type \"unknown\"", attname), errdetail("Proceeding with relation creation anyway."))); + } else if (att_typtype == TYPTYPE_PSEUDO) { - /* Special hack for pg_statistic: allow ANYARRAY during initdb */ + /* + * Refuse any attempt to create a pseudo-type column, except for + * a special hack for pg_statistic: allow ANYARRAY during initdb + */ if (atttypid != ANYARRAYOID || IsUnderPostmaster) ereport(ERROR, (errcode(ERRCODE_INVALID_TABLE_DEFINITION), errmsg("column \"%s\" has pseudo-type %s", attname, format_type_be(atttypid)))); } + else if (att_typtype == TYPTYPE_COMPOSITE) + { + /* + * For a composite type, recurse into its attributes. You might + * think this isn't necessary, but since we allow system catalogs + * to break the rule, we have to guard against the case. + */ + Relation relation; + TupleDesc tupdesc; + int i; + + relation = relation_open(get_typ_typrelid(atttypid), AccessShareLock); + + tupdesc = RelationGetDescr(relation); + + for (i = 0; i < tupdesc->natts; i++) + { + Form_pg_attribute attr = tupdesc->attrs[i]; + + if (attr->attisdropped) + continue; + CheckAttributeType(NameStr(attr->attname), attr->atttypid); + } + + relation_close(relation, AccessShareLock); + } } /* -------------------------------- @@ -710,16 +741,18 @@ static Oid AddNewRelationType(const char *typeName, Oid typeNamespace, Oid new_rel_oid, - char new_rel_kind) + char new_rel_kind, + Oid new_array_type) { return - TypeCreate(typeName, /* type name */ + TypeCreate(InvalidOid, /* no predetermined OID */ + typeName, /* type name */ typeNamespace, /* type namespace */ new_rel_oid, /* relation oid */ new_rel_kind, /* relation kind */ -1, /* internal size (varlena) */ TYPTYPE_COMPOSITE, /* type-type (composite) */ - ',', /* default array delimiter */ + DEFAULT_TYPDELIM, /* default array delimiter */ F_RECORD_IN, /* input procedure */ F_RECORD_OUT, /* output procedure */ F_RECORD_RECV, /* receive procedure */ @@ -728,6 +761,8 @@ AddNewRelationType(const char *typeName, InvalidOid, /* typmodout procedure - none */ InvalidOid, /* analyze procedure - default */ InvalidOid, /* array element type - irrelevant */ + false, /* this is not an array type */ + new_array_type, /* array type if any */ InvalidOid, /* domain base type - irrelevant */ NULL, /* default value - none */ NULL, /* default binary representation */ @@ -763,6 +798,7 @@ heap_create_with_catalog(const char *relname, Relation pg_class_desc; Relation new_rel_desc; Oid new_type_oid; + Oid new_array_oid = InvalidOid; pg_class_desc = heap_open(RelationRelationId, RowExclusiveLock); @@ -805,7 +841,24 @@ heap_create_with_catalog(const char *relname, Assert(relid == RelationGetRelid(new_rel_desc)); /* - * since defining a relation also defines a complex type, we add a new + * Decide whether to create an array type over the relation's rowtype. + * We do not create any array types for system catalogs (ie, those made + * during initdb). We create array types for regular relations, views, + * and composite types ... but not, eg, for toast tables or sequences. + */ + if (IsUnderPostmaster && (relkind == RELKIND_RELATION || + relkind == RELKIND_VIEW || + relkind == RELKIND_COMPOSITE_TYPE)) + { + /* OK, so pre-assign a type OID for the array type */ + Relation pg_type = heap_open(TypeRelationId, AccessShareLock); + + new_array_oid = GetNewOid(pg_type); + heap_close(pg_type, AccessShareLock); + } + + /* + * Since defining a relation also defines a complex type, we add a new * system type corresponding to the new relation. * * NOTE: we could get a unique-index failure here, in case the same name @@ -814,7 +867,47 @@ heap_create_with_catalog(const char *relname, new_type_oid = AddNewRelationType(relname, relnamespace, relid, - relkind); + relkind, + new_array_oid); + /* + * Now make the array type if wanted. + */ + if (OidIsValid(new_array_oid)) + { + char *relarrayname; + + relarrayname = makeArrayTypeName(relname, relnamespace); + + TypeCreate(new_array_oid, /* force the type's OID to this */ + relarrayname, /* Array type name */ + relnamespace, /* Same namespace as parent */ + InvalidOid, /* Not composite, no relationOid */ + 0, /* relkind, also N/A here */ + -1, /* Internal size (varlena) */ + TYPTYPE_BASE, /* Not composite - typelem is */ + DEFAULT_TYPDELIM, /* default array delimiter */ + F_ARRAY_IN, /* array input proc */ + F_ARRAY_OUT, /* array output proc */ + F_ARRAY_RECV, /* array recv (bin) proc */ + F_ARRAY_SEND, /* array send (bin) proc */ + InvalidOid, /* typmodin procedure - none */ + InvalidOid, /* typmodout procedure - none */ + InvalidOid, /* analyze procedure - default */ + new_type_oid, /* array element type - the rowtype */ + true, /* yes, this is an array type */ + InvalidOid, /* this has no array type */ + InvalidOid, /* domain base type - irrelevant */ + NULL, /* default value - none */ + NULL, /* default binary representation */ + false, /* passed by reference */ + 'd', /* alignment - must be the largest! */ + 'x', /* fully TOASTable */ + -1, /* typmod */ + 0, /* array dimensions for typBaseType */ + false); /* Type NOT NULL */ + + pfree(relarrayname); + } /* * now create an entry in pg_class for the relation. @@ -838,13 +931,15 @@ heap_create_with_catalog(const char *relname, oidislocal, oidinhcount); /* - * make a dependency link to force the relation to be deleted if its - * namespace is. Skip this in bootstrap mode, since we don't make - * dependencies while bootstrapping. + * Make a dependency link to force the relation to be deleted if its + * namespace is. Also make a dependency link to its owner. * - * Also make a dependency link to its owner. + * For composite types, these dependencies are tracked for the pg_type + * entry, so we needn't record them here. Also, skip this in bootstrap + * mode, since we don't make dependencies while bootstrapping. */ - if (!IsBootstrapProcessingMode()) + if (relkind != RELKIND_COMPOSITE_TYPE && + !IsBootstrapProcessingMode()) { ObjectAddress myself, referenced; @@ -857,13 +952,7 @@ heap_create_with_catalog(const char *relname, referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); - /* - * For composite types, the dependency on owner is tracked for the - * pg_type entry, so don't record it here. All other relkinds need - * their ownership tracked. - */ - if (relkind != RELKIND_COMPOSITE_TYPE) - recordDependencyOnOwner(RelationRelationId, relid, ownerid); + recordDependencyOnOwner(RelationRelationId, relid, ownerid); } /* diff --git a/src/backend/catalog/pg_shdepend.c b/src/backend/catalog/pg_shdepend.c index 9ad012db49f..e3de61344bb 100644 --- a/src/backend/catalog/pg_shdepend.c +++ b/src/backend/catalog/pg_shdepend.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/pg_shdepend.c,v 1.17 2007/03/03 19:32:54 neilc Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/pg_shdepend.c,v 1.18 2007/05/11 17:57:12 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1254,7 +1254,7 @@ shdepReassignOwned(List *roleids, Oid newrole) break; case TypeRelationId: - AlterTypeOwnerInternal(sdepForm->objid, newrole); + AlterTypeOwnerInternal(sdepForm->objid, newrole, true); break; case OperatorRelationId: diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c index efc3b8de099..83601496043 100644 --- a/src/backend/catalog/pg_type.c +++ b/src/backend/catalog/pg_type.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/pg_type.c,v 1.111 2007/04/02 03:49:37 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/pg_type.c,v 1.112 2007/05/11 17:57:12 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -22,6 +22,7 @@ #include "catalog/pg_type.h" #include "commands/typecmds.h" #include "miscadmin.h" +#include "parser/scansup.h" #include "utils/acl.h" #include "utils/builtins.h" #include "utils/fmgroids.h" @@ -90,6 +91,7 @@ TypeShellMake(const char *typeName, Oid typeNamespace) values[i++] = CharGetDatum(DEFAULT_TYPDELIM); /* typdelim */ values[i++] = ObjectIdGetDatum(InvalidOid); /* typrelid */ values[i++] = ObjectIdGetDatum(InvalidOid); /* typelem */ + values[i++] = ObjectIdGetDatum(InvalidOid); /* typarray */ values[i++] = ObjectIdGetDatum(F_SHELL_IN); /* typinput */ values[i++] = ObjectIdGetDatum(F_SHELL_OUT); /* typoutput */ values[i++] = ObjectIdGetDatum(InvalidOid); /* typreceive */ @@ -135,6 +137,7 @@ TypeShellMake(const char *typeName, Oid typeNamespace) InvalidOid, InvalidOid, InvalidOid, + false, InvalidOid, NULL, false); @@ -153,13 +156,16 @@ TypeShellMake(const char *typeName, Oid typeNamespace) * * This does all the necessary work needed to define a new type. * - * Returns the OID assigned to the new type. + * Returns the OID assigned to the new type. If newTypeOid is + * zero (the normal case), a new OID is created; otherwise we + * use exactly that OID. * ---------------------------------------------------------------- */ Oid -TypeCreate(const char *typeName, +TypeCreate(Oid newTypeOid, + const char *typeName, Oid typeNamespace, - Oid relationOid, /* only for composite types */ + Oid relationOid, /* only for relation rowtypes */ char relationKind, /* ditto */ int16 internalSize, char typeType, @@ -172,6 +178,8 @@ TypeCreate(const char *typeName, Oid typmodoutProcedure, Oid analyzeProcedure, Oid elementType, + bool isImplicitArray, + Oid arrayType, Oid baseType, const char *defaultTypeValue, /* human readable rep */ char *defaultTypeBin, /* cooked rep */ @@ -243,9 +251,9 @@ TypeCreate(const char *typeName, values[i++] = CharGetDatum(typeType); /* typtype */ values[i++] = BoolGetDatum(true); /* typisdefined */ values[i++] = CharGetDatum(typDelim); /* typdelim */ - values[i++] = ObjectIdGetDatum(typeType == TYPTYPE_COMPOSITE ? - relationOid : InvalidOid); /* typrelid */ + values[i++] = ObjectIdGetDatum(relationOid); /* typrelid */ values[i++] = ObjectIdGetDatum(elementType); /* typelem */ + values[i++] = ObjectIdGetDatum(arrayType); /* typarray */ values[i++] = ObjectIdGetDatum(inputProcedure); /* typinput */ values[i++] = ObjectIdGetDatum(outputProcedure); /* typoutput */ values[i++] = ObjectIdGetDatum(receiveProcedure); /* typreceive */ @@ -310,6 +318,10 @@ TypeCreate(const char *typeName, if (((Form_pg_type) GETSTRUCT(tup))->typowner != GetUserId()) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE, typeName); + /* trouble if caller wanted to force the OID */ + if (OidIsValid(newTypeOid)) + elog(ERROR, "cannot assign new OID to existing shell type"); + /* * Okay to update existing shell type tuple */ @@ -331,6 +343,10 @@ TypeCreate(const char *typeName, values, nulls); + /* Force the OID if requested by caller, else heap_insert does it */ + if (OidIsValid(newTypeOid)) + HeapTupleSetOid(tup, newTypeOid); + typeObjectId = simple_heap_insert(pg_type_desc, tup); } @@ -354,6 +370,7 @@ TypeCreate(const char *typeName, typmodoutProcedure, analyzeProcedure, elementType, + isImplicitArray, baseType, (defaultTypeBin ? stringToNode(defaultTypeBin) : @@ -378,8 +395,8 @@ TypeCreate(const char *typeName, void GenerateTypeDependencies(Oid typeNamespace, Oid typeObjectId, - Oid relationOid, /* only for composite types */ - char relationKind, /* ditto */ + Oid relationOid, /* only for relation rowtypes */ + char relationKind, /* ditto */ Oid owner, Oid inputProcedure, Oid outputProcedure, @@ -389,6 +406,7 @@ GenerateTypeDependencies(Oid typeNamespace, Oid typmodoutProcedure, Oid analyzeProcedure, Oid elementType, + bool isImplicitArray, Oid baseType, Node *defaultExpr, bool rebuild) @@ -406,14 +424,23 @@ GenerateTypeDependencies(Oid typeNamespace, myself.objectId = typeObjectId; myself.objectSubId = 0; - /* dependency on namespace */ - /* skip for relation rowtype, since we have indirect dependency */ - if (!OidIsValid(relationOid)) + /* + * Make dependency on namespace and shared dependency on owner. + * + * For a relation rowtype (that's not a composite type), we should skip + * these because we'll depend on them indirectly through the pg_class + * entry. Likewise, skip for implicit arrays since we'll depend on them + * through the element type. + */ + if ((!OidIsValid(relationOid) || relationKind == RELKIND_COMPOSITE_TYPE) && + !isImplicitArray) { referenced.classId = NamespaceRelationId; referenced.objectId = typeNamespace; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + + recordDependencyOnOwner(TypeRelationId, typeObjectId, owner); } /* Normal dependencies on the I/O functions */ @@ -495,17 +522,17 @@ GenerateTypeDependencies(Oid typeNamespace, } /* - * If the type is an array type, mark it auto-dependent on the base type. - * (This is a compromise between the typical case where the array type is - * automatically generated and the case where it is manually created: we'd - * prefer INTERNAL for the former case and NORMAL for the latter.) + * If the type is an implicitly-created array type, mark it as internally + * dependent on the element type. Otherwise, if it has an element type, + * the dependency is a normal one. */ if (OidIsValid(elementType)) { referenced.classId = TypeRelationId; referenced.objectId = elementType; referenced.objectSubId = 0; - recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO); + recordDependencyOn(&myself, &referenced, + isImplicitArray ? DEPENDENCY_INTERNAL : DEPENDENCY_NORMAL); } /* Normal dependency from a domain to its base type. */ @@ -520,9 +547,6 @@ GenerateTypeDependencies(Oid typeNamespace, /* Normal dependency on the default expression. */ if (defaultExpr) recordDependencyOnExpr(&myself, defaultExpr, NIL, DEPENDENCY_NORMAL); - - /* Shared dependency on owner. */ - recordDependencyOnOwner(TypeRelationId, typeObjectId, owner); } /* @@ -570,21 +594,47 @@ TypeRename(const char *oldTypeName, Oid typeNamespace, heap_close(pg_type_desc, RowExclusiveLock); } + /* - * makeArrayTypeName(typeName); - * - given a base type name, make an array of type name out of it + * makeArrayTypeName(typeName) + * - given a base type name, make an array type name for it * * the caller is responsible for pfreeing the result */ char * -makeArrayTypeName(const char *typeName) +makeArrayTypeName(const char *typeName, Oid typeNamespace) { char *arr; + int i; + Relation pg_type_desc; - if (!typeName) - return NULL; + /* + * The idea is to prepend underscores as needed until we make a name + * that doesn't collide with anything... + */ arr = palloc(NAMEDATALEN); - snprintf(arr, NAMEDATALEN, - "_%.*s", NAMEDATALEN - 2, typeName); + + pg_type_desc = heap_open(TypeRelationId, AccessShareLock); + + for (i = 1; i < NAMEDATALEN - 1; i++) + { + arr[i - 1] = '_'; + strlcpy(arr + i, typeName, NAMEDATALEN - i); + truncate_identifier(arr, strlen(arr), false); + if (!SearchSysCacheExists(TYPENAMENSP, + CStringGetDatum(arr), + ObjectIdGetDatum(typeNamespace), + 0, 0)) + break; + } + + heap_close(pg_type_desc, AccessShareLock); + + if (i >= NAMEDATALEN-1) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("could not form array type name for type \"%s\"", + typeName))); + return arr; } diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 8043799b887..a1bbf177893 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.219 2007/04/08 01:26:32 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.220 2007/05/11 17:57:12 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -2827,6 +2827,7 @@ find_composite_type_dependencies(Oid typeOid, const char *origTblName) ScanKeyData key[2]; SysScanDesc depScan; HeapTuple depTup; + Oid arrayOid; /* * We scan pg_depend to find those things that depend on the rowtype. (We @@ -2886,6 +2887,14 @@ find_composite_type_dependencies(Oid typeOid, const char *origTblName) systable_endscan(depScan); relation_close(depRel, AccessShareLock); + + /* + * If there's an array type for the rowtype, must check for uses of it, + * too. + */ + arrayOid = get_array_type(typeOid); + if (OidIsValid(arrayOid)) + find_composite_type_dependencies(arrayOid, origTblName); } @@ -5299,6 +5308,9 @@ ATPostAlterTypeParse(char *cmd, List **wqueue) * be changed separately from the parent table. Also, we can skip permission * checks (this is necessary not just an optimization, else we'd fail to * handle toast tables properly). + * + * recursing is also true if ALTER TYPE OWNER is calling us to fix up a + * free-standing composite type. */ void ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing) @@ -5370,6 +5382,7 @@ ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing) } break; case RELKIND_TOASTVALUE: + case RELKIND_COMPOSITE_TYPE: if (recursing) break; /* FALL THRU */ @@ -5448,14 +5461,22 @@ ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing) heap_freetuple(newtuple); - /* Update owner dependency reference */ - changeDependencyOnOwner(RelationRelationId, relationOid, newOwnerId); + /* + * Update owner dependency reference, if any. A composite type has + * none, because it's tracked for the pg_type entry instead of here; + * indexes don't have their own entries either. + */ + if (tuple_class->relkind != RELKIND_COMPOSITE_TYPE && + tuple_class->relkind != RELKIND_INDEX) + changeDependencyOnOwner(RelationRelationId, relationOid, + newOwnerId); /* * Also change the ownership of the table's rowtype, if it has one */ if (tuple_class->relkind != RELKIND_INDEX) - AlterTypeOwnerInternal(tuple_class->reltype, newOwnerId); + AlterTypeOwnerInternal(tuple_class->reltype, newOwnerId, + tuple_class->relkind == RELKIND_COMPOSITE_TYPE); /* * If we are operating on a table, also change the ownership of any @@ -6462,7 +6483,7 @@ AlterTableNamespace(RangeVar *relation, const char *newschema) AlterRelationNamespaceInternal(classRel, relid, oldNspOid, nspOid, true); /* Fix the table's rowtype too */ - AlterTypeNamespaceInternal(rel->rd_rel->reltype, nspOid, false); + AlterTypeNamespaceInternal(rel->rd_rel->reltype, nspOid, false, false); /* Fix other dependent stuff */ if (rel->rd_rel->relkind == RELKIND_RELATION) @@ -6625,7 +6646,7 @@ AlterSeqNamespaces(Relation classRel, Relation rel, * them to the new namespace, too. */ AlterTypeNamespaceInternal(RelationGetForm(seqRel)->reltype, - newNspOid, false); + newNspOid, false, false); /* Now we can close it. Keep the lock till end of transaction. */ relation_close(seqRel, NoLock); diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c index 5c1b9f6f0e6..7911f6df3a5 100644 --- a/src/backend/commands/typecmds.c +++ b/src/backend/commands/typecmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.101 2007/04/02 03:49:38 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.102 2007/05/11 17:57:12 tgl Exp $ * * DESCRIPTION * The "DefineFoo" routines take the parse tree and pick out the @@ -34,6 +34,7 @@ #include "access/genam.h" #include "access/heapam.h" #include "access/xact.h" +#include "catalog/catalog.h" #include "catalog/dependency.h" #include "catalog/heap.h" #include "catalog/indexing.h" @@ -118,10 +119,12 @@ DefineType(List *names, List *parameters) Oid typmodinOid = InvalidOid; Oid typmodoutOid = InvalidOid; Oid analyzeOid = InvalidOid; - char *shadow_type; + char *array_type; + Oid array_oid; ListCell *pl; Oid typoid; Oid resulttype; + Relation pg_type; /* Convert list of names to a name and namespace */ typeNamespace = QualifiedNameGetCreationNamespace(names, &typeName); @@ -133,16 +136,6 @@ DefineType(List *names, List *parameters) get_namespace_name(typeNamespace)); /* - * Type names must be one character shorter than other names, allowing - * room to create the corresponding array type name with prepended "_". - */ - if (strlen(typeName) > (NAMEDATALEN - 2)) - ereport(ERROR, - (errcode(ERRCODE_INVALID_NAME), - errmsg("type names must be %d characters or less", - NAMEDATALEN - 2))); - - /* * Look to see if type already exists (presumably as a shell; if not, * TypeCreate will complain). If it doesn't, create it as a shell, so * that the OID is known for use in the I/O function definitions. @@ -396,11 +389,17 @@ DefineType(List *names, List *parameters) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, NameListToString(analyzeName)); + /* Preassign array type OID so we can insert it in pg_type.typarray */ + pg_type = heap_open(TypeRelationId, AccessShareLock); + array_oid = GetNewOid(pg_type); + heap_close(pg_type, AccessShareLock); + /* * now have TypeCreate do all the real work. */ typoid = - TypeCreate(typeName, /* type name */ + TypeCreate(InvalidOid, /* no predetermined type OID */ + typeName, /* type name */ typeNamespace, /* namespace */ InvalidOid, /* relation oid (n/a here) */ 0, /* relation kind (ditto) */ @@ -415,6 +414,8 @@ DefineType(List *names, List *parameters) typmodoutOid,/* typmodout procedure */ analyzeOid, /* analyze procedure */ elemType, /* element type ID */ + false, /* this is not an array type */ + array_oid, /* array type we are about to create */ InvalidOid, /* base type ID (only for domains) */ defaultValue, /* default type value */ NULL, /* no binary form available */ @@ -426,19 +427,19 @@ DefineType(List *names, List *parameters) false); /* Type NOT NULL */ /* - * When we create a base type (as opposed to a complex type) we need to - * have an array entry for it in pg_type as well. + * Create the array type that goes with it. */ - shadow_type = makeArrayTypeName(typeName); + array_type = makeArrayTypeName(typeName, typeNamespace); /* alignment must be 'i' or 'd' for arrays */ alignment = (alignment == 'd') ? 'd' : 'i'; - TypeCreate(shadow_type, /* type name */ + TypeCreate(array_oid, /* force assignment of this type OID */ + array_type, /* type name */ typeNamespace, /* namespace */ InvalidOid, /* relation oid (n/a here) */ 0, /* relation kind (ditto) */ - -1, /* internal size */ + -1, /* internal size (always varlena) */ TYPTYPE_BASE, /* type-type (base type) */ DEFAULT_TYPDELIM, /* array element delimiter */ F_ARRAY_IN, /* input procedure */ @@ -449,6 +450,8 @@ DefineType(List *names, List *parameters) typmodoutOid, /* typmodout procedure */ InvalidOid, /* analyze procedure - default */ 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 */ @@ -459,7 +462,7 @@ DefineType(List *names, List *parameters) 0, /* Array dimensions of typbasetype */ false); /* Type NOT NULL */ - pfree(shadow_type); + pfree(array_type); } @@ -474,6 +477,7 @@ RemoveType(List *names, DropBehavior behavior, bool missing_ok) Oid typeoid; HeapTuple tup; ObjectAddress object; + Form_pg_type typ; /* Make a TypeName so we can use standard type lookup machinery */ typename = makeTypeNameFromNameList(names); @@ -504,14 +508,19 @@ RemoveType(List *names, DropBehavior behavior, bool missing_ok) 0, 0, 0); if (!HeapTupleIsValid(tup)) elog(ERROR, "cache lookup failed for type %u", typeoid); + typ = (Form_pg_type) GETSTRUCT(tup); /* Permission check: must own type or its namespace */ if (!pg_type_ownercheck(typeoid, GetUserId()) && - !pg_namespace_ownercheck(((Form_pg_type) GETSTRUCT(tup))->typnamespace, - GetUserId())) + !pg_namespace_ownercheck(typ->typnamespace, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE, TypeNameToString(typename)); + /* + * Note: we need no special check for array types here, as the normal + * treatment of internal dependencies handles it just fine + */ + ReleaseSysCache(tup); /* @@ -608,19 +617,6 @@ DefineDomain(CreateDomainStmt *stmt) get_namespace_name(domainNamespace)); /* - * Domainnames, unlike typenames don't need to account for the '_' prefix. - * So they can be one character longer. (This test is presently useless - * since the parser will have truncated the name to fit. But leave it - * here since we may someday support arrays of domains, in which case - * we'll be back to needing to enforce NAMEDATALEN-2.) - */ - if (strlen(domainName) > (NAMEDATALEN - 1)) - ereport(ERROR, - (errcode(ERRCODE_INVALID_NAME), - errmsg("domain names must be %d characters or less", - NAMEDATALEN - 1))); - - /* * Look up the base type. */ typeTup = typenameType(NULL, stmt->typename); @@ -805,7 +801,8 @@ DefineDomain(CreateDomainStmt *stmt) * Have TypeCreate do all the real work. */ domainoid = - TypeCreate(domainName, /* type name */ + TypeCreate(InvalidOid, /* no predetermined type OID */ + domainName, /* type name */ domainNamespace, /* namespace */ InvalidOid, /* relation oid (n/a here) */ 0, /* relation kind (ditto) */ @@ -820,6 +817,8 @@ DefineDomain(CreateDomainStmt *stmt) InvalidOid, /* typmodout procedure - none */ analyzeProcedure, /* analyze procedure */ typelem, /* element type ID */ + false, /* this isn't an array */ + InvalidOid, /* no arrays for domains (yet) */ basetypeoid, /* base type ID */ defaultValue, /* default type value (text) */ defaultValueBin, /* default type value (binary) */ @@ -949,6 +948,8 @@ DefineEnum(CreateEnumStmt *stmt) Oid enumNamespace; Oid enumTypeOid; AclResult aclresult; + Oid enumArrayOid; + Relation pg_type; /* Convert list of names to a name and namespace */ enumNamespace = QualifiedNameGetCreationNamespace(stmt->typename, @@ -960,19 +961,15 @@ DefineEnum(CreateEnumStmt *stmt) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(enumNamespace)); - /* - * Type names must be one character shorter than other names, allowing - * room to create the corresponding array type name with prepended "_". - */ - if (strlen(enumName) > (NAMEDATALEN - 2)) - ereport(ERROR, - (errcode(ERRCODE_INVALID_NAME), - errmsg("type names must be %d characters or less", - NAMEDATALEN - 2))); + /* Preassign array type OID so we can insert it in pg_type.typarray */ + pg_type = heap_open(TypeRelationId, AccessShareLock); + enumArrayOid = GetNewOid(pg_type); + heap_close(pg_type, AccessShareLock); /* Create the pg_type entry */ enumTypeOid = - TypeCreate(enumName, /* type name */ + TypeCreate(InvalidOid, /* no predetermined type OID */ + enumName, /* type name */ enumNamespace, /* namespace */ InvalidOid, /* relation oid (n/a here) */ 0, /* relation kind (ditto) */ @@ -987,6 +984,8 @@ DefineEnum(CreateEnumStmt *stmt) InvalidOid, /* typmodout procedure - none */ InvalidOid, /* analyze procedure - default */ InvalidOid, /* element type ID */ + false, /* this is not an array type */ + enumArrayOid, /* array type we are about to create */ InvalidOid, /* base type ID (only for domains) */ NULL, /* never a default type value */ NULL, /* binary default isn't sent either */ @@ -1000,14 +999,17 @@ DefineEnum(CreateEnumStmt *stmt) /* Enter the enum's values into pg_enum */ EnumValuesCreate(enumTypeOid, stmt->vals); - /* Create array type for enum */ - enumArrayName = makeArrayTypeName(enumName); + /* + * Create the array type that goes with it. + */ + enumArrayName = makeArrayTypeName(enumName, enumNamespace); - TypeCreate(enumArrayName, /* type name */ + TypeCreate(enumArrayOid, /* force assignment of this type OID */ + enumArrayName, /* type name */ enumNamespace, /* namespace */ InvalidOid, /* relation oid (n/a here) */ 0, /* relation kind (ditto) */ - -1, /* internal size */ + -1, /* internal size (always varlena) */ TYPTYPE_BASE, /* type-type (base type) */ DEFAULT_TYPDELIM, /* array element delimiter */ F_ARRAY_IN, /* input procedure */ @@ -1018,6 +1020,8 @@ DefineEnum(CreateEnumStmt *stmt) InvalidOid, /* typmodout procedure - none */ InvalidOid, /* analyze procedure - default */ enumTypeOid, /* 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 */ @@ -1026,7 +1030,7 @@ DefineEnum(CreateEnumStmt *stmt) 'x', /* ARRAY is always toastable */ -1, /* typMod (Domains only) */ 0, /* Array dimensions of typbasetype */ - false); /* Type NOT NULL */ + false); /* Type NOT NULL */ pfree(enumArrayName); } @@ -1435,7 +1439,7 @@ AlterDomainDefault(List *names, Node *defaultRaw) /* Rebuild dependencies */ GenerateTypeDependencies(typTup->typnamespace, domainoid, - typTup->typrelid, + InvalidOid, /* typrelid is n/a */ 0, /* relation kind is n/a */ typTup->typowner, typTup->typinput, @@ -1446,6 +1450,7 @@ AlterDomainDefault(List *names, Node *defaultRaw) typTup->typmodout, typTup->typanalyze, typTup->typelem, + false, /* a domain isn't an implicit array */ typTup->typbasetype, defaultExpr, true); /* Rebuild is true */ @@ -2251,7 +2256,7 @@ AlterTypeOwner(List *names, Oid newOwnerId) /* * If it's a composite type, we need to check that it really is a - * free-standing composite type, and not a table's underlying type. We + * free-standing composite type, and not a table's rowtype. We * want people to use ALTER TABLE not ALTER TYPE for that case. */ if (typTup->typtype == TYPTYPE_COMPOSITE && @@ -2261,6 +2266,16 @@ AlterTypeOwner(List *names, Oid newOwnerId) errmsg("\"%s\" is a table's row type", TypeNameToString(typename)))); + /* don't allow direct alteration of array types, either */ + if (OidIsValid(typTup->typelem) && + get_array_type(typTup->typelem) == typeOid) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot alter array type %s", + format_type_be(typeOid)), + errhint("You can alter type %s, which will alter the array type as well.", + format_type_be(typTup->typelem)))); + /* * If the new owner is the same as the existing owner, consider the * command to have succeeded. This is for dump restoration purposes. @@ -2288,16 +2303,32 @@ AlterTypeOwner(List *names, Oid newOwnerId) } /* - * Modify the owner --- okay to scribble on typTup because it's a copy + * 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). */ - typTup->typowner = newOwnerId; + if (typTup->typtype == TYPTYPE_COMPOSITE) + ATExecChangeOwner(typTup->typrelid, newOwnerId, true); + else + { + /* + * We can just apply the modification directly. + * + * okay to scribble on typTup because it's a copy + */ + typTup->typowner = newOwnerId; - simple_heap_update(rel, &tup->t_self, tup); + simple_heap_update(rel, &tup->t_self, tup); - CatalogUpdateIndexes(rel, tup); + CatalogUpdateIndexes(rel, tup); - /* Update owner dependency reference */ - changeDependencyOnOwner(TypeRelationId, typeOid, newOwnerId); + /* Update owner dependency reference */ + changeDependencyOnOwner(TypeRelationId, typeOid, newOwnerId); + + /* If it has an array type, update that too */ + if (OidIsValid(typTup->typarray)) + AlterTypeOwnerInternal(typTup->typarray, newOwnerId, false); + } } /* Clean up */ @@ -2307,12 +2338,17 @@ AlterTypeOwner(List *names, Oid newOwnerId) /* * AlterTypeOwnerInternal - change type owner unconditionally * - * This is currently only used to propagate ALTER TABLE OWNER to the - * table's rowtype, and to implement REASSIGN OWNED BY. It assumes the - * caller has done all needed checks. + * 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. + * + * 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). */ void -AlterTypeOwnerInternal(Oid typeOid, Oid newOwnerId) +AlterTypeOwnerInternal(Oid typeOid, Oid newOwnerId, + bool hasDependEntry) { Relation rel; HeapTuple tup; @@ -2336,8 +2372,13 @@ AlterTypeOwnerInternal(Oid typeOid, Oid newOwnerId) CatalogUpdateIndexes(rel, tup); - /* Update owner dependency reference */ - changeDependencyOnOwner(TypeRelationId, typeOid, newOwnerId); + /* 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); /* Clean up */ heap_close(rel, RowExclusiveLock); @@ -2352,6 +2393,7 @@ AlterTypeNamespace(List *names, const char *newschema) TypeName *typename; Oid typeOid; Oid nspOid; + Oid elemOid; /* Make a TypeName so we can use standard type lookup machinery */ typename = makeTypeNameFromNameList(names); @@ -2365,8 +2407,18 @@ AlterTypeNamespace(List *names, const char *newschema) /* get schema OID and check its permissions */ nspOid = LookupCreationNamespace(newschema); + /* don't allow direct alteration of array types */ + elemOid = get_element_type(typeOid); + if (OidIsValid(elemOid) && get_array_type(elemOid) == typeOid) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot alter array type %s", + format_type_be(typeOid)), + errhint("You can alter type %s, which will alter the array type as well.", + format_type_be(elemOid)))); + /* and do the work */ - AlterTypeNamespaceInternal(typeOid, nspOid, true); + AlterTypeNamespaceInternal(typeOid, nspOid, false, true); } /* @@ -2374,18 +2426,24 @@ AlterTypeNamespace(List *names, const char *newschema) * * Caller must have already checked privileges. * + * The function automatically recurses to process the type's array type, + * if any. isImplicitArray should be TRUE only when doing this internal + * recursion (outside callers must never try to move an array type directly). + * * If errorOnTableType is TRUE, the function errors out if the type is * a table type. ALTER TABLE has to be used to move a table to a new * namespace. */ void AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid, + bool isImplicitArray, bool errorOnTableType) { Relation rel; HeapTuple tup; Form_pg_type typform; Oid oldNspOid; + Oid arrayOid; bool isCompositeType; rel = heap_open(TypeRelationId, RowExclusiveLock); @@ -2398,6 +2456,7 @@ AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid, typform = (Form_pg_type) GETSTRUCT(tup); oldNspOid = typform->typnamespace; + arrayOid = typform->typarray; if (oldNspOid == nspOid) ereport(ERROR, @@ -2463,13 +2522,9 @@ AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid, classRel = heap_open(RelationRelationId, RowExclusiveLock); - /* - * The dependency on the schema is listed under the pg_class entry, so - * tell AlterRelationNamespaceInternal to fix it. - */ AlterRelationNamespaceInternal(classRel, typform->typrelid, oldNspOid, nspOid, - true); + false); heap_close(classRel, RowExclusiveLock); @@ -2485,19 +2540,24 @@ AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid, /* If it's a domain, it might have constraints */ if (typform->typtype == TYPTYPE_DOMAIN) AlterConstraintNamespaces(typeOid, oldNspOid, nspOid, true); + } - /* - * Update dependency on schema, if any --- a table rowtype has not got - * one. - */ - if (typform->typtype != TYPTYPE_COMPOSITE) - if (changeDependencyFor(TypeRelationId, typeOid, + /* + * 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) && + !isImplicitArray) + if (changeDependencyFor(TypeRelationId, typeOid, NamespaceRelationId, oldNspOid, nspOid) != 1) - elog(ERROR, "failed to change schema dependency for type %s", - format_type_be(typeOid)); - } + elog(ERROR, "failed to change schema dependency for type %s", + format_type_be(typeOid)); heap_freetuple(tup); heap_close(rel, RowExclusiveLock); + + /* Recursively alter the associated array type, if any */ + if (OidIsValid(arrayOid)) + AlterTypeNamespaceInternal(arrayOid, nspOid, true, true); } diff --git a/src/backend/parser/parse_type.c b/src/backend/parser/parse_type.c index 0263386d2a2..d3198f0496a 100644 --- a/src/backend/parser/parse_type.c +++ b/src/backend/parser/parse_type.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/parse_type.c,v 1.89 2007/04/27 22:05:48 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_type.c,v 1.90 2007/05/11 17:57:12 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -116,10 +116,6 @@ LookupTypeName(ParseState *pstate, const TypeName *typename) /* deconstruct the name list */ DeconstructQualifiedName(typename->names, &schemaname, &typname); - /* If an array reference, look up the array type instead */ - if (typename->arrayBounds != NIL) - typname = makeArrayTypeName(typname); - if (schemaname) { /* Look in specific schema only */ @@ -136,6 +132,10 @@ LookupTypeName(ParseState *pstate, const TypeName *typename) /* Unqualified type name, so search the search path */ restype = TypenameGetTypid(typname); } + + /* If an array reference, return the array type instead */ + if (typename->arrayBounds != NIL) + restype = get_array_type(restype); } return restype; diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c index 391870c3a69..d86a70521e3 100644 --- a/src/backend/utils/cache/lsyscache.c +++ b/src/backend/utils/cache/lsyscache.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/cache/lsyscache.c,v 1.151 2007/04/02 03:49:39 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/cache/lsyscache.c,v 1.152 2007/05/11 17:57:12 tgl Exp $ * * NOTES * Eventually, the index information should go through here, too. @@ -2203,50 +2203,24 @@ get_element_type(Oid typid) /* * get_array_type * - * Given the type OID, get the corresponding array type. + * Given the type OID, get the corresponding "true" array type. * Returns InvalidOid if no array type can be found. - * - * NB: this only considers varlena arrays to be true arrays. */ Oid get_array_type(Oid typid) { HeapTuple tp; + Oid result = InvalidOid; tp = SearchSysCache(TYPEOID, ObjectIdGetDatum(typid), 0, 0, 0); if (HeapTupleIsValid(tp)) { - Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp); - char *array_typename; - Oid namespaceId; - - array_typename = makeArrayTypeName(NameStr(typtup->typname)); - namespaceId = typtup->typnamespace; + result = ((Form_pg_type) GETSTRUCT(tp))->typarray; ReleaseSysCache(tp); - - tp = SearchSysCache(TYPENAMENSP, - PointerGetDatum(array_typename), - ObjectIdGetDatum(namespaceId), - 0, 0); - - pfree(array_typename); - - if (HeapTupleIsValid(tp)) - { - Oid result; - - typtup = (Form_pg_type) GETSTRUCT(tp); - if (typtup->typlen == -1 && typtup->typelem == typid) - result = HeapTupleGetOid(tp); - else - result = InvalidOid; - ReleaseSysCache(tp); - return result; - } } - return InvalidOid; + return result; } /* |