Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions contrib/ivorysql_ora/src/merge/ora_merge.c
Original file line number Diff line number Diff line change
Expand Up @@ -698,6 +698,7 @@ IvytransformMergeStmt(ParseState *pstate, MergeStmt *stmt)

qry->cteList = transformWithClause(pstate, stmt->withClause);
qry->hasModifyingCTE = pstate->p_hasModifyingCTE;
qry->withFuncDefs = stmt->withClause->plsql_defs;
}

/*
Expand Down
148 changes: 148 additions & 0 deletions src/backend/commands/explain.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@
#include "nodes/extensible.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "oracle_parser/ora_with_function.h"
#include "parser/analyze.h"
#include "parser/parse_type.h"
#include "parser/parsetree.h"
#include "rewrite/rewriteHandler.h"
#include "storage/bufmgr.h"
Expand Down Expand Up @@ -67,6 +69,7 @@ explain_per_node_hook_type explain_per_node_hook = NULL;
static void ExplainOneQuery(Query *query, int cursorOptions,
IntoClause *into, ExplainState *es,
ParseState *pstate, ParamListInfo params);
static void ExplainPrintWithFunctions(ExplainState *es, PlannedStmt *pstmt);
static void ExplainPrintJIT(ExplainState *es, int jit_flags,
JitInstrumentation *ji);
static void ExplainPrintSerialize(ExplainState *es,
Expand Down Expand Up @@ -747,6 +750,148 @@ ExplainPrintSettings(ExplainState *es)
}
}

/*
* ExplainPrintWithFunctions -
* Append WITH FUNCTION / WITH PROCEDURE definitions to the EXPLAIN output.
*
* Only called (and meaningful) when the planned statement originated from an
* Oracle-mode query with inline subprogram definitions. Each function is
* shown as "FUNCTION name(mode argname type, ...) RETURN rettype" (or
* "PROCEDURE name(...)" for procedures).
*/
static void
ExplainPrintWithFunctions(ExplainState *es, PlannedStmt *pstmt)
{
ListCell *lc;

if (pstmt->withFuncDefs == NIL)
return;

if (es->format != EXPLAIN_FORMAT_TEXT)
{
ExplainOpenGroup("WITH Functions", "WITH Functions", false, es);

foreach(lc, pstmt->withFuncDefs)
{
InlineFunctionDef *ifd = (InlineFunctionDef *) lfirst(lc);
StringInfoData sig;
ListCell *alc;
bool first_arg = true;

initStringInfo(&sig);

ExplainOpenGroup("WITH Function", NULL, true, es);

ExplainPropertyText("Name", ifd->funcname, es);
ExplainPropertyText("Kind", ifd->is_proc ? "procedure" : "function", es);

/* Build signature string */
appendStringInfo(&sig, "%s(", ifd->funcname);
foreach(alc, ifd->args)
{
FunctionParameter *fp = (FunctionParameter *) lfirst(alc);

if (!first_arg)
appendStringInfoString(&sig, ", ");
first_arg = false;

switch (fp->mode)
{
case FUNC_PARAM_OUT:
appendStringInfoString(&sig, "OUT ");
break;
case FUNC_PARAM_INOUT:
appendStringInfoString(&sig, "IN OUT ");
break;
default:
break;
}

if (fp->name)
appendStringInfo(&sig, "%s ", fp->name);
appendStringInfoString(&sig, TypeNameToString(fp->argType));
}
appendStringInfoChar(&sig, ')');
if (!ifd->is_proc && ifd->rettype != NULL)
appendStringInfo(&sig, " RETURN %s",
TypeNameToString(ifd->rettype));

ExplainPropertyText("Signature", sig.data, es);

if (es->verbose && ifd->src)
ExplainPropertyText("Body", ifd->src, es);

pfree(sig.data);

ExplainCloseGroup("WITH Function", NULL, true, es);
}

ExplainCloseGroup("WITH Functions", "WITH Functions", false, es);
}
else
{
foreach(lc, pstmt->withFuncDefs)
{
InlineFunctionDef *ifd = (InlineFunctionDef *) lfirst(lc);
StringInfoData sig;
ListCell *alc;
bool first_arg = true;

initStringInfo(&sig);

appendStringInfo(&sig, "WITH %s: %s(",
ifd->is_proc ? "Procedure" : "Function",
ifd->funcname);

foreach(alc, ifd->args)
{
FunctionParameter *fp = (FunctionParameter *) lfirst(alc);

if (!first_arg)
appendStringInfoString(&sig, ", ");
first_arg = false;

switch (fp->mode)
{
case FUNC_PARAM_OUT:
appendStringInfoString(&sig, "OUT ");
break;
case FUNC_PARAM_INOUT:
appendStringInfoString(&sig, "IN OUT ");
break;
default:
break;
}

if (fp->name)
appendStringInfo(&sig, "%s ", fp->name);
appendStringInfoString(&sig, TypeNameToString(fp->argType));
}
appendStringInfoChar(&sig, ')');
if (!ifd->is_proc && ifd->rettype != NULL)
appendStringInfo(&sig, " RETURN %s",
TypeNameToString(ifd->rettype));

ExplainIndentText(es);
appendStringInfo(es->str, "%s\n", sig.data);

/*
* In VERBOSE mode also show the function body, mirroring the
* "Body" property emitted by the non-TEXT branch above.
*/
if (es->verbose && ifd->src)
{
es->indent++;
ExplainIndentText(es);
appendStringInfo(es->str, "Body: %s\n", ifd->src);
es->indent--;
}

pfree(sig.data);
}
}
}

/*
* ExplainPrintPlan -
* convert a QueryDesc's plan tree to text and append it to es->str
Expand Down Expand Up @@ -803,6 +948,9 @@ ExplainPrintPlan(ExplainState *es, QueryDesc *queryDesc)
}
ExplainNode(ps, NIL, NULL, NULL, es);

/* Show WITH FUNCTION / WITH PROCEDURE definitions if any */
ExplainPrintWithFunctions(es, queryDesc->plannedstmt);

/*
* If requested, include information about GUC parameters with values that
* don't match the built-in defaults.
Expand Down
46 changes: 42 additions & 4 deletions src/backend/executor/execExpr.c
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
#include "utils/syscache.h"
#include "catalog/pg_proc.h"
#include "parser/parse_param.h"
#include "oracle_parser/ora_with_function.h"



Expand Down Expand Up @@ -2774,16 +2775,19 @@ ExecInitFunc(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid,
int argno;
ListCell *lc;
bool is_subproc_func = false;
bool is_with_func = false;

if (nodeTag(node) == T_FuncExpr)
{
FuncExpr *funcexpr = (FuncExpr *) node;

if (!FUNC_EXPR_FROM_PG_PROC(funcexpr->function_from))
if (funcexpr->function_from == FUNC_FROM_WITH_CLAUSE)
is_with_func = true;
else if (!FUNC_EXPR_FROM_PG_PROC(funcexpr->function_from))
is_subproc_func = true;
}

if (!is_subproc_func)
if (!is_subproc_func && !is_with_func)
{
/* Verify EXECUTE privilege before invoking the function. */
aclresult = object_aclcheck(ProcedureRelationId, funcid, GetUserId(), ACL_EXECUTE);
Expand Down Expand Up @@ -2813,7 +2817,36 @@ ExecInitFunc(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid,
fcinfo = scratch->d.func.fcinfo_data;

/* Set up the primary fmgr lookup information */
if (is_subproc_func)
if (is_with_func)
{
/*
* WITH-clause inline functions dispatch through the plisql call
* handler. fn_oid stores the function's index within the WITH clause
* (not a real pg_proc OID), and fn_extra stores the query EState so
* the handler can find/build the WithFuncContainer.
*
* We must store the EState of the outermost query that owns the WITH
* clause, NOT the EState of any intermediate SPI execution.
* plisql_active_with_func_estate (set by plisql_call_handler before
* invoking each WITH function) always points to the correct EState, so
* use it whenever it is available. The fallback via state->parent->state
* handles the initial call from the outer query before any WITH function
* has started executing.
*/
EState *qstate;

if (plisql_active_with_func_estate != NULL)
qstate = plisql_active_with_func_estate;
else if (state->parent && state->parent->state)
qstate = state->parent->state;
else
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("cannot execute WITH clause function outside a query")));
fmgr_subproc_info_cxt(funcid, flinfo, CurrentMemoryContext);
flinfo->fn_extra = qstate; /* override: EState, not NULL */
}
else if (is_subproc_func)
fmgr_subproc_info_cxt(funcid, flinfo, CurrentMemoryContext);
else
fmgr_info(funcid, flinfo);
Expand Down Expand Up @@ -5187,13 +5220,18 @@ ExecInitFuncWithOutParams(Expr *node, ExprState *state,
}

/*
* Get the argument names and modes
* Get the argument names and modes
*/
num_all_args = get_func_arg_info(func_tuple, &argtypes,
&argnames, &argmodes);
rettype = get_func_real_rettype(func_tuple);
funcname = NameStr(((Form_pg_proc) GETSTRUCT(func_tuple))->proname);
}
else if (funcexpr->function_from == FUNC_FROM_WITH_CLAUSE)
{
/* WITH-clause inline functions have no OUT params */
return;
}
else
{
/* skip procedure */
Expand Down
15 changes: 15 additions & 0 deletions src/backend/executor/execSRF.c
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ ExecMakeTableFunctionResult(SetExprState *setexpr,
funcexpr->funccollid = chcollationoid;
}
else if (!FUNC_EXPR_FROM_PG_PROC(funcexpr->function_from) &&
funcexpr->function_from != FUNC_FROM_WITH_CLAUSE &&
subproc_should_change_return_type(funcexpr,
&resulttype,
&chtypmod,
Expand Down Expand Up @@ -741,6 +742,20 @@ init_sexpr(Oid foid, Oid input_collation, Expr *node,
{
FuncExpr *funcexpr = (FuncExpr *) node;

/*
* WITH-clause inline functions are scalar-only by design (the lookup
* hook forces retset = false), so they must not appear here — this
* function only handles table-function and set-returning paths.
* Reject with a clear message rather than letting the call fall
* through to the PL/iSQL subproc machinery, which would surface a
* confusing internal error.
*/
if (funcexpr->function_from == FUNC_FROM_WITH_CLAUSE)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("WITH clause function cannot be used as a table or set-returning function"),
errhint("WITH FUNCTION/PROCEDURE definitions can only be invoked from scalar expression contexts.")));

if (!FUNC_EXPR_FROM_PG_PROC(funcexpr->function_from))
is_subproc_func = true;
}
Expand Down
1 change: 1 addition & 0 deletions src/backend/executor/execTuples.c
Original file line number Diff line number Diff line change
Expand Up @@ -2206,6 +2206,7 @@ ExecTypeFromTLInternal(List *targetList, bool hasrowid, bool skipjunk)
func->funccollid = chcollationoid;
}
else if (!FUNC_EXPR_FROM_PG_PROC(func->function_from) &&
func->function_from != FUNC_FROM_WITH_CLAUSE &&
subproc_should_change_return_type(func, &resulttype, &chtypmod, &chcollationoid))
{
typoid = resulttype;
Expand Down
1 change: 1 addition & 0 deletions src/backend/optimizer/plan/planner.c
Original file line number Diff line number Diff line change
Expand Up @@ -622,6 +622,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions,
result->utilityStmt = parse->utilityStmt;
result->stmt_location = parse->stmt_location;
result->stmt_len = parse->stmt_len;
result->withFuncDefs = parse->withFuncDefs;

result->jitFlags = PGJIT_NONE;
if (jit_enabled && jit_above_cost >= 0 &&
Expand Down
Loading
Loading