Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 8e68d78

Browse files
committed
Allow the syntax CREATE TYPE foo, with no parameters, to permit explicit
creation of a shell type. This allows a less hacky way of dealing with the mutual dependency between a datatype and its I/O functions: make a shell type, then make the functions, then define the datatype fully. We should fix pg_dump to handle things this way, but this commit just deals with the backend. Martijn van Oosterhout, with some corrections by Tom Lane.
1 parent 7f19339 commit 8e68d78

File tree

12 files changed

+220
-86
lines changed

12 files changed

+220
-86
lines changed

doc/src/sgml/ref/create_type.sgml

+37-15
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<!--
2-
$PostgreSQL: pgsql/doc/src/sgml/ref/create_type.sgml,v 1.60 2006/01/13 18:06:45 tgl Exp $
2+
$PostgreSQL: pgsql/doc/src/sgml/ref/create_type.sgml,v 1.61 2006/02/28 22:37:25 tgl Exp $
33
PostgreSQL documentation
44
-->
55

@@ -37,6 +37,8 @@ CREATE TYPE <replaceable class="parameter">name</replaceable> (
3737
[ , ELEMENT = <replaceable class="parameter">element</replaceable> ]
3838
[ , DELIMITER = <replaceable class="parameter">delimiter</replaceable> ]
3939
)
40+
41+
CREATE TYPE <replaceable class="parameter">name</replaceable>
4042
</synopsis>
4143
</refsynopsisdiv>
4244

@@ -142,17 +144,16 @@ CREATE TYPE <replaceable class="parameter">name</replaceable> (
142144

143145
<para>
144146
You should at this point be wondering how the input and output functions
145-
can be declared to have results or arguments of the new type, when they have
146-
to be created before the new type can be created. The answer is that the
147-
input function must be created first, then the output function (and
148-
the binary I/O functions if wanted), and finally the data type.
149-
<productname>PostgreSQL</productname> will first see the name of the new
150-
data type as the return type of the input function. It will create a
151-
<quote>shell</> type, which is simply a placeholder entry in
152-
the system catalog, and link the input function definition to the shell
153-
type. Similarly the other functions will be linked to the (now already
154-
existing) shell type. Finally, <command>CREATE TYPE</> replaces the
155-
shell entry with a complete type definition, and the new type can be used.
147+
can be declared to have results or arguments of the new type, when they
148+
have to be created before the new type can be created. The answer is that
149+
the type should first be defined as a <firstterm>shell type</>, which is a
150+
placeholder type that has no properties except a name and an owner. This
151+
is done by issuing the command <literal>CREATE TYPE
152+
<replaceable>name</></literal>, with no additional parameters. Then the
153+
I/O functions can be defined referencing the shell type. Finally,
154+
<command>CREATE TYPE</> with a full definition replaces the shell entry
155+
with a complete, valid type definition, after which the new type can be
156+
used normally.
156157
</para>
157158

158159
<para>
@@ -457,17 +458,33 @@ CREATE TYPE <replaceable class="parameter">name</replaceable> (
457458
while converting it to or from external form.
458459
</para>
459460

461+
<para>
462+
Before <productname>PostgreSQL</productname> version 8.2, the syntax
463+
<literal>CREATE TYPE <replaceable>name</></literal> did not exist.
464+
The way to create a new base type was to create its input function first.
465+
In this approach, <productname>PostgreSQL</productname> will first see
466+
the name of the new data type as the return type of the input function.
467+
The shell type is implicitly created in this situation, and then it
468+
can be referenced in the definitions of the remaining I/O functions.
469+
This approach still works, but is deprecated and may be disallowed in
470+
some future release. Also, to avoid accidentally cluttering
471+
the catalogs with shell types as a result of simple typos in function
472+
definitions, a shell type will only be made this way when the input
473+
function is written in C.
474+
</para>
475+
460476
<para>
461477
In <productname>PostgreSQL</productname> versions before 7.3, it
462-
was customary to avoid creating a shell type by replacing the
478+
was customary to avoid creating a shell type at all, by replacing the
463479
functions' forward references to the type name with the placeholder
464480
pseudotype <type>opaque</>. The <type>cstring</> arguments and
465481
results also had to be declared as <type>opaque</> before 7.3. To
466482
support loading of old dump files, <command>CREATE TYPE</> will
467-
accept functions declared using <type>opaque</>, but it will issue
468-
a notice and change the function's declaration to use the correct
483+
accept I/O functions declared using <type>opaque</>, but it will issue
484+
a notice and change the function declarations to use the correct
469485
types.
470486
</para>
487+
471488
</refsect1>
472489

473490
<refsect1>
@@ -489,6 +506,11 @@ $$ LANGUAGE SQL;
489506
This example creates the base data type <type>box</type> and then uses the
490507
type in a table definition:
491508
<programlisting>
509+
CREATE TYPE box;
510+
511+
CREATE FUNCTION my_box_in_function(cstring) RETURNS box AS ... ;
512+
CREATE FUNCTION my_box_out_function(box) RETURNS cstring AS ... ;
513+
492514
CREATE TYPE box (
493515
INTERNALLENGTH = 16,
494516
INPUT = my_box_in_function,

doc/src/sgml/xtypes.sgml

+12-9
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<!--
2-
$PostgreSQL: pgsql/doc/src/sgml/xtypes.sgml,v 1.25 2005/01/10 00:04:38 tgl Exp $
2+
$PostgreSQL: pgsql/doc/src/sgml/xtypes.sgml,v 1.26 2006/02/28 22:37:25 tgl Exp $
33
-->
44

55
<sect1 id="xtypes">
@@ -168,8 +168,16 @@ complex_send(PG_FUNCTION_ARGS)
168168
</para>
169169

170170
<para>
171-
To define the <type>complex</type> type, we need to create the
172-
user-defined I/O functions before creating the type:
171+
Once we have written the I/O functions and compiled them into a shared
172+
library, we can define the <type>complex</type> type in SQL.
173+
First we declare it as a shell type:
174+
175+
<programlisting>
176+
CREATE TYPE complex;
177+
</programlisting>
178+
179+
This serves as a placeholder that allows us to reference the type while
180+
defining its I/O functions. Now we can define the I/O functions:
173181

174182
<programlisting>
175183
CREATE FUNCTION complex_in(cstring)
@@ -192,15 +200,10 @@ CREATE FUNCTION complex_send(complex)
192200
AS '<replaceable>filename</replaceable>'
193201
LANGUAGE C IMMUTABLE STRICT;
194202
</programlisting>
195-
196-
Notice that the declarations of the input and output functions must
197-
reference the not-yet-defined type. This is allowed, but will draw
198-
warning messages that may be ignored. The input function must
199-
appear first.
200203
</para>
201204

202205
<para>
203-
Finally, we can declare the data type:
206+
Finally, we can provide the full definition of the data type:
204207
<programlisting>
205208
CREATE TYPE complex (
206209
internallength = 16,

src/backend/catalog/pg_type.c

+43-31
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/catalog/pg_type.c,v 1.104 2005/10/15 02:49:14 momjian Exp $
11+
* $PostgreSQL: pgsql/src/backend/catalog/pg_type.c,v 1.105 2006/02/28 22:37:25 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -20,23 +20,26 @@
2020
#include "catalog/pg_namespace.h"
2121
#include "catalog/pg_proc.h"
2222
#include "catalog/pg_type.h"
23+
#include "commands/typecmds.h"
2324
#include "miscadmin.h"
25+
#include "utils/acl.h"
2426
#include "utils/builtins.h"
27+
#include "utils/fmgroids.h"
2528
#include "utils/lsyscache.h"
2629
#include "utils/syscache.h"
2730

2831

2932
/* ----------------------------------------------------------------
3033
* TypeShellMake
3134
*
32-
* This procedure inserts a "shell" tuple into the type
33-
* relation. The type tuple inserted has invalid values
34-
* and in particular, the "typisdefined" field is false.
35+
* This procedure inserts a "shell" tuple into the pg_type relation.
36+
* The type tuple inserted has valid but dummy values, and its
37+
* "typisdefined" field is false indicating it's not really defined.
3538
*
36-
* This is used so that a tuple exists in the catalogs.
37-
* The invalid fields should be fixed up sometime after
38-
* this routine is called, and then the "typeisdefined"
39-
* field is set to true. -cim 6/15/90
39+
* This is used so that a tuple exists in the catalogs. The I/O
40+
* functions for the type will link to this tuple. When the full
41+
* CREATE TYPE command is issued, the bogus values will be replaced
42+
* with correct ones, and "typisdefined" will be set to true.
4043
* ----------------------------------------------------------------
4144
*/
4245
Oid
@@ -70,30 +73,35 @@ TypeShellMake(const char *typeName, Oid typeNamespace)
7073

7174
/*
7275
* initialize *values with the type name and dummy values
76+
*
77+
* The representational details are the same as int4 ... it doesn't
78+
* really matter what they are so long as they are consistent. Also
79+
* note that we give it typtype = 'p' (pseudotype) as extra insurance
80+
* that it won't be mistaken for a usable type.
7381
*/
7482
i = 0;
7583
namestrcpy(&name, typeName);
7684
values[i++] = NameGetDatum(&name); /* typname */
7785
values[i++] = ObjectIdGetDatum(typeNamespace); /* typnamespace */
7886
values[i++] = ObjectIdGetDatum(GetUserId()); /* typowner */
79-
values[i++] = Int16GetDatum(0); /* typlen */
80-
values[i++] = BoolGetDatum(false); /* typbyval */
81-
values[i++] = CharGetDatum(0); /* typtype */
82-
values[i++] = BoolGetDatum(false); /* typisdefined */
83-
values[i++] = CharGetDatum(0); /* typdelim */
84-
values[i++] = ObjectIdGetDatum(InvalidOid); /* typrelid */
85-
values[i++] = ObjectIdGetDatum(InvalidOid); /* typelem */
86-
values[i++] = ObjectIdGetDatum(InvalidOid); /* typinput */
87-
values[i++] = ObjectIdGetDatum(InvalidOid); /* typoutput */
88-
values[i++] = ObjectIdGetDatum(InvalidOid); /* typreceive */
89-
values[i++] = ObjectIdGetDatum(InvalidOid); /* typsend */
90-
values[i++] = ObjectIdGetDatum(InvalidOid); /* typanalyze */
91-
values[i++] = CharGetDatum('i'); /* typalign */
92-
values[i++] = CharGetDatum('p'); /* typstorage */
93-
values[i++] = BoolGetDatum(false); /* typnotnull */
94-
values[i++] = ObjectIdGetDatum(InvalidOid); /* typbasetype */
95-
values[i++] = Int32GetDatum(-1); /* typtypmod */
96-
values[i++] = Int32GetDatum(0); /* typndims */
87+
values[i++] = Int16GetDatum(sizeof(int4)); /* typlen */
88+
values[i++] = BoolGetDatum(true); /* typbyval */
89+
values[i++] = CharGetDatum('p'); /* typtype */
90+
values[i++] = BoolGetDatum(false); /* typisdefined */
91+
values[i++] = CharGetDatum(DEFAULT_TYPDELIM); /* typdelim */
92+
values[i++] = ObjectIdGetDatum(InvalidOid); /* typrelid */
93+
values[i++] = ObjectIdGetDatum(InvalidOid); /* typelem */
94+
values[i++] = ObjectIdGetDatum(F_SHELL_IN); /* typinput */
95+
values[i++] = ObjectIdGetDatum(F_SHELL_OUT); /* typoutput */
96+
values[i++] = ObjectIdGetDatum(InvalidOid); /* typreceive */
97+
values[i++] = ObjectIdGetDatum(InvalidOid); /* typsend */
98+
values[i++] = ObjectIdGetDatum(InvalidOid); /* typanalyze */
99+
values[i++] = CharGetDatum('i'); /* typalign */
100+
values[i++] = CharGetDatum('p'); /* typstorage */
101+
values[i++] = BoolGetDatum(false); /* typnotnull */
102+
values[i++] = ObjectIdGetDatum(InvalidOid); /* typbasetype */
103+
values[i++] = Int32GetDatum(-1); /* typtypmod */
104+
values[i++] = Int32GetDatum(0); /* typndims */
97105
nulls[i++] = 'n'; /* typdefaultbin */
98106
nulls[i++] = 'n'; /* typdefault */
99107

@@ -118,8 +126,8 @@ TypeShellMake(const char *typeName, Oid typeNamespace)
118126
InvalidOid,
119127
0,
120128
GetUserId(),
121-
InvalidOid,
122-
InvalidOid,
129+
F_SHELL_IN,
130+
F_SHELL_OUT,
123131
InvalidOid,
124132
InvalidOid,
125133
InvalidOid,
@@ -289,7 +297,13 @@ TypeCreate(const char *typeName,
289297
errmsg("type \"%s\" already exists", typeName)));
290298

291299
/*
292-
* Okay to update existing "shell" type tuple
300+
* shell type must have been created by same owner
301+
*/
302+
if (((Form_pg_type) GETSTRUCT(tup))->typowner != GetUserId())
303+
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE, typeName);
304+
305+
/*
306+
* Okay to update existing shell type tuple
293307
*/
294308
tup = heap_modifytuple(tup,
295309
RelationGetDescr(pg_type_desc),
@@ -350,8 +364,6 @@ TypeCreate(const char *typeName,
350364
* If rebuild is true, we remove existing dependencies and rebuild them
351365
* from scratch. This is needed for ALTER TYPE, and also when replacing
352366
* a shell type.
353-
*
354-
* NOTE: a shell type will have a dependency to its namespace, and no others.
355367
*/
356368
void
357369
GenerateTypeDependencies(Oid typeNamespace,

src/backend/commands/typecmds.c

+32-17
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.86 2006/01/13 18:06:45 tgl Exp $
11+
* $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.87 2006/02/28 22:37:26 tgl Exp $
1212
*
1313
* DESCRIPTION
1414
* The "DefineFoo" routines take the parse tree and pick out the
@@ -138,6 +138,37 @@ DefineType(List *names, List *parameters)
138138
errmsg("type names must be %d characters or less",
139139
NAMEDATALEN - 2)));
140140

141+
/*
142+
* Look to see if type already exists (presumably as a shell; if not,
143+
* TypeCreate will complain). If it doesn't, create it as a shell, so
144+
* that the OID is known for use in the I/O function definitions.
145+
*/
146+
typoid = GetSysCacheOid(TYPENAMENSP,
147+
CStringGetDatum(typeName),
148+
ObjectIdGetDatum(typeNamespace),
149+
0, 0);
150+
if (!OidIsValid(typoid))
151+
{
152+
typoid = TypeShellMake(typeName, typeNamespace);
153+
/* Make new shell type visible for modification below */
154+
CommandCounterIncrement();
155+
156+
/*
157+
* If the command was a parameterless CREATE TYPE, we're done ---
158+
* creating the shell type was all we're supposed to do.
159+
*/
160+
if (parameters == NIL)
161+
return;
162+
}
163+
else
164+
{
165+
/* Complain if dummy CREATE TYPE and entry already exists */
166+
if (parameters == NIL)
167+
ereport(ERROR,
168+
(errcode(ERRCODE_DUPLICATE_OBJECT),
169+
errmsg("type \"%s\" already exists", typeName)));
170+
}
171+
141172
foreach(pl, parameters)
142173
{
143174
DefElem *defel = (DefElem *) lfirst(pl);
@@ -240,22 +271,6 @@ DefineType(List *names, List *parameters)
240271
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
241272
errmsg("type output function must be specified")));
242273

243-
/*
244-
* Look to see if type already exists (presumably as a shell; if not,
245-
* TypeCreate will complain). If it doesn't, create it as a shell, so
246-
* that the OID is known for use in the I/O function definitions.
247-
*/
248-
typoid = GetSysCacheOid(TYPENAMENSP,
249-
CStringGetDatum(typeName),
250-
ObjectIdGetDatum(typeNamespace),
251-
0, 0);
252-
if (!OidIsValid(typoid))
253-
{
254-
typoid = TypeShellMake(typeName, typeNamespace);
255-
/* Make new shell type visible for modification below */
256-
CommandCounterIncrement();
257-
}
258-
259274
/*
260275
* Convert I/O proc names to OIDs
261276
*/

src/backend/parser/gram.y

+10-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
*
1212
*
1313
* IDENTIFICATION
14-
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.530 2006/02/19 00:04:27 neilc Exp $
14+
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.531 2006/02/28 22:37:26 tgl Exp $
1515
*
1616
* HISTORY
1717
* AUTHOR DATE MAJOR EVENT
@@ -2690,6 +2690,15 @@ DefineStmt:
26902690
n->definition = $4;
26912691
$$ = (Node *)n;
26922692
}
2693+
| CREATE TYPE_P any_name
2694+
{
2695+
/* Shell type (identified by lack of definition) */
2696+
DefineStmt *n = makeNode(DefineStmt);
2697+
n->kind = OBJECT_TYPE;
2698+
n->defnames = $3;
2699+
n->definition = NIL;
2700+
$$ = (Node *)n;
2701+
}
26932702
| CREATE TYPE_P any_name AS '(' TableFuncElementList ')'
26942703
{
26952704
CompositeTypeStmt *n = makeNode(CompositeTypeStmt);

0 commit comments

Comments
 (0)