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]