mirror of
https://github.com/postgres/postgres.git
synced 2025-11-03 09:13:20 +03:00
Improve parsetree representation of special functions such as CURRENT_DATE.
We implement a dozen or so parameterless functions that the SQL standard defines special syntax for. Up to now, that was done by converting them into more or less ad-hoc constructs such as "'now'::text::date". That's messy for multiple reasons: it exposes what should be implementation details to users, and performance is worse than it needs to be in several cases. To improve matters, invent a new expression node type SQLValueFunction that can represent any of these parameterless functions. Bump catversion because this changes stored parsetrees for rules. Discussion: <30058.1463091294@sss.pgh.pa.us>
This commit is contained in:
@@ -165,6 +165,8 @@ static Node *makeAndExpr(Node *lexpr, Node *rexpr, int location);
|
||||
static Node *makeOrExpr(Node *lexpr, Node *rexpr, int location);
|
||||
static Node *makeNotExpr(Node *expr, int location);
|
||||
static Node *makeAArrayExpr(List *elements, int location);
|
||||
static Node *makeSQLValueFunction(SQLValueFunctionOp op, int32 typmod,
|
||||
int location);
|
||||
static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args,
|
||||
List *args, int location);
|
||||
static List *mergeTableFuncParameters(List *func_args, List *columns);
|
||||
@@ -12330,143 +12332,63 @@ func_expr_common_subexpr:
|
||||
}
|
||||
| CURRENT_DATE
|
||||
{
|
||||
/*
|
||||
* Translate as "'now'::text::date".
|
||||
*
|
||||
* We cannot use "'now'::date" because coerce_type() will
|
||||
* immediately reduce that to a constant representing
|
||||
* today's date. We need to delay the conversion until
|
||||
* runtime, else the wrong things will happen when
|
||||
* CURRENT_DATE is used in a column default value or rule.
|
||||
*
|
||||
* This could be simplified if we had a way to generate
|
||||
* an expression tree representing runtime application
|
||||
* of type-input conversion functions. (As of PG 7.3
|
||||
* that is actually possible, but not clear that we want
|
||||
* to rely on it.)
|
||||
*
|
||||
* The token location is attached to the run-time
|
||||
* typecast, not to the Const, for the convenience of
|
||||
* pg_stat_statements (which doesn't want these constructs
|
||||
* to appear to be replaceable constants).
|
||||
*/
|
||||
Node *n;
|
||||
n = makeStringConstCast("now", -1, SystemTypeName("text"));
|
||||
$$ = makeTypeCast(n, SystemTypeName("date"), @1);
|
||||
$$ = makeSQLValueFunction(SVFOP_CURRENT_DATE, -1, @1);
|
||||
}
|
||||
| CURRENT_TIME
|
||||
{
|
||||
/*
|
||||
* Translate as "'now'::text::timetz".
|
||||
* See comments for CURRENT_DATE.
|
||||
*/
|
||||
Node *n;
|
||||
n = makeStringConstCast("now", -1, SystemTypeName("text"));
|
||||
$$ = makeTypeCast(n, SystemTypeName("timetz"), @1);
|
||||
$$ = makeSQLValueFunction(SVFOP_CURRENT_TIME, -1, @1);
|
||||
}
|
||||
| CURRENT_TIME '(' Iconst ')'
|
||||
{
|
||||
/*
|
||||
* Translate as "'now'::text::timetz(n)".
|
||||
* See comments for CURRENT_DATE.
|
||||
*/
|
||||
Node *n;
|
||||
TypeName *d;
|
||||
n = makeStringConstCast("now", -1, SystemTypeName("text"));
|
||||
d = SystemTypeName("timetz");
|
||||
d->typmods = list_make1(makeIntConst($3, @3));
|
||||
$$ = makeTypeCast(n, d, @1);
|
||||
$$ = makeSQLValueFunction(SVFOP_CURRENT_TIME_N, $3, @1);
|
||||
}
|
||||
| CURRENT_TIMESTAMP
|
||||
{
|
||||
/*
|
||||
* Translate as "now()", since we have a function that
|
||||
* does exactly what is needed.
|
||||
*/
|
||||
$$ = (Node *) makeFuncCall(SystemFuncName("now"), NIL, @1);
|
||||
$$ = makeSQLValueFunction(SVFOP_CURRENT_TIMESTAMP, -1, @1);
|
||||
}
|
||||
| CURRENT_TIMESTAMP '(' Iconst ')'
|
||||
{
|
||||
/*
|
||||
* Translate as "'now'::text::timestamptz(n)".
|
||||
* See comments for CURRENT_DATE.
|
||||
*/
|
||||
Node *n;
|
||||
TypeName *d;
|
||||
n = makeStringConstCast("now", -1, SystemTypeName("text"));
|
||||
d = SystemTypeName("timestamptz");
|
||||
d->typmods = list_make1(makeIntConst($3, @3));
|
||||
$$ = makeTypeCast(n, d, @1);
|
||||
$$ = makeSQLValueFunction(SVFOP_CURRENT_TIMESTAMP_N, $3, @1);
|
||||
}
|
||||
| LOCALTIME
|
||||
{
|
||||
/*
|
||||
* Translate as "'now'::text::time".
|
||||
* See comments for CURRENT_DATE.
|
||||
*/
|
||||
Node *n;
|
||||
n = makeStringConstCast("now", -1, SystemTypeName("text"));
|
||||
$$ = makeTypeCast((Node *)n, SystemTypeName("time"), @1);
|
||||
$$ = makeSQLValueFunction(SVFOP_LOCALTIME, -1, @1);
|
||||
}
|
||||
| LOCALTIME '(' Iconst ')'
|
||||
{
|
||||
/*
|
||||
* Translate as "'now'::text::time(n)".
|
||||
* See comments for CURRENT_DATE.
|
||||
*/
|
||||
Node *n;
|
||||
TypeName *d;
|
||||
n = makeStringConstCast("now", -1, SystemTypeName("text"));
|
||||
d = SystemTypeName("time");
|
||||
d->typmods = list_make1(makeIntConst($3, @3));
|
||||
$$ = makeTypeCast((Node *)n, d, @1);
|
||||
$$ = makeSQLValueFunction(SVFOP_LOCALTIME_N, $3, @1);
|
||||
}
|
||||
| LOCALTIMESTAMP
|
||||
{
|
||||
/*
|
||||
* Translate as "'now'::text::timestamp".
|
||||
* See comments for CURRENT_DATE.
|
||||
*/
|
||||
Node *n;
|
||||
n = makeStringConstCast("now", -1, SystemTypeName("text"));
|
||||
$$ = makeTypeCast(n, SystemTypeName("timestamp"), @1);
|
||||
$$ = makeSQLValueFunction(SVFOP_LOCALTIMESTAMP, -1, @1);
|
||||
}
|
||||
| LOCALTIMESTAMP '(' Iconst ')'
|
||||
{
|
||||
/*
|
||||
* Translate as "'now'::text::timestamp(n)".
|
||||
* See comments for CURRENT_DATE.
|
||||
*/
|
||||
Node *n;
|
||||
TypeName *d;
|
||||
n = makeStringConstCast("now", -1, SystemTypeName("text"));
|
||||
d = SystemTypeName("timestamp");
|
||||
d->typmods = list_make1(makeIntConst($3, @3));
|
||||
$$ = makeTypeCast(n, d, @1);
|
||||
$$ = makeSQLValueFunction(SVFOP_LOCALTIMESTAMP_N, $3, @1);
|
||||
}
|
||||
| CURRENT_ROLE
|
||||
{
|
||||
$$ = (Node *) makeFuncCall(SystemFuncName("current_user"), NIL, @1);
|
||||
$$ = makeSQLValueFunction(SVFOP_CURRENT_ROLE, -1, @1);
|
||||
}
|
||||
| CURRENT_USER
|
||||
{
|
||||
$$ = (Node *) makeFuncCall(SystemFuncName("current_user"), NIL, @1);
|
||||
$$ = makeSQLValueFunction(SVFOP_CURRENT_USER, -1, @1);
|
||||
}
|
||||
| SESSION_USER
|
||||
{
|
||||
$$ = (Node *) makeFuncCall(SystemFuncName("session_user"), NIL, @1);
|
||||
$$ = makeSQLValueFunction(SVFOP_SESSION_USER, -1, @1);
|
||||
}
|
||||
| USER
|
||||
{
|
||||
$$ = (Node *) makeFuncCall(SystemFuncName("current_user"), NIL, @1);
|
||||
$$ = makeSQLValueFunction(SVFOP_USER, -1, @1);
|
||||
}
|
||||
| CURRENT_CATALOG
|
||||
{
|
||||
$$ = (Node *) makeFuncCall(SystemFuncName("current_database"), NIL, @1);
|
||||
$$ = makeSQLValueFunction(SVFOP_CURRENT_CATALOG, -1, @1);
|
||||
}
|
||||
| CURRENT_SCHEMA
|
||||
{
|
||||
$$ = (Node *) makeFuncCall(SystemFuncName("current_schema"), NIL, @1);
|
||||
$$ = makeSQLValueFunction(SVFOP_CURRENT_SCHEMA, -1, @1);
|
||||
}
|
||||
| CAST '(' a_expr AS Typename ')'
|
||||
{ $$ = makeTypeCast($3, $5, @1); }
|
||||
@@ -14710,6 +14632,18 @@ makeAArrayExpr(List *elements, int location)
|
||||
return (Node *) n;
|
||||
}
|
||||
|
||||
static Node *
|
||||
makeSQLValueFunction(SQLValueFunctionOp op, int32 typmod, int location)
|
||||
{
|
||||
SQLValueFunction *svf = makeNode(SQLValueFunction);
|
||||
|
||||
svf->op = op;
|
||||
/* svf->type will be filled during parse analysis */
|
||||
svf->typmod = typmod;
|
||||
svf->location = location;
|
||||
return (Node *) svf;
|
||||
}
|
||||
|
||||
static Node *
|
||||
makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args,
|
||||
int location)
|
||||
|
||||
@@ -34,7 +34,9 @@
|
||||
#include "parser/parse_type.h"
|
||||
#include "parser/parse_agg.h"
|
||||
#include "utils/builtins.h"
|
||||
#include "utils/date.h"
|
||||
#include "utils/lsyscache.h"
|
||||
#include "utils/timestamp.h"
|
||||
#include "utils/xml.h"
|
||||
|
||||
|
||||
@@ -107,6 +109,8 @@ static Node *transformArrayExpr(ParseState *pstate, A_ArrayExpr *a,
|
||||
static Node *transformRowExpr(ParseState *pstate, RowExpr *r);
|
||||
static Node *transformCoalesceExpr(ParseState *pstate, CoalesceExpr *c);
|
||||
static Node *transformMinMaxExpr(ParseState *pstate, MinMaxExpr *m);
|
||||
static Node *transformSQLValueFunction(ParseState *pstate,
|
||||
SQLValueFunction *svf);
|
||||
static Node *transformXmlExpr(ParseState *pstate, XmlExpr *x);
|
||||
static Node *transformXmlSerialize(ParseState *pstate, XmlSerialize *xs);
|
||||
static Node *transformBooleanTest(ParseState *pstate, BooleanTest *b);
|
||||
@@ -306,6 +310,11 @@ transformExprRecurse(ParseState *pstate, Node *expr)
|
||||
result = transformMinMaxExpr(pstate, (MinMaxExpr *) expr);
|
||||
break;
|
||||
|
||||
case T_SQLValueFunction:
|
||||
result = transformSQLValueFunction(pstate,
|
||||
(SQLValueFunction *) expr);
|
||||
break;
|
||||
|
||||
case T_XmlExpr:
|
||||
result = transformXmlExpr(pstate, (XmlExpr *) expr);
|
||||
break;
|
||||
@@ -2178,6 +2187,59 @@ transformMinMaxExpr(ParseState *pstate, MinMaxExpr *m)
|
||||
return (Node *) newm;
|
||||
}
|
||||
|
||||
static Node *
|
||||
transformSQLValueFunction(ParseState *pstate, SQLValueFunction *svf)
|
||||
{
|
||||
/*
|
||||
* All we need to do is insert the correct result type and (where needed)
|
||||
* validate the typmod, so we just modify the node in-place.
|
||||
*/
|
||||
switch (svf->op)
|
||||
{
|
||||
case SVFOP_CURRENT_DATE:
|
||||
svf->type = DATEOID;
|
||||
break;
|
||||
case SVFOP_CURRENT_TIME:
|
||||
svf->type = TIMETZOID;
|
||||
break;
|
||||
case SVFOP_CURRENT_TIME_N:
|
||||
svf->type = TIMETZOID;
|
||||
svf->typmod = anytime_typmod_check(true, svf->typmod);
|
||||
break;
|
||||
case SVFOP_CURRENT_TIMESTAMP:
|
||||
svf->type = TIMESTAMPTZOID;
|
||||
break;
|
||||
case SVFOP_CURRENT_TIMESTAMP_N:
|
||||
svf->type = TIMESTAMPTZOID;
|
||||
svf->typmod = anytimestamp_typmod_check(true, svf->typmod);
|
||||
break;
|
||||
case SVFOP_LOCALTIME:
|
||||
svf->type = TIMEOID;
|
||||
break;
|
||||
case SVFOP_LOCALTIME_N:
|
||||
svf->type = TIMEOID;
|
||||
svf->typmod = anytime_typmod_check(false, svf->typmod);
|
||||
break;
|
||||
case SVFOP_LOCALTIMESTAMP:
|
||||
svf->type = TIMESTAMPOID;
|
||||
break;
|
||||
case SVFOP_LOCALTIMESTAMP_N:
|
||||
svf->type = TIMESTAMPOID;
|
||||
svf->typmod = anytimestamp_typmod_check(false, svf->typmod);
|
||||
break;
|
||||
case SVFOP_CURRENT_ROLE:
|
||||
case SVFOP_CURRENT_USER:
|
||||
case SVFOP_USER:
|
||||
case SVFOP_SESSION_USER:
|
||||
case SVFOP_CURRENT_CATALOG:
|
||||
case SVFOP_CURRENT_SCHEMA:
|
||||
svf->type = NAMEOID;
|
||||
break;
|
||||
}
|
||||
|
||||
return (Node *) svf;
|
||||
}
|
||||
|
||||
static Node *
|
||||
transformXmlExpr(ParseState *pstate, XmlExpr *x)
|
||||
{
|
||||
|
||||
@@ -1761,6 +1761,49 @@ FigureColnameInternal(Node *node, char **name)
|
||||
return 2;
|
||||
}
|
||||
break;
|
||||
case T_SQLValueFunction:
|
||||
/* make these act like a function or variable */
|
||||
switch (((SQLValueFunction *) node)->op)
|
||||
{
|
||||
case SVFOP_CURRENT_DATE:
|
||||
*name = "current_date";
|
||||
return 2;
|
||||
case SVFOP_CURRENT_TIME:
|
||||
case SVFOP_CURRENT_TIME_N:
|
||||
*name = "current_time";
|
||||
return 2;
|
||||
case SVFOP_CURRENT_TIMESTAMP:
|
||||
case SVFOP_CURRENT_TIMESTAMP_N:
|
||||
*name = "current_timestamp";
|
||||
return 2;
|
||||
case SVFOP_LOCALTIME:
|
||||
case SVFOP_LOCALTIME_N:
|
||||
*name = "localtime";
|
||||
return 2;
|
||||
case SVFOP_LOCALTIMESTAMP:
|
||||
case SVFOP_LOCALTIMESTAMP_N:
|
||||
*name = "localtimestamp";
|
||||
return 2;
|
||||
case SVFOP_CURRENT_ROLE:
|
||||
*name = "current_role";
|
||||
return 2;
|
||||
case SVFOP_CURRENT_USER:
|
||||
*name = "current_user";
|
||||
return 2;
|
||||
case SVFOP_USER:
|
||||
*name = "user";
|
||||
return 2;
|
||||
case SVFOP_SESSION_USER:
|
||||
*name = "session_user";
|
||||
return 2;
|
||||
case SVFOP_CURRENT_CATALOG:
|
||||
*name = "current_catalog";
|
||||
return 2;
|
||||
case SVFOP_CURRENT_SCHEMA:
|
||||
*name = "current_schema";
|
||||
return 2;
|
||||
}
|
||||
break;
|
||||
case T_XmlExpr:
|
||||
/* make SQL/XML functions act like a regular function */
|
||||
switch (((XmlExpr *) node)->op)
|
||||
|
||||
Reference in New Issue
Block a user