diff options
author | Pavan Deolasee | 2017-01-17 05:53:37 +0000 |
---|---|---|
committer | Pavan Deolasee | 2017-05-05 04:59:33 +0000 |
commit | 9ddddcb8d51fd640f59401ea9bc335d08bf5a23c (patch) | |
tree | 28d06c679a8c88ded40ed3952b0cb9a7ab05fa73 | |
parent | 1e1f85b67f577cd86b4aa0b5387dfaba7272a28e (diff) |
Handle multi-command queries correctly inside SQL as well as plpgsql functions.
Postgres-XL sends down utility statements to the remote nodes as plain query
strings. When there are multiple commands in a query string, separated by ';',
we were incorrectly sending down the entire query string again and again while
handling each command. This can lead to unpleasant as well as incorrect
behaviour. This was earlier handled for execution via psql, but this patch
fixes it for SPI and other places such as extension creation and SQL function
handling.
-rw-r--r-- | src/backend/commands/extension.c | 14 | ||||
-rw-r--r-- | src/backend/executor/functions.c | 28 | ||||
-rw-r--r-- | src/backend/executor/spi.c | 36 | ||||
-rw-r--r-- | src/test/regress/expected/xc_misc.out | 75 | ||||
-rw-r--r-- | src/test/regress/sql/xc_misc.sql | 31 |
5 files changed, 146 insertions, 38 deletions
diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c index 680c22dee2..be8641b573 100644 --- a/src/backend/commands/extension.c +++ b/src/backend/commands/extension.c @@ -683,13 +683,14 @@ static void execute_sql_string(const char *sql, const char *filename) { List *raw_parsetree_list; + List *querysource_list; DestReceiver *dest; - ListCell *lc1; + ListCell *lc1, *lc3; /* * Parse the SQL string into a list of raw parse trees. */ - raw_parsetree_list = pg_parse_query(sql); + raw_parsetree_list = pg_parse_query_get_source(sql, &querysource_list); /* All output from SELECTs goes to the bit bucket */ dest = CreateDestReceiver(DestNone); @@ -699,14 +700,15 @@ execute_sql_string(const char *sql, const char *filename) * parsetree. We must fully execute each query before beginning parse * analysis on the next one, since there may be interdependencies. */ - foreach(lc1, raw_parsetree_list) + forboth(lc1, raw_parsetree_list, lc3, querysource_list) { Node *parsetree = (Node *) lfirst(lc1); + char *querysource = (char *) lfirst(lc3); List *stmt_list; ListCell *lc2; stmt_list = pg_analyze_and_rewrite(parsetree, - sql, + querysource, NULL, 0); stmt_list = pg_plan_queries(stmt_list, CURSOR_OPT_PARALLEL_OK, NULL); @@ -730,7 +732,7 @@ execute_sql_string(const char *sql, const char *filename) QueryDesc *qdesc; qdesc = CreateQueryDesc((PlannedStmt *) stmt, - sql, + querysource, GetActiveSnapshot(), NULL, dest, NULL, 0); @@ -744,7 +746,7 @@ execute_sql_string(const char *sql, const char *filename) else { ProcessUtility(stmt, - sql, + querysource, PROCESS_UTILITY_QUERY, NULL, dest, diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c index 4e482f397f..06b4a57656 100644 --- a/src/backend/executor/functions.c +++ b/src/backend/executor/functions.c @@ -72,6 +72,7 @@ typedef struct execution_state bool lazyEval; /* true if should fetch one row at a time */ Node *stmt; /* PlannedStmt or utility statement */ QueryDesc *qd; /* null unless status == RUN */ + char *src; /* source query resulting in this state */ } execution_state; @@ -156,6 +157,7 @@ static Node *sql_fn_make_param(SQLFunctionParseInfoPtr pinfo, static Node *sql_fn_resolve_param_name(SQLFunctionParseInfoPtr pinfo, const char *paramname, int location); static List *init_execution_state(List *queryTree_list, + List *querySource_list, SQLFunctionCachePtr fcache, bool lazyEvalOK); static void init_sql_fcache(FmgrInfo *finfo, Oid collation, bool lazyEvalOK); @@ -474,19 +476,21 @@ sql_fn_resolve_param_name(SQLFunctionParseInfoPtr pinfo, */ static List * init_execution_state(List *queryTree_list, + List *querySource_list, SQLFunctionCachePtr fcache, bool lazyEvalOK) { List *eslist = NIL; execution_state *lasttages = NULL; - ListCell *lc1; + ListCell *lc1, *lc3; - foreach(lc1, queryTree_list) + forboth(lc1, queryTree_list, lc3, querySource_list) { List *qtlist = (List *) lfirst(lc1); + char *querysource = (char *) lfirst(lc3); execution_state *firstes = NULL; execution_state *preves = NULL; - ListCell *lc2; + ListCell *lc2, *lc4; foreach(lc2, qtlist) { @@ -553,6 +557,7 @@ init_execution_state(List *queryTree_list, newes->lazyEval = false; /* might change below */ newes->stmt = stmt; newes->qd = NULL; + newes->src = pstrdup(querysource); if (queryTree->canSetTag) lasttages = newes; @@ -610,9 +615,10 @@ init_sql_fcache(FmgrInfo *finfo, Oid collation, bool lazyEvalOK) Form_pg_proc procedureStruct; SQLFunctionCachePtr fcache; List *raw_parsetree_list; + List *querysource_list; List *queryTree_list; List *flat_query_list; - ListCell *lc; + ListCell *lc, *lc2; Datum tmp; bool isNull; @@ -713,17 +719,18 @@ init_sql_fcache(FmgrInfo *finfo, Oid collation, bool lazyEvalOK) * but we'll not worry about it until the module is rewritten to use * plancache.c. */ - raw_parsetree_list = pg_parse_query(fcache->src); + raw_parsetree_list = pg_parse_query_get_source(fcache->src, &querysource_list); queryTree_list = NIL; flat_query_list = NIL; - foreach(lc, raw_parsetree_list) + forboth(lc, raw_parsetree_list, lc2, querysource_list) { Node *parsetree = (Node *) lfirst(lc); + char *querysource = (char *) lfirst(lc2); List *queryTree_sublist; queryTree_sublist = pg_analyze_and_rewrite_params(parsetree, - fcache->src, + querysource, (ParserSetupHook) sql_fn_parser_setup, fcache->pinfo); queryTree_list = lappend(queryTree_list, queryTree_sublist); @@ -774,6 +781,7 @@ init_sql_fcache(FmgrInfo *finfo, Oid collation, bool lazyEvalOK) /* Finally, plan the queries */ fcache->func_state = init_execution_state(queryTree_list, + querysource_list, fcache, lazyEvalOK); @@ -818,14 +826,14 @@ postquel_start(execution_state *es, SQLFunctionCachePtr fcache) if (IsA(es->stmt, PlannedStmt)) es->qd = CreateQueryDesc((PlannedStmt *) es->stmt, - fcache->src, + es->src, GetActiveSnapshot(), InvalidSnapshot, dest, fcache->paramLI, 0); else es->qd = CreateUtilityQueryDesc(es->stmt, - fcache->src, + es->src, GetActiveSnapshot(), dest, fcache->paramLI); @@ -865,7 +873,7 @@ postquel_getnext(execution_state *es, SQLFunctionCachePtr fcache) ProcessUtility((es->qd->plannedstmt ? (Node *) es->qd->plannedstmt : es->qd->utilitystmt), - fcache->src, + es->src, PROCESS_UTILITY_QUERY, es->qd->params, es->qd->dest, diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c index d971d936c6..0a3c65e6f7 100644 --- a/src/backend/executor/spi.c +++ b/src/backend/executor/spi.c @@ -52,6 +52,7 @@ static Portal SPI_cursor_open_internal(const char *name, SPIPlanPtr plan, #ifdef PGXC static void _SPI_pgxc_prepare_plan(const char *src, List *src_parsetree, + List *query_source, SPIPlanPtr plan); #endif static void _SPI_prepare_plan(const char *src, SPIPlanPtr plan); @@ -405,7 +406,8 @@ SPI_execute_direct(const char *remote_sql, char *nodename) plan.cursor_options = 0; /* Now pass the ExecDirectStmt parsetree node */ - _SPI_pgxc_prepare_plan(execdirect.data, list_make1(stmt), &plan); + _SPI_pgxc_prepare_plan(execdirect.data, list_make1(stmt), + list_make1(execdirect.data), &plan); res = _SPI_execute_plan(&plan, NULL, InvalidSnapshot, InvalidSnapshot, false, true, 0); @@ -1888,7 +1890,7 @@ static void _SPI_prepare_plan(const char *src, SPIPlanPtr plan) { #ifdef PGXC - _SPI_pgxc_prepare_plan(src, NULL, plan); + _SPI_pgxc_prepare_plan(src, NULL, NULL, plan); } /* @@ -1898,12 +1900,14 @@ _SPI_prepare_plan(const char *src, SPIPlanPtr plan) * transparent to the user. */ static void -_SPI_pgxc_prepare_plan(const char *src, List *src_parsetree, SPIPlanPtr plan) +_SPI_pgxc_prepare_plan(const char *src, List *src_parsetree, + List *query_source, SPIPlanPtr plan) { #endif List *raw_parsetree_list; + List *querysource_list; List *plancache_list; - ListCell *list_item; + ListCell *list_item, *list_item2; ErrorContextCallback spierrcontext; /* @@ -1920,19 +1924,23 @@ _SPI_pgxc_prepare_plan(const char *src, List *src_parsetree, SPIPlanPtr plan) #ifdef PGXC /* Parse it only if there isn't an already parsed tree passed */ if (src_parsetree) + { raw_parsetree_list = src_parsetree; + querysource_list = query_source; + } else #endif - raw_parsetree_list = pg_parse_query(src); + raw_parsetree_list = pg_parse_query_get_source(src, &querysource_list); /* * Do parse analysis and rule rewrite for each raw parsetree, storing the * results into unsaved plancache entries. */ plancache_list = NIL; - foreach(list_item, raw_parsetree_list) + forboth(list_item, raw_parsetree_list, list_item2, querysource_list) { Node *parsetree = (Node *) lfirst(list_item); + char *querysource = (char *) lfirst (list_item2); List *stmt_list; CachedPlanSource *plansource; @@ -1941,7 +1949,7 @@ _SPI_pgxc_prepare_plan(const char *src, List *src_parsetree, SPIPlanPtr plan) * needs to see the unmodified raw parse tree. */ plansource = CreateCachedPlan(parsetree, - src, + querysource, #ifdef PGXC NULL, #endif @@ -1955,14 +1963,14 @@ _SPI_pgxc_prepare_plan(const char *src, List *src_parsetree, SPIPlanPtr plan) { Assert(plan->nargs == 0); stmt_list = pg_analyze_and_rewrite_params(parsetree, - src, + querysource, plan->parserSetup, plan->parserSetupArg); } else { stmt_list = pg_analyze_and_rewrite(parsetree, - src, + querysource, plan->argtypes, plan->nargs); } @@ -2013,8 +2021,9 @@ static void _SPI_prepare_oneshot_plan(const char *src, SPIPlanPtr plan) { List *raw_parsetree_list; + List *querysource_list; List *plancache_list; - ListCell *list_item; + ListCell *list_item, *list_item2; ErrorContextCallback spierrcontext; /* @@ -2028,20 +2037,21 @@ _SPI_prepare_oneshot_plan(const char *src, SPIPlanPtr plan) /* * Parse the request string into a list of raw parse trees. */ - raw_parsetree_list = pg_parse_query(src); + raw_parsetree_list = pg_parse_query_get_source(src, &querysource_list); /* * Construct plancache entries, but don't do parse analysis yet. */ plancache_list = NIL; - foreach(list_item, raw_parsetree_list) + forboth(list_item, raw_parsetree_list, list_item2, querysource_list) { Node *parsetree = (Node *) lfirst(list_item); + char *querysource = (char *) lfirst (list_item2); CachedPlanSource *plansource; plansource = CreateOneShotCachedPlan(parsetree, - src, + querysource, CreateCommandTag(parsetree)); plancache_list = lappend(plancache_list, plansource); diff --git a/src/test/regress/expected/xc_misc.out b/src/test/regress/expected/xc_misc.out index 75d207cccb..b378aa495e 100644 --- a/src/test/regress/expected/xc_misc.out +++ b/src/test/regress/expected/xc_misc.out @@ -51,14 +51,79 @@ drop table t1_misc; create table my_tab1 (a int); insert into my_tab1 values(1); create function f1 () returns setof my_tab1 as $$ create table my_tab2 (a int); select * from my_tab1; $$ language sql; +select f1(); + f1 +----- + (1) +(1 row) + +drop function f1(); +-- fail since my_tab4 does not exist +create function f1 () returns setof my_tab2 as $$ create table my_tab3 (a int); select * from my_tab4; $$ language sql; +ERROR: relation "my_tab4" does not exist +LINE 1: ...as $$ create table my_tab3 (a int); select * from my_tab4; $... + ^ SET check_function_bodies = false; -create function f1 () returns setof my_tab1 as $$ create table my_tab2 (a int); select * from my_tab1; $$ language sql; -ERROR: function "f1" already exists with same argument types +-- should be created since check_function_bodies is false +create function f1 () returns setof my_tab2 as $$ create table my_tab3 (a int); select * from my_tab4; $$ language sql; +-- execution would fail though select f1(); -ERROR: Unexpected response from Datanode -CONTEXT: SQL function "f1" statement 1 -SET check_function_bodies = true; +ERROR: relation "my_tab4" does not exist +LINE 1: create table my_tab3 (a int); select * from my_tab4; + ^ +QUERY: create table my_tab3 (a int); select * from my_tab4; +CONTEXT: SQL function "f1" during startup drop function f1(); +SET check_function_bodies = true; +-- check handling of multi-command statements in plpgsql block +do +$$ +declare +begin + execute 'create table my_tab6(a int); create table my_tab7(a int)'; +end +$$ language plpgsql; +-- check handling of multi-command statements in sql function +create or replace function f2() returns void as $$ create table my_tab8(a int); create table my_tab9(a int); $$ language sql; +select f2(); + f2 +---- + +(1 row) + +\d+ my_tab6 + Table "public.my_tab6" + Column | Type | Modifiers | Storage | Stats target | Description +--------+---------+-----------+---------+--------------+------------- + a | integer | | plain | | +Distribute By: HASH(a) +Location Nodes: ALL DATANODES + +\d+ my_tab7 + Table "public.my_tab7" + Column | Type | Modifiers | Storage | Stats target | Description +--------+---------+-----------+---------+--------------+------------- + a | integer | | plain | | +Distribute By: HASH(a) +Location Nodes: ALL DATANODES + +\d+ my_tab8 + Table "public.my_tab8" + Column | Type | Modifiers | Storage | Stats target | Description +--------+---------+-----------+---------+--------------+------------- + a | integer | | plain | | +Distribute By: HASH(a) +Location Nodes: ALL DATANODES + +\d+ my_tab9 + Table "public.my_tab9" + Column | Type | Modifiers | Storage | Stats target | Description +--------+---------+-----------+---------+--------------+------------- + a | integer | | plain | | +Distribute By: HASH(a) +Location Nodes: ALL DATANODES + +drop table my_tab6, my_tab7, my_tab8, my_tab9; -- Test pl-pgsql functions containing utility statements CREATE OR REPLACE FUNCTION test_fun_2() RETURNS SETOF my_tab1 AS ' DECLARE diff --git a/src/test/regress/sql/xc_misc.sql b/src/test/regress/sql/xc_misc.sql index 30db55a1a3..5b5057a440 100644 --- a/src/test/regress/sql/xc_misc.sql +++ b/src/test/regress/sql/xc_misc.sql @@ -44,16 +44,39 @@ create table my_tab1 (a int); insert into my_tab1 values(1); create function f1 () returns setof my_tab1 as $$ create table my_tab2 (a int); select * from my_tab1; $$ language sql; +select f1(); +drop function f1(); +-- fail since my_tab4 does not exist +create function f1 () returns setof my_tab2 as $$ create table my_tab3 (a int); select * from my_tab4; $$ language sql; SET check_function_bodies = false; - -create function f1 () returns setof my_tab1 as $$ create table my_tab2 (a int); select * from my_tab1; $$ language sql; - +-- should be created since check_function_bodies is false +create function f1 () returns setof my_tab2 as $$ create table my_tab3 (a int); select * from my_tab4; $$ language sql; +-- execution would fail though select f1(); +drop function f1(); SET check_function_bodies = true; -drop function f1(); +-- check handling of multi-command statements in plpgsql block +do +$$ +declare +begin + execute 'create table my_tab6(a int); create table my_tab7(a int)'; +end +$$ language plpgsql; + +-- check handling of multi-command statements in sql function +create or replace function f2() returns void as $$ create table my_tab8(a int); create table my_tab9(a int); $$ language sql; +select f2(); + +\d+ my_tab6 +\d+ my_tab7 +\d+ my_tab8 +\d+ my_tab9 + +drop table my_tab6, my_tab7, my_tab8, my_tab9; -- Test pl-pgsql functions containing utility statements |