spi.diff
application/octet-stream
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: unified
| File | + | − |
|---|---|---|
| src/backend/executor/spi.c | 329 | 0 |
| src/backend/utils/error/elog.c | 17 | 0 |
| src/include/executor/spi.h | 13 | 0 |
| src/include/utils/elog.h | 1 | 0 |
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index 4a6b966..4bfc4f4 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -22,6 +22,8 @@
#include "commands/trigger.h"
#include "executor/executor.h"
#include "executor/spi_priv.h"
+#include "nodes/pg_list.h"
+#include "parser/analyze.h"
#include "tcop/pquery.h"
#include "tcop/utility.h"
#include "utils/builtins.h"
@@ -31,6 +33,7 @@
#include "utils/snapmgr.h"
#include "utils/syscache.h"
#include "utils/typcache.h"
+#include "rewrite/rewriteHandler.h"
uint32 SPI_processed = 0;
@@ -50,6 +53,11 @@ static Portal SPI_cursor_open_internal(const char *name, SPIPlanPtr plan,
static void _SPI_prepare_plan(const char *src, SPIPlanPtr plan,
ParamListInfo boundParams);
+static int _SPI_prepare_statement_plan(const char *src, SPIPlanPtr plan,
+ SPIParamCallback pcb, void *cb_data);
+
+static int _SPI_execute_statements(const char *src);
+
static int _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
Snapshot snapshot, Snapshot crosscheck_snapshot,
bool read_only, bool fire_triggers, long tcount);
@@ -524,6 +532,55 @@ SPI_execute_with_args(const char *src,
}
SPIPlanPtr
+SPI_prepare_statement(const char *src, int cursorOptions, SPIParamCallback pcb, void *cbdata, TupleDesc *resultDesc)
+{
+ _SPI_plan plan;
+ SPIPlanPtr rplan;
+
+ SPI_result = _SPI_begin_call(true);
+ if (SPI_result < 0)
+ return NULL;
+
+ memset(&plan, 0, sizeof(_SPI_plan));
+ plan.magic = _SPI_PLAN_MAGIC;
+ plan.cursor_options = cursorOptions;
+ plan.nargs = -1;
+ plan.argtypes = NULL;
+
+ SPI_result = _SPI_prepare_statement_plan(src, &plan, pcb, cbdata);
+
+ if (SPI_result < 0)
+ rplan = NULL;
+ else
+ {
+ rplan = _SPI_copy_plan(&plan, _SPI_current->procCxt);
+ if (rplan != NULL)
+ *resultDesc =
+ ((CachedPlanSource *) linitial(rplan->plancache_list))->resultDesc;
+ }
+
+ _SPI_end_call(true);
+
+ return rplan;
+}
+
+int
+SPI_execute_statements(const char *src)
+{
+ int r = -1;
+
+ SPI_result = _SPI_begin_call(true);
+ if (SPI_result < 0)
+ return r;
+
+ SPI_result = r = _SPI_execute_statements(src);
+
+ _SPI_end_call(true);
+
+ return r;
+}
+
+SPIPlanPtr
SPI_prepare(const char *src, int nargs, Oid *argtypes)
{
return SPI_prepare_cursor(src, nargs, argtypes, 0);
@@ -1648,6 +1705,278 @@ spi_printtup(TupleTableSlot *slot, DestReceiver *self)
*/
/*
+ * Parse and plan a *single* statement.
+ *
+ * Given a destination plan, parse and plan the given statement string into the
+ * SPIPlanPtr.
+ *
+ * In contrast to _SPI_prepare_plan, this function will analyze the raw parse
+ * tree and extract the statement's parameters from the query. After collecting
+ * the parameter types, the SPIParamCallback is invoked to allow the original caller
+ * to note the parameter types, and to provide actual parameters if so desired.
+ *
+ * This requires that the given SQL is a single statement.
+ * Transaction and STDIN/STDOUT COPY statements are restricted.
+ */
+static int
+_SPI_prepare_statement_plan(const char *src, SPIPlanPtr plan, SPIParamCallback spipcb, void *cbdata)
+{
+ List *raw_parsetree_list;
+ Node *rpt;
+ List *plancache_list;
+ Query *query;
+ ErrorContextCallback spierrcontext;
+ Oid *argtypes = NULL;
+ int nargs = -1;
+ int cursor_options = plan->cursor_options;
+ List *stmt_list;
+ CachedPlanSource *plansource;
+ CachedPlan *cplan;
+ Datum *param_datums = NULL;
+ bool *param_nulls = NULL;
+ ParamListInfo boundParams;
+ const char *commandTag;
+
+ /*
+ * Setup error traceback support for ereport()
+ */
+ spierrcontext.callback = _SPI_error_callback;
+ spierrcontext.arg = (void *) src;
+ spierrcontext.previous = error_context_stack;
+ error_context_stack = &spierrcontext;
+
+ /*
+ * Parse the request string into a list of raw parse trees.
+ */
+ raw_parsetree_list = pg_parse_query(src);
+ if (list_length(raw_parsetree_list) != 1)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("cannot insert multiple commands into a prepared statement")));
+ }
+
+ rpt = (Node *) linitial(raw_parsetree_list);
+
+ /*
+ * Validate that it fits the basic criteria for a single SPI statement.
+ *
+ * Don't wait for execution time to fail on these.
+ */
+ if (IsA(rpt, TransactionStmt))
+ {
+ SPI_result = SPI_ERROR_TRANSACTION;
+ goto fail;
+ }
+ if (IsA(rpt, CopyStmt) && ((CopyStmt *) rpt)->filename == NULL)
+ {
+ SPI_result = SPI_ERROR_COPY;
+ goto fail;
+ }
+
+ /*
+ * for repalloc
+ */
+ nargs = 0;
+ argtypes = palloc(sizeof(Oid) * 0);
+
+ /* Need a copyObject here to keep parser from modifying raw tree */
+ query = parse_analyze_varparams(
+ (Node *) copyObject(rpt), src, &argtypes, &nargs);
+
+ plan->argtypes = argtypes;
+ plan->nargs = nargs;
+
+ commandTag = CreateCommandTag(rpt);
+ /*
+ * Run the callback to give the caller the parameter
+ * type Oids and, potentially, collect parameters for the statement.
+ */
+ spipcb(cbdata, commandTag, nargs, argtypes, ¶m_datums, ¶m_nulls);
+
+ /*
+ * If the callback set some parameters, use them as constants in the plan.
+ */
+ if (PointerIsValid(param_datums))
+ {
+ if (!PointerIsValid(param_nulls))
+ {
+ SPI_result = SPI_ERROR_ARGUMENT;
+ goto fail;
+ }
+
+ boundParams = _SPI_convert_params(nargs, argtypes,
+ param_datums, param_nulls,
+ PARAM_FLAG_CONST);
+ }
+ else
+ boundParams = NULL;
+
+ /*
+ * Don't rewrite utility statements.
+ */
+ if (query->commandType == CMD_UTILITY)
+ stmt_list = list_make1(query);
+ else
+ stmt_list = QueryRewrite(query);
+
+ stmt_list = pg_plan_queries(stmt_list, cursor_options, boundParams);
+
+ plansource = (CachedPlanSource *) palloc0(sizeof(CachedPlanSource));
+ cplan = (CachedPlan *) palloc0(sizeof(CachedPlan));
+
+ plansource->raw_parse_tree = rpt;
+ plansource->query_string = pstrdup(src);
+ plansource->commandTag = commandTag;
+ plansource->param_types = argtypes;
+ plansource->num_params = nargs;
+ plansource->fully_planned = true;
+ plansource->fixed_result = true;
+ plansource->resultDesc = PlanCacheComputeResultDesc(stmt_list);
+ plansource->plan = cplan;
+
+ cplan->stmt_list = stmt_list;
+ cplan->fully_planned = true;
+
+ plancache_list = NIL;
+ plancache_list = lappend(plancache_list, plansource);
+ plan->plancache_list = plancache_list;
+
+ /*
+ * Pop the error context stack
+ */
+ error_context_stack = spierrcontext.previous;
+
+ return 0;
+fail:
+ error_context_stack = spierrcontext.previous;
+ return SPI_result;
+}
+
+/*
+ * Parse, plan and execute all the statements in the given string.
+ * Execution occurs directly after a statement is planned and *before* the next
+ * statement is planned. This allows statements to depend on objects created by
+ * statements executed prior.
+ *
+ * This interface is designed for allowing users to execute bulk DDL/DML.
+ */
+static int
+_SPI_execute_statements(const char *src)
+{
+ int r = 0;
+ List *raw_parsetree_list;
+ ListCell *list_item;
+ DestReceiver *dest;
+ ErrorContextCallback spierrcontext;
+
+ /*
+ * Setup error traceback support for ereport()
+ */
+ spierrcontext.callback = _SPI_error_callback;
+ spierrcontext.arg = (void *) src;
+ spierrcontext.previous = error_context_stack;
+ error_context_stack = &spierrcontext;
+
+ /*
+ * Parse the request string into a list of raw parse trees.
+ */
+ raw_parsetree_list = pg_parse_query(src);
+
+ /*
+ * Everything is sent to DestNone.
+ */
+ _SPI_current->processed = 0;
+ _SPI_current->lastoid = InvalidOid;
+ _SPI_current->tuptable = NULL;
+ dest = CreateDestReceiver(DestNone);
+
+ /*
+ * Iterate over the parsetree list, executing each statement.
+ */
+ foreach(list_item, raw_parsetree_list)
+ {
+ Node *parsetree = (Node *) lfirst(list_item);
+ List *stmt_list;
+ ListCell *lc2;
+
+ /*
+ * Before each statement.
+ */
+ CommandCounterIncrement();
+
+ /*
+ * No parameters.
+ */
+ stmt_list = pg_analyze_and_rewrite(parsetree, src, NULL, 0);
+ stmt_list = pg_plan_queries(stmt_list, 0, NULL);
+
+ foreach(lc2, stmt_list)
+ {
+ Node *stmt = (Node *) lfirst(lc2);
+
+ if (!IsA(stmt, PlannedStmt))
+ {
+ /*
+ * Filter prohibited statements.
+ */
+ if (IsA(stmt, CopyStmt))
+ {
+ CopyStmt *cstmt = (CopyStmt *) stmt;
+
+ if (cstmt->filename == NULL)
+ {
+ return SPI_ERROR_COPY;
+ }
+ }
+ else if (IsA(stmt, TransactionStmt))
+ {
+ return SPI_ERROR_TRANSACTION;
+ }
+ }
+
+ /*
+ * Before each command.
+ */
+ CommandCounterIncrement();
+ PushActiveSnapshot(GetTransactionSnapshot());
+
+ if (IsA(stmt, PlannedStmt) &&
+ ((PlannedStmt *) stmt)->utilityStmt == NULL)
+ {
+ QueryDesc *qdesc;
+
+ qdesc = CreateQueryDesc(
+ (PlannedStmt *) stmt, src,
+ GetActiveSnapshot(), /* xact snapshot */
+ InvalidSnapshot, /* no crosscheck */
+ dest, NULL, false);
+ r = _SPI_pquery(qdesc, true, 0);
+ FreeQueryDesc(qdesc);
+ }
+ else
+ {
+ ProcessUtility(stmt, src,
+ NULL, /* no params */
+ false, /* not top level */
+ dest, /* DestNone */
+ NULL);
+ r = SPI_OK_UTILITY;
+ }
+
+ PopActiveSnapshot();
+
+ if (r < 0)
+ return r;
+ }
+ }
+
+ error_context_stack = spierrcontext.previous;
+
+ return r;
+}
+
+/*
* Parse and plan a querystring.
*
* At entry, plan->argtypes and plan->nargs (or alternatively plan->parserSetup
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index 025ca35..5b7ffa2 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -1021,6 +1021,23 @@ internalerrquery(const char *query)
}
/*
+ * getelevel --- return the currently set elevel
+ *
+ * This is only intended for use in error callback subroutines, since there
+ * is no other place outside elog.c where the concept is meaningful.
+ */
+int
+getelevel(void)
+{
+ ErrorData *edata = &errordata[errordata_stack_depth];
+
+ /* we don't bother incrementing recursion_depth */
+ CHECK_STACK_DEPTH();
+
+ return edata->elevel;
+}
+
+/*
* geterrcode --- return the currently set SQLSTATE error code
*
* This is only intended for use in error callback subroutines, since there
diff --git a/src/include/executor/spi.h b/src/include/executor/spi.h
index 33f4f15..b23f7a1 100644
--- a/src/include/executor/spi.h
+++ b/src/include/executor/spi.h
@@ -31,6 +31,16 @@ typedef struct SPITupleTable
/* Plans are opaque structs for standard users of SPI */
typedef struct _SPI_plan *SPIPlanPtr;
+/*
+ * Callback function used by SPI_prepare_statement to give the
+ * caller the type Oid's of the statement's parameters.
+ *
+ * Additionally, the caller has the ability to give the param_values.
+ */
+typedef void (*SPIParamCallback)(
+ void *cb_data, const char *commandTag,
+ int nargs, Oid *typoids, Datum **param_values, char **param_nulls);
+
#define SPI_ERROR_CONNECT (-1)
#define SPI_ERROR_COPY (-2)
#define SPI_ERROR_OPUNKNOWN (-3)
@@ -88,6 +98,9 @@ extern int SPI_execute_with_args(const char *src,
int nargs, Oid *argtypes,
Datum *Values, const char *Nulls,
bool read_only, long tcount);
+extern int SPI_execute_statements(const char *src);
+extern SPIPlanPtr SPI_prepare_statement(const char *src, int cursorOptions,
+ SPIParamCallback pcb, void *pcb_arg, TupleDesc *resultDesc);
extern SPIPlanPtr SPI_prepare(const char *src, int nargs, Oid *argtypes);
extern SPIPlanPtr SPI_prepare_cursor(const char *src, int nargs, Oid *argtypes,
int cursorOptions);
diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h
index 0982ca4..ea5a1c1 100644
--- a/src/include/utils/elog.h
+++ b/src/include/utils/elog.h
@@ -180,6 +180,7 @@ extern int errposition(int cursorpos);
extern int internalerrposition(int cursorpos);
extern int internalerrquery(const char *query);
+extern int getelevel(void);
extern int geterrcode(void);
extern int geterrposition(void);
extern int getinternalerrposition(void);