On Tue, May 24, 2016 at 02:16:40PM -0400, Tom Lane wrote:
> David Fetter <da...@fetter.org> 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 <da...@fetter.org> http://fetter.org/
Phone: +1 415 235 3778  AIM: dfetter666  Yahoo!: dfetter
Skype: davidfetter      XMPP: david.fet...@gmail.com

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 (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to