Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
P
Postgres FD Implementation
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Abuhujair Javed
Postgres FD Implementation
Commits
a341a96c
Commit
a341a96c
authored
Jan 19, 2005
by
Neil Conway
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Refactor transformExpr() by creating separate functions for most of the
expression types.
parent
a73198ac
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
827 additions
and
761 deletions
+827
-761
src/backend/parser/parse_expr.c
src/backend/parser/parse_expr.c
+827
-761
No files found.
src/backend/parser/parse_expr.c
View file @
a341a96c
...
...
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.1
79 2005/01/12 17:32:36 tgl
Exp $
* $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.1
80 2005/01/19 23:45:24 neilc
Exp $
*
*-------------------------------------------------------------------------
*/
...
...
@@ -37,9 +37,27 @@
bool
Transform_null_equals
=
false
;
static
Node
*
transformParamRef
(
ParseState
*
pstate
,
ParamRef
*
pref
);
static
Node
*
transformAExprOp
(
ParseState
*
pstate
,
A_Expr
*
a
);
static
Node
*
transformAExprAnd
(
ParseState
*
pstate
,
A_Expr
*
a
);
static
Node
*
transformAExprOr
(
ParseState
*
pstate
,
A_Expr
*
a
);
static
Node
*
transformAExprNot
(
ParseState
*
pstate
,
A_Expr
*
a
);
static
Node
*
transformAExprOpAny
(
ParseState
*
pstate
,
A_Expr
*
a
);
static
Node
*
transformAExprOpAll
(
ParseState
*
pstate
,
A_Expr
*
a
);
static
Node
*
transformAExprDistinct
(
ParseState
*
pstate
,
A_Expr
*
a
);
static
Node
*
transformAExprNullIf
(
ParseState
*
pstate
,
A_Expr
*
a
);
static
Node
*
transformAExprOf
(
ParseState
*
pstate
,
A_Expr
*
a
);
static
Node
*
transformFuncCall
(
ParseState
*
pstate
,
FuncCall
*
fn
);
static
Node
*
transformCaseExpr
(
ParseState
*
pstate
,
CaseExpr
*
c
);
static
Node
*
transformSubLink
(
ParseState
*
pstate
,
SubLink
*
sublink
);
static
Node
*
transformArrayExpr
(
ParseState
*
pstate
,
ArrayExpr
*
a
);
static
Node
*
transformRowExpr
(
ParseState
*
pstate
,
RowExpr
*
r
);
static
Node
*
transformCoalesceExpr
(
ParseState
*
pstate
,
CoalesceExpr
*
c
);
static
Node
*
transformBooleanTest
(
ParseState
*
pstate
,
BooleanTest
*
b
);
static
Node
*
transformColumnRef
(
ParseState
*
pstate
,
ColumnRef
*
cref
);
static
Node
*
transformWholeRowRef
(
ParseState
*
pstate
,
char
*
schemaname
,
char
*
relname
);
static
Node
*
transformBooleanTest
(
ParseState
*
pstate
,
BooleanTest
*
b
);
static
Node
*
transformIndirection
(
ParseState
*
pstate
,
Node
*
basenode
,
List
*
indirection
);
static
Node
*
typecast_expression
(
ParseState
*
pstate
,
Node
*
expr
,
...
...
@@ -90,65 +108,13 @@ transformExpr(ParseState *pstate, Node *expr)
switch
(
nodeTag
(
expr
))
{
case
T_ColumnRef
:
{
result
=
transformColumnRef
(
pstate
,
(
ColumnRef
*
)
expr
);
break
;
}
case
T_ParamRef
:
{
ParamRef
*
pref
=
(
ParamRef
*
)
expr
;
int
paramno
=
pref
->
number
;
ParseState
*
toppstate
;
Param
*
param
;
/*
* Find topmost ParseState, which is where paramtype info
* lives.
*/
toppstate
=
pstate
;
while
(
toppstate
->
parentParseState
!=
NULL
)
toppstate
=
toppstate
->
parentParseState
;
result
=
transformColumnRef
(
pstate
,
(
ColumnRef
*
)
expr
);
break
;
/* Check parameter number is in range */
if
(
paramno
<=
0
)
/* probably can't happen? */
ereport
(
ERROR
,
(
errcode
(
ERRCODE_UNDEFINED_PARAMETER
),
errmsg
(
"there is no parameter $%d"
,
paramno
)));
if
(
paramno
>
toppstate
->
p_numparams
)
{
if
(
!
toppstate
->
p_variableparams
)
ereport
(
ERROR
,
(
errcode
(
ERRCODE_UNDEFINED_PARAMETER
),
errmsg
(
"there is no parameter $%d"
,
paramno
)));
/* Okay to enlarge param array */
if
(
toppstate
->
p_paramtypes
)
toppstate
->
p_paramtypes
=
(
Oid
*
)
repalloc
(
toppstate
->
p_paramtypes
,
paramno
*
sizeof
(
Oid
));
else
toppstate
->
p_paramtypes
=
(
Oid
*
)
palloc
(
paramno
*
sizeof
(
Oid
));
/* Zero out the previously-unreferenced slots */
MemSet
(
toppstate
->
p_paramtypes
+
toppstate
->
p_numparams
,
0
,
(
paramno
-
toppstate
->
p_numparams
)
*
sizeof
(
Oid
));
toppstate
->
p_numparams
=
paramno
;
}
if
(
toppstate
->
p_variableparams
)
{
/* If not seen before, initialize to UNKNOWN type */
if
(
toppstate
->
p_paramtypes
[
paramno
-
1
]
==
InvalidOid
)
toppstate
->
p_paramtypes
[
paramno
-
1
]
=
UNKNOWNOID
;
}
case
T_ParamRef
:
result
=
transformParamRef
(
pstate
,
(
ParamRef
*
)
expr
);
break
;
param
=
makeNode
(
Param
);
param
->
paramkind
=
PARAM_NUM
;
param
->
paramid
=
(
AttrNumber
)
paramno
;
param
->
paramtype
=
toppstate
->
p_paramtypes
[
paramno
-
1
];
result
=
(
Node
*
)
param
;
break
;
}
case
T_A_Const
:
{
A_Const
*
con
=
(
A_Const
*
)
expr
;
...
...
@@ -160,6 +126,7 @@ transformExpr(ParseState *pstate, Node *expr)
con
->
typename
);
break
;
}
case
T_A_Indirection
:
{
A_Indirection
*
ind
=
(
A_Indirection
*
)
expr
;
...
...
@@ -169,6 +136,7 @@ transformExpr(ParseState *pstate, Node *expr)
ind
->
indirection
);
break
;
}
case
T_TypeCast
:
{
TypeCast
*
tc
=
(
TypeCast
*
)
expr
;
...
...
@@ -177,6 +145,7 @@ transformExpr(ParseState *pstate, Node *expr)
result
=
typecast_expression
(
pstate
,
arg
,
tc
->
typename
);
break
;
}
case
T_A_Expr
:
{
A_Expr
*
a
=
(
A_Expr
*
)
expr
;
...
...
@@ -184,701 +153,61 @@ transformExpr(ParseState *pstate, Node *expr)
switch
(
a
->
kind
)
{
case
AEXPR_OP
:
{
Node
*
lexpr
=
a
->
lexpr
;
Node
*
rexpr
=
a
->
rexpr
;
/*
* Special-case "foo = NULL" and "NULL = foo"
* for compatibility with standards-broken
* products (like Microsoft's). Turn these
* into IS NULL exprs.
*/
if
(
Transform_null_equals
&&
list_length
(
a
->
name
)
==
1
&&
strcmp
(
strVal
(
linitial
(
a
->
name
)),
"="
)
==
0
&&
(
exprIsNullConstant
(
lexpr
)
||
exprIsNullConstant
(
rexpr
)))
{
NullTest
*
n
=
makeNode
(
NullTest
);
n
->
nulltesttype
=
IS_NULL
;
if
(
exprIsNullConstant
(
lexpr
))
n
->
arg
=
(
Expr
*
)
rexpr
;
else
n
->
arg
=
(
Expr
*
)
lexpr
;
result
=
transformExpr
(
pstate
,
(
Node
*
)
n
);
}
else
if
(
lexpr
&&
IsA
(
lexpr
,
RowExpr
)
&&
rexpr
&&
IsA
(
rexpr
,
SubLink
)
&&
((
SubLink
*
)
rexpr
)
->
subLinkType
==
EXPR_SUBLINK
)
{
/*
* Convert "row op subselect" into a
* MULTIEXPR sublink. Formerly the
* grammar did this, but now that a row
* construct is allowed anywhere in
* expressions, it's easier to do it here.
*/
SubLink
*
s
=
(
SubLink
*
)
rexpr
;
s
->
subLinkType
=
MULTIEXPR_SUBLINK
;
s
->
lefthand
=
((
RowExpr
*
)
lexpr
)
->
args
;
s
->
operName
=
a
->
name
;
result
=
transformExpr
(
pstate
,
(
Node
*
)
s
);
}
else
if
(
lexpr
&&
IsA
(
lexpr
,
RowExpr
)
&&
rexpr
&&
IsA
(
rexpr
,
RowExpr
))
{
/* "row op row" */
result
=
make_row_op
(
pstate
,
a
->
name
,
lexpr
,
rexpr
);
}
else
{
/* Ordinary scalar operator */
lexpr
=
transformExpr
(
pstate
,
lexpr
);
rexpr
=
transformExpr
(
pstate
,
rexpr
);
result
=
(
Node
*
)
make_op
(
pstate
,
a
->
name
,
lexpr
,
rexpr
);
}
}
result
=
transformAExprOp
(
pstate
,
a
);
break
;
case
AEXPR_AND
:
{
Node
*
lexpr
=
transformExpr
(
pstate
,
a
->
lexpr
);
Node
*
rexpr
=
transformExpr
(
pstate
,
a
->
rexpr
);
lexpr
=
coerce_to_boolean
(
pstate
,
lexpr
,
"AND"
);
rexpr
=
coerce_to_boolean
(
pstate
,
rexpr
,
"AND"
);
result
=
(
Node
*
)
makeBoolExpr
(
AND_EXPR
,
list_make2
(
lexpr
,
rexpr
));
}
result
=
transformAExprAnd
(
pstate
,
a
);
break
;
case
AEXPR_OR
:
{
Node
*
lexpr
=
transformExpr
(
pstate
,
a
->
lexpr
);
Node
*
rexpr
=
transformExpr
(
pstate
,
a
->
rexpr
);
lexpr
=
coerce_to_boolean
(
pstate
,
lexpr
,
"OR"
);
rexpr
=
coerce_to_boolean
(
pstate
,
rexpr
,
"OR"
);
result
=
(
Node
*
)
makeBoolExpr
(
OR_EXPR
,
list_make2
(
lexpr
,
rexpr
));
}
result
=
transformAExprOr
(
pstate
,
a
);
break
;
case
AEXPR_NOT
:
{
Node
*
rexpr
=
transformExpr
(
pstate
,
a
->
rexpr
);
rexpr
=
coerce_to_boolean
(
pstate
,
rexpr
,
"NOT"
);
result
=
(
Node
*
)
makeBoolExpr
(
NOT_EXPR
,
list_make1
(
rexpr
));
}
result
=
transformAExprNot
(
pstate
,
a
);
break
;
case
AEXPR_OP_ANY
:
{
Node
*
lexpr
=
transformExpr
(
pstate
,
a
->
lexpr
);
Node
*
rexpr
=
transformExpr
(
pstate
,
a
->
rexpr
);
result
=
(
Node
*
)
make_scalar_array_op
(
pstate
,
a
->
name
,
true
,
lexpr
,
rexpr
);
}
result
=
transformAExprOpAny
(
pstate
,
a
);
break
;
case
AEXPR_OP_ALL
:
{
Node
*
lexpr
=
transformExpr
(
pstate
,
a
->
lexpr
);
Node
*
rexpr
=
transformExpr
(
pstate
,
a
->
rexpr
);
result
=
(
Node
*
)
make_scalar_array_op
(
pstate
,
a
->
name
,
false
,
lexpr
,
rexpr
);
}
result
=
transformAExprOpAll
(
pstate
,
a
);
break
;
case
AEXPR_DISTINCT
:
{
Node
*
lexpr
=
a
->
lexpr
;
Node
*
rexpr
=
a
->
rexpr
;
if
(
lexpr
&&
IsA
(
lexpr
,
RowExpr
)
&&
rexpr
&&
IsA
(
rexpr
,
RowExpr
))
{
/* "row op row" */
result
=
make_row_distinct_op
(
pstate
,
a
->
name
,
lexpr
,
rexpr
);
}
else
{
/* Ordinary scalar operator */
lexpr
=
transformExpr
(
pstate
,
lexpr
);
rexpr
=
transformExpr
(
pstate
,
rexpr
);
result
=
(
Node
*
)
make_distinct_op
(
pstate
,
a
->
name
,
lexpr
,
rexpr
);
}
}
result
=
transformAExprDistinct
(
pstate
,
a
);
break
;
case
AEXPR_NULLIF
:
{
Node
*
lexpr
=
transformExpr
(
pstate
,
a
->
lexpr
);
Node
*
rexpr
=
transformExpr
(
pstate
,
a
->
rexpr
);
result
=
(
Node
*
)
make_op
(
pstate
,
a
->
name
,
lexpr
,
rexpr
);
if
(((
OpExpr
*
)
result
)
->
opresulttype
!=
BOOLOID
)
ereport
(
ERROR
,
(
errcode
(
ERRCODE_DATATYPE_MISMATCH
),
errmsg
(
"NULLIF requires = operator to yield boolean"
)));
/*
* We rely on NullIfExpr and OpExpr being same
* struct
*/
NodeSetTag
(
result
,
T_NullIfExpr
);
}
result
=
transformAExprNullIf
(
pstate
,
a
);
break
;
case
AEXPR_OF
:
{
/*
* Checking an expression for match to type.
* Will result in a boolean constant node.
*/
ListCell
*
telem
;
A_Const
*
n
;
Oid
ltype
,
rtype
;
bool
matched
=
FALSE
;
Node
*
lexpr
=
transformExpr
(
pstate
,
a
->
lexpr
);
ltype
=
exprType
(
lexpr
);
foreach
(
telem
,
(
List
*
)
a
->
rexpr
)
{
rtype
=
LookupTypeName
(
lfirst
(
telem
));
matched
=
(
rtype
==
ltype
);
if
(
matched
)
break
;
}
/*
* Expect two forms: equals or not equals.
* Flip the sense of the result for not
* equals.
*/
if
(
strcmp
(
strVal
(
linitial
(
a
->
name
)),
"!="
)
==
0
)
matched
=
(
!
matched
);
n
=
makeNode
(
A_Const
);
n
->
val
.
type
=
T_String
;
n
->
val
.
val
.
str
=
(
matched
?
"t"
:
"f"
);
n
->
typename
=
SystemTypeName
(
"bool"
);
result
=
transformExpr
(
pstate
,
(
Node
*
)
n
);
}
result
=
transformAExprOf
(
pstate
,
a
);
break
;
default:
elog
(
ERROR
,
"unrecognized A_Expr kind: %d"
,
a
->
kind
);
}
break
;
}
case
T_FuncCall
:
{
FuncCall
*
fn
=
(
FuncCall
*
)
expr
;
List
*
targs
;
ListCell
*
args
;
result
=
transformFuncCall
(
pstate
,
(
FuncCall
*
)
expr
);
break
;
/*
* Transform the list of arguments. We use a shallow list
* copy and then transform-in-place to avoid O(N^2)
* behavior from repeated lappend's.
*
* XXX: repeated lappend() would no longer result in O(n^2)
* behavior; worth reconsidering this design?
*/
targs
=
list_copy
(
fn
->
args
);
foreach
(
args
,
targs
)
{
lfirst
(
args
)
=
transformExpr
(
pstate
,
(
Node
*
)
lfirst
(
args
));
}
result
=
ParseFuncOrColumn
(
pstate
,
fn
->
funcname
,
targs
,
fn
->
agg_star
,
fn
->
agg_distinct
,
false
);
break
;
}
case
T_SubLink
:
{
SubLink
*
sublink
=
(
SubLink
*
)
expr
;
List
*
qtrees
;
Query
*
qtree
;
/* If we already transformed this node, do nothing */
if
(
IsA
(
sublink
->
subselect
,
Query
))
{
result
=
expr
;
break
;
}
pstate
->
p_hasSubLinks
=
true
;
qtrees
=
parse_sub_analyze
(
sublink
->
subselect
,
pstate
);
if
(
list_length
(
qtrees
)
!=
1
)
elog
(
ERROR
,
"bad query in sub-select"
);
qtree
=
(
Query
*
)
linitial
(
qtrees
);
if
(
qtree
->
commandType
!=
CMD_SELECT
||
qtree
->
resultRelation
!=
0
)
elog
(
ERROR
,
"bad query in sub-select"
);
sublink
->
subselect
=
(
Node
*
)
qtree
;
if
(
sublink
->
subLinkType
==
EXISTS_SUBLINK
)
{
/*
* EXISTS needs no lefthand or combining operator.
* These fields should be NIL already, but make sure.
*/
sublink
->
lefthand
=
NIL
;
sublink
->
operName
=
NIL
;
sublink
->
operOids
=
NIL
;
sublink
->
useOr
=
FALSE
;
}
else
if
(
sublink
->
subLinkType
==
EXPR_SUBLINK
||
sublink
->
subLinkType
==
ARRAY_SUBLINK
)
{
ListCell
*
tlist_item
=
list_head
(
qtree
->
targetList
);
/*
* Make sure the subselect delivers a single column
* (ignoring resjunk targets).
*/
if
(
tlist_item
==
NULL
||
((
TargetEntry
*
)
lfirst
(
tlist_item
))
->
resdom
->
resjunk
)
ereport
(
ERROR
,
(
errcode
(
ERRCODE_SYNTAX_ERROR
),
errmsg
(
"subquery must return a column"
)));
while
((
tlist_item
=
lnext
(
tlist_item
))
!=
NULL
)
{
if
(
!
((
TargetEntry
*
)
lfirst
(
tlist_item
))
->
resdom
->
resjunk
)
ereport
(
ERROR
,
(
errcode
(
ERRCODE_SYNTAX_ERROR
),
errmsg
(
"subquery must return only one column"
)));
}
/*
* EXPR and ARRAY need no lefthand or combining
* operator. These fields should be NIL already, but
* make sure.
*/
sublink
->
lefthand
=
NIL
;
sublink
->
operName
=
NIL
;
sublink
->
operOids
=
NIL
;
sublink
->
useOr
=
FALSE
;
}
else
{
/* ALL, ANY, or MULTIEXPR: generate operator list */
List
*
left_list
=
sublink
->
lefthand
;
List
*
right_list
=
qtree
->
targetList
;
int
row_length
=
list_length
(
left_list
);
bool
needNot
=
false
;
List
*
op
=
sublink
->
operName
;
char
*
opname
=
strVal
(
llast
(
op
));
ListCell
*
l
;
ListCell
*
ll_item
;
/* transform lefthand expressions */
foreach
(
l
,
left_list
)
lfirst
(
l
)
=
transformExpr
(
pstate
,
lfirst
(
l
));
/*
* If the expression is "<> ALL" (with unqualified
* opname) then convert it to "NOT IN". This is a
* hack to improve efficiency of expressions output by
* pre-7.4 Postgres.
*/
if
(
sublink
->
subLinkType
==
ALL_SUBLINK
&&
list_length
(
op
)
==
1
&&
strcmp
(
opname
,
"<>"
)
==
0
)
{
sublink
->
subLinkType
=
ANY_SUBLINK
;
opname
=
pstrdup
(
"="
);
op
=
list_make1
(
makeString
(
opname
));
sublink
->
operName
=
op
;
needNot
=
true
;
}
/* Set useOr if op is "<>" (possibly qualified) */
if
(
strcmp
(
opname
,
"<>"
)
==
0
)
sublink
->
useOr
=
TRUE
;
else
sublink
->
useOr
=
FALSE
;
/* Combining operators other than =/<> is dubious... */
if
(
row_length
!=
1
&&
strcmp
(
opname
,
"="
)
!=
0
&&
strcmp
(
opname
,
"<>"
)
!=
0
)
ereport
(
ERROR
,
(
errcode
(
ERRCODE_FEATURE_NOT_SUPPORTED
),
errmsg
(
"row comparison cannot use operator %s"
,
opname
)));
/*
* To build the list of combining operator OIDs, we
* must scan subquery's targetlist to find values that
* will be matched against lefthand values. We need
* to ignore resjunk targets, so doing the outer
* iteration over right_list is easier than doing it
* over left_list.
*/
sublink
->
operOids
=
NIL
;
ll_item
=
list_head
(
left_list
);
foreach
(
l
,
right_list
)
{
TargetEntry
*
tent
=
(
TargetEntry
*
)
lfirst
(
l
);
Node
*
lexpr
;
Operator
optup
;
Form_pg_operator
opform
;
if
(
tent
->
resdom
->
resjunk
)
continue
;
if
(
ll_item
==
NULL
)
ereport
(
ERROR
,
(
errcode
(
ERRCODE_SYNTAX_ERROR
),
errmsg
(
"subquery has too many columns"
)));
lexpr
=
lfirst
(
ll_item
);
ll_item
=
lnext
(
ll_item
);
/*
* It's OK to use oper() not compatible_oper()
* here, because make_subplan() will insert type
* coercion calls if needed.
*/
optup
=
oper
(
op
,
exprType
(
lexpr
),
exprType
((
Node
*
)
tent
->
expr
),
false
);
opform
=
(
Form_pg_operator
)
GETSTRUCT
(
optup
);
if
(
opform
->
oprresult
!=
BOOLOID
)
ereport
(
ERROR
,
(
errcode
(
ERRCODE_DATATYPE_MISMATCH
),
errmsg
(
"operator %s must return type boolean, not type %s"
,
opname
,
format_type_be
(
opform
->
oprresult
)),
errhint
(
"The operator of a quantified predicate subquery must return type boolean."
)));
if
(
get_func_retset
(
opform
->
oprcode
))
ereport
(
ERROR
,
(
errcode
(
ERRCODE_DATATYPE_MISMATCH
),
errmsg
(
"operator %s must not return a set"
,
opname
),
errhint
(
"The operator of a quantified predicate subquery must return type boolean."
)));
sublink
->
operOids
=
lappend_oid
(
sublink
->
operOids
,
oprid
(
optup
));
ReleaseSysCache
(
optup
);
}
if
(
ll_item
!=
NULL
)
ereport
(
ERROR
,
(
errcode
(
ERRCODE_SYNTAX_ERROR
),
errmsg
(
"subquery has too few columns"
)));
if
(
needNot
)
{
expr
=
coerce_to_boolean
(
pstate
,
expr
,
"NOT"
);
expr
=
(
Node
*
)
makeBoolExpr
(
NOT_EXPR
,
list_make1
(
expr
));
}
}
result
=
(
Node
*
)
expr
;
break
;
}
result
=
transformSubLink
(
pstate
,
(
SubLink
*
)
expr
);
break
;
case
T_CaseExpr
:
{
CaseExpr
*
c
=
(
CaseExpr
*
)
expr
;
CaseExpr
*
newc
;
Node
*
arg
;
CaseTestExpr
*
placeholder
;
List
*
newargs
;
List
*
typeids
;
ListCell
*
l
;
Node
*
defresult
;
Oid
ptype
;
/* If we already transformed this node, do nothing */
if
(
OidIsValid
(
c
->
casetype
))
{
result
=
expr
;
break
;
}
newc
=
makeNode
(
CaseExpr
);
/* transform the test expression, if any */
arg
=
transformExpr
(
pstate
,
(
Node
*
)
c
->
arg
);
/* generate placeholder for test expression */
if
(
arg
)
{
/*
* If test expression is an untyped literal, force it to
* text. We have to do something now because we won't be
* able to do this coercion on the placeholder. This is
* not as flexible as what was done in 7.4 and before,
* but it's good enough to handle the sort of silly
* coding commonly seen.
*/
if
(
exprType
(
arg
)
==
UNKNOWNOID
)
arg
=
coerce_to_common_type
(
pstate
,
arg
,
TEXTOID
,
"CASE"
);
placeholder
=
makeNode
(
CaseTestExpr
);
placeholder
->
typeId
=
exprType
(
arg
);
placeholder
->
typeMod
=
exprTypmod
(
arg
);
}
else
placeholder
=
NULL
;
newc
->
arg
=
(
Expr
*
)
arg
;
/* transform the list of arguments */
newargs
=
NIL
;
typeids
=
NIL
;
foreach
(
l
,
c
->
args
)
{
CaseWhen
*
w
=
(
CaseWhen
*
)
lfirst
(
l
);
CaseWhen
*
neww
=
makeNode
(
CaseWhen
);
Node
*
warg
;
Assert
(
IsA
(
w
,
CaseWhen
));
warg
=
(
Node
*
)
w
->
expr
;
if
(
placeholder
)
{
/* shorthand form was specified, so expand... */
warg
=
(
Node
*
)
makeSimpleA_Expr
(
AEXPR_OP
,
"="
,
(
Node
*
)
placeholder
,
warg
);
}
neww
->
expr
=
(
Expr
*
)
transformExpr
(
pstate
,
warg
);
neww
->
expr
=
(
Expr
*
)
coerce_to_boolean
(
pstate
,
(
Node
*
)
neww
->
expr
,
"CASE/WHEN"
);
warg
=
(
Node
*
)
w
->
result
;
neww
->
result
=
(
Expr
*
)
transformExpr
(
pstate
,
warg
);
newargs
=
lappend
(
newargs
,
neww
);
typeids
=
lappend_oid
(
typeids
,
exprType
((
Node
*
)
neww
->
result
));
}
newc
->
args
=
newargs
;
/* transform the default clause */
defresult
=
(
Node
*
)
c
->
defresult
;
if
(
defresult
==
NULL
)
{
A_Const
*
n
=
makeNode
(
A_Const
);
n
->
val
.
type
=
T_Null
;
defresult
=
(
Node
*
)
n
;
}
newc
->
defresult
=
(
Expr
*
)
transformExpr
(
pstate
,
defresult
);
/*
* Note: default result is considered the most significant
* type in determining preferred type. This is how the
* code worked before, but it seems a little bogus to me
* --- tgl
*/
typeids
=
lcons_oid
(
exprType
((
Node
*
)
newc
->
defresult
),
typeids
);
ptype
=
select_common_type
(
typeids
,
"CASE"
);
Assert
(
OidIsValid
(
ptype
));
newc
->
casetype
=
ptype
;
/* Convert default result clause, if necessary */
newc
->
defresult
=
(
Expr
*
)
coerce_to_common_type
(
pstate
,
(
Node
*
)
newc
->
defresult
,
ptype
,
"CASE/ELSE"
);
/* Convert when-clause results, if necessary */
foreach
(
l
,
newc
->
args
)
{
CaseWhen
*
w
=
(
CaseWhen
*
)
lfirst
(
l
);
w
->
result
=
(
Expr
*
)
coerce_to_common_type
(
pstate
,
(
Node
*
)
w
->
result
,
ptype
,
"CASE/WHEN"
);
}
result
=
(
Node
*
)
newc
;
break
;
}
result
=
transformCaseExpr
(
pstate
,
(
CaseExpr
*
)
expr
);
break
;
case
T_ArrayExpr
:
{
ArrayExpr
*
a
=
(
ArrayExpr
*
)
expr
;
ArrayExpr
*
newa
=
makeNode
(
ArrayExpr
);
List
*
newelems
=
NIL
;
List
*
newcoercedelems
=
NIL
;
List
*
typeids
=
NIL
;
ListCell
*
element
;
Oid
array_type
;
Oid
element_type
;
/* Transform the element expressions */
foreach
(
element
,
a
->
elements
)
{
Node
*
e
=
(
Node
*
)
lfirst
(
element
);
Node
*
newe
;
newe
=
transformExpr
(
pstate
,
e
);
newelems
=
lappend
(
newelems
,
newe
);
typeids
=
lappend_oid
(
typeids
,
exprType
(
newe
));
}
/* Select a common type for the elements */
element_type
=
select_common_type
(
typeids
,
"ARRAY"
);
/* Coerce arguments to common type if necessary */
foreach
(
element
,
newelems
)
{
Node
*
e
=
(
Node
*
)
lfirst
(
element
);
Node
*
newe
;
newe
=
coerce_to_common_type
(
pstate
,
e
,
element_type
,
"ARRAY"
);
newcoercedelems
=
lappend
(
newcoercedelems
,
newe
);
}
/* Do we have an array type to use? */
array_type
=
get_array_type
(
element_type
);
if
(
array_type
!=
InvalidOid
)
{
/* Elements are presumably of scalar type */
newa
->
multidims
=
false
;
}
else
{
/* Must be nested array expressions */
newa
->
multidims
=
true
;
array_type
=
element_type
;
element_type
=
get_element_type
(
array_type
);
if
(
!
OidIsValid
(
element_type
))
ereport
(
ERROR
,
(
errcode
(
ERRCODE_UNDEFINED_OBJECT
),
errmsg
(
"could not find array type for data type %s"
,
format_type_be
(
array_type
))));
}
newa
->
array_typeid
=
array_type
;
newa
->
element_typeid
=
element_type
;
newa
->
elements
=
newcoercedelems
;
result
=
(
Node
*
)
newa
;
break
;
}
result
=
transformArrayExpr
(
pstate
,
(
ArrayExpr
*
)
expr
);
break
;
case
T_RowExpr
:
{
RowExpr
*
r
=
(
RowExpr
*
)
expr
;
RowExpr
*
newr
=
makeNode
(
RowExpr
);
List
*
newargs
=
NIL
;
ListCell
*
arg
;
/* Transform the field expressions */
foreach
(
arg
,
r
->
args
)
{
Node
*
e
=
(
Node
*
)
lfirst
(
arg
);
Node
*
newe
;
newe
=
transformExpr
(
pstate
,
e
);
newargs
=
lappend
(
newargs
,
newe
);
}
newr
->
args
=
newargs
;
/* Barring later casting, we consider the type RECORD */
newr
->
row_typeid
=
RECORDOID
;
newr
->
row_format
=
COERCE_IMPLICIT_CAST
;
result
=
(
Node
*
)
newr
;
break
;
}
result
=
transformRowExpr
(
pstate
,
(
RowExpr
*
)
expr
);
break
;
case
T_CoalesceExpr
:
{
CoalesceExpr
*
c
=
(
CoalesceExpr
*
)
expr
;
CoalesceExpr
*
newc
=
makeNode
(
CoalesceExpr
);
List
*
newargs
=
NIL
;
List
*
newcoercedargs
=
NIL
;
List
*
typeids
=
NIL
;
ListCell
*
args
;
foreach
(
args
,
c
->
args
)
{
Node
*
e
=
(
Node
*
)
lfirst
(
args
);
Node
*
newe
;
newe
=
transformExpr
(
pstate
,
e
);
newargs
=
lappend
(
newargs
,
newe
);
typeids
=
lappend_oid
(
typeids
,
exprType
(
newe
));
}
newc
->
coalescetype
=
select_common_type
(
typeids
,
"COALESCE"
);
/* Convert arguments if necessary */
foreach
(
args
,
newargs
)
{
Node
*
e
=
(
Node
*
)
lfirst
(
args
);
Node
*
newe
;
newe
=
coerce_to_common_type
(
pstate
,
e
,
newc
->
coalescetype
,
"COALESCE"
);
newcoercedargs
=
lappend
(
newcoercedargs
,
newe
);
}
newc
->
args
=
newcoercedargs
;
result
=
(
Node
*
)
newc
;
break
;
}
result
=
transformCoalesceExpr
(
pstate
,
(
CoalesceExpr
*
)
expr
);
break
;
case
T_NullTest
:
{
...
...
@@ -891,45 +220,8 @@ transformExpr(ParseState *pstate, Node *expr)
}
case
T_BooleanTest
:
{
BooleanTest
*
b
=
(
BooleanTest
*
)
expr
;
const
char
*
clausename
;
switch
(
b
->
booltesttype
)
{
case
IS_TRUE
:
clausename
=
"IS TRUE"
;
break
;
case
IS_NOT_TRUE
:
clausename
=
"IS NOT TRUE"
;
break
;
case
IS_FALSE
:
clausename
=
"IS FALSE"
;
break
;
case
IS_NOT_FALSE
:
clausename
=
"IS NOT FALSE"
;
break
;
case
IS_UNKNOWN
:
clausename
=
"IS UNKNOWN"
;
break
;
case
IS_NOT_UNKNOWN
:
clausename
=
"IS NOT UNKNOWN"
;
break
;
default:
elog
(
ERROR
,
"unrecognized booltesttype: %d"
,
(
int
)
b
->
booltesttype
);
clausename
=
NULL
;
/* keep compiler quiet */
}
b
->
arg
=
(
Expr
*
)
transformExpr
(
pstate
,
(
Node
*
)
b
->
arg
);
b
->
arg
=
(
Expr
*
)
coerce_to_boolean
(
pstate
,
(
Node
*
)
b
->
arg
,
clausename
);
result
=
expr
;
break
;
}
result
=
transformBooleanTest
(
pstate
,
(
BooleanTest
*
)
expr
);
break
;
/*********************************************
* Quietly accept node types that may be presented when we are
...
...
@@ -1203,6 +495,780 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
return
node
;
}
static
Node
*
transformParamRef
(
ParseState
*
pstate
,
ParamRef
*
pref
)
{
int
paramno
=
pref
->
number
;
ParseState
*
toppstate
;
Param
*
param
;
/*
* Find topmost ParseState, which is where paramtype info lives.
*/
toppstate
=
pstate
;
while
(
toppstate
->
parentParseState
!=
NULL
)
toppstate
=
toppstate
->
parentParseState
;
/* Check parameter number is in range */
if
(
paramno
<=
0
)
/* probably can't happen? */
ereport
(
ERROR
,
(
errcode
(
ERRCODE_UNDEFINED_PARAMETER
),
errmsg
(
"there is no parameter $%d"
,
paramno
)));
if
(
paramno
>
toppstate
->
p_numparams
)
{
if
(
!
toppstate
->
p_variableparams
)
ereport
(
ERROR
,
(
errcode
(
ERRCODE_UNDEFINED_PARAMETER
),
errmsg
(
"there is no parameter $%d"
,
paramno
)));
/* Okay to enlarge param array */
if
(
toppstate
->
p_paramtypes
)
toppstate
->
p_paramtypes
=
(
Oid
*
)
repalloc
(
toppstate
->
p_paramtypes
,
paramno
*
sizeof
(
Oid
));
else
toppstate
->
p_paramtypes
=
(
Oid
*
)
palloc
(
paramno
*
sizeof
(
Oid
));
/* Zero out the previously-unreferenced slots */
MemSet
(
toppstate
->
p_paramtypes
+
toppstate
->
p_numparams
,
0
,
(
paramno
-
toppstate
->
p_numparams
)
*
sizeof
(
Oid
));
toppstate
->
p_numparams
=
paramno
;
}
if
(
toppstate
->
p_variableparams
)
{
/* If not seen before, initialize to UNKNOWN type */
if
(
toppstate
->
p_paramtypes
[
paramno
-
1
]
==
InvalidOid
)
toppstate
->
p_paramtypes
[
paramno
-
1
]
=
UNKNOWNOID
;
}
param
=
makeNode
(
Param
);
param
->
paramkind
=
PARAM_NUM
;
param
->
paramid
=
(
AttrNumber
)
paramno
;
param
->
paramtype
=
toppstate
->
p_paramtypes
[
paramno
-
1
];
return
(
Node
*
)
param
;
}
static
Node
*
transformAExprOp
(
ParseState
*
pstate
,
A_Expr
*
a
)
{
Node
*
lexpr
=
a
->
lexpr
;
Node
*
rexpr
=
a
->
rexpr
;
Node
*
result
;
/*
* Special-case "foo = NULL" and "NULL = foo" for compatibility
* with standards-broken products (like Microsoft's). Turn these
* into IS NULL exprs.
*/
if
(
Transform_null_equals
&&
list_length
(
a
->
name
)
==
1
&&
strcmp
(
strVal
(
linitial
(
a
->
name
)),
"="
)
==
0
&&
(
exprIsNullConstant
(
lexpr
)
||
exprIsNullConstant
(
rexpr
)))
{
NullTest
*
n
=
makeNode
(
NullTest
);
n
->
nulltesttype
=
IS_NULL
;
if
(
exprIsNullConstant
(
lexpr
))
n
->
arg
=
(
Expr
*
)
rexpr
;
else
n
->
arg
=
(
Expr
*
)
lexpr
;
result
=
transformExpr
(
pstate
,
(
Node
*
)
n
);
}
else
if
(
lexpr
&&
IsA
(
lexpr
,
RowExpr
)
&&
rexpr
&&
IsA
(
rexpr
,
SubLink
)
&&
((
SubLink
*
)
rexpr
)
->
subLinkType
==
EXPR_SUBLINK
)
{
/*
* Convert "row op subselect" into a MULTIEXPR sublink.
* Formerly the grammar did this, but now that a row construct
* is allowed anywhere in expressions, it's easier to do it
* here.
*/
SubLink
*
s
=
(
SubLink
*
)
rexpr
;
s
->
subLinkType
=
MULTIEXPR_SUBLINK
;
s
->
lefthand
=
((
RowExpr
*
)
lexpr
)
->
args
;
s
->
operName
=
a
->
name
;
result
=
transformExpr
(
pstate
,
(
Node
*
)
s
);
}
else
if
(
lexpr
&&
IsA
(
lexpr
,
RowExpr
)
&&
rexpr
&&
IsA
(
rexpr
,
RowExpr
))
{
/* "row op row" */
result
=
make_row_op
(
pstate
,
a
->
name
,
lexpr
,
rexpr
);
}
else
{
/* Ordinary scalar operator */
lexpr
=
transformExpr
(
pstate
,
lexpr
);
rexpr
=
transformExpr
(
pstate
,
rexpr
);
result
=
(
Node
*
)
make_op
(
pstate
,
a
->
name
,
lexpr
,
rexpr
);
}
return
result
;
}
static
Node
*
transformAExprAnd
(
ParseState
*
pstate
,
A_Expr
*
a
)
{
Node
*
lexpr
=
transformExpr
(
pstate
,
a
->
lexpr
);
Node
*
rexpr
=
transformExpr
(
pstate
,
a
->
rexpr
);
lexpr
=
coerce_to_boolean
(
pstate
,
lexpr
,
"AND"
);
rexpr
=
coerce_to_boolean
(
pstate
,
rexpr
,
"AND"
);
return
(
Node
*
)
makeBoolExpr
(
AND_EXPR
,
list_make2
(
lexpr
,
rexpr
));
}
static
Node
*
transformAExprOr
(
ParseState
*
pstate
,
A_Expr
*
a
)
{
Node
*
lexpr
=
transformExpr
(
pstate
,
a
->
lexpr
);
Node
*
rexpr
=
transformExpr
(
pstate
,
a
->
rexpr
);
lexpr
=
coerce_to_boolean
(
pstate
,
lexpr
,
"OR"
);
rexpr
=
coerce_to_boolean
(
pstate
,
rexpr
,
"OR"
);
return
(
Node
*
)
makeBoolExpr
(
OR_EXPR
,
list_make2
(
lexpr
,
rexpr
));
}
static
Node
*
transformAExprNot
(
ParseState
*
pstate
,
A_Expr
*
a
)
{
Node
*
rexpr
=
transformExpr
(
pstate
,
a
->
rexpr
);
rexpr
=
coerce_to_boolean
(
pstate
,
rexpr
,
"NOT"
);
return
(
Node
*
)
makeBoolExpr
(
NOT_EXPR
,
list_make1
(
rexpr
));
}
static
Node
*
transformAExprOpAny
(
ParseState
*
pstate
,
A_Expr
*
a
)
{
Node
*
lexpr
=
transformExpr
(
pstate
,
a
->
lexpr
);
Node
*
rexpr
=
transformExpr
(
pstate
,
a
->
rexpr
);
return
(
Node
*
)
make_scalar_array_op
(
pstate
,
a
->
name
,
true
,
lexpr
,
rexpr
);
}
static
Node
*
transformAExprOpAll
(
ParseState
*
pstate
,
A_Expr
*
a
)
{
Node
*
lexpr
=
transformExpr
(
pstate
,
a
->
lexpr
);
Node
*
rexpr
=
transformExpr
(
pstate
,
a
->
rexpr
);
return
(
Node
*
)
make_scalar_array_op
(
pstate
,
a
->
name
,
false
,
lexpr
,
rexpr
);
}
static
Node
*
transformAExprDistinct
(
ParseState
*
pstate
,
A_Expr
*
a
)
{
Node
*
lexpr
=
a
->
lexpr
;
Node
*
rexpr
=
a
->
rexpr
;
if
(
lexpr
&&
IsA
(
lexpr
,
RowExpr
)
&&
rexpr
&&
IsA
(
rexpr
,
RowExpr
))
{
/* "row op row" */
return
make_row_distinct_op
(
pstate
,
a
->
name
,
lexpr
,
rexpr
);
}
else
{
/* Ordinary scalar operator */
lexpr
=
transformExpr
(
pstate
,
lexpr
);
rexpr
=
transformExpr
(
pstate
,
rexpr
);
return
(
Node
*
)
make_distinct_op
(
pstate
,
a
->
name
,
lexpr
,
rexpr
);
}
}
static
Node
*
transformAExprNullIf
(
ParseState
*
pstate
,
A_Expr
*
a
)
{
Node
*
lexpr
=
transformExpr
(
pstate
,
a
->
lexpr
);
Node
*
rexpr
=
transformExpr
(
pstate
,
a
->
rexpr
);
Node
*
result
;
result
=
(
Node
*
)
make_op
(
pstate
,
a
->
name
,
lexpr
,
rexpr
);
if
(((
OpExpr
*
)
result
)
->
opresulttype
!=
BOOLOID
)
ereport
(
ERROR
,
(
errcode
(
ERRCODE_DATATYPE_MISMATCH
),
errmsg
(
"NULLIF requires = operator to yield boolean"
)));
/*
* We rely on NullIfExpr and OpExpr being the same struct
*/
NodeSetTag
(
result
,
T_NullIfExpr
);
return
result
;
}
static
Node
*
transformAExprOf
(
ParseState
*
pstate
,
A_Expr
*
a
)
{
/*
* Checking an expression for match to type. Will result in a
* boolean constant node.
*/
ListCell
*
telem
;
A_Const
*
n
;
Oid
ltype
,
rtype
;
bool
matched
=
false
;
Node
*
lexpr
=
transformExpr
(
pstate
,
a
->
lexpr
);
ltype
=
exprType
(
lexpr
);
foreach
(
telem
,
(
List
*
)
a
->
rexpr
)
{
rtype
=
LookupTypeName
(
lfirst
(
telem
));
matched
=
(
rtype
==
ltype
);
if
(
matched
)
break
;
}
/*
* Expect two forms: equals or not equals. Flip the sense of the
* result for not equals.
*/
if
(
strcmp
(
strVal
(
linitial
(
a
->
name
)),
"!="
)
==
0
)
matched
=
(
!
matched
);
n
=
makeNode
(
A_Const
);
n
->
val
.
type
=
T_String
;
n
->
val
.
val
.
str
=
(
matched
?
"t"
:
"f"
);
n
->
typename
=
SystemTypeName
(
"bool"
);
return
transformExpr
(
pstate
,
(
Node
*
)
n
);
}
static
Node
*
transformFuncCall
(
ParseState
*
pstate
,
FuncCall
*
fn
)
{
List
*
targs
;
ListCell
*
args
;
/*
* Transform the list of arguments. We use a shallow list copy
* and then transform-in-place to avoid O(N^2) behavior from
* repeated lappend's.
*
* XXX: repeated lappend() would no longer result in O(n^2)
* behavior; worth reconsidering this design?
*/
targs
=
list_copy
(
fn
->
args
);
foreach
(
args
,
targs
)
{
lfirst
(
args
)
=
transformExpr
(
pstate
,
(
Node
*
)
lfirst
(
args
));
}
return
ParseFuncOrColumn
(
pstate
,
fn
->
funcname
,
targs
,
fn
->
agg_star
,
fn
->
agg_distinct
,
false
);
}
static
Node
*
transformCaseExpr
(
ParseState
*
pstate
,
CaseExpr
*
c
)
{
CaseExpr
*
newc
;
Node
*
arg
;
CaseTestExpr
*
placeholder
;
List
*
newargs
;
List
*
typeids
;
ListCell
*
l
;
Node
*
defresult
;
Oid
ptype
;
/* If we already transformed this node, do nothing */
if
(
OidIsValid
(
c
->
casetype
))
return
(
Node
*
)
c
;
newc
=
makeNode
(
CaseExpr
);
/* transform the test expression, if any */
arg
=
transformExpr
(
pstate
,
(
Node
*
)
c
->
arg
);
/* generate placeholder for test expression */
if
(
arg
)
{
/*
* If test expression is an untyped literal, force it to text.
* We have to do something now because we won't be able to do
* this coercion on the placeholder. This is not as flexible
* as what was done in 7.4 and before, but it's good enough to
* handle the sort of silly coding commonly seen.
*/
if
(
exprType
(
arg
)
==
UNKNOWNOID
)
arg
=
coerce_to_common_type
(
pstate
,
arg
,
TEXTOID
,
"CASE"
);
placeholder
=
makeNode
(
CaseTestExpr
);
placeholder
->
typeId
=
exprType
(
arg
);
placeholder
->
typeMod
=
exprTypmod
(
arg
);
}
else
placeholder
=
NULL
;
newc
->
arg
=
(
Expr
*
)
arg
;
/* transform the list of arguments */
newargs
=
NIL
;
typeids
=
NIL
;
foreach
(
l
,
c
->
args
)
{
CaseWhen
*
w
=
(
CaseWhen
*
)
lfirst
(
l
);
CaseWhen
*
neww
=
makeNode
(
CaseWhen
);
Node
*
warg
;
Assert
(
IsA
(
w
,
CaseWhen
));
warg
=
(
Node
*
)
w
->
expr
;
if
(
placeholder
)
{
/* shorthand form was specified, so expand... */
warg
=
(
Node
*
)
makeSimpleA_Expr
(
AEXPR_OP
,
"="
,
(
Node
*
)
placeholder
,
warg
);
}
neww
->
expr
=
(
Expr
*
)
transformExpr
(
pstate
,
warg
);
neww
->
expr
=
(
Expr
*
)
coerce_to_boolean
(
pstate
,
(
Node
*
)
neww
->
expr
,
"CASE/WHEN"
);
warg
=
(
Node
*
)
w
->
result
;
neww
->
result
=
(
Expr
*
)
transformExpr
(
pstate
,
warg
);
newargs
=
lappend
(
newargs
,
neww
);
typeids
=
lappend_oid
(
typeids
,
exprType
((
Node
*
)
neww
->
result
));
}
newc
->
args
=
newargs
;
/* transform the default clause */
defresult
=
(
Node
*
)
c
->
defresult
;
if
(
defresult
==
NULL
)
{
A_Const
*
n
=
makeNode
(
A_Const
);
n
->
val
.
type
=
T_Null
;
defresult
=
(
Node
*
)
n
;
}
newc
->
defresult
=
(
Expr
*
)
transformExpr
(
pstate
,
defresult
);
/*
* Note: default result is considered the most significant type in
* determining preferred type. This is how the code worked before,
* but it seems a little bogus to me
* --- tgl
*/
typeids
=
lcons_oid
(
exprType
((
Node
*
)
newc
->
defresult
),
typeids
);
ptype
=
select_common_type
(
typeids
,
"CASE"
);
Assert
(
OidIsValid
(
ptype
));
newc
->
casetype
=
ptype
;
/* Convert default result clause, if necessary */
newc
->
defresult
=
(
Expr
*
)
coerce_to_common_type
(
pstate
,
(
Node
*
)
newc
->
defresult
,
ptype
,
"CASE/ELSE"
);
/* Convert when-clause results, if necessary */
foreach
(
l
,
newc
->
args
)
{
CaseWhen
*
w
=
(
CaseWhen
*
)
lfirst
(
l
);
w
->
result
=
(
Expr
*
)
coerce_to_common_type
(
pstate
,
(
Node
*
)
w
->
result
,
ptype
,
"CASE/WHEN"
);
}
return
(
Node
*
)
newc
;
}
static
Node
*
transformSubLink
(
ParseState
*
pstate
,
SubLink
*
sublink
)
{
List
*
qtrees
;
Query
*
qtree
;
Node
*
result
=
(
Node
*
)
sublink
;
/* If we already transformed this node, do nothing */
if
(
IsA
(
sublink
->
subselect
,
Query
))
return
result
;
pstate
->
p_hasSubLinks
=
true
;
qtrees
=
parse_sub_analyze
(
sublink
->
subselect
,
pstate
);
if
(
list_length
(
qtrees
)
!=
1
)
elog
(
ERROR
,
"bad query in sub-select"
);
qtree
=
(
Query
*
)
linitial
(
qtrees
);
if
(
qtree
->
commandType
!=
CMD_SELECT
||
qtree
->
resultRelation
!=
0
)
elog
(
ERROR
,
"bad query in sub-select"
);
sublink
->
subselect
=
(
Node
*
)
qtree
;
if
(
sublink
->
subLinkType
==
EXISTS_SUBLINK
)
{
/*
* EXISTS needs no lefthand or combining operator. These
* fields should be NIL already, but make sure.
*/
sublink
->
lefthand
=
NIL
;
sublink
->
operName
=
NIL
;
sublink
->
operOids
=
NIL
;
sublink
->
useOr
=
FALSE
;
}
else
if
(
sublink
->
subLinkType
==
EXPR_SUBLINK
||
sublink
->
subLinkType
==
ARRAY_SUBLINK
)
{
ListCell
*
tlist_item
=
list_head
(
qtree
->
targetList
);
/*
* Make sure the subselect delivers a single column (ignoring
* resjunk targets).
*/
if
(
tlist_item
==
NULL
||
((
TargetEntry
*
)
lfirst
(
tlist_item
))
->
resdom
->
resjunk
)
ereport
(
ERROR
,
(
errcode
(
ERRCODE_SYNTAX_ERROR
),
errmsg
(
"subquery must return a column"
)));
while
((
tlist_item
=
lnext
(
tlist_item
))
!=
NULL
)
{
if
(
!
((
TargetEntry
*
)
lfirst
(
tlist_item
))
->
resdom
->
resjunk
)
ereport
(
ERROR
,
(
errcode
(
ERRCODE_SYNTAX_ERROR
),
errmsg
(
"subquery must return only one column"
)));
}
/*
* EXPR and ARRAY need no lefthand or combining
* operator. These fields should be NIL already, but make
* sure.
*/
sublink
->
lefthand
=
NIL
;
sublink
->
operName
=
NIL
;
sublink
->
operOids
=
NIL
;
sublink
->
useOr
=
FALSE
;
}
else
{
/* ALL, ANY, or MULTIEXPR: generate operator list */
List
*
left_list
=
sublink
->
lefthand
;
List
*
right_list
=
qtree
->
targetList
;
int
row_length
=
list_length
(
left_list
);
bool
needNot
=
false
;
List
*
op
=
sublink
->
operName
;
char
*
opname
=
strVal
(
llast
(
op
));
ListCell
*
l
;
ListCell
*
ll_item
;
/* transform lefthand expressions */
foreach
(
l
,
left_list
)
lfirst
(
l
)
=
transformExpr
(
pstate
,
lfirst
(
l
));
/*
* If the expression is "<> ALL" (with unqualified opname)
* then convert it to "NOT IN". This is a hack to improve
* efficiency of expressions output by pre-7.4 Postgres.
*/
if
(
sublink
->
subLinkType
==
ALL_SUBLINK
&&
list_length
(
op
)
==
1
&&
strcmp
(
opname
,
"<>"
)
==
0
)
{
sublink
->
subLinkType
=
ANY_SUBLINK
;
opname
=
pstrdup
(
"="
);
op
=
list_make1
(
makeString
(
opname
));
sublink
->
operName
=
op
;
needNot
=
true
;
}
/* Set useOr if op is "<>" (possibly qualified) */
if
(
strcmp
(
opname
,
"<>"
)
==
0
)
sublink
->
useOr
=
TRUE
;
else
sublink
->
useOr
=
FALSE
;
/* Combining operators other than =/<> is dubious... */
if
(
row_length
!=
1
&&
strcmp
(
opname
,
"="
)
!=
0
&&
strcmp
(
opname
,
"<>"
)
!=
0
)
ereport
(
ERROR
,
(
errcode
(
ERRCODE_FEATURE_NOT_SUPPORTED
),
errmsg
(
"row comparison cannot use operator %s"
,
opname
)));
/*
* To build the list of combining operator OIDs, we must scan
* subquery's targetlist to find values that will be matched
* against lefthand values. We need to ignore resjunk
* targets, so doing the outer iteration over right_list is
* easier than doing it over left_list.
*/
sublink
->
operOids
=
NIL
;
ll_item
=
list_head
(
left_list
);
foreach
(
l
,
right_list
)
{
TargetEntry
*
tent
=
(
TargetEntry
*
)
lfirst
(
l
);
Node
*
lexpr
;
Operator
optup
;
Form_pg_operator
opform
;
if
(
tent
->
resdom
->
resjunk
)
continue
;
if
(
ll_item
==
NULL
)
ereport
(
ERROR
,
(
errcode
(
ERRCODE_SYNTAX_ERROR
),
errmsg
(
"subquery has too many columns"
)));
lexpr
=
lfirst
(
ll_item
);
ll_item
=
lnext
(
ll_item
);
/*
* It's OK to use oper() not compatible_oper() here,
* because make_subplan() will insert type coercion calls
* if needed.
*/
optup
=
oper
(
op
,
exprType
(
lexpr
),
exprType
((
Node
*
)
tent
->
expr
),
false
);
opform
=
(
Form_pg_operator
)
GETSTRUCT
(
optup
);
if
(
opform
->
oprresult
!=
BOOLOID
)
ereport
(
ERROR
,
(
errcode
(
ERRCODE_DATATYPE_MISMATCH
),
errmsg
(
"operator %s must return type boolean, not type %s"
,
opname
,
format_type_be
(
opform
->
oprresult
)),
errhint
(
"The operator of a quantified predicate subquery must return type boolean."
)));
if
(
get_func_retset
(
opform
->
oprcode
))
ereport
(
ERROR
,
(
errcode
(
ERRCODE_DATATYPE_MISMATCH
),
errmsg
(
"operator %s must not return a set"
,
opname
),
errhint
(
"The operator of a quantified predicate subquery must return type boolean."
)));
sublink
->
operOids
=
lappend_oid
(
sublink
->
operOids
,
oprid
(
optup
));
ReleaseSysCache
(
optup
);
}
if
(
ll_item
!=
NULL
)
ereport
(
ERROR
,
(
errcode
(
ERRCODE_SYNTAX_ERROR
),
errmsg
(
"subquery has too few columns"
)));
if
(
needNot
)
{
result
=
coerce_to_boolean
(
pstate
,
result
,
"NOT"
);
result
=
(
Node
*
)
makeBoolExpr
(
NOT_EXPR
,
list_make1
(
result
));
}
}
return
result
;
}
static
Node
*
transformArrayExpr
(
ParseState
*
pstate
,
ArrayExpr
*
a
)
{
ArrayExpr
*
newa
=
makeNode
(
ArrayExpr
);
List
*
newelems
=
NIL
;
List
*
newcoercedelems
=
NIL
;
List
*
typeids
=
NIL
;
ListCell
*
element
;
Oid
array_type
;
Oid
element_type
;
/* Transform the element expressions */
foreach
(
element
,
a
->
elements
)
{
Node
*
e
=
(
Node
*
)
lfirst
(
element
);
Node
*
newe
;
newe
=
transformExpr
(
pstate
,
e
);
newelems
=
lappend
(
newelems
,
newe
);
typeids
=
lappend_oid
(
typeids
,
exprType
(
newe
));
}
/* Select a common type for the elements */
element_type
=
select_common_type
(
typeids
,
"ARRAY"
);
/* Coerce arguments to common type if necessary */
foreach
(
element
,
newelems
)
{
Node
*
e
=
(
Node
*
)
lfirst
(
element
);
Node
*
newe
;
newe
=
coerce_to_common_type
(
pstate
,
e
,
element_type
,
"ARRAY"
);
newcoercedelems
=
lappend
(
newcoercedelems
,
newe
);
}
/* Do we have an array type to use? */
array_type
=
get_array_type
(
element_type
);
if
(
array_type
!=
InvalidOid
)
{
/* Elements are presumably of scalar type */
newa
->
multidims
=
false
;
}
else
{
/* Must be nested array expressions */
newa
->
multidims
=
true
;
array_type
=
element_type
;
element_type
=
get_element_type
(
array_type
);
if
(
!
OidIsValid
(
element_type
))
ereport
(
ERROR
,
(
errcode
(
ERRCODE_UNDEFINED_OBJECT
),
errmsg
(
"could not find array type for data type %s"
,
format_type_be
(
array_type
))));
}
newa
->
array_typeid
=
array_type
;
newa
->
element_typeid
=
element_type
;
newa
->
elements
=
newcoercedelems
;
return
(
Node
*
)
newa
;
}
static
Node
*
transformRowExpr
(
ParseState
*
pstate
,
RowExpr
*
r
)
{
RowExpr
*
newr
=
makeNode
(
RowExpr
);
List
*
newargs
=
NIL
;
ListCell
*
arg
;
/* Transform the field expressions */
foreach
(
arg
,
r
->
args
)
{
Node
*
e
=
(
Node
*
)
lfirst
(
arg
);
Node
*
newe
;
newe
=
transformExpr
(
pstate
,
e
);
newargs
=
lappend
(
newargs
,
newe
);
}
newr
->
args
=
newargs
;
/* Barring later casting, we consider the type RECORD */
newr
->
row_typeid
=
RECORDOID
;
newr
->
row_format
=
COERCE_IMPLICIT_CAST
;
return
(
Node
*
)
newr
;
}
static
Node
*
transformCoalesceExpr
(
ParseState
*
pstate
,
CoalesceExpr
*
c
)
{
CoalesceExpr
*
newc
=
makeNode
(
CoalesceExpr
);
List
*
newargs
=
NIL
;
List
*
newcoercedargs
=
NIL
;
List
*
typeids
=
NIL
;
ListCell
*
args
;
foreach
(
args
,
c
->
args
)
{
Node
*
e
=
(
Node
*
)
lfirst
(
args
);
Node
*
newe
;
newe
=
transformExpr
(
pstate
,
e
);
newargs
=
lappend
(
newargs
,
newe
);
typeids
=
lappend_oid
(
typeids
,
exprType
(
newe
));
}
newc
->
coalescetype
=
select_common_type
(
typeids
,
"COALESCE"
);
/* Convert arguments if necessary */
foreach
(
args
,
newargs
)
{
Node
*
e
=
(
Node
*
)
lfirst
(
args
);
Node
*
newe
;
newe
=
coerce_to_common_type
(
pstate
,
e
,
newc
->
coalescetype
,
"COALESCE"
);
newcoercedargs
=
lappend
(
newcoercedargs
,
newe
);
}
newc
->
args
=
newcoercedargs
;
return
(
Node
*
)
newc
;
}
static
Node
*
transformBooleanTest
(
ParseState
*
pstate
,
BooleanTest
*
b
)
{
const
char
*
clausename
;
switch
(
b
->
booltesttype
)
{
case
IS_TRUE
:
clausename
=
"IS TRUE"
;
break
;
case
IS_NOT_TRUE
:
clausename
=
"IS NOT TRUE"
;
break
;
case
IS_FALSE
:
clausename
=
"IS FALSE"
;
break
;
case
IS_NOT_FALSE
:
clausename
=
"IS NOT FALSE"
;
break
;
case
IS_UNKNOWN
:
clausename
=
"IS UNKNOWN"
;
break
;
case
IS_NOT_UNKNOWN
:
clausename
=
"IS NOT UNKNOWN"
;
break
;
default:
elog
(
ERROR
,
"unrecognized booltesttype: %d"
,
(
int
)
b
->
booltesttype
);
clausename
=
NULL
;
/* keep compiler quiet */
}
b
->
arg
=
(
Expr
*
)
transformExpr
(
pstate
,
(
Node
*
)
b
->
arg
);
b
->
arg
=
(
Expr
*
)
coerce_to_boolean
(
pstate
,
(
Node
*
)
b
->
arg
,
clausename
);
return
(
Node
*
)
b
;
}
/*
* Construct a whole-row reference to represent the notation "relation.*".
*
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment