firtst release
Revision | 90830faef0282f475b74f8f841a1415622336bb8 (tree) |
---|---|
Time | 2014-12-22 20:51:43 |
Author | Kyotaro Horiguchi <horiguchi.kyotaro@lab....> |
Commiter | Kyotaro Horiguchi |
Fixed a bug related to SQL statements in PL/pgSQL functions.
At least since 9.1, true query strings of SQL statements executed in
PL/pgSQL functions were found that were not obtained correctly by the
previous implement, it is because that PLpgSQL_stmt variable did not
have proper query string to be read for hints. Instead, it is changed
to read them from the top of error_context_stack in
pg_hint_plan_planner(). This change made a slight difference in
behavior which doesn't affect its work so a part of the regtest was
also changed. And added some regression tests for it.
Regression tests for PL/pgSQL added.
Tests for defined PL/pgSQL functions are omitted from regression
tests so added it.
@@ -8010,14 +8010,16 @@ EXPLAIN (COSTS false) SELECT id FROM t1 WHERE t1.id = 1; | ||
8010 | 8010 | Index Cond: (id = 1) |
8011 | 8011 | (2 rows) |
8012 | 8012 | |
8013 | -DO LANGUAGE plpgsql $$ | |
8013 | +-- static function | |
8014 | +CREATE FUNCTION testfunc() RETURNS RECORD AS $$ | |
8014 | 8015 | DECLARE |
8015 | - id integer; | |
8016 | + ret record; | |
8016 | 8017 | BEGIN |
8017 | - SELECT /*+SeqScan(t1)*/ t1.id INTO id FROM t1 WHERE t1.id = 1; | |
8018 | - RETURN; | |
8018 | + SELECT /*+ SeqScan(t1) */ * INTO ret FROM t1 LIMIT 1; | |
8019 | + RETURN ret; | |
8019 | 8020 | END; |
8020 | -$$; | |
8021 | +$$ LANGUAGE plpgsql; | |
8022 | +SELECT testfunc(); | |
8021 | 8023 | LOG: pg_hint_plan: |
8022 | 8024 | used hint: |
8023 | 8025 | SeqScan(t1) |
@@ -8025,8 +8027,209 @@ not used hint: | ||
8025 | 8027 | duplication hint: |
8026 | 8028 | error hint: |
8027 | 8029 | |
8028 | -CONTEXT: SQL statement "SELECT /*+SeqScan(t1)*/ t1.id FROM t1 WHERE t1.id = 1" | |
8029 | -PL/pgSQL function inline_code_block line 5 at SQL statement | |
8030 | +CONTEXT: SQL statement "SELECT /*+ SeqScan(t1) */ * FROM t1 LIMIT 1" | |
8031 | +PL/pgSQL function testfunc() line 5 at SQL statement | |
8032 | + testfunc | |
8033 | +---------- | |
8034 | + (1,1) | |
8035 | +(1 row) | |
8036 | + | |
8037 | +-- dynamic function | |
8038 | +DROP FUNCTION testfunc(); | |
8039 | +CREATE FUNCTION testfunc() RETURNS void AS $$ | |
8040 | +BEGIN | |
8041 | + EXECUTE format('/*+ SeqScan(t1) */ SELECT * FROM t1'); | |
8042 | +END; | |
8043 | +$$ LANGUAGE plpgsql; | |
8044 | +SELECT testfunc(); | |
8045 | +LOG: pg_hint_plan: | |
8046 | +used hint: | |
8047 | +SeqScan(t1) | |
8048 | +not used hint: | |
8049 | +duplication hint: | |
8050 | +error hint: | |
8051 | + | |
8052 | +CONTEXT: SQL statement "/*+ SeqScan(t1) */ SELECT * FROM t1" | |
8053 | +PL/pgSQL function testfunc() line 3 at EXECUTE statement | |
8054 | + testfunc | |
8055 | +---------- | |
8056 | + | |
8057 | +(1 row) | |
8058 | + | |
8059 | +-- This should not use SeqScan(t1) | |
8060 | +/*+ IndexScan(t1) */ SELECT * from t1 LIMIT 1; | |
8061 | +LOG: pg_hint_plan: | |
8062 | +used hint: | |
8063 | +IndexScan(t1) | |
8064 | +not used hint: | |
8065 | +duplication hint: | |
8066 | +error hint: | |
8067 | + | |
8068 | + id | val | |
8069 | +----+----- | |
8070 | + 1 | 1 | |
8071 | +(1 row) | |
8072 | + | |
8073 | +-- Perform | |
8074 | +DROP FUNCTION testfunc(); | |
8075 | +CREATE FUNCTION testfunc() RETURNS void AS $$ | |
8076 | +BEGIN | |
8077 | + PERFORM 1, /*+ SeqScan(t1) */ * from t1; | |
8078 | +END; | |
8079 | +$$ LANGUAGE plpgsql; | |
8080 | +SELECT testfunc(); | |
8081 | +LOG: pg_hint_plan: | |
8082 | +used hint: | |
8083 | +SeqScan(t1) | |
8084 | +not used hint: | |
8085 | +duplication hint: | |
8086 | +error hint: | |
8087 | + | |
8088 | +CONTEXT: SQL statement "SELECT 1, /*+ SeqScan(t1) */ * from t1" | |
8089 | +PL/pgSQL function testfunc() line 3 at PERFORM | |
8090 | + testfunc | |
8091 | +---------- | |
8092 | + | |
8093 | +(1 row) | |
8094 | + | |
8095 | +-- FOR loop | |
8096 | +DROP FUNCTION testfunc(); | |
8097 | +CREATE FUNCTION testfunc() RETURNS int AS $$ | |
8098 | +DECLARE | |
8099 | + sum int; | |
8100 | + v int; | |
8101 | +BEGIN | |
8102 | + sum := 0; | |
8103 | + FOR v IN SELECT /*+ SeqScan(t1) */ v FROM t1 ORDER BY id LOOP | |
8104 | + sum := sum + v; | |
8105 | + END LOOP; | |
8106 | + RETURN v; | |
8107 | +END; | |
8108 | +$$ LANGUAGE plpgsql; | |
8109 | +SELECT testfunc(); | |
8110 | +LOG: pg_hint_plan: | |
8111 | +used hint: | |
8112 | +SeqScan(t1) | |
8113 | +not used hint: | |
8114 | +duplication hint: | |
8115 | +error hint: | |
8116 | + | |
8117 | +CONTEXT: SQL statement "SELECT /*+ SeqScan(t1) */ v FROM t1 ORDER BY id" | |
8118 | +PL/pgSQL function testfunc() line 7 at FOR over SELECT rows | |
8119 | + testfunc | |
8120 | +---------- | |
8121 | + | |
8122 | +(1 row) | |
8123 | + | |
8124 | +-- Dynamic FOR loop | |
8125 | +DROP FUNCTION testfunc(); | |
8126 | +CREATE FUNCTION testfunc() RETURNS int AS $$ | |
8127 | +DECLARE | |
8128 | + sum int; | |
8129 | + v int; | |
8130 | + i int; | |
8131 | +BEGIN | |
8132 | + sum := 0; | |
8133 | + FOR v IN EXECUTE 'SELECT /*+ SeqScan(t1) */ val FROM t1 ORDER BY id' LOOP | |
8134 | + sum := sum + v; | |
8135 | + END LOOP; | |
8136 | + RETURN v; | |
8137 | +END; | |
8138 | +$$ LANGUAGE plpgsql; | |
8139 | +SELECT testfunc(); | |
8140 | +LOG: pg_hint_plan: | |
8141 | +used hint: | |
8142 | +SeqScan(t1) | |
8143 | +not used hint: | |
8144 | +duplication hint: | |
8145 | +error hint: | |
8146 | + | |
8147 | +CONTEXT: SQL statement "SELECT /*+ SeqScan(t1) */ val FROM t1 ORDER BY id" | |
8148 | +PL/pgSQL function testfunc() line 8 at FOR over EXECUTE statement | |
8149 | + testfunc | |
8150 | +---------- | |
8151 | + 0 | |
8152 | +(1 row) | |
8153 | + | |
8154 | +-- Cursor FOR loop | |
8155 | +DROP FUNCTION testfunc(); | |
8156 | +CREATE FUNCTION testfunc() RETURNS int AS $$ | |
8157 | +DECLARE | |
8158 | + ref CURSOR FOR SELECT /*+ SeqScan(t1) */ * FROM t1 ORDER BY id; | |
8159 | + rec record; | |
8160 | + sum int := 0; | |
8161 | +BEGIN | |
8162 | + FOR rec IN ref LOOP | |
8163 | + sum := sum + rec.val; | |
8164 | + END LOOP; | |
8165 | + RETURN sum; | |
8166 | +END; | |
8167 | +$$ LANGUAGE plpgsql; | |
8168 | +SELECT testfunc(); | |
8169 | +LOG: pg_hint_plan: | |
8170 | +used hint: | |
8171 | +SeqScan(t1) | |
8172 | +not used hint: | |
8173 | +duplication hint: | |
8174 | +error hint: | |
8175 | + | |
8176 | +CONTEXT: SQL statement "SELECT /*+ SeqScan(t1) */ * FROM t1 ORDER BY id" | |
8177 | +PL/pgSQL function testfunc() line 7 at FOR over cursor | |
8178 | + testfunc | |
8179 | +---------- | |
8180 | + 495000 | |
8181 | +(1 row) | |
8182 | + | |
8183 | +-- RETURN QUERY | |
8184 | +DROP FUNCTION testfunc(); | |
8185 | +CREATE FUNCTION testfunc() RETURNS SETOF t1 AS $$ | |
8186 | +BEGIN | |
8187 | + RETURN QUERY SELECT /*+ SeqScan(t1) */ * FROM t1 ORDER BY id; | |
8188 | +END; | |
8189 | +$$ LANGUAGE plpgsql; | |
8190 | +SELECT * FROM testfunc() LIMIT 1; | |
8191 | +LOG: pg_hint_plan: | |
8192 | +used hint: | |
8193 | +SeqScan(t1) | |
8194 | +not used hint: | |
8195 | +duplication hint: | |
8196 | +error hint: | |
8197 | + | |
8198 | +CONTEXT: SQL statement "SELECT /*+ SeqScan(t1) */ * FROM t1 ORDER BY id" | |
8199 | +PL/pgSQL function testfunc() line 3 at RETURN QUERY | |
8200 | + id | val | |
8201 | +----+----- | |
8202 | + 1 | 1 | |
8203 | +(1 row) | |
8204 | + | |
8205 | +-- Test for error exit from inner SQL statement. | |
8206 | +DROP FUNCTION testfunc(); | |
8207 | +CREATE FUNCTION testfunc() RETURNS SETOF t1 AS $$ | |
8208 | +BEGIN | |
8209 | + RETURN QUERY SELECT /*+ SeqScan(t1) */ * FROM ttx ORDER BY id; | |
8210 | +END; | |
8211 | +$$ LANGUAGE plpgsql; | |
8212 | +SELECT * FROM testfunc() LIMIT 1; | |
8213 | +ERROR: relation "ttx" does not exist | |
8214 | +LINE 1: SELECT /*+ SeqScan(t1) */ * FROM ttx ORDER BY id | |
8215 | + ^ | |
8216 | +QUERY: SELECT /*+ SeqScan(t1) */ * FROM ttx ORDER BY id | |
8217 | +CONTEXT: PL/pgSQL function testfunc() line 3 at RETURN QUERY | |
8218 | +-- this should not use SeqScan(t1) hint. | |
8219 | +/*+ IndexScan(t1) */ SELECT * from t1 LIMIT 1; | |
8220 | +LOG: pg_hint_plan: | |
8221 | +used hint: | |
8222 | +IndexScan(t1) | |
8223 | +not used hint: | |
8224 | +duplication hint: | |
8225 | +error hint: | |
8226 | + | |
8227 | + id | val | |
8228 | +----+----- | |
8229 | + 1 | 1 | |
8230 | +(1 row) | |
8231 | + | |
8232 | +DROP FUNCTION testfunc(); | |
8030 | 8233 | DROP EXTENSION pg_hint_plan; |
8031 | 8234 | -- |
8032 | 8235 | -- Rows hint tests |
@@ -4354,37 +4354,7 @@ SQL statement "SELECT /*+ IndexScan(t_1) */ nested_planner(cnt - 1) | ||
4354 | 4354 | ORDER BY t_1.c1 LIMIT 1" |
4355 | 4355 | PL/pgSQL function nested_planner(integer) line 12 at SQL statement |
4356 | 4356 | LOG: pg_hint_plan: |
4357 | -used hint: | |
4358 | -not used hint: | |
4359 | -IndexScan(t_1) | |
4360 | -duplication hint: | |
4361 | -error hint: | |
4362 | - | |
4363 | -CONTEXT: SQL statement "SELECT 0" | |
4364 | -PL/pgSQL function nested_planner(integer) line 9 at RETURN | |
4365 | -SQL statement "SELECT /*+ IndexScan(t_1) */ nested_planner(cnt - 1) FROM s1.t1 t_1 | |
4366 | - JOIN s1.t2 t_2 ON (t_1.c1 = t_2.c1) | |
4367 | - ORDER BY t_1.c1 LIMIT 1" | |
4368 | -PL/pgSQL function nested_planner(integer) line 12 at SQL statement | |
4369 | -SQL statement "SELECT /*+ IndexScan(t_1) */ nested_planner(cnt - 1) FROM s1.t1 t_1 | |
4370 | - JOIN s1.t2 t_2 ON (t_1.c1 = t_2.c1) | |
4371 | - ORDER BY t_1.c1 LIMIT 1" | |
4372 | -PL/pgSQL function nested_planner(integer) line 12 at SQL statement | |
4373 | -SQL statement "SELECT /*+ IndexScan(t_1) */ nested_planner(cnt - 1) FROM s1.t1 t_1 | |
4374 | - JOIN s1.t2 t_2 ON (t_1.c1 = t_2.c1) | |
4375 | - ORDER BY t_1.c1 LIMIT 1" | |
4376 | -PL/pgSQL function nested_planner(integer) line 12 at SQL statement | |
4377 | -SQL statement "SELECT /*+ IndexScan(t_1) */ nested_planner(cnt - 1) FROM s1.t1 t_1 | |
4378 | - JOIN s1.t2 t_2 ON (t_1.c1 = t_2.c1) | |
4379 | - ORDER BY t_1.c1 LIMIT 1" | |
4380 | -PL/pgSQL function nested_planner(integer) line 12 at SQL statement | |
4381 | -LOG: pg_hint_plan: | |
4382 | -used hint: | |
4383 | -IndexScan(t_1) | |
4384 | -not used hint: | |
4385 | -duplication hint: | |
4386 | -error hint: | |
4387 | - | |
4357 | +no hint | |
4388 | 4358 | CONTEXT: SQL statement "SELECT /*+ IndexScan(t_1) */ nested_planner(cnt - 1) FROM s1.t1 t_1 |
4389 | 4359 | JOIN s1.t2 t_2 ON (t_1.c1 = t_2.c1) |
4390 | 4360 | ORDER BY t_1.c1 LIMIT 1" |
@@ -439,7 +439,13 @@ static int debug_level = 0; | ||
439 | 439 | static int pg_hint_plan_message_level = INFO; |
440 | 440 | /* Default is off, to keep backward compatibility. */ |
441 | 441 | static bool pg_hint_plan_enable_hint_table = false; |
442 | -static bool hidestmt = false; | |
442 | + | |
443 | +/* Internal static variables. */ | |
444 | +static bool hidestmt = false; /* Allow or inhibit STATEMENT: output */ | |
445 | + | |
446 | +static int plpgsql_recurse_level = 0; /* PLpgSQL recursion level */ | |
447 | +static int hint_inhibit_level = 0; /* Inhibit hinting if this is above 0 */ | |
448 | + /* (This could not be above 1) */ | |
443 | 449 | |
444 | 450 | static const struct config_enum_entry parse_messages_level_options[] = { |
445 | 451 | {"debug", DEBUG2, true}, |
@@ -525,13 +531,6 @@ static const HintParser parsers[] = { | ||
525 | 531 | {NULL, NULL, HINT_KEYWORD_UNRECOGNIZED} |
526 | 532 | }; |
527 | 533 | |
528 | -/* | |
529 | - * PL/pgSQL plugin for retrieving string representation of each query during | |
530 | - * function execution. | |
531 | - */ | |
532 | -static const char *plpgsql_query_string = NULL; | |
533 | -static enum PLpgSQL_stmt_types plpgsql_query_string_src; | |
534 | - | |
535 | 534 | PLpgSQL_plugin plugin_funcs = { |
536 | 535 | NULL, |
537 | 536 | NULL, |
@@ -542,9 +541,6 @@ PLpgSQL_plugin plugin_funcs = { | ||
542 | 541 | NULL, |
543 | 542 | }; |
544 | 543 | |
545 | -/* Current nesting depth of SPI calls, used to prevent recursive calls */ | |
546 | -static int nested_level = 0; | |
547 | - | |
548 | 544 | /* |
549 | 545 | * Module load callbacks |
550 | 546 | */ |
@@ -1597,7 +1593,7 @@ get_hints_from_table(const char *client_query, const char *client_application) | ||
1597 | 1593 | |
1598 | 1594 | PG_TRY(); |
1599 | 1595 | { |
1600 | - ++nested_level; | |
1596 | + hint_inhibit_level++; | |
1601 | 1597 | |
1602 | 1598 | SPI_connect(); |
1603 | 1599 |
@@ -1635,11 +1631,11 @@ get_hints_from_table(const char *client_query, const char *client_application) | ||
1635 | 1631 | |
1636 | 1632 | SPI_finish(); |
1637 | 1633 | |
1638 | - --nested_level; | |
1634 | + hint_inhibit_level--; | |
1639 | 1635 | } |
1640 | 1636 | PG_CATCH(); |
1641 | 1637 | { |
1642 | - --nested_level; | |
1638 | + hint_inhibit_level--; | |
1643 | 1639 | PG_RE_THROW(); |
1644 | 1640 | } |
1645 | 1641 | PG_END_TRY(); |
@@ -1655,15 +1651,21 @@ get_query_string(void) | ||
1655 | 1651 | { |
1656 | 1652 | const char *p; |
1657 | 1653 | |
1658 | - if (stmt_name) | |
1654 | + if (plpgsql_recurse_level > 0) | |
1655 | + { | |
1656 | + /* | |
1657 | + * This is quite ugly but this is the only point I could find where | |
1658 | + * we can get the query string. | |
1659 | + */ | |
1660 | + p = (char*)error_context_stack->arg; | |
1661 | + } | |
1662 | + else if (stmt_name) | |
1659 | 1663 | { |
1660 | 1664 | PreparedStatement *entry; |
1661 | 1665 | |
1662 | 1666 | entry = FetchPreparedStatement(stmt_name, true); |
1663 | 1667 | p = entry->plansource->query_string; |
1664 | 1668 | } |
1665 | - else if (plpgsql_query_string) | |
1666 | - p = plpgsql_query_string; | |
1667 | 1669 | else |
1668 | 1670 | p = debug_query_string; |
1669 | 1671 |
@@ -2326,7 +2328,7 @@ pg_hint_plan_ProcessUtility(Node *parsetree, const char *queryString, | ||
2326 | 2328 | * Use standard planner if pg_hint_plan is disabled or current nesting |
2327 | 2329 | * depth is nesting depth of SPI calls. |
2328 | 2330 | */ |
2329 | - if (!pg_hint_plan_enable_hint || nested_level > 0) | |
2331 | + if (!pg_hint_plan_enable_hint || hint_inhibit_level > 0) | |
2330 | 2332 | { |
2331 | 2333 | if (debug_level > 1) |
2332 | 2334 | ereport(pg_hint_plan_message_level, |
@@ -2488,13 +2490,13 @@ pg_hint_plan_planner(Query *parse, int cursorOptions, ParamListInfo boundParams) | ||
2488 | 2490 | * depth is nesting depth of SPI calls. Other hook functions try to change |
2489 | 2491 | * plan with current_hint if any, so set it to NULL. |
2490 | 2492 | */ |
2491 | - if (!pg_hint_plan_enable_hint || nested_level > 0) | |
2493 | + if (!pg_hint_plan_enable_hint || hint_inhibit_level > 0) | |
2492 | 2494 | { |
2493 | 2495 | if (debug_level > 1) |
2494 | 2496 | elog(pg_hint_plan_message_level, |
2495 | 2497 | "pg_hint_plan%s: planner: enable_hint=%d," |
2496 | - " nested_level=%d", | |
2497 | - qnostr, pg_hint_plan_enable_hint, nested_level); | |
2498 | + " hint_inhibit_level=%d", | |
2499 | + qnostr, pg_hint_plan_enable_hint, hint_inhibit_level); | |
2498 | 2500 | hidestmt = true; |
2499 | 2501 | |
2500 | 2502 | goto standard_planner_proc; |
@@ -3095,18 +3097,17 @@ pg_hint_plan_get_relation_info(PlannerInfo *root, Oid relationObjectId, | ||
3095 | 3097 | * Do nothing if we don't have a valid hint in this context or current |
3096 | 3098 | * nesting depth is at SPI calls. |
3097 | 3099 | */ |
3098 | - | |
3099 | - if (!current_hint || nested_level > 0) | |
3100 | + if (!current_hint || hint_inhibit_level > 0) | |
3100 | 3101 | { |
3101 | 3102 | if (debug_level > 1) |
3102 | 3103 | ereport(pg_hint_plan_message_level, |
3103 | 3104 | (errhidestmt(true), |
3104 | 3105 | errmsg ("pg_hint_plan%s: get_relation_info" |
3105 | 3106 | " no hint to apply: relation=%u(%s), inhparent=%d," |
3106 | - " current_hint=%p, nested_level=%d", | |
3107 | + " current_hint=%p, hint_inhibit_level=%d", | |
3107 | 3108 | qnostr, relationObjectId, |
3108 | 3109 | get_rel_name(relationObjectId), |
3109 | - inhparent, current_hint, nested_level))); | |
3110 | + inhparent, current_hint, hint_inhibit_level))); | |
3110 | 3111 | return; |
3111 | 3112 | } |
3112 | 3113 |
@@ -3124,10 +3125,10 @@ pg_hint_plan_get_relation_info(PlannerInfo *root, Oid relationObjectId, | ||
3124 | 3125 | (errhidestmt(true), |
3125 | 3126 | errmsg ("pg_hint_plan%s: get_relation_info" |
3126 | 3127 | " skipping inh parent: relation=%u(%s), inhparent=%d," |
3127 | - " current_hint=%p, nested_level=%d", | |
3128 | + " current_hint=%p, hint_inhibit_level=%d", | |
3128 | 3129 | qnostr, relationObjectId, |
3129 | 3130 | get_rel_name(relationObjectId), |
3130 | - inhparent, current_hint, nested_level))); | |
3131 | + inhparent, current_hint, hint_inhibit_level))); | |
3131 | 3132 | return; |
3132 | 3133 | } |
3133 | 3134 |
@@ -3226,10 +3227,10 @@ pg_hint_plan_get_relation_info(PlannerInfo *root, Oid relationObjectId, | ||
3226 | 3227 | errmsg("pg_hint_plan%s: get_relation_info:" |
3227 | 3228 | " index deletion by parent hint: " |
3228 | 3229 | "relation=%u(%s), inhparent=%d, current_hint=%p," |
3229 | - " nested_level=%d", | |
3230 | + " hint_inhibit_level=%d", | |
3230 | 3231 | qnostr, relationObjectId, |
3231 | 3232 | get_rel_name(relationObjectId), |
3232 | - inhparent, current_hint, nested_level))); | |
3233 | + inhparent, current_hint, hint_inhibit_level))); | |
3233 | 3234 | return; |
3234 | 3235 | } |
3235 | 3236 |
@@ -3247,10 +3248,10 @@ pg_hint_plan_get_relation_info(PlannerInfo *root, Oid relationObjectId, | ||
3247 | 3248 | errmsg ("pg_hint_plan%s: get_relation_info" |
3248 | 3249 | " index deletion:" |
3249 | 3250 | " relation=%u(%s), inhparent=%d, current_hint=%p," |
3250 | - " nested_level=%d, scanmask=0x%x", | |
3251 | + " hint_inhibit_level=%d, scanmask=0x%x", | |
3251 | 3252 | qnostr, relationObjectId, |
3252 | 3253 | get_rel_name(relationObjectId), |
3253 | - inhparent, current_hint, nested_level, | |
3254 | + inhparent, current_hint, hint_inhibit_level, | |
3254 | 3255 | hint->enforce_mask))); |
3255 | 3256 | } |
3256 | 3257 | else |
@@ -3261,10 +3262,10 @@ pg_hint_plan_get_relation_info(PlannerInfo *root, Oid relationObjectId, | ||
3261 | 3262 | errmsg ("pg_hint_plan%s: get_relation_info" |
3262 | 3263 | " no hint applied:" |
3263 | 3264 | " relation=%u(%s), inhparent=%d, current_hint=%p," |
3264 | - " nested_level=%d, scanmask=0x%x", | |
3265 | + " hint_inhibit_level=%d, scanmask=0x%x", | |
3265 | 3266 | qnostr, relationObjectId, |
3266 | 3267 | get_rel_name(relationObjectId), |
3267 | - inhparent, current_hint, nested_level, | |
3268 | + inhparent, current_hint, hint_inhibit_level, | |
3268 | 3269 | current_hint->init_scan_mask))); |
3269 | 3270 | set_scan_config_options(current_hint->init_scan_mask, |
3270 | 3271 | current_hint->context); |
@@ -3934,7 +3935,7 @@ pg_hint_plan_join_search(PlannerInfo *root, int levels_needed, | ||
3934 | 3935 | * valid hint is supplied or current nesting depth is nesting depth of SPI |
3935 | 3936 | * calls. |
3936 | 3937 | */ |
3937 | - if (!current_hint || nested_level > 0) | |
3938 | + if (!current_hint || hint_inhibit_level > 0) | |
3938 | 3939 | { |
3939 | 3940 | if (prev_join_search) |
3940 | 3941 | return (*prev_join_search) (root, levels_needed, initial_rels); |
@@ -4030,48 +4031,7 @@ set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, | ||
4030 | 4031 | static void |
4031 | 4032 | pg_hint_plan_plpgsql_stmt_beg(PLpgSQL_execstate *estate, PLpgSQL_stmt *stmt) |
4032 | 4033 | { |
4033 | - PLpgSQL_expr *expr = NULL; | |
4034 | - | |
4035 | - switch ((enum PLpgSQL_stmt_types) stmt->cmd_type) | |
4036 | - { | |
4037 | - case PLPGSQL_STMT_FORS: | |
4038 | - expr = ((PLpgSQL_stmt_fors *) stmt)->query; | |
4039 | - break; | |
4040 | - case PLPGSQL_STMT_FORC: | |
4041 | - expr = ((PLpgSQL_var *) (estate->datums[((PLpgSQL_stmt_forc *)stmt)->curvar]))->cursor_explicit_expr; | |
4042 | - break; | |
4043 | - case PLPGSQL_STMT_RETURN_QUERY: | |
4044 | - if (((PLpgSQL_stmt_return_query *) stmt)->query != NULL) | |
4045 | - expr = ((PLpgSQL_stmt_return_query *) stmt)->query; | |
4046 | - else | |
4047 | - expr = ((PLpgSQL_stmt_return_query *) stmt)->dynquery; | |
4048 | - break; | |
4049 | - case PLPGSQL_STMT_EXECSQL: | |
4050 | - expr = ((PLpgSQL_stmt_execsql *) stmt)->sqlstmt; | |
4051 | - break; | |
4052 | - case PLPGSQL_STMT_DYNEXECUTE: | |
4053 | - expr = ((PLpgSQL_stmt_dynexecute *) stmt)->query; | |
4054 | - break; | |
4055 | - case PLPGSQL_STMT_DYNFORS: | |
4056 | - expr = ((PLpgSQL_stmt_dynfors *) stmt)->query; | |
4057 | - break; | |
4058 | - case PLPGSQL_STMT_OPEN: | |
4059 | - if (((PLpgSQL_stmt_open *) stmt)->query != NULL) | |
4060 | - expr = ((PLpgSQL_stmt_open *) stmt)->query; | |
4061 | - else if (((PLpgSQL_stmt_open *) stmt)->dynquery != NULL) | |
4062 | - expr = ((PLpgSQL_stmt_open *) stmt)->dynquery; | |
4063 | - else | |
4064 | - expr = ((PLpgSQL_var *) (estate->datums[((PLpgSQL_stmt_open *)stmt)->curvar]))->cursor_explicit_expr; | |
4065 | - break; | |
4066 | - default: | |
4067 | - break; | |
4068 | - } | |
4069 | - | |
4070 | - if (expr) | |
4071 | - { | |
4072 | - plpgsql_query_string = expr->query; | |
4073 | - plpgsql_query_string_src = (enum PLpgSQL_stmt_types) stmt->cmd_type; | |
4074 | - } | |
4034 | + plpgsql_recurse_level++; | |
4075 | 4035 | } |
4076 | 4036 | |
4077 | 4037 | /* |
@@ -4082,9 +4042,7 @@ pg_hint_plan_plpgsql_stmt_beg(PLpgSQL_execstate *estate, PLpgSQL_stmt *stmt) | ||
4082 | 4042 | static void |
4083 | 4043 | pg_hint_plan_plpgsql_stmt_end(PLpgSQL_execstate *estate, PLpgSQL_stmt *stmt) |
4084 | 4044 | { |
4085 | - if (plpgsql_query_string && | |
4086 | - plpgsql_query_string_src == stmt->cmd_type) | |
4087 | - plpgsql_query_string = NULL; | |
4045 | + plpgsql_recurse_level--; | |
4088 | 4046 | } |
4089 | 4047 | |
4090 | 4048 | void plpgsql_query_erase_callback(ResourceReleasePhase phase, |
@@ -4094,8 +4052,8 @@ void plpgsql_query_erase_callback(ResourceReleasePhase phase, | ||
4094 | 4052 | { |
4095 | 4053 | if (phase != RESOURCE_RELEASE_AFTER_LOCKS) |
4096 | 4054 | return; |
4097 | - /* Force erase stored plpgsql query string */ | |
4098 | - plpgsql_query_string = NULL; | |
4055 | + /* Cancel plpgsql nest level*/ | |
4056 | + plpgsql_recurse_level = 0; | |
4099 | 4057 | } |
4100 | 4058 | |
4101 | 4059 | #define standard_join_search pg_hint_plan_standard_join_search |
@@ -836,14 +836,110 @@ VACUUM ANALYZE hint_plan.hints; | ||
836 | 836 | |
837 | 837 | -- plpgsql test |
838 | 838 | EXPLAIN (COSTS false) SELECT id FROM t1 WHERE t1.id = 1; |
839 | -DO LANGUAGE plpgsql $$ | |
839 | + | |
840 | +-- static function | |
841 | +CREATE FUNCTION testfunc() RETURNS RECORD AS $$ | |
842 | +DECLARE | |
843 | + ret record; | |
844 | +BEGIN | |
845 | + SELECT /*+ SeqScan(t1) */ * INTO ret FROM t1 LIMIT 1; | |
846 | + RETURN ret; | |
847 | +END; | |
848 | +$$ LANGUAGE plpgsql; | |
849 | +SELECT testfunc(); | |
850 | + | |
851 | +-- dynamic function | |
852 | +DROP FUNCTION testfunc(); | |
853 | +CREATE FUNCTION testfunc() RETURNS void AS $$ | |
854 | +BEGIN | |
855 | + EXECUTE format('/*+ SeqScan(t1) */ SELECT * FROM t1'); | |
856 | +END; | |
857 | +$$ LANGUAGE plpgsql; | |
858 | +SELECT testfunc(); | |
859 | + | |
860 | +-- This should not use SeqScan(t1) | |
861 | +/*+ IndexScan(t1) */ SELECT * from t1 LIMIT 1; | |
862 | + | |
863 | +-- Perform | |
864 | +DROP FUNCTION testfunc(); | |
865 | +CREATE FUNCTION testfunc() RETURNS void AS $$ | |
866 | +BEGIN | |
867 | + PERFORM 1, /*+ SeqScan(t1) */ * from t1; | |
868 | +END; | |
869 | +$$ LANGUAGE plpgsql; | |
870 | +SELECT testfunc(); | |
871 | + | |
872 | +-- FOR loop | |
873 | +DROP FUNCTION testfunc(); | |
874 | +CREATE FUNCTION testfunc() RETURNS int AS $$ | |
875 | +DECLARE | |
876 | + sum int; | |
877 | + v int; | |
878 | +BEGIN | |
879 | + sum := 0; | |
880 | + FOR v IN SELECT /*+ SeqScan(t1) */ v FROM t1 ORDER BY id LOOP | |
881 | + sum := sum + v; | |
882 | + END LOOP; | |
883 | + RETURN v; | |
884 | +END; | |
885 | +$$ LANGUAGE plpgsql; | |
886 | +SELECT testfunc(); | |
887 | + | |
888 | +-- Dynamic FOR loop | |
889 | +DROP FUNCTION testfunc(); | |
890 | +CREATE FUNCTION testfunc() RETURNS int AS $$ | |
840 | 891 | DECLARE |
841 | - id integer; | |
892 | + sum int; | |
893 | + v int; | |
894 | + i int; | |
895 | +BEGIN | |
896 | + sum := 0; | |
897 | + FOR v IN EXECUTE 'SELECT /*+ SeqScan(t1) */ val FROM t1 ORDER BY id' LOOP | |
898 | + sum := sum + v; | |
899 | + END LOOP; | |
900 | + RETURN v; | |
901 | +END; | |
902 | +$$ LANGUAGE plpgsql; | |
903 | +SELECT testfunc(); | |
904 | + | |
905 | +-- Cursor FOR loop | |
906 | +DROP FUNCTION testfunc(); | |
907 | +CREATE FUNCTION testfunc() RETURNS int AS $$ | |
908 | +DECLARE | |
909 | + ref CURSOR FOR SELECT /*+ SeqScan(t1) */ * FROM t1 ORDER BY id; | |
910 | + rec record; | |
911 | + sum int := 0; | |
912 | +BEGIN | |
913 | + FOR rec IN ref LOOP | |
914 | + sum := sum + rec.val; | |
915 | + END LOOP; | |
916 | + RETURN sum; | |
917 | +END; | |
918 | +$$ LANGUAGE plpgsql; | |
919 | +SELECT testfunc(); | |
920 | + | |
921 | +-- RETURN QUERY | |
922 | +DROP FUNCTION testfunc(); | |
923 | +CREATE FUNCTION testfunc() RETURNS SETOF t1 AS $$ | |
842 | 924 | BEGIN |
843 | - SELECT /*+SeqScan(t1)*/ t1.id INTO id FROM t1 WHERE t1.id = 1; | |
844 | - RETURN; | |
925 | + RETURN QUERY SELECT /*+ SeqScan(t1) */ * FROM t1 ORDER BY id; | |
845 | 926 | END; |
846 | -$$; | |
927 | +$$ LANGUAGE plpgsql; | |
928 | +SELECT * FROM testfunc() LIMIT 1; | |
929 | + | |
930 | +-- Test for error exit from inner SQL statement. | |
931 | +DROP FUNCTION testfunc(); | |
932 | +CREATE FUNCTION testfunc() RETURNS SETOF t1 AS $$ | |
933 | +BEGIN | |
934 | + RETURN QUERY SELECT /*+ SeqScan(t1) */ * FROM ttx ORDER BY id; | |
935 | +END; | |
936 | +$$ LANGUAGE plpgsql; | |
937 | +SELECT * FROM testfunc() LIMIT 1; | |
938 | + | |
939 | +-- this should not use SeqScan(t1) hint. | |
940 | +/*+ IndexScan(t1) */ SELECT * from t1 LIMIT 1; | |
941 | + | |
942 | +DROP FUNCTION testfunc(); | |
847 | 943 | DROP EXTENSION pg_hint_plan; |
848 | 944 | |
849 | 945 | -- |