prestogres 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +4 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +20 -0
- data/LICENSE +202 -0
- data/NOTICE +22 -0
- data/README.md +217 -0
- data/Rakefile +13 -0
- data/VERSION +1 -0
- data/bin/prestogres +254 -0
- data/config/pcp.conf.sample +28 -0
- data/config/pgpool.conf +678 -0
- data/config/pool_hba.conf +84 -0
- data/config/pool_passwd +0 -0
- data/config/postgresql.conf +2 -0
- data/ext/.gitignore +6 -0
- data/ext/depend +26 -0
- data/ext/extconf.rb +4 -0
- data/ext/prestogres_config.c +12 -0
- data/pgpool2/.gitignore +36 -0
- data/pgpool2/AUTHORS +4 -0
- data/pgpool2/COPYING +12 -0
- data/pgpool2/ChangeLog +1 -0
- data/pgpool2/INSTALL +1 -0
- data/pgpool2/Makefile.am +159 -0
- data/pgpool2/Makefile.in +1187 -0
- data/pgpool2/NEWS +4960 -0
- data/pgpool2/README +1 -0
- data/pgpool2/README.euc_jp +1 -0
- data/pgpool2/README.online-recovery +62 -0
- data/pgpool2/TODO +103 -0
- data/pgpool2/ac_func_accept_argtypes.m4 +85 -0
- data/pgpool2/aclocal.m4 +1088 -0
- data/pgpool2/c-compiler.m4 +134 -0
- data/pgpool2/c-library.m4 +325 -0
- data/pgpool2/child.c +2097 -0
- data/pgpool2/config.guess +1532 -0
- data/pgpool2/config.h.in +332 -0
- data/pgpool2/config.sub +1640 -0
- data/pgpool2/configure +15752 -0
- data/pgpool2/configure.in +392 -0
- data/pgpool2/depcomp +522 -0
- data/pgpool2/doc/basebackup.sh +17 -0
- data/pgpool2/doc/pgpool-de.html +4220 -0
- data/pgpool2/doc/pgpool-en.html +5738 -0
- data/pgpool2/doc/pgpool-fr.html +4118 -0
- data/pgpool2/doc/pgpool-ja.css +198 -0
- data/pgpool2/doc/pgpool-ja.html +11279 -0
- data/pgpool2/doc/pgpool-zh_cn.html +4445 -0
- data/pgpool2/doc/pgpool.css +280 -0
- data/pgpool2/doc/pgpool_remote_start +13 -0
- data/pgpool2/doc/recovery.conf.sample +117 -0
- data/pgpool2/doc/tutorial-en.html +707 -0
- data/pgpool2/doc/tutorial-ja.html +422 -0
- data/pgpool2/doc/tutorial-memqcache-en.html +325 -0
- data/pgpool2/doc/tutorial-memqcache-ja.html +370 -0
- data/pgpool2/doc/tutorial-memqcache-zh_cn.html +322 -0
- data/pgpool2/doc/tutorial-watchdog-en.html +306 -0
- data/pgpool2/doc/tutorial-watchdog-ja.html +343 -0
- data/pgpool2/doc/tutorial-watchdog-zh_cn.html +301 -0
- data/pgpool2/doc/tutorial-zh_cn.html +537 -0
- data/pgpool2/doc/watchdog.png +0 -0
- data/pgpool2/doc/wd-en.html +236 -0
- data/pgpool2/doc/wd-en.jpg +0 -0
- data/pgpool2/doc/wd-ja.html +219 -0
- data/pgpool2/doc/wd-ja.jpg +0 -0
- data/pgpool2/doc/wd-zh_cn.html +201 -0
- data/pgpool2/doc/where_to_send_queries.odg +0 -0
- data/pgpool2/doc/where_to_send_queries.pdf +0 -0
- data/pgpool2/general.m4 +166 -0
- data/pgpool2/getopt_long.c +200 -0
- data/pgpool2/getopt_long.h +44 -0
- data/pgpool2/install-sh +251 -0
- data/pgpool2/ltmain.sh +8406 -0
- data/pgpool2/m4/libtool.m4 +7360 -0
- data/pgpool2/m4/ltoptions.m4 +368 -0
- data/pgpool2/m4/ltsugar.m4 +123 -0
- data/pgpool2/m4/ltversion.m4 +23 -0
- data/pgpool2/m4/lt~obsolete.m4 +92 -0
- data/pgpool2/main.c +2971 -0
- data/pgpool2/md5.c +444 -0
- data/pgpool2/md5.h +28 -0
- data/pgpool2/missing +360 -0
- data/pgpool2/mkinstalldirs +40 -0
- data/pgpool2/parser/Makefile.am +50 -0
- data/pgpool2/parser/Makefile.in +559 -0
- data/pgpool2/parser/copyfuncs.c +3310 -0
- data/pgpool2/parser/gram.c +39100 -0
- data/pgpool2/parser/gram.h +940 -0
- data/pgpool2/parser/gram.y +13408 -0
- data/pgpool2/parser/gramparse.h +74 -0
- data/pgpool2/parser/keywords.c +32 -0
- data/pgpool2/parser/keywords.h +39 -0
- data/pgpool2/parser/kwlist.h +425 -0
- data/pgpool2/parser/kwlookup.c +88 -0
- data/pgpool2/parser/list.c +1156 -0
- data/pgpool2/parser/makefuncs.c +518 -0
- data/pgpool2/parser/makefuncs.h +83 -0
- data/pgpool2/parser/memnodes.h +79 -0
- data/pgpool2/parser/nodes.c +29 -0
- data/pgpool2/parser/nodes.h +609 -0
- data/pgpool2/parser/outfuncs.c +5790 -0
- data/pgpool2/parser/parsenodes.h +2615 -0
- data/pgpool2/parser/parser.c +262 -0
- data/pgpool2/parser/parser.h +46 -0
- data/pgpool2/parser/pg_class.h +158 -0
- data/pgpool2/parser/pg_config_manual.h +273 -0
- data/pgpool2/parser/pg_list.h +352 -0
- data/pgpool2/parser/pg_trigger.h +147 -0
- data/pgpool2/parser/pg_wchar.h +492 -0
- data/pgpool2/parser/pool_memory.c +342 -0
- data/pgpool2/parser/pool_memory.h +77 -0
- data/pgpool2/parser/pool_parser.h +222 -0
- data/pgpool2/parser/pool_string.c +121 -0
- data/pgpool2/parser/pool_string.h +37 -0
- data/pgpool2/parser/primnodes.h +1280 -0
- data/pgpool2/parser/scan.c +4094 -0
- data/pgpool2/parser/scan.l +1451 -0
- data/pgpool2/parser/scanner.h +120 -0
- data/pgpool2/parser/scansup.c +221 -0
- data/pgpool2/parser/scansup.h +28 -0
- data/pgpool2/parser/snprintf.c +1102 -0
- data/pgpool2/parser/stringinfo.c +294 -0
- data/pgpool2/parser/stringinfo.h +178 -0
- data/pgpool2/parser/value.c +78 -0
- data/pgpool2/parser/value.h +62 -0
- data/pgpool2/parser/wchar.c +2048 -0
- data/pgpool2/pcp.conf.sample +28 -0
- data/pgpool2/pcp/Makefile.am +40 -0
- data/pgpool2/pcp/Makefile.in +771 -0
- data/pgpool2/pcp/libpcp_ext.h +250 -0
- data/pgpool2/pcp/md5.c +444 -0
- data/pgpool2/pcp/md5.h +28 -0
- data/pgpool2/pcp/pcp.c +1652 -0
- data/pgpool2/pcp/pcp.h +61 -0
- data/pgpool2/pcp/pcp_attach_node.c +172 -0
- data/pgpool2/pcp/pcp_detach_node.c +185 -0
- data/pgpool2/pcp/pcp_error.c +87 -0
- data/pgpool2/pcp/pcp_node_count.c +160 -0
- data/pgpool2/pcp/pcp_node_info.c +198 -0
- data/pgpool2/pcp/pcp_pool_status.c +166 -0
- data/pgpool2/pcp/pcp_proc_count.c +166 -0
- data/pgpool2/pcp/pcp_proc_info.c +261 -0
- data/pgpool2/pcp/pcp_promote_node.c +185 -0
- data/pgpool2/pcp/pcp_recovery_node.c +172 -0
- data/pgpool2/pcp/pcp_stop_pgpool.c +179 -0
- data/pgpool2/pcp/pcp_stream.c +385 -0
- data/pgpool2/pcp/pcp_stream.h +52 -0
- data/pgpool2/pcp/pcp_systemdb_info.c +194 -0
- data/pgpool2/pcp/pcp_watchdog_info.c +211 -0
- data/pgpool2/pcp_child.c +1493 -0
- data/pgpool2/pg_md5.c +305 -0
- data/pgpool2/pgpool.8.in +121 -0
- data/pgpool2/pgpool.conf +553 -0
- data/pgpool2/pgpool.conf.sample +666 -0
- data/pgpool2/pgpool.conf.sample-master-slave +665 -0
- data/pgpool2/pgpool.conf.sample-replication +664 -0
- data/pgpool2/pgpool.conf.sample-stream +664 -0
- data/pgpool2/pgpool.spec +264 -0
- data/pgpool2/pgpool_adm/TODO +7 -0
- data/pgpool2/pgpool_adm/pgpool_adm--1.0.sql +85 -0
- data/pgpool2/pgpool_adm/pgpool_adm.c +558 -0
- data/pgpool2/pgpool_adm/pgpool_adm.control +5 -0
- data/pgpool2/pgpool_adm/pgpool_adm.h +46 -0
- data/pgpool2/pgpool_adm/pgpool_adm.sql.in +85 -0
- data/pgpool2/pool.h +655 -0
- data/pgpool2/pool_auth.c +1390 -0
- data/pgpool2/pool_config.c +5007 -0
- data/pgpool2/pool_config.h +284 -0
- data/pgpool2/pool_config.l +3281 -0
- data/pgpool2/pool_config_md5.c +29 -0
- data/pgpool2/pool_connection_pool.c +812 -0
- data/pgpool2/pool_error.c +242 -0
- data/pgpool2/pool_globals.c +27 -0
- data/pgpool2/pool_hba.c +1723 -0
- data/pgpool2/pool_hba.conf.sample +67 -0
- data/pgpool2/pool_ip.c +567 -0
- data/pgpool2/pool_ip.h +65 -0
- data/pgpool2/pool_ipc.h +38 -0
- data/pgpool2/pool_lobj.c +242 -0
- data/pgpool2/pool_lobj.h +32 -0
- data/pgpool2/pool_memqcache.c +3818 -0
- data/pgpool2/pool_memqcache.h +268 -0
- data/pgpool2/pool_params.c +163 -0
- data/pgpool2/pool_passwd.c +249 -0
- data/pgpool2/pool_passwd.h +41 -0
- data/pgpool2/pool_path.c +193 -0
- data/pgpool2/pool_path.h +81 -0
- data/pgpool2/pool_process_context.c +247 -0
- data/pgpool2/pool_process_context.h +62 -0
- data/pgpool2/pool_process_query.c +5001 -0
- data/pgpool2/pool_process_reporting.c +1671 -0
- data/pgpool2/pool_process_reporting.h +44 -0
- data/pgpool2/pool_proto2.c +671 -0
- data/pgpool2/pool_proto_modules.c +3524 -0
- data/pgpool2/pool_proto_modules.h +185 -0
- data/pgpool2/pool_query_cache.c +1020 -0
- data/pgpool2/pool_query_context.c +1871 -0
- data/pgpool2/pool_query_context.h +105 -0
- data/pgpool2/pool_relcache.c +284 -0
- data/pgpool2/pool_relcache.h +78 -0
- data/pgpool2/pool_rewrite_outfuncs.c +9060 -0
- data/pgpool2/pool_rewrite_query.c +715 -0
- data/pgpool2/pool_rewrite_query.h +192 -0
- data/pgpool2/pool_select_walker.c +1150 -0
- data/pgpool2/pool_select_walker.h +68 -0
- data/pgpool2/pool_sema.c +161 -0
- data/pgpool2/pool_session_context.c +952 -0
- data/pgpool2/pool_session_context.h +203 -0
- data/pgpool2/pool_shmem.c +185 -0
- data/pgpool2/pool_signal.c +158 -0
- data/pgpool2/pool_signal.h +61 -0
- data/pgpool2/pool_ssl.c +339 -0
- data/pgpool2/pool_stream.c +962 -0
- data/pgpool2/pool_stream.h +61 -0
- data/pgpool2/pool_system.c +659 -0
- data/pgpool2/pool_timestamp.c +1215 -0
- data/pgpool2/pool_timestamp.h +38 -0
- data/pgpool2/pool_type.h +171 -0
- data/pgpool2/pool_worker_child.c +384 -0
- data/pgpool2/ps_status.c +404 -0
- data/pgpool2/recovery.c +435 -0
- data/pgpool2/redhat/pgpool.conf.sample.patch +52 -0
- data/pgpool2/redhat/pgpool.init +201 -0
- data/pgpool2/redhat/pgpool.sysconfig +7 -0
- data/pgpool2/redhat/rpm_installer/basebackup-replication.sh +53 -0
- data/pgpool2/redhat/rpm_installer/basebackup-stream.sh +55 -0
- data/pgpool2/redhat/rpm_installer/config_for_script +17 -0
- data/pgpool2/redhat/rpm_installer/failover.sh +64 -0
- data/pgpool2/redhat/rpm_installer/getsources.sh +141 -0
- data/pgpool2/redhat/rpm_installer/install.sh +1363 -0
- data/pgpool2/redhat/rpm_installer/pgpool_recovery_pitr +47 -0
- data/pgpool2/redhat/rpm_installer/pgpool_remote_start +15 -0
- data/pgpool2/redhat/rpm_installer/recovery.conf +4 -0
- data/pgpool2/redhat/rpm_installer/uninstall.sh +57 -0
- data/pgpool2/sample/dist_def_pgbench.sql +73 -0
- data/pgpool2/sample/pgpool.pam +3 -0
- data/pgpool2/sample/pgpool_recovery +20 -0
- data/pgpool2/sample/pgpool_recovery_pitr +19 -0
- data/pgpool2/sample/pgpool_remote_start +13 -0
- data/pgpool2/sample/replicate_def_pgbench.sql +18 -0
- data/pgpool2/sql/insert_lock.sql +15 -0
- data/pgpool2/sql/pgpool-recovery/pgpool-recovery.c +280 -0
- data/pgpool2/sql/pgpool-recovery/pgpool-recovery.sql.in +19 -0
- data/pgpool2/sql/pgpool-recovery/pgpool_recovery--1.0.sql +24 -0
- data/pgpool2/sql/pgpool-recovery/pgpool_recovery.control +5 -0
- data/pgpool2/sql/pgpool-recovery/uninstall_pgpool-recovery.sql +3 -0
- data/pgpool2/sql/pgpool-regclass/pgpool-regclass.c +206 -0
- data/pgpool2/sql/pgpool-regclass/pgpool-regclass.sql.in +4 -0
- data/pgpool2/sql/pgpool-regclass/pgpool_regclass--1.0.sql +7 -0
- data/pgpool2/sql/pgpool-regclass/pgpool_regclass.control +5 -0
- data/pgpool2/sql/pgpool-regclass/uninstall_pgpool-regclass.sql +1 -0
- data/pgpool2/sql/system_db.sql +38 -0
- data/pgpool2/strlcpy.c +85 -0
- data/pgpool2/test/C/test_extended.c +98 -0
- data/pgpool2/test/jdbc/.cvsignore +2 -0
- data/pgpool2/test/jdbc/AutoCommitTest.java +45 -0
- data/pgpool2/test/jdbc/BatchTest.java +55 -0
- data/pgpool2/test/jdbc/ColumnTest.java +60 -0
- data/pgpool2/test/jdbc/CreateTempTableTest.java +48 -0
- data/pgpool2/test/jdbc/InsertTest.java +34 -0
- data/pgpool2/test/jdbc/LockTest.java +36 -0
- data/pgpool2/test/jdbc/PgpoolTest.java +75 -0
- data/pgpool2/test/jdbc/README.euc_jp +73 -0
- data/pgpool2/test/jdbc/RunTest.java +83 -0
- data/pgpool2/test/jdbc/SelectTest.java +37 -0
- data/pgpool2/test/jdbc/UpdateTest.java +32 -0
- data/pgpool2/test/jdbc/expected/CreateTempTable +1 -0
- data/pgpool2/test/jdbc/expected/autocommit +10 -0
- data/pgpool2/test/jdbc/expected/batch +1 -0
- data/pgpool2/test/jdbc/expected/column +100 -0
- data/pgpool2/test/jdbc/expected/insert +1 -0
- data/pgpool2/test/jdbc/expected/lock +100 -0
- data/pgpool2/test/jdbc/expected/select +2 -0
- data/pgpool2/test/jdbc/expected/update +1 -0
- data/pgpool2/test/jdbc/pgpool.properties +7 -0
- data/pgpool2/test/jdbc/prepare.sql +54 -0
- data/pgpool2/test/jdbc/run.sh +6 -0
- data/pgpool2/test/parser/.cvsignore +6 -0
- data/pgpool2/test/parser/README +32 -0
- data/pgpool2/test/parser/expected/copy.out +17 -0
- data/pgpool2/test/parser/expected/create.out +64 -0
- data/pgpool2/test/parser/expected/cursor.out +37 -0
- data/pgpool2/test/parser/expected/delete.out +10 -0
- data/pgpool2/test/parser/expected/drop.out +12 -0
- data/pgpool2/test/parser/expected/insert.out +13 -0
- data/pgpool2/test/parser/expected/misc.out +28 -0
- data/pgpool2/test/parser/expected/prepare.out +4 -0
- data/pgpool2/test/parser/expected/privileges.out +31 -0
- data/pgpool2/test/parser/expected/scanner.out +30 -0
- data/pgpool2/test/parser/expected/select.out +89 -0
- data/pgpool2/test/parser/expected/transaction.out +38 -0
- data/pgpool2/test/parser/expected/update.out +11 -0
- data/pgpool2/test/parser/expected/v84.out +37 -0
- data/pgpool2/test/parser/expected/v90.out +25 -0
- data/pgpool2/test/parser/expected/var.out +22 -0
- data/pgpool2/test/parser/input/alter.sql +2 -0
- data/pgpool2/test/parser/input/copy.sql +17 -0
- data/pgpool2/test/parser/input/create.sql +64 -0
- data/pgpool2/test/parser/input/cursor.sql +37 -0
- data/pgpool2/test/parser/input/delete.sql +10 -0
- data/pgpool2/test/parser/input/drop.sql +12 -0
- data/pgpool2/test/parser/input/insert.sql +13 -0
- data/pgpool2/test/parser/input/misc.sql +28 -0
- data/pgpool2/test/parser/input/prepare.sql +4 -0
- data/pgpool2/test/parser/input/privileges.sql +31 -0
- data/pgpool2/test/parser/input/scanner.sql +34 -0
- data/pgpool2/test/parser/input/select.sql +89 -0
- data/pgpool2/test/parser/input/transaction.sql +38 -0
- data/pgpool2/test/parser/input/update.sql +11 -0
- data/pgpool2/test/parser/input/v84.sql +37 -0
- data/pgpool2/test/parser/input/v90.sql +38 -0
- data/pgpool2/test/parser/input/var.sql +22 -0
- data/pgpool2/test/parser/main.c +96 -0
- data/pgpool2/test/parser/parse_schedule +16 -0
- data/pgpool2/test/parser/pool.h +13 -0
- data/pgpool2/test/parser/run-test +62 -0
- data/pgpool2/test/pdo-test/README.euc_jp +58 -0
- data/pgpool2/test/pdo-test/SQLlist/test1.sql +3 -0
- data/pgpool2/test/pdo-test/SQLlist/test2.sql +3 -0
- data/pgpool2/test/pdo-test/collections.inc +11 -0
- data/pgpool2/test/pdo-test/def.inc +7 -0
- data/pgpool2/test/pdo-test/log.txt +0 -0
- data/pgpool2/test/pdo-test/mod/database.inc +36 -0
- data/pgpool2/test/pdo-test/mod/def.inc +0 -0
- data/pgpool2/test/pdo-test/mod/errorhandler.inc +27 -0
- data/pgpool2/test/pdo-test/pdotest.php +11 -0
- data/pgpool2/test/pdo-test/regsql.inc +56 -0
- data/pgpool2/test/pgpool_setup +898 -0
- data/pgpool2/test/regression/README +39 -0
- data/pgpool2/test/regression/clean.sh +21 -0
- data/pgpool2/test/regression/libs.sh +16 -0
- data/pgpool2/test/regression/regress.sh +166 -0
- data/pgpool2/test/regression/tests/001.load_balance/test.sh +128 -0
- data/pgpool2/test/regression/tests/002.native_replication/PgTester.java +47 -0
- data/pgpool2/test/regression/tests/002.native_replication/create.sql +6 -0
- data/pgpool2/test/regression/tests/002.native_replication/test.sh +71 -0
- data/pgpool2/test/regression/tests/003.failover/expected.r +6 -0
- data/pgpool2/test/regression/tests/003.failover/expected.s +6 -0
- data/pgpool2/test/regression/tests/003.failover/test.sh +45 -0
- data/pgpool2/test/regression/tests/004.watchdog/master.conf +12 -0
- data/pgpool2/test/regression/tests/004.watchdog/standby.conf +19 -0
- data/pgpool2/test/regression/tests/004.watchdog/test.sh +52 -0
- data/pgpool2/test/regression/tests/050.bug58/test.sh +50 -0
- data/pgpool2/test/regression/tests/051.bug60/bug.sql +12 -0
- data/pgpool2/test/regression/tests/051.bug60/database-clean.sql +6 -0
- data/pgpool2/test/regression/tests/051.bug60/database-setup.sql +28 -0
- data/pgpool2/test/regression/tests/051.bug60/test.sh +79 -0
- data/pgpool2/test/regression/tests/052.do_query/test.sh +44 -0
- data/pgpool2/test/regression/tests/053.insert_lock_hangs/test.sh +81 -0
- data/pgpool2/test/regression/tests/054.postgres_fdw/test.sh +67 -0
- data/pgpool2/test/regression/tests/055.backend_all_down/test.sh +52 -0
- data/pgpool2/test/regression/tests/056.bug63/jdbctest2.java +66 -0
- data/pgpool2/test/regression/tests/056.bug63/test.sh +47 -0
- data/pgpool2/test/regression/tests/057.bug61/test.sh +40 -0
- data/pgpool2/test/regression/tests/058.bug68/jdbctest3.java +45 -0
- data/pgpool2/test/regression/tests/058.bug68/test.sh +47 -0
- data/pgpool2/test/timestamp/expected/insert.out +16 -0
- data/pgpool2/test/timestamp/expected/misc.out +3 -0
- data/pgpool2/test/timestamp/expected/update.out +6 -0
- data/pgpool2/test/timestamp/input/insert.sql +16 -0
- data/pgpool2/test/timestamp/input/misc.sql +3 -0
- data/pgpool2/test/timestamp/input/update.sql +6 -0
- data/pgpool2/test/timestamp/main.c +129 -0
- data/pgpool2/test/timestamp/parse_schedule +3 -0
- data/pgpool2/test/timestamp/run-test +69 -0
- data/pgpool2/version.h +1 -0
- data/pgpool2/watchdog/Makefile.am +17 -0
- data/pgpool2/watchdog/Makefile.in +505 -0
- data/pgpool2/watchdog/test/stab.c +266 -0
- data/pgpool2/watchdog/test/test.c +85 -0
- data/pgpool2/watchdog/test/wd_child_t.c +87 -0
- data/pgpool2/watchdog/test/wd_lifecheck_t.c +87 -0
- data/pgpool2/watchdog/test/wd_packet_t.c +87 -0
- data/pgpool2/watchdog/test/wd_ping_t.c +20 -0
- data/pgpool2/watchdog/watchdog.c +408 -0
- data/pgpool2/watchdog/watchdog.h +209 -0
- data/pgpool2/watchdog/wd_child.c +444 -0
- data/pgpool2/watchdog/wd_ext.h +123 -0
- data/pgpool2/watchdog/wd_heartbeat.c +577 -0
- data/pgpool2/watchdog/wd_if.c +216 -0
- data/pgpool2/watchdog/wd_init.c +126 -0
- data/pgpool2/watchdog/wd_interlock.c +347 -0
- data/pgpool2/watchdog/wd_lifecheck.c +512 -0
- data/pgpool2/watchdog/wd_list.c +429 -0
- data/pgpool2/watchdog/wd_packet.c +1159 -0
- data/pgpool2/watchdog/wd_ping.c +330 -0
- data/pgpool2/ylwrap +223 -0
- data/pgsql/presto_client.py +346 -0
- data/pgsql/prestogres.py +156 -0
- data/pgsql/setup_functions.sql +21 -0
- data/pgsql/setup_language.sql +3 -0
- data/prestogres.gemspec +23 -0
- metadata +496 -0
|
@@ -0,0 +1,3524 @@
|
|
|
1
|
+
/* -*-pgsql-c-*- */
|
|
2
|
+
/*
|
|
3
|
+
* $Header$
|
|
4
|
+
*
|
|
5
|
+
* pgpool: a language independent connection pool server for PostgreSQL
|
|
6
|
+
* written by Tatsuo Ishii
|
|
7
|
+
*
|
|
8
|
+
* Copyright (c) 2003-2013 PgPool Global Development Group
|
|
9
|
+
*
|
|
10
|
+
* Permission to use, copy, modify, and distribute this software and
|
|
11
|
+
* its documentation for any purpose and without fee is hereby
|
|
12
|
+
* granted, provided that the above copyright notice appear in all
|
|
13
|
+
* copies and that both that copyright notice and this permission
|
|
14
|
+
* notice appear in supporting documentation, and that the name of the
|
|
15
|
+
* author not be used in advertising or publicity pertaining to
|
|
16
|
+
* distribution of the software without specific, written prior
|
|
17
|
+
* permission. The author makes no representations about the
|
|
18
|
+
* suitability of this software for any purpose. It is provided "as
|
|
19
|
+
* is" without express or implied warranty.
|
|
20
|
+
*
|
|
21
|
+
*---------------------------------------------------------------------
|
|
22
|
+
* pool_proto_modules.c: modules corresponding to message protocols.
|
|
23
|
+
* used by pool_process_query()
|
|
24
|
+
*---------------------------------------------------------------------
|
|
25
|
+
*/
|
|
26
|
+
#include "config.h"
|
|
27
|
+
#include <errno.h>
|
|
28
|
+
|
|
29
|
+
#ifdef HAVE_SYS_TYPES_H
|
|
30
|
+
#include <sys/types.h>
|
|
31
|
+
#endif
|
|
32
|
+
#ifdef HAVE_SYS_TIME_H
|
|
33
|
+
#include <sys/time.h>
|
|
34
|
+
#endif
|
|
35
|
+
#ifdef HAVE_SYS_SELECT_H
|
|
36
|
+
#include <sys/select.h>
|
|
37
|
+
#endif
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
#include <stdlib.h>
|
|
41
|
+
#include <unistd.h>
|
|
42
|
+
#include <string.h>
|
|
43
|
+
#include <netinet/in.h>
|
|
44
|
+
#include <ctype.h>
|
|
45
|
+
|
|
46
|
+
#include "pool.h"
|
|
47
|
+
#include "pool_signal.h"
|
|
48
|
+
#include "pool_timestamp.h"
|
|
49
|
+
#include "pool_proto_modules.h"
|
|
50
|
+
#include "pool_rewrite_query.h"
|
|
51
|
+
#include "pool_relcache.h"
|
|
52
|
+
#include "pool_stream.h"
|
|
53
|
+
#include "pool_config.h"
|
|
54
|
+
#include "parser/pool_string.h"
|
|
55
|
+
#include "pool_session_context.h"
|
|
56
|
+
#include "pool_query_context.h"
|
|
57
|
+
#include "pool_lobj.h"
|
|
58
|
+
#include "pool_select_walker.h"
|
|
59
|
+
#include "pool_memqcache.h"
|
|
60
|
+
|
|
61
|
+
char *copy_table = NULL; /* copy table name */
|
|
62
|
+
char *copy_schema = NULL; /* copy table name */
|
|
63
|
+
char copy_delimiter; /* copy delimiter char */
|
|
64
|
+
char *copy_null = NULL; /* copy null string */
|
|
65
|
+
|
|
66
|
+
/*
|
|
67
|
+
* Non 0 if allow to close internal transaction. This variable was
|
|
68
|
+
* introduced on 2008/4/3 not to close an internal transaction when
|
|
69
|
+
* Sync message is received after receiving Parse message. This hack
|
|
70
|
+
* is for PHP-PDO.
|
|
71
|
+
*/
|
|
72
|
+
static int allow_close_transaction = 1;
|
|
73
|
+
|
|
74
|
+
int is_select_pgcatalog = 0;
|
|
75
|
+
int is_select_for_update = 0; /* 1 if SELECT INTO or SELECT FOR UPDATE */
|
|
76
|
+
bool is_parallel_table = false;
|
|
77
|
+
|
|
78
|
+
/*
|
|
79
|
+
* last query string sent to simpleQuery()
|
|
80
|
+
*/
|
|
81
|
+
char query_string_buffer[QUERY_STRING_BUFFER_LEN];
|
|
82
|
+
|
|
83
|
+
/*
|
|
84
|
+
* query string produced by nodeToString() in simpleQuery().
|
|
85
|
+
* this variable only useful when enable_query_cache is true.
|
|
86
|
+
*/
|
|
87
|
+
char *parsed_query = NULL;
|
|
88
|
+
|
|
89
|
+
static int check_errors(POOL_CONNECTION_POOL *backend, int backend_id);
|
|
90
|
+
static void generate_error_message(char *prefix, int specific_error, char *query);
|
|
91
|
+
static POOL_STATUS parse_before_bind(POOL_CONNECTION *frontend,
|
|
92
|
+
POOL_CONNECTION_POOL *backend,
|
|
93
|
+
POOL_SENT_MESSAGE *message);
|
|
94
|
+
static int* find_victim_nodes(int *ntuples, int nmembers, int master_node, int *number_of_nodes);
|
|
95
|
+
static int extract_ntuples(char *message);
|
|
96
|
+
static POOL_STATUS close_standby_transactions(POOL_CONNECTION *frontend,
|
|
97
|
+
POOL_CONNECTION_POOL *backend);
|
|
98
|
+
|
|
99
|
+
/*
|
|
100
|
+
* Process Query('Q') message
|
|
101
|
+
* Query messages include an SQL string.
|
|
102
|
+
*/
|
|
103
|
+
POOL_STATUS SimpleQuery(POOL_CONNECTION *frontend,
|
|
104
|
+
POOL_CONNECTION_POOL *backend, int len, char *contents)
|
|
105
|
+
{
|
|
106
|
+
static char *sq_config = "pool_status";
|
|
107
|
+
static char *sq_pools = "pool_pools";
|
|
108
|
+
static char *sq_processes = "pool_processes";
|
|
109
|
+
static char *sq_nodes = "pool_nodes";
|
|
110
|
+
static char *sq_version = "pool_version";
|
|
111
|
+
static char *sq_cache = "pool_cache";
|
|
112
|
+
int commit;
|
|
113
|
+
List *parse_tree_list;
|
|
114
|
+
Node *node = NULL;
|
|
115
|
+
POOL_STATUS status;
|
|
116
|
+
char *string;
|
|
117
|
+
int lock_kind;
|
|
118
|
+
bool is_likely_select = false;
|
|
119
|
+
int specific_error = 0;
|
|
120
|
+
|
|
121
|
+
POOL_SESSION_CONTEXT *session_context;
|
|
122
|
+
POOL_QUERY_CONTEXT *query_context;
|
|
123
|
+
POOL_MEMORY_POOL *old_context;
|
|
124
|
+
|
|
125
|
+
/* Get session context */
|
|
126
|
+
session_context = pool_get_session_context();
|
|
127
|
+
if (!session_context)
|
|
128
|
+
{
|
|
129
|
+
pool_error("SimpleQuery: cannot get session context");
|
|
130
|
+
return POOL_END;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/* save last query string for logging purpose */
|
|
134
|
+
strlcpy(query_string_buffer, contents, sizeof(query_string_buffer));
|
|
135
|
+
|
|
136
|
+
/* show ps status */
|
|
137
|
+
query_ps_status(contents, backend);
|
|
138
|
+
|
|
139
|
+
/* log query to log file if necessary */
|
|
140
|
+
if (pool_config->log_statement)
|
|
141
|
+
{
|
|
142
|
+
pool_log("statement: %s", contents);
|
|
143
|
+
}
|
|
144
|
+
else
|
|
145
|
+
{
|
|
146
|
+
pool_debug("statement2: %s", contents);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/*
|
|
150
|
+
* Fetch memory cache if possible
|
|
151
|
+
*/
|
|
152
|
+
is_likely_select = pool_is_likely_select(contents);
|
|
153
|
+
|
|
154
|
+
/*
|
|
155
|
+
* If memory query cache enabled and the query seems to be a
|
|
156
|
+
* SELECT use query cache if possible. However if we are in an
|
|
157
|
+
* explicit transaction and we had writing query before, we should
|
|
158
|
+
* not use query cache. This means that even the writing query is
|
|
159
|
+
* not anything related to the table which is used the SELECT, we
|
|
160
|
+
* do not use cache. Of course we could analyze the SELECT to see
|
|
161
|
+
* if it uses the table modified in the transaction, but it will
|
|
162
|
+
* need parsing query and accessing to system catalog, which will
|
|
163
|
+
* add significant overhead. Moreover if we are in aborted
|
|
164
|
+
* transaction, commands should be ignored, so we should not use
|
|
165
|
+
* query cache.
|
|
166
|
+
*/
|
|
167
|
+
if (pool_config->memory_cache_enabled && is_likely_select &&
|
|
168
|
+
!pool_is_writing_transaction() &&
|
|
169
|
+
TSTATE(backend, MASTER_SLAVE ? PRIMARY_NODE_ID : REAL_MASTER_NODE_ID) != 'E')
|
|
170
|
+
{
|
|
171
|
+
bool foundp;
|
|
172
|
+
|
|
173
|
+
/* If the query is SELECT from table to cache, try to fetch cached result. */
|
|
174
|
+
status = pool_fetch_from_memory_cache(frontend, backend, contents, &foundp);
|
|
175
|
+
|
|
176
|
+
if (status != POOL_CONTINUE)
|
|
177
|
+
return status;
|
|
178
|
+
|
|
179
|
+
if (foundp)
|
|
180
|
+
{
|
|
181
|
+
pool_ps_idle_display(backend);
|
|
182
|
+
pool_set_skip_reading_from_backends();
|
|
183
|
+
pool_stats_count_up_num_cache_hits();
|
|
184
|
+
return POOL_CONTINUE;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/* Create query context */
|
|
189
|
+
query_context = pool_init_query_context();
|
|
190
|
+
if (!query_context)
|
|
191
|
+
{
|
|
192
|
+
pool_error("SimpleQuery: pool_init_query_context failed");
|
|
193
|
+
return POOL_END;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/* switch memory context */
|
|
197
|
+
if (pool_memory == NULL)
|
|
198
|
+
pool_memory = pool_memory_create(PARSER_BLOCK_SIZE);
|
|
199
|
+
old_context = pool_memory_context_switch_to(query_context->memory_context);
|
|
200
|
+
|
|
201
|
+
/* parse SQL string */
|
|
202
|
+
parse_tree_list = raw_parser(contents);
|
|
203
|
+
|
|
204
|
+
if (parse_tree_list == NIL)
|
|
205
|
+
{
|
|
206
|
+
/* is the query empty? */
|
|
207
|
+
if (*contents == '\0' || *contents == ';')
|
|
208
|
+
{
|
|
209
|
+
/*
|
|
210
|
+
* JBoss sends empty queries for checking connections.
|
|
211
|
+
* We replace the empty query with SELECT command not
|
|
212
|
+
* to affect load balance.
|
|
213
|
+
* [Pgpool-general] Confused about JDBC and load balancing
|
|
214
|
+
*/
|
|
215
|
+
parse_tree_list = raw_parser(POOL_DUMMY_READ_QUERY);
|
|
216
|
+
}
|
|
217
|
+
else
|
|
218
|
+
{
|
|
219
|
+
/*
|
|
220
|
+
* Unable to parse the query. Probably syntax error or the
|
|
221
|
+
* query is too new and our parser cannot understand. Treat as
|
|
222
|
+
* if it were an DELETE command. Note that the DELETE command
|
|
223
|
+
* does not execute, instead the original query will be sent
|
|
224
|
+
* to backends, which may or may not cause an actual syntax errors.
|
|
225
|
+
* The command will be sent to all backends in replication mode
|
|
226
|
+
* or master/primary in master/slave mode.
|
|
227
|
+
*/
|
|
228
|
+
if (!strcmp(remote_host, "[local]"))
|
|
229
|
+
{
|
|
230
|
+
pool_log("SimpleQuery: Unable to parse the query: \"%s\" from local client", contents);
|
|
231
|
+
}
|
|
232
|
+
else
|
|
233
|
+
{
|
|
234
|
+
pool_log("SimpleQuery: Unable to parse the query: \"%s\" from client %s(%s)", contents, remote_host, remote_port);
|
|
235
|
+
}
|
|
236
|
+
parse_tree_list = raw_parser(POOL_DUMMY_WRITE_QUERY);
|
|
237
|
+
query_context->is_parse_error = true;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
if (parse_tree_list != NIL)
|
|
242
|
+
{
|
|
243
|
+
/*
|
|
244
|
+
* XXX: Currently we only process the first element of the parse tree.
|
|
245
|
+
* rest of multiple statements are silently discarded.
|
|
246
|
+
*/
|
|
247
|
+
node = (Node *) lfirst(list_head(parse_tree_list));
|
|
248
|
+
|
|
249
|
+
if (pool_config->enable_query_cache &&
|
|
250
|
+
SYSDB_STATUS == CON_UP &&
|
|
251
|
+
IsA(node, SelectStmt) &&
|
|
252
|
+
!(is_select_pgcatalog = IsSelectpgcatalog(node, backend)))
|
|
253
|
+
{
|
|
254
|
+
/*
|
|
255
|
+
* POOL_CONTINUE of here represent cache found, and messages were
|
|
256
|
+
* sent to frontend already.
|
|
257
|
+
*/
|
|
258
|
+
if (pool_execute_query_cache_lookup(frontend, backend, node) == POOL_CONTINUE)
|
|
259
|
+
{
|
|
260
|
+
pool_query_context_destroy(query_context);
|
|
261
|
+
pool_set_skip_reading_from_backends();
|
|
262
|
+
pool_memory_context_switch_to(old_context);
|
|
263
|
+
return POOL_CONTINUE;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/*
|
|
268
|
+
* Start query context
|
|
269
|
+
*/
|
|
270
|
+
pool_start_query(query_context, contents, len, node);
|
|
271
|
+
|
|
272
|
+
/*
|
|
273
|
+
* If the query is DROP DATABASE, after executing it, cache files directory must be discarded.
|
|
274
|
+
* So we have to get the DB's oid before it will be DROPped.
|
|
275
|
+
*/
|
|
276
|
+
if (pool_config->memory_cache_enabled && is_drop_database(node))
|
|
277
|
+
{
|
|
278
|
+
DropdbStmt *stmt = (DropdbStmt *)node;
|
|
279
|
+
query_context->dboid = pool_get_database_oid_from_dbname(stmt->dbname);
|
|
280
|
+
if (query_context->dboid != 0)
|
|
281
|
+
{
|
|
282
|
+
pool_debug("DB's oid to discard its cache directory: dboid = %d", query_context->dboid);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/*
|
|
287
|
+
* Check if multi statement query
|
|
288
|
+
*/
|
|
289
|
+
if (parse_tree_list && list_length(parse_tree_list) > 1)
|
|
290
|
+
{
|
|
291
|
+
query_context->is_multi_statement = true;
|
|
292
|
+
}
|
|
293
|
+
else
|
|
294
|
+
{
|
|
295
|
+
query_context->is_multi_statement = false;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
if (PARALLEL_MODE)
|
|
299
|
+
{
|
|
300
|
+
bool parallel = true;
|
|
301
|
+
|
|
302
|
+
is_parallel_table = is_partition_table(backend,node);
|
|
303
|
+
status = pool_do_parallel_query(frontend, backend, node, ¶llel, &contents, &len);
|
|
304
|
+
if (parallel)
|
|
305
|
+
{
|
|
306
|
+
pool_query_context_destroy(query_context);
|
|
307
|
+
pool_memory_context_switch_to(old_context);
|
|
308
|
+
return status;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/* check COPY FROM STDIN
|
|
313
|
+
* if true, set copy_* variable
|
|
314
|
+
*/
|
|
315
|
+
check_copy_from_stdin(node);
|
|
316
|
+
|
|
317
|
+
/* status reporting? */
|
|
318
|
+
if (IsA(node, VariableShowStmt))
|
|
319
|
+
{
|
|
320
|
+
bool is_valid_show_command = false;
|
|
321
|
+
VariableShowStmt *vnode = (VariableShowStmt *)node;
|
|
322
|
+
|
|
323
|
+
if (!strcmp(sq_config, vnode->name))
|
|
324
|
+
{
|
|
325
|
+
is_valid_show_command = true;
|
|
326
|
+
pool_debug("config reporting");
|
|
327
|
+
config_reporting(frontend, backend);
|
|
328
|
+
}
|
|
329
|
+
else if (!strcmp(sq_pools, vnode->name))
|
|
330
|
+
{
|
|
331
|
+
is_valid_show_command = true;
|
|
332
|
+
pool_debug("pools reporting");
|
|
333
|
+
pools_reporting(frontend, backend);
|
|
334
|
+
}
|
|
335
|
+
else if (!strcmp(sq_processes, vnode->name))
|
|
336
|
+
{
|
|
337
|
+
is_valid_show_command = true;
|
|
338
|
+
pool_debug("processes reporting");
|
|
339
|
+
processes_reporting(frontend, backend);
|
|
340
|
+
}
|
|
341
|
+
else if (!strcmp(sq_nodes, vnode->name))
|
|
342
|
+
{
|
|
343
|
+
is_valid_show_command = true;
|
|
344
|
+
pool_debug("nodes reporting");
|
|
345
|
+
nodes_reporting(frontend, backend);
|
|
346
|
+
}
|
|
347
|
+
else if (!strcmp(sq_version, vnode->name))
|
|
348
|
+
{
|
|
349
|
+
is_valid_show_command = true;
|
|
350
|
+
pool_debug("version reporting");
|
|
351
|
+
version_reporting(frontend, backend);
|
|
352
|
+
}
|
|
353
|
+
else if (!strcmp(sq_cache, vnode->name))
|
|
354
|
+
{
|
|
355
|
+
is_valid_show_command = true;
|
|
356
|
+
pool_debug("cache reporting");
|
|
357
|
+
cache_reporting(frontend, backend);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
if (is_valid_show_command)
|
|
361
|
+
{
|
|
362
|
+
pool_ps_idle_display(backend);
|
|
363
|
+
pool_query_context_destroy(query_context);
|
|
364
|
+
pool_set_skip_reading_from_backends();
|
|
365
|
+
pool_memory_context_switch_to(old_context);
|
|
366
|
+
return POOL_CONTINUE;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/*
|
|
371
|
+
* If the table is to be cached, set is_cache_safe TRUE and register table oids.
|
|
372
|
+
*/
|
|
373
|
+
if (pool_config->memory_cache_enabled && query_context->is_multi_statement == false)
|
|
374
|
+
{
|
|
375
|
+
bool is_select_query = false;
|
|
376
|
+
int num_oids;
|
|
377
|
+
int *oids;
|
|
378
|
+
int i;
|
|
379
|
+
|
|
380
|
+
/* Check if the query is actually SELECT */
|
|
381
|
+
if (is_likely_select && IsA(node, SelectStmt))
|
|
382
|
+
{
|
|
383
|
+
is_select_query = true;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/* Switch the flag of is_cache_safe in session_context */
|
|
387
|
+
if (is_select_query && !query_context->is_parse_error &&
|
|
388
|
+
pool_is_allow_to_cache(query_context->parse_tree,
|
|
389
|
+
query_context->original_query))
|
|
390
|
+
{
|
|
391
|
+
pool_set_cache_safe();
|
|
392
|
+
}
|
|
393
|
+
else
|
|
394
|
+
{
|
|
395
|
+
pool_unset_cache_safe();
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/* If table is to be cached and the query is DML, save the table oid */
|
|
399
|
+
if (!is_select_query && !query_context->is_parse_error)
|
|
400
|
+
{
|
|
401
|
+
num_oids = pool_extract_table_oids(node, &oids);
|
|
402
|
+
|
|
403
|
+
if (num_oids > 0)
|
|
404
|
+
{
|
|
405
|
+
/* Save to oid buffer */
|
|
406
|
+
for (i=0;i<num_oids;i++)
|
|
407
|
+
{
|
|
408
|
+
pool_add_dml_table_oid(oids[i]);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
/*
|
|
415
|
+
* Decide where to send query
|
|
416
|
+
*/
|
|
417
|
+
pool_where_to_send(query_context, query_context->original_query,
|
|
418
|
+
query_context->parse_tree);
|
|
419
|
+
|
|
420
|
+
/*
|
|
421
|
+
* if this is DROP DATABASE command, send USR1 signal to parent and
|
|
422
|
+
* ask it to close all idle connections.
|
|
423
|
+
* XXX This is overkill. It would be better to close the idle
|
|
424
|
+
* connection for the database which DROP DATABASE command tries
|
|
425
|
+
* to drop. This is impossible at this point, since we have no way
|
|
426
|
+
* to pass such info to other processes.
|
|
427
|
+
*/
|
|
428
|
+
if (is_drop_database(node))
|
|
429
|
+
{
|
|
430
|
+
int stime = 5; /* XXX give arbitrary time to allow closing idle connections */
|
|
431
|
+
|
|
432
|
+
pool_debug("Query: sending SIGUSR1 signal to parent");
|
|
433
|
+
|
|
434
|
+
Req_info->kind = CLOSE_IDLE_REQUEST;
|
|
435
|
+
kill(getppid(), SIGUSR1); /* send USR1 signal to parent */
|
|
436
|
+
|
|
437
|
+
/* we need to loop over here since we will get USR1 signal while sleeping */
|
|
438
|
+
while (stime > 0)
|
|
439
|
+
{
|
|
440
|
+
stime = sleep(stime);
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
/*
|
|
445
|
+
* determine if we need to lock the table
|
|
446
|
+
* to keep SERIAL data consistency among servers
|
|
447
|
+
* conditions:
|
|
448
|
+
* - replication is enabled
|
|
449
|
+
* - protocol is V3
|
|
450
|
+
* - statement is INSERT
|
|
451
|
+
* - either "INSERT LOCK" comment exists or insert_lock directive specified
|
|
452
|
+
*/
|
|
453
|
+
if (!RAW_MODE)
|
|
454
|
+
{
|
|
455
|
+
/*
|
|
456
|
+
* If there's only one node to send the command, there's no
|
|
457
|
+
* point to start a transaction.
|
|
458
|
+
*/
|
|
459
|
+
if (pool_multi_node_to_be_sent(query_context))
|
|
460
|
+
{
|
|
461
|
+
/* start a transaction if needed */
|
|
462
|
+
if (start_internal_transaction(frontend, backend, (Node *)node) != POOL_CONTINUE)
|
|
463
|
+
return POOL_END;
|
|
464
|
+
|
|
465
|
+
/* check if need lock */
|
|
466
|
+
lock_kind = need_insert_lock(backend, contents, node);
|
|
467
|
+
if (lock_kind)
|
|
468
|
+
{
|
|
469
|
+
/* if so, issue lock command */
|
|
470
|
+
status = insert_lock(frontend, backend, contents, (InsertStmt *)node, lock_kind);
|
|
471
|
+
if (status != POOL_CONTINUE)
|
|
472
|
+
{
|
|
473
|
+
pool_query_context_destroy(query_context);
|
|
474
|
+
return status;
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
else if (REPLICATION && contents == NULL && start_internal_transaction(frontend, backend, node))
|
|
480
|
+
{
|
|
481
|
+
pool_query_context_destroy(query_context);
|
|
482
|
+
return POOL_ERROR;
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
if (MAJOR(backend) == PROTO_MAJOR_V2 && is_start_transaction_query(node))
|
|
487
|
+
{
|
|
488
|
+
int i;
|
|
489
|
+
|
|
490
|
+
for (i=0;i<NUM_BACKENDS;i++)
|
|
491
|
+
{
|
|
492
|
+
if(VALID_BACKEND(i))
|
|
493
|
+
TSTATE(backend, i) = 'T';
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
if (node)
|
|
498
|
+
{
|
|
499
|
+
POOL_SENT_MESSAGE *msg = NULL;
|
|
500
|
+
|
|
501
|
+
if (IsA(node, PrepareStmt))
|
|
502
|
+
{
|
|
503
|
+
msg = pool_create_sent_message('Q', len, contents, 0,
|
|
504
|
+
((PrepareStmt *)node)->name,
|
|
505
|
+
query_context);
|
|
506
|
+
if (!msg)
|
|
507
|
+
{
|
|
508
|
+
pool_error("SimpleQuery: cannot create query message: %s", strerror(errno));
|
|
509
|
+
return POOL_END;
|
|
510
|
+
}
|
|
511
|
+
session_context->uncompleted_message = msg;
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
string = query_context->original_query;
|
|
516
|
+
|
|
517
|
+
if (!RAW_MODE)
|
|
518
|
+
{
|
|
519
|
+
/* check if query is "COMMIT" or "ROLLBACK" */
|
|
520
|
+
commit = is_commit_or_rollback_query(node);
|
|
521
|
+
|
|
522
|
+
/*
|
|
523
|
+
* Query is not commit/rollback
|
|
524
|
+
*/
|
|
525
|
+
if (!commit)
|
|
526
|
+
{
|
|
527
|
+
char *rewrite_query;
|
|
528
|
+
|
|
529
|
+
if (node)
|
|
530
|
+
{
|
|
531
|
+
POOL_SENT_MESSAGE *msg = NULL;
|
|
532
|
+
|
|
533
|
+
if (IsA(node, PrepareStmt))
|
|
534
|
+
{
|
|
535
|
+
msg = session_context->uncompleted_message;
|
|
536
|
+
}
|
|
537
|
+
else if (IsA(node, ExecuteStmt))
|
|
538
|
+
{
|
|
539
|
+
msg = pool_get_sent_message('Q', ((ExecuteStmt *)node)->name);
|
|
540
|
+
if (!msg)
|
|
541
|
+
msg = pool_get_sent_message('P', ((ExecuteStmt *)node)->name);
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
/* rewrite `now()' to timestamp literal */
|
|
545
|
+
rewrite_query = rewrite_timestamp(backend, query_context->parse_tree, false, msg);
|
|
546
|
+
|
|
547
|
+
/*
|
|
548
|
+
* If the query is BEGIN READ WRITE or
|
|
549
|
+
* BEGIN ... SERIALIZABLE in master/slave mode,
|
|
550
|
+
* we send BEGIN to slaves/standbys instead.
|
|
551
|
+
* original_query which is BEGIN READ WRITE is sent to primary.
|
|
552
|
+
* rewritten_query which is BEGIN is sent to standbys.
|
|
553
|
+
*/
|
|
554
|
+
if (pool_need_to_treat_as_if_default_transaction(query_context))
|
|
555
|
+
{
|
|
556
|
+
rewrite_query = pstrdup("BEGIN");
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
if (rewrite_query != NULL)
|
|
560
|
+
{
|
|
561
|
+
query_context->rewritten_query = rewrite_query;
|
|
562
|
+
query_context->rewritten_length = strlen(rewrite_query) + 1;
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
/*
|
|
567
|
+
* Optimization effort: If there's only one session, we do
|
|
568
|
+
* not need to wait for the master node's response, and
|
|
569
|
+
* could execute the query concurrently.
|
|
570
|
+
*/
|
|
571
|
+
if (pool_config->num_init_children == 1)
|
|
572
|
+
{
|
|
573
|
+
/* Send query to all DB nodes at once */
|
|
574
|
+
status = pool_send_and_wait(query_context, 0, 0);
|
|
575
|
+
/* free_parser(); */
|
|
576
|
+
return status;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
/* Send the query to master node */
|
|
580
|
+
if (pool_send_and_wait(query_context, 1, MASTER_NODE_ID) != POOL_CONTINUE)
|
|
581
|
+
{
|
|
582
|
+
pool_query_context_destroy(query_context);
|
|
583
|
+
return POOL_END;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
/* Check specific errors */
|
|
587
|
+
specific_error = check_errors(backend, MASTER_NODE_ID);
|
|
588
|
+
if (specific_error)
|
|
589
|
+
{
|
|
590
|
+
/* log error message */
|
|
591
|
+
generate_error_message("SimpleQuery: ", specific_error, contents);
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
if (specific_error)
|
|
596
|
+
{
|
|
597
|
+
char msg[1024] = POOL_ERROR_QUERY; /* large enough */
|
|
598
|
+
int len = strlen(msg);
|
|
599
|
+
|
|
600
|
+
memset(msg + len, 0, sizeof(int));
|
|
601
|
+
|
|
602
|
+
/* send query to other nodes */
|
|
603
|
+
query_context->rewritten_query = msg;
|
|
604
|
+
query_context->rewritten_length = len;
|
|
605
|
+
if (pool_send_and_wait(query_context, -1, MASTER_NODE_ID) != POOL_CONTINUE)
|
|
606
|
+
return POOL_END;
|
|
607
|
+
}
|
|
608
|
+
else
|
|
609
|
+
{
|
|
610
|
+
/*
|
|
611
|
+
* Send the query to other than master node.
|
|
612
|
+
*/
|
|
613
|
+
if (pool_send_and_wait(query_context, -1, MASTER_NODE_ID) != POOL_CONTINUE)
|
|
614
|
+
{
|
|
615
|
+
pool_query_context_destroy(query_context);
|
|
616
|
+
return POOL_END;
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
/* Send "COMMIT" or "ROLLBACK" to only master node if query is "COMMIT" or "ROLLBACK" */
|
|
621
|
+
if (commit)
|
|
622
|
+
{
|
|
623
|
+
if (pool_send_and_wait(query_context, 1, MASTER_NODE_ID) != POOL_CONTINUE)
|
|
624
|
+
{
|
|
625
|
+
pool_query_context_destroy(query_context);
|
|
626
|
+
return POOL_END;
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
/* free_parser(); */
|
|
630
|
+
}
|
|
631
|
+
else
|
|
632
|
+
{
|
|
633
|
+
if (pool_send_and_wait(query_context, 1, MASTER_NODE_ID) != POOL_CONTINUE)
|
|
634
|
+
{
|
|
635
|
+
pool_query_context_destroy(query_context);
|
|
636
|
+
return POOL_END;
|
|
637
|
+
}
|
|
638
|
+
/* free_parser(); */
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
/* switch memory context */
|
|
642
|
+
pool_memory_context_switch_to(old_context);
|
|
643
|
+
|
|
644
|
+
return POOL_CONTINUE;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
/*
|
|
648
|
+
* process EXECUTE (V3 only)
|
|
649
|
+
*/
|
|
650
|
+
POOL_STATUS Execute(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *backend,
|
|
651
|
+
int len, char *contents)
|
|
652
|
+
{
|
|
653
|
+
int commit = 0;
|
|
654
|
+
char *query = NULL;
|
|
655
|
+
Node *node;
|
|
656
|
+
int specific_error = 0;
|
|
657
|
+
POOL_SESSION_CONTEXT *session_context;
|
|
658
|
+
POOL_QUERY_CONTEXT *query_context;
|
|
659
|
+
POOL_SENT_MESSAGE *bind_msg;
|
|
660
|
+
|
|
661
|
+
/* Get session context */
|
|
662
|
+
session_context = pool_get_session_context();
|
|
663
|
+
if (!session_context)
|
|
664
|
+
{
|
|
665
|
+
pool_error("Execute: cannot get session context");
|
|
666
|
+
return POOL_END;
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
pool_debug("Execute: portal name <%s>", contents);
|
|
670
|
+
|
|
671
|
+
bind_msg = pool_get_sent_message('B', contents);
|
|
672
|
+
if (!bind_msg)
|
|
673
|
+
{
|
|
674
|
+
pool_error("Execute: cannot get bind message");
|
|
675
|
+
return POOL_END;
|
|
676
|
+
}
|
|
677
|
+
if(!bind_msg->query_context)
|
|
678
|
+
{
|
|
679
|
+
pool_error("Execute: cannot get query context");
|
|
680
|
+
return POOL_END;
|
|
681
|
+
}
|
|
682
|
+
if (!bind_msg->query_context->original_query)
|
|
683
|
+
{
|
|
684
|
+
pool_error("Execute: cannot get original query");
|
|
685
|
+
return POOL_END;
|
|
686
|
+
}
|
|
687
|
+
if (!bind_msg->query_context->parse_tree)
|
|
688
|
+
{
|
|
689
|
+
pool_error("Execute: cannot get parse tree");
|
|
690
|
+
return POOL_END;
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
query_context = bind_msg->query_context;
|
|
694
|
+
node = bind_msg->query_context->parse_tree;
|
|
695
|
+
query = bind_msg->query_context->original_query;
|
|
696
|
+
|
|
697
|
+
strlcpy(query_string_buffer, query, sizeof(query_string_buffer));
|
|
698
|
+
|
|
699
|
+
pool_debug("Execute: query string = <%s>", query);
|
|
700
|
+
|
|
701
|
+
/*
|
|
702
|
+
* Fetch memory cache if possible
|
|
703
|
+
*/
|
|
704
|
+
if (pool_config->memory_cache_enabled && pool_is_likely_select(query))
|
|
705
|
+
{
|
|
706
|
+
bool foundp;
|
|
707
|
+
POOL_STATUS status;
|
|
708
|
+
char *search_query = NULL;
|
|
709
|
+
int len;
|
|
710
|
+
char *tmp;
|
|
711
|
+
#define STR_ALLOC_SIZE 1024
|
|
712
|
+
|
|
713
|
+
len = strlen(query)+1;
|
|
714
|
+
search_query = (char *)malloc(len);
|
|
715
|
+
if (search_query == NULL)
|
|
716
|
+
{
|
|
717
|
+
pool_error("Execute: malloc failed");
|
|
718
|
+
return POOL_END;
|
|
719
|
+
}
|
|
720
|
+
strcpy(search_query, query);
|
|
721
|
+
|
|
722
|
+
/*
|
|
723
|
+
* Add bind message's info to query to search.
|
|
724
|
+
*/
|
|
725
|
+
if (query_context->is_cache_safe && bind_msg->param_offset && bind_msg->contents)
|
|
726
|
+
{
|
|
727
|
+
/* Extract binary contents from bind message */
|
|
728
|
+
char *query_in_bind_msg = bind_msg->contents + bind_msg->param_offset;
|
|
729
|
+
char hex_str[4]; /* 02X chars + white space + null end */
|
|
730
|
+
int i;
|
|
731
|
+
int alloc_len;
|
|
732
|
+
|
|
733
|
+
alloc_len = (len/STR_ALLOC_SIZE+1)*STR_ALLOC_SIZE;
|
|
734
|
+
search_query = realloc(search_query, alloc_len);
|
|
735
|
+
if (search_query == NULL)
|
|
736
|
+
{
|
|
737
|
+
pool_error("Execute: realloc failed");
|
|
738
|
+
return POOL_END;
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
for (i = 0; i < bind_msg->len - bind_msg->param_offset; i++)
|
|
742
|
+
{
|
|
743
|
+
int hexlen;
|
|
744
|
+
|
|
745
|
+
snprintf(hex_str, sizeof(hex_str), (i == 0) ? " %02X" : "%02X", 0xff & query_in_bind_msg[i]);
|
|
746
|
+
hexlen = strlen(hex_str);
|
|
747
|
+
|
|
748
|
+
if ((len+hexlen) >= alloc_len)
|
|
749
|
+
{
|
|
750
|
+
alloc_len += STR_ALLOC_SIZE;
|
|
751
|
+
search_query = realloc(search_query, alloc_len);
|
|
752
|
+
if (search_query == NULL)
|
|
753
|
+
{
|
|
754
|
+
pool_error("Execute: realloc failed");
|
|
755
|
+
return POOL_END;
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
strcat(search_query, hex_str);
|
|
759
|
+
len += hexlen;
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
query_context->query_w_hex = search_query;
|
|
763
|
+
|
|
764
|
+
/*
|
|
765
|
+
* When a transaction is committed, query_context->temp_cache->query is used
|
|
766
|
+
* to create md5 hash to search for query cache.
|
|
767
|
+
* So overwrite the query text in temp cache to the one with the hex of bind message.
|
|
768
|
+
* If not, md5 hash will be created by the query text without bind message, and
|
|
769
|
+
* it will happen to find cache never or to get a wrong result.
|
|
770
|
+
*
|
|
771
|
+
* However, It is possible that temp_cache does not exist.
|
|
772
|
+
* Consider following scenario:
|
|
773
|
+
* - In the previous execute cache is overflowed, and
|
|
774
|
+
* temp_cache discarded.
|
|
775
|
+
* - In the subsequent bind/execute uses the same portal
|
|
776
|
+
*/
|
|
777
|
+
if (query_context->temp_cache)
|
|
778
|
+
{
|
|
779
|
+
tmp = (char *)malloc(sizeof(char) * strlen(search_query) + 1);
|
|
780
|
+
if (tmp == NULL)
|
|
781
|
+
{
|
|
782
|
+
pool_error("Execute: malloc failed");
|
|
783
|
+
return POOL_END;
|
|
784
|
+
}
|
|
785
|
+
free(query_context->temp_cache->query);
|
|
786
|
+
query_context->temp_cache->query = tmp;
|
|
787
|
+
strcpy(query_context->temp_cache->query, search_query);
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
/* If the query is SELECT from table to cache, try to fetch cached result. */
|
|
792
|
+
status = pool_fetch_from_memory_cache(frontend, backend, search_query, &foundp);
|
|
793
|
+
|
|
794
|
+
if (status != POOL_CONTINUE)
|
|
795
|
+
return status;
|
|
796
|
+
|
|
797
|
+
if (foundp)
|
|
798
|
+
{
|
|
799
|
+
pool_ps_idle_display(backend);
|
|
800
|
+
pool_set_skip_reading_from_backends();
|
|
801
|
+
pool_stats_count_up_num_cache_hits();
|
|
802
|
+
pool_unset_query_in_progress();
|
|
803
|
+
return POOL_CONTINUE;
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
session_context->query_context = query_context;
|
|
808
|
+
/*
|
|
809
|
+
* Calling pool_where_to_send here is dangerous because the node
|
|
810
|
+
* parse/bind has been sent could be change by
|
|
811
|
+
* pool_where_to_send() and it leads to "portal not found"
|
|
812
|
+
* etc. errors.
|
|
813
|
+
*/
|
|
814
|
+
|
|
815
|
+
/* check if query is "COMMIT" or "ROLLBACK" */
|
|
816
|
+
commit = is_commit_or_rollback_query(node);
|
|
817
|
+
|
|
818
|
+
if (REPLICATION || PARALLEL_MODE)
|
|
819
|
+
{
|
|
820
|
+
/*
|
|
821
|
+
* Query is not commit/rollback
|
|
822
|
+
*/
|
|
823
|
+
if (!commit)
|
|
824
|
+
{
|
|
825
|
+
/* Send the query to master node */
|
|
826
|
+
if (pool_extended_send_and_wait(query_context, "E", len, contents, 1, MASTER_NODE_ID) != POOL_CONTINUE)
|
|
827
|
+
{
|
|
828
|
+
return POOL_END;
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
/* Check specific errors */
|
|
832
|
+
specific_error = check_errors(backend, MASTER_NODE_ID);
|
|
833
|
+
if (specific_error)
|
|
834
|
+
{
|
|
835
|
+
/* log error message */
|
|
836
|
+
generate_error_message("Execute: ", specific_error, contents);
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
if (specific_error)
|
|
841
|
+
{
|
|
842
|
+
char msg[1024] = "pgpool_error_portal"; /* large enough */
|
|
843
|
+
int len = strlen(msg);
|
|
844
|
+
|
|
845
|
+
memset(msg + len, 0, sizeof(int));
|
|
846
|
+
|
|
847
|
+
/* send query to other nodes */
|
|
848
|
+
if (pool_extended_send_and_wait(query_context, "E", len, msg, -1, MASTER_NODE_ID) != POOL_CONTINUE)
|
|
849
|
+
return POOL_END;
|
|
850
|
+
}
|
|
851
|
+
else
|
|
852
|
+
{
|
|
853
|
+
if (pool_extended_send_and_wait(query_context, "E", len, contents, -1, MASTER_NODE_ID) != POOL_CONTINUE)
|
|
854
|
+
return POOL_END;
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
/* send "COMMIT" or "ROLLBACK" to only master node if query is "COMMIT" or "ROLLBACK" */
|
|
858
|
+
if (commit)
|
|
859
|
+
{
|
|
860
|
+
if (pool_extended_send_and_wait(query_context, "E", len, contents, 1, MASTER_NODE_ID) != POOL_CONTINUE)
|
|
861
|
+
{
|
|
862
|
+
return POOL_END;
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
else
|
|
867
|
+
{
|
|
868
|
+
if (pool_extended_send_and_wait(query_context, "E", len, contents, 1, MASTER_NODE_ID) != POOL_CONTINUE)
|
|
869
|
+
{
|
|
870
|
+
return POOL_END;
|
|
871
|
+
}
|
|
872
|
+
if (pool_extended_send_and_wait(query_context, "E", len, contents, -1, MASTER_NODE_ID) != POOL_CONTINUE)
|
|
873
|
+
{
|
|
874
|
+
return POOL_END;
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
return POOL_CONTINUE;
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
/*
|
|
882
|
+
* process Parse (V3 only)
|
|
883
|
+
*/
|
|
884
|
+
POOL_STATUS Parse(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *backend,
|
|
885
|
+
int len, char *contents)
|
|
886
|
+
{
|
|
887
|
+
int deadlock_detected = 0;
|
|
888
|
+
int insert_stmt_with_lock = 0;
|
|
889
|
+
char *name;
|
|
890
|
+
char *stmt;
|
|
891
|
+
List *parse_tree_list;
|
|
892
|
+
Node *node = NULL;
|
|
893
|
+
POOL_SENT_MESSAGE *msg;
|
|
894
|
+
POOL_STATUS status;
|
|
895
|
+
POOL_MEMORY_POOL *old_context;
|
|
896
|
+
POOL_SESSION_CONTEXT *session_context;
|
|
897
|
+
POOL_QUERY_CONTEXT *query_context;
|
|
898
|
+
|
|
899
|
+
/* Get session context */
|
|
900
|
+
session_context = pool_get_session_context();
|
|
901
|
+
if (!session_context)
|
|
902
|
+
{
|
|
903
|
+
pool_error("Parse: cannot get session context");
|
|
904
|
+
return POOL_END;
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
/* Create query context */
|
|
908
|
+
query_context = pool_init_query_context();
|
|
909
|
+
|
|
910
|
+
pool_debug("Parse: statement name <%s>", contents);
|
|
911
|
+
|
|
912
|
+
name = contents;
|
|
913
|
+
stmt = contents + strlen(contents) + 1;
|
|
914
|
+
|
|
915
|
+
/* switch memory context */
|
|
916
|
+
if (pool_memory == NULL)
|
|
917
|
+
pool_memory = pool_memory_create(PARSER_BLOCK_SIZE);
|
|
918
|
+
old_context = pool_memory_context_switch_to(query_context->memory_context);
|
|
919
|
+
|
|
920
|
+
/* parse SQL string */
|
|
921
|
+
parse_tree_list = raw_parser(stmt);
|
|
922
|
+
|
|
923
|
+
if (parse_tree_list == NIL)
|
|
924
|
+
{
|
|
925
|
+
/* is the query empty? */
|
|
926
|
+
if (*stmt == '\0' || *stmt == ';')
|
|
927
|
+
{
|
|
928
|
+
/*
|
|
929
|
+
* JBoss sends empty queries for checking connections.
|
|
930
|
+
* We replace the empty query with SELECT command not
|
|
931
|
+
* to affect load balance.
|
|
932
|
+
* [Pgpool-general] Confused about JDBC and load balancing
|
|
933
|
+
*/
|
|
934
|
+
parse_tree_list = raw_parser(POOL_DUMMY_READ_QUERY);
|
|
935
|
+
}
|
|
936
|
+
else
|
|
937
|
+
{
|
|
938
|
+
/*
|
|
939
|
+
* Unable to parse the query. Probably syntax error or the
|
|
940
|
+
* query is too new and our parser cannot understand. Treat as
|
|
941
|
+
* if it were an DELETE command. Note that the DELETE command
|
|
942
|
+
* does not execute, instead the original query will be sent
|
|
943
|
+
* to backends, which may or may not cause an actual syntax errors.
|
|
944
|
+
* The command will be sent to all backends in replication mode
|
|
945
|
+
* or master/primary in master/slave mode.
|
|
946
|
+
*/
|
|
947
|
+
if (!strcmp(remote_host, "[local]"))
|
|
948
|
+
{
|
|
949
|
+
pool_log("Parse: Unable to parse the query: \"%s\" from local client", stmt);
|
|
950
|
+
}
|
|
951
|
+
else
|
|
952
|
+
{
|
|
953
|
+
pool_log("Parse: Unable to parse the query: \"%s\" from client %s(%s)", stmt, remote_host, remote_port);
|
|
954
|
+
}
|
|
955
|
+
parse_tree_list = raw_parser(POOL_DUMMY_WRITE_QUERY);
|
|
956
|
+
query_context->is_parse_error = true;
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
if (parse_tree_list != NIL)
|
|
961
|
+
{
|
|
962
|
+
/* Save last query string for logging purpose */
|
|
963
|
+
snprintf(query_string_buffer, sizeof(query_string_buffer), "Parse: %s", stmt);
|
|
964
|
+
|
|
965
|
+
node = (Node *) lfirst(list_head(parse_tree_list));
|
|
966
|
+
|
|
967
|
+
insert_stmt_with_lock = need_insert_lock(backend, stmt, node);
|
|
968
|
+
|
|
969
|
+
/*
|
|
970
|
+
* Start query context
|
|
971
|
+
*/
|
|
972
|
+
pool_start_query(query_context, pstrdup(stmt), strlen(stmt) + 1, node);
|
|
973
|
+
|
|
974
|
+
msg = pool_create_sent_message('P', len, contents, 0, name, query_context);
|
|
975
|
+
if (!msg)
|
|
976
|
+
{
|
|
977
|
+
pool_error("Parse: cannot create parse message: %s", strerror(errno));
|
|
978
|
+
return POOL_END;
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
session_context->uncompleted_message = msg;
|
|
982
|
+
|
|
983
|
+
/*
|
|
984
|
+
* If the table is to be cached, set is_cache_safe TRUE and register table oids.
|
|
985
|
+
*/
|
|
986
|
+
if (pool_config->memory_cache_enabled)
|
|
987
|
+
{
|
|
988
|
+
bool is_likely_select = false;
|
|
989
|
+
bool is_select_query = false;
|
|
990
|
+
int num_oids;
|
|
991
|
+
int *oids;
|
|
992
|
+
int i;
|
|
993
|
+
|
|
994
|
+
/* Check if the query is actually SELECT */
|
|
995
|
+
is_likely_select = pool_is_likely_select(query_context->original_query);
|
|
996
|
+
if (is_likely_select && IsA(node, SelectStmt))
|
|
997
|
+
{
|
|
998
|
+
is_select_query = true;
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
/* Switch the flag of is_cache_safe in session_context */
|
|
1002
|
+
if (is_select_query && !query_context->is_parse_error &&
|
|
1003
|
+
pool_is_allow_to_cache(query_context->parse_tree,
|
|
1004
|
+
query_context->original_query))
|
|
1005
|
+
{
|
|
1006
|
+
pool_set_cache_safe();
|
|
1007
|
+
}
|
|
1008
|
+
else
|
|
1009
|
+
{
|
|
1010
|
+
pool_unset_cache_safe();
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
/* If table is to be cached and the query is DML, save the table oid */
|
|
1014
|
+
if (!is_select_query && !query_context->is_parse_error)
|
|
1015
|
+
{
|
|
1016
|
+
num_oids = pool_extract_table_oids(node, &oids);
|
|
1017
|
+
|
|
1018
|
+
if (num_oids > 0)
|
|
1019
|
+
{
|
|
1020
|
+
/* Save to oid buffer */
|
|
1021
|
+
for (i=0;i<num_oids;i++)
|
|
1022
|
+
{
|
|
1023
|
+
pool_add_dml_table_oid(oids[i]);
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
/*
|
|
1030
|
+
* Decide where to send query
|
|
1031
|
+
*/
|
|
1032
|
+
pool_where_to_send(query_context, query_context->original_query,
|
|
1033
|
+
query_context->parse_tree);
|
|
1034
|
+
{
|
|
1035
|
+
int alloc_len = len - strlen(stmt) + strlen(query_context->original_query);
|
|
1036
|
+
contents = palloc(alloc_len);
|
|
1037
|
+
strcpy(contents, name);
|
|
1038
|
+
strcpy(contents + strlen(name) + 1, query_context->original_query);
|
|
1039
|
+
memcpy(contents + strlen(name) + 1 + strlen(query_context->original_query) + 1,
|
|
1040
|
+
stmt + strlen(stmt) + 1,
|
|
1041
|
+
len - (strlen(name) + 1 + strlen(stmt) + 1));
|
|
1042
|
+
len = alloc_len;
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
if (REPLICATION)
|
|
1046
|
+
{
|
|
1047
|
+
char *rewrite_query;
|
|
1048
|
+
bool rewrite_to_params = true;
|
|
1049
|
+
|
|
1050
|
+
/*
|
|
1051
|
+
* rewrite `now()'.
|
|
1052
|
+
* if stmt is unnamed, we rewrite `now()' to timestamp constant.
|
|
1053
|
+
* else we rewrite `now()' to params and expand that at Bind
|
|
1054
|
+
* message.
|
|
1055
|
+
*/
|
|
1056
|
+
if (*name == '\0')
|
|
1057
|
+
rewrite_to_params = false;
|
|
1058
|
+
msg->num_tsparams = 0;
|
|
1059
|
+
rewrite_query = rewrite_timestamp(backend, node, rewrite_to_params, msg);
|
|
1060
|
+
if (rewrite_query != NULL)
|
|
1061
|
+
{
|
|
1062
|
+
int alloc_len = len - strlen(stmt) + strlen(rewrite_query);
|
|
1063
|
+
contents = pool_memory_realloc(session_context->memory_context,
|
|
1064
|
+
msg->contents, alloc_len);
|
|
1065
|
+
strcpy(contents, name);
|
|
1066
|
+
strcpy(contents + strlen(name) + 1, rewrite_query);
|
|
1067
|
+
memcpy(contents + strlen(name) + strlen(rewrite_query) + 2,
|
|
1068
|
+
stmt + strlen(stmt) + 1,
|
|
1069
|
+
len - (strlen(name) + strlen(stmt) + 2));
|
|
1070
|
+
|
|
1071
|
+
len = alloc_len;
|
|
1072
|
+
name = contents;
|
|
1073
|
+
stmt = contents + strlen(name) + 1;
|
|
1074
|
+
pool_debug("Parse: rewrite query %s %s len=%d", name, stmt, len);
|
|
1075
|
+
|
|
1076
|
+
msg->len = len;
|
|
1077
|
+
msg->contents = contents;
|
|
1078
|
+
|
|
1079
|
+
query_context->rewritten_query = rewrite_query;
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
/*
|
|
1084
|
+
* If the query is BEGIN READ WRITE in master/slave mode,
|
|
1085
|
+
* we send BEGIN instead of it to slaves/standbys.
|
|
1086
|
+
* original_query which is BEGIN READ WRITE is sent to primary.
|
|
1087
|
+
* rewritten_query which is BEGIN is sent to standbys.
|
|
1088
|
+
*/
|
|
1089
|
+
if (is_start_transaction_query(query_context->parse_tree) &&
|
|
1090
|
+
is_read_write((TransactionStmt *)query_context->parse_tree) &&
|
|
1091
|
+
MASTER_SLAVE)
|
|
1092
|
+
{
|
|
1093
|
+
query_context->rewritten_query = pstrdup("BEGIN");
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
pool_memory_context_switch_to(old_context);
|
|
1098
|
+
|
|
1099
|
+
/*
|
|
1100
|
+
* If in replication mode, send "SYNC" message if not in a transaction.
|
|
1101
|
+
*/
|
|
1102
|
+
if (REPLICATION)
|
|
1103
|
+
{
|
|
1104
|
+
char kind;
|
|
1105
|
+
|
|
1106
|
+
if (TSTATE(backend, MASTER_NODE_ID) != 'T')
|
|
1107
|
+
{
|
|
1108
|
+
int i;
|
|
1109
|
+
|
|
1110
|
+
/* synchronize transaction state */
|
|
1111
|
+
for (i = 0; i < NUM_BACKENDS; i++)
|
|
1112
|
+
{
|
|
1113
|
+
if (!VALID_BACKEND(i))
|
|
1114
|
+
continue;
|
|
1115
|
+
|
|
1116
|
+
/* send sync message */
|
|
1117
|
+
send_extended_protocol_message(backend, i, "S", 0, "");
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
kind = pool_read_kind(backend);
|
|
1121
|
+
if (kind != 'Z')
|
|
1122
|
+
{
|
|
1123
|
+
pool_query_context_destroy(query_context);
|
|
1124
|
+
return POOL_END;
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
/*
|
|
1128
|
+
* SYNC message returns "Ready for Query" message.
|
|
1129
|
+
*/
|
|
1130
|
+
if (ReadyForQuery(frontend, backend, 0, false) != POOL_CONTINUE)
|
|
1131
|
+
{
|
|
1132
|
+
pool_query_context_destroy(query_context);
|
|
1133
|
+
return POOL_END;
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
/*
|
|
1137
|
+
* set in_progress flag, because ReadyForQuery unset it.
|
|
1138
|
+
* in_progress flag influences VALID_BACKEND.
|
|
1139
|
+
*/
|
|
1140
|
+
if (!pool_is_query_in_progress())
|
|
1141
|
+
pool_set_query_in_progress();
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
if (is_strict_query(query_context->parse_tree))
|
|
1145
|
+
{
|
|
1146
|
+
start_internal_transaction(frontend, backend, query_context->parse_tree);
|
|
1147
|
+
allow_close_transaction = 1;
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
if (insert_stmt_with_lock)
|
|
1151
|
+
{
|
|
1152
|
+
/* start a transaction if needed and lock the table */
|
|
1153
|
+
status = insert_lock(frontend, backend, stmt, (InsertStmt *)query_context->parse_tree, insert_stmt_with_lock);
|
|
1154
|
+
if (status != POOL_CONTINUE)
|
|
1155
|
+
{
|
|
1156
|
+
pool_query_context_destroy(query_context);
|
|
1157
|
+
return status;
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
/*
|
|
1163
|
+
* Cannot call free_parser() here. Since "string" might be allocated in parser context.
|
|
1164
|
+
* free_parser();
|
|
1165
|
+
*/
|
|
1166
|
+
|
|
1167
|
+
if (REPLICATION || PARALLEL_MODE || MASTER_SLAVE)
|
|
1168
|
+
{
|
|
1169
|
+
/*
|
|
1170
|
+
* We must synchronize because Parse message acquires table
|
|
1171
|
+
* locks.
|
|
1172
|
+
*/
|
|
1173
|
+
pool_debug("Parse: waiting for master completing the query");
|
|
1174
|
+
if (pool_extended_send_and_wait(query_context, "P", len, contents, 1, MASTER_NODE_ID) != POOL_CONTINUE)
|
|
1175
|
+
{
|
|
1176
|
+
pool_query_context_destroy(query_context);
|
|
1177
|
+
return POOL_END;
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
/*
|
|
1181
|
+
* We must check deadlock error because a aborted transaction
|
|
1182
|
+
* by detecting deadlock isn't same on all nodes.
|
|
1183
|
+
* If a transaction is aborted on master node, pgpool send a
|
|
1184
|
+
* error query to another nodes.
|
|
1185
|
+
*/
|
|
1186
|
+
deadlock_detected = detect_deadlock_error(MASTER(backend), MAJOR(backend));
|
|
1187
|
+
if (deadlock_detected < 0)
|
|
1188
|
+
{
|
|
1189
|
+
pool_query_context_destroy(query_context);
|
|
1190
|
+
return POOL_END;
|
|
1191
|
+
}
|
|
1192
|
+
else
|
|
1193
|
+
{
|
|
1194
|
+
/*
|
|
1195
|
+
* Check if other than deadlock error detected. If so, emit
|
|
1196
|
+
* log. This is useful when invalid encoding error occurs. In
|
|
1197
|
+
* this case, PostgreSQL does not report what statement caused
|
|
1198
|
+
* that error and make users confused.
|
|
1199
|
+
*/
|
|
1200
|
+
per_node_error_log(backend, MASTER_NODE_ID, stmt, "Parse: Error or notice message from backend: ", true);
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1203
|
+
if (deadlock_detected)
|
|
1204
|
+
{
|
|
1205
|
+
POOL_QUERY_CONTEXT *error_qc;
|
|
1206
|
+
|
|
1207
|
+
error_qc = pool_init_query_context();
|
|
1208
|
+
pool_start_query(error_qc, POOL_ERROR_QUERY, strlen(POOL_ERROR_QUERY) + 1, node);
|
|
1209
|
+
pool_copy_prep_where(query_context->where_to_send, error_qc->where_to_send);
|
|
1210
|
+
|
|
1211
|
+
pool_log("Parse: received deadlock error message from master node");
|
|
1212
|
+
|
|
1213
|
+
if (pool_send_and_wait(error_qc, -1, MASTER_NODE_ID) != POOL_CONTINUE)
|
|
1214
|
+
{
|
|
1215
|
+
pool_query_context_destroy(query_context);
|
|
1216
|
+
return POOL_END;
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
pool_query_context_destroy(error_qc);
|
|
1220
|
+
pool_set_query_in_progress();
|
|
1221
|
+
session_context->query_context = query_context;
|
|
1222
|
+
}
|
|
1223
|
+
else
|
|
1224
|
+
{
|
|
1225
|
+
if (pool_extended_send_and_wait(query_context, "P", len, contents, -1, MASTER_NODE_ID) != POOL_CONTINUE)
|
|
1226
|
+
{
|
|
1227
|
+
pool_query_context_destroy(query_context);
|
|
1228
|
+
return POOL_END;
|
|
1229
|
+
}
|
|
1230
|
+
}
|
|
1231
|
+
}
|
|
1232
|
+
else
|
|
1233
|
+
{
|
|
1234
|
+
if (pool_extended_send_and_wait(query_context, "P", len, contents, 1, MASTER_NODE_ID) != POOL_CONTINUE)
|
|
1235
|
+
{
|
|
1236
|
+
pool_query_context_destroy(query_context);
|
|
1237
|
+
return POOL_END;
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
/*
|
|
1242
|
+
* Ok. we are safe to call free_parser();
|
|
1243
|
+
*/
|
|
1244
|
+
/* free_parser(); */
|
|
1245
|
+
|
|
1246
|
+
return POOL_CONTINUE;
|
|
1247
|
+
|
|
1248
|
+
}
|
|
1249
|
+
|
|
1250
|
+
POOL_STATUS Bind(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *backend,
|
|
1251
|
+
int len, char *contents)
|
|
1252
|
+
{
|
|
1253
|
+
char *pstmt_name;
|
|
1254
|
+
char *portal_name;
|
|
1255
|
+
char *rewrite_msg;
|
|
1256
|
+
POOL_SENT_MESSAGE *parse_msg;
|
|
1257
|
+
POOL_SENT_MESSAGE *bind_msg;
|
|
1258
|
+
POOL_SESSION_CONTEXT *session_context;
|
|
1259
|
+
POOL_QUERY_CONTEXT *query_context;
|
|
1260
|
+
int insert_stmt_with_lock = 0;
|
|
1261
|
+
|
|
1262
|
+
/* Get session context */
|
|
1263
|
+
session_context = pool_get_session_context();
|
|
1264
|
+
if (!session_context)
|
|
1265
|
+
{
|
|
1266
|
+
pool_error("Bind: cannot get session context");
|
|
1267
|
+
return POOL_END;
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1270
|
+
/*
|
|
1271
|
+
* Rewrite message
|
|
1272
|
+
*/
|
|
1273
|
+
portal_name = contents;
|
|
1274
|
+
pstmt_name = contents + strlen(portal_name) + 1;
|
|
1275
|
+
|
|
1276
|
+
parse_msg = pool_get_sent_message('Q', pstmt_name);
|
|
1277
|
+
if (!parse_msg)
|
|
1278
|
+
parse_msg = pool_get_sent_message('P', pstmt_name);
|
|
1279
|
+
if (!parse_msg)
|
|
1280
|
+
{
|
|
1281
|
+
pool_error("Bind: cannot get parse message \"%s\"", pstmt_name);
|
|
1282
|
+
return POOL_END;
|
|
1283
|
+
}
|
|
1284
|
+
|
|
1285
|
+
bind_msg = pool_create_sent_message('B', len, contents,
|
|
1286
|
+
parse_msg->num_tsparams, portal_name,
|
|
1287
|
+
parse_msg->query_context);
|
|
1288
|
+
if (!bind_msg)
|
|
1289
|
+
{
|
|
1290
|
+
pool_error("Bind: cannot create bind message: %s", strerror(errno));
|
|
1291
|
+
return POOL_END;
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1294
|
+
query_context = parse_msg->query_context;
|
|
1295
|
+
if (!query_context)
|
|
1296
|
+
{
|
|
1297
|
+
pool_error("Bind: cannot get query context");
|
|
1298
|
+
return POOL_END;
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1301
|
+
/*
|
|
1302
|
+
* If the query can be cached, save its offset of query text in bind message's content.
|
|
1303
|
+
*/
|
|
1304
|
+
if (query_context->is_cache_safe)
|
|
1305
|
+
{
|
|
1306
|
+
bind_msg->param_offset = sizeof(char) * (strlen(portal_name) + strlen(pstmt_name) + 2);
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1309
|
+
session_context->uncompleted_message = bind_msg;
|
|
1310
|
+
|
|
1311
|
+
/* rewrite bind message */
|
|
1312
|
+
if (REPLICATION && bind_msg->num_tsparams > 0)
|
|
1313
|
+
{
|
|
1314
|
+
rewrite_msg = bind_rewrite_timestamp(backend, bind_msg, contents, &len);
|
|
1315
|
+
if (rewrite_msg != NULL)
|
|
1316
|
+
contents = rewrite_msg;
|
|
1317
|
+
}
|
|
1318
|
+
|
|
1319
|
+
session_context->query_context = query_context;
|
|
1320
|
+
|
|
1321
|
+
if (pool_config->load_balance_mode && pool_is_writing_transaction())
|
|
1322
|
+
{
|
|
1323
|
+
pool_where_to_send(query_context, query_context->original_query,
|
|
1324
|
+
query_context->parse_tree);
|
|
1325
|
+
|
|
1326
|
+
if (parse_before_bind(frontend, backend, parse_msg) != POOL_CONTINUE)
|
|
1327
|
+
return POOL_END;
|
|
1328
|
+
}
|
|
1329
|
+
|
|
1330
|
+
/*
|
|
1331
|
+
* Start a transaction if necessary
|
|
1332
|
+
*/
|
|
1333
|
+
pool_debug("Bind: checking strict query");
|
|
1334
|
+
if (is_strict_query(query_context->parse_tree))
|
|
1335
|
+
{
|
|
1336
|
+
pool_debug("Bind: strict query");
|
|
1337
|
+
start_internal_transaction(frontend, backend, query_context->parse_tree);
|
|
1338
|
+
allow_close_transaction = 1;
|
|
1339
|
+
}
|
|
1340
|
+
|
|
1341
|
+
pool_debug("Bind: checking insert lock");
|
|
1342
|
+
insert_stmt_with_lock = need_insert_lock(backend, query_context->original_query, query_context->parse_tree);
|
|
1343
|
+
if (insert_stmt_with_lock)
|
|
1344
|
+
{
|
|
1345
|
+
pool_debug("Bind: issuing insert lock");
|
|
1346
|
+
/* issue a LOCK command to keep consistency */
|
|
1347
|
+
if (insert_lock(frontend, backend, query_context->original_query, (InsertStmt *)query_context->parse_tree, insert_stmt_with_lock) != POOL_CONTINUE)
|
|
1348
|
+
{
|
|
1349
|
+
pool_query_context_destroy(query_context);
|
|
1350
|
+
return POOL_END;
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1354
|
+
pool_debug("Bind: waiting for master completing the query");
|
|
1355
|
+
if (pool_extended_send_and_wait(query_context, "B", len, contents, 1, MASTER_NODE_ID)
|
|
1356
|
+
!= POOL_CONTINUE)
|
|
1357
|
+
{
|
|
1358
|
+
return POOL_END;
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
if (pool_extended_send_and_wait(query_context, "B", len, contents, -1, MASTER_NODE_ID)
|
|
1362
|
+
!= POOL_CONTINUE)
|
|
1363
|
+
{
|
|
1364
|
+
return POOL_END;
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
return POOL_CONTINUE;
|
|
1368
|
+
}
|
|
1369
|
+
|
|
1370
|
+
POOL_STATUS Describe(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *backend,
|
|
1371
|
+
int len, char *contents)
|
|
1372
|
+
{
|
|
1373
|
+
POOL_SENT_MESSAGE *msg;
|
|
1374
|
+
POOL_SESSION_CONTEXT *session_context;
|
|
1375
|
+
POOL_QUERY_CONTEXT *query_context;
|
|
1376
|
+
|
|
1377
|
+
/* Get session context */
|
|
1378
|
+
session_context = pool_get_session_context();
|
|
1379
|
+
if (!session_context)
|
|
1380
|
+
{
|
|
1381
|
+
pool_error("Describe: cannot get session context");
|
|
1382
|
+
return POOL_END;
|
|
1383
|
+
}
|
|
1384
|
+
|
|
1385
|
+
/* Prepared Statement */
|
|
1386
|
+
if (*contents == 'S')
|
|
1387
|
+
{
|
|
1388
|
+
msg = pool_get_sent_message('Q', contents+1);
|
|
1389
|
+
if (!msg)
|
|
1390
|
+
msg = pool_get_sent_message('P', contents+1);
|
|
1391
|
+
if (!msg)
|
|
1392
|
+
{
|
|
1393
|
+
pool_error("Describe: cannot get parse message");
|
|
1394
|
+
return POOL_END;
|
|
1395
|
+
}
|
|
1396
|
+
}
|
|
1397
|
+
/* Portal */
|
|
1398
|
+
else
|
|
1399
|
+
{
|
|
1400
|
+
msg = pool_get_sent_message('B', contents+1);
|
|
1401
|
+
if (!msg)
|
|
1402
|
+
{
|
|
1403
|
+
pool_error("Describe: cannot get bind message");
|
|
1404
|
+
return POOL_END;
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1407
|
+
|
|
1408
|
+
query_context = msg->query_context;
|
|
1409
|
+
|
|
1410
|
+
if (query_context == NULL)
|
|
1411
|
+
{
|
|
1412
|
+
pool_error("Describe: cannot get query context");
|
|
1413
|
+
return POOL_END;
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1416
|
+
session_context->query_context = query_context;
|
|
1417
|
+
|
|
1418
|
+
/*
|
|
1419
|
+
* Calling pool_where_to_send here is dangerous because the node
|
|
1420
|
+
* parse/bind has been sent could be change by
|
|
1421
|
+
* pool_where_to_send() and it leads to "portal not found"
|
|
1422
|
+
* etc. errors.
|
|
1423
|
+
*/
|
|
1424
|
+
pool_debug("Describe: waiting for master completing the query");
|
|
1425
|
+
if (pool_extended_send_and_wait(query_context, "D", len, contents, 1, MASTER_NODE_ID)
|
|
1426
|
+
!= POOL_CONTINUE)
|
|
1427
|
+
return POOL_END;
|
|
1428
|
+
|
|
1429
|
+
if (pool_extended_send_and_wait(query_context, "D", len, contents, -1, MASTER_NODE_ID)
|
|
1430
|
+
!= POOL_CONTINUE)
|
|
1431
|
+
return POOL_END;
|
|
1432
|
+
|
|
1433
|
+
return POOL_CONTINUE;
|
|
1434
|
+
}
|
|
1435
|
+
|
|
1436
|
+
|
|
1437
|
+
POOL_STATUS Close(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *backend,
|
|
1438
|
+
int len, char *contents)
|
|
1439
|
+
{
|
|
1440
|
+
POOL_SENT_MESSAGE *msg;
|
|
1441
|
+
POOL_SESSION_CONTEXT *session_context;
|
|
1442
|
+
POOL_QUERY_CONTEXT *query_context;
|
|
1443
|
+
|
|
1444
|
+
/* Get session context */
|
|
1445
|
+
session_context = pool_get_session_context();
|
|
1446
|
+
if (!session_context)
|
|
1447
|
+
{
|
|
1448
|
+
pool_error("Close: cannot get session context");
|
|
1449
|
+
return POOL_END;
|
|
1450
|
+
}
|
|
1451
|
+
|
|
1452
|
+
/* Prepared Statement */
|
|
1453
|
+
if (*contents == 'S')
|
|
1454
|
+
{
|
|
1455
|
+
msg = pool_get_sent_message('Q', contents+1);
|
|
1456
|
+
if (!msg)
|
|
1457
|
+
msg = pool_get_sent_message('P', contents+1);
|
|
1458
|
+
if (!msg)
|
|
1459
|
+
{
|
|
1460
|
+
pool_error("Close: cannot get parse message");
|
|
1461
|
+
return POOL_END;
|
|
1462
|
+
}
|
|
1463
|
+
}
|
|
1464
|
+
/* Portal */
|
|
1465
|
+
else if (*contents == 'P')
|
|
1466
|
+
{
|
|
1467
|
+
msg = pool_get_sent_message('B', contents+1);
|
|
1468
|
+
if (!msg)
|
|
1469
|
+
{
|
|
1470
|
+
pool_error("Close: cannot get bind message");
|
|
1471
|
+
return POOL_END;
|
|
1472
|
+
}
|
|
1473
|
+
}
|
|
1474
|
+
else
|
|
1475
|
+
{
|
|
1476
|
+
pool_error("Close: invalid message");
|
|
1477
|
+
return POOL_END;
|
|
1478
|
+
}
|
|
1479
|
+
|
|
1480
|
+
session_context->uncompleted_message = msg;
|
|
1481
|
+
query_context = msg->query_context;
|
|
1482
|
+
|
|
1483
|
+
if (!query_context)
|
|
1484
|
+
{
|
|
1485
|
+
pool_error("Close: cannot get query context");
|
|
1486
|
+
return POOL_END;
|
|
1487
|
+
}
|
|
1488
|
+
|
|
1489
|
+
session_context->query_context = query_context;
|
|
1490
|
+
/* pool_where_to_send(query_context, query_context->original_query, query_context->parse_tree); */
|
|
1491
|
+
|
|
1492
|
+
pool_debug("Close: waiting for master completing the query");
|
|
1493
|
+
if (pool_extended_send_and_wait(query_context, "C", len, contents, 1, MASTER_NODE_ID)
|
|
1494
|
+
!= POOL_CONTINUE)
|
|
1495
|
+
return POOL_END;
|
|
1496
|
+
|
|
1497
|
+
if (pool_extended_send_and_wait(query_context, "C", len, contents, -1, MASTER_NODE_ID)
|
|
1498
|
+
!= POOL_CONTINUE)
|
|
1499
|
+
return POOL_END;
|
|
1500
|
+
|
|
1501
|
+
return POOL_CONTINUE;
|
|
1502
|
+
}
|
|
1503
|
+
|
|
1504
|
+
|
|
1505
|
+
POOL_STATUS FunctionCall3(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *backend,
|
|
1506
|
+
int len, char *contents)
|
|
1507
|
+
{
|
|
1508
|
+
/*
|
|
1509
|
+
* If Function call message for lo_creat, rewrite it
|
|
1510
|
+
*/
|
|
1511
|
+
char *rewrite_lo;
|
|
1512
|
+
int rewrite_len;
|
|
1513
|
+
|
|
1514
|
+
rewrite_lo = pool_rewrite_lo_creat('F', contents, len, frontend,
|
|
1515
|
+
backend, &rewrite_len);
|
|
1516
|
+
|
|
1517
|
+
if (rewrite_lo != NULL)
|
|
1518
|
+
{
|
|
1519
|
+
contents = rewrite_lo;
|
|
1520
|
+
len = rewrite_len;
|
|
1521
|
+
}
|
|
1522
|
+
return SimpleForwardToBackend('F', frontend, backend, len, contents);
|
|
1523
|
+
}
|
|
1524
|
+
|
|
1525
|
+
/*
|
|
1526
|
+
* Process ReadyForQuery('Z') message.
|
|
1527
|
+
* If send_ready is true, send 'Z' message to frontend.
|
|
1528
|
+
* If cache_commit is true, commit or discard query cache according to
|
|
1529
|
+
* transaction state.
|
|
1530
|
+
*
|
|
1531
|
+
* - if the error status "mismatch_ntuples" is set, send an error query
|
|
1532
|
+
* to all DB nodes to abort transaction or do failover.
|
|
1533
|
+
* - internal transaction is closed
|
|
1534
|
+
*/
|
|
1535
|
+
POOL_STATUS ReadyForQuery(POOL_CONNECTION *frontend,
|
|
1536
|
+
POOL_CONNECTION_POOL *backend, bool send_ready, bool cache_commit)
|
|
1537
|
+
{
|
|
1538
|
+
int i;
|
|
1539
|
+
int len;
|
|
1540
|
+
signed char kind;
|
|
1541
|
+
signed char state;
|
|
1542
|
+
POOL_SESSION_CONTEXT *session_context;
|
|
1543
|
+
Node *node = NULL;
|
|
1544
|
+
char *query = NULL;
|
|
1545
|
+
|
|
1546
|
+
/* Get session context */
|
|
1547
|
+
session_context = pool_get_session_context();
|
|
1548
|
+
if (!session_context)
|
|
1549
|
+
{
|
|
1550
|
+
pool_error("ReadyForQuery: cannot get session context");
|
|
1551
|
+
return POOL_END;
|
|
1552
|
+
}
|
|
1553
|
+
|
|
1554
|
+
/*
|
|
1555
|
+
* If the numbers of update tuples are differ and
|
|
1556
|
+
* failover_if_affected_tuples_mismatch is false, we abort
|
|
1557
|
+
* transactions by using do_error_command. If
|
|
1558
|
+
* failover_if_affected_tuples_mismatch is true, trigger failover.
|
|
1559
|
+
* This only works with PROTO_MAJOR_V3.
|
|
1560
|
+
*/
|
|
1561
|
+
if (session_context->mismatch_ntuples && MAJOR(backend) == PROTO_MAJOR_V3)
|
|
1562
|
+
{
|
|
1563
|
+
int i;
|
|
1564
|
+
char kind;
|
|
1565
|
+
|
|
1566
|
+
/*
|
|
1567
|
+
* If failover_if_affected_tuples_mismatch, is true, then
|
|
1568
|
+
* decide victim nodes by using find_victim_nodes and
|
|
1569
|
+
* degenerate them.
|
|
1570
|
+
*/
|
|
1571
|
+
if (pool_config->failover_if_affected_tuples_mismatch)
|
|
1572
|
+
{
|
|
1573
|
+
int *victim_nodes;
|
|
1574
|
+
int number_of_nodes;
|
|
1575
|
+
char msgbuf[128];
|
|
1576
|
+
|
|
1577
|
+
victim_nodes = find_victim_nodes(session_context->ntuples, NUM_BACKENDS,
|
|
1578
|
+
MASTER_NODE_ID, &number_of_nodes);
|
|
1579
|
+
if (victim_nodes)
|
|
1580
|
+
{
|
|
1581
|
+
int i;
|
|
1582
|
+
String *msg;
|
|
1583
|
+
POOL_MEMORY_POOL *old_context;
|
|
1584
|
+
|
|
1585
|
+
if (session_context->query_context)
|
|
1586
|
+
old_context = pool_memory_context_switch_to(session_context->query_context->memory_context);
|
|
1587
|
+
else
|
|
1588
|
+
old_context = pool_memory_context_switch_to(session_context->memory_context);
|
|
1589
|
+
|
|
1590
|
+
msg = init_string("ReadyForQuery: Degenerate backends:");
|
|
1591
|
+
|
|
1592
|
+
for (i=0;i<number_of_nodes;i++)
|
|
1593
|
+
{
|
|
1594
|
+
snprintf(msgbuf, sizeof(msgbuf), " %d", victim_nodes[i]);
|
|
1595
|
+
string_append_char(msg, msgbuf);
|
|
1596
|
+
}
|
|
1597
|
+
pool_log("%s", msg->data);
|
|
1598
|
+
free_string(msg);
|
|
1599
|
+
|
|
1600
|
+
msg = init_string("ReadyForQuery: Number of affected tuples are:");
|
|
1601
|
+
|
|
1602
|
+
for (i=0;i<NUM_BACKENDS;i++)
|
|
1603
|
+
{
|
|
1604
|
+
snprintf(msgbuf, sizeof(msgbuf), " %d", session_context->ntuples[i]);
|
|
1605
|
+
string_append_char(msg, msgbuf);
|
|
1606
|
+
}
|
|
1607
|
+
pool_log("%s", msg->data);
|
|
1608
|
+
free_string(msg);
|
|
1609
|
+
|
|
1610
|
+
pool_memory_context_switch_to(old_context);
|
|
1611
|
+
|
|
1612
|
+
degenerate_backend_set(victim_nodes, number_of_nodes);
|
|
1613
|
+
child_exit(1);
|
|
1614
|
+
}
|
|
1615
|
+
else
|
|
1616
|
+
{
|
|
1617
|
+
pool_error("ReadyForQuery: find_victim_nodes returned no victim node");
|
|
1618
|
+
}
|
|
1619
|
+
}
|
|
1620
|
+
|
|
1621
|
+
/*
|
|
1622
|
+
* XXX: discard rest of ReadyForQuery packet
|
|
1623
|
+
*/
|
|
1624
|
+
if (pool_read_message_length(backend) < 0)
|
|
1625
|
+
return POOL_END;
|
|
1626
|
+
|
|
1627
|
+
state = pool_read_kind(backend);
|
|
1628
|
+
if (state < 0)
|
|
1629
|
+
return POOL_END;
|
|
1630
|
+
|
|
1631
|
+
for (i = 0; i < NUM_BACKENDS; i++)
|
|
1632
|
+
{
|
|
1633
|
+
if (VALID_BACKEND(i))
|
|
1634
|
+
{
|
|
1635
|
+
/* abort transaction on all nodes. */
|
|
1636
|
+
do_error_command(CONNECTION(backend, i), PROTO_MAJOR_V3);
|
|
1637
|
+
}
|
|
1638
|
+
}
|
|
1639
|
+
|
|
1640
|
+
/* loop through until we get ReadyForQuery */
|
|
1641
|
+
for(;;)
|
|
1642
|
+
{
|
|
1643
|
+
kind = pool_read_kind(backend);
|
|
1644
|
+
if (kind < 0)
|
|
1645
|
+
return POOL_END;
|
|
1646
|
+
|
|
1647
|
+
if (kind == 'Z')
|
|
1648
|
+
break;
|
|
1649
|
+
|
|
1650
|
+
/* put the message back to read buffer */
|
|
1651
|
+
for (i=0;i<NUM_BACKENDS;i++)
|
|
1652
|
+
{
|
|
1653
|
+
if (VALID_BACKEND(i))
|
|
1654
|
+
{
|
|
1655
|
+
pool_unread(CONNECTION(backend,i), &kind, 1);
|
|
1656
|
+
}
|
|
1657
|
+
}
|
|
1658
|
+
|
|
1659
|
+
/* discard rest of the packet */
|
|
1660
|
+
if (pool_discard_packet(backend) != POOL_CONTINUE)
|
|
1661
|
+
{
|
|
1662
|
+
pool_error("ReadyForQuery: pool_discard_packet failed");
|
|
1663
|
+
return POOL_END;
|
|
1664
|
+
}
|
|
1665
|
+
}
|
|
1666
|
+
session_context->mismatch_ntuples = false;
|
|
1667
|
+
}
|
|
1668
|
+
|
|
1669
|
+
/*
|
|
1670
|
+
* if a transaction is started for insert lock, we need to close
|
|
1671
|
+
* the transaction.
|
|
1672
|
+
*/
|
|
1673
|
+
/* if (pool_is_query_in_progress() && allow_close_transaction) */
|
|
1674
|
+
if (allow_close_transaction)
|
|
1675
|
+
{
|
|
1676
|
+
if (end_internal_transaction(frontend, backend) != POOL_CONTINUE)
|
|
1677
|
+
return POOL_END;
|
|
1678
|
+
}
|
|
1679
|
+
|
|
1680
|
+
if (MAJOR(backend) == PROTO_MAJOR_V3)
|
|
1681
|
+
{
|
|
1682
|
+
if ((len = pool_read_message_length(backend)) < 0)
|
|
1683
|
+
return POOL_END;
|
|
1684
|
+
|
|
1685
|
+
/*
|
|
1686
|
+
* Set transaction state for each node
|
|
1687
|
+
*/
|
|
1688
|
+
state = TSTATE(backend,
|
|
1689
|
+
MASTER_SLAVE ? PRIMARY_NODE_ID : REAL_MASTER_NODE_ID);
|
|
1690
|
+
|
|
1691
|
+
for (i=0;i<NUM_BACKENDS;i++)
|
|
1692
|
+
{
|
|
1693
|
+
if (!VALID_BACKEND(i))
|
|
1694
|
+
continue;
|
|
1695
|
+
|
|
1696
|
+
if (pool_read(CONNECTION(backend, i), &kind, sizeof(kind)))
|
|
1697
|
+
return POOL_END;
|
|
1698
|
+
|
|
1699
|
+
TSTATE(backend, i) = kind;
|
|
1700
|
+
|
|
1701
|
+
pool_debug("ReadyForQuery: transaction state:%c", state);
|
|
1702
|
+
|
|
1703
|
+
/*
|
|
1704
|
+
* The transaction state to be returned to frontend is
|
|
1705
|
+
* master's.
|
|
1706
|
+
*/
|
|
1707
|
+
if (i == (MASTER_SLAVE ? PRIMARY_NODE_ID : REAL_MASTER_NODE_ID))
|
|
1708
|
+
{
|
|
1709
|
+
state = kind;
|
|
1710
|
+
}
|
|
1711
|
+
}
|
|
1712
|
+
}
|
|
1713
|
+
|
|
1714
|
+
if (send_ready)
|
|
1715
|
+
{
|
|
1716
|
+
pool_write(frontend, "Z", 1);
|
|
1717
|
+
|
|
1718
|
+
if (MAJOR(backend) == PROTO_MAJOR_V3)
|
|
1719
|
+
{
|
|
1720
|
+
len = htonl(len);
|
|
1721
|
+
pool_write(frontend, &len, sizeof(len));
|
|
1722
|
+
pool_write(frontend, &state, 1);
|
|
1723
|
+
}
|
|
1724
|
+
pool_flush(frontend);
|
|
1725
|
+
}
|
|
1726
|
+
|
|
1727
|
+
if (pool_is_query_in_progress())
|
|
1728
|
+
{
|
|
1729
|
+
node = pool_get_parse_tree();
|
|
1730
|
+
|
|
1731
|
+
if (pool_is_command_success())
|
|
1732
|
+
{
|
|
1733
|
+
query = pool_get_query_string();
|
|
1734
|
+
|
|
1735
|
+
if (node)
|
|
1736
|
+
{
|
|
1737
|
+
/*
|
|
1738
|
+
* If the query was BEGIN/START TRANSACTION, clear the
|
|
1739
|
+
* history that we had a writing command in the transaction
|
|
1740
|
+
* and forget the transaction isolation level.
|
|
1741
|
+
*
|
|
1742
|
+
* XXX If BEGIN is received while we are already in an
|
|
1743
|
+
* explicit transaction, the command *successes*
|
|
1744
|
+
* (just with a NOTICE message). In this case we lose
|
|
1745
|
+
* "writing_transaction" etc. info.
|
|
1746
|
+
*/
|
|
1747
|
+
if (is_start_transaction_query(node))
|
|
1748
|
+
{
|
|
1749
|
+
pool_unset_writing_transaction();
|
|
1750
|
+
pool_unset_failed_transaction();
|
|
1751
|
+
pool_unset_transaction_isolation();
|
|
1752
|
+
}
|
|
1753
|
+
|
|
1754
|
+
/*
|
|
1755
|
+
* If the query was COMMIT/ABORT, clear the history
|
|
1756
|
+
* that we had a writing command in the transaction
|
|
1757
|
+
* and forget the transaction isolation level. This
|
|
1758
|
+
* is necessary if succeeding transaction is not an
|
|
1759
|
+
* explicit one.
|
|
1760
|
+
*/
|
|
1761
|
+
else if (is_commit_or_rollback_query(node))
|
|
1762
|
+
{
|
|
1763
|
+
pool_unset_writing_transaction();
|
|
1764
|
+
pool_unset_failed_transaction();
|
|
1765
|
+
pool_unset_transaction_isolation();
|
|
1766
|
+
}
|
|
1767
|
+
|
|
1768
|
+
/*
|
|
1769
|
+
* SET TRANSACTION ISOLATION LEVEL SERIALIZABLE or SET
|
|
1770
|
+
* SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL
|
|
1771
|
+
* SERIALIZABLE, remember it.
|
|
1772
|
+
*/
|
|
1773
|
+
else if (is_set_transaction_serializable(node))
|
|
1774
|
+
{
|
|
1775
|
+
pool_set_transaction_isolation(POOL_SERIALIZABLE);
|
|
1776
|
+
}
|
|
1777
|
+
|
|
1778
|
+
/*
|
|
1779
|
+
* If 2PC commands has been executed, automatically close
|
|
1780
|
+
* transactions on standbys if there is any open
|
|
1781
|
+
* transaction since 2PC commands close transaction on
|
|
1782
|
+
* primary.
|
|
1783
|
+
*/
|
|
1784
|
+
else if (is_2pc_transaction_query(node))
|
|
1785
|
+
{
|
|
1786
|
+
if (close_standby_transactions(frontend, backend) != POOL_CONTINUE)
|
|
1787
|
+
return POOL_END;
|
|
1788
|
+
}
|
|
1789
|
+
|
|
1790
|
+
else if (!is_select_query(node, query))
|
|
1791
|
+
{
|
|
1792
|
+
/*
|
|
1793
|
+
* If the query was not READ SELECT, and we are in an
|
|
1794
|
+
* explicit transaction, remember that we had a write
|
|
1795
|
+
* query in this transaction.
|
|
1796
|
+
*/
|
|
1797
|
+
if (TSTATE(backend, MASTER_SLAVE ? PRIMARY_NODE_ID : REAL_MASTER_NODE_ID) == 'T')
|
|
1798
|
+
{
|
|
1799
|
+
pool_set_writing_transaction();
|
|
1800
|
+
}
|
|
1801
|
+
|
|
1802
|
+
/*
|
|
1803
|
+
* If the query was CREATE TEMP TABLE, discard
|
|
1804
|
+
* temp table relcache because we might have had
|
|
1805
|
+
* persistent table relation cache which has table
|
|
1806
|
+
* name as the temp table.
|
|
1807
|
+
*/
|
|
1808
|
+
if (IsA(node, CreateStmt))
|
|
1809
|
+
{
|
|
1810
|
+
CreateStmt *create_table_stmt = (CreateStmt *)node;
|
|
1811
|
+
if (create_table_stmt->relation->relpersistence)
|
|
1812
|
+
discard_temp_table_relcache();
|
|
1813
|
+
}
|
|
1814
|
+
}
|
|
1815
|
+
}
|
|
1816
|
+
|
|
1817
|
+
/* Memory cache enabled? */
|
|
1818
|
+
if (cache_commit && pool_config->memory_cache_enabled)
|
|
1819
|
+
{
|
|
1820
|
+
/* If we are doing extended query and the state is after EXECUTE,
|
|
1821
|
+
* then we can commit cache.
|
|
1822
|
+
* We check latter condition by looking at query_context->query_w_hex.
|
|
1823
|
+
* This check is necessary for certain frame work such as PHP PDO.
|
|
1824
|
+
* It sends Sync message right after PARSE and it produces
|
|
1825
|
+
* "Ready for query" message from backend.
|
|
1826
|
+
*/
|
|
1827
|
+
if (pool_is_doing_extended_query_message())
|
|
1828
|
+
{
|
|
1829
|
+
if (session_context->query_context->query_state[MASTER_NODE_ID] == POOL_EXECUTE_COMPLETE)
|
|
1830
|
+
{
|
|
1831
|
+
pool_handle_query_cache(backend, session_context->query_context->query_w_hex, node, state);
|
|
1832
|
+
free(session_context->query_context->query_w_hex);
|
|
1833
|
+
session_context->query_context->query_w_hex = NULL;
|
|
1834
|
+
}
|
|
1835
|
+
}
|
|
1836
|
+
else
|
|
1837
|
+
{
|
|
1838
|
+
if (MAJOR(backend) != PROTO_MAJOR_V3)
|
|
1839
|
+
{
|
|
1840
|
+
state = 'I'; /* XXX I don't think query cache works with PROTO2 protocol */
|
|
1841
|
+
}
|
|
1842
|
+
pool_handle_query_cache(backend, query, node, state);
|
|
1843
|
+
}
|
|
1844
|
+
}
|
|
1845
|
+
}
|
|
1846
|
+
/*
|
|
1847
|
+
* If PREPARE or extended query protocol commands caused error,
|
|
1848
|
+
* remove the temporary saved message.
|
|
1849
|
+
* (except when ReadyForQuery() is called during Parse() of extended queries)
|
|
1850
|
+
*/
|
|
1851
|
+
else
|
|
1852
|
+
{
|
|
1853
|
+
if ((pool_is_doing_extended_query_message() &&
|
|
1854
|
+
session_context->query_context->query_state[MASTER_NODE_ID] != POOL_UNPARSED &&
|
|
1855
|
+
session_context->uncompleted_message) ||
|
|
1856
|
+
(!pool_is_doing_extended_query_message() && session_context->uncompleted_message))
|
|
1857
|
+
{
|
|
1858
|
+
pool_add_sent_message(session_context->uncompleted_message);
|
|
1859
|
+
pool_remove_sent_message(session_context->uncompleted_message->kind,
|
|
1860
|
+
session_context->uncompleted_message->name);
|
|
1861
|
+
session_context->uncompleted_message = NULL;
|
|
1862
|
+
}
|
|
1863
|
+
}
|
|
1864
|
+
|
|
1865
|
+
pool_unset_query_in_progress();
|
|
1866
|
+
}
|
|
1867
|
+
|
|
1868
|
+
if (!pool_is_doing_extended_query_message())
|
|
1869
|
+
{
|
|
1870
|
+
if (!(node && IsA(node, PrepareStmt)))
|
|
1871
|
+
pool_query_context_destroy(pool_get_session_context()->query_context);
|
|
1872
|
+
}
|
|
1873
|
+
|
|
1874
|
+
/*
|
|
1875
|
+
* Show ps idle status
|
|
1876
|
+
*/
|
|
1877
|
+
pool_ps_idle_display(backend);
|
|
1878
|
+
|
|
1879
|
+
return POOL_CONTINUE;
|
|
1880
|
+
}
|
|
1881
|
+
|
|
1882
|
+
/*
|
|
1883
|
+
* Close running transactions on standbys.
|
|
1884
|
+
*/
|
|
1885
|
+
static POOL_STATUS close_standby_transactions(POOL_CONNECTION *frontend,
|
|
1886
|
+
POOL_CONNECTION_POOL *backend)
|
|
1887
|
+
{
|
|
1888
|
+
int i;
|
|
1889
|
+
|
|
1890
|
+
for (i=0;i<NUM_BACKENDS;i++)
|
|
1891
|
+
{
|
|
1892
|
+
if (CONNECTION_SLOT(backend, i) &&
|
|
1893
|
+
TSTATE(backend, i) == 'T' &&
|
|
1894
|
+
BACKEND_INFO(i).backend_status == CON_UP &&
|
|
1895
|
+
(MASTER_SLAVE ? PRIMARY_NODE_ID : REAL_MASTER_NODE_ID) != i)
|
|
1896
|
+
{
|
|
1897
|
+
per_node_statement_log(backend, i, "COMMIT");
|
|
1898
|
+
if (do_command(frontend, CONNECTION(backend, i), "COMMIT", MAJOR(backend),
|
|
1899
|
+
MASTER_CONNECTION(backend)->pid,
|
|
1900
|
+
MASTER_CONNECTION(backend)->key, 0) != POOL_CONTINUE)
|
|
1901
|
+
{
|
|
1902
|
+
return POOL_END;
|
|
1903
|
+
}
|
|
1904
|
+
}
|
|
1905
|
+
}
|
|
1906
|
+
return POOL_CONTINUE;
|
|
1907
|
+
}
|
|
1908
|
+
|
|
1909
|
+
POOL_STATUS ParseComplete(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *backend)
|
|
1910
|
+
{
|
|
1911
|
+
POOL_SESSION_CONTEXT *session_context;
|
|
1912
|
+
|
|
1913
|
+
/* Get session context */
|
|
1914
|
+
session_context = pool_get_session_context();
|
|
1915
|
+
if (!session_context)
|
|
1916
|
+
{
|
|
1917
|
+
pool_error("ParseComplete: cannot get session context");
|
|
1918
|
+
return POOL_END;
|
|
1919
|
+
}
|
|
1920
|
+
|
|
1921
|
+
if (session_context->uncompleted_message)
|
|
1922
|
+
{
|
|
1923
|
+
POOL_QUERY_CONTEXT *qc;
|
|
1924
|
+
|
|
1925
|
+
pool_add_sent_message(session_context->uncompleted_message);
|
|
1926
|
+
|
|
1927
|
+
qc = session_context->uncompleted_message->query_context;
|
|
1928
|
+
if (qc)
|
|
1929
|
+
pool_set_query_state(qc, POOL_PARSE_COMPLETE);
|
|
1930
|
+
|
|
1931
|
+
session_context->uncompleted_message = NULL;
|
|
1932
|
+
}
|
|
1933
|
+
|
|
1934
|
+
return SimpleForwardToFrontend('1', frontend, backend);
|
|
1935
|
+
}
|
|
1936
|
+
|
|
1937
|
+
POOL_STATUS BindComplete(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *backend)
|
|
1938
|
+
{
|
|
1939
|
+
POOL_SESSION_CONTEXT *session_context;
|
|
1940
|
+
|
|
1941
|
+
/* Get session context */
|
|
1942
|
+
session_context = pool_get_session_context();
|
|
1943
|
+
if (!session_context)
|
|
1944
|
+
{
|
|
1945
|
+
pool_error("BindComplete: cannot get session context");
|
|
1946
|
+
return POOL_END;
|
|
1947
|
+
}
|
|
1948
|
+
|
|
1949
|
+
if (session_context->uncompleted_message)
|
|
1950
|
+
{
|
|
1951
|
+
POOL_QUERY_CONTEXT *qc;
|
|
1952
|
+
|
|
1953
|
+
pool_add_sent_message(session_context->uncompleted_message);
|
|
1954
|
+
|
|
1955
|
+
qc = session_context->uncompleted_message->query_context;
|
|
1956
|
+
if (qc)
|
|
1957
|
+
pool_set_query_state(qc, POOL_BIND_COMPLETE);
|
|
1958
|
+
|
|
1959
|
+
session_context->uncompleted_message = NULL;
|
|
1960
|
+
}
|
|
1961
|
+
|
|
1962
|
+
return SimpleForwardToFrontend('2', frontend, backend);
|
|
1963
|
+
}
|
|
1964
|
+
|
|
1965
|
+
POOL_STATUS CloseComplete(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *backend)
|
|
1966
|
+
{
|
|
1967
|
+
POOL_SESSION_CONTEXT *session_context;
|
|
1968
|
+
POOL_STATUS status;
|
|
1969
|
+
|
|
1970
|
+
/* Get session context */
|
|
1971
|
+
session_context = pool_get_session_context();
|
|
1972
|
+
if (!session_context)
|
|
1973
|
+
{
|
|
1974
|
+
pool_error("CloseComplete: cannot get session context");
|
|
1975
|
+
return POOL_END;
|
|
1976
|
+
}
|
|
1977
|
+
|
|
1978
|
+
/* Send CloseComplete(3) to frontend before removing the target message */
|
|
1979
|
+
status = SimpleForwardToFrontend('3', frontend, backend);
|
|
1980
|
+
|
|
1981
|
+
/* Remove the target message */
|
|
1982
|
+
if (session_context->uncompleted_message)
|
|
1983
|
+
{
|
|
1984
|
+
pool_remove_sent_message(session_context->uncompleted_message->kind,
|
|
1985
|
+
session_context->uncompleted_message->name);
|
|
1986
|
+
session_context->uncompleted_message = NULL;
|
|
1987
|
+
}
|
|
1988
|
+
else
|
|
1989
|
+
{
|
|
1990
|
+
pool_error("CloseComplete: uncompleted message not found");
|
|
1991
|
+
return POOL_END;
|
|
1992
|
+
}
|
|
1993
|
+
|
|
1994
|
+
return status;
|
|
1995
|
+
}
|
|
1996
|
+
|
|
1997
|
+
POOL_STATUS CommandComplete(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *backend)
|
|
1998
|
+
{
|
|
1999
|
+
int i;
|
|
2000
|
+
int len, len1, sendlen;
|
|
2001
|
+
int status;
|
|
2002
|
+
int rows;
|
|
2003
|
+
char *p, *p1, *p2;
|
|
2004
|
+
bool update_or_delete = false;
|
|
2005
|
+
POOL_SESSION_CONTEXT *session_context;
|
|
2006
|
+
|
|
2007
|
+
/* Get session context */
|
|
2008
|
+
session_context = pool_get_session_context();
|
|
2009
|
+
if (!session_context)
|
|
2010
|
+
{
|
|
2011
|
+
pool_error("CommandComplete: cannot get session context");
|
|
2012
|
+
return POOL_END;
|
|
2013
|
+
}
|
|
2014
|
+
|
|
2015
|
+
if (session_context->query_context != NULL)
|
|
2016
|
+
{
|
|
2017
|
+
Node *node = session_context->query_context->parse_tree;
|
|
2018
|
+
|
|
2019
|
+
if (IsA(node, PrepareStmt))
|
|
2020
|
+
{
|
|
2021
|
+
if (session_context->uncompleted_message)
|
|
2022
|
+
{
|
|
2023
|
+
pool_add_sent_message(session_context->uncompleted_message);
|
|
2024
|
+
session_context->uncompleted_message = NULL;
|
|
2025
|
+
}
|
|
2026
|
+
}
|
|
2027
|
+
else if (IsA(node, DeallocateStmt))
|
|
2028
|
+
{
|
|
2029
|
+
char *name;
|
|
2030
|
+
|
|
2031
|
+
name = ((DeallocateStmt *)node)->name;
|
|
2032
|
+
if (name == NULL)
|
|
2033
|
+
{
|
|
2034
|
+
pool_remove_sent_messages('Q');
|
|
2035
|
+
pool_remove_sent_messages('P');
|
|
2036
|
+
}
|
|
2037
|
+
else
|
|
2038
|
+
{
|
|
2039
|
+
pool_remove_sent_message('Q', name);
|
|
2040
|
+
pool_remove_sent_message('P', name);
|
|
2041
|
+
}
|
|
2042
|
+
}
|
|
2043
|
+
else if (IsA(node, DiscardStmt))
|
|
2044
|
+
{
|
|
2045
|
+
DiscardStmt *stmt = (DiscardStmt *)node;
|
|
2046
|
+
|
|
2047
|
+
if (stmt->target == DISCARD_PLANS)
|
|
2048
|
+
{
|
|
2049
|
+
pool_remove_sent_messages('Q');
|
|
2050
|
+
pool_remove_sent_messages('P');
|
|
2051
|
+
}
|
|
2052
|
+
else if (stmt->target == DISCARD_ALL)
|
|
2053
|
+
{
|
|
2054
|
+
pool_clear_sent_message_list();
|
|
2055
|
+
}
|
|
2056
|
+
}
|
|
2057
|
+
/*
|
|
2058
|
+
* JDBC driver sends "BEGIN" query internally if
|
|
2059
|
+
* setAutoCommit(false). But it does not send Sync message
|
|
2060
|
+
* after "BEGIN" query. In extended query protocol,
|
|
2061
|
+
* PostgreSQL returns ReadyForQuery when a client sends Sync
|
|
2062
|
+
* message. Problem is, pgpool can't know the transaction
|
|
2063
|
+
* state without receiving ReadyForQuery. So we remember that
|
|
2064
|
+
* we need to send Sync message internally afterward, whenever
|
|
2065
|
+
* we receive BEGIN in extended protocol.
|
|
2066
|
+
*/
|
|
2067
|
+
else if (IsA(node, TransactionStmt))
|
|
2068
|
+
{
|
|
2069
|
+
TransactionStmt *stmt = (TransactionStmt *) node;
|
|
2070
|
+
|
|
2071
|
+
if (stmt->kind == TRANS_STMT_BEGIN || stmt->kind == TRANS_STMT_START)
|
|
2072
|
+
{
|
|
2073
|
+
int i;
|
|
2074
|
+
|
|
2075
|
+
for (i = 0; i < NUM_BACKENDS; i++)
|
|
2076
|
+
{
|
|
2077
|
+
if (!VALID_BACKEND(i))
|
|
2078
|
+
continue;
|
|
2079
|
+
|
|
2080
|
+
TSTATE(backend, i) = 'T';
|
|
2081
|
+
}
|
|
2082
|
+
pool_unset_writing_transaction();
|
|
2083
|
+
pool_unset_failed_transaction();
|
|
2084
|
+
pool_unset_transaction_isolation();
|
|
2085
|
+
}
|
|
2086
|
+
}
|
|
2087
|
+
|
|
2088
|
+
}
|
|
2089
|
+
|
|
2090
|
+
status = pool_read(MASTER(backend), &len, sizeof(len));
|
|
2091
|
+
if (status < 0)
|
|
2092
|
+
{
|
|
2093
|
+
pool_error("CommandComplete: error while reading message length");
|
|
2094
|
+
return POOL_END;
|
|
2095
|
+
}
|
|
2096
|
+
|
|
2097
|
+
len = ntohl(len);
|
|
2098
|
+
len -= 4;
|
|
2099
|
+
len1 = len;
|
|
2100
|
+
|
|
2101
|
+
p = pool_read2(MASTER(backend), len);
|
|
2102
|
+
if (p == NULL)
|
|
2103
|
+
return POOL_END;
|
|
2104
|
+
p1 = malloc(len);
|
|
2105
|
+
if (p1 == NULL)
|
|
2106
|
+
{
|
|
2107
|
+
pool_error("CommandComplete: malloc failed");
|
|
2108
|
+
return POOL_ERROR;
|
|
2109
|
+
}
|
|
2110
|
+
memcpy(p1, p, len);
|
|
2111
|
+
|
|
2112
|
+
rows = extract_ntuples(p);
|
|
2113
|
+
|
|
2114
|
+
/*
|
|
2115
|
+
* Save number of affected tuples of master node.
|
|
2116
|
+
*/
|
|
2117
|
+
session_context->ntuples[MASTER_NODE_ID] = rows;
|
|
2118
|
+
|
|
2119
|
+
if (strstr(p, "UPDATE") || strstr(p, "DELETE"))
|
|
2120
|
+
update_or_delete = true;
|
|
2121
|
+
|
|
2122
|
+
for (i=0;i<NUM_BACKENDS;i++)
|
|
2123
|
+
{
|
|
2124
|
+
if (!IS_MASTER_NODE_ID(i))
|
|
2125
|
+
{
|
|
2126
|
+
if (!VALID_BACKEND(i))
|
|
2127
|
+
{
|
|
2128
|
+
session_context->ntuples[i] = -1;
|
|
2129
|
+
continue;
|
|
2130
|
+
}
|
|
2131
|
+
|
|
2132
|
+
status = pool_read(CONNECTION(backend, i), &len, sizeof(len));
|
|
2133
|
+
if (status < 0)
|
|
2134
|
+
{
|
|
2135
|
+
pool_error("CommandComplete: error while reading message length");
|
|
2136
|
+
return POOL_END;
|
|
2137
|
+
}
|
|
2138
|
+
|
|
2139
|
+
len = ntohl(len);
|
|
2140
|
+
len -= 4;
|
|
2141
|
+
|
|
2142
|
+
p = pool_read2(CONNECTION(backend, i), len);
|
|
2143
|
+
if (p == NULL)
|
|
2144
|
+
return POOL_END;
|
|
2145
|
+
|
|
2146
|
+
if (len != len1)
|
|
2147
|
+
{
|
|
2148
|
+
pool_debug("CommandComplete: length does not match between backends master(%d) %d th backend(%d)",
|
|
2149
|
+
len, i, len1);
|
|
2150
|
+
}
|
|
2151
|
+
|
|
2152
|
+
int n = extract_ntuples(p);
|
|
2153
|
+
|
|
2154
|
+
/*
|
|
2155
|
+
* Save number of affected tuples.
|
|
2156
|
+
*/
|
|
2157
|
+
session_context->ntuples[i] = n;
|
|
2158
|
+
|
|
2159
|
+
/*
|
|
2160
|
+
* if we are in the parallel mode, we have to sum up the number
|
|
2161
|
+
* of affected rows
|
|
2162
|
+
*/
|
|
2163
|
+
if (PARALLEL_MODE && is_parallel_table && update_or_delete)
|
|
2164
|
+
{
|
|
2165
|
+
rows += n;
|
|
2166
|
+
}
|
|
2167
|
+
else
|
|
2168
|
+
{
|
|
2169
|
+
if (rows != n)
|
|
2170
|
+
{
|
|
2171
|
+
/*
|
|
2172
|
+
* Remember that we have different number of UPDATE/DELETE
|
|
2173
|
+
* affected tuples in backends.
|
|
2174
|
+
*/
|
|
2175
|
+
session_context->mismatch_ntuples = true;
|
|
2176
|
+
}
|
|
2177
|
+
}
|
|
2178
|
+
}
|
|
2179
|
+
}
|
|
2180
|
+
|
|
2181
|
+
if (session_context->mismatch_ntuples)
|
|
2182
|
+
{
|
|
2183
|
+
char msgbuf[128];
|
|
2184
|
+
POOL_MEMORY_POOL *old_context;
|
|
2185
|
+
|
|
2186
|
+
if (session_context->query_context)
|
|
2187
|
+
old_context = pool_memory_context_switch_to(session_context->query_context->memory_context);
|
|
2188
|
+
else
|
|
2189
|
+
old_context = pool_memory_context_switch_to(session_context->memory_context);
|
|
2190
|
+
|
|
2191
|
+
String *msg = init_string("pgpool detected difference of the number of inserted, updated or deleted tuples. Possible last query was: \"");
|
|
2192
|
+
string_append_char(msg, query_string_buffer);
|
|
2193
|
+
string_append_char(msg, "\"");
|
|
2194
|
+
pool_send_error_message(frontend, MAJOR(backend),
|
|
2195
|
+
"XX001", msg->data, "",
|
|
2196
|
+
"check data consistency between master and other db node", __FILE__, __LINE__);
|
|
2197
|
+
pool_error("%s", msg->data);
|
|
2198
|
+
free_string(msg);
|
|
2199
|
+
|
|
2200
|
+
msg = init_string("CommandComplete: Number of affected tuples are:");
|
|
2201
|
+
|
|
2202
|
+
for (i=0;i<NUM_BACKENDS;i++)
|
|
2203
|
+
{
|
|
2204
|
+
snprintf(msgbuf, sizeof(msgbuf), " %d", session_context->ntuples[i]);
|
|
2205
|
+
string_append_char(msg, msgbuf);
|
|
2206
|
+
}
|
|
2207
|
+
pool_log("%s", msg->data);
|
|
2208
|
+
free_string(msg);
|
|
2209
|
+
|
|
2210
|
+
pool_memory_context_switch_to(old_context);
|
|
2211
|
+
}
|
|
2212
|
+
else
|
|
2213
|
+
{
|
|
2214
|
+
if (PARALLEL_MODE && is_parallel_table && update_or_delete)
|
|
2215
|
+
{
|
|
2216
|
+
char tmp[32];
|
|
2217
|
+
|
|
2218
|
+
strncpy(tmp, p1, 7);
|
|
2219
|
+
sprintf(tmp+7, "%d", rows);
|
|
2220
|
+
|
|
2221
|
+
p2 = strdup(tmp);
|
|
2222
|
+
if (p2 == NULL)
|
|
2223
|
+
{
|
|
2224
|
+
pool_error("CommandComplete: malloc failed");
|
|
2225
|
+
free(p1);
|
|
2226
|
+
return POOL_ERROR;
|
|
2227
|
+
}
|
|
2228
|
+
|
|
2229
|
+
free(p1);
|
|
2230
|
+
p1 = p2;
|
|
2231
|
+
len1 = strlen(p2) + 1;
|
|
2232
|
+
}
|
|
2233
|
+
|
|
2234
|
+
pool_write(frontend, "C", 1);
|
|
2235
|
+
sendlen = htonl(len1+4);
|
|
2236
|
+
pool_write(frontend, &sendlen, sizeof(sendlen));
|
|
2237
|
+
pool_write_and_flush(frontend, p1, len1);
|
|
2238
|
+
}
|
|
2239
|
+
|
|
2240
|
+
/* save the received result for each kind */
|
|
2241
|
+
if (pool_config->enable_query_cache && SYSDB_STATUS == CON_UP)
|
|
2242
|
+
{
|
|
2243
|
+
query_cache_register('C', frontend, backend->info->database, p1, len1);
|
|
2244
|
+
}
|
|
2245
|
+
|
|
2246
|
+
/* Save the received result to buffer for each kind */
|
|
2247
|
+
if (pool_config->memory_cache_enabled)
|
|
2248
|
+
{
|
|
2249
|
+
if (pool_is_cache_safe() && !pool_is_cache_exceeded())
|
|
2250
|
+
{
|
|
2251
|
+
memqcache_register('C', frontend, p1, len1);
|
|
2252
|
+
}
|
|
2253
|
+
}
|
|
2254
|
+
|
|
2255
|
+
free(p1);
|
|
2256
|
+
|
|
2257
|
+
if (pool_is_doing_extended_query_message())
|
|
2258
|
+
{
|
|
2259
|
+
pool_set_query_state(session_context->query_context, POOL_EXECUTE_COMPLETE);
|
|
2260
|
+
}
|
|
2261
|
+
|
|
2262
|
+
return POOL_CONTINUE;
|
|
2263
|
+
}
|
|
2264
|
+
|
|
2265
|
+
POOL_STATUS ErrorResponse3(POOL_CONNECTION *frontend,
|
|
2266
|
+
POOL_CONNECTION_POOL *backend)
|
|
2267
|
+
{
|
|
2268
|
+
POOL_STATUS ret;
|
|
2269
|
+
|
|
2270
|
+
ret = SimpleForwardToFrontend('E', frontend, backend);
|
|
2271
|
+
if (ret != POOL_CONTINUE)
|
|
2272
|
+
return ret;
|
|
2273
|
+
|
|
2274
|
+
ret = raise_intentional_error_if_need(backend);
|
|
2275
|
+
if (ret != POOL_CONTINUE)
|
|
2276
|
+
return ret;
|
|
2277
|
+
|
|
2278
|
+
#ifdef NOT_USED
|
|
2279
|
+
for (i = 0;i < NUM_BACKENDS; i++)
|
|
2280
|
+
{
|
|
2281
|
+
if (VALID_BACKEND(i))
|
|
2282
|
+
{
|
|
2283
|
+
POOL_CONNECTION *cp = CONNECTION(backend, i);
|
|
2284
|
+
|
|
2285
|
+
/* We need to send "sync" message to backend in extend mode
|
|
2286
|
+
* so that it accepts next command.
|
|
2287
|
+
* Note that this may be overkill since client may send
|
|
2288
|
+
* it by itself. Moreover we do not need it in non-extend mode.
|
|
2289
|
+
* At this point we regard it is not harmful since error response
|
|
2290
|
+
* will not be sent too frequently.
|
|
2291
|
+
*/
|
|
2292
|
+
pool_write(cp, "S", 1);
|
|
2293
|
+
res1 = htonl(4);
|
|
2294
|
+
if (pool_write_and_flush(cp, &res1, sizeof(res1)) < 0)
|
|
2295
|
+
{
|
|
2296
|
+
return POOL_END;
|
|
2297
|
+
}
|
|
2298
|
+
}
|
|
2299
|
+
}
|
|
2300
|
+
|
|
2301
|
+
while ((ret = read_kind_from_backend(frontend, backend, &kind1)) == POOL_CONTINUE)
|
|
2302
|
+
{
|
|
2303
|
+
if (kind1 == 'Z') /* ReadyForQuery? */
|
|
2304
|
+
break;
|
|
2305
|
+
|
|
2306
|
+
ret = SimpleForwardToFrontend(kind1, frontend, backend);
|
|
2307
|
+
if (ret != POOL_CONTINUE)
|
|
2308
|
+
return ret;
|
|
2309
|
+
pool_flush(frontend);
|
|
2310
|
+
}
|
|
2311
|
+
|
|
2312
|
+
if (ret != POOL_CONTINUE)
|
|
2313
|
+
return ret;
|
|
2314
|
+
|
|
2315
|
+
for (i = 0; i < NUM_BACKENDS; i++)
|
|
2316
|
+
{
|
|
2317
|
+
if (VALID_BACKEND(i))
|
|
2318
|
+
{
|
|
2319
|
+
status = pool_read(CONNECTION(backend, i), &res1, sizeof(res1));
|
|
2320
|
+
if (status < 0)
|
|
2321
|
+
{
|
|
2322
|
+
pool_error("SimpleForwardToFrontend: error while reading message length");
|
|
2323
|
+
return POOL_END;
|
|
2324
|
+
}
|
|
2325
|
+
res1 = ntohl(res1) - sizeof(res1);
|
|
2326
|
+
p1 = pool_read2(CONNECTION(backend, i), res1);
|
|
2327
|
+
if (p1 == NULL)
|
|
2328
|
+
return POOL_END;
|
|
2329
|
+
}
|
|
2330
|
+
}
|
|
2331
|
+
#endif
|
|
2332
|
+
|
|
2333
|
+
return POOL_CONTINUE;
|
|
2334
|
+
}
|
|
2335
|
+
|
|
2336
|
+
POOL_STATUS FunctionCall(POOL_CONNECTION *frontend,
|
|
2337
|
+
POOL_CONNECTION_POOL *backend)
|
|
2338
|
+
{
|
|
2339
|
+
char dummy[2];
|
|
2340
|
+
int oid;
|
|
2341
|
+
int argn;
|
|
2342
|
+
int i;
|
|
2343
|
+
|
|
2344
|
+
for (i=0;i<NUM_BACKENDS;i++)
|
|
2345
|
+
{
|
|
2346
|
+
if (VALID_BACKEND(i))
|
|
2347
|
+
{
|
|
2348
|
+
pool_write(CONNECTION(backend, i), "F", 1);
|
|
2349
|
+
}
|
|
2350
|
+
}
|
|
2351
|
+
|
|
2352
|
+
/* dummy */
|
|
2353
|
+
if (pool_read(frontend, dummy, sizeof(dummy)) < 0)
|
|
2354
|
+
return POOL_ERROR;
|
|
2355
|
+
|
|
2356
|
+
for (i=0;i<NUM_BACKENDS;i++)
|
|
2357
|
+
{
|
|
2358
|
+
if (VALID_BACKEND(i))
|
|
2359
|
+
{
|
|
2360
|
+
pool_write(CONNECTION(backend, i), dummy, sizeof(dummy));
|
|
2361
|
+
}
|
|
2362
|
+
}
|
|
2363
|
+
|
|
2364
|
+
/* function object id */
|
|
2365
|
+
if (pool_read(frontend, &oid, sizeof(oid)) < 0)
|
|
2366
|
+
return POOL_ERROR;
|
|
2367
|
+
|
|
2368
|
+
for (i=0;i<NUM_BACKENDS;i++)
|
|
2369
|
+
{
|
|
2370
|
+
if (VALID_BACKEND(i))
|
|
2371
|
+
{
|
|
2372
|
+
pool_write(CONNECTION(backend, i), &oid, sizeof(oid));
|
|
2373
|
+
}
|
|
2374
|
+
}
|
|
2375
|
+
|
|
2376
|
+
/* number of arguments */
|
|
2377
|
+
if (pool_read(frontend, &argn, sizeof(argn)) < 0)
|
|
2378
|
+
return POOL_ERROR;
|
|
2379
|
+
|
|
2380
|
+
for (i=0;i<NUM_BACKENDS;i++)
|
|
2381
|
+
{
|
|
2382
|
+
if (VALID_BACKEND(i))
|
|
2383
|
+
{
|
|
2384
|
+
pool_write(CONNECTION(backend, i), &argn, sizeof(argn));
|
|
2385
|
+
}
|
|
2386
|
+
}
|
|
2387
|
+
|
|
2388
|
+
argn = ntohl(argn);
|
|
2389
|
+
|
|
2390
|
+
for (i=0;i<argn;i++)
|
|
2391
|
+
{
|
|
2392
|
+
int len;
|
|
2393
|
+
char *arg;
|
|
2394
|
+
|
|
2395
|
+
/* length of each argument in bytes */
|
|
2396
|
+
if (pool_read(frontend, &len, sizeof(len)) < 0)
|
|
2397
|
+
return POOL_ERROR;
|
|
2398
|
+
|
|
2399
|
+
for (i=0;i<NUM_BACKENDS;i++)
|
|
2400
|
+
{
|
|
2401
|
+
if (VALID_BACKEND(i))
|
|
2402
|
+
{
|
|
2403
|
+
pool_write(CONNECTION(backend, i), &len, sizeof(len));
|
|
2404
|
+
}
|
|
2405
|
+
}
|
|
2406
|
+
|
|
2407
|
+
len = ntohl(len);
|
|
2408
|
+
|
|
2409
|
+
/* argument value itself */
|
|
2410
|
+
if ((arg = pool_read2(frontend, len)) == NULL)
|
|
2411
|
+
return POOL_ERROR;
|
|
2412
|
+
|
|
2413
|
+
for (i=0;i<NUM_BACKENDS;i++)
|
|
2414
|
+
{
|
|
2415
|
+
if (VALID_BACKEND(i))
|
|
2416
|
+
{
|
|
2417
|
+
pool_write(CONNECTION(backend, i), arg, len);
|
|
2418
|
+
}
|
|
2419
|
+
}
|
|
2420
|
+
}
|
|
2421
|
+
|
|
2422
|
+
for (i=0;i<NUM_BACKENDS;i++)
|
|
2423
|
+
{
|
|
2424
|
+
if (VALID_BACKEND(i))
|
|
2425
|
+
{
|
|
2426
|
+
if (pool_flush(CONNECTION(backend, i)))
|
|
2427
|
+
return POOL_ERROR;
|
|
2428
|
+
}
|
|
2429
|
+
}
|
|
2430
|
+
return POOL_CONTINUE;
|
|
2431
|
+
}
|
|
2432
|
+
|
|
2433
|
+
POOL_STATUS ProcessFrontendResponse(POOL_CONNECTION *frontend,
|
|
2434
|
+
POOL_CONNECTION_POOL *backend)
|
|
2435
|
+
{
|
|
2436
|
+
char fkind;
|
|
2437
|
+
char *bufp = NULL;
|
|
2438
|
+
char *contents;
|
|
2439
|
+
POOL_STATUS status;
|
|
2440
|
+
int len;
|
|
2441
|
+
POOL_SESSION_CONTEXT *session_context;
|
|
2442
|
+
|
|
2443
|
+
/* Get session context */
|
|
2444
|
+
session_context = pool_get_session_context();
|
|
2445
|
+
if (!session_context)
|
|
2446
|
+
{
|
|
2447
|
+
pool_error("Parse: cannot get session context");
|
|
2448
|
+
return POOL_END;
|
|
2449
|
+
}
|
|
2450
|
+
|
|
2451
|
+
if (pool_read_buffer_is_empty(frontend) && frontend->no_forward != 0)
|
|
2452
|
+
return POOL_CONTINUE;
|
|
2453
|
+
|
|
2454
|
+
if (pool_read(frontend, &fkind, 1) < 0)
|
|
2455
|
+
{
|
|
2456
|
+
pool_log("ProcessFrontendResponse: failed to read kind from frontend. frontend abnormally exited");
|
|
2457
|
+
return POOL_END;
|
|
2458
|
+
}
|
|
2459
|
+
|
|
2460
|
+
pool_debug("ProcessFrontendResponse: kind from frontend %c(%02x)", fkind, fkind);
|
|
2461
|
+
|
|
2462
|
+
if (MAJOR(backend) == PROTO_MAJOR_V3)
|
|
2463
|
+
{
|
|
2464
|
+
if (pool_read(frontend, &len, sizeof(len)) < 0)
|
|
2465
|
+
return POOL_END;
|
|
2466
|
+
len = ntohl(len) - 4;
|
|
2467
|
+
if (len > 0)
|
|
2468
|
+
bufp = pool_read2(frontend, len);
|
|
2469
|
+
}
|
|
2470
|
+
else
|
|
2471
|
+
{
|
|
2472
|
+
if (fkind != 'F')
|
|
2473
|
+
bufp = pool_read_string(frontend, &len, 0);
|
|
2474
|
+
}
|
|
2475
|
+
|
|
2476
|
+
if (len > 0 && bufp == NULL)
|
|
2477
|
+
return POOL_END;
|
|
2478
|
+
|
|
2479
|
+
if (fkind != 'S' && pool_is_ignore_till_sync())
|
|
2480
|
+
{
|
|
2481
|
+
/*
|
|
2482
|
+
* Flag setting for calling ProcessBackendResponse()
|
|
2483
|
+
* in pool_process_query().
|
|
2484
|
+
*/
|
|
2485
|
+
if (!pool_is_query_in_progress())
|
|
2486
|
+
pool_set_query_in_progress();
|
|
2487
|
+
return POOL_CONTINUE;
|
|
2488
|
+
}
|
|
2489
|
+
|
|
2490
|
+
pool_unset_doing_extended_query_message();
|
|
2491
|
+
|
|
2492
|
+
/*
|
|
2493
|
+
* Allocate buffer and copy the packet contents. Because inside
|
|
2494
|
+
* these protocol modules, pool_read2 maybe called and modify its
|
|
2495
|
+
* buffer contents.
|
|
2496
|
+
*/
|
|
2497
|
+
contents = malloc(len);
|
|
2498
|
+
if (contents == NULL)
|
|
2499
|
+
{
|
|
2500
|
+
pool_error("ProcessFrontendResponse: cannot allocate memory. request size:%d", len);
|
|
2501
|
+
return POOL_ERROR;
|
|
2502
|
+
}
|
|
2503
|
+
memcpy(contents, bufp, len);
|
|
2504
|
+
|
|
2505
|
+
switch (fkind)
|
|
2506
|
+
{
|
|
2507
|
+
POOL_QUERY_CONTEXT *query_context;
|
|
2508
|
+
char *query;
|
|
2509
|
+
Node *node;
|
|
2510
|
+
List *parse_tree_list;
|
|
2511
|
+
POOL_MEMORY_POOL *old_context;
|
|
2512
|
+
|
|
2513
|
+
case 'X': /* Terminate */
|
|
2514
|
+
free(contents);
|
|
2515
|
+
return POOL_END;
|
|
2516
|
+
|
|
2517
|
+
case 'Q': /* Query */
|
|
2518
|
+
allow_close_transaction = 1;
|
|
2519
|
+
status = SimpleQuery(frontend, backend, len, contents);
|
|
2520
|
+
break;
|
|
2521
|
+
|
|
2522
|
+
case 'E': /* Execute */
|
|
2523
|
+
allow_close_transaction = 1;
|
|
2524
|
+
pool_set_doing_extended_query_message();
|
|
2525
|
+
if (!pool_is_query_in_progress() && !pool_is_ignore_till_sync())
|
|
2526
|
+
pool_set_query_in_progress();
|
|
2527
|
+
status = Execute(frontend, backend, len, contents);
|
|
2528
|
+
break;
|
|
2529
|
+
|
|
2530
|
+
case 'P': /* Parse */
|
|
2531
|
+
allow_close_transaction = 0;
|
|
2532
|
+
pool_set_doing_extended_query_message();
|
|
2533
|
+
if (!pool_is_query_in_progress() && !pool_is_ignore_till_sync())
|
|
2534
|
+
pool_set_query_in_progress();
|
|
2535
|
+
status = Parse(frontend, backend, len, contents);
|
|
2536
|
+
break;
|
|
2537
|
+
|
|
2538
|
+
case 'B': /* Bind */
|
|
2539
|
+
pool_set_doing_extended_query_message();
|
|
2540
|
+
if (!pool_is_query_in_progress() && !pool_is_ignore_till_sync())
|
|
2541
|
+
pool_set_query_in_progress();
|
|
2542
|
+
status = Bind(frontend, backend, len, contents);
|
|
2543
|
+
break;
|
|
2544
|
+
|
|
2545
|
+
case 'C': /* Close */
|
|
2546
|
+
if (!pool_is_query_in_progress() && !pool_is_ignore_till_sync())
|
|
2547
|
+
pool_set_query_in_progress();
|
|
2548
|
+
status = Close(frontend, backend, len, contents);
|
|
2549
|
+
break;
|
|
2550
|
+
|
|
2551
|
+
case 'D': /* Describe */
|
|
2552
|
+
pool_set_doing_extended_query_message();
|
|
2553
|
+
if (!pool_is_query_in_progress() && !pool_is_ignore_till_sync())
|
|
2554
|
+
pool_set_query_in_progress();
|
|
2555
|
+
status = Describe(frontend, backend, len, contents);
|
|
2556
|
+
break;
|
|
2557
|
+
|
|
2558
|
+
case 'S': /* Sync */
|
|
2559
|
+
pool_set_doing_extended_query_message();
|
|
2560
|
+
if (pool_is_ignore_till_sync())
|
|
2561
|
+
pool_unset_ignore_till_sync();
|
|
2562
|
+
if (!pool_is_query_in_progress())
|
|
2563
|
+
pool_set_query_in_progress();
|
|
2564
|
+
status = SimpleForwardToBackend(fkind, frontend, backend, len, contents);
|
|
2565
|
+
break;
|
|
2566
|
+
|
|
2567
|
+
case 'F': /* FunctionCall */
|
|
2568
|
+
/*
|
|
2569
|
+
* Create dummy query context as if it were an INSERT.
|
|
2570
|
+
*/
|
|
2571
|
+
query_context = pool_init_query_context();
|
|
2572
|
+
if (!query_context)
|
|
2573
|
+
{
|
|
2574
|
+
pool_error("ProcessFrontendResponse: pool_init_query_context failed");
|
|
2575
|
+
free(contents);
|
|
2576
|
+
return POOL_END;
|
|
2577
|
+
}
|
|
2578
|
+
old_context = pool_memory_context_switch_to(query_context->memory_context);
|
|
2579
|
+
query = "INSERT INTO foo VALUES(1)";
|
|
2580
|
+
parse_tree_list = raw_parser(query);
|
|
2581
|
+
node = (Node *) lfirst(list_head(parse_tree_list));
|
|
2582
|
+
pool_start_query(query_context, query, strlen(query) + 1, node);
|
|
2583
|
+
pool_where_to_send(query_context, query_context->original_query,
|
|
2584
|
+
query_context->parse_tree);
|
|
2585
|
+
|
|
2586
|
+
if (MAJOR(backend) == PROTO_MAJOR_V3)
|
|
2587
|
+
status = FunctionCall3(frontend, backend, len, contents);
|
|
2588
|
+
else
|
|
2589
|
+
status = FunctionCall(frontend, backend);
|
|
2590
|
+
|
|
2591
|
+
pool_memory_context_switch_to(old_context);
|
|
2592
|
+
break;
|
|
2593
|
+
|
|
2594
|
+
case 'c': /* CopyDone */
|
|
2595
|
+
case 'd': /* CopyData */
|
|
2596
|
+
case 'f': /* CopyFail */
|
|
2597
|
+
case 'H': /* Flush */
|
|
2598
|
+
if (MAJOR(backend) == PROTO_MAJOR_V3)
|
|
2599
|
+
{
|
|
2600
|
+
status = SimpleForwardToBackend(fkind, frontend, backend, len, contents);
|
|
2601
|
+
break;
|
|
2602
|
+
}
|
|
2603
|
+
|
|
2604
|
+
default:
|
|
2605
|
+
pool_error("ProcessFrontendResponse: unknown message type %c(%02x)", fkind, fkind);
|
|
2606
|
+
status = POOL_ERROR;
|
|
2607
|
+
}
|
|
2608
|
+
free(contents);
|
|
2609
|
+
|
|
2610
|
+
if (status != POOL_CONTINUE)
|
|
2611
|
+
status = POOL_ERROR;
|
|
2612
|
+
return status;
|
|
2613
|
+
}
|
|
2614
|
+
|
|
2615
|
+
POOL_STATUS ProcessBackendResponse(POOL_CONNECTION *frontend,
|
|
2616
|
+
POOL_CONNECTION_POOL *backend,
|
|
2617
|
+
int *state, short *num_fields)
|
|
2618
|
+
{
|
|
2619
|
+
int status;
|
|
2620
|
+
char kind;
|
|
2621
|
+
POOL_SESSION_CONTEXT *session_context;
|
|
2622
|
+
|
|
2623
|
+
/* Get session context */
|
|
2624
|
+
session_context = pool_get_session_context();
|
|
2625
|
+
if (!session_context)
|
|
2626
|
+
{
|
|
2627
|
+
pool_error("ProcessBackendResponse: cannot get session context");
|
|
2628
|
+
return POOL_END;
|
|
2629
|
+
}
|
|
2630
|
+
|
|
2631
|
+
if (pool_is_ignore_till_sync())
|
|
2632
|
+
{
|
|
2633
|
+
if (pool_is_query_in_progress())
|
|
2634
|
+
pool_unset_query_in_progress();
|
|
2635
|
+
return POOL_CONTINUE;
|
|
2636
|
+
}
|
|
2637
|
+
|
|
2638
|
+
if (pool_is_skip_reading_from_backends())
|
|
2639
|
+
{
|
|
2640
|
+
pool_unset_skip_reading_from_backends();
|
|
2641
|
+
return POOL_CONTINUE;
|
|
2642
|
+
}
|
|
2643
|
+
|
|
2644
|
+
status = read_kind_from_backend(frontend, backend, &kind);
|
|
2645
|
+
if (status != POOL_CONTINUE)
|
|
2646
|
+
return status;
|
|
2647
|
+
|
|
2648
|
+
/*
|
|
2649
|
+
* Sanity check
|
|
2650
|
+
*/
|
|
2651
|
+
if (kind == 0)
|
|
2652
|
+
{
|
|
2653
|
+
pool_error("ProcessBackendResponse: kind is 0!");
|
|
2654
|
+
return POOL_ERROR;
|
|
2655
|
+
}
|
|
2656
|
+
|
|
2657
|
+
pool_debug("ProcessBackendResponse: kind from backend: %c", kind);
|
|
2658
|
+
|
|
2659
|
+
if (MAJOR(backend) == PROTO_MAJOR_V3)
|
|
2660
|
+
{
|
|
2661
|
+
switch (kind)
|
|
2662
|
+
{
|
|
2663
|
+
case 'G': /* CopyInResponse */
|
|
2664
|
+
status = CopyInResponse(frontend, backend);
|
|
2665
|
+
break;
|
|
2666
|
+
|
|
2667
|
+
case 'S': /* ParameterStatus */
|
|
2668
|
+
status = ParameterStatus(frontend, backend);
|
|
2669
|
+
break;
|
|
2670
|
+
|
|
2671
|
+
case 'Z': /* ReadyForQuery */
|
|
2672
|
+
status = ReadyForQuery(frontend, backend, true, true);
|
|
2673
|
+
pool_debug("ProcessBackendResponse: Ready For Query");
|
|
2674
|
+
break;
|
|
2675
|
+
|
|
2676
|
+
case '1': /* ParseComplete */
|
|
2677
|
+
status = ParseComplete(frontend, backend);
|
|
2678
|
+
pool_set_command_success();
|
|
2679
|
+
pool_unset_query_in_progress();
|
|
2680
|
+
break;
|
|
2681
|
+
|
|
2682
|
+
case '2': /* BindComplete */
|
|
2683
|
+
status = BindComplete(frontend, backend);
|
|
2684
|
+
pool_set_command_success();
|
|
2685
|
+
pool_unset_query_in_progress();
|
|
2686
|
+
break;
|
|
2687
|
+
|
|
2688
|
+
case '3': /* CloseComplete */
|
|
2689
|
+
status = CloseComplete(frontend, backend);
|
|
2690
|
+
pool_set_command_success();
|
|
2691
|
+
pool_unset_query_in_progress();
|
|
2692
|
+
break;
|
|
2693
|
+
|
|
2694
|
+
case 'E': /* ErrorResponse */
|
|
2695
|
+
status = ErrorResponse3(frontend, backend);
|
|
2696
|
+
pool_unset_command_success();
|
|
2697
|
+
if (TSTATE(backend, MASTER_SLAVE ? PRIMARY_NODE_ID :
|
|
2698
|
+
REAL_MASTER_NODE_ID) != 'I')
|
|
2699
|
+
pool_set_failed_transaction();
|
|
2700
|
+
if (pool_is_doing_extended_query_message())
|
|
2701
|
+
{
|
|
2702
|
+
pool_set_ignore_till_sync();
|
|
2703
|
+
pool_unset_query_in_progress();
|
|
2704
|
+
}
|
|
2705
|
+
break;
|
|
2706
|
+
|
|
2707
|
+
case 'C': /* CommandComplete */
|
|
2708
|
+
status = CommandComplete(frontend, backend);
|
|
2709
|
+
pool_set_command_success();
|
|
2710
|
+
if (pool_is_doing_extended_query_message())
|
|
2711
|
+
pool_unset_query_in_progress();
|
|
2712
|
+
break;
|
|
2713
|
+
|
|
2714
|
+
case 'I': /* EmptyQueryResponse */
|
|
2715
|
+
status = SimpleForwardToFrontend(kind, frontend, backend);
|
|
2716
|
+
if (pool_is_doing_extended_query_message())
|
|
2717
|
+
pool_unset_query_in_progress();
|
|
2718
|
+
break;
|
|
2719
|
+
|
|
2720
|
+
case 'T': /* RowDescription */
|
|
2721
|
+
status = SimpleForwardToFrontend(kind, frontend, backend);
|
|
2722
|
+
if (pool_is_doing_extended_query_message())
|
|
2723
|
+
pool_unset_query_in_progress();
|
|
2724
|
+
break;
|
|
2725
|
+
|
|
2726
|
+
case 'n': /* NoData */
|
|
2727
|
+
status = SimpleForwardToFrontend(kind, frontend, backend);
|
|
2728
|
+
if (pool_is_doing_extended_query_message())
|
|
2729
|
+
pool_unset_query_in_progress();
|
|
2730
|
+
break;
|
|
2731
|
+
|
|
2732
|
+
case 's': /* PortalSuspended */
|
|
2733
|
+
status = SimpleForwardToFrontend(kind, frontend, backend);
|
|
2734
|
+
if (pool_is_doing_extended_query_message())
|
|
2735
|
+
pool_unset_query_in_progress();
|
|
2736
|
+
break;
|
|
2737
|
+
|
|
2738
|
+
default:
|
|
2739
|
+
status = SimpleForwardToFrontend(kind, frontend, backend);
|
|
2740
|
+
if (pool_flush(frontend))
|
|
2741
|
+
return POOL_END;
|
|
2742
|
+
break;
|
|
2743
|
+
}
|
|
2744
|
+
}
|
|
2745
|
+
else
|
|
2746
|
+
{
|
|
2747
|
+
switch (kind)
|
|
2748
|
+
{
|
|
2749
|
+
case 'A': /* NotificationResponse */
|
|
2750
|
+
status = NotificationResponse(frontend, backend);
|
|
2751
|
+
break;
|
|
2752
|
+
|
|
2753
|
+
case 'B': /* BinaryRow */
|
|
2754
|
+
status = BinaryRow(frontend, backend, *num_fields);
|
|
2755
|
+
break;
|
|
2756
|
+
|
|
2757
|
+
case 'C': /* CompletedResponse */
|
|
2758
|
+
status = CompletedResponse(frontend, backend);
|
|
2759
|
+
break;
|
|
2760
|
+
|
|
2761
|
+
case 'D': /* AsciiRow */
|
|
2762
|
+
status = AsciiRow(frontend, backend, *num_fields);
|
|
2763
|
+
break;
|
|
2764
|
+
|
|
2765
|
+
case 'E': /* ErrorResponse */
|
|
2766
|
+
status = ErrorResponse(frontend, backend);
|
|
2767
|
+
if (TSTATE(backend, MASTER_SLAVE ? PRIMARY_NODE_ID :
|
|
2768
|
+
REAL_MASTER_NODE_ID) != 'I')
|
|
2769
|
+
pool_set_failed_transaction();
|
|
2770
|
+
break;
|
|
2771
|
+
|
|
2772
|
+
case 'G': /* CopyInResponse */
|
|
2773
|
+
status = CopyInResponse(frontend, backend);
|
|
2774
|
+
break;
|
|
2775
|
+
|
|
2776
|
+
case 'H': /* CopyOutResponse */
|
|
2777
|
+
status = CopyOutResponse(frontend, backend);
|
|
2778
|
+
break;
|
|
2779
|
+
|
|
2780
|
+
case 'I': /* EmptyQueryResponse */
|
|
2781
|
+
status = EmptyQueryResponse(frontend, backend);
|
|
2782
|
+
break;
|
|
2783
|
+
|
|
2784
|
+
case 'N': /* NoticeResponse */
|
|
2785
|
+
status = NoticeResponse(frontend, backend);
|
|
2786
|
+
break;
|
|
2787
|
+
|
|
2788
|
+
case 'P': /* CursorResponse */
|
|
2789
|
+
status = CursorResponse(frontend, backend);
|
|
2790
|
+
break;
|
|
2791
|
+
|
|
2792
|
+
case 'T': /* RowDescription */
|
|
2793
|
+
status = RowDescription(frontend, backend, num_fields);
|
|
2794
|
+
break;
|
|
2795
|
+
|
|
2796
|
+
case 'V': /* FunctionResultResponse and FunctionVoidResponse */
|
|
2797
|
+
status = FunctionResultResponse(frontend, backend);
|
|
2798
|
+
break;
|
|
2799
|
+
|
|
2800
|
+
case 'Z': /* ReadyForQuery */
|
|
2801
|
+
status = ReadyForQuery(frontend, backend, true, true);
|
|
2802
|
+
break;
|
|
2803
|
+
|
|
2804
|
+
default:
|
|
2805
|
+
pool_error("Unknown message type %c(%02x)", kind, kind);
|
|
2806
|
+
exit(1);
|
|
2807
|
+
}
|
|
2808
|
+
}
|
|
2809
|
+
|
|
2810
|
+
/* Do we receive ready for query while processing reset
|
|
2811
|
+
* request?
|
|
2812
|
+
*/
|
|
2813
|
+
if (kind == 'Z' && frontend->no_forward && *state == 1)
|
|
2814
|
+
{
|
|
2815
|
+
*state = 0;
|
|
2816
|
+
}
|
|
2817
|
+
|
|
2818
|
+
if (status != POOL_CONTINUE)
|
|
2819
|
+
status = POOL_ERROR;
|
|
2820
|
+
return status;
|
|
2821
|
+
}
|
|
2822
|
+
|
|
2823
|
+
POOL_STATUS CopyInResponse(POOL_CONNECTION *frontend,
|
|
2824
|
+
POOL_CONNECTION_POOL *backend)
|
|
2825
|
+
{
|
|
2826
|
+
POOL_STATUS status;
|
|
2827
|
+
|
|
2828
|
+
/* forward to the frontend */
|
|
2829
|
+
if (MAJOR(backend) == PROTO_MAJOR_V3)
|
|
2830
|
+
{
|
|
2831
|
+
if (SimpleForwardToFrontend('G', frontend, backend) != POOL_CONTINUE)
|
|
2832
|
+
return POOL_END;
|
|
2833
|
+
if (pool_flush(frontend) != POOL_CONTINUE)
|
|
2834
|
+
return POOL_END;
|
|
2835
|
+
}
|
|
2836
|
+
else
|
|
2837
|
+
if (pool_write_and_flush(frontend, "G", 1) < 0)
|
|
2838
|
+
return POOL_END;
|
|
2839
|
+
|
|
2840
|
+
status = CopyDataRows(frontend, backend, 1);
|
|
2841
|
+
return status;
|
|
2842
|
+
}
|
|
2843
|
+
|
|
2844
|
+
POOL_STATUS CopyOutResponse(POOL_CONNECTION *frontend,
|
|
2845
|
+
POOL_CONNECTION_POOL *backend)
|
|
2846
|
+
{
|
|
2847
|
+
POOL_STATUS status;
|
|
2848
|
+
|
|
2849
|
+
/* forward to the frontend */
|
|
2850
|
+
if (MAJOR(backend) == PROTO_MAJOR_V3)
|
|
2851
|
+
{
|
|
2852
|
+
if (SimpleForwardToFrontend('H', frontend, backend) != POOL_CONTINUE)
|
|
2853
|
+
return POOL_END;
|
|
2854
|
+
if (pool_flush(frontend) != POOL_CONTINUE)
|
|
2855
|
+
return POOL_END;
|
|
2856
|
+
}
|
|
2857
|
+
else
|
|
2858
|
+
if (pool_write_and_flush(frontend, "H", 1) < 0)
|
|
2859
|
+
return POOL_END;
|
|
2860
|
+
|
|
2861
|
+
status = CopyDataRows(frontend, backend, 0);
|
|
2862
|
+
return status;
|
|
2863
|
+
}
|
|
2864
|
+
|
|
2865
|
+
POOL_STATUS CopyDataRows(POOL_CONNECTION *frontend,
|
|
2866
|
+
POOL_CONNECTION_POOL *backend, int copyin)
|
|
2867
|
+
{
|
|
2868
|
+
char *string = NULL;
|
|
2869
|
+
int len;
|
|
2870
|
+
int i;
|
|
2871
|
+
DistDefInfo *info = NULL;
|
|
2872
|
+
|
|
2873
|
+
#ifdef DEBUG
|
|
2874
|
+
int j = 0;
|
|
2875
|
+
char buf[1024];
|
|
2876
|
+
#endif
|
|
2877
|
+
|
|
2878
|
+
if (copyin && pool_config->parallel_mode == TRUE)
|
|
2879
|
+
{
|
|
2880
|
+
info = pool_get_dist_def_info(MASTER_CONNECTION(backend)->sp->database,
|
|
2881
|
+
copy_schema,
|
|
2882
|
+
copy_table);
|
|
2883
|
+
}
|
|
2884
|
+
|
|
2885
|
+
for (;;)
|
|
2886
|
+
{
|
|
2887
|
+
if (copyin)
|
|
2888
|
+
{
|
|
2889
|
+
if (MAJOR(backend) == PROTO_MAJOR_V3)
|
|
2890
|
+
{
|
|
2891
|
+
char kind;
|
|
2892
|
+
int sendlen;
|
|
2893
|
+
char *p, *p1;
|
|
2894
|
+
|
|
2895
|
+
if (pool_read(frontend, &kind, 1) < 0)
|
|
2896
|
+
return POOL_END;
|
|
2897
|
+
|
|
2898
|
+
if (info && kind == 'd')
|
|
2899
|
+
{
|
|
2900
|
+
int id;
|
|
2901
|
+
if (pool_read(frontend, &sendlen, sizeof(sendlen)))
|
|
2902
|
+
{
|
|
2903
|
+
return POOL_END;
|
|
2904
|
+
}
|
|
2905
|
+
|
|
2906
|
+
len = ntohl(sendlen) - 4;
|
|
2907
|
+
|
|
2908
|
+
if (len <= 0)
|
|
2909
|
+
return POOL_CONTINUE;
|
|
2910
|
+
|
|
2911
|
+
p = pool_read2(frontend, len);
|
|
2912
|
+
if (p == NULL)
|
|
2913
|
+
return POOL_END;
|
|
2914
|
+
|
|
2915
|
+
/* copy end ? */
|
|
2916
|
+
if (len == 3 && memcmp(p, "\\.\n", 3) == 0)
|
|
2917
|
+
{
|
|
2918
|
+
for (i=0;i<NUM_BACKENDS;i++)
|
|
2919
|
+
{
|
|
2920
|
+
if (VALID_BACKEND(i))
|
|
2921
|
+
{
|
|
2922
|
+
if (pool_write(CONNECTION(backend, i), &kind, 1))
|
|
2923
|
+
return POOL_END;
|
|
2924
|
+
if (pool_write(CONNECTION(backend, i), &sendlen, sizeof(sendlen)))
|
|
2925
|
+
return POOL_END;
|
|
2926
|
+
if (pool_write(CONNECTION(backend, i), p, len))
|
|
2927
|
+
return POOL_END;
|
|
2928
|
+
}
|
|
2929
|
+
}
|
|
2930
|
+
}
|
|
2931
|
+
else
|
|
2932
|
+
{
|
|
2933
|
+
p1 = parse_copy_data(p, len, copy_delimiter, info->dist_key_col_id);
|
|
2934
|
+
|
|
2935
|
+
if (!p1)
|
|
2936
|
+
{
|
|
2937
|
+
pool_error("CopyDataRow: cannot parse data");
|
|
2938
|
+
return POOL_END;
|
|
2939
|
+
}
|
|
2940
|
+
else if (strcmp(p1, copy_null) == 0)
|
|
2941
|
+
{
|
|
2942
|
+
pool_error("CopyDataRow: key parameter is NULL");
|
|
2943
|
+
free(p1);
|
|
2944
|
+
return POOL_END;
|
|
2945
|
+
}
|
|
2946
|
+
|
|
2947
|
+
id = pool_get_id(info, p1);
|
|
2948
|
+
pool_debug("CopyDataRow: copying id: %d", id);
|
|
2949
|
+
free(p1);
|
|
2950
|
+
if (id < 0 || !VALID_BACKEND(id))
|
|
2951
|
+
{
|
|
2952
|
+
pool_error("CopyDataRow: pool_get_id returns invalid id: %d", id);
|
|
2953
|
+
exit(1);
|
|
2954
|
+
}
|
|
2955
|
+
if (pool_write(CONNECTION(backend, id), &kind, 1))
|
|
2956
|
+
{
|
|
2957
|
+
return POOL_END;
|
|
2958
|
+
}
|
|
2959
|
+
if (pool_write(CONNECTION(backend, id), &sendlen, sizeof(sendlen)))
|
|
2960
|
+
{
|
|
2961
|
+
return POOL_END;
|
|
2962
|
+
}
|
|
2963
|
+
if (pool_write_and_flush(CONNECTION(backend, id), p, len))
|
|
2964
|
+
{
|
|
2965
|
+
return POOL_END;
|
|
2966
|
+
}
|
|
2967
|
+
}
|
|
2968
|
+
}
|
|
2969
|
+
else
|
|
2970
|
+
{
|
|
2971
|
+
char *contents = NULL;
|
|
2972
|
+
|
|
2973
|
+
pool_debug("CopyDataRows: read kind from frontend %c(%02x)", kind, kind);
|
|
2974
|
+
|
|
2975
|
+
if (pool_read(frontend, &len, sizeof(len)) < 0)
|
|
2976
|
+
return POOL_END;
|
|
2977
|
+
len = ntohl(len) - 4;
|
|
2978
|
+
if (len > 0)
|
|
2979
|
+
contents = pool_read2(frontend, len);
|
|
2980
|
+
|
|
2981
|
+
SimpleForwardToBackend(kind, frontend, backend, len, contents);
|
|
2982
|
+
}
|
|
2983
|
+
|
|
2984
|
+
/* CopyData? */
|
|
2985
|
+
if (kind == 'd')
|
|
2986
|
+
continue;
|
|
2987
|
+
else
|
|
2988
|
+
{
|
|
2989
|
+
pool_debug("CopyDataRows: copyin kind other than d (%c)", kind);
|
|
2990
|
+
break;
|
|
2991
|
+
}
|
|
2992
|
+
}
|
|
2993
|
+
else
|
|
2994
|
+
string = pool_read_string(frontend, &len, 1);
|
|
2995
|
+
}
|
|
2996
|
+
else
|
|
2997
|
+
{
|
|
2998
|
+
/* CopyOut */
|
|
2999
|
+
if (MAJOR(backend) == PROTO_MAJOR_V3)
|
|
3000
|
+
{
|
|
3001
|
+
signed char kind;
|
|
3002
|
+
|
|
3003
|
+
if ((kind = pool_read_kind(backend)) < 0)
|
|
3004
|
+
return POOL_END;
|
|
3005
|
+
|
|
3006
|
+
SimpleForwardToFrontend(kind, frontend, backend);
|
|
3007
|
+
|
|
3008
|
+
/* CopyData? */
|
|
3009
|
+
if (kind == 'd')
|
|
3010
|
+
continue;
|
|
3011
|
+
else
|
|
3012
|
+
break;
|
|
3013
|
+
}
|
|
3014
|
+
else
|
|
3015
|
+
{
|
|
3016
|
+
for (i=0;i<NUM_BACKENDS;i++)
|
|
3017
|
+
{
|
|
3018
|
+
if (VALID_BACKEND(i))
|
|
3019
|
+
{
|
|
3020
|
+
string = pool_read_string(CONNECTION(backend, i), &len, 1);
|
|
3021
|
+
}
|
|
3022
|
+
}
|
|
3023
|
+
}
|
|
3024
|
+
}
|
|
3025
|
+
|
|
3026
|
+
if (string == NULL)
|
|
3027
|
+
return POOL_END;
|
|
3028
|
+
|
|
3029
|
+
#ifdef DEBUG
|
|
3030
|
+
strlcpy(buf, string, sizeof(buf));
|
|
3031
|
+
pool_debug("copy line %d %d bytes :%s:", j++, len, buf);
|
|
3032
|
+
#endif
|
|
3033
|
+
|
|
3034
|
+
if (copyin)
|
|
3035
|
+
{
|
|
3036
|
+
for (i=0;i<NUM_BACKENDS;i++)
|
|
3037
|
+
{
|
|
3038
|
+
if (VALID_BACKEND(i))
|
|
3039
|
+
{
|
|
3040
|
+
pool_write(CONNECTION(backend, i), string, len);
|
|
3041
|
+
}
|
|
3042
|
+
}
|
|
3043
|
+
}
|
|
3044
|
+
else
|
|
3045
|
+
pool_write(frontend, string, len);
|
|
3046
|
+
|
|
3047
|
+
if (len == PROTO_MAJOR_V3)
|
|
3048
|
+
{
|
|
3049
|
+
/* end of copy? */
|
|
3050
|
+
if (string[0] == '\\' &&
|
|
3051
|
+
string[1] == '.' &&
|
|
3052
|
+
string[2] == '\n')
|
|
3053
|
+
{
|
|
3054
|
+
break;
|
|
3055
|
+
}
|
|
3056
|
+
}
|
|
3057
|
+
}
|
|
3058
|
+
|
|
3059
|
+
if (copyin)
|
|
3060
|
+
{
|
|
3061
|
+
for (i=0;i<NUM_BACKENDS;i++)
|
|
3062
|
+
{
|
|
3063
|
+
if (VALID_BACKEND(i))
|
|
3064
|
+
{
|
|
3065
|
+
if (pool_flush(CONNECTION(backend, i)) <0)
|
|
3066
|
+
return POOL_END;
|
|
3067
|
+
|
|
3068
|
+
if (synchronize(CONNECTION(backend, i)))
|
|
3069
|
+
return POOL_END;
|
|
3070
|
+
}
|
|
3071
|
+
}
|
|
3072
|
+
}
|
|
3073
|
+
else
|
|
3074
|
+
if (pool_flush(frontend) <0)
|
|
3075
|
+
return POOL_END;
|
|
3076
|
+
|
|
3077
|
+
return POOL_CONTINUE;
|
|
3078
|
+
}
|
|
3079
|
+
|
|
3080
|
+
/*
|
|
3081
|
+
* This function raises intentional error to make backends the same
|
|
3082
|
+
* transaction state.
|
|
3083
|
+
*/
|
|
3084
|
+
POOL_STATUS raise_intentional_error_if_need(POOL_CONNECTION_POOL *backend)
|
|
3085
|
+
{
|
|
3086
|
+
int i;
|
|
3087
|
+
POOL_STATUS ret;
|
|
3088
|
+
POOL_SESSION_CONTEXT *session_context;
|
|
3089
|
+
POOL_QUERY_CONTEXT *query_context;
|
|
3090
|
+
|
|
3091
|
+
/* Get session context */
|
|
3092
|
+
session_context = pool_get_session_context();
|
|
3093
|
+
if (!session_context)
|
|
3094
|
+
{
|
|
3095
|
+
pool_error("raise_intentional_error_if_need: cannot get session context");
|
|
3096
|
+
return POOL_END;
|
|
3097
|
+
}
|
|
3098
|
+
|
|
3099
|
+
query_context = session_context->query_context;
|
|
3100
|
+
|
|
3101
|
+
if (MASTER_SLAVE &&
|
|
3102
|
+
TSTATE(backend, PRIMARY_NODE_ID) == 'T' &&
|
|
3103
|
+
PRIMARY_NODE_ID != MASTER_NODE_ID &&
|
|
3104
|
+
query_context &&
|
|
3105
|
+
is_select_query(query_context->parse_tree, query_context->original_query))
|
|
3106
|
+
{
|
|
3107
|
+
pool_set_node_to_be_sent(query_context, PRIMARY_NODE_ID);
|
|
3108
|
+
if (pool_is_doing_extended_query_message())
|
|
3109
|
+
{
|
|
3110
|
+
ret = do_error_execute_command(backend, PRIMARY_NODE_ID, PROTO_MAJOR_V3);
|
|
3111
|
+
if (ret != POOL_CONTINUE)
|
|
3112
|
+
return ret;
|
|
3113
|
+
}
|
|
3114
|
+
else
|
|
3115
|
+
{
|
|
3116
|
+
ret = do_error_command(CONNECTION(backend, PRIMARY_NODE_ID), MAJOR(backend));
|
|
3117
|
+
if (ret != POOL_CONTINUE)
|
|
3118
|
+
return ret;
|
|
3119
|
+
}
|
|
3120
|
+
pool_debug("raise_intentional_error: intentional error occurred");
|
|
3121
|
+
}
|
|
3122
|
+
|
|
3123
|
+
if (REPLICATION &&
|
|
3124
|
+
TSTATE(backend, REAL_MASTER_NODE_ID) == 'T' &&
|
|
3125
|
+
!pool_config->replicate_select &&
|
|
3126
|
+
query_context &&
|
|
3127
|
+
is_select_query(query_context->parse_tree, query_context->original_query))
|
|
3128
|
+
{
|
|
3129
|
+
pool_setall_node_to_be_sent(query_context);
|
|
3130
|
+
for (i = 0; i < NUM_BACKENDS; i++)
|
|
3131
|
+
{
|
|
3132
|
+
if (VALID_BACKEND(i) && REAL_MASTER_NODE_ID != i)
|
|
3133
|
+
{
|
|
3134
|
+
/*
|
|
3135
|
+
* We must abort transaction to sync transaction state.
|
|
3136
|
+
* If the error was caused by an Execute message,
|
|
3137
|
+
* we must send invalid Execute message to abort
|
|
3138
|
+
* transaction.
|
|
3139
|
+
*
|
|
3140
|
+
* Because extended query protocol ignores all
|
|
3141
|
+
* messages before receiving Sync message inside error state.
|
|
3142
|
+
*/
|
|
3143
|
+
if (pool_is_doing_extended_query_message())
|
|
3144
|
+
{
|
|
3145
|
+
ret = do_error_execute_command(backend, i, PROTO_MAJOR_V3);
|
|
3146
|
+
if (ret != POOL_CONTINUE)
|
|
3147
|
+
return ret;
|
|
3148
|
+
}
|
|
3149
|
+
else
|
|
3150
|
+
{
|
|
3151
|
+
ret = do_error_command(CONNECTION(backend, i), MAJOR(backend));
|
|
3152
|
+
if (ret != POOL_CONTINUE)
|
|
3153
|
+
return ret;
|
|
3154
|
+
}
|
|
3155
|
+
}
|
|
3156
|
+
}
|
|
3157
|
+
}
|
|
3158
|
+
|
|
3159
|
+
return POOL_CONTINUE;
|
|
3160
|
+
}
|
|
3161
|
+
|
|
3162
|
+
void
|
|
3163
|
+
query_cache_register(char kind, POOL_CONNECTION *frontend, char *database, char *data, int data_len)
|
|
3164
|
+
{
|
|
3165
|
+
static int inside_T; /* flag to see the result data sequence */
|
|
3166
|
+
int result;
|
|
3167
|
+
|
|
3168
|
+
if (is_select_pgcatalog || is_select_for_update)
|
|
3169
|
+
return;
|
|
3170
|
+
|
|
3171
|
+
if (kind == 'T' && parsed_query)
|
|
3172
|
+
{
|
|
3173
|
+
result = pool_query_cache_register(kind, frontend, database, data, data_len, parsed_query);
|
|
3174
|
+
if (result < 0)
|
|
3175
|
+
{
|
|
3176
|
+
pool_error("pool_query_cache_register: query cache registration failed");
|
|
3177
|
+
inside_T = 0;
|
|
3178
|
+
}
|
|
3179
|
+
else
|
|
3180
|
+
{
|
|
3181
|
+
inside_T = 1;
|
|
3182
|
+
}
|
|
3183
|
+
}
|
|
3184
|
+
else if ((kind == 'D' || kind == 'C' || kind == 'E') && inside_T)
|
|
3185
|
+
{
|
|
3186
|
+
result = pool_query_cache_register(kind, frontend, database, data, data_len, NULL);
|
|
3187
|
+
if (kind == 'C' || kind == 'E' || result < 0)
|
|
3188
|
+
{
|
|
3189
|
+
if (result < 0)
|
|
3190
|
+
pool_error("pool_query_cache_register: query cache registration failed");
|
|
3191
|
+
else
|
|
3192
|
+
pool_debug("pool_query_cache_register: query cache saved");
|
|
3193
|
+
|
|
3194
|
+
inside_T = 0;
|
|
3195
|
+
free(parsed_query);
|
|
3196
|
+
parsed_query = NULL;
|
|
3197
|
+
}
|
|
3198
|
+
}
|
|
3199
|
+
}
|
|
3200
|
+
|
|
3201
|
+
/*
|
|
3202
|
+
* Check various errors from backend. return values: 0: no error 1:
|
|
3203
|
+
* deadlock detected 2: serialization error detected 3: query cancel
|
|
3204
|
+
* detected: 4
|
|
3205
|
+
*/
|
|
3206
|
+
static int check_errors(POOL_CONNECTION_POOL *backend, int backend_id)
|
|
3207
|
+
{
|
|
3208
|
+
|
|
3209
|
+
/*
|
|
3210
|
+
* Check dead lock error on the master node and abort
|
|
3211
|
+
* transactions on all nodes if so.
|
|
3212
|
+
*/
|
|
3213
|
+
if (detect_deadlock_error(CONNECTION(backend, backend_id), MAJOR(backend)) == SPECIFIED_ERROR)
|
|
3214
|
+
return 1;
|
|
3215
|
+
|
|
3216
|
+
/*
|
|
3217
|
+
* Check serialization failure error and abort
|
|
3218
|
+
* transactions on all nodes if so. Otherwise we allow
|
|
3219
|
+
* data inconsistency among DB nodes. See following
|
|
3220
|
+
* scenario: (M:master, S:slave)
|
|
3221
|
+
*
|
|
3222
|
+
* M:S1:BEGIN;
|
|
3223
|
+
* M:S2:BEGIN;
|
|
3224
|
+
* S:S1:BEGIN;
|
|
3225
|
+
* S:S2:BEGIN;
|
|
3226
|
+
* M:S1:SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
|
|
3227
|
+
* M:S2:SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
|
|
3228
|
+
* S:S1:SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
|
|
3229
|
+
* S:S2:SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
|
|
3230
|
+
* M:S1:UPDATE t1 SET i = i + 1;
|
|
3231
|
+
* S:S1:UPDATE t1 SET i = i + 1;
|
|
3232
|
+
* M:S2:UPDATE t1 SET i = i + 1; <-- blocked
|
|
3233
|
+
* S:S1:COMMIT;
|
|
3234
|
+
* M:S1:COMMIT;
|
|
3235
|
+
* M:S2:ERROR: could not serialize access due to concurrent update
|
|
3236
|
+
* S:S2:UPDATE t1 SET i = i + 1; <-- success in UPDATE and data becomes inconsistent!
|
|
3237
|
+
*/
|
|
3238
|
+
if (detect_serialization_error(CONNECTION(backend, backend_id), MAJOR(backend), true) == SPECIFIED_ERROR)
|
|
3239
|
+
return 2;
|
|
3240
|
+
|
|
3241
|
+
/*
|
|
3242
|
+
* check "SET TRANSACTION ISOLATION LEVEL must be called before any query" error.
|
|
3243
|
+
* This happens in following scenario:
|
|
3244
|
+
*
|
|
3245
|
+
* M:S1:BEGIN;
|
|
3246
|
+
* S:S1:BEGIN;
|
|
3247
|
+
* M:S1:SELECT 1; <-- only sent to MASTER
|
|
3248
|
+
* M:S1:SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
|
|
3249
|
+
* S:S1:SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
|
|
3250
|
+
* M: <-- error
|
|
3251
|
+
* S: <-- ok since no previous SELECT is sent. kind mismatch error occurs!
|
|
3252
|
+
*/
|
|
3253
|
+
if (detect_active_sql_transaction_error(CONNECTION(backend, backend_id), MAJOR(backend)) == SPECIFIED_ERROR)
|
|
3254
|
+
return 3;
|
|
3255
|
+
|
|
3256
|
+
/* check query cancel error */
|
|
3257
|
+
if (detect_query_cancel_error(CONNECTION(backend, backend_id), MAJOR(backend)) == SPECIFIED_ERROR)
|
|
3258
|
+
return 4;
|
|
3259
|
+
|
|
3260
|
+
return 0;
|
|
3261
|
+
}
|
|
3262
|
+
|
|
3263
|
+
static void generate_error_message(char *prefix, int specific_error, char *query)
|
|
3264
|
+
{
|
|
3265
|
+
POOL_SESSION_CONTEXT *session_context;
|
|
3266
|
+
POOL_MEMORY_POOL *old_context;
|
|
3267
|
+
|
|
3268
|
+
session_context = pool_get_session_context();
|
|
3269
|
+
if (!session_context)
|
|
3270
|
+
return;
|
|
3271
|
+
|
|
3272
|
+
static char *error_messages[] = {
|
|
3273
|
+
"received deadlock error message from master node. query: %s",
|
|
3274
|
+
"received serialization failure error message from master node. query: %s",
|
|
3275
|
+
"received SET TRANSACTION ISOLATION LEVEL must be called before any query error. query: %s",
|
|
3276
|
+
"received query cancel error message from master node. query: %s"
|
|
3277
|
+
};
|
|
3278
|
+
|
|
3279
|
+
String *msg;
|
|
3280
|
+
|
|
3281
|
+
if (specific_error < 1 || specific_error > sizeof(error_messages)/sizeof(char *))
|
|
3282
|
+
{
|
|
3283
|
+
pool_error("generate_error_message: invalid specific_error: %d", specific_error);
|
|
3284
|
+
return;
|
|
3285
|
+
}
|
|
3286
|
+
|
|
3287
|
+
specific_error--;
|
|
3288
|
+
|
|
3289
|
+
if (session_context->query_context)
|
|
3290
|
+
old_context = pool_memory_context_switch_to(session_context->query_context->memory_context);
|
|
3291
|
+
else
|
|
3292
|
+
old_context = pool_memory_context_switch_to(session_context->memory_context);
|
|
3293
|
+
|
|
3294
|
+
msg = init_string(prefix);
|
|
3295
|
+
string_append_char(msg, error_messages[specific_error]);
|
|
3296
|
+
pool_error(msg->data, query);
|
|
3297
|
+
free_string(msg);
|
|
3298
|
+
|
|
3299
|
+
pool_memory_context_switch_to(old_context);
|
|
3300
|
+
}
|
|
3301
|
+
|
|
3302
|
+
/*
|
|
3303
|
+
* Make per DB node statement log
|
|
3304
|
+
*/
|
|
3305
|
+
void per_node_statement_log(POOL_CONNECTION_POOL *backend, int node_id, char *query)
|
|
3306
|
+
{
|
|
3307
|
+
POOL_CONNECTION_POOL_SLOT *slot = backend->slots[node_id];
|
|
3308
|
+
|
|
3309
|
+
if (pool_config->log_per_node_statement)
|
|
3310
|
+
pool_log("DB node id: %d backend pid: %d statement: %s", node_id, ntohl(slot->pid), query);
|
|
3311
|
+
}
|
|
3312
|
+
|
|
3313
|
+
/*
|
|
3314
|
+
* Check kind and produce error message
|
|
3315
|
+
* All data read in this function is returned to stream.
|
|
3316
|
+
*/
|
|
3317
|
+
void per_node_error_log(POOL_CONNECTION_POOL *backend, int node_id, char *query, char *prefix, bool unread)
|
|
3318
|
+
{
|
|
3319
|
+
POOL_CONNECTION_POOL_SLOT *slot = backend->slots[node_id];
|
|
3320
|
+
char *message;
|
|
3321
|
+
|
|
3322
|
+
if (pool_extract_error_message(true, CONNECTION(backend, node_id), MAJOR(backend), true, &message) > 0)
|
|
3323
|
+
{
|
|
3324
|
+
pool_log("%s: DB node id: %d backend pid: %d statement: %s message: %s",
|
|
3325
|
+
prefix, node_id, ntohl(slot->pid), query, message);
|
|
3326
|
+
}
|
|
3327
|
+
}
|
|
3328
|
+
|
|
3329
|
+
static POOL_STATUS parse_before_bind(POOL_CONNECTION *frontend,
|
|
3330
|
+
POOL_CONNECTION_POOL *backend,
|
|
3331
|
+
POOL_SENT_MESSAGE *message)
|
|
3332
|
+
{
|
|
3333
|
+
int i;
|
|
3334
|
+
int len = message->len;
|
|
3335
|
+
char kind = '\0';
|
|
3336
|
+
char *contents = message->contents;
|
|
3337
|
+
bool parse_was_sent = false;
|
|
3338
|
+
bool backup[MAX_NUM_BACKENDS];
|
|
3339
|
+
POOL_STATUS status;
|
|
3340
|
+
POOL_QUERY_CONTEXT *qc = message->query_context;
|
|
3341
|
+
|
|
3342
|
+
memcpy(backup, qc->where_to_send, sizeof(qc->where_to_send));
|
|
3343
|
+
|
|
3344
|
+
/* expect to send to master node only */
|
|
3345
|
+
for (i = 0; i < NUM_BACKENDS; i++)
|
|
3346
|
+
{
|
|
3347
|
+
if (qc->where_to_send[i] && statecmp(qc->query_state[i], POOL_PARSE_COMPLETE) < 0)
|
|
3348
|
+
{
|
|
3349
|
+
pool_debug("parse_before_bind: waiting for backend %d completing parse", i);
|
|
3350
|
+
if (pool_extended_send_and_wait(qc, "P", len, contents, 1, i) != POOL_CONTINUE)
|
|
3351
|
+
return POOL_END;
|
|
3352
|
+
}
|
|
3353
|
+
else
|
|
3354
|
+
{
|
|
3355
|
+
qc->where_to_send[i] = 0;
|
|
3356
|
+
}
|
|
3357
|
+
}
|
|
3358
|
+
|
|
3359
|
+
for (i = 0; i < NUM_BACKENDS; i++)
|
|
3360
|
+
{
|
|
3361
|
+
if (qc->where_to_send[i])
|
|
3362
|
+
{
|
|
3363
|
+
parse_was_sent = true;
|
|
3364
|
+
break;
|
|
3365
|
+
}
|
|
3366
|
+
}
|
|
3367
|
+
|
|
3368
|
+
if (parse_was_sent)
|
|
3369
|
+
{
|
|
3370
|
+
while (kind != '1')
|
|
3371
|
+
{
|
|
3372
|
+
status = read_kind_from_backend(frontend, backend, &kind);
|
|
3373
|
+
if (status != POOL_CONTINUE)
|
|
3374
|
+
{
|
|
3375
|
+
memcpy(qc->where_to_send, backup, sizeof(backup));
|
|
3376
|
+
return status;
|
|
3377
|
+
}
|
|
3378
|
+
status = pool_discard_packet_contents(backend);
|
|
3379
|
+
if (status != POOL_CONTINUE)
|
|
3380
|
+
{
|
|
3381
|
+
memcpy(qc->where_to_send, backup, sizeof(backup));
|
|
3382
|
+
return status;
|
|
3383
|
+
}
|
|
3384
|
+
}
|
|
3385
|
+
}
|
|
3386
|
+
|
|
3387
|
+
memcpy(qc->where_to_send, backup, sizeof(backup));
|
|
3388
|
+
return POOL_CONTINUE;
|
|
3389
|
+
}
|
|
3390
|
+
|
|
3391
|
+
/*
|
|
3392
|
+
* Find victim nodes by "decide by majority" rule and returns array
|
|
3393
|
+
* of victim node ids. If no victim is found, return NULL.
|
|
3394
|
+
*
|
|
3395
|
+
* Arguments:
|
|
3396
|
+
* ntuples: Array of number of affected tuples. -1 represents down node.
|
|
3397
|
+
* nmembers: Number of elements in ntuples.
|
|
3398
|
+
* master_node: The master node id. Less than 0 means ignore this parameter.
|
|
3399
|
+
* number_of_nodes: Number of elements in victim nodes array.
|
|
3400
|
+
*
|
|
3401
|
+
* Note: If no one wins and master_node >= 0, winner would be the
|
|
3402
|
+
* master and other nodes who has same number of tuples as the master.
|
|
3403
|
+
*
|
|
3404
|
+
* Caution: Returned victim node array is allocated in static memory
|
|
3405
|
+
* of this function. Subsequent calls to this function will overwrite
|
|
3406
|
+
* the memory.
|
|
3407
|
+
*/
|
|
3408
|
+
static int* find_victim_nodes(int *ntuples, int nmembers, int master_node, int *number_of_nodes)
|
|
3409
|
+
{
|
|
3410
|
+
static int victim_nodes[MAX_NUM_BACKENDS];
|
|
3411
|
+
static int votes[MAX_NUM_BACKENDS];
|
|
3412
|
+
int maxvotes;
|
|
3413
|
+
int majority_ntuples;
|
|
3414
|
+
int me;
|
|
3415
|
+
int cnt;
|
|
3416
|
+
int healthy_nodes;
|
|
3417
|
+
int i, j;
|
|
3418
|
+
|
|
3419
|
+
healthy_nodes = 0;
|
|
3420
|
+
*number_of_nodes = 0;
|
|
3421
|
+
maxvotes = 0;
|
|
3422
|
+
majority_ntuples = 0;
|
|
3423
|
+
|
|
3424
|
+
for (i=0;i<nmembers;i++)
|
|
3425
|
+
{
|
|
3426
|
+
me = ntuples[i];
|
|
3427
|
+
|
|
3428
|
+
/* Health node? */
|
|
3429
|
+
if (me < 0)
|
|
3430
|
+
{
|
|
3431
|
+
votes[i] = -1;
|
|
3432
|
+
continue;
|
|
3433
|
+
}
|
|
3434
|
+
|
|
3435
|
+
healthy_nodes++;
|
|
3436
|
+
votes[i] = 1;
|
|
3437
|
+
|
|
3438
|
+
for (j=0;j<nmembers;j++)
|
|
3439
|
+
{
|
|
3440
|
+
if (i != j && me == ntuples[j])
|
|
3441
|
+
{
|
|
3442
|
+
votes[i]++;
|
|
3443
|
+
|
|
3444
|
+
if (votes[i] > maxvotes)
|
|
3445
|
+
{
|
|
3446
|
+
maxvotes = votes[i];
|
|
3447
|
+
majority_ntuples = me;
|
|
3448
|
+
}
|
|
3449
|
+
}
|
|
3450
|
+
}
|
|
3451
|
+
}
|
|
3452
|
+
|
|
3453
|
+
/* Everyone is different */
|
|
3454
|
+
if (maxvotes == 1)
|
|
3455
|
+
{
|
|
3456
|
+
/* Master node is specified? */
|
|
3457
|
+
if (master_node < 0)
|
|
3458
|
+
return NULL;
|
|
3459
|
+
|
|
3460
|
+
/*
|
|
3461
|
+
* If master node is specified, let it and others who has same
|
|
3462
|
+
* ntuples win.
|
|
3463
|
+
*/
|
|
3464
|
+
majority_ntuples = ntuples[master_node];
|
|
3465
|
+
}
|
|
3466
|
+
else
|
|
3467
|
+
{
|
|
3468
|
+
/* Find number of majority */
|
|
3469
|
+
cnt = 0;
|
|
3470
|
+
for (i=0;i<nmembers;i++)
|
|
3471
|
+
{
|
|
3472
|
+
if (votes[i] == maxvotes)
|
|
3473
|
+
{
|
|
3474
|
+
cnt++;
|
|
3475
|
+
}
|
|
3476
|
+
}
|
|
3477
|
+
|
|
3478
|
+
if (cnt <= healthy_nodes / 2.0)
|
|
3479
|
+
{
|
|
3480
|
+
/* No one wins */
|
|
3481
|
+
|
|
3482
|
+
/* Master node is specified? */
|
|
3483
|
+
if (master_node < 0)
|
|
3484
|
+
return NULL;
|
|
3485
|
+
|
|
3486
|
+
/*
|
|
3487
|
+
* If master node is specified, let it and others who has same
|
|
3488
|
+
* ntuples win.
|
|
3489
|
+
*/
|
|
3490
|
+
majority_ntuples = ntuples[master_node];
|
|
3491
|
+
}
|
|
3492
|
+
}
|
|
3493
|
+
|
|
3494
|
+
/* Make victim nodes list */
|
|
3495
|
+
for (i=0;i<nmembers;i++)
|
|
3496
|
+
{
|
|
3497
|
+
if (ntuples[i] >= 0 && ntuples[i] != majority_ntuples)
|
|
3498
|
+
{
|
|
3499
|
+
victim_nodes[(*number_of_nodes)++] = i;
|
|
3500
|
+
}
|
|
3501
|
+
}
|
|
3502
|
+
|
|
3503
|
+
return victim_nodes;
|
|
3504
|
+
}
|
|
3505
|
+
|
|
3506
|
+
/*
|
|
3507
|
+
* Extract the number of tuples from CommandComplete message
|
|
3508
|
+
*/
|
|
3509
|
+
static int extract_ntuples(char *message)
|
|
3510
|
+
{
|
|
3511
|
+
char *rows;
|
|
3512
|
+
|
|
3513
|
+
if ((rows = strstr(message, "UPDATE")) || (rows = strstr(message, "DELETE")))
|
|
3514
|
+
rows +=7;
|
|
3515
|
+
else if ((rows = strstr(message, "INSERT")))
|
|
3516
|
+
{
|
|
3517
|
+
rows += 7;
|
|
3518
|
+
while (*rows && *rows != ' ') rows++;
|
|
3519
|
+
}
|
|
3520
|
+
else
|
|
3521
|
+
return 0;
|
|
3522
|
+
|
|
3523
|
+
return atoi(rows);
|
|
3524
|
+
}
|