On Tue, May 24, 2016 at 02:16:40PM -0400, Tom Lane wrote:
> David Fetter <[email protected]> writes:
> > Per discussion on IRC and some test code, COPY can't take parameters
> > in a PREPARE, which feature would make it even more useful.
>
> Uh, what?
>
> regression=# prepare foo as copy c from stdin;
> ERROR: syntax error at or near "copy"
> LINE 1: prepare foo as copy c from stdin;
> ^
>
> Passing parameters into a utility statement of any stripe is a
> pretty considerable project, IIRC; the infrastructure isn't there.
Maybe it should be, at least for some of the utility statements.
Please find attached a patch which, according to Andrew Gierth, its
author, just barely qualifies as a PoC. Yes, it's had to break a
couple of messages in the regression tests.
Cheers,
David.
--
David Fetter <[email protected]> http://fetter.org/
Phone: +1 415 235 3778 AIM: dfetter666 Yahoo!: dfetter
Skype: davidfetter XMPP: [email protected]
Remember to vote!
Consider donating to Postgres: http://www.postgresql.org/about/donate
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 3201476..66ae54e 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -279,12 +279,12 @@ static const char BinarySignature[11] =
"PGCOPY\n\377\r\n\0";
/* non-export function prototypes */
-static CopyState BeginCopy(bool is_from, Relation rel, Node *raw_query,
+static CopyState BeginCopy(bool is_from, Relation rel, Node *raw_query,
ParamListInfo params,
const char *queryString, const Oid queryRelId, List
*attnamelist,
List *options);
static void EndCopy(CopyState cstate);
static void ClosePipeToProgram(CopyState cstate);
-static CopyState BeginCopyTo(Relation rel, Node *query, const char
*queryString,
+static CopyState BeginCopyTo(Relation rel, Node *query, ParamListInfo params,
const char *queryString,
const Oid queryRelId, const char *filename, bool
is_program,
List *attnamelist, List *options);
static void EndCopyTo(CopyState cstate);
@@ -787,7 +787,7 @@ CopyLoadRawBuf(CopyState cstate)
* the table or the specifically requested columns.
*/
Oid
-DoCopy(const CopyStmt *stmt, const char *queryString, uint64 *processed)
+DoCopy(const CopyStmt *stmt, const char *queryString, ParamListInfo params,
uint64 *processed)
{
CopyState cstate;
bool is_from = stmt->is_from;
@@ -944,7 +944,7 @@ DoCopy(const CopyStmt *stmt, const char *queryString,
uint64 *processed)
}
else
{
- cstate = BeginCopyTo(rel, query, queryString, relid,
+ cstate = BeginCopyTo(rel, query, params, queryString, relid,
stmt->filename,
stmt->is_program,
stmt->attlist,
stmt->options);
*processed = DoCopyTo(cstate); /* copy from database to file */
@@ -1321,6 +1321,7 @@ static CopyState
BeginCopy(bool is_from,
Relation rel,
Node *raw_query,
+ ParamListInfo params,
const char *queryString,
const Oid queryRelId,
List *attnamelist,
@@ -1391,11 +1392,16 @@ BeginCopy(bool is_from,
* function and is executed repeatedly. (See also the same
hack in
* DECLARE CURSOR and PREPARE.) XXX FIXME someday.
*/
- rewritten = pg_analyze_and_rewrite((Node *)
copyObject(raw_query),
-
queryString, NULL, 0);
+ if (!IsA(raw_query,List))
+ {
+ rewritten = pg_analyze_and_rewrite((Node *)
copyObject(raw_query),
+
queryString, NULL, 0);
+ }
+ else
+ rewritten = (List *) raw_query;
/* check that we got back something we can work with */
- if (rewritten == NIL)
+ if (rewritten == NIL || linitial(rewritten) == NIL)
{
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
@@ -1453,7 +1459,7 @@ BeginCopy(bool is_from,
}
/* plan the query */
- plan = pg_plan_query(query, 0, NULL);
+ plan = pg_plan_query(query, 0, params);
/*
* With row level security and a user using "COPY relation TO",
we
@@ -1495,7 +1501,7 @@ BeginCopy(bool is_from,
cstate->queryDesc = CreateQueryDesc(plan, queryString,
GetActiveSnapshot(),
InvalidSnapshot,
-
dest, NULL, 0);
+
dest, params, 0);
/*
* Call ExecutorStart to prepare the plan for execution.
@@ -1682,6 +1688,7 @@ EndCopy(CopyState cstate)
static CopyState
BeginCopyTo(Relation rel,
Node *query,
+ ParamListInfo params,
const char *queryString,
const Oid queryRelId,
const char *filename,
@@ -1725,7 +1732,7 @@ BeginCopyTo(Relation rel,
RelationGetRelationName(rel))));
}
- cstate = BeginCopy(false, rel, query, queryString, queryRelId,
attnamelist,
+ cstate = BeginCopy(false, rel, query, params, queryString, queryRelId,
attnamelist,
options);
oldcontext = MemoryContextSwitchTo(cstate->copycontext);
@@ -2670,7 +2677,7 @@ BeginCopyFrom(Relation rel,
MemoryContext oldcontext;
bool volatile_defexprs;
- cstate = BeginCopy(true, rel, NULL, NULL, InvalidOid, attnamelist,
options);
+ cstate = BeginCopy(true, rel, NULL, NULL, NULL, InvalidOid,
attnamelist, options);
oldcontext = MemoryContextSwitchTo(cstate->copycontext);
/* Initialize state variables */
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 29c8c4e..a6d9dd6 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -299,6 +299,13 @@ transformStmt(ParseState *pstate, Node *parseTree)
result = makeNode(Query);
result->commandType = CMD_UTILITY;
result->utilityStmt = (Node *) parseTree;
+
+ if (IsA(parseTree,CopyStmt)
+ && ((CopyStmt *)parseTree)->query != NIL)
+ {
+ CopyStmt *stmt = (CopyStmt *) parseTree;
+ stmt->query = transformStmt(pstate,
stmt->query);
+ }
break;
}
@@ -343,6 +350,7 @@ analyze_requires_snapshot(Node *parseTree)
case T_ExplainStmt:
case T_CreateTableAsStmt:
+ case T_CopyStmt:
/* yes, because we must analyze the contained statement
*/
result = true;
break;
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 68811f1..4a18fec 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -739,6 +739,16 @@ pg_rewrite_query(Query *query)
if (query->commandType == CMD_UTILITY)
{
+ if (query->utilityStmt
+ && IsA(query->utilityStmt,CopyStmt)
+ && ((CopyStmt *)(query->utilityStmt))->query)
+ {
+ CopyStmt *stmt = (CopyStmt *)(query->utilityStmt);
+ Assert(IsA(stmt->query,Query));
+ stmt->query = (Node *) QueryRewrite((Query
*)(stmt->query));
+ if (stmt->query == (Node *) NIL)
+ stmt->query = (Node *) list_make1(NIL);
+ }
/* don't rewrite utilities, just dump 'em into result list */
querytree_list = list_make1(query);
}
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index ac50c2a..a7deead 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -540,7 +540,7 @@ standard_ProcessUtility(Node *parsetree,
{
uint64 processed;
- DoCopy((CopyStmt *) parsetree, queryString,
&processed);
+ DoCopy((CopyStmt *) parsetree, queryString,
params, &processed);
if (completionTag)
snprintf(completionTag,
COMPLETION_TAG_BUFSIZE,
"COPY " UINT64_FORMAT,
processed);
diff --git a/src/include/commands/copy.h b/src/include/commands/copy.h
index 314d1f7..44c2c66 100644
--- a/src/include/commands/copy.h
+++ b/src/include/commands/copy.h
@@ -21,7 +21,7 @@
/* CopyStateData is private in commands/copy.c */
typedef struct CopyStateData *CopyState;
-extern Oid DoCopy(const CopyStmt *stmt, const char *queryString,
+extern Oid DoCopy(const CopyStmt *stmt, const char *queryString, ParamListInfo
params,
uint64 *processed);
extern void ProcessCopyOptions(CopyState cstate, bool is_from, List *options);
--
Sent via pgsql-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers