Skip to content

Commit 350cb38

Browse files
committed
Clean up handling of explicit NULL constants. Cases like
SELECT null::text; SELECT int4fac(null); work as expected now. In some cases a NULL must be surrounded by parentheses: SELECT 2 + null; fails SELECT 2 + (null); OK This is a grammatical ambiguity that seems difficult to avoid. Other than that, NULLs seem to behave about like you'd expect. The internal implementation is that NULL constants are typed as UNKNOWN (like untyped string constants) until the parser can deduce the right type.
1 parent bd5ea42 commit 350cb38

File tree

4 files changed

+67
-43
lines changed

4 files changed

+67
-43
lines changed

src/backend/nodes/equalfuncs.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
*
88
*
99
* IDENTIFICATION
10-
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.53 1999/12/13 01:26:53 tgl Exp $
10+
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.54 1999/12/24 06:43:32 tgl Exp $
1111
*
1212
*-------------------------------------------------------------------------
1313
*/
@@ -144,6 +144,13 @@ _equalConst(Const *a, Const *b)
144144
if (a->constbyval != b->constbyval)
145145
return false;
146146
/* XXX What about constisset and constiscast? */
147+
/*
148+
* We treat all NULL constants of the same type as equal.
149+
* Someday this might need to change? But datumIsEqual
150+
* doesn't work on nulls, so...
151+
*/
152+
if (a->constisnull)
153+
return true;
147154
return (datumIsEqual(a->constvalue, b->constvalue,
148155
a->consttype, a->constbyval, a->constlen));
149156
}

src/backend/parser/parse_expr.c

Lines changed: 27 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
*
88
*
99
* IDENTIFICATION
10-
* $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.62 1999/12/17 01:25:25 momjian Exp $
10+
* $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.63 1999/12/24 06:43:33 tgl Exp $
1111
*
1212
*-------------------------------------------------------------------------
1313
*/
@@ -353,7 +353,7 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
353353
n->val.type = T_Null;
354354
c->defresult = (Node *) n;
355355
}
356-
c->defresult = transformExpr(pstate, (Node *) c->defresult, precedence);
356+
c->defresult = transformExpr(pstate, c->defresult, precedence);
357357

358358
/* now check types across result clauses... */
359359
c->casetype = exprType(c->defresult);
@@ -369,32 +369,30 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
369369
if (wtype && (wtype != UNKNOWNOID)
370370
&& (wtype != ptype))
371371
{
372-
/* so far, only nulls so take anything... */
373-
if (!ptype)
372+
if (!ptype || ptype == UNKNOWNOID)
374373
{
374+
/* so far, only nulls so take anything... */
375375
ptype = wtype;
376376
pcategory = TypeCategory(ptype);
377377
}
378-
379-
/*
380-
* both types in different categories? then not
381-
* much hope...
382-
*/
383378
else if ((TypeCategory(wtype) != pcategory)
384379
|| ((TypeCategory(wtype) == USER_TYPE)
385380
&& (TypeCategory(c->casetype) == USER_TYPE)))
386381
{
382+
/*
383+
* both types in different categories?
384+
* then not much hope...
385+
*/
387386
elog(ERROR, "CASE/WHEN types '%s' and '%s' not matched",
388387
typeidTypeName(c->casetype), typeidTypeName(wtype));
389388
}
390-
391-
/*
392-
* new one is preferred and can convert? then take
393-
* it...
394-
*/
395389
else if (IsPreferredType(pcategory, wtype)
396390
&& can_coerce_type(1, &ptype, &wtype))
397391
{
392+
/*
393+
* new one is preferred and can convert?
394+
* then take it...
395+
*/
398396
ptype = wtype;
399397
pcategory = TypeCategory(ptype);
400398
}
@@ -404,9 +402,8 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
404402
/* Convert default result clause, if necessary */
405403
if (c->casetype != ptype)
406404
{
407-
if (!c->casetype)
405+
if (!c->casetype || c->casetype == UNKNOWNOID)
408406
{
409-
410407
/*
411408
* default clause is NULL, so assign preferred
412409
* type from WHEN clauses...
@@ -694,11 +691,12 @@ exprTypmod(Node *expr)
694691
static Node *
695692
parser_typecast(Value *expr, TypeName *typename, int32 atttypmod)
696693
{
697-
Const *adt;
698-
Datum lcp;
694+
Const *con;
699695
Type tp;
696+
Datum datum;
700697
char *const_string = NULL;
701698
bool string_palloced = false;
699+
bool isNull = false;
702700

703701
switch (nodeTag(expr))
704702
{
@@ -713,6 +711,9 @@ parser_typecast(Value *expr, TypeName *typename, int32 atttypmod)
713711
string_palloced = true;
714712
const_string = float8out(&expr->val.dval);
715713
break;
714+
case T_Null:
715+
isNull = true;
716+
break;
716717
default:
717718
elog(ERROR,
718719
"parser_typecast: cannot cast this expression to type '%s'",
@@ -729,18 +730,21 @@ parser_typecast(Value *expr, TypeName *typename, int32 atttypmod)
729730
else
730731
tp = (Type) typenameType(typename->name);
731732

732-
lcp = stringTypeDatum(tp, const_string, atttypmod);
733+
if (isNull)
734+
datum = (Datum) NULL;
735+
else
736+
datum = stringTypeDatum(tp, const_string, atttypmod);
733737

734-
adt = makeConst(typeTypeId(tp),
738+
con = makeConst(typeTypeId(tp),
735739
typeLen(tp),
736-
(Datum) lcp,
737-
false,
740+
datum,
741+
isNull,
738742
typeByVal(tp),
739743
false, /* not a set */
740744
true /* is cast */ );
741745

742746
if (string_palloced)
743747
pfree(const_string);
744748

745-
return (Node *) adt;
749+
return (Node *) con;
746750
}

src/backend/parser/parse_node.c

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
*
88
*
99
* IDENTIFICATION
10-
* $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.33 1999/11/22 17:56:21 momjian Exp $
10+
* $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.34 1999/12/24 06:43:33 tgl Exp $
1111
*
1212
*-------------------------------------------------------------------------
1313
*/
@@ -61,28 +61,27 @@ make_operand(char *opname,
6161
Oid target_typeId)
6262
{
6363
Node *result;
64-
Type target_type;
64+
Type target_type = typeidType(target_typeId);
6565

6666
if (tree != NULL)
6767
{
68-
result = tree;
69-
target_type = typeidType(target_typeId);
70-
disallow_setop(opname, target_type, result);
71-
68+
disallow_setop(opname, target_type, tree);
7269
/* must coerce? */
7370
if (target_typeId != orig_typeId)
7471
result = coerce_type(NULL, tree, orig_typeId, target_typeId, -1);
72+
else
73+
result = tree;
7574
}
76-
/* otherwise, this is a NULL value */
7775
else
7876
{
77+
/* otherwise, this is a NULL value */
7978
Const *con = makeNode(Const);
8079

8180
con->consttype = target_typeId;
82-
con->constlen = 0;
83-
con->constvalue = (Datum) (struct varlena *) NULL;
81+
con->constlen = typeLen(target_type);
82+
con->constvalue = (Datum) NULL;
8483
con->constisnull = true;
85-
con->constbyval = true;
84+
con->constbyval = typeByVal(target_type);
8685
con->constisset = false;
8786
result = (Node *) con;
8887
}
@@ -394,7 +393,8 @@ transformArraySubscripts(ParseState *pstate,
394393
* of the "natural" type for the constant. For strings we produce
395394
* a constant of type UNKNOWN ---- representation is the same as text,
396395
* but this indicates to later type resolution that we're not sure that
397-
* it should be considered text.
396+
* it should be considered text. Explicit "NULL" constants are also
397+
* typed as UNKNOWN.
398398
*/
399399
Const *
400400
make_const(Value *value)
@@ -444,7 +444,13 @@ make_const(Value *value)
444444
elog(NOTICE, "make_const: unknown type %d\n", nodeTag(value));
445445

446446
/* return a null const */
447-
con = makeConst(0, 0, (Datum) NULL, true, false, false, false);
447+
con = makeConst(UNKNOWNOID,
448+
-1,
449+
(Datum) NULL,
450+
true,
451+
false,
452+
false,
453+
false);
448454
return con;
449455
}
450456

src/backend/utils/adt/ruleutils.c

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* out of it's tuple
44
*
55
* IDENTIFICATION
6-
* $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.35 1999/12/13 01:27:01 tgl Exp $
6+
* $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.36 1999/12/24 06:43:34 tgl Exp $
77
*
88
* This software is copyrighted by Jan Wieck - Hamburg.
99
*
@@ -1606,12 +1606,6 @@ get_const_expr(Const *constval, deparse_context *context)
16061606
char *valptr;
16071607
bool isnull = FALSE;
16081608

1609-
if (constval->constisnull)
1610-
{
1611-
appendStringInfo(buf, "NULL");
1612-
return;
1613-
}
1614-
16151609
typetup = SearchSysCacheTuple(TYPEOID,
16161610
ObjectIdGetDatum(constval->consttype),
16171611
0, 0, 0);
@@ -1620,6 +1614,19 @@ get_const_expr(Const *constval, deparse_context *context)
16201614

16211615
typeStruct = (Form_pg_type) GETSTRUCT(typetup);
16221616

1617+
if (constval->constisnull)
1618+
{
1619+
/*
1620+
* Always label the type of a NULL constant. This not only
1621+
* prevents misdecisions about the type, but it ensures that
1622+
* our output is a valid b_expr.
1623+
*/
1624+
extval = pstrdup(NameStr(typeStruct->typname));
1625+
appendStringInfo(buf, "NULL::%s", quote_identifier(extval));
1626+
pfree(extval);
1627+
return;
1628+
}
1629+
16231630
fmgr_info(typeStruct->typoutput, &finfo_output);
16241631
extval = (char *) (*fmgr_faddr(&finfo_output)) (constval->constvalue,
16251632
&isnull, -1);

0 commit comments

Comments
 (0)