diff options
23 files changed, 473 insertions, 270 deletions
diff --git a/src/backend/catalog/system_functions.sql b/src/backend/catalog/system_functions.sql index b7c65ea37d7..07c0d89c4f8 100644 --- a/src/backend/catalog/system_functions.sql +++ b/src/backend/catalog/system_functions.sql @@ -601,32 +601,6 @@ LANGUAGE internal STRICT IMMUTABLE PARALLEL SAFE AS 'unicode_is_normalized'; --- Functions with SQL-mandated special syntax and some defaults. -CREATE OR REPLACE FUNCTION - "current_time"(int4 DEFAULT NULL) - RETURNS timetz - LANGUAGE internal - STABLE PARALLEL SAFE -AS 'current_time'; -CREATE OR REPLACE FUNCTION - "current_timestamp"(int4 DEFAULT NULL) - RETURNS timestamptz - LANGUAGE internal - STABLE PARALLEL SAFE - AS 'current_timestamp'; -CREATE OR REPLACE FUNCTION - "localtime"(int4 DEFAULT NULL) - RETURNS time - LANGUAGE internal - STABLE PARALLEL SAFE - AS 'sql_localtime'; -CREATE OR REPLACE FUNCTION - "localtimestamp"(int4 DEFAULT NULL) - RETURNS timestamp - LANGUAGE internal - STABLE PARALLEL SAFE - AS 'sql_localtimestamp'; - -- -- The default permissions for functions mean that anyone can execute them. -- A number of functions shouldn't be executable by just anyone, but rather diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c index dcf56446c74..bf257a41c85 100644 --- a/src/backend/executor/execExpr.c +++ b/src/backend/executor/execExpr.c @@ -2213,6 +2213,17 @@ ExecInitExprRec(Expr *node, ExprState *state, break; } + case T_SQLValueFunction: + { + SQLValueFunction *svf = (SQLValueFunction *) node; + + scratch.opcode = EEOP_SQLVALUEFUNCTION; + scratch.d.sqlvaluefunction.svf = svf; + + ExprEvalPushStep(state, &scratch); + break; + } + case T_XmlExpr: { XmlExpr *xexpr = (XmlExpr *) node; diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index ca44d39100a..7cc443ec520 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -455,6 +455,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull) &&CASE_EEOP_DISTINCT, &&CASE_EEOP_NOT_DISTINCT, &&CASE_EEOP_NULLIF, + &&CASE_EEOP_SQLVALUEFUNCTION, &&CASE_EEOP_CURRENTOFEXPR, &&CASE_EEOP_NEXTVALUEEXPR, &&CASE_EEOP_ARRAYEXPR, @@ -1305,6 +1306,17 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull) EEO_NEXT(); } + EEO_CASE(EEOP_SQLVALUEFUNCTION) + { + /* + * Doesn't seem worthwhile to have an inline implementation + * efficiency-wise. + */ + ExecEvalSQLValueFunction(state, op); + + EEO_NEXT(); + } + EEO_CASE(EEOP_CURRENTOFEXPR) { /* error invocation uses space, and shouldn't ever occur */ @@ -2498,6 +2510,67 @@ ExecEvalParamExtern(ExprState *state, ExprEvalStep *op, ExprContext *econtext) } /* + * Evaluate a SQLValueFunction expression. + */ +void +ExecEvalSQLValueFunction(ExprState *state, ExprEvalStep *op) +{ + LOCAL_FCINFO(fcinfo, 0); + SQLValueFunction *svf = op->d.sqlvaluefunction.svf; + + *op->resnull = false; + + /* + * Note: current_schema() can return NULL. current_user() etc currently + * cannot, but might as well code those cases the same way for safety. + */ + switch (svf->op) + { + case SVFOP_CURRENT_DATE: + *op->resvalue = DateADTGetDatum(GetSQLCurrentDate()); + break; + case SVFOP_CURRENT_TIME: + case SVFOP_CURRENT_TIME_N: + *op->resvalue = TimeTzADTPGetDatum(GetSQLCurrentTime(svf->typmod)); + break; + case SVFOP_CURRENT_TIMESTAMP: + case SVFOP_CURRENT_TIMESTAMP_N: + *op->resvalue = TimestampTzGetDatum(GetSQLCurrentTimestamp(svf->typmod)); + break; + case SVFOP_LOCALTIME: + case SVFOP_LOCALTIME_N: + *op->resvalue = TimeADTGetDatum(GetSQLLocalTime(svf->typmod)); + break; + case SVFOP_LOCALTIMESTAMP: + case SVFOP_LOCALTIMESTAMP_N: + *op->resvalue = TimestampGetDatum(GetSQLLocalTimestamp(svf->typmod)); + break; + case SVFOP_CURRENT_ROLE: + case SVFOP_CURRENT_USER: + case SVFOP_USER: + InitFunctionCallInfoData(*fcinfo, NULL, 0, InvalidOid, NULL, NULL); + *op->resvalue = current_user(fcinfo); + *op->resnull = fcinfo->isnull; + break; + case SVFOP_SESSION_USER: + InitFunctionCallInfoData(*fcinfo, NULL, 0, InvalidOid, NULL, NULL); + *op->resvalue = session_user(fcinfo); + *op->resnull = fcinfo->isnull; + break; + case SVFOP_CURRENT_CATALOG: + InitFunctionCallInfoData(*fcinfo, NULL, 0, InvalidOid, NULL, NULL); + *op->resvalue = current_database(fcinfo); + *op->resnull = fcinfo->isnull; + break; + case SVFOP_CURRENT_SCHEMA: + InitFunctionCallInfoData(*fcinfo, NULL, 0, InvalidOid, NULL, NULL); + *op->resvalue = current_schema(fcinfo); + *op->resnull = fcinfo->isnull; + break; + } +} + +/* * Raise error if a CURRENT OF expression is evaluated. * * The planner should convert CURRENT OF into a TidScan qualification, or some diff --git a/src/backend/jit/llvm/llvmjit_expr.c b/src/backend/jit/llvm/llvmjit_expr.c index 2c3d64ea6e5..774db57ae2e 100644 --- a/src/backend/jit/llvm/llvmjit_expr.c +++ b/src/backend/jit/llvm/llvmjit_expr.c @@ -1549,6 +1549,12 @@ llvm_compile_expr(ExprState *state) break; } + case EEOP_SQLVALUEFUNCTION: + build_EvalXFunc(b, mod, "ExecEvalSQLValueFunction", + v_state, op); + LLVMBuildBr(b, opblocks[opno + 1]); + break; + case EEOP_CURRENTOFEXPR: build_EvalXFunc(b, mod, "ExecEvalCurrentOfExpr", v_state, op); diff --git a/src/backend/jit/llvm/llvmjit_types.c b/src/backend/jit/llvm/llvmjit_types.c index feb8208b797..41ac4c6f45c 100644 --- a/src/backend/jit/llvm/llvmjit_types.c +++ b/src/backend/jit/llvm/llvmjit_types.c @@ -126,6 +126,7 @@ void *referenced_functions[] = ExecEvalRow, ExecEvalRowNotNull, ExecEvalRowNull, + ExecEvalSQLValueFunction, ExecEvalScalarArrayOp, ExecEvalHashedScalarArrayOp, ExecEvalSubPlan, diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c index fe3a113c8fc..0ed8712a631 100644 --- a/src/backend/nodes/nodeFuncs.c +++ b/src/backend/nodes/nodeFuncs.c @@ -210,6 +210,9 @@ exprType(const Node *expr) case T_MinMaxExpr: type = ((const MinMaxExpr *) expr)->minmaxtype; break; + case T_SQLValueFunction: + type = ((const SQLValueFunction *) expr)->type; + break; case T_XmlExpr: if (((const XmlExpr *) expr)->op == IS_DOCUMENT) type = BOOLOID; @@ -486,6 +489,8 @@ exprTypmod(const Node *expr) return typmod; } break; + case T_SQLValueFunction: + return ((const SQLValueFunction *) expr)->typmod; case T_JsonValueExpr: return exprTypmod((Node *) ((const JsonValueExpr *) expr)->formatted_expr); case T_JsonConstructorExpr: @@ -930,6 +935,13 @@ exprCollation(const Node *expr) case T_MinMaxExpr: coll = ((const MinMaxExpr *) expr)->minmaxcollid; break; + case T_SQLValueFunction: + /* Returns either NAME or a non-collatable type */ + if (((const SQLValueFunction *) expr)->type == NAMEOID) + coll = C_COLLATION_OID; + else + coll = InvalidOid; + break; case T_XmlExpr: /* @@ -1167,6 +1179,11 @@ exprSetCollation(Node *expr, Oid collation) case T_MinMaxExpr: ((MinMaxExpr *) expr)->minmaxcollid = collation; break; + case T_SQLValueFunction: + Assert((((SQLValueFunction *) expr)->type == NAMEOID) ? + (collation == C_COLLATION_OID) : + (collation == InvalidOid)); + break; case T_XmlExpr: Assert((((XmlExpr *) expr)->op == IS_XMLSERIALIZE) ? (collation == DEFAULT_COLLATION_OID) : @@ -1468,6 +1485,10 @@ exprLocation(const Node *expr) /* GREATEST/LEAST keyword should always be the first thing */ loc = ((const MinMaxExpr *) expr)->location; break; + case T_SQLValueFunction: + /* function keyword should always be the first thing */ + loc = ((const SQLValueFunction *) expr)->location; + break; case T_XmlExpr: { const XmlExpr *xexpr = (const XmlExpr *) expr; @@ -1789,10 +1810,10 @@ set_sa_opfuncid(ScalarArrayOpExpr *opexpr) * for themselves, in case additional checks should be made, or because they * have special rules about which parts of the tree need to be visited. * - * Note: we ignore MinMaxExpr, XmlExpr, CoerceToDomain, and NextValueExpr - * nodes, because they do not contain SQL function OIDs. However, they can - * invoke SQL-visible functions, so callers should take thought about how - * to treat them. + * Note: we ignore MinMaxExpr, SQLValueFunction, XmlExpr, CoerceToDomain, + * and NextValueExpr nodes, because they do not contain SQL function OIDs. + * However, they can invoke SQL-visible functions, so callers should take + * thought about how to treat them. */ bool check_functions_in_node(Node *node, check_function_callback checker, @@ -2008,6 +2029,7 @@ expression_tree_walker_impl(Node *node, case T_Const: case T_Param: case T_CaseTestExpr: + case T_SQLValueFunction: case T_CoerceToDomainValue: case T_SetToDefault: case T_CurrentOfExpr: @@ -2836,6 +2858,7 @@ expression_tree_mutator_impl(Node *node, break; case T_Param: case T_CaseTestExpr: + case T_SQLValueFunction: case T_JsonFormat: case T_CoerceToDomainValue: case T_SetToDefault: @@ -3797,6 +3820,7 @@ raw_expression_tree_walker_impl(Node *node, case T_JsonFormat: case T_SetToDefault: case T_CurrentOfExpr: + case T_SQLValueFunction: case T_Integer: case T_Float: case T_Boolean: diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c index 0a2562c149a..e60603df814 100644 --- a/src/backend/optimizer/path/costsize.c +++ b/src/backend/optimizer/path/costsize.c @@ -4606,6 +4606,7 @@ cost_qual_eval_walker(Node *node, cost_qual_eval_context *context) } } else if (IsA(node, MinMaxExpr) || + IsA(node, SQLValueFunction) || IsA(node, XmlExpr) || IsA(node, CoerceToDomain) || IsA(node, NextValueExpr)) diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index 11269fee3ed..7f453b04f8b 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -412,6 +412,12 @@ contain_mutable_functions_walker(Node *node, void *context) /* Check all subnodes */ } + if (IsA(node, SQLValueFunction)) + { + /* all variants of SQLValueFunction are stable */ + return true; + } + if (IsA(node, NextValueExpr)) { /* NextValueExpr is volatile */ @@ -560,8 +566,8 @@ contain_volatile_functions_walker(Node *node, void *context) /* * See notes in contain_mutable_functions_walker about why we treat - * MinMaxExpr, XmlExpr, and CoerceToDomain as immutable. Hence, none of - * them are of interest here. + * MinMaxExpr, XmlExpr, and CoerceToDomain as immutable, while + * SQLValueFunction is stable. Hence, none of them are of interest here. */ /* Recurse to check arguments */ @@ -606,9 +612,10 @@ contain_volatile_functions_not_nextval_walker(Node *node, void *context) /* * See notes in contain_mutable_functions_walker about why we treat - * MinMaxExpr, XmlExpr, and CoerceToDomain as immutable. Hence, none of - * them are of interest here. Also, since we're intentionally ignoring - * nextval(), presumably we should ignore NextValueExpr. + * MinMaxExpr, XmlExpr, and CoerceToDomain as immutable, while + * SQLValueFunction is stable. Hence, none of them are of interest here. + * Also, since we're intentionally ignoring nextval(), presumably we + * should ignore NextValueExpr. */ /* Recurse to check arguments */ @@ -754,8 +761,8 @@ max_parallel_hazard_walker(Node *node, max_parallel_hazard_context *context) * (Note: in principle that's wrong because a domain constraint could * contain a parallel-unsafe function; but useful constraints probably * never would have such, and assuming they do would cripple use of - * parallel query in the presence of domain types.) NextValueExpr is - * parallel-unsafe. + * parallel query in the presence of domain types.) SQLValueFunction + * should be safe in all cases. NextValueExpr is parallel-unsafe. */ if (IsA(node, CoerceToDomain)) { @@ -1202,6 +1209,7 @@ contain_leaked_vars_walker(Node *node, void *context) case T_CaseExpr: case T_CaseTestExpr: case T_RowExpr: + case T_SQLValueFunction: case T_NullTest: case T_BooleanTest: case T_NextValueExpr: @@ -3243,6 +3251,23 @@ eval_const_expressions_mutator(Node *node, newcoalesce->location = coalesceexpr->location; return (Node *) newcoalesce; } + case T_SQLValueFunction: + { + /* + * All variants of SQLValueFunction are stable, so if we are + * estimating the expression's value, we should evaluate the + * current function value. Otherwise just copy. + */ + SQLValueFunction *svf = (SQLValueFunction *) node; + + if (context->estimate) + return (Node *) evaluate_expr((Expr *) svf, + svf->type, + svf->typmod, + InvalidOid); + else + return copyObject((Node *) svf); + } case T_FieldSelect: { /* diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index d6426f3b8e0..6f5aa8a3cbc 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -198,6 +198,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); @@ -15288,87 +15290,51 @@ func_expr_common_subexpr: } | CURRENT_DATE { - $$ = (Node *) makeFuncCall(SystemFuncName("current_date"), - NIL, - COERCE_SQL_SYNTAX, - @1); + $$ = makeSQLValueFunction(SVFOP_CURRENT_DATE, -1, @1); } | CURRENT_TIME { - $$ = (Node *) makeFuncCall(SystemFuncName("current_time"), - NIL, - COERCE_SQL_SYNTAX, - @1); + $$ = makeSQLValueFunction(SVFOP_CURRENT_TIME, -1, @1); } | CURRENT_TIME '(' Iconst ')' { - $$ = (Node *) makeFuncCall(SystemFuncName("current_time"), - list_make1(makeIntConst($3, @3)), - COERCE_SQL_SYNTAX, - @1); + $$ = makeSQLValueFunction(SVFOP_CURRENT_TIME_N, $3, @1); } | CURRENT_TIMESTAMP { - $$ = (Node *) makeFuncCall(SystemFuncName("current_timestamp"), - NIL, - COERCE_SQL_SYNTAX, - @1); + $$ = makeSQLValueFunction(SVFOP_CURRENT_TIMESTAMP, -1, @1); } | CURRENT_TIMESTAMP '(' Iconst ')' { - $$ = (Node *) makeFuncCall(SystemFuncName("current_timestamp"), - list_make1(makeIntConst($3, @3)), - COERCE_SQL_SYNTAX, - @1); + $$ = makeSQLValueFunction(SVFOP_CURRENT_TIMESTAMP_N, $3, @1); } | LOCALTIME { - $$ = (Node *) makeFuncCall(SystemFuncName("localtime"), - NIL, - COERCE_SQL_SYNTAX, - @1); + $$ = makeSQLValueFunction(SVFOP_LOCALTIME, -1, @1); } | LOCALTIME '(' Iconst ')' { - $$ = (Node *) makeFuncCall(SystemFuncName("localtime"), - list_make1(makeIntConst($3, @3)), - COERCE_SQL_SYNTAX, - @1); + $$ = makeSQLValueFunction(SVFOP_LOCALTIME_N, $3, @1); } | LOCALTIMESTAMP { - $$ = (Node *) makeFuncCall(SystemFuncName("localtimestamp"), - NIL, - COERCE_SQL_SYNTAX, - @1); + $$ = makeSQLValueFunction(SVFOP_LOCALTIMESTAMP, -1, @1); } | LOCALTIMESTAMP '(' Iconst ')' { - $$ = (Node *) makeFuncCall(SystemFuncName("localtimestamp"), - list_make1(makeIntConst($3, @3)), - COERCE_SQL_SYNTAX, - @1); + $$ = makeSQLValueFunction(SVFOP_LOCALTIMESTAMP_N, $3, @1); } | CURRENT_ROLE { - $$ = (Node *) makeFuncCall(SystemFuncName("current_role"), - NIL, - COERCE_SQL_SYNTAX, - @1); + $$ = makeSQLValueFunction(SVFOP_CURRENT_ROLE, -1, @1); } | CURRENT_USER { - $$ = (Node *) makeFuncCall(SystemFuncName("current_user"), - NIL, - COERCE_SQL_SYNTAX, - @1); + $$ = makeSQLValueFunction(SVFOP_CURRENT_USER, -1, @1); } | SESSION_USER { - $$ = (Node *) makeFuncCall(SystemFuncName("session_user"), - NIL, - COERCE_SQL_SYNTAX, - @1); + $$ = makeSQLValueFunction(SVFOP_SESSION_USER, -1, @1); } | SYSTEM_USER { @@ -15379,24 +15345,15 @@ func_expr_common_subexpr: } | USER { - $$ = (Node *) makeFuncCall(SystemFuncName("user"), - NIL, - COERCE_SQL_SYNTAX, - @1); + $$ = makeSQLValueFunction(SVFOP_USER, -1, @1); } | CURRENT_CATALOG { - $$ = (Node *) makeFuncCall(SystemFuncName("current_catalog"), - NIL, - COERCE_SQL_SYNTAX, - @1); + $$ = makeSQLValueFunction(SVFOP_CURRENT_CATALOG, -1, @1); } | CURRENT_SCHEMA { - $$ = (Node *) makeFuncCall(SystemFuncName("current_schema"), - NIL, - COERCE_SQL_SYNTAX, - @1); + $$ = makeSQLValueFunction(SVFOP_CURRENT_SCHEMA, -1, @1); } | CAST '(' a_expr AS Typename ')' { $$ = makeTypeCast($3, $5, @1); } @@ -18520,6 +18477,18 @@ makeAArrayExpr(List *elements, int location) } 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) { diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index 64356436ef7..0b3632735bf 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -64,6 +64,8 @@ static Node *transformArrayExpr(ParseState *pstate, A_ArrayExpr *a, static Node *transformRowExpr(ParseState *pstate, RowExpr *r, bool allowDefault); 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); @@ -250,6 +252,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; @@ -2221,6 +2228,59 @@ transformMinMaxExpr(ParseState *pstate, MinMaxExpr *m) } 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) { XmlExpr *newx; diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index e77b542fd76..4cca97ff9c1 100644 --- a/src/backend/parser/parse_target.c +++ b/src/backend/parser/parse_target.c @@ -1876,6 +1876,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) diff --git a/src/backend/utils/adt/date.c b/src/backend/utils/adt/date.c index a163fbb4ab5..ae0f24de2c3 100644 --- a/src/backend/utils/adt/date.c +++ b/src/backend/utils/adt/date.c @@ -46,6 +46,27 @@ /* common code for timetypmodin and timetztypmodin */ static int32 +anytime_typmodin(bool istz, ArrayType *ta) +{ + int32 *tl; + int n; + + tl = ArrayGetIntegerTypmods(ta, &n); + + /* + * we're not too tense about good error message here because grammar + * shouldn't allow wrong number of modifiers for TIME + */ + if (n != 1) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid type modifier"))); + + return anytime_typmod_check(istz, tl[0]); +} + +/* exported so parse_expr.c can use it */ +int32 anytime_typmod_check(bool istz, int32 typmod) { if (typmod < 0) @@ -66,26 +87,6 @@ anytime_typmod_check(bool istz, int32 typmod) return typmod; } -static int32 -anytime_typmodin(bool istz, ArrayType *ta) -{ - int32 *tl; - int n; - - tl = ArrayGetIntegerTypmods(ta, &n); - - /* - * we're not too tense about good error message here because grammar - * shouldn't allow wrong number of modifiers for TIME - */ - if (n != 1) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("invalid type modifier"))); - - return anytime_typmod_check(istz, tl[0]); -} - /* common code for timetypmodout and timetztypmodout */ static char * anytime_typmodout(bool istz, int32 typmod) @@ -301,10 +302,10 @@ EncodeSpecialDate(DateADT dt, char *str) /* - * current_date -- implements CURRENT_DATE + * GetSQLCurrentDate -- implements CURRENT_DATE */ -Datum -current_date(PG_FUNCTION_ARGS) +DateADT +GetSQLCurrentDate(void) { struct pg_tm tm; @@ -330,56 +331,46 @@ current_date(PG_FUNCTION_ARGS) cache_mday = tm.tm_mday; } - return DateADTGetDatum(cache_date); + return cache_date; } /* - * current_time -- implements CURRENT_TIME, CURRENT_TIME(n) + * GetSQLCurrentTime -- implements CURRENT_TIME, CURRENT_TIME(n) */ -Datum -current_time(PG_FUNCTION_ARGS) +TimeTzADT * +GetSQLCurrentTime(int32 typmod) { TimeTzADT *result; struct pg_tm tt, *tm = &tt; fsec_t fsec; int tz; - int32 typmod = -1; - - if (!PG_ARGISNULL(0)) - typmod = anytime_typmod_check(true, PG_GETARG_INT32(0)); GetCurrentTimeUsec(tm, &fsec, &tz); result = (TimeTzADT *) palloc(sizeof(TimeTzADT)); tm2timetz(tm, fsec, tz, result); AdjustTimeForTypmod(&(result->time), typmod); - - return TimeTzADTPGetDatum(result); + return result; } /* - * sql_localtime -- implements LOCALTIME, LOCALTIME(n) + * GetSQLLocalTime -- implements LOCALTIME, LOCALTIME(n) */ -Datum -sql_localtime(PG_FUNCTION_ARGS) +TimeADT +GetSQLLocalTime(int32 typmod) { TimeADT result; struct pg_tm tt, *tm = &tt; fsec_t fsec; int tz; - int32 typmod = -1; - - if (!PG_ARGISNULL(0)) - typmod = anytime_typmod_check(false, PG_GETARG_INT32(0)); GetCurrentTimeUsec(tm, &fsec, &tz); tm2time(tm, fsec, &result); AdjustTimeForTypmod(&result, typmod); - - return TimeADTGetDatum(result); + return result; } diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 60f9d08d5dd..e93d66a7ec5 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -8254,6 +8254,7 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags) case T_RowExpr: case T_CoalesceExpr: case T_MinMaxExpr: + case T_SQLValueFunction: case T_XmlExpr: case T_NextValueExpr: case T_NullIfExpr: @@ -9242,6 +9243,67 @@ get_rule_expr(Node *node, deparse_context *context, } break; + case T_SQLValueFunction: + { + SQLValueFunction *svf = (SQLValueFunction *) node; + + /* + * Note: this code knows that typmod for time, timestamp, and + * timestamptz just prints as integer. + */ + switch (svf->op) + { + case SVFOP_CURRENT_DATE: + appendStringInfoString(buf, "CURRENT_DATE"); + break; + case SVFOP_CURRENT_TIME: + appendStringInfoString(buf, "CURRENT_TIME"); + break; + case SVFOP_CURRENT_TIME_N: + appendStringInfo(buf, "CURRENT_TIME(%d)", svf->typmod); + break; + case SVFOP_CURRENT_TIMESTAMP: + appendStringInfoString(buf, "CURRENT_TIMESTAMP"); + break; + case SVFOP_CURRENT_TIMESTAMP_N: + appendStringInfo(buf, "CURRENT_TIMESTAMP(%d)", + svf->typmod); + break; + case SVFOP_LOCALTIME: + appendStringInfoString(buf, "LOCALTIME"); + break; + case SVFOP_LOCALTIME_N: + appendStringInfo(buf, "LOCALTIME(%d)", svf->typmod); + break; + case SVFOP_LOCALTIMESTAMP: + appendStringInfoString(buf, "LOCALTIMESTAMP"); + break; + case SVFOP_LOCALTIMESTAMP_N: + appendStringInfo(buf, "LOCALTIMESTAMP(%d)", + svf->typmod); + break; + case SVFOP_CURRENT_ROLE: + appendStringInfoString(buf, "CURRENT_ROLE"); + break; + case SVFOP_CURRENT_USER: + appendStringInfoString(buf, "CURRENT_USER"); + break; + case SVFOP_USER: + appendStringInfoString(buf, "USER"); + break; + case SVFOP_SESSION_USER: + appendStringInfoString(buf, "SESSION_USER"); + break; + case SVFOP_CURRENT_CATALOG: + appendStringInfoString(buf, "CURRENT_CATALOG"); + break; + case SVFOP_CURRENT_SCHEMA: + appendStringInfoString(buf, "CURRENT_SCHEMA"); + break; + } + } + break; + case T_XmlExpr: { XmlExpr *xexpr = (XmlExpr *) node; @@ -9816,6 +9878,7 @@ looks_like_function(Node *node) case T_NullIfExpr: case T_CoalesceExpr: case T_MinMaxExpr: + case T_SQLValueFunction: case T_XmlExpr: /* these are all accepted by func_expr_common_subexpr */ return true; @@ -10218,33 +10281,6 @@ get_windowfunc_expr_helper(WindowFunc *wfunc, deparse_context *context, } /* - * get_func_sql_syntax_time - * - * Parse back argument of SQL-syntax function call related to a time or a - * timestamp. These require a specific handling when their typmod is given - * by the function caller through their SQL keyword. - */ -static void -get_func_sql_syntax_time(List *args, deparse_context *context) -{ - StringInfo buf = context->buf; - Const *cons; - - if (list_length(args) != 1) - return; - - cons = (Const *) linitial(args); - Assert(IsA(cons, Const)); - - if (!cons->constisnull) - { - appendStringInfoString(buf, "("); - get_rule_expr((Node *) cons, context, false); - appendStringInfoString(buf, ")"); - } -} - -/* * get_func_sql_syntax - Parse back a SQL-syntax function call * * Returns true if we successfully deparsed, false if we did not @@ -10470,48 +10506,10 @@ get_func_sql_syntax(FuncExpr *expr, deparse_context *context) appendStringInfoChar(buf, ')'); return true; - case F_CURRENT_CATALOG: - appendStringInfoString(buf, "CURRENT_CATALOG"); - return true; - case F_CURRENT_ROLE: - appendStringInfoString(buf, "CURRENT_ROLE"); - return true; - case F_CURRENT_SCHEMA: - appendStringInfoString(buf, "CURRENT_SCHEMA"); - return true; - case F_CURRENT_USER: - appendStringInfoString(buf, "CURRENT_USER"); - return true; - case F_USER: - appendStringInfoString(buf, "USER"); - return true; - case F_SESSION_USER: - appendStringInfoString(buf, "SESSION_USER"); - return true; case F_SYSTEM_USER: appendStringInfoString(buf, "SYSTEM_USER"); return true; - case F_CURRENT_DATE: - appendStringInfoString(buf, "CURRENT_DATE"); - return true; - case F_CURRENT_TIME: - appendStringInfoString(buf, "CURRENT_TIME"); - get_func_sql_syntax_time(expr->args, context); - return true; - case F_CURRENT_TIMESTAMP: - appendStringInfoString(buf, "CURRENT_TIMESTAMP"); - get_func_sql_syntax_time(expr->args, context); - return true; - case F_LOCALTIME: - appendStringInfoString(buf, "LOCALTIME"); - get_func_sql_syntax_time(expr->args, context); - return true; - case F_LOCALTIMESTAMP: - appendStringInfoString(buf, "LOCALTIMESTAMP"); - get_func_sql_syntax_time(expr->args, context); - return true; - case F_XMLEXISTS: /* XMLEXISTS ... extra parens because args are c_expr */ appendStringInfoString(buf, "XMLEXISTS(("); diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c index aaadc68ae6a..0e50aaec5a5 100644 --- a/src/backend/utils/adt/timestamp.c +++ b/src/backend/utils/adt/timestamp.c @@ -83,6 +83,27 @@ static Timestamp timestamptz2timestamp(TimestampTz timestamp); /* common code for timestamptypmodin and timestamptztypmodin */ static int32 +anytimestamp_typmodin(bool istz, ArrayType *ta) +{ + int32 *tl; + int n; + + tl = ArrayGetIntegerTypmods(ta, &n); + + /* + * we're not too tense about good error message here because grammar + * shouldn't allow wrong number of modifiers for TIMESTAMP + */ + if (n != 1) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid type modifier"))); + + return anytimestamp_typmod_check(istz, tl[0]); +} + +/* exported so parse_expr.c can use it */ +int32 anytimestamp_typmod_check(bool istz, int32 typmod) { if (typmod < 0) @@ -103,26 +124,6 @@ anytimestamp_typmod_check(bool istz, int32 typmod) return typmod; } -static int32 -anytimestamp_typmodin(bool istz, ArrayType *ta) -{ - int32 *tl; - int n; - - tl = ArrayGetIntegerTypmods(ta, &n); - - /* - * we're not too tense about good error message here because grammar - * shouldn't allow wrong number of modifiers for TIMESTAMP - */ - if (n != 1) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("invalid type modifier"))); - - return anytimestamp_typmod_check(istz, tl[0]); -} - /* common code for timestamptypmodout and timestamptztypmodout */ static char * anytimestamp_typmodout(bool istz, int32 typmod) @@ -1594,42 +1595,33 @@ GetCurrentTimestamp(void) } /* - * current_timestamp -- implements CURRENT_TIMESTAMP, CURRENT_TIMESTAMP(n) + * GetSQLCurrentTimestamp -- implements CURRENT_TIMESTAMP, CURRENT_TIMESTAMP(n) */ -Datum -current_timestamp(PG_FUNCTION_ARGS) +TimestampTz +GetSQLCurrentTimestamp(int32 typmod) { TimestampTz ts; - int32 typmod = -1; - - if (!PG_ARGISNULL(0)) - typmod = anytimestamp_typmod_check(true, PG_GETARG_INT32(0)); ts = GetCurrentTransactionStartTimestamp(); if (typmod >= 0) AdjustTimestampForTypmod(&ts, typmod, NULL); - return TimestampTzGetDatum(ts); + return ts; } /* - * sql_localtimestamp -- implements LOCALTIMESTAMP, LOCALTIMESTAMP(n) + * GetSQLLocalTimestamp -- implements LOCALTIMESTAMP, LOCALTIMESTAMP(n) */ -Datum -sql_localtimestamp(PG_FUNCTION_ARGS) +Timestamp +GetSQLLocalTimestamp(int32 typmod) { Timestamp ts; - int32 typmod = -1; - - if (!PG_ARGISNULL(0)) - typmod = anytimestamp_typmod_check(false, PG_GETARG_INT32(0)); ts = timestamptz2timestamp(GetCurrentTransactionStartTimestamp()); if (typmod >= 0) AdjustTimestampForTypmod(&ts, typmod, NULL); - return TimestampGetDatum(ts); + return ts; } - /* * timeofday(*) -- returns the current time as a text. */ diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index d10cc28b0c1..e617381bf40 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -57,6 +57,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 202305121 +#define CATALOG_VERSION_NO 202305171 #endif diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index b2bc81b15f8..d7a60b39b58 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -1505,38 +1505,12 @@ { oid => '745', descr => 'current user name', proname => 'current_user', provolatile => 's', prorettype => 'name', proargtypes => '', prosrc => 'current_user' }, -{ oid => '9695', descr => 'current role name', - proname => 'current_role', provolatile => 's', prorettype => 'name', - proargtypes => '', prosrc => 'current_user' }, -{ oid => '9696', descr => 'user name', - proname => 'user', provolatile => 's', prorettype => 'name', - proargtypes => '', prosrc => 'current_user' }, -{ oid => '9697', descr => 'name of the current database', - proname => 'current_catalog', provolatile => 's', prorettype => 'name', - proargtypes => '', prosrc => 'current_database' }, { oid => '746', descr => 'session user name', proname => 'session_user', provolatile => 's', prorettype => 'name', proargtypes => '', prosrc => 'session_user' }, { oid => '9977', descr => 'system user name', proname => 'system_user', provolatile => 's', prorettype => 'text', proargtypes => '', prosrc => 'system_user' }, -{ oid => '9978', descr => 'current date', - proname => 'current_date', provolatile => 's', prorettype => 'date', - proargtypes => '', prosrc => 'current_date' }, -{ oid => '9979', descr => 'current time', - proname => 'current_time', proisstrict => 'f', provolatile => 's', - prorettype => 'timetz', proargtypes => 'int4', prosrc => 'current_time' }, -{ oid => '9980', descr => 'current timestamp', - proname => 'current_timestamp', proisstrict => 'f', provolatile => 's', - prorettype => 'timestamptz', proargtypes => 'int4', - prosrc => 'current_timestamp' }, -{ oid => '9981', descr => 'local time', - proname => 'localtime', proisstrict => 'f', provolatile => 's', - prorettype => 'time', proargtypes => 'int4', prosrc => 'sql_localtime' }, -{ oid => '9982', descr => 'local timestamp', - proname => 'localtimestamp', proisstrict => 'f', provolatile => 's', - prorettype => 'timestamp', proargtypes => 'int4', - prosrc => 'sql_localtimestamp' }, { oid => '744', proname => 'array_eq', prorettype => 'bool', diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h index 157b0d85f29..048573c2bcb 100644 --- a/src/include/executor/execExpr.h +++ b/src/include/executor/execExpr.h @@ -171,6 +171,7 @@ typedef enum ExprEvalOp EEOP_DISTINCT, EEOP_NOT_DISTINCT, EEOP_NULLIF, + EEOP_SQLVALUEFUNCTION, EEOP_CURRENTOFEXPR, EEOP_NEXTVALUEEXPR, EEOP_ARRAYEXPR, @@ -418,6 +419,12 @@ typedef struct ExprEvalStep FunctionCallInfo fcinfo_data_in; } iocoerce; + /* for EEOP_SQLVALUEFUNCTION */ + struct + { + SQLValueFunction *svf; + } sqlvaluefunction; + /* for EEOP_NEXTVALUEEXPR */ struct { @@ -769,6 +776,7 @@ extern void ExecEvalParamExec(ExprState *state, ExprEvalStep *op, ExprContext *econtext); extern void ExecEvalParamExtern(ExprState *state, ExprEvalStep *op, ExprContext *econtext); +extern void ExecEvalSQLValueFunction(ExprState *state, ExprEvalStep *op); extern void ExecEvalCurrentOfExpr(ExprState *state, ExprEvalStep *op); extern void ExecEvalNextValueExpr(ExprState *state, ExprEvalStep *op); extern void ExecEvalRowNull(ExprState *state, ExprEvalStep *op, diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h index be9c29f0bfb..08e7dae73f1 100644 --- a/src/include/nodes/primnodes.h +++ b/src/include/nodes/primnodes.h @@ -1446,6 +1446,49 @@ typedef struct MinMaxExpr } MinMaxExpr; /* + * SQLValueFunction - parameterless functions with special grammar productions + * + * The SQL standard categorizes some of these as <datetime value function> + * and others as <general value specification>. We call 'em SQLValueFunctions + * for lack of a better term. We store type and typmod of the result so that + * some code doesn't need to know each function individually, and because + * we would need to store typmod anyway for some of the datetime functions. + * Note that currently, all variants return non-collating datatypes, so we do + * not need a collation field; also, all these functions are stable. + */ +typedef enum SQLValueFunctionOp +{ + SVFOP_CURRENT_DATE, + SVFOP_CURRENT_TIME, + SVFOP_CURRENT_TIME_N, + SVFOP_CURRENT_TIMESTAMP, + SVFOP_CURRENT_TIMESTAMP_N, + SVFOP_LOCALTIME, + SVFOP_LOCALTIME_N, + SVFOP_LOCALTIMESTAMP, + SVFOP_LOCALTIMESTAMP_N, + SVFOP_CURRENT_ROLE, + SVFOP_CURRENT_USER, + SVFOP_USER, + SVFOP_SESSION_USER, + SVFOP_CURRENT_CATALOG, + SVFOP_CURRENT_SCHEMA +} SQLValueFunctionOp; + +typedef struct SQLValueFunction +{ + Expr xpr; + SQLValueFunctionOp op; /* which function this is */ + /* + * Result type/typmod. Type is fully determined by "op", so no need to + * include this Oid in the query jumbling. + */ + Oid type pg_node_attr(query_jumble_ignore); + int32 typmod; + int location; /* token location, or -1 if unknown */ +} SQLValueFunction; + +/* * XmlExpr - various SQL/XML functions requiring special grammar productions * * 'name' carries the "NAME foo" argument (already XML-escaped). diff --git a/src/include/utils/date.h b/src/include/utils/date.h index 5fd886b3dba..97e1a021217 100644 --- a/src/include/utils/date.h +++ b/src/include/utils/date.h @@ -96,6 +96,7 @@ TimeTzADTPGetDatum(const TimeTzADT *X) /* date.c */ +extern int32 anytime_typmod_check(bool istz, int32 typmod); extern double date2timestamp_no_overflow(DateADT dateVal); extern Timestamp date2timestamp_opt_overflow(DateADT dateVal, int *overflow); extern TimestampTz date2timestamptz_opt_overflow(DateADT dateVal, int *overflow); @@ -103,6 +104,9 @@ extern int32 date_cmp_timestamp_internal(DateADT dateVal, Timestamp dt2); extern int32 date_cmp_timestamptz_internal(DateADT dateVal, TimestampTz dt2); extern void EncodeSpecialDate(DateADT dt, char *str); +extern DateADT GetSQLCurrentDate(void); +extern TimeTzADT *GetSQLCurrentTime(int32 typmod); +extern TimeADT GetSQLLocalTime(int32 typmod); extern int time2tm(TimeADT time, struct pg_tm *tm, fsec_t *fsec); extern int timetz2tm(TimeTzADT *time, struct pg_tm *tm, fsec_t *fsec, int *tzp); extern int tm2time(struct pg_tm *tm, fsec_t fsec, TimeADT *result); diff --git a/src/include/utils/timestamp.h b/src/include/utils/timestamp.h index edd59dc4322..c4dd96c8c97 100644 --- a/src/include/utils/timestamp.h +++ b/src/include/utils/timestamp.h @@ -95,7 +95,11 @@ extern PGDLLIMPORT TimestampTz PgReloadTime; /* Internal routines (not fmgr-callable) */ +extern int32 anytimestamp_typmod_check(bool istz, int32 typmod); + extern TimestampTz GetCurrentTimestamp(void); +extern TimestampTz GetSQLCurrentTimestamp(int32 typmod); +extern Timestamp GetSQLLocalTimestamp(int32 typmod); extern void TimestampDifference(TimestampTz start_time, TimestampTz stop_time, long *secs, int *microsecs); extern long TimestampDifferenceMilliseconds(TimestampTz start_time, diff --git a/src/test/regress/expected/expressions.out b/src/test/regress/expected/expressions.out index d2c6db1bd50..caeeb196740 100644 --- a/src/test/regress/expected/expressions.out +++ b/src/test/regress/expected/expressions.out @@ -2,7 +2,7 @@ -- expression evaluation tests that don't fit into a more specific file -- -- --- Tests for various FuncCalls with COERCE_SQL_SYNTAX. +-- Tests for SQLValueFunction -- -- current_date (always matches because of transactional behaviour) SELECT date(now())::text = current_date::text; diff --git a/src/test/regress/sql/expressions.sql b/src/test/regress/sql/expressions.sql index d315ef5af50..e02c21f3368 100644 --- a/src/test/regress/sql/expressions.sql +++ b/src/test/regress/sql/expressions.sql @@ -3,7 +3,7 @@ -- -- --- Tests for various FuncCalls with COERCE_SQL_SYNTAX. +-- Tests for SQLValueFunction -- diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index b4058b88c3e..06da7cd4284 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -2425,6 +2425,8 @@ SQLFunctionCache SQLFunctionCachePtr SQLFunctionParseInfo SQLFunctionParseInfoPtr +SQLValueFunction +SQLValueFunctionOp SSL SSLExtensionInfoContext SSL_CTX |