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

Commit 7fdb430

Browse files
committed
Fix a bunch of problems with domains by making them use special input functions
that apply the necessary domain constraint checks immediately. This fixes cases where domain constraints went unchecked for statement parameters, PL function local variables and results, etc. We can also eliminate existing special cases for domains in places that had gotten it right, eg COPY. Also, allow domains over domains (base of a domain is another domain type). This almost worked before, but was disallowed because the original patch hadn't gotten it quite right.
1 parent 89a67e5 commit 7fdb430

File tree

20 files changed

+549
-196
lines changed

20 files changed

+549
-196
lines changed

doc/src/sgml/ref/create_domain.sgml

Lines changed: 18 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<!--
2-
$PostgreSQL: pgsql/doc/src/sgml/ref/create_domain.sgml,v 1.27 2005/12/25 01:41:15 neilc Exp $
2+
$PostgreSQL: pgsql/doc/src/sgml/ref/create_domain.sgml,v 1.28 2006/04/05 22:11:54 tgl Exp $
33
PostgreSQL documentation
44
-->
55

@@ -35,8 +35,10 @@ where <replaceable class="PARAMETER">constraint</replaceable> is:
3535
<title>Description</title>
3636

3737
<para>
38-
<command>CREATE DOMAIN</command> creates a new data domain. The
39-
user who defines a domain becomes its owner.
38+
<command>CREATE DOMAIN</command> creates a new domain. A domain is
39+
essentially a data type with optional constraints (restrictions on
40+
the allowed set of values).
41+
The user who defines a domain becomes its owner.
4042
</para>
4143

4244
<para>
@@ -48,24 +50,13 @@ where <replaceable class="PARAMETER">constraint</replaceable> is:
4850
</para>
4951

5052
<para>
51-
Domains are useful for abstracting common fields between tables
52-
into a single location for maintenance. For example, an email address
53-
column may be used in several tables, all with the same properties.
54-
Define a domain and use that rather than setting up each table's
55-
constraints individually.
53+
Domains are useful for abstracting common constraints on fields into
54+
a single location for maintenance. For example, several tables might
55+
contain email address columns, all requiring the same CHECK constraint
56+
to verify the address syntax.
57+
Define a domain rather than setting up each table's constraint
58+
individually.
5659
</para>
57-
58-
<caution>
59-
<para>
60-
At present, declaring a function result value as a domain
61-
is pretty dangerous, because none of the procedural languages enforce domain constraints
62-
on their results. You'll need to make sure that the function code itself
63-
respects the constraints. In <application>PL/pgSQL</>, one possible
64-
workaround is to explicitly cast the result value to the domain type
65-
when you return it. <application>PL/pgSQL</> does not enforce domain
66-
constraints for local variables within functions, either.
67-
</para>
68-
</caution>
6960
</refsect1>
7061

7162
<refsect1>
@@ -156,7 +147,7 @@ where <replaceable class="PARAMETER">constraint</replaceable> is:
156147
<literal>CHECK</> clauses specify integrity constraints or tests
157148
which values of the domain must satisfy.
158149
Each constraint must be an expression
159-
producing a Boolean result. It should use the name <literal>VALUE</>
150+
producing a Boolean result. It should use the key word <literal>VALUE</>
160151
to refer to the value being tested.
161152
</para>
162153

@@ -185,12 +176,12 @@ OR VALUE ~ '^\\d{5}-\\d{4}$'
185176
);
186177

187178
CREATE TABLE us_snail_addy (
188-
address_id SERIAL PRIMARY KEY
189-
, street1 TEXT NOT NULL
190-
, street2 TEXT
191-
, street3 TEXT
192-
, city TEXT NOT NULL
193-
, postal us_postal_code NOT NULL
179+
address_id SERIAL PRIMARY KEY,
180+
street1 TEXT NOT NULL,
181+
street2 TEXT,
182+
street3 TEXT,
183+
city TEXT NOT NULL,
184+
postal us_postal_code NOT NULL
194185
);
195186
</programlisting>
196187
</para>

doc/src/sgml/ref/create_type.sgml

Lines changed: 2 additions & 1 deletion
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.62 2006/04/04 19:35:32 tgl Exp $
2+
$PostgreSQL: pgsql/doc/src/sgml/ref/create_type.sgml,v 1.63 2006/04/05 22:11:54 tgl Exp $
33
PostgreSQL documentation
44
-->
55

@@ -591,6 +591,7 @@ CREATE TABLE big_objs (
591591
<member><xref linkend="sql-createfunction" endterm="sql-createfunction-title"></member>
592592
<member><xref linkend="sql-droptype" endterm="sql-droptype-title"></member>
593593
<member><xref linkend="sql-altertype" endterm="sql-altertype-title"></member>
594+
<member><xref linkend="sql-createdomain" endterm="sql-createdomain-title"></member>
594595
</simplelist>
595596
</refsect1>
596597

src/backend/access/common/printtup.c

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
* Portions Copyright (c) 1994, Regents of the University of California
1010
*
1111
* IDENTIFICATION
12-
* $PostgreSQL: pgsql/src/backend/access/common/printtup.c,v 1.95 2006/04/04 19:35:33 tgl Exp $
12+
* $PostgreSQL: pgsql/src/backend/access/common/printtup.c,v 1.96 2006/04/05 22:11:54 tgl Exp $
1313
*
1414
*-------------------------------------------------------------------------
1515
*/
@@ -177,7 +177,6 @@ SendRowDescriptionMessage(TupleDesc typeinfo, List *targetlist, int16 *formats)
177177
{
178178
Oid atttypid = attrs[i]->atttypid;
179179
int32 atttypmod = attrs[i]->atttypmod;
180-
Oid basetype;
181180

182181
pq_sendstring(&buf, NameStr(attrs[i]->attname));
183182
/* column ID info appears in protocol 3.0 and up */
@@ -203,12 +202,7 @@ SendRowDescriptionMessage(TupleDesc typeinfo, List *targetlist, int16 *formats)
203202
}
204203
}
205204
/* If column is a domain, send the base type and typmod instead */
206-
basetype = getBaseType(atttypid);
207-
if (basetype != atttypid)
208-
{
209-
atttypmod = get_typtypmod(atttypid);
210-
atttypid = basetype;
211-
}
205+
atttypid = getBaseTypeAndTypmod(atttypid, &atttypmod);
212206
pq_sendint(&buf, (int) atttypid, sizeof(atttypid));
213207
pq_sendint(&buf, attrs[i]->attlen, sizeof(attrs[i]->attlen));
214208
/* typmod appears in protocol 2.0 and up */

src/backend/commands/copy.c

Lines changed: 1 addition & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/commands/copy.c,v 1.262 2006/04/04 19:35:33 tgl Exp $
11+
* $PostgreSQL: pgsql/src/backend/commands/copy.c,v 1.263 2006/04/05 22:11:54 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -1545,9 +1545,7 @@ CopyFrom(CopyState cstate)
15451545
FmgrInfo oid_in_function;
15461546
Oid *typioparams;
15471547
Oid oid_typioparam;
1548-
ExprState **constraintexprs;
15491548
bool *force_notnull;
1550-
bool hasConstraints = false;
15511549
int attnum;
15521550
int i;
15531551
Oid in_func_oid;
@@ -1608,7 +1606,6 @@ CopyFrom(CopyState cstate)
16081606
typioparams = (Oid *) palloc(num_phys_attrs * sizeof(Oid));
16091607
defmap = (int *) palloc(num_phys_attrs * sizeof(int));
16101608
defexprs = (ExprState **) palloc(num_phys_attrs * sizeof(ExprState *));
1611-
constraintexprs = (ExprState **) palloc0(num_phys_attrs * sizeof(ExprState *));
16121609
force_notnull = (bool *) palloc(num_phys_attrs * sizeof(bool));
16131610

16141611
for (attnum = 1; attnum <= num_phys_attrs; attnum++)
@@ -1646,35 +1643,6 @@ CopyFrom(CopyState cstate)
16461643
num_defaults++;
16471644
}
16481645
}
1649-
1650-
/* If it's a domain type, set up to check domain constraints */
1651-
if (get_typtype(attr[attnum - 1]->atttypid) == 'd')
1652-
{
1653-
Param *prm;
1654-
Node *node;
1655-
1656-
/*
1657-
* Easiest way to do this is to use parse_coerce.c to set up an
1658-
* expression that checks the constraints. (At present, the
1659-
* expression might contain a length-coercion-function call and/or
1660-
* CoerceToDomain nodes.) The bottom of the expression is a Param
1661-
* node so that we can fill in the actual datum during the data
1662-
* input loop.
1663-
*/
1664-
prm = makeNode(Param);
1665-
prm->paramkind = PARAM_EXEC;
1666-
prm->paramid = 0;
1667-
prm->paramtype = getBaseType(attr[attnum - 1]->atttypid);
1668-
1669-
node = coerce_to_domain((Node *) prm,
1670-
prm->paramtype,
1671-
attr[attnum - 1]->atttypid,
1672-
COERCE_IMPLICIT_CAST, false, false);
1673-
1674-
constraintexprs[attnum - 1] = ExecPrepareExpr((Expr *) node,
1675-
estate);
1676-
hasConstraints = true;
1677-
}
16781646
}
16791647

16801648
/* Prepare to catch AFTER triggers. */
@@ -1743,11 +1711,6 @@ CopyFrom(CopyState cstate)
17431711
nfields = file_has_oids ? (attr_count + 1) : attr_count;
17441712
field_strings = (char **) palloc(nfields * sizeof(char *));
17451713

1746-
/* Make room for a PARAM_EXEC value for domain constraint checks */
1747-
if (hasConstraints)
1748-
econtext->ecxt_param_exec_vals = (ParamExecData *)
1749-
palloc0(sizeof(ParamExecData));
1750-
17511714
/* Initialize state variables */
17521715
cstate->fe_eof = false;
17531716
cstate->eol_type = EOL_UNKNOWN;
@@ -1942,33 +1905,6 @@ CopyFrom(CopyState cstate)
19421905
nulls[defmap[i]] = ' ';
19431906
}
19441907

1945-
/* Next apply any domain constraints */
1946-
if (hasConstraints)
1947-
{
1948-
ParamExecData *prmdata = &econtext->ecxt_param_exec_vals[0];
1949-
1950-
for (i = 0; i < num_phys_attrs; i++)
1951-
{
1952-
ExprState *exprstate = constraintexprs[i];
1953-
1954-
if (exprstate == NULL)
1955-
continue; /* no constraint for this attr */
1956-
1957-
/* Insert current row's value into the Param value */
1958-
prmdata->value = values[i];
1959-
prmdata->isnull = (nulls[i] == 'n');
1960-
1961-
/*
1962-
* Execute the constraint expression. Allow the expression to
1963-
* replace the value (consider e.g. a timestamp precision
1964-
* restriction).
1965-
*/
1966-
values[i] = ExecEvalExpr(exprstate, econtext,
1967-
&isnull, NULL);
1968-
nulls[i] = isnull ? 'n' : ' ';
1969-
}
1970-
}
1971-
19721908
/* And now we can form the input tuple. */
19731909
tuple = heap_formtuple(tupDesc, values, nulls);
19741910

@@ -2043,7 +1979,6 @@ CopyFrom(CopyState cstate)
20431979
pfree(typioparams);
20441980
pfree(defmap);
20451981
pfree(defexprs);
2046-
pfree(constraintexprs);
20471982
pfree(force_notnull);
20481983

20491984
ExecDropSingleTupleTableSlot(slot);

src/backend/commands/typecmds.c

Lines changed: 12 additions & 16 deletions
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.89 2006/03/14 22:48:18 tgl Exp $
11+
* $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.90 2006/04/05 22:11:55 tgl Exp $
1212
*
1313
* DESCRIPTION
1414
* The "DefineFoo" routines take the parse tree and pick out the
@@ -536,6 +536,7 @@ DefineDomain(CreateDomainStmt *stmt)
536536
Oid sendProcedure;
537537
Oid analyzeProcedure;
538538
bool byValue;
539+
Oid typelem;
539540
char delimiter;
540541
char alignment;
541542
char storage;
@@ -547,7 +548,6 @@ DefineDomain(CreateDomainStmt *stmt)
547548
char *defaultValueBin = NULL;
548549
bool typNotNull = false;
549550
bool nullDefined = false;
550-
Oid basetypelem;
551551
int32 typNDims = list_length(stmt->typename->arrayBounds);
552552
HeapTuple typeTup;
553553
List *schema = stmt->constraints;
@@ -589,12 +589,12 @@ DefineDomain(CreateDomainStmt *stmt)
589589
basetypeoid = HeapTupleGetOid(typeTup);
590590

591591
/*
592-
* Base type must be a plain base type. Domains over pseudo types would
593-
* create a security hole. Domains of domains might be made to work in
594-
* the future, but not today. Ditto for domains over complex types.
592+
* Base type must be a plain base type or another domain. Domains over
593+
* pseudotypes would create a security hole. Domains over composite
594+
* types might be made to work in the future, but not today.
595595
*/
596596
typtype = baseType->typtype;
597-
if (typtype != 'b')
597+
if (typtype != 'b' && typtype != 'd')
598598
ereport(ERROR,
599599
(errcode(ERRCODE_DATATYPE_MISMATCH),
600600
errmsg("\"%s\" is not a valid base type for a domain",
@@ -612,13 +612,16 @@ DefineDomain(CreateDomainStmt *stmt)
612612
/* Storage Length */
613613
internalLength = baseType->typlen;
614614

615+
/* Array element type (in case base type is an array) */
616+
typelem = baseType->typelem;
617+
615618
/* Array element Delimiter */
616619
delimiter = baseType->typdelim;
617620

618621
/* I/O Functions */
619-
inputProcedure = baseType->typinput;
622+
inputProcedure = F_DOMAIN_IN;
620623
outputProcedure = baseType->typoutput;
621-
receiveProcedure = baseType->typreceive;
624+
receiveProcedure = F_DOMAIN_RECV;
622625
sendProcedure = baseType->typsend;
623626

624627
/* Analysis function */
@@ -636,13 +639,6 @@ DefineDomain(CreateDomainStmt *stmt)
636639
if (!isnull)
637640
defaultValueBin = DatumGetCString(DirectFunctionCall1(textout, datum));
638641

639-
/*
640-
* Pull out the typelem name of the parent OID.
641-
*
642-
* This is what enables us to make a domain of an array
643-
*/
644-
basetypelem = baseType->typelem;
645-
646642
/*
647643
* Run through constraints manually to avoid the additional processing
648644
* conducted by DefineRelation() and friends.
@@ -776,7 +772,7 @@ DefineDomain(CreateDomainStmt *stmt)
776772
receiveProcedure, /* receive procedure */
777773
sendProcedure, /* send procedure */
778774
analyzeProcedure, /* analyze procedure */
779-
basetypelem, /* element type ID */
775+
typelem, /* element type ID */
780776
basetypeoid, /* base type ID */
781777
defaultValue, /* default type value (text) */
782778
defaultValueBin, /* default type value (binary) */

src/backend/optimizer/prep/preptlist.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
* Portions Copyright (c) 1994, Regents of the University of California
1616
*
1717
* IDENTIFICATION
18-
* $PostgreSQL: pgsql/src/backend/optimizer/prep/preptlist.c,v 1.80 2006/03/05 15:58:31 momjian Exp $
18+
* $PostgreSQL: pgsql/src/backend/optimizer/prep/preptlist.c,v 1.81 2006/04/05 22:11:55 tgl Exp $
1919
*
2020
*-------------------------------------------------------------------------
2121
*/
@@ -249,7 +249,7 @@ expand_targetlist(List *tlist, int command_type,
249249
true, /* isnull */
250250
att_tup->attbyval);
251251
new_expr = coerce_to_domain(new_expr,
252-
InvalidOid,
252+
InvalidOid, -1,
253253
atttype,
254254
COERCE_IMPLICIT_CAST,
255255
false,

0 commit comments

Comments
 (0)