stand-alone composite types patch (was [HACKERS] Proposal: stand-alone composite types) - Mailing list pgsql-patches
From | Joe Conway |
---|---|
Subject | stand-alone composite types patch (was [HACKERS] Proposal: stand-alone composite types) |
Date | |
Msg-id | 3D51F2DB.3070204@joeconway.com Whole thread Raw |
Responses |
Re: stand-alone composite types patch (was [HACKERS] Proposal: stand-alone composite types)
|
List | pgsql-patches |
Joe Conway wrote: > Based on Tom's suggestion, I propose the following: > > 1. Define a new pg_class relkind as 'c' for composite. Currently relkind > can be: 'S' sequence, 'i' index, 'r' relation, 's' special, 't' > toast, and 'v' view. > > 2. Borrow the needed parts from CREATE and DROP VIEW to implement a new > form of the CREATE TYPE command, with syntax something like: > > CREATE TYPE typename AS ( column_name data_type [, ... ] ) > > This would add a pg_class entry of relkind 'c', and add a new > pg_type entry of typtype 'c', with typrelid pointing to the > pg_class entry. Essentially, this new stand-alone composite type > looks a lot like a view without any rules. Items 1 and 2 from the proposal above are implemented in the attached patch. I was able to get rid of the reduce/reduce conflict with Tom's help (thanks Tom!). test=# CREATE TYPE compfoo AS (f1 int, f2 int); CREATE TYPE test=# CREATE FUNCTION getfoo() RETURNS SETOF compfoo AS 'SELECT fooid, foosubid FROM foo' LANGUAGE SQL; CREATE FUNCTION test=# SELECT * FROM getfoo(); f1 | f2 ----+---- 1 | 1 1 | 2 (2 rows) test=# DROP TYPE compfoo; NOTICE: function getfoo() depends on type compfoo ERROR: Cannot drop relation compfoo because other objects depend on it Use DROP ... CASCADE to drop the dependent objects too test=# DROP TYPE compfoo CASCADE; NOTICE: Drop cascades to function getfoo() DROP TYPE Passes all regression tests (well, I'm on RedHat 7.3, so there are three "expected" failures). Doc and regression adjustments included. If there are no objections, please apply. Thanks, Joe Index: doc/src/sgml/ref/create_type.sgml =================================================================== RCS file: /opt/src/cvs/pgsql-server/doc/src/sgml/ref/create_type.sgml,v retrieving revision 1.30 diff -c -r1.30 create_type.sgml *** doc/src/sgml/ref/create_type.sgml 24 Jul 2002 19:11:07 -0000 1.30 --- doc/src/sgml/ref/create_type.sgml 8 Aug 2002 03:54:32 -0000 *************** *** 30,35 **** --- 30,42 ---- [ , ALIGNMENT = <replaceable class="parameter">alignment</replaceable> ] [ , STORAGE = <replaceable class="parameter">storage</replaceable> ] ) + + CREATE TYPE <replaceable class="parameter">typename</replaceable> AS + ( <replaceable class="PARAMETER">column_definition_list</replaceable> ) + + where <replaceable class="PARAMETER">column_definition_list</replaceable> can be: + + ( <replaceable class="PARAMETER">column_name</replaceable> <replaceable class="PARAMETER">data_type</replaceable> [, ...] ) </synopsis> <refsect2 id="R2-SQL-CREATETYPE-1"> *************** *** 138,143 **** --- 145,169 ---- </para> </listitem> </varlistentry> + + <varlistentry> + <term><replaceable class="PARAMETER">column_name</replaceable></term> + <listitem> + <para> + The name of a column of the composite type. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><replaceable class="PARAMETER">data_type</replaceable></term> + <listitem> + <para> + The name of an existing data type. + </para> + </listitem> + </varlistentry> + </variablelist> </para> </refsect2> *************** *** 191,199 **** </para> <para> ! <command>CREATE TYPE</command> requires the registration of two functions ! (using CREATE FUNCTION) before defining the type. The ! representation of a new base type is determined by <replaceable class="parameter">input_function</replaceable>, which converts the type's external representation to an internal representation usable by the --- 217,225 ---- </para> <para> ! The first form of <command>CREATE TYPE</command> requires the ! registration of two functions (using CREATE FUNCTION) before defining the ! type. The representation of a new base type is determined by <replaceable class="parameter">input_function</replaceable>, which converts the type's external representation to an internal representation usable by the *************** *** 288,293 **** --- 314,327 ---- <literal>extended</literal> and <literal>external</literal> items.) </para> + <para> + The second form of <command>CREATE TYPE</command> requires a column + definition list in the form ( <replaceable class="PARAMETER">column_name</replaceable> + <replaceable class="PARAMETER">data_type</replaceable> [, ... ] ). This + creates a composite type, similar to that of a TABLE or VIEW relation. + A stand-alone composite type is useful as the return type of FUNCTION. + </para> + <refsect2> <title>Array Types</title> *************** *** 370,375 **** --- 404,418 ---- CREATE TYPE bigobj (INPUT = lo_filein, OUTPUT = lo_fileout, INTERNALLENGTH = VARIABLE); CREATE TABLE big_objs (id int4, obj bigobj); + </programlisting> + </para> + + <para> + This example creates a composite type and uses it in + a table function definition: + <programlisting> + CREATE TYPE compfoo AS (f1 int, f2 int); + CREATE FUNCTION getfoo() RETURNS SETOF compfoo AS 'SELECT fooid, foorefid FROM foo' LANGUAGE SQL; </programlisting> </para> </refsect1> Index: src/backend/catalog/heap.c =================================================================== RCS file: /opt/src/cvs/pgsql-server/src/backend/catalog/heap.c,v retrieving revision 1.219 diff -c -r1.219 heap.c *** src/backend/catalog/heap.c 6 Aug 2002 02:36:33 -0000 1.219 --- src/backend/catalog/heap.c 8 Aug 2002 02:49:47 -0000 *************** *** 764,770 **** /* * We create the disk file for this relation here */ ! if (relkind != RELKIND_VIEW) heap_storage_create(new_rel_desc); /* --- 764,770 ---- /* * We create the disk file for this relation here */ ! if (relkind != RELKIND_VIEW && relkind != RELKIND_COMPOSITE_TYPE) heap_storage_create(new_rel_desc); /* *************** *** 1135,1141 **** /* * unlink the relation's physical file and finish up. */ ! if (rel->rd_rel->relkind != RELKIND_VIEW) smgrunlink(DEFAULT_SMGR, rel); /* --- 1135,1142 ---- /* * unlink the relation's physical file and finish up. */ ! if (rel->rd_rel->relkind != RELKIND_VIEW && ! rel->rd_rel->relkind != RELKIND_COMPOSITE_TYPE) smgrunlink(DEFAULT_SMGR, rel); /* Index: src/backend/commands/typecmds.c =================================================================== RCS file: /opt/src/cvs/pgsql-server/src/backend/commands/typecmds.c,v retrieving revision 1.8 diff -c -r1.8 typecmds.c *** src/backend/commands/typecmds.c 24 Jul 2002 19:11:09 -0000 1.8 --- src/backend/commands/typecmds.c 8 Aug 2002 02:49:47 -0000 *************** *** 38,43 **** --- 38,44 ---- #include "catalog/namespace.h" #include "catalog/pg_type.h" #include "commands/defrem.h" + #include "commands/tablecmds.h" #include "miscadmin.h" #include "parser/parse_func.h" #include "parser/parse_type.h" *************** *** 49,55 **** static Oid findTypeIOFunction(List *procname, bool isOutput); ! /* * DefineType --- 50,57 ---- static Oid findTypeIOFunction(List *procname, bool isOutput); ! static void DefineCompositeTypeRelation(const RangeVar *typevar, ! List *coldeflist); /* * DefineType *************** *** 251,256 **** --- 253,259 ---- Oid typeoid; HeapTuple tup; ObjectAddress object; + char typtype; /* Make a TypeName so we can use standard type lookup machinery */ typename = makeNode(TypeName); *************** *** 277,289 **** GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, TypeNameToString(typename)); ReleaseSysCache(tup); /* * Do the deletion */ ! object.classId = RelOid_pg_type; ! object.objectId = typeoid; object.objectSubId = 0; performDeletion(&object, behavior); --- 280,317 ---- GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, TypeNameToString(typename)); + typtype = ((Form_pg_type) GETSTRUCT(tup))->typtype; + ReleaseSysCache(tup); /* * Do the deletion */ ! if (typtype != 'c') ! { ! object.classId = RelOid_pg_type; ! object.objectId = typeoid; ! } ! else ! { ! Oid relid = typeidTypeRelid(typeoid); ! Relation rel = relation_open(relid, AccessShareLock); ! char relkind = rel->rd_rel->relkind; ! ! relation_close(rel, AccessShareLock); ! ! if (relkind == RELKIND_COMPOSITE_TYPE) ! { ! object.classId = RelOid_pg_class; ! object.objectId = relid; ! } ! else ! { ! object.classId = RelOid_pg_type; ! object.objectId = typeoid; ! } ! ! } object.objectSubId = 0; performDeletion(&object, behavior); *************** *** 665,668 **** --- 693,758 ---- } return procOid; + } + + /*--------------------------------------------------------------------- + * DefineCompositeTypeRelation + * + * Create a Composite Type relation. + * `DefineRelation' does all the work, we just provide the correct + * arguments! + * + * If the relation already exists, then 'DefineRelation' will abort + * the xact... + *--------------------------------------------------------------------- + */ + static void + DefineCompositeTypeRelation(const RangeVar *typevar, List *coldeflist) + { + Oid relid; + CreateStmt *createStmt = makeNode(CreateStmt); + + if (coldeflist == NIL) + elog(ERROR, "attempted to define composite type relation with" + " no attrs"); + + /* + * now create the parameters for keys/inheritance etc. All of them are + * nil... + */ + createStmt->relation = (RangeVar *) typevar; + createStmt->tableElts = coldeflist; + createStmt->inhRelations = NIL; + createStmt->constraints = NIL; + createStmt->hasoids = false; + + /* + * finally create the relation... + */ + relid = DefineRelation(createStmt, RELKIND_COMPOSITE_TYPE); + } + + /*------------------------------------------------------------------- + * DefineCompositeType + * + * - takes a "typevar", "coldeflist" pair and then + * constructs the "virtual" relation + *------------------------------------------------------------------- + */ + void + DefineCompositeType(const RangeVar *typevar, List *coldeflist) + { + /* + * Create the composite type relation + * + * NOTE: if it already exists, the xact will be aborted. + */ + DefineCompositeTypeRelation(typevar, coldeflist); + + /* + * The relation we have just created is not visible to any other + * commands running with the same transaction & command id. So, + * increment the command id counter (but do NOT pfree any memory!!!!) + */ + CommandCounterIncrement(); } Index: src/backend/nodes/copyfuncs.c =================================================================== RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/copyfuncs.c,v retrieving revision 1.200 diff -c -r1.200 copyfuncs.c *** src/backend/nodes/copyfuncs.c 4 Aug 2002 19:48:09 -0000 1.200 --- src/backend/nodes/copyfuncs.c 8 Aug 2002 02:49:47 -0000 *************** *** 2233,2238 **** --- 2233,2249 ---- return newnode; } + static CompositeTypeStmt * + _copyCompositeTypeStmt(CompositeTypeStmt *from) + { + CompositeTypeStmt *newnode = makeNode(CompositeTypeStmt); + + Node_Copy(from, newnode, typevar); + Node_Copy(from, newnode, coldeflist); + + return newnode; + } + static ViewStmt * _copyViewStmt(ViewStmt *from) { *************** *** 2938,2943 **** --- 2949,2957 ---- break; case T_TransactionStmt: retval = _copyTransactionStmt(from); + break; + case T_CompositeTypeStmt: + retval = _copyCompositeTypeStmt(from); break; case T_ViewStmt: retval = _copyViewStmt(from); Index: src/backend/nodes/equalfuncs.c =================================================================== RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/equalfuncs.c,v retrieving revision 1.149 diff -c -r1.149 equalfuncs.c *** src/backend/nodes/equalfuncs.c 4 Aug 2002 23:49:59 -0000 1.149 --- src/backend/nodes/equalfuncs.c 8 Aug 2002 02:49:47 -0000 *************** *** 1062,1067 **** --- 1062,1078 ---- } static bool + _equalCompositeTypeStmt(CompositeTypeStmt *a, CompositeTypeStmt *b) + { + if (!equal(a->typevar, b->typevar)) + return false; + if (!equal(a->coldeflist, b->coldeflist)) + return false; + + return true; + } + + static bool _equalViewStmt(ViewStmt *a, ViewStmt *b) { if (!equal(a->view, b->view)) *************** *** 2110,2115 **** --- 2121,2129 ---- break; case T_TransactionStmt: retval = _equalTransactionStmt(a, b); + break; + case T_CompositeTypeStmt: + retval = _equalCompositeTypeStmt(a, b); break; case T_ViewStmt: retval = _equalViewStmt(a, b); Index: src/backend/parser/gram.y =================================================================== RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/gram.y,v retrieving revision 2.357 diff -c -r2.357 gram.y *** src/backend/parser/gram.y 6 Aug 2002 05:40:45 -0000 2.357 --- src/backend/parser/gram.y 8 Aug 2002 03:38:46 -0000 *************** *** 205,211 **** %type <list> stmtblock, stmtmulti, OptTableElementList, TableElementList, OptInherit, definition, ! opt_distinct, opt_definition, func_args, func_args_list, func_as, createfunc_opt_list oper_argtypes, RuleActionList, RuleActionMulti, opt_column_list, columnList, opt_name_list, --- 205,211 ---- %type <list> stmtblock, stmtmulti, OptTableElementList, TableElementList, OptInherit, definition, ! opt_distinct, opt_definition, func_args, rowdefinition func_args_list, func_as, createfunc_opt_list oper_argtypes, RuleActionList, RuleActionMulti, opt_column_list, columnList, opt_name_list, *************** *** 2247,2252 **** --- 2247,2285 ---- n->definition = $4; $$ = (Node *)n; } + | CREATE TYPE_P any_name AS rowdefinition + { + CompositeTypeStmt *n = makeNode(CompositeTypeStmt); + RangeVar *r = makeNode(RangeVar); + + switch (length($3)) + { + case 1: + r->catalogname = NULL; + r->schemaname = NULL; + r->relname = strVal(lfirst($3)); + break; + case 2: + r->catalogname = NULL; + r->schemaname = strVal(lfirst($3)); + r->relname = strVal(lsecond($3)); + break; + case 3: + r->catalogname = strVal(lfirst($3)); + r->schemaname = strVal(lsecond($3)); + r->relname = strVal(lfirst(lnext(lnext($3)))); + break; + default: + elog(ERROR, + "Improper qualified name " + "(too many dotted names): %s", + NameListToString($3)); + break; + } + n->typevar = r; + n->coldeflist = $5; + $$ = (Node *)n; + } | CREATE CHARACTER SET opt_as any_name GET definition opt_collate { DefineStmt *n = makeNode(DefineStmt); *************** *** 2255,2260 **** --- 2288,2296 ---- n->definition = $7; $$ = (Node *)n; } + ; + + rowdefinition: '(' TableFuncElementList ')' { $$ = $2; } ; definition: '(' def_list ')' { $$ = $2; } Index: src/backend/tcop/postgres.c =================================================================== RCS file: /opt/src/cvs/pgsql-server/src/backend/tcop/postgres.c,v retrieving revision 1.280 diff -c -r1.280 postgres.c *** src/backend/tcop/postgres.c 6 Aug 2002 05:24:04 -0000 1.280 --- src/backend/tcop/postgres.c 8 Aug 2002 02:49:47 -0000 *************** *** 2264,2269 **** --- 2264,2273 ---- } break; + case T_CompositeTypeStmt: + tag = "CREATE TYPE"; + break; + case T_ViewStmt: tag = "CREATE VIEW"; break; Index: src/backend/tcop/utility.c =================================================================== RCS file: /opt/src/cvs/pgsql-server/src/backend/tcop/utility.c,v retrieving revision 1.169 diff -c -r1.169 utility.c *** src/backend/tcop/utility.c 7 Aug 2002 21:45:02 -0000 1.169 --- src/backend/tcop/utility.c 8 Aug 2002 02:49:47 -0000 *************** *** 573,578 **** --- 573,586 ---- } break; + case T_CompositeTypeStmt: /* CREATE TYPE (composite) */ + { + CompositeTypeStmt *stmt = (CompositeTypeStmt *) parsetree; + + DefineCompositeType(stmt->typevar, stmt->coldeflist); + } + break; + case T_ViewStmt: /* CREATE VIEW */ { ViewStmt *stmt = (ViewStmt *) parsetree; Index: src/include/catalog/pg_class.h =================================================================== RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_class.h,v retrieving revision 1.70 diff -c -r1.70 pg_class.h *** src/include/catalog/pg_class.h 2 Aug 2002 18:15:09 -0000 1.70 --- src/include/catalog/pg_class.h 8 Aug 2002 02:49:47 -0000 *************** *** 169,173 **** --- 169,174 ---- #define RELKIND_UNCATALOGED 'u' /* temporary heap */ #define RELKIND_TOASTVALUE 't' /* moved off huge values */ #define RELKIND_VIEW 'v' /* view */ + #define RELKIND_COMPOSITE_TYPE 'c' /* composite type */ #endif /* PG_CLASS_H */ Index: src/include/commands/defrem.h =================================================================== RCS file: /opt/src/cvs/pgsql-server/src/include/commands/defrem.h,v retrieving revision 1.43 diff -c -r1.43 defrem.h *** src/include/commands/defrem.h 29 Jul 2002 22:14:11 -0000 1.43 --- src/include/commands/defrem.h 8 Aug 2002 02:49:47 -0000 *************** *** 58,63 **** --- 58,64 ---- extern void RemoveTypeById(Oid typeOid); extern void DefineDomain(CreateDomainStmt *stmt); extern void RemoveDomain(List *names, DropBehavior behavior); + extern void DefineCompositeType(const RangeVar *typevar, List *coldeflist); extern void DefineOpClass(CreateOpClassStmt *stmt); extern void RemoveOpClass(RemoveOpClassStmt *stmt); Index: src/include/nodes/nodes.h =================================================================== RCS file: /opt/src/cvs/pgsql-server/src/include/nodes/nodes.h,v retrieving revision 1.114 diff -c -r1.114 nodes.h *** src/include/nodes/nodes.h 29 Jul 2002 22:14:11 -0000 1.114 --- src/include/nodes/nodes.h 8 Aug 2002 02:49:47 -0000 *************** *** 238,243 **** --- 238,244 ---- T_PrivTarget, T_InsertDefault, T_CreateOpClassItem, + T_CompositeTypeStmt, /* * TAGS FOR FUNCTION-CALL CONTEXT AND RESULTINFO NODES (see fmgr.h) Index: src/include/nodes/parsenodes.h =================================================================== RCS file: /opt/src/cvs/pgsql-server/src/include/nodes/parsenodes.h,v retrieving revision 1.198 diff -c -r1.198 parsenodes.h *** src/include/nodes/parsenodes.h 4 Aug 2002 19:48:10 -0000 1.198 --- src/include/nodes/parsenodes.h 8 Aug 2002 02:49:47 -0000 *************** *** 1402,1407 **** --- 1402,1419 ---- } TransactionStmt; /* ---------------------- + * Create Type Statement, composite types + * ---------------------- + */ + typedef struct CompositeTypeStmt + { + NodeTag type; + RangeVar *typevar; /* the composite type to be created */ + List *coldeflist; /* list of ColumnDef nodes */ + } CompositeTypeStmt; + + + /* ---------------------- * Create View Statement * ---------------------- */ Index: src/test/regress/expected/create_type.out =================================================================== RCS file: /opt/src/cvs/pgsql-server/src/test/regress/expected/create_type.out,v retrieving revision 1.4 diff -c -r1.4 create_type.out *** src/test/regress/expected/create_type.out 6 Sep 2001 02:07:42 -0000 1.4 --- src/test/regress/expected/create_type.out 8 Aug 2002 03:41:27 -0000 *************** *** 37,40 **** --- 37,53 ---- zippo | 42 (1 row) + -- Test stand-alone composite type + CREATE TYPE default_test_row AS (f1 text_w_default, f2 int42); + CREATE FUNCTION get_default_test() RETURNS SETOF default_test_row AS ' + SELECT * FROM default_test; + ' LANGUAGE SQL; + SELECT * FROM get_default_test(); + f1 | f2 + -------+---- + zippo | 42 + (1 row) + + DROP TYPE default_test_row CASCADE; + NOTICE: Drop cascades to function get_default_test() DROP TABLE default_test; Index: src/test/regress/sql/create_type.sql =================================================================== RCS file: /opt/src/cvs/pgsql-server/src/test/regress/sql/create_type.sql,v retrieving revision 1.4 diff -c -r1.4 create_type.sql *** src/test/regress/sql/create_type.sql 6 Sep 2001 02:07:42 -0000 1.4 --- src/test/regress/sql/create_type.sql 8 Aug 2002 03:09:48 -0000 *************** *** 41,44 **** --- 41,56 ---- SELECT * FROM default_test; + -- Test stand-alone composite type + + CREATE TYPE default_test_row AS (f1 text_w_default, f2 int42); + + CREATE FUNCTION get_default_test() RETURNS SETOF default_test_row AS ' + SELECT * FROM default_test; + ' LANGUAGE SQL; + + SELECT * FROM get_default_test(); + + DROP TYPE default_test_row CASCADE; + DROP TABLE default_test;
pgsql-patches by date: