pgpool2.git / summary / log / commit / refs

commit    5836bc5565619001ffb79cc96890b7de32039637
Author:   Tatsuo Ishii <ishii at sraoss.co.jp>
Date:     Sun Aug 01 08:38:17 2010 +0000

    Allow to specify functions those are writing to DB in SELECT.
    For this purpose, new directive "white_function_lis" and
    "black_function_list" added.
    Such SELECT are not load balanced in replication mode and
    master slave mode.


doc/pgpool-ja.html | 73 ++++++++++++++++++++++++++++++----- pgpool.conf.sample | 22 +++++++++++ pgpool.conf.sample-master-slave | 22 +++++++++++ pgpool.conf.sample-replication | 22 +++++++++++ pgpool.conf.sample-stream | 22 +++++++++++ pool_config.c | 74 ++++++++++++++++++++++++++++++++--- pool_config.h | 5 ++- pool_config.l | 74 ++++++++++++++++++++++++++++++++--- pool_query_context.c | 9 +++++ pool_select_walker.c | 85 ++++++++++++++++++++++++++++++++++++++++- pool_select_walker.h | 1 + 11 files changed, 385 insertions(+), 24 deletions(-) diff --git a/doc/pgpool-ja.html b/doc/pgpool-ja.html index 65694a6ca..8d8485f8d 100644 --- a/doc/pgpool-ja.html +++ b/doc/pgpool-ja.html @@ -8,7 +8,7 @@ <body> <!-- hhmts start --> -Last modified: Wed Jul 28 16:28:54 JST 2010 +Last modified: Sun Aug 1 17:11:12 JST 2010 <!-- hhmts end --> <body bgcolor="#ffffff"> @@ -837,7 +837,7 @@ raw$B%b!<%I$HF1MM$NF0:n$r$7$^$9!#(B <h1><a name="replication_mode"></a>$B%l%W%j%1!<%7%g%s%b!<%I(B</h1> <p> -$B%l%W%j%1!<%7%g%s$rM-8z$K$9$k%b!<%I$G$9!#(B +$B%l%W%j%1!<%7%g%s$rM-8z$K$9$k%b!<%I$G$9(B($B@_Dj%U%!%$%k$N?w7A$O(Bpgpool.conf-replication)$B!#(B raw$B%b!<%I!"%3%M%/%7%g%s%W!<%k%b!<%I$K2C$(!"0J2<$r@_Dj$7$^$9!#(B </p> @@ -879,6 +879,40 @@ raw$B%b!<%I!"%3%M%/%7%g%s%W!<%k%b!<%I$K2C$(!"0J2<$r@_Dj$7$^$9!#(B $B%G%U%)%k%HCM$O(Bfalse$B$G$9!#(B </p> +<a name="white_function_list"></a> +<dt>white_function_list +<dd> +<p> +$B%G!<%?%Y!<%9$KBP$7$F(B<strong>$B99?7$r9T$J$o$J$$4X?tL>(B</strong>$B$r%3%s%^6h@Z$j$G;XDj$7$^$9!#(B +$B$3$N%j%9%H$K4^$^$l$J$$4X?t8F$S=P$7$r4^$`(BSELECT$B$O!"Ii2YJ,;6$NBP>]$H$O$J$i$:!"%l%W%j%1!<%7%g%s%b!<%I$K$*$$$F$O$9$Y$F$N(BDB$B%N!<%I$G<B9T$5$l$^$9!#(B +($B%^%9%?!<%9%l!<%V%b!<%I$K$*$$$F$O!"%^%9%?!<(B(primary)DB$B%N!<%I$K$N$_Aw?.$5$l$^$9(B)$B!#(B +</p> + +<a name="black_function_list"></a> +<dt>black_function_list +<dd> +<p> +$B%G!<%?%Y!<%9$KBP$7$F(B<strong>$B99?7$r9T$J$&4X?tL>(B</strong>$B$r%3%s%^6h@Z$j$G;XDj$7$^$9!#(B +$B$3$N%j%9%H$K4^$^$l$k4X?t8F$S=P$7$r4^$`(BSELECT$B$O!"Ii2YJ,;6$NBP>]$H$O$J$i$:!"%l%W%j%1!<%7%g%s%b!<%I$K$*$$$F$O$9$Y$F$N(BDB$B%N!<%I$G<B9T$5$l$^$9!#(B +$B$3$N%j%9%H$K4^$^$l$J$$4X?t8F$S=P$7$r4^$`(BSELECT$B$O!"Ii2YJ,;6$NBP>]$H$J$j$^$9!#(B +</p> +<p> +white_function_list$B$H(Bblack_function_list$B$NN>J}$r6u0J30$K$9$k$3$H$O$G$-$^$;$s!#(B +$B$I$A$i$+0lJ}$N$_$K4X?tL>$r;XDj$7$^$9!#(B +</p> +<p> +pgpool-II 3.0$B$h$jA0$N%P!<%8%g%s$G$O!"8GDj$G(Bnextval$B$H(Bsetval$B$,=q$-9~$_$r9T$J$&4X?t$H$7$FG'<1$5$l$F$$$^$7$?!#(B +$B$=$l$HF1$8F0:n$r9T$J$o$;$k$K$O!"0J2<$N$h$&$K(Bwhite_function_list$B$H(Bblack_function_list$B$r;XDj$7$^$9!#(B +</p> +<pre> +white_function_list = '' +black_function_list = 'upper,lower' +</pre> + +<dt>replicate_select +<dd> +<p> + <a name="replicate_select"> <dt>replicate_select <dd> @@ -1170,7 +1204,7 @@ load_balance_mode = true $B$r@_Dj$7$?>l9g!"0J2<$N>r7o$N$9$Y$F$rK~$?$7$?;~$K(BS <ul> <li>PostgreSQL$B$N%P!<%8%g%s$,(B7.4$B0J9_$G$"$k(B <li>$BLd$$9g$o$;$,L@<(E*$J%H%i%s%/%6%7%g%s%V%m%C%/$NFbB&$K$J$$(B($B$D$^$j!"(BBEGIN$B$rH/9T$7$F$$$J$$(B) -<li>SELECT$BJ8(B(WITH$BIU$b4^$`!#(BSELECT nextval $B$H(B SELECT setval $B$O=|$/(B)$B$^$?$O(BCOPY TO STDOUT, EXPLAIN, EXPLAIN ANALYZE SELECT... $B$N$$$:$l$+$G$"$k(B +<li>SELECT$BJ8(B(WITH$BIU$b4^$`!#(Bblack_list$B$^$?$O(Bwhite_list$B$G;XDj$5$l$?=q$-9~$_$r4^$`(BSELECT$B$r=|$/(B)$B$^$?$O(BCOPY TO STDOUT, EXPLAIN, EXPLAIN ANALYZE SELECT... $B$N$$$:$l$+$G$"$k(B <li>SELECT INTO $BJ8$G$O$J$$(B <li>SELECT FOR UPDATE/SELECT FOR SHARE$BJ8$G$O$J$$(B <li>$B%/%(%jJ8;zNs$,(B SELECT$B$^$?$O(BWITH $B$G;O$^$k(B(ignore_leading_white_space = true$B$N>l9g$O6uGr$OL5;k$7$^$9(B) @@ -1227,13 +1261,10 @@ true$B$J$i$P!"%U%'%$%k%*!<%P$,5/$-$^$9!#$=$N:]!"0J2<$N$h$&$J%(%i!<%a%C%;!<%8$,I <h1><a name="master_slave_mode"></a>$B%^%9%?!<%9%l!<%V%b!<%I(B</h1> <p> - master/slave$B%b!<%I$O!"(BSlony-I$B$d(BStreaming Replication$B$N$h$&$J!"(Bmaster/slave$B<0$N%l%W%j%1!<%7%g(B - $B%s%=%U%H$K%l%W%j%1!<%7%g%s$r$^$+$;$k%b!<%I$G$9!#$3$N%b!<%I$G;H$&$?$a$K(B - $B$O!"%l%W%j%1!<%7%g%s%b!<%I$HF1$8$h$&$K!"(BDB$B%N!<%I$N%[%9%H>pJs(B - $B$r%;%C%H$7!"(Bmaster_slave_mode$B$H(Bload_balance_mode$B$r(Btrue$B$K$7$^$9!#$3$N$H(B - $B$-!"Ld$$9g$o$;$K$h$C$F%^%9%?!<(BDB$B$@$1$KLd$$9g$o$;$,Aw$i$l$k>l9g$H!"(BDB - $B%N!<%I$N4V$G%m!<%I%P%i%s%9$5$l$FLd$$9g$o$;$,Aw$i$l$k>l9g$,$"$j$^$9!#(B - $B%m!<%I%P%i%s%9$N>r7o$O%l%W%j%1!<%7%g%s%b!<%I$HF1$8$G$9!#(B + master/slave$B%b!<%I$O!"(BSlony-I$B$d(BStreaming Replication$B$N$h$&$J!"(Bmaster/slave$B<0$N%l%W%j%1!<%7%g%s%=%U%H$K%l%W%j%1!<%7%g%s$r$^$+$;$k%b!<%I$G$9!#(B +$B$3$N%b!<%I$G;H$&$?$a$K$O!"%l%W%j%1!<%7%g%s%b!<%I$HF1$8$h$&$K!"(BDB$B%N!<%I$N%[%9%H>pJs$r%;%C%H$7!"(Bmaster_slave_mode$B$H(Bload_balance_mode$B$r(Btrue$B$K$7$^$9!#(B +$B$3$N$H$-!"Ld$$9g$o$;$K$h$C$F%^%9%?!<(BDB$B$@$1$KLd$$9g$o$;$,Aw$i$l$k>l9g$H!"(BDB$B%N!<%I$N4V$G%m!<%I%P%i%s%9$5$l$FLd$$9g$o$;$,Aw$i$l$k>l9g$,$"$j$^$9!#(B +$B%m!<%I%P%i%s%9$N>r7o$O%l%W%j%1!<%7%g%s%b!<%I$HF1$8$G$9!#(B </p> <p> @@ -1245,10 +1276,16 @@ true$B$J$i$P!"%U%'%$%k%*!<%P$,5/$-$^$9!#$=$N:]!"0J2<$N$h$&$J%(%i!<%a%C%;!<%8$,I $B$^$?!"(B'master_slave_sub_mode'$B$r;XDj$7$^$9!#$3$l$O!"(B'slony'($B%G%U%)%k%H(B)$B$+!"(B'stream'$B$G$9!#(B 'slony'$B$O(BSlony-I$B$rMxMQ$9$k;~$K;XDj$7$^$9!#(B 'stream'$B$O!"(BPostgreSQL$BAH$_9~$_$N(BStreaming Replication$B$rMxMQ$9$k$H$-$K;XDj$7$^$9!#(B +Slony-I$B$r;H$&>l9g$N@_Dj%U%!%$%k$N?w7A$O(Bpgpool.conf.sample-master-slave$B$G$9!#(B +Streaming Replication$B$r;H$&>l9g$N?w7A$O(B@gpool.con.sample-stream$B$G$9!#(B </p> <p> $B$3$N%Q%i%a!<%?$rJQ99$7$?;~$K$O(B pgpool-II $B$r:F5/F0$7$F$/$@$5$$!#(B </p> +<p> +$B%^%9%?!<%9%l!<%V%b!<%I$G$b!"(BDB$B=q$-9~$_$r9T$J$&4X?t$N8F$S=P$7$r4^$`(BSELECT$B$rIi2YJ,;6$NBP>]$+$i30$9;XDj$r(Bwhite_function_list$B$H(Bblack_function_list$B$G9T$J$&$3$H$,$G$-$^$9!#(B +$B>\:Y$O(B<a href="#white_function_list">white_function_list</a>$B$N9`$r$4Mw2<$5$$!#(B +</p> <h1><a name="stream">Streaming Replication$B$X$NBP1~(B</h1> <p> @@ -3455,6 +3492,18 @@ pgpool-II$B$N%A%e!<%H%j%"%k$O(B<a href="tutorial-ja.html">$B$3$3(B<a/>$B$K$ $B$3$N%P!<%8%g%s$O(B3.0$B7ONs$N:G=i$NHG$G!"(B2.2$B7O$d(B2.3$B7O$+$i$N!V%a%8%c!<%P!<%8%g%s%"%C%W!W$K$"$?$j$^$9!#(B PostgreSQL 9.0$B$N?75!G=$G$"$k(BStreaming Replication/Hot Standby$B9=@.$KBP1~$9$k$J$I!"B?$/$N5!G=$,DI2C$5$l$k$H6&$K!"FbIt9=B$$,@0M}$5$l$F8+DL$7$,NI$/$J$C$FJ]<i@-$,8~>e$7$F$$$^$9!#(B </p> +<p> +$B%^%9%?!<%9%l!<%V%b!<%IA4HL$GB?$/$N2~A1$,$J$5$l$F$$$^$9!#(B +<ul> + <li>$BITI,MW$J(BDB$B%N!<%I$K(Bparse/bind$B%a%C%;!<%8$,Aw$i$l$J$/$J$j%m%C%/6%9g$,8:$j$^$7$?(B + <li>$BITI,MW$JFbIt%H%i%s%6%/%7%g%s$N5/F0$,$J$/$J$j!"%*!<%P%X%C%I$,7Z8:$7$F$$$^$9(B + <li>$B0l;~%F!<%V%k$r0U<1$;$:$K0BA4$K;H$($k$h$&$K$J$j$^$7$?(B + <li>$B=q$-9~$_$rH<$&4X?t8F$S=P$7$r9T$J$&(BSELECT$B$r%^%9%?!<(B(primay)$B$G$N$_<B9T$9$k$h$&$K@)8f$G$-$k$h$&$K$J$j$^$7$?(B +</ul> +</p> +<p> +$B%l%W%j%1!<%7%g%s%b!<%I$K$*$$$F$b!"=q$-9~$_$rH<$&4X?t8F$S=P$7$r9T$J$&(BSELECT$B$rIi2YJ,;6$9$k$+$I$&$+$N@)8f$G$-$k$h$&$K$J$k$J$I$N2~NI$,2C$($i$l$F$$$^$9!#(B +</p> <h3>$B?75!G=(B</h3> <p> @@ -3477,6 +3526,10 @@ pgpool-II$B$O4pK\E*$K$O(Bmaster/slave mode$B$H$7$FF0:n$7$^$9$,!"$=$N:]$K(B " </ul> + <li>$B4X?t8F$S=P$7$rH<$&(BSELECT$B$r@)8f$9$k@_Dj9`L\(Bwhite_function_list$B$H(Bback_function_list$B$,DI2C$5$l$^$7$?(B(Tatsuo) + + <li>$B%^%9%?!<%9%l!<%V%b!<%I$K$*$$$F!"0l;~%F!<%V%k$r8!:w$9$k(BSELECT$B$O!"@09g@-$rJ]$D$?$a$K>o$K%^%9%?!<(B(primary)$B$G<B9T$5$l$k$h$&$K$J$j$^$7$?(B(Tatsuo) + <li>$B%^%9%?%9%l!<%V%b!<%I$G!"L@<(E*$J%H%i%s%6%/%7%g%sFb$G<B9T$5$l$J$$(BSQL$B%3%^%s%I$K$*$$$F!"<+F0E*$K%H%i%s%6%/%7%g%s$r3+;O$9$k$3$H$r;_$a$^$7$?!#$3$l$OITI,MW$G$7$?!#$3$l$K$h$C$F!"%Q%U%)!<%^%s%9$,8~>e$7$F$$$^$9(B(Tatsuo) <li>$B%^%9%?%9%l!<%V%b!<%I$G!"L@<(E*$J%H%i%s%6%/%7%g%sFb$G<B9T$5$l$k(BSELECT$B%3%^%s%I$,Ii2YJ,;6$G$-$k$h$&$K$J$j$^$7$?(B(Tatsuo, Kitagawa) diff --git a/pgpool.conf.sample b/pgpool.conf.sample index 3d2dd54d0..0cd035d65 100644 --- a/pgpool.conf.sample +++ b/pgpool.conf.sample @@ -87,6 +87,28 @@ reset_query_list = 'ABORT; DISCARD ALL' # for 8.2 or older this should be as follows. #reset_query_list = 'ABORT; RESET ALL; SET SESSION AUTHORIZATION DEFAULT' +# white_function_list is a comma separated list of function names +# those do not write to database. Any functions not listed here +# are regarded to write to database and SELECTs including such +# writer-functions will be executed on master(primary) in master/slave +# mode, or executed on all DB nodes in replication mode. +# +# black_function_list is a comma separated list of function names +# those write to database. Any functions not listed here +# are regarded not to write to database and SELECTs including such +# read-only-functions will be executed on any DB nodes. +# +# You cannot make full both white_function_list and +# black_function_list at the same time. If you specify something in +# one of them, you should make empty other. +# +# Pre 3.0 pgpool-II recognizes nextval and setval in hard coded +# way. Following setting will do the same as the previous version. +# white_function_list = '' +# black_function_list = 'nextval,setval' +white_function_list = '' +black_function_list = 'nextval,setval' + # If true print timestamp on each log line. print_timestamp = true diff --git a/pgpool.conf.sample-master-slave b/pgpool.conf.sample-master-slave index 341932a83..2a9231b78 100644 --- a/pgpool.conf.sample-master-slave +++ b/pgpool.conf.sample-master-slave @@ -86,6 +86,28 @@ reset_query_list = 'ABORT; DISCARD ALL' # for 8.2 or older this should be as follows. #reset_query_list = 'ABORT; RESET ALL; SET SESSION AUTHORIZATION DEFAULT' +# white_function_list is a comma separated list of function names +# those do not write to database. Any functions not listed here +# are regarded to write to database and SELECTs including such +# writer-functions will be executed on master(primary) in master/slave +# mode, or executed on all DB nodes in replication mode. +# +# black_function_list is a comma separated list of function names +# those write to database. Any functions not listed here +# are regarded not to write to database and SELECTs including such +# read-only-functions will be executed on any DB nodes. +# +# You cannot make full both white_function_list and +# black_function_list at the same time. If you specify something in +# one of them, you should make empty other. +# +# Pre 3.0 pgpool-II recognizes nextval and setval in hard coded +# way. Following setting will do the same as the previous version. +# white_function_list = '' +# black_function_list = 'nextval,setval' +white_function_list = '' +black_function_list = 'nextval,setval' + # If true print timestamp on each log line. print_timestamp = true diff --git a/pgpool.conf.sample-replication b/pgpool.conf.sample-replication index 76ce1142f..f46b254e8 100644 --- a/pgpool.conf.sample-replication +++ b/pgpool.conf.sample-replication @@ -86,6 +86,28 @@ reset_query_list = 'ABORT; DISCARD ALL' # for 8.2 or older this should be as follows. #reset_query_list = 'ABORT; RESET ALL; SET SESSION AUTHORIZATION DEFAULT' +# white_function_list is a comma separated list of function names +# those do not write to database. Any functions not listed here +# are regarded to write to database and SELECTs including such +# writer-functions will be executed on master(primary) in master/slave +# mode, or executed on all DB nodes in replication mode. +# +# black_function_list is a comma separated list of function names +# those write to database. Any functions not listed here +# are regarded not to write to database and SELECTs including such +# read-only-functions will be executed on any DB nodes. +# +# You cannot make full both white_function_list and +# black_function_list at the same time. If you specify something in +# one of them, you should make empty other. +# +# Pre 3.0 pgpool-II recognizes nextval and setval in hard coded +# way. Following setting will do the same as the previous version. +# white_function_list = '' +# black_function_list = 'nextval,setval' +white_function_list = '' +black_function_list = 'nextval,setval' + # If true print timestamp on each log line. print_timestamp = true diff --git a/pgpool.conf.sample-stream b/pgpool.conf.sample-stream index a1f964ba4..c4cbcde55 100644 --- a/pgpool.conf.sample-stream +++ b/pgpool.conf.sample-stream @@ -86,6 +86,28 @@ reset_query_list = 'ABORT; DISCARD ALL' # for 8.2 or older this should be as follows. #reset_query_list = 'ABORT; RESET ALL; SET SESSION AUTHORIZATION DEFAULT' +# white_function_list is a comma separated list of function names +# those do not write to database. Any functions not listed here +# are regarded to write to database and SELECTs including such +# writer-functions will be executed on master(primary) in master/slave +# mode, or executed on all DB nodes in replication mode. +# +# black_function_list is a comma separated list of function names +# those write to database. Any functions not listed here +# are regarded not to write to database and SELECTs including such +# read-only-functions will be executed on any DB nodes. +# +# You cannot make full both white_function_list and +# black_function_list at the same time. If you specify something in +# one of them, you should make empty other. +# +# Pre 3.0 pgpool-II recognizes nextval and setval in hard coded +# way. Following setting will do the same as the previous version. +# white_function_list = '' +# black_function_list = 'nextval,setval' +white_function_list = '' +black_function_list = 'nextval,setval' + # If true print timestamp on each log line. print_timestamp = true diff --git a/pool_config.c b/pool_config.c index 3bd1125c1..c625a25d7 100644 --- a/pool_config.c +++ b/pool_config.c @@ -1885,7 +1885,10 @@ int pool_init_config(void) pool_config->replicate_select = 0; pool_config->reset_query_list = default_reset_query_list; pool_config->num_reset_queries = sizeof(default_reset_query_list)/sizeof(char *); - pool_config->reset_query_list = default_reset_query_list; + pool_config->white_function_list = NULL; + pool_config->num_white_function_list = 0; + pool_config->black_function_list = NULL; + pool_config->num_black_function_list = 0; pool_config->print_timestamp = 1; pool_config->master_slave_mode = 0; pool_config->master_slave_sub_mode = "slony"; @@ -2397,6 +2400,60 @@ int pool_get_config(char *confpath, POOL_CONFIG_CONTEXT context) } } + else if (!strcmp(key, "white_function_list") && + CHECK_CONTEXT(INIT_CONFIG|RELOAD_CONFIG, context)) + { + char *str; + + if (token != POOL_STRING && token != POOL_UNQUOTED_STRING && token != POOL_KEY) + { + PARSE_ERROR(); + fclose(fd); + return(-1); + } + str = extract_string(yytext, token); + if (str == NULL) + { + fclose(fd); + return(-1); + } + pool_config->white_function_list = + extract_string_tokens(str, ",", &pool_config->num_white_function_list); + + if (pool_config->white_function_list == NULL) + { + fclose(fd); + return(-1); + } + } + + else if (!strcmp(key, "black_function_list") && + CHECK_CONTEXT(INIT_CONFIG|RELOAD_CONFIG, context)) + { + char *str; + + if (token != POOL_STRING && token != POOL_UNQUOTED_STRING && token != POOL_KEY) + { + PARSE_ERROR(); + fclose(fd); + return(-1); + } + str = extract_string(yytext, token); + if (str == NULL) + { + fclose(fd); + return(-1); + } + pool_config->black_function_list = + extract_string_tokens(str, ",", &pool_config->num_black_function_list); + + if (pool_config->black_function_list == NULL) + { + fclose(fd); + return(-1); + } + } + else if (!strcmp(key, "print_timestamp") && CHECK_CONTEXT(INIT_CONFIG, context)) { int v = eval_logical(yytext); @@ -3414,20 +3471,25 @@ int eval_logical(char *str) } /* - * extract tokens separated by delimi from str. return value is an + * Extract tokens separated by delimi from str. Return value is an * array of pointers to malloced strings. number of tokens is set to - * n; note that str will be destroyed by strtok(). Also return value - * points to static data, that means subsequent call will change the - * return value. + * n; note that str will be destroyed by strtok(). */ #define MAXTOKENS 1024 static char **extract_string_tokens(char *str, char *delimi, int *n) { char *token; - static char *tokens[MAXTOKENS]; + static char **tokens; *n = 0; + tokens = malloc(MAXTOKENS*sizeof(char *)); + if (tokens == NULL) + { + pool_error("extract_string_tokens: out of memory"); + return NULL; + } + for (token = strtok(str, delimi); token != NULL && *n < MAXTOKENS; token = strtok(NULL, delimi)) { tokens[*n] = strdup(token); diff --git a/pool_config.h b/pool_config.h index 6d690c2f1..fb772adcc 100644 --- a/pool_config.h +++ b/pool_config.h @@ -74,7 +74,8 @@ typedef struct { int replicate_select; /* if non 0, replicate SELECT statement when load balancing is disabled. */ char **reset_query_list; /* comma separated list of quries to be issued at the end of session */ - + char **white_function_list; /* list of functions with no side effetcs */ + char **black_function_list; /* list of functions with side effetcs */ int print_timestamp; /* if non 0, print time stamp to each log line */ int master_slave_mode; /* if non 0, operate in master/slave mode */ char *master_slave_sub_mode; /* either "slony" or "stream" */ @@ -139,6 +140,8 @@ typedef struct { int replication_enabled; /* replication mode enabled */ int master_slave_enabled; /* master/slave mode enabled */ int num_reset_queries; /* number of queries in reset_query_list */ + int num_white_function_list; /* number of functions in white_function_list */ + int num_black_function_list; /* number of functions in black_function_list */ /* ssl configuration */ int ssl; /* if non 0, activate ssl support (frontend+backend) */ diff --git a/pool_config.l b/pool_config.l index 646d13d15..4a420ccd0 100644 --- a/pool_config.l +++ b/pool_config.l @@ -160,7 +160,10 @@ int pool_init_config(void) pool_config->replicate_select = 0; pool_config->reset_query_list = default_reset_query_list; pool_config->num_reset_queries = sizeof(default_reset_query_list)/sizeof(char *); - pool_config->reset_query_list = default_reset_query_list; + pool_config->white_function_list = NULL; + pool_config->num_white_function_list = 0; + pool_config->black_function_list = NULL; + pool_config->num_black_function_list = 0; pool_config->print_timestamp = 1; pool_config->master_slave_mode = 0; pool_config->master_slave_sub_mode = "slony"; @@ -672,6 +675,60 @@ int pool_get_config(char *confpath, POOL_CONFIG_CONTEXT context) } } + else if (!strcmp(key, "white_function_list") && + CHECK_CONTEXT(INIT_CONFIG|RELOAD_CONFIG, context)) + { + char *str; + + if (token != POOL_STRING && token != POOL_UNQUOTED_STRING && token != POOL_KEY) + { + PARSE_ERROR(); + fclose(fd); + return(-1); + } + str = extract_string(yytext, token); + if (str == NULL) + { + fclose(fd); + return(-1); + } + pool_config->white_function_list = + extract_string_tokens(str, ",", &pool_config->num_white_function_list); + + if (pool_config->white_function_list == NULL) + { + fclose(fd); + return(-1); + } + } + + else if (!strcmp(key, "black_function_list") && + CHECK_CONTEXT(INIT_CONFIG|RELOAD_CONFIG, context)) + { + char *str; + + if (token != POOL_STRING && token != POOL_UNQUOTED_STRING && token != POOL_KEY) + { + PARSE_ERROR(); + fclose(fd); + return(-1); + } + str = extract_string(yytext, token); + if (str == NULL) + { + fclose(fd); + return(-1); + } + pool_config->black_function_list = + extract_string_tokens(str, ",", &pool_config->num_black_function_list); + + if (pool_config->black_function_list == NULL) + { + fclose(fd); + return(-1); + } + } + else if (!strcmp(key, "print_timestamp") && CHECK_CONTEXT(INIT_CONFIG, context)) { int v = eval_logical(yytext); @@ -1689,20 +1746,25 @@ int eval_logical(char *str) } /* - * extract tokens separated by delimi from str. return value is an + * Extract tokens separated by delimi from str. Return value is an * array of pointers to malloced strings. number of tokens is set to - * n; note that str will be destroyed by strtok(). Also return value - * points to static data, that means subsequent call will change the - * return value. + * n; note that str will be destroyed by strtok(). */ #define MAXTOKENS 1024 static char **extract_string_tokens(char *str, char *delimi, int *n) { char *token; - static char *tokens[MAXTOKENS]; + static char **tokens; *n = 0; + tokens = malloc(MAXTOKENS*sizeof(char *)); + if (tokens == NULL) + { + pool_error("extract_string_tokens: out of memory"); + return NULL; + } + for (token = strtok(str, delimi); token != NULL && *n < MAXTOKENS; token = strtok(NULL, delimi)) { tokens[*n] = strdup(token); diff --git a/pool_query_context.c b/pool_query_context.c index a3864e20c..3fcf890e9 100644 --- a/pool_query_context.c +++ b/pool_query_context.c @@ -372,6 +372,15 @@ void pool_where_to_send(POOL_QUERY_CONTEXT *query_context, char *query, Node *no pool_set_node_to_be_sent(query_context, REAL_MASTER_NODE_ID); } + /* + * If a writing function call is used, + * we prefer to send to the primary. + */ + else if (pool_has_function_call(node)) + { + pool_set_node_to_be_sent(query_context, REAL_MASTER_NODE_ID); + } + /* * If temporary table is used in the SELECT, * we prefer to send to the primary. diff --git a/pool_select_walker.c b/pool_select_walker.c index a768af19d..70a12a9bc 100644 --- a/pool_select_walker.c +++ b/pool_select_walker.c @@ -23,18 +23,101 @@ #include <string.h> #include "pool.h" +#include "pool_config.h" #include "pool_select_walker.h" #include "pool_relcache.h" #include "parser/parsenodes.h" #include "pool_session_context.h" typedef struct { - bool has_temp_table; /* True if temporary table is used */ + bool has_temp_table; /* True if temporary table is used */ + bool has_function_call; /* True if write function call is used */ + } SelectContext; +static bool function_call_walker(Node *node, void *context); static bool temp_table_walker(Node *node, void *context); static bool is_temp_table(char *table_name); +/* + * Return true if this SELECT has function calls. + */ +bool pool_has_function_call(Node *node) +{ + + SelectContext ctx; + + if (!IsA(node, SelectStmt)) + return false; + + ctx.has_function_call = false; + + raw_expression_tree_walker(node, function_call_walker, &ctx); + + return ctx.has_function_call; +} + +/* + * Walker function to find a function call + */ +static bool function_call_walker(Node *node, void *context) +{ + SelectContext *ctx = (SelectContext *) context; + int i; + + if (node == NULL) + return false; + + if (IsA(node, FuncCall)) + { + FuncCall *fcall = (FuncCall *)node; + char *fname; + + if (list_length(fcall->funcname)) + { + fname = strVal(linitial(fcall->funcname)); + + pool_debug("function_call_walker: function name: %s", fname); + + /* + * Check white list if any. + */ + if (pool_config->num_white_function_list > 0) + { + for (i=0;i<pool_config->num_white_function_list;i++) + { + /* If the function is found in the white list, we can ignore it */ + if (!strcasecmp(pool_config->white_function_list[i], fname)) + { + return raw_expression_tree_walker(node, function_call_walker, context); + } + } + /* + * Since the function was not found in white list, we + * have found a writing function. + */ + ctx->has_function_call = true; + return false; + } + + /* + * Check black list if any. + */ + for (i=0;i<pool_config->num_black_function_list;i++) + { + /* Is the function found in the black list? */ + if (!strcasecmp(pool_config->black_function_list[i], fname)) + { + /* Found. */ + ctx->has_function_call = true; + return false; + } + } + } + } + return raw_expression_tree_walker(node, function_call_walker, context); +} + /* * Return true if this SELECT has temporary table. */ diff --git a/pool_select_walker.h b/pool_select_walker.h index 2996ae4ed..d1dd0114f 100644 --- a/pool_select_walker.h +++ b/pool_select_walker.h @@ -29,6 +29,7 @@ #include "pool.h" #include "parser/nodes.h" +extern bool pool_has_function_call(Node *node); extern bool pool_has_temp_table(Node *node); extern bool raw_expression_tree_walker(Node *node, bool (*walker) (), void *context); [parent: 6d10cf974b2d]