Attached is my rework of David Fetter's array of composites patch. It has all the agreed modifications and checks, except altering the name mangling.

If people prefer I can apply this as it stands and then get working on the name mangling piece. Or I can hold off.

I'm still wondering if we can get away without a catalog change on that - e.g. could we look up an array type by looking for a pg_type entry containing the base type's oid in the typelem field? Or would that be too slow?

cheers

andrew
Index: doc/src/sgml/array.sgml
===================================================================
RCS file: /cvsroot/pgsql/doc/src/sgml/array.sgml,v
retrieving revision 1.60
diff -c -r1.60 array.sgml
*** doc/src/sgml/array.sgml	6 Apr 2007 19:22:38 -0000	1.60
--- doc/src/sgml/array.sgml	7 May 2007 17:17:29 -0000
***************
*** 11,17 ****
    <productname>PostgreSQL</productname> allows columns of a table to be
    defined as variable-length multidimensional arrays. Arrays of any
    built-in or user-defined base type or enum type can be created.
!   (Arrays of composite types or domains are not yet supported, however.)
   </para>
  
   <sect2>
--- 11,18 ----
    <productname>PostgreSQL</productname> allows columns of a table to be
    defined as variable-length multidimensional arrays. Arrays of any
    built-in or user-defined base type or enum type can be created.
!   Arrays of composite types are now supported.  Arrays of domains are
!   not yet supported.
   </para>
  
   <sect2>
Index: src/backend/catalog/heap.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/catalog/heap.c,v
retrieving revision 1.318
diff -c -r1.318 heap.c
*** src/backend/catalog/heap.c	2 Apr 2007 03:49:37 -0000	1.318
--- src/backend/catalog/heap.c	7 May 2007 17:17:31 -0000
***************
*** 45,50 ****
--- 45,51 ----
  #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"
***************
*** 402,417 ****
  	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)
  		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 */
--- 403,442 ----
  	char		att_typtype = get_typtype(atttypid);
  
  	/*
! 	 * For a composite type, recurse into its attributes. Otherwise, 
  	 *
! 	 * a) warn user, but don't fail, if column to be created has UNKNOWN type
! 	 *    (usually as a result of a 'retrieve into' - jolly)
! 	 *
! 	 * b) Refuse any attempt to create a pseudo-type column, except for 
! 	 *    pg_statistic hack.
  	 */
! 	if (att_typtype == TYPTYPE_COMPOSITE)
! 	{
! 		Relation relation;
! 		TupleDesc tupdesc;
! 		int i;
! 
! 		relation = RelationIdGetRelation(get_typ_typrelid(atttypid));
! 
! 		tupdesc = RelationGetDescr(relation);
! 
! 		for (i = 0; i < tupdesc->natts; i++)
! 		{
! 			if (tupdesc->attrs[i]->attisdropped)
! 				continue;
! 			CheckAttributeType(attname, tupdesc->attrs[i]->atttypid);
! 		}
! 
! 		RelationClose(relation);
! 	}
! 	else if (atttypid == UNKNOWNOID)
! 	{
  		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 */
***************
*** 763,768 ****
--- 788,794 ----
  	Relation	pg_class_desc;
  	Relation	new_rel_desc;
  	Oid			new_type_oid;
+ 	char	   *relarrayname;
  
  	pg_class_desc = heap_open(RelationRelationId, RowExclusiveLock);
  
***************
*** 815,820 ****
--- 841,880 ----
  									  relnamespace,
  									  relid,
  									  relkind);
+ 	/*
+ 	 * Add in the corresponding array types if appropriate.
+ 	 */
+ 	if (IsUnderPostmaster && (relkind == RELKIND_RELATION ||
+ 							  relkind == RELKIND_VIEW ||
+ 							  relkind == RELKIND_COMPOSITE_TYPE))
+ 	{
+ 		relarrayname = makeArrayTypeName(relname);
+ 		TypeCreate(relarrayname,		/* Array type name */
+ 				   relnamespace,		/* Same namespace as parent */
+ 				   InvalidOid,			/* Not composite, so no relationOid  */
+ 				   0,					/* relkind, also N/A here */
+ 				   -1,					/* Internal size, unlimited */
+ 				   TYPTYPE_BASE,		/* Not a complex type - typelem is */
+ 				   DEFAULT_TYPDELIM,	/* Use the default */
+ 				   F_ARRAY_IN,			/* Macro for array input procedure */
+ 				   F_ARRAY_OUT,			/* Macro for array output procedure */
+ 				   F_ARRAY_RECV,		/* Macro for array receive (binary input) procedure */
+ 				   F_ARRAY_SEND,		/* Macro for array send (binary output) procedure */
+ 				   InvalidOid,			/* No input typmod */
+ 				   InvalidOid,			/* No output typmod */
+ 				   InvalidOid,			/* Default ANALYZE procedure */
+ 				   new_type_oid,		/* The OID just created */
+ 				   InvalidOid,			/* No base type--this isn't a DOMAIN */
+ 				   NULL,				/* No default type value */
+ 				   NULL,				/* Don't send binary */
+ 				   false,				/* Never passed by value */
+ 				   'd',					/* Type align. Largest for safety */
+ 				   'x',					/* Always TOASTable */
+ 				   -1,					/* No typMod for non-domain types. */
+ 				   0,					/* Array diminsions of typbasetype */
+ 				   false);				/* Type NOT NULL */
+ 		pfree(relarrayname);	/* Seems like the right thing to do here. */
+ 	}
  
  	/*
  	 * now create an entry in pg_class for the relation.
Index: src/backend/commands/tablecmds.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v
retrieving revision 1.219
diff -c -r1.219 tablecmds.c
*** src/backend/commands/tablecmds.c	8 Apr 2007 01:26:32 -0000	1.219
--- src/backend/commands/tablecmds.c	7 May 2007 17:17:36 -0000
***************
*** 287,298 ****
  	Datum		reloptions;
  	ListCell   *listptr;
  	AttrNumber	attnum;
  
  	/*
! 	 * Truncate relname to appropriate length (probably a waste of time, as
! 	 * parser should have done this already).
  	 */
! 	StrNCpy(relname, stmt->relation->relname, NAMEDATALEN);
  
  	/*
  	 * Check consistency of arguments
--- 287,309 ----
  	Datum		reloptions;
  	ListCell   *listptr;
  	AttrNumber	attnum;
+ 	char	   *relarrayname;
  
  	/*
! 	 * Truncate relname to appropriate length (probably a waste of time, as *
! 	 * parser should have done this already).  Because tables and views now get
! 	 * an array type, this depends on the relkind.
  	 */
! 	if (relkind == RELKIND_RELATION ||
! 		relkind == RELKIND_VIEW ||
! 		relkind == RELKIND_COMPOSITE_TYPE)
! 	{
! 		StrNCpy(relname, stmt->relation->relname, NAMEDATALEN-2);
! 	}
! 	else
! 	{
! 		StrNCpy(relname, stmt->relation->relname, NAMEDATALEN);
! 	}
  
  	/*
  	 * Check consistency of arguments
***************
*** 6461,6468 ****
  
  	AlterRelationNamespaceInternal(classRel, relid, oldNspOid, nspOid, true);
  
! 	/* Fix the table's rowtype too */
  	AlterTypeNamespaceInternal(rel->rd_rel->reltype, nspOid, false);
  
  	/* Fix other dependent stuff */
  	if (rel->rd_rel->relkind == RELKIND_RELATION)
--- 6472,6480 ----
  
  	AlterRelationNamespaceInternal(classRel, relid, oldNspOid, nspOid, true);
  
! 	/* Fix the table's types too */
  	AlterTypeNamespaceInternal(rel->rd_rel->reltype, nspOid, false);
+ 	AlterTypeNamespaceInternal(get_array_type(rel->rd_rel->reltype), nspOid, false);
  
  	/* Fix other dependent stuff */
  	if (rel->rd_rel->relkind == RELKIND_RELATION)
Index: src/backend/commands/typecmds.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/commands/typecmds.c,v
retrieving revision 1.101
diff -c -r1.101 typecmds.c
*** src/backend/commands/typecmds.c	2 Apr 2007 03:49:38 -0000	1.101
--- src/backend/commands/typecmds.c	7 May 2007 17:17:37 -0000
***************
*** 474,479 ****
--- 474,480 ----
  	Oid			typeoid;
  	HeapTuple	tup;
  	ObjectAddress object;
+ 	Form_pg_type typ;
  
  	/* Make a TypeName so we can use standard type lookup machinery */
  	typename = makeTypeNameFromNameList(names);
***************
*** 505,513 ****
  	if (!HeapTupleIsValid(tup))
  		elog(ERROR, "cache lookup failed for type %u", typeoid);
  
  	/* Permission check: must own type or its namespace */
  	if (!pg_type_ownercheck(typeoid, GetUserId()) &&
! 	  !pg_namespace_ownercheck(((Form_pg_type) GETSTRUCT(tup))->typnamespace,
  							   GetUserId()))
  		aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
  					   TypeNameToString(typename));
--- 506,524 ----
  	if (!HeapTupleIsValid(tup))
  		elog(ERROR, "cache lookup failed for type %u", typeoid);
  
+ 	typ = (Form_pg_type) GETSTRUCT(tup);
+ 
+ 	/* don't alow direct deletion of array types */
+ 	if (typ->typelem != 0 && typ->typlen == -1)
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
+ 					 errmsg("cannot delete array type \"%s\" ",
+ 							TypeNameToString(typename))));
+ 
+ 
  	/* Permission check: must own type or its namespace */
  	if (!pg_type_ownercheck(typeoid, GetUserId()) &&
! 	  !pg_namespace_ownercheck(typ->typnamespace,
  							   GetUserId()))
  		aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
  					   TypeNameToString(typename));
***************
*** 2249,2254 ****
--- 2260,2273 ----
  		elog(ERROR, "cache lookup failed for type %u", typeOid);
  	typTup = (Form_pg_type) GETSTRUCT(tup);
  
+ 	/* don't allow direct alteration of array types */
+ 	if (typTup->typelem != 0 && typTup->typlen == -1)
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
+ 					 errmsg("cannot alter array type \"%s\" ",
+ 							TypeNameToString(typename))));
+ 
+ 
  	/*
  	 * 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
***************
*** 2352,2357 ****
--- 2371,2379 ----
  	TypeName   *typename;
  	Oid			typeOid;
  	Oid			nspOid;
+ 	Relation	rel;
+ 	HeapTuple	tup;
+ 	Form_pg_type typ;
  
  	/* Make a TypeName so we can use standard type lookup machinery */
  	typename = makeTypeNameFromNameList(names);
***************
*** 2365,2372 ****
--- 2387,2420 ----
  	/* get schema OID and check its permissions */
  	nspOid = LookupCreationNamespace(newschema);
  
+ 	/* don't allow direct alteration of array types */
+ 
+ 	rel = heap_open(TypeRelationId, RowExclusiveLock);
+ 
+ 	tup = SearchSysCacheCopy(TYPEOID,
+ 							 ObjectIdGetDatum(typeOid),
+ 							 0, 0, 0);
+ 	if (!HeapTupleIsValid(tup))
+ 		elog(ERROR, "cache lookup failed for type %u", typeOid);
+ 
+ 	typ = (Form_pg_type) GETSTRUCT(tup);
+ 
+ 	if (typ->typelem != 0 && typ->typlen == -1)
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
+ 					 errmsg("cannot alter array type \"%s\" ",
+ 							TypeNameToString(typename))));
+ 
+ 
+ 	heap_freetuple(tup);
+ 
+ 	heap_close(rel, RowExclusiveLock);
+ 
  	/* and do the work */
  	AlterTypeNamespaceInternal(typeOid, nspOid, true);
+ 	typeOid = get_array_type(typeOid);
+ 	if (typeOid != InvalidOid)
+ 		AlterTypeNamespaceInternal(typeOid, nspOid, true);
  }
  
  /*
***************
*** 2431,2442 ****
  
  	/* Detect whether type is a composite type (but not a table rowtype) */
  	isCompositeType =
! 		(typform->typtype == TYPTYPE_COMPOSITE &&
  		 get_rel_relkind(typform->typrelid) == RELKIND_COMPOSITE_TYPE);
  
  	/* Enforce not-table-type if requested */
  	if (typform->typtype == TYPTYPE_COMPOSITE && !isCompositeType &&
! 		errorOnTableType)
  		ereport(ERROR,
  				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
  				 errmsg("%s is a table's row type",
--- 2479,2490 ----
  
  	/* Detect whether type is a composite type (but not a table rowtype) */
  	isCompositeType =
! 		(typform->typtype == TYPTYPE_COMPOSITE && typform->typrelid != InvalidOid &&
  		 get_rel_relkind(typform->typrelid) == RELKIND_COMPOSITE_TYPE);
  
  	/* Enforce not-table-type if requested */
  	if (typform->typtype == TYPTYPE_COMPOSITE && !isCompositeType &&
! 		typform->typrelid != InvalidOid && errorOnTableType)
  		ereport(ERROR,
  				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
  				 errmsg("%s is a table's row type",
***************
*** 2457,2463 ****
  	 * We need to modify the pg_class tuple as well to reflect the change of
  	 * schema.
  	 */
! 	if (isCompositeType)
  	{
  		Relation	classRel;
  
--- 2505,2511 ----
  	 * We need to modify the pg_class tuple as well to reflect the change of
  	 * schema.
  	 */
! 	if (isCompositeType && typform->typrelid != InvalidOid)
  	{
  		Relation	classRel;
  
***************
*** 2490,2496 ****
  		 * Update dependency on schema, if any --- a table rowtype has not got
  		 * one.
  		 */
! 		if (typform->typtype != TYPTYPE_COMPOSITE)
  			if (changeDependencyFor(TypeRelationId, typeOid,
  								NamespaceRelationId, oldNspOid, nspOid) != 1)
  				elog(ERROR, "failed to change schema dependency for type %s",
--- 2538,2544 ----
  		 * Update dependency on schema, if any --- a table rowtype has not got
  		 * one.
  		 */
! 		if (typform->typtype != TYPTYPE_COMPOSITE || typform->typrelid == InvalidOid)
  			if (changeDependencyFor(TypeRelationId, typeOid,
  								NamespaceRelationId, oldNspOid, nspOid) != 1)
  				elog(ERROR, "failed to change schema dependency for type %s",
Index: src/test/regress/expected/alter_table.out
===================================================================
RCS file: /cvsroot/pgsql/src/test/regress/expected/alter_table.out,v
retrieving revision 1.101
diff -c -r1.101 alter_table.out
*** src/test/regress/expected/alter_table.out	14 Feb 2007 01:58:58 -0000	1.101
--- src/test/regress/expected/alter_table.out	7 May 2007 17:17:41 -0000
***************
*** 1456,1468 ****
--- 1456,1471 ----
  
  -- clean up
  drop schema alter2 cascade;
+ NOTICE:  drop cascades to type alter2.ctype[]
  NOTICE:  drop cascades to composite type alter2.ctype
  NOTICE:  drop cascades to type alter2.ctype
  NOTICE:  drop cascades to type alter2.posint
  NOTICE:  drop cascades to function alter2.plus1(integer)
+ NOTICE:  drop cascades to type alter2.v1[]
  NOTICE:  drop cascades to view alter2.v1
  NOTICE:  drop cascades to rule _RETURN on view alter2.v1
  NOTICE:  drop cascades to sequence alter2.t1_f1_seq
  NOTICE:  drop cascades to default for table alter2.t1 column f1
+ NOTICE:  drop cascades to type alter2.t1[]
  NOTICE:  drop cascades to table alter2.t1
  NOTICE:  drop cascades to constraint t1_f2_check on table alter2.t1
Index: src/test/regress/expected/type_sanity.out
===================================================================
RCS file: /cvsroot/pgsql/src/test/regress/expected/type_sanity.out,v
retrieving revision 1.29
diff -c -r1.29 type_sanity.out
*** src/test/regress/expected/type_sanity.out	2 Apr 2007 03:49:42 -0000	1.29
--- src/test/regress/expected/type_sanity.out	7 May 2007 17:17:41 -0000
***************
*** 49,55 ****
  -- or basic types that do.
  SELECT p1.oid, p1.typname
  FROM pg_type as p1
! WHERE (p1.typtype = 'c' AND p1.typrelid = 0) OR
      (p1.typtype != 'c' AND p1.typrelid != 0);
   oid | typname 
  -----+---------
--- 49,55 ----
  -- or basic types that do.
  SELECT p1.oid, p1.typname
  FROM pg_type as p1
! WHERE (p1.typtype = 'c' AND p1.typrelid = 0 AND p1.typname !~ '^_') OR
      (p1.typtype != 'c' AND p1.typrelid != 0);
   oid | typname 
  -----+---------
Index: src/test/regress/sql/type_sanity.sql
===================================================================
RCS file: /cvsroot/pgsql/src/test/regress/sql/type_sanity.sql,v
retrieving revision 1.29
diff -c -r1.29 type_sanity.sql
*** src/test/regress/sql/type_sanity.sql	2 Apr 2007 03:49:42 -0000	1.29
--- src/test/regress/sql/type_sanity.sql	7 May 2007 17:17:41 -0000
***************
*** 46,52 ****
  
  SELECT p1.oid, p1.typname
  FROM pg_type as p1
! WHERE (p1.typtype = 'c' AND p1.typrelid = 0) OR
      (p1.typtype != 'c' AND p1.typrelid != 0);
  
  -- Look for basic or enum types that don't have an array type.
--- 46,52 ----
  
  SELECT p1.oid, p1.typname
  FROM pg_type as p1
! WHERE (p1.typtype = 'c' AND p1.typrelid = 0 AND p1.typname !~ '^_') OR
      (p1.typtype != 'c' AND p1.typrelid != 0);
  
  -- Look for basic or enum types that don't have an array type.
---------------------------(end of broadcast)---------------------------
TIP 1: if posting/reading through Usenet, please send an appropriate
       subscribe-nomail command to [EMAIL PROTECTED] so that your
       message can get through to the mailing list cleanly

Reply via email to