├── .gitignore ├── COPYRIGHT ├── INSTALL.dblink_plus ├── Makefile ├── README.md ├── REGRESS.dblink_plus ├── SPECS ├── dblink_plus_pg15.spec ├── dblink_plus_pg15_ora.spec ├── dblink_plus_pg16.spec ├── dblink_plus_pg16_ora.spec ├── dblink_plus_pg17.spec └── dblink_plus_pg17_ora.spec ├── dblink.c ├── dblink.h ├── dblink_internal.h ├── dblink_mysql.c ├── dblink_oracle.c ├── dblink_plus.control ├── dblink_plus.sql.in ├── dblink_postgres.c ├── dblink_sqlite3.c ├── docs ├── dblink_plus-img.ppt ├── dblink_plus-ja.png ├── index.html └── style.css ├── expected ├── init.out ├── mysql.out ├── oracle.out ├── postgres.out ├── sqlite3.out └── standby_postgres.out ├── regress.conf ├── regress_init.sh ├── sql ├── init.sql ├── mysql.sql ├── oracle.sql ├── postgres.sql ├── sqlite3.sql └── standby_postgres.sql ├── standby_schedule └── uninstall_dblink_plus.sql /.gitignore: -------------------------------------------------------------------------------- 1 | dblink*.o 2 | dblink_plus.sql 3 | dblink_plus*.so 4 | tags 5 | regression.out 6 | regression.diffs 7 | results 8 | results/* 9 | *.bc 10 | -------------------------------------------------------------------------------- /COPYRIGHT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011-2025, NIPPON TELEGRAPH AND TELEPHONE CORPORATION 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * Neither the name of the NIPPON TELEGRAPH AND TELEPHONE CORPORATION 13 | (NTT) nor the names of its contributors may be used to endorse or 14 | promote products derived from this software without specific prior 15 | written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | -------------------------------------------------------------------------------- /INSTALL.dblink_plus: -------------------------------------------------------------------------------- 1 | Pre-requisite 2 | ============ 3 | PostgreSQL installation is mandatory for dblink_plus module. 4 | This package requires PostgreSQL 8.4 or later. 5 | 6 | Other administration tools that use this module may have different 7 | requirements. Please consult the tool's documentation for further details 8 | at: [http://interdbconnect.sourceforge.net/dblink_plus/dblink_plus-ja.html] 9 | 10 | 11 | Installation 12 | =========== 13 | This module is normally distributed as a PostgreSQL 'contrib' module. 14 | In order to install it from a pre-configured source tree or without 15 | source tree using PGXS, run the following commands as a user with 16 | appropriate privileges from the dblink_plus source directory: 17 | 18 | $ make [ORACLE=0] [MYSQL=0] [SQLITE3=0] 19 | # make [ORACLE=0] [MYSQL=0] [SQLITE3=0] install 20 | options: 21 | - ORACLE : skip installation for remote Oracle server if it is set to 0, 22 | - MYSQL : skip installation for remote MySQL server if it is set to 0, 23 | - SQLITE3 : skip installation for remote SQLite server if it is set to 0. 24 | 25 | If user do not want to use dblink_plus for Oracle, MySQL or sqlite3, 26 | setting above flags to zero avoids dblibk_plus installation for specified 27 | database connectivity. 28 | 29 | 30 | Registration to databases 31 | ======================== 32 | To install dblink_plus functions in the database, run the dblink_plus.sql like, 33 | 34 | $ psql -d postgres -U postgres -f $PGHOME/share/extension/dblink_plus.sql 35 | 36 | 37 | [NOTE] 38 | If $PGHOME does not contains "pgsql" or "postgresql"as directory names, 39 | PostgreSQL explicitly adds "postgresql" as directory just before $PGHOME/share. 40 | So above commands becomes: 41 | $ psql -d postgres -U postgres -f $PGHOME/share/postgresql/extension/dblink_plus.sql 42 | 43 | It also can be done by CREATE EXTENSION for PostgreSQL 9.1 and after. 44 | $ psql -d postgres -U postgres 45 | postgres=> CREATE EXTENSION dblink_plus; 46 | 47 | Configuration for remote server 48 | =================== 49 | In order to connect to remote server by dblink_plus, users have to create several 50 | objects in local PostgreSQL server. 51 | 52 | - foreign data wrapper which uses dblink_plus connectors as validator 53 | - foreign server which uses the above foreign data wrapper 54 | - mapping between local database user and remote database user 55 | 56 | Here shows examples of configuration for remote PostgreSQL and remote Oracle. 57 | Users have to modify parameters as per their environment. 58 | 59 | **** Configure dblink_plus for remote PostgreSQL server **** 60 | 61 | 1. Set up remote PostgreSQL server and add contents to following files as below: 62 | 63 | $ vim $PGDATA/postgresql.conf 64 | --- 65 | # zero disables the feature (change requires restart) 66 | max_prepared_transactions = 1 67 | # dblink_plus GUC parameter 68 | dblink_plus.use_xa = true 69 | --- 70 | 71 | $ vim $PGDATA/pg_hba.conf 72 | --- 73 | # TYPE DATABASE USER ADDRESS METHOD 74 | # IPv4 local connections: 75 | host all all 192.168.56.101/32 trust 76 | #ADDRESS is the IP address of your local PostgreSQL server on which dblink_plus is running. 77 | --- 78 | 79 | 2. Create user and table in the remote database: 80 | 81 | $ psql -d postgres 82 | postgres=# create user test with password 'test123'; 83 | postgres=# alter user test with login; 84 | 85 | $ psql -d postgres -U test 86 | postgres=> create table t1(id integer); 87 | postgres=> insert into t1 values(1); 88 | postgres=> insert into t1 values(2); 89 | 90 | 3. Install dblink_plus into your local PostgreSQL and create objects for remote PostgreSQL: 91 | 92 | $ psql -d postgres -U postgres 93 | postgres=# CREATE FOREIGN DATA WRAPPER postgres VALIDATOR dblink.postgres; 94 | postgres=# CREATE SERVER pg_server FOREIGN DATA WRAPPER postgres OPTIONS (host '192.168.56.102', port '5432', dbname 'postgres'); 95 | postgres=# CREATE USER MAPPING FOR postgres SERVER pg_server OPTIONS (user 'test' password 'test123'); 96 | 97 | 4. Now you can access from local PostgreSQL to remote PostgreSQL by dblink functions. 98 | 99 | postgres=# SELECT * FROM dblink.query ('pg_server', 'select id from t1') as t1(c1 int); 100 | 101 | **************************************** 102 | 103 | **** Configure dblink_plus for remote Oracle server **** 104 | 105 | 1. Install Oracle Client and configure at local server 106 | 107 | Download and install Instant Oracle client source or rpm from: 108 | [http://www.oracle.com/technetwork/topics/linuxx86-64soft-092277.html] 109 | Follow oracle documentation for Oracle client installation. 110 | 111 | Set environmental variables for OS user who start PostgreSQL like: 112 | 113 | export ORACLE_HOME=/usr/lib/oracle/11.2/client64 114 | export LD_LIBRARY_PATH=/usr/lib/oracle/11.2/client64/lib:$LD_LIBRARY_PATH 115 | export TNS_ADMIN=$ORACLE_HOME/network/admin/ 116 | 117 | Please modify the path for your environment. 118 | 119 | In addition, users may update shared libralies setting like below: 120 | 121 | $ su - 122 | # vim /etc/ld.so.conf 123 | --- 124 | /usr/lib/oracle/11.2/client64/lib 125 | --- 126 | 127 | # ldconfig 128 | # exit 129 | 130 | 131 | 2. Create a user and a sample table on remote Oracle side 132 | 133 | 134 | # sqlplus sys as sysdba 135 | (Enter password which you have set for sys user at the time Oracle installation.) 136 | 137 | SQL> CREATE USER test IDENTIFIED by test123; 138 | SQL> ALTER USER test default tablespace SYSTEM; 139 | SQL> GRANT CONNECT, CREATE session, CREATE table, CREATE view, CREATE procedure,CREATE synonym to test; 140 | SQL> ALTER USER test quota 100M on SYSTEM; 141 | SQL> quit 142 | 143 | # sqlplus test/test123 144 | SQL> create table t1(id integer); 145 | SQL> insert into t1 values(1); 146 | SQL> / 147 | 148 | 3. Create tnsnames.ora file on local server 149 | 150 | # su root 151 | # mkdir -p /usr/lib/oracle/11.2/client64/network/admin/ 152 | # vim /usr/lib/oracle/11.2/client64/network/admin/tnsnames.ora 153 | --- 154 | XE = 155 | (DESCRIPTION = 156 | (ADDRESS = (PROTOCOL = TCP)(HOST = 172.26.126.62)(PORT = 1521)) 157 | (CONNECT_DATA = 158 | (SERVER = DEDICATED) 159 | (SERVICE_NAME = XE) 160 | ) 161 | ) 162 | --- 163 | 164 | In this file (HOST = 172.26.126.62)(PORT = 1521) HOST is IP address of 165 | remote Oracle server and PORT is port number on which Oracle server is running. 166 | "XE" is the Oracle service name. 167 | Check tnsnames.ora file on the remote Oracle server to know service name. 168 | 169 | 4. Install dblink_plus into your local database 170 | 171 | $ psql -d postgres -U postgres 172 | postgres=# CREATE FOREIGN DATA WRAPPER oracle VALIDATOR dblink.oracle; 173 | postgres=# CREATE SERVER ora_server FOREIGN DATA WRAPPER oracle OPTIONS (dbname 'ORCL'); 174 | postgres=# CREATE USER MAPPING FOR postgres SERVER ora_server OPTIONS (user 'test', password 'test123'); 175 | 176 | 5. Now you can access from local PostgreSQL to remote Oracle by dblink functions. 177 | 178 | postgres=# SELECT * FROM dblink.query ('ora_server', 'select id from t1') as t1(c1 int); 179 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # dblink_plus: Makefile 3 | # 4 | # Copyright (c) 2011-2025, NIPPON TELEGRAPH AND TELEPHONE CORPORATION 5 | # 6 | MODULE_big = dblink_plus 7 | DATA_built = dblink_plus.sql dblink_plus--1.0.10.sql 8 | DATA = uninstall_dblink_plus.sql 9 | OBJS = dblink.o dblink_postgres.o 10 | 11 | EXTENSION = dblink_plus 12 | 13 | REGRESS = init postgres 14 | PG_CPPFLAGS = -I$(libpq_srcdir) 15 | SHLIB_LINK = $(libpq) 16 | 17 | EXTRA_CLEAN = gmon.out 18 | 19 | ifneq ($(MYSQL),0) 20 | OBJS += dblink_mysql.o 21 | REGRESS += mysql 22 | PG_CPPFLAGS += -DENABLE_MYSQL 23 | SHLIB_LINK += -lmysqlclient 24 | endif 25 | 26 | ifneq ($(ORACLE),0) 27 | OBJS += dblink_oracle.o 28 | REGRESS += oracle 29 | PG_CPPFLAGS += -DENABLE_ORACLE 30 | PG_CPPFLAGS += -I$(ORACLE_HOME)/oci/include/ # win32 31 | PG_CPPFLAGS += -I$(ORACLE_HOME)/rdbms/public # linux 32 | PG_CPPFLAGS += -I$(ORACLE_INCLUDE)/ # linux with oracle client only 33 | endif 34 | 35 | ifneq ($(SQLITE3),0) 36 | OBJS += dblink_sqlite3.o 37 | REGRESS += sqlite3 38 | PG_CPPFLAGS += -DENABLE_SQLITE3 39 | SHLIB_LINK += -lsqlite3 40 | endif 41 | 42 | PG_CONFIG = pg_config 43 | PGXS := $(shell $(PG_CONFIG) --pgxs) 44 | include $(PGXS) 45 | 46 | ifneq ($(ORACLE),0) 47 | ifeq ($(PORTNAME),win32) 48 | SHLIB_LINK += -L$(ORACLE_HOME)/lib -loci 49 | else 50 | SHLIB_LINK += -L$(ORACLE_HOME)/lib -lclntsh 51 | endif 52 | endif 53 | 54 | dblink_plus--1.0.10.sql: 55 | head -n -2 dblink_plus.sql.in | tail -n +4 > dblink_plus--1.0.10.sql 56 | 57 | standbycheck: all 58 | $(pg_regress_installcheck) --schedule=standby_schedule --use-existing --dbname=contrib_regression --port=$(PGPORT) 59 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | dblink_plus 2 | ======= 3 | This tools enables to connect from PostgreSQL server to other databases. 4 | Currently it supports to connect to PostgreSQL, Oracle Database, MySQL, Sqlite3. 5 | 6 | Quick Introduction 7 | --- 8 | With dblink_plus, users can throw SQL to external databases like below: 9 | 10 | =# BEGIN; 11 | =# SELECT dblink.connect('ora_conn', 'server_oracle', false); 12 | =# SELECT dblink.query('ora_conn', 'SELECT c1, c2 FROM tbl') AS t(c1 int, c2 text); -- get rows 13 | =# SELECT dblink.exec('ora_conn', 'UPDATE tbl SET c3 = 999 WHERE c1=1'); -- modify rows 14 | =# COMMIT; 15 | 16 | Please take a look to documentation http://ossc-db.github.io/dblink_plus/index.html. 17 | 18 | -------------------------------------------------------------------------------- /REGRESS.dblink_plus: -------------------------------------------------------------------------------- 1 | Setting for regression tests 2 | =========================== 3 | 4 | To run regression tests, users have to set up PostgreSQL server and remote server. 5 | If users want to do stanbycheck, it is also required to configure replication 6 | set up with master and standby PostgreSQL servers. Follow the documentation 7 | to set up streaming replication. 8 | https://wiki.postgresql.org/wiki/Streaming_Replication 9 | 10 | Before running regression tests, update environment settings in regress.conf 11 | and then execute regression tests. 12 | 13 | $ vim regress.conf 14 | --- 15 | #PostgreSQL server configuration settings for regression test-- 16 | PG_USER='postgres' (PostgreSQL user name for regression) 17 | 18 | #Oracle server configuration settings for regression test-- 19 | OR_DBNM='dbt' (Oracle service/database name) 20 | OR_USER='scott' (Oracle user name) 21 | OR_PASS='tiger' (Oracle user password) 22 | --- 23 | 24 | Above file is used when 'make installcheck' command is executed. 25 | This commands internally execute regress_init.sh which modifies 26 | regression scripts from regress.conf. 27 | 28 | 29 | Regression tests against remote servers 30 | ====================================== 31 | 32 | To do regression tests of dblink_plus, just run make installcheck like below. 33 | 34 | $ make [ORACLE=0] [MYSQL=0] [SQLITE3=0] installcheck 35 | options: 36 | - ORACLE : skip the regression tests for remote Oracle server if it is set to 0, 37 | - MYSQL : skip the regression tests for remote MySQL server if it is set to 0, 38 | - SQLITE3 : skip the regression tests for remote SQLite server if it is set to 0. 39 | 40 | Above installcheck command executes regression scripts on local PostgreSQL server 41 | and throws dblink_plus queries on remote PostgreSQL, Oracle, MySQL, sqlite3 servers if specified. 42 | 43 | Regression tests against local standby PostgreSQL server 44 | ======================================================= 45 | 46 | There are additional tests with local standby PostgreSQL server to remote servers. 47 | 48 | $ make PGPORT=standby_server_port_number standbycheck 49 | 50 | It helps to run read only queries on remote server through dblink_plus on standby server. 51 | User need to provide standby server port number to connect with local standby server. 52 | 53 | This regression test is introduced to test feature "use_xa parameter as GUC parameter" 54 | so that users can configure this parameter from postgresql.conf or using set command in 55 | local PostgreSQL. 56 | -------------------------------------------------------------------------------- /SPECS/dblink_plus_pg15.spec: -------------------------------------------------------------------------------- 1 | # SPEC file for dblink_plus 2 | # Copyright(C) 2025 NIPPON TELEGRAPH AND TELEPHONE CORPORATION 3 | 4 | %define _pgdir /usr/pgsql-15 5 | %define _bindir %{_pgdir}/bin 6 | %define _libdir %{_pgdir}/lib 7 | %define _datadir %{_pgdir}/share/extension 8 | %define _bcdir %{_libdir}/bitcode/dblink_plus 9 | %define _bc_ind_dir %{_libdir}/bitcode 10 | 11 | ## Set general information 12 | Summary: PostgreSQL module to connect PostgreSQL/Oracle 13 | Name: dblink_plus 14 | Version: 1.0.10 15 | Release: 1%{?dist} 16 | License: BSD 17 | Group: Applications/Databases 18 | Source0: %{name}-%{version}.tar.gz 19 | URL: https://github.com/ossc-db/dblink_plus 20 | BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-%(%{__id_u} -n) 21 | Vendor: NIPPON TELEGRAPH AND TELEPHONE CORPORATION 22 | #Following is needed to remove auto-discovery of oci library files(not under RPM package management) 23 | AutoReqProv: no 24 | 25 | ## We use postgresql-devel package 26 | BuildRequires: postgresql15-devel 27 | Requires: postgresql15-libs 28 | 29 | ## Description 30 | %description 31 | dblink_plus is a PostgreSQL module which supports connections to other databases. 32 | It is similar to contrib/dblink except that it can connect to Oracle, MySQL and sqlite3. 33 | 34 | Note that this package is available for only PostgreSQL 15. 35 | 36 | %package llvmjit 37 | Requires: postgresql15-server, postgresql15-llvmjit 38 | Requires: dblink_plus = 1.0.10 39 | Summary: Just-in-time compilation support for dblink_plus 40 | 41 | %description llvmjit 42 | Just-in-time compilation support for dblink_plus 1.0.10 43 | 44 | ## prework 45 | %prep 46 | %setup -q -n %{name}-%{version} 47 | 48 | ## Set variables for build environment 49 | %build 50 | USE_PGXS=1 make %{?_smp_mflags} MYSQL=0 SQLITE3=0 ORACLE=0 51 | 52 | ## Set variables for install 53 | %install 54 | rm -rf %{buildroot} 55 | USE_PGXS=1 make install MYSQL=0 SQLITE3=0 ORACLE=0 DESTDIR=%{buildroot} 56 | install -m 644 COPYRIGHT %{buildroot}%{_datadir}/COPYRIGHT_dblink_plus 57 | 58 | %clean 59 | rm -rf %{buildroot} 60 | 61 | %files 62 | %defattr(0755,root,root) 63 | %{_libdir}/dblink_plus.so 64 | %defattr(0644,root,root) 65 | %{_datadir}/dblink_plus.sql 66 | %{_datadir}/dblink_plus--1.0.10.sql 67 | %{_datadir}/dblink_plus.control 68 | %{_datadir}/uninstall_dblink_plus.sql 69 | %{_datadir}/COPYRIGHT_dblink_plus 70 | 71 | %files llvmjit 72 | %defattr(0644,root,root,0755) 73 | %{_bcdir} 74 | %defattr(0644,root,root) 75 | %{_bc_ind_dir}/dblink_plus.index.bc 76 | 77 | # History. 78 | %changelog 79 | * Fri Jan 17 2025 - NTT OSS Center 1.0.10-1 80 | Support PG17. 81 | * Thu Jan 18 2024 - NTT OSS Center 1.0.9-1 82 | Support PG16. 83 | * Thu Jan 12 2023 - NTT OSS Center 1.0.8-1 84 | Support PG15. 85 | * Wed Jan 12 2022 - NTT OSS Center 1.0.7-1 86 | Support PG14. 87 | * Thu Jan 07 2021 - NTT OSS Center 1.0.6-1 88 | Support PG13 and fix how to install bitcode. 89 | * Mon Nov 25 2019 - NTT OSS Center 1.0.5-1 90 | Support PG12. 91 | * Tue Jan 22 2019 - NTT OSS Center 1.0.4-1 92 | Support PG11. 93 | -------------------------------------------------------------------------------- /SPECS/dblink_plus_pg15_ora.spec: -------------------------------------------------------------------------------- 1 | # SPEC file for dblink_plus 2 | # Copyright(C) 2025 NIPPON TELEGRAPH AND TELEPHONE CORPORATION 3 | 4 | %define _pgdir /usr/pgsql-15 5 | %define _bindir %{_pgdir}/bin 6 | %define _libdir %{_pgdir}/lib 7 | %define _datadir %{_pgdir}/share/extension 8 | %define _bcdir %{_libdir}/bitcode/dblink_plus 9 | %define _bc_ind_dir %{_libdir}/bitcode 10 | 11 | ## Set general information 12 | Summary: PostgreSQL module to connect PostgreSQL/Oracle 13 | Name: dblink_plus 14 | Version: 1.0.10 15 | Release: 1%{?dist} 16 | License: BSD 17 | Group: Applications/Databases 18 | Source0: %{name}-%{version}.tar.gz 19 | URL: https://github.com/ossc-db/dblink_plus 20 | BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-%(%{__id_u} -n) 21 | Vendor: NIPPON TELEGRAPH AND TELEPHONE CORPORATION 22 | #Following is needed to remove auto-discovery of oci library files(not under RPM package management) 23 | AutoReqProv: no 24 | 25 | ## We use postgresql-devel package 26 | BuildRequires: postgresql15-devel 27 | Requires: postgresql15-libs 28 | 29 | ## Description 30 | %description 31 | dblink_plus is a PostgreSQL module which supports connections to other databases. 32 | It is similar to contrib/dblink except that it can connect to Oracle, MySQL and sqlite3. 33 | 34 | Note that this package is available for only PostgreSQL 15. 35 | 36 | %package llvmjit 37 | Requires: postgresql15-server, postgresql15-llvmjit 38 | Requires: dblink_plus = 1.0.10 39 | Summary: Just-in-time compilation support for dblink_plus 40 | 41 | %description llvmjit 42 | Just-in-time compilation support for dblink_plus 1.0.10 43 | 44 | ## prework 45 | %prep 46 | %setup -q -n %{name}-%{version} 47 | 48 | ## Set variables for build environment 49 | %build 50 | USE_PGXS=1 make %{?_smp_mflags} MYSQL=0 SQLITE3=0 ORACLE=1 51 | 52 | ## Set variables for install 53 | %install 54 | rm -rf %{buildroot} 55 | USE_PGXS=1 make install MYSQL=0 SQLITE3=0 ORACLE=1 DESTDIR=%{buildroot} 56 | install -m 644 COPYRIGHT %{buildroot}%{_datadir}/COPYRIGHT_dblink_plus 57 | 58 | %clean 59 | rm -rf %{buildroot} 60 | 61 | %files 62 | %defattr(0755,root,root) 63 | %{_libdir}/dblink_plus.so 64 | %defattr(0644,root,root) 65 | %{_datadir}/dblink_plus.sql 66 | %{_datadir}/dblink_plus--1.0.10.sql 67 | %{_datadir}/dblink_plus.control 68 | %{_datadir}/uninstall_dblink_plus.sql 69 | %{_datadir}/COPYRIGHT_dblink_plus 70 | 71 | %files llvmjit 72 | %defattr(0644,root,root,0755) 73 | %{_bcdir} 74 | %defattr(0644,root,root) 75 | %{_bc_ind_dir}/dblink_plus.index.bc 76 | 77 | # History. 78 | %changelog 79 | * Fri Jan 17 2025 - NTT OSS Center 1.0.10-1 80 | Support PG17. 81 | * Thu Jan 18 2024 - NTT OSS Center 1.0.9-1 82 | Support PG16. 83 | * Thu Jan 12 2023 - NTT OSS Center 1.0.8-1 84 | Support PG15. 85 | * Wed Jan 12 2022 - NTT OSS Center 1.0.7-1 86 | Support PG14. 87 | * Thu Jan 07 2021 - NTT OSS Center 1.0.6-1 88 | Support PG13 and fix how to install bitcode. 89 | * Mon Nov 25 2019 - NTT OSS Center 1.0.5-1 90 | Support PG12. 91 | * Tue Jan 22 2019 - NTT OSS Center 1.0.4-1 92 | Support PG11. 93 | -------------------------------------------------------------------------------- /SPECS/dblink_plus_pg16.spec: -------------------------------------------------------------------------------- 1 | # SPEC file for dblink_plus 2 | # Copyright(C) 2025 NIPPON TELEGRAPH AND TELEPHONE CORPORATION 3 | 4 | %define _pgdir /usr/pgsql-16 5 | %define _bindir %{_pgdir}/bin 6 | %define _libdir %{_pgdir}/lib 7 | %define _datadir %{_pgdir}/share/extension 8 | %define _bcdir %{_libdir}/bitcode/dblink_plus 9 | %define _bc_ind_dir %{_libdir}/bitcode 10 | 11 | ## Set general information 12 | Summary: PostgreSQL module to connect PostgreSQL/Oracle 13 | Name: dblink_plus 14 | Version: 1.0.10 15 | Release: 1%{?dist} 16 | License: BSD 17 | Group: Applications/Databases 18 | Source0: %{name}-%{version}.tar.gz 19 | URL: https://github.com/ossc-db/dblink_plus 20 | BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-%(%{__id_u} -n) 21 | Vendor: NIPPON TELEGRAPH AND TELEPHONE CORPORATION 22 | #Following is needed to remove auto-discovery of oci library files(not under RPM package management) 23 | AutoReqProv: no 24 | 25 | ## We use postgresql-devel package 26 | BuildRequires: postgresql16-devel 27 | Requires: postgresql16-libs 28 | 29 | ## Description 30 | %description 31 | dblink_plus is a PostgreSQL module which supports connections to other databases. 32 | It is similar to contrib/dblink except that it can connect to Oracle, MySQL and sqlite3. 33 | 34 | Note that this package is available for only PostgreSQL 16. 35 | 36 | %package llvmjit 37 | Requires: postgresql16-server, postgresql16-llvmjit 38 | Requires: dblink_plus = 1.0.10 39 | Summary: Just-in-time compilation support for dblink_plus 40 | 41 | %description llvmjit 42 | Just-in-time compilation support for dblink_plus 1.0.10 43 | 44 | ## prework 45 | %prep 46 | %setup -q -n %{name}-%{version} 47 | 48 | ## Set variables for build environment 49 | %build 50 | USE_PGXS=1 make %{?_smp_mflags} MYSQL=0 SQLITE3=0 ORACLE=0 51 | 52 | ## Set variables for install 53 | %install 54 | rm -rf %{buildroot} 55 | USE_PGXS=1 make install MYSQL=0 SQLITE3=0 ORACLE=0 DESTDIR=%{buildroot} 56 | install -m 644 COPYRIGHT %{buildroot}%{_datadir}/COPYRIGHT_dblink_plus 57 | 58 | %clean 59 | rm -rf %{buildroot} 60 | 61 | %files 62 | %defattr(0755,root,root) 63 | %{_libdir}/dblink_plus.so 64 | %defattr(0644,root,root) 65 | %{_datadir}/dblink_plus.sql 66 | %{_datadir}/dblink_plus--1.0.10.sql 67 | %{_datadir}/dblink_plus.control 68 | %{_datadir}/uninstall_dblink_plus.sql 69 | %{_datadir}/COPYRIGHT_dblink_plus 70 | 71 | %files llvmjit 72 | %defattr(0644,root,root,0755) 73 | %{_bcdir} 74 | %defattr(0644,root,root) 75 | %{_bc_ind_dir}/dblink_plus.index.bc 76 | 77 | # History. 78 | %changelog 79 | * Fri Jan 17 2025 - NTT OSS Center 1.0.10-1 80 | Support PG17. 81 | * Thu Jan 18 2024 - NTT OSS Center 1.0.9-1 82 | Support PG16. 83 | * Thu Jan 12 2023 - NTT OSS Center 1.0.8-1 84 | Support PG15. 85 | * Wed Jan 12 2022 - NTT OSS Center 1.0.7-1 86 | Support PG14. 87 | * Thu Jan 07 2021 - NTT OSS Center 1.0.6-1 88 | Support PG13 and fix how to install bitcode. 89 | * Mon Nov 25 2019 - NTT OSS Center 1.0.5-1 90 | Support PG12. 91 | * Tue Jan 22 2019 - NTT OSS Center 1.0.4-1 92 | Support PG11. 93 | -------------------------------------------------------------------------------- /SPECS/dblink_plus_pg16_ora.spec: -------------------------------------------------------------------------------- 1 | # SPEC file for dblink_plus 2 | # Copyright(C) 2025 NIPPON TELEGRAPH AND TELEPHONE CORPORATION 3 | 4 | %define _pgdir /usr/pgsql-16 5 | %define _bindir %{_pgdir}/bin 6 | %define _libdir %{_pgdir}/lib 7 | %define _datadir %{_pgdir}/share/extension 8 | %define _bcdir %{_libdir}/bitcode/dblink_plus 9 | %define _bc_ind_dir %{_libdir}/bitcode 10 | 11 | ## Set general information 12 | Summary: PostgreSQL module to connect PostgreSQL/Oracle 13 | Name: dblink_plus 14 | Version: 1.0.10 15 | Release: 1%{?dist} 16 | License: BSD 17 | Group: Applications/Databases 18 | Source0: %{name}-%{version}.tar.gz 19 | URL: https://github.com/ossc-db/dblink_plus 20 | BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-%(%{__id_u} -n) 21 | Vendor: NIPPON TELEGRAPH AND TELEPHONE CORPORATION 22 | #Following is needed to remove auto-discovery of oci library files(not under RPM package management) 23 | AutoReqProv: no 24 | 25 | ## We use postgresql-devel package 26 | BuildRequires: postgresql16-devel 27 | Requires: postgresql16-libs 28 | 29 | ## Description 30 | %description 31 | dblink_plus is a PostgreSQL module which supports connections to other databases. 32 | It is similar to contrib/dblink except that it can connect to Oracle, MySQL and sqlite3. 33 | 34 | Note that this package is available for only PostgreSQL 16. 35 | 36 | %package llvmjit 37 | Requires: postgresql16-server, postgresql16-llvmjit 38 | Requires: dblink_plus = 1.0.10 39 | Summary: Just-in-time compilation support for dblink_plus 40 | 41 | %description llvmjit 42 | Just-in-time compilation support for dblink_plus 1.0.10 43 | 44 | ## prework 45 | %prep 46 | %setup -q -n %{name}-%{version} 47 | 48 | ## Set variables for build environment 49 | %build 50 | USE_PGXS=1 make %{?_smp_mflags} MYSQL=0 SQLITE3=0 ORACLE=1 51 | 52 | ## Set variables for install 53 | %install 54 | rm -rf %{buildroot} 55 | USE_PGXS=1 make install MYSQL=0 SQLITE3=0 ORACLE=1 DESTDIR=%{buildroot} 56 | install -m 644 COPYRIGHT %{buildroot}%{_datadir}/COPYRIGHT_dblink_plus 57 | 58 | %clean 59 | rm -rf %{buildroot} 60 | 61 | %files 62 | %defattr(0755,root,root) 63 | %{_libdir}/dblink_plus.so 64 | %defattr(0644,root,root) 65 | %{_datadir}/dblink_plus.sql 66 | %{_datadir}/dblink_plus--1.0.10.sql 67 | %{_datadir}/dblink_plus.control 68 | %{_datadir}/uninstall_dblink_plus.sql 69 | %{_datadir}/COPYRIGHT_dblink_plus 70 | 71 | %files llvmjit 72 | %defattr(0644,root,root,0755) 73 | %{_bcdir} 74 | %defattr(0644,root,root) 75 | %{_bc_ind_dir}/dblink_plus.index.bc 76 | 77 | # History. 78 | %changelog 79 | * Fri Jan 17 2025 - NTT OSS Center 1.0.10-1 80 | Support PG17. 81 | * Thu Jan 18 2024 - NTT OSS Center 1.0.9-1 82 | Support PG16. 83 | * Thu Jan 12 2023 - NTT OSS Center 1.0.8-1 84 | Support PG15. 85 | * Wed Jan 12 2022 - NTT OSS Center 1.0.7-1 86 | Support PG14. 87 | * Thu Jan 07 2021 - NTT OSS Center 1.0.6-1 88 | Support PG13 and fix how to install bitcode. 89 | * Mon Nov 25 2019 - NTT OSS Center 1.0.5-1 90 | Support PG12. 91 | * Tue Jan 22 2019 - NTT OSS Center 1.0.4-1 92 | Support PG11. 93 | -------------------------------------------------------------------------------- /SPECS/dblink_plus_pg17.spec: -------------------------------------------------------------------------------- 1 | # SPEC file for dblink_plus 2 | # Copyright(C) 2025 NIPPON TELEGRAPH AND TELEPHONE CORPORATION 3 | 4 | %define _pgdir /usr/pgsql-17 5 | %define _bindir %{_pgdir}/bin 6 | %define _libdir %{_pgdir}/lib 7 | %define _datadir %{_pgdir}/share/extension 8 | %define _bcdir %{_libdir}/bitcode/dblink_plus 9 | %define _bc_ind_dir %{_libdir}/bitcode 10 | 11 | ## Set general information 12 | Summary: PostgreSQL module to connect PostgreSQL/Oracle 13 | Name: dblink_plus 14 | Version: 1.0.10 15 | Release: 1%{?dist} 16 | License: BSD 17 | Group: Applications/Databases 18 | Source0: %{name}-%{version}.tar.gz 19 | URL: https://github.com/ossc-db/dblink_plus 20 | BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-%(%{__id_u} -n) 21 | Vendor: NIPPON TELEGRAPH AND TELEPHONE CORPORATION 22 | #Following is needed to remove auto-discovery of oci library files(not under RPM package management) 23 | AutoReqProv: no 24 | 25 | ## We use postgresql-devel package 26 | BuildRequires: postgresql17-devel 27 | Requires: postgresql17-libs 28 | 29 | ## Description 30 | %description 31 | dblink_plus is a PostgreSQL module which supports connections to other databases. 32 | It is similar to contrib/dblink except that it can connect to Oracle, MySQL and sqlite3. 33 | 34 | Note that this package is available for only PostgreSQL 17. 35 | 36 | %package llvmjit 37 | Requires: postgresql17-server, postgresql17-llvmjit 38 | Requires: dblink_plus = 1.0.10 39 | Summary: Just-in-time compilation support for dblink_plus 40 | 41 | %description llvmjit 42 | Just-in-time compilation support for dblink_plus 1.0.10 43 | 44 | ## prework 45 | %prep 46 | %setup -q -n %{name}-%{version} 47 | 48 | ## Set variables for build environment 49 | %build 50 | USE_PGXS=1 make %{?_smp_mflags} MYSQL=0 SQLITE3=0 ORACLE=0 51 | 52 | ## Set variables for install 53 | %install 54 | rm -rf %{buildroot} 55 | USE_PGXS=1 make install MYSQL=0 SQLITE3=0 ORACLE=0 DESTDIR=%{buildroot} 56 | install -m 644 COPYRIGHT %{buildroot}%{_datadir}/COPYRIGHT_dblink_plus 57 | 58 | %clean 59 | rm -rf %{buildroot} 60 | 61 | %files 62 | %defattr(0755,root,root) 63 | %{_libdir}/dblink_plus.so 64 | %defattr(0644,root,root) 65 | %{_datadir}/dblink_plus.sql 66 | %{_datadir}/dblink_plus--1.0.10.sql 67 | %{_datadir}/dblink_plus.control 68 | %{_datadir}/uninstall_dblink_plus.sql 69 | %{_datadir}/COPYRIGHT_dblink_plus 70 | 71 | %files llvmjit 72 | %defattr(0644,root,root,0755) 73 | %{_bcdir} 74 | %defattr(0644,root,root) 75 | %{_bc_ind_dir}/dblink_plus.index.bc 76 | 77 | # History. 78 | %changelog 79 | * Fri Jan 17 2025 - NTT OSS Center 1.0.10-1 80 | Support PG17. 81 | * Thu Jan 18 2024 - NTT OSS Center 1.0.9-1 82 | Support PG16. 83 | * Thu Jan 12 2023 - NTT OSS Center 1.0.8-1 84 | Support PG15. 85 | * Wed Jan 12 2022 - NTT OSS Center 1.0.7-1 86 | Support PG14. 87 | * Thu Jan 07 2021 - NTT OSS Center 1.0.6-1 88 | Support PG13 and fix how to install bitcode. 89 | * Mon Nov 25 2019 - NTT OSS Center 1.0.5-1 90 | Support PG12. 91 | * Tue Jan 22 2019 - NTT OSS Center 1.0.4-1 92 | Support PG11. 93 | -------------------------------------------------------------------------------- /SPECS/dblink_plus_pg17_ora.spec: -------------------------------------------------------------------------------- 1 | # SPEC file for dblink_plus 2 | # Copyright(C) 2025 NIPPON TELEGRAPH AND TELEPHONE CORPORATION 3 | 4 | %define _pgdir /usr/pgsql-17 5 | %define _bindir %{_pgdir}/bin 6 | %define _libdir %{_pgdir}/lib 7 | %define _datadir %{_pgdir}/share/extension 8 | %define _bcdir %{_libdir}/bitcode/dblink_plus 9 | %define _bc_ind_dir %{_libdir}/bitcode 10 | 11 | ## Set general information 12 | Summary: PostgreSQL module to connect PostgreSQL/Oracle 13 | Name: dblink_plus 14 | Version: 1.0.10 15 | Release: 1%{?dist} 16 | License: BSD 17 | Group: Applications/Databases 18 | Source0: %{name}-%{version}.tar.gz 19 | URL: https://github.com/ossc-db/dblink_plus 20 | BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-%(%{__id_u} -n) 21 | Vendor: NIPPON TELEGRAPH AND TELEPHONE CORPORATION 22 | #Following is needed to remove auto-discovery of oci library files(not under RPM package management) 23 | AutoReqProv: no 24 | 25 | ## We use postgresql-devel package 26 | BuildRequires: postgresql17-devel 27 | Requires: postgresql17-libs 28 | 29 | ## Description 30 | %description 31 | dblink_plus is a PostgreSQL module which supports connections to other databases. 32 | It is similar to contrib/dblink except that it can connect to Oracle, MySQL and sqlite3. 33 | 34 | Note that this package is available for only PostgreSQL 17. 35 | 36 | %package llvmjit 37 | Requires: postgresql17-server, postgresql17-llvmjit 38 | Requires: dblink_plus = 1.0.10 39 | Summary: Just-in-time compilation support for dblink_plus 40 | 41 | %description llvmjit 42 | Just-in-time compilation support for dblink_plus 1.0.10 43 | 44 | ## prework 45 | %prep 46 | %setup -q -n %{name}-%{version} 47 | 48 | ## Set variables for build environment 49 | %build 50 | USE_PGXS=1 make %{?_smp_mflags} MYSQL=0 SQLITE3=0 ORACLE=1 51 | 52 | ## Set variables for install 53 | %install 54 | rm -rf %{buildroot} 55 | USE_PGXS=1 make install MYSQL=0 SQLITE3=0 ORACLE=1 DESTDIR=%{buildroot} 56 | install -m 644 COPYRIGHT %{buildroot}%{_datadir}/COPYRIGHT_dblink_plus 57 | 58 | %clean 59 | rm -rf %{buildroot} 60 | 61 | %files 62 | %defattr(0755,root,root) 63 | %{_libdir}/dblink_plus.so 64 | %defattr(0644,root,root) 65 | %{_datadir}/dblink_plus.sql 66 | %{_datadir}/dblink_plus--1.0.10.sql 67 | %{_datadir}/dblink_plus.control 68 | %{_datadir}/uninstall_dblink_plus.sql 69 | %{_datadir}/COPYRIGHT_dblink_plus 70 | 71 | %files llvmjit 72 | %defattr(0644,root,root,0755) 73 | %{_bcdir} 74 | %defattr(0644,root,root) 75 | %{_bc_ind_dir}/dblink_plus.index.bc 76 | 77 | # History. 78 | %changelog 79 | * Fri Jan 17 2025 - NTT OSS Center 1.0.10-1 80 | Support PG17. 81 | * Thu Jan 18 2024 - NTT OSS Center 1.0.9-1 82 | Support PG16. 83 | * Thu Jan 12 2023 - NTT OSS Center 1.0.8-1 84 | Support PG15. 85 | * Wed Jan 12 2022 - NTT OSS Center 1.0.7-1 86 | Support PG14. 87 | * Thu Jan 07 2021 - NTT OSS Center 1.0.6-1 88 | Support PG13 and fix how to install bitcode. 89 | * Mon Nov 25 2019 - NTT OSS Center 1.0.5-1 90 | Support PG12. 91 | * Tue Jan 22 2019 - NTT OSS Center 1.0.4-1 92 | Support PG11. 93 | -------------------------------------------------------------------------------- /dblink.c: -------------------------------------------------------------------------------- 1 | /* 2 | * dblink.c 3 | * 4 | * Copyright (c) 2011-2025, NIPPON TELEGRAPH AND TELEPHONE CORPORATION 5 | */ 6 | #include "postgres.h" 7 | 8 | #include "access/reloptions.h" 9 | #include "access/xact.h" 10 | #include "catalog/pg_foreign_server.h" 11 | #include "catalog/pg_user_mapping.h" 12 | #include "commands/trigger.h" 13 | #include "executor/spi.h" 14 | #include "foreign/foreign.h" 15 | #include "funcapi.h" 16 | #include "miscadmin.h" 17 | #include "parser/scansup.h" 18 | #include "utils/acl.h" 19 | #include "utils/builtins.h" 20 | #include "utils/memutils.h" 21 | 22 | #include "dblink.h" 23 | #include "dblink_internal.h" 24 | 25 | #include 26 | #ifndef WIN32 27 | #include 28 | #endif /* ! WIN32 */ 29 | 30 | /* required to configure GUC paramer */ 31 | #include "utils/guc.h" 32 | 33 | /* 34 | * TODO: replace elog() to ereport() with proper error codes. 35 | */ 36 | 37 | PG_MODULE_MAGIC; 38 | 39 | #undef open 40 | 41 | #define NameGetTextDatum(name) CStringGetTextDatum(NameStr(name)) 42 | 43 | /* initial number of connection hashes */ 44 | #define NUMCONN 16 45 | 46 | /* global transaction id */ 47 | #define DBLINK_GTRID_PREFIX "dblink_plus_" 48 | #define DBLINK_GTRID_MAXSIZE 32 49 | 50 | typedef enum ConnStatus 51 | { 52 | CS_UNUSED = 0, /* unconnected */ 53 | CS_IDLE, /* connection idle */ 54 | CS_USED, /* in transaction */ 55 | CS_PREPARED, /* transaction is prepared and ready to commit */ 56 | } ConnStatus; 57 | 58 | static const char *ConnStatusName[] = 59 | { 60 | "unused", 61 | "idle", 62 | "used", 63 | "prepared", 64 | }; 65 | 66 | typedef enum FetchType 67 | { 68 | FT_QUERY = 0, 69 | FT_FETCH, 70 | FT_CALL 71 | } FetchType; 72 | 73 | typedef struct Conn 74 | { 75 | NameData name; 76 | dblink_connection *connection; 77 | ConnStatus status; 78 | Oid server; /* server oid (only for display) */ 79 | bool use_xa; /* true iff use automatic transaction */ 80 | bool keep; /* true iff connected by user */ 81 | } Conn; 82 | 83 | typedef struct Cursor 84 | { 85 | int32 id; 86 | dblink_cursor *cursor; 87 | } Cursor; 88 | 89 | PG_FUNCTION_INFO_V1(dblink_connect); 90 | PG_FUNCTION_INFO_V1(dblink_connect_name); 91 | PG_FUNCTION_INFO_V1(dblink_disconnect); 92 | PG_FUNCTION_INFO_V1(dblink_query); 93 | PG_FUNCTION_INFO_V1(dblink_exec); 94 | PG_FUNCTION_INFO_V1(dblink_open); 95 | PG_FUNCTION_INFO_V1(dblink_fetch); 96 | PG_FUNCTION_INFO_V1(dblink_close); 97 | PG_FUNCTION_INFO_V1(dblink_call); 98 | PG_FUNCTION_INFO_V1(dblink_connections); 99 | PG_FUNCTION_INFO_V1(dblink_atcommit); 100 | 101 | extern Datum dblink_connect(PG_FUNCTION_ARGS); 102 | extern Datum dblink_connect_name(PG_FUNCTION_ARGS); 103 | extern Datum dblink_disconnect(PG_FUNCTION_ARGS); 104 | extern Datum dblink_query(PG_FUNCTION_ARGS); 105 | extern Datum dblink_exec(PG_FUNCTION_ARGS); 106 | extern Datum dblink_open(PG_FUNCTION_ARGS); 107 | extern Datum dblink_fetch(PG_FUNCTION_ARGS); 108 | extern Datum dblink_close(PG_FUNCTION_ARGS); 109 | extern Datum dblink_call(PG_FUNCTION_ARGS); 110 | extern Datum dblink_connections(PG_FUNCTION_ARGS); 111 | extern Datum dblink_atcommit(PG_FUNCTION_ARGS); 112 | 113 | PG_FUNCTION_INFO_V1(dblink_mysql); 114 | PG_FUNCTION_INFO_V1(dblink_oracle); 115 | PG_FUNCTION_INFO_V1(dblink_sqlite3); 116 | 117 | extern Datum dblink_mysql(PG_FUNCTION_ARGS); 118 | extern Datum dblink_oracle(PG_FUNCTION_ARGS); 119 | extern Datum dblink_sqlite3(PG_FUNCTION_ARGS); 120 | 121 | extern void _PG_init(void); 122 | 123 | static Conn *searchLink(const text *name, HASHACTION action, bool *found); 124 | static Conn *searchLinkByName(const NameData *name, HASHACTION action, bool *found); 125 | static Conn *doConnect(const text *name, const text *servername, bool *isNew); 126 | static Conn *doTransaction(const text *name); 127 | static ForeignServer *getServerByName(const char *name); 128 | static List *getConnectionParams(ForeignServer *server, ForeignDataWrapper *fdw); 129 | static void closeCursors(void); 130 | 131 | static void AtCommit_dblink(void); 132 | static void AtEOXact_dblink(XactEvent event, void *arg); 133 | static void RegisterCommitCallback(void); 134 | 135 | static HTAB *connections; /* all connections managed by the module */ 136 | static List *cursors; /* all cursors managed by the module */ 137 | static int32 next_cursor_id = 0; 138 | 139 | #define INVALID_CURSOR 0 140 | 141 | /* This variable is use by a custom GUC parameter dblink_plus.use_xa. 142 | * This parameter is true as default in order to maintain the previous behavior.*/ 143 | bool use_xa; 144 | 145 | /* 146 | * Module load callback 147 | */ 148 | 149 | void 150 | _PG_init(void) 151 | { 152 | /* Define custom GUC variables. */ 153 | DefineCustomBoolVariable("dblink_plus.use_xa", 154 | "Parameter used for dblink_plus.", 155 | NULL, 156 | &use_xa, 157 | true, 158 | PGC_USERSET, 159 | 0, 160 | #if PG_VERSION_NUM >= 90100 161 | NULL, 162 | #endif 163 | NULL, NULL); 164 | 165 | EmitWarningsOnPlaceholders("dblink_plus"); 166 | 167 | srandom((unsigned int) time(NULL)); 168 | 169 | } 170 | 171 | /* 172 | * dblink_connect(server text) : boolean 173 | */ 174 | Datum 175 | dblink_connect(PG_FUNCTION_ARGS) 176 | { 177 | text *name = PG_GETARG_TEXT_PP(0); 178 | Conn *conn; 179 | bool isNew; 180 | 181 | conn = doConnect(name, name, &isNew); 182 | conn->use_xa = PG_GETARG_BOOL(1); 183 | conn->keep = true; 184 | 185 | PG_RETURN_BOOL(isNew); 186 | } 187 | 188 | /* 189 | * dblink_connect_name(name text, server text) : boolean 190 | */ 191 | Datum 192 | dblink_connect_name(PG_FUNCTION_ARGS) 193 | { 194 | text *name = PG_GETARG_TEXT_PP(0); 195 | text *server = PG_GETARG_TEXT_PP(1); 196 | Conn *conn; 197 | bool isNew; 198 | 199 | conn = doConnect(name, server, &isNew); 200 | conn->use_xa = PG_GETARG_BOOL(2); 201 | conn->keep = true; 202 | 203 | PG_RETURN_BOOL(isNew); 204 | } 205 | 206 | /* 207 | * dblink_disconnect(name text) : boolean 208 | */ 209 | Datum 210 | dblink_disconnect(PG_FUNCTION_ARGS) 211 | { 212 | text *name = PG_GETARG_TEXT_PP(0); 213 | Conn *conn; 214 | 215 | conn = searchLink(name, HASH_FIND, NULL); 216 | if (conn == NULL) 217 | PG_RETURN_BOOL(false); /* not found */ 218 | 219 | /* 220 | * set to unkeep, but actual disconnection is postponed to 221 | * the end of transaction. 222 | */ 223 | conn->keep = false; 224 | PG_RETURN_BOOL(true); 225 | } 226 | 227 | typedef struct query_context 228 | { 229 | dblink_cursor *cursor; 230 | const char **values; 231 | } query_context; 232 | 233 | static Datum 234 | generic_fetch(FetchType fetch_type, PG_FUNCTION_ARGS) 235 | { 236 | query_context *context; 237 | FuncCallContext *fctx; 238 | AttInMetadata *attinmeta; 239 | 240 | if (SRF_IS_FIRSTCALL()) 241 | { 242 | Conn *conn; 243 | TupleDesc tupdesc; 244 | MemoryContext mctx; 245 | dblink_cursor *cursor; 246 | int nfields; 247 | 248 | /* Build a tuple descriptor for our result type */ 249 | if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) 250 | elog(ERROR, "return type must be a row type"); 251 | 252 | fctx = SRF_FIRSTCALL_INIT(); 253 | 254 | if (fetch_type != FT_FETCH) 255 | { 256 | char *sql = text_to_cstring(PG_GETARG_TEXT_PP(1)); 257 | int32 fetchsize = PG_GETARG_INT32(2); 258 | int32 max_value_len = PG_GETARG_INT32(3); 259 | 260 | conn = doTransaction(PG_GETARG_TEXT_PP(0)); 261 | if (fetch_type == FT_QUERY) 262 | cursor = conn->connection->open(conn->connection, sql, fetchsize, max_value_len); 263 | else 264 | cursor = conn->connection->call(conn->connection, sql, fetchsize, max_value_len); 265 | } 266 | else 267 | { 268 | int32 id = PG_GETARG_INT32(0); 269 | int32 howmany = PG_GETARG_INT32(1); 270 | ListCell *cell; 271 | 272 | if (howmany < 1) 273 | SRF_RETURN_DONE(fctx); /* no rows required */ 274 | fctx->max_calls = howmany; 275 | 276 | cursor = NULL; 277 | foreach(cell, cursors) 278 | { 279 | Cursor *cur = (Cursor *) lfirst(cell); 280 | if (cur->id == id) 281 | { 282 | cursor = cur->cursor; 283 | break; 284 | } 285 | } 286 | } 287 | 288 | if (cursor == NULL) 289 | SRF_RETURN_DONE(fctx); /* cursor not found or no tuples */ 290 | 291 | nfields = cursor->nfields; 292 | if (nfields < 1) 293 | SRF_RETURN_DONE(fctx); /* no fields */ 294 | 295 | mctx = MemoryContextSwitchTo(fctx->multi_call_memory_ctx); 296 | context = palloc(sizeof(query_context)); 297 | context->cursor = cursor; 298 | context->values = palloc(nfields * sizeof(char *)); 299 | 300 | /* make sure we have a persistent copy of the tupdesc */ 301 | tupdesc = CreateTupleDescCopy(tupdesc); 302 | 303 | /* store needed metadata for subsequent calls */ 304 | attinmeta = TupleDescGetAttInMetadata(tupdesc); 305 | fctx->attinmeta = attinmeta; 306 | 307 | fctx->user_fctx = context; 308 | MemoryContextSwitchTo(mctx); 309 | } 310 | else 311 | { 312 | fctx = SRF_PERCALL_SETUP(); 313 | attinmeta = fctx->attinmeta; 314 | context = fctx->user_fctx; 315 | } 316 | 317 | /* Exit if fetch limit exceeded. Don't close cursor in this case. */ 318 | if (fetch_type == FT_FETCH && fctx->call_cntr >= fctx->max_calls) 319 | SRF_RETURN_DONE(fctx); 320 | 321 | if (context->cursor->fetch(context->cursor, context->values)) 322 | { 323 | HeapTuple tuple; 324 | Datum result; 325 | 326 | tuple = BuildTupleFromCStrings(attinmeta, (char **) context->values); 327 | result = HeapTupleGetDatum(tuple); 328 | 329 | SRF_RETURN_NEXT(fctx, result); 330 | } 331 | else 332 | { 333 | ListCell *cell; 334 | #if PG_VERSION_NUM < 130000 335 | ListCell *prev; 336 | #endif 337 | 338 | context->cursor->close(context->cursor); 339 | 340 | /* forget cursor */ 341 | #if PG_VERSION_NUM < 130000 342 | prev = NULL; 343 | #endif 344 | foreach(cell, cursors) 345 | { 346 | Cursor *cur = (Cursor *) lfirst(cell); 347 | 348 | if (cur->cursor == context->cursor) 349 | { 350 | #if PG_VERSION_NUM < 130000 351 | cursors = list_delete_cell(cursors, cell, prev); 352 | #else 353 | cursors = foreach_delete_current(cursors, cell); 354 | #endif 355 | break; 356 | } 357 | 358 | #if PG_VERSION_NUM < 130000 359 | prev = cell; 360 | #endif 361 | } 362 | 363 | SRF_RETURN_DONE(fctx); 364 | } 365 | } 366 | 367 | /* 368 | * dblink_query(name text, sql text) : SETOF record 369 | */ 370 | Datum 371 | dblink_query(PG_FUNCTION_ARGS) 372 | { 373 | return generic_fetch(FT_QUERY, fcinfo); 374 | } 375 | 376 | /* 377 | * dblink_exec(name text, sql text) : bigint 378 | */ 379 | Datum 380 | dblink_exec(PG_FUNCTION_ARGS) 381 | { 382 | Conn *conn = doTransaction(PG_GETARG_TEXT_PP(0)); 383 | char *sql = text_to_cstring(PG_GETARG_TEXT_PP(1)); 384 | int64 ntuples; 385 | 386 | ntuples = conn->connection->exec(conn->connection, sql); 387 | 388 | PG_RETURN_INT64(ntuples); 389 | } 390 | 391 | /* 392 | * dblink_open(name text, sql text, fetchsize integer) : cursor 393 | */ 394 | Datum 395 | dblink_open(PG_FUNCTION_ARGS) 396 | { 397 | Conn *conn; 398 | dblink_cursor *cursor; 399 | char *sql = text_to_cstring(PG_GETARG_TEXT_PP(1)); 400 | int32 fetchsize = PG_GETARG_INT32(2); 401 | int32 max_value_len = PG_GETARG_INT32(3); 402 | MemoryContext ctx; 403 | Cursor *cur; 404 | 405 | conn = doTransaction(PG_GETARG_TEXT_PP(0)); 406 | cursor = conn->connection->open(conn->connection, sql, fetchsize, max_value_len); 407 | if (cursor == NULL) 408 | PG_RETURN_INT32(INVALID_CURSOR); 409 | 410 | ctx = MemoryContextSwitchTo(TopMemoryContext); 411 | cur = (Cursor *) palloc(sizeof(Cursor)); 412 | cur->id = ++next_cursor_id; 413 | if (cur->id == INVALID_CURSOR) 414 | cur->id = ++next_cursor_id; /* reassign if invalid */ 415 | cur->cursor = cursor; 416 | cursors = lappend(cursors, cur); 417 | MemoryContextSwitchTo(ctx); 418 | 419 | PG_RETURN_INT32(cur->id); 420 | } 421 | 422 | /* 423 | * dblink_fetch(cursor, howmany integer) : SETOF record 424 | */ 425 | Datum 426 | dblink_fetch(PG_FUNCTION_ARGS) 427 | { 428 | return generic_fetch(FT_FETCH, fcinfo); 429 | } 430 | 431 | /* 432 | * dblink_close(cur cursor) : boolean 433 | */ 434 | Datum 435 | dblink_close(PG_FUNCTION_ARGS) 436 | { 437 | int32 id = PG_GETARG_INT32(0); 438 | ListCell *cell; 439 | 440 | #if PG_VERSION_NUM < 130000 441 | ListCell *prev; 442 | /* forget cursor */ 443 | prev = NULL; 444 | #endif 445 | foreach(cell, cursors) 446 | { 447 | Cursor *cur = (Cursor *) lfirst(cell); 448 | 449 | if (cur->id == id) 450 | { 451 | cur->cursor->close(cur->cursor); 452 | #if PG_VERSION_NUM < 130000 453 | cursors = list_delete_cell(cursors, cell, prev); 454 | #else 455 | cursors = foreach_delete_current(cursors, cell); 456 | #endif 457 | PG_RETURN_BOOL(true); 458 | } 459 | #if PG_VERSION_NUM < 130000 460 | prev = cell; 461 | #endif 462 | } 463 | 464 | PG_RETURN_BOOL(false); 465 | } 466 | 467 | /* 468 | * dblink_call(name text, sql text) : SETOF record 469 | */ 470 | Datum 471 | dblink_call(PG_FUNCTION_ARGS) 472 | { 473 | return generic_fetch(FT_CALL, fcinfo); 474 | } 475 | 476 | /* 477 | * dblink_connections() : 478 | * SETOF (name text, server oid, status text, keep boolean) 479 | */ 480 | #define DBLINK_COLS 5 481 | Datum 482 | dblink_connections(PG_FUNCTION_ARGS) 483 | { 484 | ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; 485 | TupleDesc tupdesc; 486 | Tuplestorestate *tupstore; 487 | MemoryContext per_query_ctx; 488 | MemoryContext oldcontext; 489 | 490 | /* check to see if caller supports us returning a tuplestore */ 491 | if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo)) 492 | ereport(ERROR, 493 | (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), 494 | errmsg("set-valued function called in context that cannot accept a set"))); 495 | if (!(rsinfo->allowedModes & SFRM_Materialize)) 496 | ereport(ERROR, 497 | (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), 498 | errmsg("materialize mode required, but it is not " \ 499 | "allowed in this context"))); 500 | 501 | /* Build a tuple descriptor for our result type */ 502 | if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) 503 | elog(ERROR, "return type must be a row type"); 504 | 505 | per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; 506 | oldcontext = MemoryContextSwitchTo(per_query_ctx); 507 | 508 | tupstore = tuplestore_begin_heap(true, false, work_mem); 509 | rsinfo->returnMode = SFRM_Materialize; 510 | rsinfo->setResult = tupstore; 511 | rsinfo->setDesc = tupdesc; 512 | 513 | if (connections) 514 | { 515 | HASH_SEQ_STATUS seq; 516 | Conn *conn; 517 | 518 | hash_seq_init(&seq, connections); 519 | while ((conn = (Conn *) hash_seq_search(&seq)) != NULL) 520 | { 521 | Datum values[DBLINK_COLS]; 522 | bool nulls[DBLINK_COLS]; 523 | int i = 0; 524 | 525 | /* generate junk in short-term context */ 526 | MemoryContextSwitchTo(oldcontext); 527 | 528 | memset(values, 0, sizeof(values)); 529 | memset(nulls, 0, sizeof(nulls)); 530 | 531 | /* use query for server type */ 532 | values[i++] = CStringGetTextDatum(NameStr(conn->name)); 533 | values[i++] = ObjectIdGetDatum(conn->server); 534 | values[i++] = CStringGetTextDatum(ConnStatusName[conn->status]); 535 | values[i++] = BoolGetDatum(conn->use_xa); 536 | values[i++] = BoolGetDatum(conn->keep); 537 | 538 | Assert(i == DBLINK_COLS); 539 | 540 | /* switch to appropriate context while storing the tuple */ 541 | MemoryContextSwitchTo(per_query_ctx); 542 | tuplestore_putvalues(tupstore, tupdesc, values, nulls); 543 | } 544 | } 545 | 546 | MemoryContextSwitchTo(oldcontext); 547 | 548 | return (Datum) 0; 549 | } 550 | 551 | Datum 552 | dblink_atcommit(PG_FUNCTION_ARGS) 553 | { 554 | TriggerData *trigdata = (TriggerData *) fcinfo->context; 555 | 556 | /* make sure it's called as a trigger at all */ 557 | if (!CALLED_AS_TRIGGER(fcinfo) || 558 | !TRIGGER_FIRED_AFTER(trigdata->tg_event) || 559 | !TRIGGER_FIRED_FOR_STATEMENT(trigdata->tg_event)) 560 | elog(ERROR, "invalid trigger call"); 561 | 562 | AtCommit_dblink(); 563 | 564 | PG_RETURN_POINTER(NULL); 565 | } 566 | 567 | /* 568 | * search a connection by text type 569 | */ 570 | static Conn * 571 | searchLink(const text *name, HASHACTION action, bool *found) 572 | { 573 | char *short_name; 574 | NameData key; 575 | 576 | /* 577 | * Truncate name if it's too long for identifier. If this is a call for 578 | * a new connection, warn user that the new connection has truncated name. 579 | */ 580 | short_name = text_to_cstring(name); 581 | truncate_identifier(short_name, strlen(short_name), action == HASH_ENTER); 582 | MemSet(key.data, 0, NAMEDATALEN); 583 | strncpy(key.data, short_name, strlen(short_name)); 584 | 585 | return searchLinkByName(&key, action, found); 586 | } 587 | 588 | /* 589 | * search a connection by name type 590 | */ 591 | static Conn * 592 | searchLinkByName(const NameData *name, HASHACTION action, bool *found) 593 | { 594 | if (!connections) 595 | { 596 | HASHCTL ctl; 597 | 598 | ctl.keysize = NAMEDATALEN; 599 | ctl.entrysize = sizeof(Conn); 600 | connections = hash_create("dblink", NUMCONN, &ctl, HASH_ELEM); 601 | 602 | /* register transaction callbacks */ 603 | RegisterXactCallback(AtEOXact_dblink, 0); 604 | } 605 | 606 | return (Conn *) hash_search(connections, name, action, found); 607 | } 608 | 609 | static Conn * 610 | doConnect(const text *name, const text *servername, bool *isNew) 611 | { 612 | char *short_name; 613 | Conn *conn; 614 | bool found; 615 | ForeignServer *server; 616 | ForeignDataWrapper *fdw; 617 | List *params; 618 | 619 | /* 620 | * Truncate server name if it's too long for identifier because catalog 621 | * lookup functions assumes that the given name is shorter than 622 | * NAMEDATALEN. We don't warn users about this truncation, to make the 623 | * behavior to be identical to contrib/dblink. 624 | */ 625 | short_name = text_to_cstring(servername); 626 | truncate_identifier(short_name, strlen(short_name), false); 627 | server = getServerByName(short_name); 628 | fdw = GetForeignDataWrapper(server->fdwid); 629 | 630 | if (fdw->fdwvalidator == InvalidOid) 631 | elog(ERROR, "server '%s' and foreign data wrapper '%s' have no connector", 632 | server->servername, fdw->fdwname); 633 | 634 | params = getConnectionParams(server, fdw); 635 | 636 | conn = searchLink(name, HASH_ENTER, &found); 637 | if (found && conn->status != CS_UNUSED) 638 | { 639 | if (conn->server != server->serverid) 640 | elog(ERROR, "same name for different server"); 641 | } 642 | else 643 | { 644 | conn->status = CS_UNUSED; 645 | conn->server = server->serverid; 646 | /* assign value setting in postgresql.conf dblink_plus.use_xa variable to connection use_xa.*/ 647 | conn->use_xa = use_xa; 648 | } 649 | 650 | if (conn->status == CS_UNUSED) 651 | { 652 | dblink_connection *connection = NULL; 653 | 654 | /* call validator with DBLINKOID mode with dummy parameters. */ 655 | OidFunctionCall4(fdw->fdwvalidator, 656 | PointerGetDatum(NULL), 657 | ObjectIdGetDatum(DBLINKOID), 658 | PointerGetDatum(&connection), 659 | PointerGetDatum(params)); 660 | 661 | if (connection == NULL) 662 | elog(ERROR, "foreign data wrapper '%s' is not a connector", fdw->fdwname); 663 | 664 | conn->connection = connection; 665 | conn->status = CS_IDLE; 666 | conn->keep = false; 667 | } 668 | 669 | if (isNew) 670 | *isNew = !found; 671 | 672 | return conn; 673 | } 674 | 675 | static Conn * 676 | doTransaction(const text *name) 677 | { 678 | Conn *conn; 679 | 680 | conn = searchLink(name, HASH_FIND, NULL); 681 | if (conn == NULL) 682 | conn = doConnect(name, name, NULL); 683 | 684 | switch (conn->status) 685 | { 686 | case CS_IDLE: 687 | if (conn->use_xa) 688 | { 689 | /* set gtrid parts */ 690 | conn->connection->gtrid.pid = (int) getpid(); 691 | conn->connection->gtrid.rand = (int) random(); 692 | /* start transaction automatically */ 693 | if (!conn->connection->command(conn->connection, DBLINK_XA_START)) 694 | elog(ERROR, "DBLINK_XA_START failed for '%s'", NameStr(conn->name)); 695 | conn->status = CS_USED; 696 | RegisterCommitCallback(); 697 | } 698 | break; 699 | case CS_USED: 700 | /* noop */ 701 | break; 702 | default: 703 | elog(ERROR, "unexpected status: %d", conn->status); 704 | break; 705 | } 706 | 707 | return conn; 708 | } 709 | 710 | /* 711 | * get foreign server by name and check acl. 712 | */ 713 | static ForeignServer * 714 | getServerByName(const char *name) 715 | { 716 | ForeignServer *server; 717 | AclResult aclresult; 718 | 719 | server = GetForeignServerByName(name, false); 720 | 721 | /* Check permissions, user must have usage on the server. */ 722 | #if PG_VERSION_NUM >= 160000 723 | aclresult = object_aclcheck(ForeignServerRelationId, server->serverid, GetUserId(), ACL_USAGE); 724 | #else 725 | aclresult = pg_foreign_server_aclcheck(server->serverid, GetUserId(), ACL_USAGE); 726 | #endif 727 | if (aclresult != ACLCHECK_OK) 728 | 729 | #if PG_VERSION_NUM < 110000 730 | aclcheck_error(aclresult, ACL_KIND_FOREIGN_SERVER, server->servername); 731 | #else 732 | aclcheck_error(aclresult, OBJECT_FOREIGN_SERVER, server->servername); 733 | #endif /* PG_VERSION_NUM */ 734 | 735 | return server; 736 | } 737 | 738 | static void 739 | RegisterCommitCallback(void) 740 | { 741 | /* this DELETE is only for registering two-phase trigger. */ 742 | SPI_connect(); 743 | SPI_exec("DELETE FROM dblink.atcommit", 1); 744 | SPI_finish(); 745 | } 746 | 747 | /* 748 | * Commit all remote transaction with 2PC. 749 | */ 750 | static void 751 | AtCommit_dblink(void) 752 | { 753 | closeCursors(); 754 | if (connections) 755 | { 756 | HASH_SEQ_STATUS seq; 757 | Conn *conn; 758 | 759 | hash_seq_init(&seq, connections); 760 | while ((conn = (Conn *) hash_seq_search(&seq)) != NULL) 761 | { 762 | if (conn->status == CS_USED) 763 | { 764 | if (!conn->connection->command(conn->connection, DBLINK_XA_PREPARE)) 765 | elog(ERROR, "DBLINK_XA_PREPARE failed for '%s'", NameStr(conn->name)); 766 | conn->status = CS_PREPARED; 767 | } 768 | } 769 | 770 | hash_seq_init(&seq, connections); 771 | while ((conn = (Conn *) hash_seq_search(&seq)) != NULL) 772 | { 773 | if (conn->status == CS_PREPARED) 774 | { 775 | if (!conn->connection->command(conn->connection, DBLINK_XA_COMMIT)) 776 | { 777 | /* XXX: or FATAL? */ 778 | elog(WARNING, "DBLINK_XA_COMMIT failed for '%s'", NameStr(conn->name)); 779 | } 780 | conn->status = CS_IDLE; 781 | } 782 | } 783 | } 784 | } 785 | 786 | /* 787 | * Rollback all remote transaction on error. 788 | */ 789 | static void 790 | AtEOXact_dblink(XactEvent event, void *arg) 791 | { 792 | HASH_SEQ_STATUS seq; 793 | Conn *conn; 794 | 795 | closeCursors(); 796 | if (connections == NULL || hash_get_num_entries(connections) < 1) 797 | return; 798 | 799 | hash_seq_init(&seq, connections); 800 | while ((conn = (Conn *) hash_seq_search(&seq)) != NULL) 801 | { 802 | switch (conn->status) 803 | { 804 | case CS_USED: 805 | conn->connection->command(conn->connection, DBLINK_ROLLBACK); 806 | conn->status = CS_IDLE; 807 | break; 808 | case CS_PREPARED: 809 | conn->connection->command(conn->connection, DBLINK_XA_ROLLBACK); 810 | conn->status = CS_IDLE; 811 | break; 812 | default: 813 | break; 814 | } 815 | 816 | /* disconnect automatic connections */ 817 | if (conn->status == CS_IDLE && !conn->keep) 818 | { 819 | conn->connection->disconnect(conn->connection); 820 | conn->status = CS_UNUSED; 821 | } 822 | 823 | /* remove if unused */ 824 | if (conn->status == CS_UNUSED) 825 | searchLinkByName(&conn->name, HASH_REMOVE, NULL); 826 | } 827 | } 828 | 829 | /* 830 | * Obtain connection string for a foreign server 831 | */ 832 | static List * 833 | getConnectionParams(ForeignServer *server, ForeignDataWrapper *fdw) 834 | { 835 | UserMapping *user_mapping; 836 | Oid serverid; 837 | Oid fdwid; 838 | Oid userid; 839 | List *result; 840 | 841 | serverid = server->serverid; 842 | fdwid = server->fdwid; 843 | userid = GetUserId(); 844 | user_mapping = GetUserMapping(userid, serverid); 845 | fdw = GetForeignDataWrapper(fdwid); 846 | 847 | result = list_copy(fdw->options); 848 | result = list_concat(result, list_copy(server->options)); 849 | result = list_concat(result, list_copy(user_mapping->options)); 850 | 851 | return result; 852 | } 853 | 854 | static void 855 | closeCursors(void) 856 | { 857 | ListCell *cell; 858 | 859 | if (cursors != NIL) 860 | { 861 | foreach(cell, cursors) 862 | { 863 | Cursor *cur = (Cursor *) lfirst(cell); 864 | cur->cursor->close(cur->cursor); 865 | } 866 | list_free(cursors); 867 | cursors = NIL; 868 | } 869 | } 870 | 871 | void 872 | dblink_error(int sqlstate, 873 | const char *message, 874 | const char *detail, 875 | const char *hint, 876 | const char *context) 877 | { 878 | ereport(ERROR, (errcode(sqlstate), 879 | message ? errmsg("%s", message) : errmsg("unknown error"), 880 | detail ? errdetail("%s", detail) : 0, 881 | hint ? errhint("%s", hint) : 0, 882 | context ? errcontext("%s", context) : 0)); 883 | } 884 | 885 | void dblink_elog(int level, const char *message) 886 | { 887 | elog(level, "%s", message); 888 | } 889 | 890 | void 891 | set_dblink_gtrid(char *str, 892 | size_t size, 893 | const char *format, 894 | dblink_connection base) 895 | { 896 | char gtrid[DBLINK_GTRID_MAXSIZE+1]; 897 | 898 | /* set global transaction id from gtrid parts */ 899 | snprintf(gtrid, lengthof(gtrid), "%s%010d%010d", 900 | DBLINK_GTRID_PREFIX, base.gtrid.pid, base.gtrid.rand); 901 | snprintf(str, size, format, gtrid); 902 | } 903 | 904 | /* 905 | * connectors 906 | */ 907 | 908 | typedef struct Option 909 | { 910 | const char *optname; 911 | Oid optcontext; /* Oid of catalog in which option may appear */ 912 | } Option; 913 | 914 | static bool 915 | is_conninfo_option(const char *option, Oid context, const Option options[]) 916 | { 917 | const Option *opt; 918 | 919 | for (opt = options; opt->optname; opt++) 920 | if ((context == opt->optcontext || context == InvalidOid) && strcmp(opt->optname, option) == 0) 921 | return true; 922 | return false; 923 | } 924 | 925 | static bool 926 | validate_options(Datum options_datum, Oid catalog, const Option options[]) 927 | { 928 | List *options_list; 929 | ListCell *cell; 930 | 931 | options_list = untransformRelOptions(options_datum); 932 | 933 | foreach(cell, options_list) 934 | { 935 | DefElem *def = lfirst(cell); 936 | 937 | if (!is_conninfo_option(def->defname, catalog, options)) 938 | { 939 | const Option *opt; 940 | StringInfoData buf; 941 | 942 | /* 943 | * Unknown option specified, complain about it. Provide a hint 944 | * with list of valid options for the object. 945 | */ 946 | initStringInfo(&buf); 947 | for (opt = options; opt->optname; opt++) 948 | if (catalog == InvalidOid || catalog == opt->optcontext) 949 | appendStringInfo(&buf, "%s%s", (buf.len > 0) ? ", " : "", 950 | opt->optname); 951 | 952 | ereport(ERROR, 953 | (errcode(ERRCODE_SYNTAX_ERROR), 954 | errmsg("invalid option \"%s\"", def->defname), 955 | errhint("Valid options in this context are: %s", buf.data))); 956 | 957 | return false; 958 | } 959 | } 960 | 961 | return true; 962 | } 963 | 964 | #if defined(ENABLE_MYSQL) || defined(ENABLE_ORACLE) || defined(ENABLE_SQLITE3) 965 | static void 966 | parse_options(List *args, const Option options[], const char *params[]) 967 | { 968 | int i; 969 | 970 | for (i = 0; options[i].optname; i++) 971 | { 972 | ListCell *cell; 973 | 974 | params[i] = NULL; 975 | foreach(cell, args) 976 | { 977 | DefElem *def = lfirst(cell); 978 | 979 | if (pg_strcasecmp(def->defname, options[i].optname) == 0) 980 | { 981 | params[i] = strVal(def->arg); 982 | break; 983 | } 984 | } 985 | } 986 | } 987 | #endif 988 | 989 | Datum 990 | dblink_mysql(PG_FUNCTION_ARGS) 991 | { 992 | static const Option mysql_options[] = { 993 | {"user", UserMappingRelationId}, 994 | {"password", UserMappingRelationId}, 995 | {"dbname", ForeignServerRelationId}, 996 | {"host", ForeignServerRelationId}, 997 | {"port", ForeignServerRelationId}, 998 | {NULL, InvalidOid} 999 | }; 1000 | 1001 | Oid catalog = PG_GETARG_OID(1); 1002 | 1003 | if (catalog == DBLINKOID) 1004 | { 1005 | #ifdef ENABLE_MYSQL 1006 | dblink_connection **connection; 1007 | const char *params[5]; 1008 | 1009 | connection = (dblink_connection **) PG_GETARG_POINTER(2); 1010 | if (!connection) 1011 | PG_RETURN_BOOL(false); 1012 | 1013 | parse_options((List *) PG_GETARG_POINTER(3), mysql_options, params); 1014 | 1015 | *connection = mylink_connect(params[0], params[1], params[2], 1016 | params[3], params[4] ? atoi(params[4]) : 0); 1017 | PG_RETURN_BOOL(*connection != NULL); 1018 | #else 1019 | elog(ERROR, "dblink.mysql is disabled"); 1020 | PG_RETURN_BOOL(false); 1021 | #endif 1022 | } 1023 | else 1024 | { 1025 | PG_RETURN_BOOL(validate_options( 1026 | PG_GETARG_DATUM(0), catalog, mysql_options)); 1027 | } 1028 | } 1029 | 1030 | Datum 1031 | dblink_oracle(PG_FUNCTION_ARGS) 1032 | { 1033 | static const Option oracle_options[] = { 1034 | {"user", UserMappingRelationId}, 1035 | {"password", UserMappingRelationId}, 1036 | {"dbname", ForeignServerRelationId}, 1037 | {"max_value_len", ForeignServerRelationId}, 1038 | {NULL, InvalidOid} 1039 | }; 1040 | 1041 | Oid catalog = PG_GETARG_OID(1); 1042 | 1043 | if (catalog == DBLINKOID) 1044 | { 1045 | #ifdef ENABLE_ORACLE 1046 | dblink_connection **connection; 1047 | const char *params[4]; 1048 | int64 val; 1049 | int32 max_value_len; 1050 | char *endptr; 1051 | 1052 | connection = (dblink_connection **) PG_GETARG_POINTER(2); 1053 | if (!connection) 1054 | PG_RETURN_BOOL(false); 1055 | 1056 | parse_options((List *) PG_GETARG_POINTER(3), oracle_options, params); 1057 | 1058 | /* get max_value_len */ 1059 | max_value_len = -1; 1060 | if (params[3]) 1061 | { 1062 | errno = 0; 1063 | val = strtol(params[3], &endptr, 0); 1064 | if (endptr == params[3] || errno == ERANGE || 1065 | val != (int64) ((int32) val)) 1066 | { 1067 | elog(ERROR, 1068 | "dblink.oracle: invalid value for parameter \"%s\": \"%s\"", 1069 | oracle_options[3].optname, params[3]); 1070 | PG_RETURN_BOOL(false); 1071 | } 1072 | max_value_len = (int32) val; 1073 | } 1074 | 1075 | *connection = oralink_connect(params[0], params[1], params[2], max_value_len); 1076 | PG_RETURN_BOOL(*connection != NULL); 1077 | #else 1078 | elog(ERROR, "dblink.oracle is disabled"); 1079 | PG_RETURN_BOOL(false); 1080 | #endif 1081 | } 1082 | else 1083 | { 1084 | PG_RETURN_BOOL(validate_options( 1085 | PG_GETARG_DATUM(0), catalog, oracle_options)); 1086 | } 1087 | } 1088 | 1089 | Datum 1090 | dblink_sqlite3(PG_FUNCTION_ARGS) 1091 | { 1092 | static const Option sqlite3_options[] = { 1093 | {"location", ForeignServerRelationId}, 1094 | {NULL, InvalidOid} 1095 | }; 1096 | 1097 | Oid catalog = PG_GETARG_OID(1); 1098 | 1099 | if (catalog == DBLINKOID) 1100 | { 1101 | #ifdef ENABLE_SQLITE3 1102 | dblink_connection **connection; 1103 | const char *params[1]; 1104 | 1105 | connection = (dblink_connection **) PG_GETARG_POINTER(2); 1106 | if (!connection) 1107 | PG_RETURN_BOOL(false); 1108 | 1109 | parse_options((List *) PG_GETARG_POINTER(3), sqlite3_options, params); 1110 | 1111 | *connection = sq3link_connect(params[0]); 1112 | PG_RETURN_BOOL(*connection != NULL); 1113 | #else 1114 | elog(ERROR, "dblink.sqlite3 is disabled"); 1115 | PG_RETURN_BOOL(false); 1116 | #endif 1117 | } 1118 | else 1119 | { 1120 | PG_RETURN_BOOL(validate_options( 1121 | PG_GETARG_DATUM(0), catalog, sqlite3_options)); 1122 | } 1123 | } 1124 | -------------------------------------------------------------------------------- /dblink.h: -------------------------------------------------------------------------------- 1 | /* 2 | * dblink.h 3 | * 4 | * Copyright (c) 2011-2025, NIPPON TELEGRAPH AND TELEPHONE CORPORATION 5 | */ 6 | #ifndef DBLINK_H 7 | #define DBLINK_H 8 | 9 | #define DBLINKOID 0xFFFFFFFF 10 | 11 | typedef enum dblink_command 12 | { 13 | DBLINK_BEGIN, 14 | DBLINK_COMMIT, 15 | DBLINK_ROLLBACK, 16 | DBLINK_XA_START, 17 | DBLINK_XA_PREPARE, 18 | DBLINK_XA_COMMIT, 19 | DBLINK_XA_ROLLBACK 20 | } dblink_command; 21 | 22 | typedef struct dblink_connection dblink_connection; 23 | typedef struct dblink_cursor dblink_cursor; 24 | typedef struct dblink_gtrid dblink_gtrid; 25 | 26 | /* 27 | * gtrid parts 28 | */ 29 | struct dblink_gtrid 30 | { 31 | int pid; /* process id */ 32 | int rand; /* random number */ 33 | }; 34 | 35 | /* 36 | * interface dblink_connection 37 | */ 38 | 39 | typedef void (*dblink_disconnect_t)(dblink_connection *conn); 40 | typedef int64 (*dblink_exec_t)(dblink_connection *conn, const char *sql); 41 | typedef dblink_cursor *(*dblink_open_t)(dblink_connection *conn, const char *sql, int32 fetchsize, int max_value_len); 42 | typedef dblink_cursor *(*dblink_call_t)(dblink_connection *conn, const char *func, int32 fetchsize, int max_value_len); 43 | typedef bool (*dblink_command_t)(dblink_connection *conn, dblink_command type); 44 | 45 | struct dblink_connection 46 | { 47 | dblink_disconnect_t disconnect; 48 | dblink_exec_t exec; 49 | dblink_open_t open; 50 | dblink_call_t call; 51 | dblink_command_t command; 52 | dblink_gtrid gtrid; 53 | }; 54 | 55 | /* 56 | * interface dblink_cursor 57 | */ 58 | 59 | typedef bool (*dblink_fetch_t)(dblink_cursor *cur, const char *values[]); 60 | typedef void (*dblink_close_t)(dblink_cursor *cur); 61 | 62 | struct dblink_cursor 63 | { 64 | dblink_fetch_t fetch; 65 | dblink_close_t close; 66 | int nfields; 67 | }; 68 | 69 | /* 70 | * internal utility functions 71 | */ 72 | 73 | extern void dblink_error(int sqlstate, 74 | const char *message, 75 | const char *detail, 76 | const char *hint, 77 | const char *context); 78 | extern void dblink_elog(int level, const char *message); 79 | extern void set_dblink_gtrid(char *str, 80 | size_t size, 81 | const char *format, 82 | dblink_connection base); 83 | 84 | #endif /* DBLINK_H */ 85 | -------------------------------------------------------------------------------- /dblink_internal.h: -------------------------------------------------------------------------------- 1 | /* 2 | * dblink_internal.h 3 | * 4 | * Copyright (c) 2025, NIPPON TELEGRAPH AND TELEPHONE CORPORATION 5 | */ 6 | #ifndef DBLINK_INTERNAL_H 7 | #define DBLINK_INTERNAL_H 8 | 9 | #ifdef ENABLE_MYSQL 10 | extern dblink_connection *mylink_connect( 11 | const char *user, 12 | const char *password, 13 | const char *dbname, 14 | const char *host, 15 | int port); 16 | #endif 17 | 18 | #ifdef ENABLE_ORACLE 19 | extern dblink_connection *oralink_connect( 20 | const char *user, 21 | const char *password, 22 | const char *dbname, 23 | int max_value_len); 24 | #endif 25 | 26 | #ifdef ENABLE_SQLITE3 27 | extern dblink_connection *sq3link_connect( 28 | const char *location); 29 | #endif 30 | 31 | #endif /* DBLINK_INTERNAL_H */ 32 | -------------------------------------------------------------------------------- /dblink_mysql.c: -------------------------------------------------------------------------------- 1 | /* 2 | * dblink_mysql.c 3 | * 4 | * Copyright (c) 2011-2025, NIPPON TELEGRAPH AND TELEPHONE CORPORATION 5 | */ 6 | #ifdef ENABLE_MYSQL 7 | 8 | #include "postgres.h" 9 | #include "lib/stringinfo.h" 10 | #include "mb/pg_wchar.h" 11 | 12 | #include "dblink.h" 13 | #include "dblink_internal.h" 14 | 15 | #include 16 | 17 | typedef struct mylink_connection 18 | { 19 | dblink_connection base; 20 | MYSQL conn; 21 | bool use_xa; /* true iff use automatic transaction */ 22 | } mylink_connection; 23 | 24 | typedef struct mylink_cursor 25 | { 26 | dblink_cursor base; 27 | MYSQL_RES *res; 28 | } mylink_cursor; 29 | 30 | static void mylink_disconnect(mylink_connection *conn); 31 | static int64 mylink_exec(mylink_connection *conn, const char *sql); 32 | static mylink_cursor *mylink_open(mylink_connection *conn, const char *sql, int fetchsize, int max_value_len); 33 | static mylink_cursor *mylink_call(mylink_connection *conn, const char *func, int fetchsize, int max_value_len); 34 | static bool mylink_command(mylink_connection *conn, dblink_command type); 35 | 36 | static bool mylink_fetch(mylink_cursor *cur, const char *values[]); 37 | static void mylink_close(mylink_cursor *cur); 38 | 39 | static mylink_connection *mylink_connection_new(void); 40 | static mylink_cursor *mylink_cursor_new(MYSQL_RES *res); 41 | static void mylink_error(MYSQL *conn, const char *message); 42 | 43 | static mylink_connection * 44 | mylink_connection_new(void) 45 | { 46 | mylink_connection *p; 47 | 48 | p = malloc(sizeof(mylink_connection)); 49 | p->base.disconnect = (dblink_disconnect_t) mylink_disconnect; 50 | p->base.exec = (dblink_exec_t) mylink_exec; 51 | p->base.open = (dblink_open_t) mylink_open; 52 | p->base.call = (dblink_call_t) mylink_call; 53 | p->base.command = (dblink_command_t) mylink_command; 54 | mysql_init(&p->conn); 55 | 56 | return p; 57 | } 58 | 59 | static mylink_cursor * 60 | mylink_cursor_new(MYSQL_RES *res) 61 | { 62 | mylink_cursor *p; 63 | 64 | p = malloc(sizeof(mylink_cursor)); 65 | p->base.fetch = (dblink_fetch_t) mylink_fetch; 66 | p->base.close = (dblink_close_t) mylink_close; 67 | p->base.nfields = mysql_num_fields(res); 68 | p->res = res; 69 | 70 | return p; 71 | } 72 | 73 | dblink_connection * 74 | mylink_connect(const char *user, const char *password, const char *dbname, 75 | const char *host, int port) 76 | { 77 | mylink_connection *conn; 78 | StringInfoData sql; 79 | const char *encoding; 80 | 81 | conn = mylink_connection_new(); 82 | if (NULL == mysql_real_connect(&conn->conn, 83 | host, user, password, dbname, port, NULL, 0)) 84 | { 85 | const char *message = mysql_error(&conn->conn); 86 | if (message) 87 | message = pstrdup(message); 88 | mylink_disconnect(conn); 89 | dblink_error(ERRCODE_SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION, 90 | "could not establish connection", message, NULL, NULL); 91 | } 92 | 93 | /* set character encodings */ 94 | encoding = GetDatabaseEncodingName(); 95 | 96 | initStringInfo(&sql); 97 | appendStringInfo(&sql, "set character_set_client = %s", encoding); 98 | mysql_query(&conn->conn, sql.data); 99 | 100 | resetStringInfo(&sql); 101 | appendStringInfo(&sql, "set character_set_results = %s", encoding); 102 | mysql_query(&conn->conn, sql.data); 103 | 104 | resetStringInfo(&sql); 105 | appendStringInfo(&sql, "set character_set_connection = %s", encoding); 106 | mysql_query(&conn->conn, sql.data); 107 | 108 | return (dblink_connection *) conn; 109 | } 110 | 111 | static void 112 | mylink_disconnect(mylink_connection *conn) 113 | { 114 | if (conn != NULL) 115 | { 116 | mysql_close(&conn->conn); 117 | free(conn); 118 | } 119 | } 120 | 121 | static int64 122 | mylink_exec(mylink_connection *conn, const char *sql) 123 | { 124 | if (mysql_query(&conn->conn, sql)) 125 | mylink_error(&conn->conn, "sql failed"); 126 | 127 | return (int64) mysql_affected_rows(&conn->conn); 128 | } 129 | 130 | static mylink_cursor * 131 | mylink_open(mylink_connection *conn, const char *sql, int fetchsize, int max_value_len) 132 | { 133 | MYSQL_RES *res; 134 | 135 | if (mysql_query(&conn->conn, sql)) 136 | mylink_error(&conn->conn, "sql failed"); 137 | 138 | /* next all rows */ 139 | res = mysql_store_result(&conn->conn); 140 | 141 | return mylink_cursor_new(res); 142 | } 143 | 144 | static mylink_cursor * 145 | mylink_call(mylink_connection *conn, const char *func, int fetchsize, int max_value_len) 146 | { 147 | StringInfoData sql; 148 | 149 | initStringInfo(&sql); 150 | appendStringInfoString(&sql, "SELECT "); 151 | appendStringInfoString(&sql, func); 152 | 153 | return mylink_open(conn, sql.data, fetchsize, max_value_len); 154 | } 155 | 156 | static bool 157 | mylink_command(mylink_connection *conn, dblink_command type) 158 | { 159 | char sql[256]; 160 | MYSQL_RES *res; 161 | 162 | switch (type) 163 | { 164 | case DBLINK_BEGIN: 165 | conn->use_xa = false; 166 | strlcpy(sql, "BEGIN", lengthof(sql)); 167 | break; 168 | case DBLINK_COMMIT: 169 | return mysql_commit(&conn->conn); 170 | case DBLINK_ROLLBACK: 171 | if (!conn->use_xa) 172 | return mysql_rollback(&conn->conn); 173 | 174 | set_dblink_gtrid(sql, lengthof(sql), 175 | "XA END '%s'", conn->base); 176 | if (mysql_query(&conn->conn, sql)) 177 | return false; 178 | /* XXX: need this? */ 179 | res = mysql_store_result(&conn->conn); 180 | mysql_free_result(res); 181 | 182 | set_dblink_gtrid(sql, lengthof(sql), 183 | "XA ROLLBACK '%s'", conn->base); 184 | break; 185 | case DBLINK_XA_START: 186 | conn->use_xa = true; 187 | set_dblink_gtrid(sql, lengthof(sql), 188 | "XA START '%s'", conn->base); 189 | break; 190 | case DBLINK_XA_PREPARE: 191 | set_dblink_gtrid(sql, lengthof(sql), 192 | "XA END '%s'", conn->base); 193 | if (mysql_query(&conn->conn, sql)) 194 | return false; 195 | /* XXX: need this? */ 196 | res = mysql_store_result(&conn->conn); 197 | mysql_free_result(res); 198 | 199 | set_dblink_gtrid(sql, lengthof(sql), 200 | "XA PREPARE '%s'", conn->base); 201 | break; 202 | case DBLINK_XA_COMMIT: 203 | set_dblink_gtrid(sql, lengthof(sql), 204 | "XA COMMIT '%s'", conn->base); 205 | break; 206 | case DBLINK_XA_ROLLBACK: 207 | set_dblink_gtrid(sql, lengthof(sql), 208 | "XA ROLLBACK '%s'", conn->base); 209 | break; 210 | default: 211 | return false; 212 | } 213 | 214 | if (mysql_query(&conn->conn, sql)) 215 | return false; 216 | /* XXX: need this? */ 217 | res = mysql_store_result(&conn->conn); 218 | mysql_free_result(res); 219 | 220 | return true; 221 | } 222 | 223 | static bool 224 | mylink_fetch(mylink_cursor *cur, const char *values[]) 225 | { 226 | MYSQL_ROW row; 227 | const unsigned long *lengths; 228 | int nfields; 229 | int c; 230 | 231 | nfields = mysql_num_fields(cur->res); 232 | row = mysql_fetch_row(cur->res); 233 | if (row == NULL) 234 | return false; 235 | 236 | lengths = mysql_fetch_lengths(cur->res); 237 | 238 | for (c = 0; c < nfields; c++) 239 | { 240 | if (row[c]) 241 | values[c] = pnstrdup(row[c], lengths[c]); 242 | else 243 | values[c] = NULL; 244 | } 245 | return true; 246 | } 247 | 248 | static void 249 | mylink_close(mylink_cursor *cur) 250 | { 251 | mysql_free_result(cur->res); 252 | } 253 | 254 | static void 255 | mylink_error(MYSQL *conn, const char *message) 256 | { 257 | int sqlstate = ERRCODE_EXTERNAL_ROUTINE_EXCEPTION; 258 | /* TODO: implement mapping */ 259 | /* sqlstate = mysql_errno(&conn->conn); */ 260 | 261 | dblink_error(sqlstate, message, mysql_error(conn), NULL, NULL); 262 | } 263 | 264 | #endif 265 | -------------------------------------------------------------------------------- /dblink_oracle.c: -------------------------------------------------------------------------------- 1 | /* 2 | * dblink_oracle.c 3 | * 4 | * Copyright (c) 2011-2025, NIPPON TELEGRAPH AND TELEPHONE CORPORATION 5 | */ 6 | #ifdef ENABLE_ORACLE 7 | 8 | #define text pg_text 9 | #include "c.h" 10 | #undef text 11 | 12 | #include "postgres.h" 13 | #include "utils/memutils.h" 14 | 15 | #include "utils/elog.h" 16 | #include "mb/pg_wchar.h" 17 | #include "dblink.h" 18 | #include "dblink_internal.h" 19 | #include 20 | #include 21 | 22 | #define COLUNM_SIZE 50 23 | #define CALL_COLUNM_SIZE 8192 24 | #define DEF_FETCH_SIZE 100 25 | 26 | typedef struct oralink_connection 27 | { 28 | dblink_connection base; 29 | OCIEnv *env; 30 | OCIError *error; 31 | OCISvcCtx *svcctx; 32 | OCITrans *trans; 33 | OCIServer *server; 34 | OCISession *session; 35 | int max_value_len; 36 | sword trans_stat; 37 | } oralink_connection; 38 | 39 | typedef struct oralink_cursor 40 | { 41 | dblink_cursor base; 42 | OCIStmt *stmt; 43 | OCIStmt *org_stmt; /* use dblink_call() */ 44 | OCIDefine **defnhp; 45 | char **values; 46 | sb2 **indicator; 47 | ub4 *allocsize; 48 | sb4 *data_type; 49 | OCILobLocator **lob_loc; 50 | int max_value_len; 51 | sb4 fetchsize; 52 | sb4 rowcount; 53 | int current; 54 | sword status; 55 | oralink_connection *conn; 56 | } oralink_cursor; 57 | 58 | static void oralink_disconnect(oralink_connection *conn); 59 | static int64 oralink_exec(oralink_connection *conn, const char *sql); 60 | static oralink_cursor *oralink_open(oralink_connection *conn, const char *sql, int fetchsize, int max_value_len); 61 | static bool oralink_command(oralink_connection *conn, dblink_command type); 62 | 63 | static bool oralink_fetch(oralink_cursor *cur, const char *values[]); 64 | static void oralink_close(oralink_cursor *cur); 65 | static oralink_cursor *oralink_call(oralink_connection *conn, const char *func, int fetchsize, int max_value_len); 66 | 67 | static oralink_connection *oralink_connection_new(void); 68 | static oralink_cursor *oralink_cursor_new(void); 69 | static void oralink_lobread(oralink_cursor *cur, int field); 70 | static void oralink_error(OCIError *errhp, sword status); 71 | static void oralink_elog(OCIError *errhp, sword status); 72 | static void oralink_message(char *message, int size, OCIError *errhp, sword status); 73 | static void *oralink_malloc(size_t size); 74 | static void *oralink_calloc(size_t nmemb, size_t size); 75 | static void *oralink_realloc(void *ptr, size_t size); 76 | static void oralink_free(void *ptr); 77 | 78 | static oralink_connection * 79 | oralink_connection_new(void) 80 | { 81 | oralink_connection *p; 82 | 83 | p = calloc(sizeof(oralink_connection), 1); 84 | p->base.disconnect = (dblink_disconnect_t) oralink_disconnect; 85 | p->base.exec = (dblink_exec_t) oralink_exec; 86 | p->base.open = (dblink_open_t) oralink_open; 87 | p->base.call = (dblink_call_t) oralink_call; 88 | p->base.command = (dblink_command_t) oralink_command; 89 | 90 | return p; 91 | } 92 | 93 | static oralink_cursor * 94 | oralink_cursor_new(void) 95 | { 96 | oralink_cursor *p; 97 | 98 | p = oralink_calloc(sizeof(oralink_cursor), 1); 99 | p->base.fetch = (dblink_fetch_t) oralink_fetch; 100 | p->base.close = (dblink_close_t) oralink_close; 101 | p->base.nfields = 0; 102 | 103 | return p; 104 | } 105 | 106 | static ub2 107 | oralink_charset_id(pg_enc pg_encode) 108 | { 109 | OCIEnv *envp; 110 | ub2 charset_id; 111 | static char *ora_enc[] = { 112 | "US7ASCII", /* SQL/ASCII */ 113 | "JA16EUC", /* EUC for Japanese */ 114 | "ZHT32EUC", /* EUC for Chinese */ 115 | 0, /* EUC for Korean */ 116 | 0, /* EUC for Taiwan */ 117 | 0, /* EUC-JIS-2004 */ 118 | "UTF8", /* Unicode UTF8 */ 119 | 0, /* Mule internal code */ 120 | "WE8ISO8859P1", /* ISO-8859-1 Latin 1 */ 121 | "EE8ISO8859P2", /* ISO-8859-2 Latin 2 */ 122 | "SE8ISO8859P3", /* ISO-8859-3 Latin 3 */ 123 | "NEE8ISO8859P4", /* ISO-8859-4 Latin 4 */ 124 | "WE8ISO8859P9", /* ISO-8859-9 Latin 5 */ 125 | "NE8ISO8859P10", /* ISO-8859-10 Latin6 */ 126 | "BLT8ISO8859P13", /* ISO-8859-13 Latin7 */ 127 | "CEL8ISO8859P14", /* ISO-8859-14 Latin8 */ 128 | "WE8ISO8859P15", /* ISO-8859-15 Latin9 */ 129 | 0, /* ISO-8859-16 Latin10 */ 130 | "AR8MSWIN1256", /* windows-1256 */ 131 | "VN8MSWIN1258", /* Windows-1258 */ 132 | 0, /* (MS-DOS CP866) */ 133 | 0, /* windows-874 */ 134 | "CL8KOI8R", /* KOI8-R */ 135 | "CL8MSWIN1251", /* windows-1251 */ 136 | "WE8MSWIN1252", /* windows-1252 */ 137 | "CL8ISO8859P5", /* ISO-8859-5 */ 138 | "AR8ISO8859P6", /* ISO-8859-6 */ 139 | "EL8ISO8859P7", /* ISO-8859-7 */ 140 | "IW8ISO8859P8", /* ISO-8859-8 */ 141 | "EE8MSWIN1250", /* windows-1250 */ 142 | "EL8MSWIN1253", /* windows-1253 */ 143 | "TR8MSWIN1254", /* windows-1254 */ 144 | "IW8MSWIN1255", /* windows-1255 */ 145 | "BLT8MSWIN1257", /* windows-1257 */ 146 | "CL8KOI8U", /* KOI8-U */ 147 | "JA16SJISTILDE", /* Shift JIS (Winindows-932) */ 148 | "ZHT16BIG5", /* Big5 (Windows-950) */ 149 | "ZHS16GBK", /* GBK (Windows-936) */ 150 | 0, /* UHC (Windows-949) */ 151 | 0, /* GB18030 */ 152 | 0, /* EUC for Korean JOHAB */ 153 | 0 /* Shift-JIS-2004 */ 154 | }; 155 | 156 | if (pg_encode >= sizeof(ora_enc) / sizeof(char *)) 157 | return 0; 158 | if (OCIEnvCreate(&envp, OCI_DEFAULT, NULL, 0, 0, 0, 0, NULL)) 159 | return 0; 160 | charset_id = OCINlsCharSetNameToId(envp, (oratext *)ora_enc[pg_encode]); 161 | OCIHandleFree(envp, OCI_HTYPE_ENV); 162 | 163 | return charset_id; 164 | } 165 | 166 | dblink_connection * 167 | oralink_connect(const char *user, 168 | const char *password, 169 | const char *dbname, 170 | int max_value_len) 171 | { 172 | oralink_connection *conn; 173 | sword status; 174 | ub2 charsetid; 175 | 176 | conn = oralink_connection_new(); 177 | 178 | if (max_value_len <= 0) 179 | conn->max_value_len = 0; 180 | else 181 | conn->max_value_len = max_value_len; 182 | 183 | charsetid = oralink_charset_id(GetDatabaseEncoding()); 184 | status = OCIEnvNlsCreate(&conn->env, OCI_DEFAULT, 185 | NULL, 0, 0, 0, 0, NULL, charsetid, charsetid); 186 | if (status != OCI_SUCCESS) 187 | return NULL; 188 | 189 | /* init error handle */ 190 | OCIHandleAlloc(conn->env, (dvoid **)&conn->error, OCI_HTYPE_ERROR, 0, NULL); 191 | 192 | /* init server handle & attach */ 193 | OCIHandleAlloc(conn->env, (void **) &conn->server, 194 | OCI_HTYPE_SERVER, 0, NULL); 195 | OCIServerAttach(conn->server, conn->error, 196 | (OraText *) dbname, dbname ? strlen(dbname) : 0, OCI_DEFAULT); 197 | 198 | /* set the external name and internal name in server handle */ 199 | OCIAttrSet(conn->server, OCI_HTYPE_SERVER, 200 | "dblink_exname", strlen("dblink_exname"), 201 | OCI_ATTR_EXTERNAL_NAME, conn->error); 202 | OCIAttrSet(conn->server, OCI_HTYPE_SERVER, 203 | "dblink_inname", strlen("dblink_inname"), 204 | OCI_ATTR_INTERNAL_NAME, conn->error); 205 | 206 | /* init user session handle & set param */ 207 | OCIHandleAlloc(conn->env, (void **) &conn->session, 208 | OCI_HTYPE_SESSION, 0, NULL); 209 | OCIAttrSet(conn->session, OCI_HTYPE_SESSION, 210 | (char *) user, user ? strlen(user) : 0, 211 | OCI_ATTR_USERNAME, conn->error); 212 | OCIAttrSet(conn->session, OCI_HTYPE_SESSION, 213 | (char *) password, password ? strlen(password) : 0, 214 | OCI_ATTR_PASSWORD, conn->error); 215 | 216 | /* init service context handle & set param */ 217 | OCIHandleAlloc(conn->env, (void **) &conn->svcctx, 218 | OCI_HTYPE_SVCCTX, 0, NULL); 219 | OCIAttrSet(conn->svcctx, OCI_HTYPE_SVCCTX, 220 | conn->server, 0, OCI_ATTR_SERVER, conn->error); 221 | OCIHandleAlloc(conn->env, (void **) &conn->trans, OCI_HTYPE_TRANS, 0, NULL); 222 | OCIAttrSet(conn->svcctx, OCI_HTYPE_SVCCTX, 223 | conn->trans, 0, OCI_ATTR_TRANS, conn->error); 224 | 225 | status = OCISessionBegin (conn->svcctx, conn->error, conn->session, 226 | OCI_CRED_RDBMS, 0); 227 | if (status != OCI_SUCCESS && status != OCI_SUCCESS_WITH_INFO) 228 | { 229 | char message[1024]; 230 | oralink_message(message, lengthof(message), conn->error, status); 231 | oralink_disconnect(conn); 232 | dblink_error(ERRCODE_SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION, 233 | "could not establish connection", message, NULL, NULL); 234 | } 235 | if (status == OCI_SUCCESS_WITH_INFO) 236 | oralink_elog(conn->error, status); 237 | 238 | OCIAttrSet(conn->svcctx, OCI_HTYPE_SVCCTX, 239 | conn->session, 0, OCI_ATTR_SESSION, conn->error); 240 | 241 | return (dblink_connection *) conn; 242 | } 243 | 244 | static void 245 | oralink_disconnect(oralink_connection *conn) 246 | { 247 | sword status; 248 | 249 | if (conn == NULL) 250 | return; 251 | 252 | status = OCISessionEnd(conn->svcctx, conn->error, conn->session, 0); 253 | if (status != OCI_SUCCESS) 254 | oralink_elog(conn->error, status); 255 | 256 | OCIServerDetach(conn->server, conn->error, OCI_DEFAULT); 257 | 258 | /* cleanup handles */ 259 | if (conn->error) 260 | OCIHandleFree(conn->error, OCI_HTYPE_ERROR); 261 | if (conn->svcctx) 262 | OCIHandleFree(conn->svcctx, OCI_HTYPE_SVCCTX); 263 | if (conn->server) 264 | OCIHandleFree(conn->server, OCI_HTYPE_SERVER); 265 | if (conn->session) 266 | OCIHandleFree(conn->session, OCI_HTYPE_SESSION); 267 | if (conn->trans) 268 | OCIHandleFree(conn->trans, OCI_HTYPE_TRANS); 269 | if (conn->env) 270 | OCIHandleFree(conn->env, OCI_HTYPE_ENV); 271 | 272 | free(conn); 273 | } 274 | 275 | static OCIStmt * 276 | stmt_create(oralink_connection *conn, const char *sql) 277 | { 278 | OCIStmt *stmt = NULL; 279 | 280 | OCIHandleAlloc(conn->env, (dvoid **) &stmt, OCI_HTYPE_STMT, 0, NULL); 281 | OCIStmtPrepare(stmt, conn->error, (OraText *) sql, 282 | strlen(sql), OCI_NTV_SYNTAX, OCI_DEFAULT); 283 | 284 | return stmt; 285 | } 286 | 287 | static int64 288 | oralink_exec(oralink_connection *conn, const char *sql) 289 | { 290 | sword status; 291 | OCIStmt *stmt; 292 | int affected_rows; 293 | 294 | stmt = stmt_create(conn, sql); 295 | status = OCIStmtExecute(conn->svcctx, stmt, conn->error, 296 | 1, 0, NULL, NULL, OCI_DEFAULT); 297 | if (status != OCI_SUCCESS && status != OCI_SUCCESS_WITH_INFO) 298 | { 299 | oralink_error(conn->error, status); 300 | } 301 | if (status == OCI_SUCCESS_WITH_INFO) 302 | oralink_elog(conn->error, status); 303 | 304 | OCIAttrGet(stmt, OCI_HTYPE_STMT, &affected_rows, 305 | 0, OCI_ATTR_ROW_COUNT, conn->error); 306 | 307 | OCIHandleFree(stmt, OCI_HTYPE_STMT); 308 | 309 | return affected_rows; 310 | } 311 | 312 | static oralink_cursor * 313 | oralink_open(oralink_connection *conn, const char *sql, int fetchsize, int max_value_len) 314 | { 315 | oralink_cursor *cur; 316 | sword status; 317 | int i; 318 | ub4 mode; 319 | 320 | cur = oralink_cursor_new(); 321 | cur->conn = conn; 322 | cur->stmt = stmt_create(conn, sql); 323 | cur->fetchsize = (fetchsize > 0) ? fetchsize : DEF_FETCH_SIZE; 324 | cur->rowcount = 0; 325 | cur->current = 0; 326 | 327 | if (max_value_len < 0) 328 | cur->max_value_len = conn->max_value_len; 329 | else 330 | cur->max_value_len = max_value_len; 331 | 332 | if (cur->max_value_len <= 0) 333 | mode = OCI_STMT_SCROLLABLE_READONLY; 334 | else 335 | mode = OCI_DEFAULT; 336 | 337 | status = OCIStmtExecute(conn->svcctx, cur->stmt, conn->error, 338 | 0, 0, NULL, NULL, mode); 339 | if (status != OCI_SUCCESS && status != OCI_SUCCESS_WITH_INFO) 340 | { 341 | oralink_close(cur); 342 | oralink_error(conn->error, status); 343 | } 344 | if (status == OCI_SUCCESS_WITH_INFO) 345 | oralink_elog(conn->error, status); 346 | 347 | status = OCIAttrGet(cur->stmt, OCI_HTYPE_STMT, 348 | &cur->base.nfields, 0, OCI_ATTR_PARAM_COUNT, conn->error); 349 | if (status != OCI_SUCCESS && status != OCI_SUCCESS_WITH_INFO) 350 | { 351 | oralink_close(cur); 352 | oralink_error(conn->error, status); 353 | } 354 | if (status == OCI_SUCCESS_WITH_INFO) 355 | oralink_elog(conn->error, status); 356 | 357 | /* Declare define handle */ 358 | cur->defnhp = oralink_calloc(cur->base.nfields * sizeof(OCIDefine *), 1); 359 | cur->values = oralink_calloc(cur->base.nfields * sizeof(char *), 1); 360 | cur->indicator = oralink_calloc(cur->base.nfields * sizeof(sb2 *), 1); 361 | cur->allocsize = oralink_calloc(cur->base.nfields * sizeof(ub4), 1); 362 | cur->data_type = oralink_calloc(cur->base.nfields * sizeof(sb4), 1); 363 | cur->lob_loc = oralink_calloc(cur->base.nfields * sizeof(OCILobLocator *), 1); 364 | 365 | for (i = 0; i < cur->base.nfields; i++) 366 | { 367 | OCIParam *hp = 0; 368 | 369 | status = OCIParamGet(cur->stmt, OCI_HTYPE_STMT, 370 | conn->error, (dvoid **)&hp, i + 1); 371 | status = OCIAttrGet(hp, OCI_DTYPE_PARAM, 372 | &cur->data_type[i], 0, OCI_ATTR_DATA_TYPE, conn->error); 373 | 374 | switch (cur->data_type[i]) 375 | { 376 | case SQLT_CLOB: 377 | OCIDescriptorAlloc((dvoid *)conn->env, 378 | (dvoid **)&cur->lob_loc[i], 379 | (ub4)OCI_DTYPE_LOB, (size_t)0, (dvoid **)0); 380 | cur->fetchsize = 1; 381 | break; 382 | case SQLT_LNG: 383 | case SQLT_BIN: 384 | case SQLT_LBI: 385 | case SQLT_BLOB: 386 | case SQLT_BFILE: 387 | dblink_error(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION, 388 | "Not supported data type.", NULL, NULL, NULL); 389 | break; 390 | default: 391 | cur->data_type[i] = SQLT_STR; 392 | break; 393 | } 394 | } 395 | 396 | for (i = 0; i < cur->base.nfields; i++) 397 | { 398 | if (cur->max_value_len <= 0) 399 | cur->allocsize[i] = COLUNM_SIZE; 400 | else 401 | cur->allocsize[i] = cur->max_value_len + 1; 402 | 403 | cur->values[i] = oralink_malloc(cur->allocsize[i] * cur->fetchsize); 404 | cur->indicator[i] = oralink_calloc(sizeof(sb2) * cur->fetchsize, 1); 405 | 406 | status = OCIDefineByPos(cur->stmt, 407 | &cur->defnhp[i], 408 | conn->error, 409 | (ub4) i + 1, 410 | (cur->data_type[i] == SQLT_STR) ? (dvoid *)cur->values[i] 411 | : (dvoid *)&cur->lob_loc[i], 412 | (cur->data_type[i] == SQLT_STR) ? cur->allocsize[i] : 0, 413 | cur->data_type[i], 414 | (dvoid *) cur->indicator[i], 415 | (ub2 *) 0, (ub2 *) 0, OCI_DEFAULT); 416 | 417 | if (status != OCI_SUCCESS && status != OCI_SUCCESS_WITH_INFO) 418 | { 419 | oralink_close(cur); 420 | oralink_error(conn->error, status); 421 | } 422 | if (status == OCI_SUCCESS_WITH_INFO) 423 | oralink_elog(conn->error, status); 424 | } 425 | 426 | return cur; 427 | } 428 | 429 | static bool 430 | oralink_fetch(oralink_cursor *cur, const char *values[]) 431 | { 432 | int i; 433 | int n; 434 | int rev_count = 0; 435 | ub4 fetch_mode = OCI_FETCH_NEXT; 436 | 437 | while (cur->current == 0) 438 | { 439 | text errbuf[512]; 440 | sb4 errcode = 0; 441 | int realloc_count = 0; 442 | 443 | for (i = 0; i < cur->base.nfields; i++) 444 | memset(cur->indicator[i], 0, sizeof(sb2) * cur->fetchsize); 445 | 446 | cur->status = OCIStmtFetch2(cur->stmt, cur->conn->error, 447 | cur->fetchsize, fetch_mode, rev_count, OCI_DEFAULT); 448 | 449 | if (cur->status == OCI_ERROR) 450 | { 451 | OCIErrorGet ((void *) cur->conn->error, (ub4) 1, (text *) NULL, 452 | &errcode, errbuf, (ub4) sizeof(errbuf), (ub4) OCI_HTYPE_ERROR); 453 | if (errcode != 1406) 454 | oralink_error(cur->conn->error, cur->status); 455 | } 456 | 457 | if (cur->status == OCI_NO_DATA) 458 | { 459 | ub4 size = sizeof(size); 460 | OCIAttrGet(cur->stmt, OCI_HTYPE_STMT, (dvoid *)&cur->rowcount, 461 | (ub4 *)&size, (ub4)OCI_ATTR_ROWS_FETCHED, cur->conn->error); 462 | 463 | if (cur->rowcount == 0) 464 | return false; 465 | } 466 | else 467 | cur->rowcount = cur->fetchsize; 468 | 469 | for (i = 0; i < cur->base.nfields; i++) 470 | { 471 | sb2 w_indicator = 0; 472 | 473 | for (n = 0; n < cur->rowcount; n++) 474 | { 475 | if (*(cur->indicator[i] + n) == -2) 476 | { 477 | w_indicator = -2; 478 | break; 479 | } 480 | if (w_indicator < *(cur->indicator[i] + n)) 481 | w_indicator = *(cur->indicator[i] + n); 482 | } 483 | if (w_indicator > 0) 484 | if (cur->allocsize[i] >= w_indicator + 1) 485 | cur->allocsize[i]++; 486 | else 487 | cur->allocsize[i] = w_indicator + 1; 488 | else if (w_indicator == -2) 489 | cur->allocsize[i] += USHRT_MAX; 490 | else 491 | continue; 492 | 493 | realloc_count++; 494 | cur->values[i] = oralink_realloc(cur->values[i], 495 | cur->allocsize[i] * cur->fetchsize); 496 | cur->status = OCIDefineByPos(cur->stmt, 497 | &cur->defnhp[i], 498 | cur->conn->error, 499 | (ub4)i + 1, 500 | (dvoid *)cur->values[i], 501 | cur->allocsize[i], 502 | SQLT_STR, 503 | (dvoid *)cur->indicator[i], 504 | (ub2 *) 0, (ub2 *) 0, OCI_DEFAULT); 505 | if (cur->status != OCI_SUCCESS && 506 | cur->status != OCI_SUCCESS_WITH_INFO) 507 | { 508 | oralink_error(cur->conn->error, cur->status); 509 | } 510 | if (cur->status == OCI_SUCCESS_WITH_INFO) 511 | oralink_elog(cur->conn->error, cur->status); 512 | } 513 | if (realloc_count == 0) 514 | break; 515 | 516 | fetch_mode = OCI_FETCH_RELATIVE; 517 | rev_count = 1 - cur->rowcount; 518 | } 519 | 520 | if (cur->status == OCI_NO_DATA && cur->current == cur->rowcount) 521 | { 522 | cur->current = 0; 523 | return false; 524 | } 525 | 526 | for (i = 0; i < cur->base.nfields; i++) 527 | { 528 | if (*(cur->indicator[i] + cur->current) == -1) 529 | { 530 | values[i] = 0; 531 | continue; 532 | } 533 | 534 | if (cur->data_type[i] == SQLT_STR) 535 | values[i] = cur->values[i] + cur->allocsize[i] * cur->current; 536 | else 537 | { 538 | oralink_lobread(cur, i); 539 | values[i] = cur->values[i]; 540 | } 541 | } 542 | 543 | cur->current++; 544 | if (cur->current == cur->fetchsize) 545 | cur->current = 0; 546 | 547 | return true; 548 | } 549 | 550 | static void 551 | oralink_close(oralink_cursor *cur) 552 | { 553 | int i; 554 | 555 | if (cur->lob_loc) 556 | { 557 | for (i = 0; i < cur->base.nfields; i++) 558 | { 559 | if (cur->lob_loc[i]) 560 | OCIDescriptorFree(cur->lob_loc[i], OCI_DTYPE_LOB); 561 | } 562 | oralink_free(cur->lob_loc); 563 | } 564 | if (cur->stmt) 565 | OCIHandleFree(cur->stmt, OCI_HTYPE_STMT); 566 | if (cur->org_stmt) 567 | OCIHandleFree(cur->org_stmt, OCI_HTYPE_STMT); 568 | if (cur->defnhp) 569 | oralink_free(cur->defnhp); 570 | if (cur->values) 571 | { 572 | for (i = 0; i < cur->base.nfields; i++) 573 | { 574 | if (cur->values[i]) 575 | oralink_free(cur->values[i]); 576 | } 577 | oralink_free(cur->values); 578 | } 579 | if (cur->indicator) 580 | { 581 | for (i = 0; i < cur->base.nfields; i++) 582 | { 583 | if (cur->indicator[i]) 584 | oralink_free(cur->indicator[i]); 585 | } 586 | oralink_free(cur->indicator); 587 | } 588 | if (cur->allocsize) 589 | oralink_free(cur->allocsize); 590 | if (cur->data_type) 591 | oralink_free(cur->data_type); 592 | oralink_free(cur); 593 | } 594 | 595 | static oralink_cursor * 596 | oralink_call(oralink_connection *conn, const char *func, int fetchsize, int max_value_len) 597 | { 598 | oralink_cursor *cur; 599 | sword status; 600 | int i; 601 | char *str; 602 | char *sql; 603 | char *p; 604 | OCIBind *bnd1p; 605 | 606 | str = strdup(func); 607 | if ((p = strchr(str, '('))) 608 | *p++ = 0; 609 | else 610 | return false; 611 | /* sql needs extra length (here rounded to 64) for fixed content */ 612 | sql = calloc(strlen(str) + strlen(p) + 64, 1); 613 | snprintf(sql, strlen(str) + strlen(p) + 64, "BEGIN %s(:cursor, %s; END;", 614 | str, p); 615 | free(str); 616 | 617 | cur = oralink_cursor_new(); 618 | cur->conn = conn; 619 | cur->org_stmt = stmt_create(conn, sql); 620 | cur->fetchsize = (fetchsize > 0) ? fetchsize : 1; 621 | cur->rowcount = 0; 622 | cur->current = 0; 623 | free(sql); 624 | 625 | if (max_value_len < 0) 626 | cur->max_value_len = conn->max_value_len; 627 | else 628 | cur->max_value_len = max_value_len; 629 | 630 | OCIHandleAlloc(conn->env, (dvoid **)&cur->stmt, OCI_HTYPE_STMT, 0, NULL); 631 | status = OCIBindByName(cur->org_stmt, (OCIBind **)&bnd1p, 632 | conn->error, (OraText *)":cursor", -1, 633 | (dvoid *)&cur->stmt, (sb4)0, SQLT_RSET, (dvoid *)0, 634 | (ub2 *)0, (ub2 *)0, (ub4)0, (ub4 *)0, (ub4)OCI_DEFAULT); 635 | if (status != OCI_SUCCESS && status != OCI_SUCCESS_WITH_INFO) 636 | { 637 | oralink_close(cur); 638 | oralink_error(conn->error, status); 639 | } 640 | 641 | status = OCIStmtExecute(conn->svcctx, cur->org_stmt, conn->error, 642 | 1, 0, NULL, NULL, OCI_DEFAULT); 643 | if (status != OCI_SUCCESS && status != OCI_SUCCESS_WITH_INFO) 644 | { 645 | oralink_close(cur); 646 | oralink_error(conn->error, status); 647 | } 648 | if (status == OCI_SUCCESS_WITH_INFO) 649 | oralink_elog(conn->error, status); 650 | 651 | status = OCIAttrGet(cur->stmt, OCI_HTYPE_STMT, 652 | &cur->base.nfields, 0, OCI_ATTR_PARAM_COUNT, conn->error); 653 | if (status != OCI_SUCCESS && status != OCI_SUCCESS_WITH_INFO) 654 | { 655 | oralink_close(cur); 656 | oralink_error(conn->error, status); 657 | } 658 | 659 | cur->defnhp = oralink_calloc(cur->base.nfields * sizeof(OCIDefine *), 1); 660 | cur->values = oralink_calloc(cur->base.nfields * sizeof(char *), 1); 661 | cur->indicator = oralink_calloc(cur->base.nfields * sizeof(sb2 *), 1); 662 | cur->allocsize = oralink_calloc(cur->base.nfields * sizeof(ub4), 1); 663 | cur->data_type = oralink_calloc(cur->base.nfields * sizeof(sb4), 1); 664 | cur->lob_loc = oralink_calloc(cur->base.nfields * sizeof(OCILobLocator *), 1); 665 | 666 | for (i = 0; i < cur->base.nfields; i++) 667 | { 668 | if (cur->max_value_len <= 0) 669 | cur->allocsize[i] = CALL_COLUNM_SIZE; 670 | else 671 | cur->allocsize[i] = cur->max_value_len + 1; 672 | 673 | cur->values[i] = oralink_malloc(cur->allocsize[i] * cur->fetchsize); 674 | cur->indicator[i] = oralink_calloc(sizeof(sb2) * cur->fetchsize, 1); 675 | cur->data_type[i] = SQLT_STR; 676 | 677 | status = OCIDefineByPos(cur->stmt, 678 | &cur->defnhp[i], 679 | conn->error, 680 | (ub4) i + 1, 681 | (dvoid *) cur->values[i], 682 | cur->allocsize[i], 683 | cur->data_type[i], 684 | (dvoid *) cur->indicator[i], 685 | (ub2 *) 0, (ub2 *) 0, OCI_DEFAULT); 686 | 687 | if (status != OCI_SUCCESS && status != OCI_SUCCESS_WITH_INFO) 688 | { 689 | oralink_close(cur); 690 | oralink_error(conn->error, status); 691 | } 692 | if (status == OCI_SUCCESS_WITH_INFO) 693 | oralink_elog(conn->error, status); 694 | } 695 | 696 | return cur; 697 | } 698 | 699 | static bool 700 | oralink_command(oralink_connection *conn, dblink_command type) 701 | { 702 | sword status; 703 | XID gxid; 704 | char str[XIDDATASIZE]; 705 | int i; 706 | 707 | switch (type) 708 | { 709 | case DBLINK_BEGIN: 710 | status = OCITransStart(conn->svcctx, conn->error, 60, OCI_TRANS_NEW); 711 | break; 712 | case DBLINK_XA_START: 713 | /* set format id = 1000 */ 714 | gxid.formatID = 1000; 715 | 716 | /* set global transaction id from gtrid parts */ 717 | set_dblink_gtrid(str, lengthof(str), "%s", conn->base); 718 | gxid.gtrid_length = strlen(str); 719 | 720 | for (i = 0; i < gxid.gtrid_length; i++) 721 | gxid.data[i] = str[i]; 722 | 723 | /* set branch id = 1 */ 724 | gxid.bqual_length = 1; 725 | gxid.data[i] = 1; 726 | 727 | /* set XID */ 728 | OCIAttrSet(conn->trans, OCI_HTYPE_TRANS, &gxid, sizeof(XID), 729 | OCI_ATTR_XID, conn->error); 730 | 731 | status = OCITransStart(conn->svcctx, conn->error, 60, OCI_TRANS_NEW); 732 | conn->trans_stat = OCI_SUCCESS; 733 | break; 734 | case DBLINK_COMMIT: 735 | status = OCITransCommit(conn->svcctx, conn->error, OCI_DEFAULT); 736 | break; 737 | case DBLINK_ROLLBACK: 738 | status = OCITransRollback(conn->svcctx, conn->error, OCI_DEFAULT); 739 | break; 740 | case DBLINK_XA_PREPARE: 741 | status = OCITransPrepare(conn->svcctx, conn->error, OCI_DEFAULT); 742 | conn->trans_stat = status; 743 | break; 744 | case DBLINK_XA_COMMIT: 745 | if (conn->trans_stat != OCI_SUCCESS_WITH_INFO) 746 | status = OCITransCommit(conn->svcctx, 747 | conn->error, OCI_TRANS_TWOPHASE); 748 | else 749 | status = OCI_SUCCESS; 750 | break; 751 | case DBLINK_XA_ROLLBACK: 752 | status = OCITransForget(conn->svcctx, conn->error, OCI_DEFAULT); 753 | break; 754 | default: 755 | return false; 756 | } 757 | 758 | if (status != OCI_SUCCESS && status != OCI_SUCCESS_WITH_INFO) 759 | { 760 | oralink_elog(conn->error, status); 761 | return false; 762 | } 763 | 764 | return true; 765 | } 766 | 767 | static void 768 | oralink_lobread(oralink_cursor *cur, int field) 769 | { 770 | #define LOB_ALLOC_SIZE 32768 771 | #define MAX_LOB_SIZE 0xFFFFFFFFU 772 | oralink_connection *conn = cur->conn; 773 | sword status; 774 | ub4 amount = MAX_LOB_SIZE; 775 | ub4 offset = 0; 776 | char *ptr; 777 | 778 | status = OCILobOpen(conn->svcctx, conn->error, 779 | cur->lob_loc[field], OCI_LOB_READONLY); 780 | if (status != OCI_SUCCESS && status != OCI_SUCCESS_WITH_INFO) 781 | oralink_error(conn->error, status); 782 | 783 | if (cur->allocsize[field] < LOB_ALLOC_SIZE) 784 | { 785 | cur->allocsize[field] = LOB_ALLOC_SIZE; 786 | cur->values[field] = oralink_realloc(cur->values[field], cur->allocsize[field]); 787 | } 788 | 789 | while (1) 790 | { 791 | status = OCILobRead(conn->svcctx, conn->error, cur->lob_loc[field], 792 | &amount, offset + 1, (dvoid *)(cur->values[field] + offset), 793 | cur->allocsize[field] - offset, (dvoid *)0, 794 | 0, (ub2)0, (ub1)SQLCS_IMPLICIT); 795 | if (status != OCI_NEED_DATA) 796 | { 797 | if (status != OCI_SUCCESS && status != OCI_SUCCESS_WITH_INFO) 798 | oralink_error(conn->error, status); 799 | 800 | *(cur->values[field] + offset + amount) = 0; 801 | break; 802 | } 803 | if (MAX_LOB_SIZE - cur->allocsize[field] < LOB_ALLOC_SIZE) 804 | { 805 | OCIBreak(conn->svcctx, conn->error); 806 | OCILobClose(conn->svcctx, conn->error, cur->lob_loc[field]); 807 | dblink_error(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION, 808 | "LOB data buffer overflowed.", NULL, NULL, NULL); 809 | } 810 | cur->allocsize[field] += LOB_ALLOC_SIZE; 811 | ptr = oralink_realloc(cur->values[field], cur->allocsize[field]); 812 | if (ptr == NULL) 813 | { 814 | OCIBreak(conn->svcctx, conn->error); 815 | OCILobClose(conn->svcctx, conn->error, cur->lob_loc[field]); 816 | dblink_error(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION, 817 | "LOB data buffer allocate error.", NULL, NULL, NULL); 818 | } 819 | cur->values[field] = ptr; 820 | offset += amount; 821 | } 822 | 823 | OCILobClose(conn->svcctx, conn->error, cur->lob_loc[field]); 824 | } 825 | 826 | static void 827 | oralink_error(OCIError *errhp, sword status) 828 | { 829 | char message[1024]; 830 | 831 | oralink_message(message, lengthof(message), errhp, status); 832 | dblink_error(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION, message, NULL, NULL, NULL); 833 | } 834 | 835 | static void 836 | oralink_elog(OCIError *errhp, sword status) 837 | { 838 | char message[1024]; 839 | 840 | oralink_message(message, lengthof(message), errhp, status); 841 | dblink_elog(LOG, message); 842 | } 843 | 844 | static void 845 | oralink_message(char *message, int size, OCIError *errhp, sword status) 846 | { 847 | text errbuf[512]; 848 | sb4 oracode = 0; 849 | 850 | switch (status) 851 | { 852 | case OCI_NEED_DATA: 853 | strlcpy(message, "OCI_NEED_DATA", size); 854 | break; 855 | case OCI_NO_DATA: 856 | strlcpy(message, "OCI_NO_DATA", size); 857 | break; 858 | case OCI_ERROR: 859 | OCIErrorGet ((void *) errhp, (ub4) 1, (text *) NULL, &oracode, 860 | errbuf, (ub4) sizeof(errbuf), (ub4) OCI_HTYPE_ERROR); 861 | snprintf(message, size, "OCI_ERROR - %s", errbuf); 862 | break; 863 | case OCI_SUCCESS_WITH_INFO: 864 | OCIErrorGet ((void *) errhp, (ub4) 1, (text *) NULL, &oracode, 865 | errbuf, (ub4) sizeof(errbuf), (ub4) OCI_HTYPE_ERROR); 866 | snprintf(message, size, "OCI_SUCCESS_WITH_INFO - %s", errbuf); 867 | break; 868 | case OCI_INVALID_HANDLE: 869 | strlcpy(message, "OCI_INVALID_HANDLE", size); 870 | break; 871 | case OCI_STILL_EXECUTING: 872 | strlcpy(message, "OCI_STILL_EXECUTING", size); 873 | break; 874 | case OCI_CONTINUE: 875 | strlcpy(message, "OCI_CONTINUE", size); 876 | break; 877 | default: 878 | snprintf(message, size, "OCI: %d", status); 879 | break; 880 | } 881 | } 882 | 883 | static void *oralink_malloc(size_t size) 884 | { 885 | MemoryContext old_context; 886 | void *p; 887 | 888 | old_context = MemoryContextSwitchTo(TopTransactionContext); 889 | p = palloc(size); 890 | MemoryContextSwitchTo(old_context); 891 | return p; 892 | } 893 | 894 | static void *oralink_calloc(size_t nmemb, size_t size) 895 | { 896 | MemoryContext old_context; 897 | void *p; 898 | 899 | old_context = MemoryContextSwitchTo(TopTransactionContext); 900 | p = palloc0(nmemb * size); 901 | MemoryContextSwitchTo(old_context); 902 | return p; 903 | } 904 | 905 | static void *oralink_realloc(void *ptr, size_t size) 906 | { 907 | MemoryContext old_context; 908 | void *p; 909 | 910 | old_context = MemoryContextSwitchTo(TopTransactionContext); 911 | p = repalloc(ptr, size); 912 | MemoryContextSwitchTo(old_context); 913 | return p; 914 | } 915 | 916 | static void oralink_free(void *ptr) 917 | { 918 | if (ptr) 919 | pfree(ptr); 920 | } 921 | 922 | #endif 923 | -------------------------------------------------------------------------------- /dblink_plus.control: -------------------------------------------------------------------------------- 1 | # intarray extension 2 | comment = 'Connect to external database server, and then run the SQL' 3 | default_version = '1.0.10' 4 | module_pathname = '$libdir/dblink_plus' 5 | relocatable = true 6 | -------------------------------------------------------------------------------- /dblink_plus.sql.in: -------------------------------------------------------------------------------- 1 | SET search_path = public; 2 | 3 | BEGIN; 4 | 5 | ---------------------------------------------------------------------------- 6 | -- dblink 7 | ---------------------------------------------------------------------------- 8 | 9 | CREATE SCHEMA dblink; 10 | 11 | CREATE DOMAIN dblink.cursor integer; 12 | 13 | CREATE FUNCTION dblink.connect( 14 | server text, 15 | use_xa boolean DEFAULT true) 16 | RETURNS boolean AS 17 | 'MODULE_PATHNAME', 'dblink_connect' LANGUAGE C STRICT; 18 | 19 | CREATE FUNCTION dblink.connect( 20 | name text, 21 | server text, 22 | use_xa boolean DEFAULT true) 23 | RETURNS boolean AS 24 | 'MODULE_PATHNAME', 'dblink_connect_name' LANGUAGE C STRICT; 25 | 26 | CREATE FUNCTION dblink.disconnect(name text) 27 | RETURNS boolean AS 28 | 'MODULE_PATHNAME', 'dblink_disconnect' LANGUAGE C STRICT; 29 | 30 | CREATE FUNCTION dblink.query(name text, sql text, fetchsize integer DEFAULT 0, max_value_len integer DEFAULT -1) 31 | RETURNS SETOF record AS 32 | 'MODULE_PATHNAME', 'dblink_query' LANGUAGE C STRICT; 33 | 34 | CREATE FUNCTION dblink.exec(name text, sql text) 35 | RETURNS bigint AS 36 | 'MODULE_PATHNAME', 'dblink_exec' LANGUAGE C STRICT; 37 | 38 | CREATE FUNCTION dblink.open(name text, sql text, fetchsize integer DEFAULT 100, max_value_len integer DEFAULT -1) 39 | RETURNS dblink.cursor AS 40 | 'MODULE_PATHNAME', 'dblink_open' LANGUAGE C STRICT; 41 | 42 | CREATE FUNCTION dblink.fetch(dblink.cursor, howmany integer DEFAULT 1) 43 | RETURNS SETOF record AS 44 | 'MODULE_PATHNAME', 'dblink_fetch' LANGUAGE C STRICT; 45 | 46 | CREATE FUNCTION dblink.close(dblink.cursor) 47 | RETURNS boolean AS 48 | 'MODULE_PATHNAME', 'dblink_close' LANGUAGE C STRICT; 49 | 50 | CREATE FUNCTION dblink.call(name text, sql text, fetchsize integer DEFAULT 0, max_value_len integer DEFAULT -1) 51 | RETURNS SETOF record AS 52 | 'MODULE_PATHNAME', 'dblink_call' LANGUAGE C STRICT; 53 | 54 | CREATE FUNCTION dblink.connections( 55 | OUT name text, 56 | OUT server oid, 57 | OUT status text, 58 | OUT use_xa boolean, 59 | OUT keep boolean) 60 | RETURNS SETOF record AS 61 | 'MODULE_PATHNAME', 'dblink_connections' LANGUAGE C STRICT; 62 | 63 | CREATE VIEW dblink.connections AS SELECT * FROM dblink.connections(); 64 | 65 | ---- dblink: internals ---- 66 | 67 | CREATE FUNCTION dblink.atcommit() RETURNS trigger 68 | AS 'MODULE_PATHNAME', 'dblink_atcommit' LANGUAGE C; 69 | 70 | CREATE TABLE dblink.atcommit (dummy integer); 71 | 72 | CREATE TRIGGER atcommit AFTER DELETE ON dblink.atcommit 73 | FOR EACH STATEMENT EXECUTE PROCEDURE dblink.atcommit(); 74 | 75 | UPDATE pg_catalog.pg_trigger 76 | SET tgdeferrable = true, tginitdeferred = true 77 | WHERE tgrelid = 'dblink.atcommit'::regclass 78 | AND tgname = 'atcommit'; 79 | 80 | ---- connectors ---- 81 | CREATE FUNCTION dblink.postgres(text[], oid) RETURNS boolean 82 | AS 'MODULE_PATHNAME', 'dblink_postgres' LANGUAGE C; 83 | 84 | CREATE FUNCTION dblink.mysql(text[], oid) RETURNS boolean 85 | AS 'MODULE_PATHNAME', 'dblink_mysql' LANGUAGE C; 86 | 87 | CREATE FUNCTION dblink.oracle(text[], oid) RETURNS boolean 88 | AS 'MODULE_PATHNAME', 'dblink_oracle' LANGUAGE C; 89 | 90 | CREATE FUNCTION dblink.sqlite3(text[], oid) RETURNS boolean 91 | AS 'MODULE_PATHNAME', 'dblink_sqlite3' LANGUAGE C; 92 | 93 | COMMIT; 94 | -------------------------------------------------------------------------------- /dblink_postgres.c: -------------------------------------------------------------------------------- 1 | /* 2 | * dblink_postgres.c 3 | * 4 | * Copyright (c) 2011-2025, NIPPON TELEGRAPH AND TELEPHONE CORPORATION 5 | */ 6 | #include "postgres.h" 7 | 8 | #include "fmgr.h" 9 | #include "mb/pg_wchar.h" 10 | #include "lib/stringinfo.h" 11 | #include "nodes/parsenodes.h" 12 | #include "libpq-fe.h" 13 | #include "utils/memutils.h" 14 | 15 | #if PG_VERSION_NUM < 130000 16 | #include "fe_utils/connect.h" 17 | #else 18 | #include "common/connect.h" 19 | #endif 20 | 21 | extern Datum postgresql_fdw_validator(PG_FUNCTION_ARGS); 22 | 23 | #include "dblink.h" 24 | 25 | typedef struct pglink_connection 26 | { 27 | dblink_connection base; 28 | PGconn *conn; 29 | } pglink_connection; 30 | 31 | typedef struct pglink_value 32 | { 33 | char **fields; 34 | } pglink_value; 35 | 36 | /* PQresult wrapper cursor */ 37 | typedef struct pglink_cursor 38 | { 39 | dblink_cursor base; 40 | 41 | pglink_value *values; 42 | int ntuples; 43 | 44 | int row; 45 | } pglink_cursor; 46 | 47 | /* Server-side cursor */ 48 | typedef struct pglink_srvcur 49 | { 50 | pglink_cursor base; 51 | PGconn *conn; 52 | int fetchsize; 53 | char name[NAMEDATALEN]; /* cursor name */ 54 | } pglink_srvcur; 55 | 56 | PG_FUNCTION_INFO_V1(dblink_postgres); 57 | extern Datum dblink_postgres(PG_FUNCTION_ARGS); 58 | 59 | static pglink_connection *pglink_connection_new(PGconn *conn); 60 | static void pglink_disconnect(pglink_connection *conn); 61 | static int64 pglink_exec(pglink_connection *conn, const char *sql); 62 | static pglink_cursor *pglink_open(pglink_connection *conn, const char *sql, int32 fetchsize, int32 max_value_len); 63 | static pglink_cursor *pglink_call(pglink_connection *conn, const char *func, int32 fetchsize, int32 max_value_len); 64 | static bool pglink_command(pglink_connection *conn, dblink_command type); 65 | 66 | static pglink_cursor *pglink_cursor_new(void); 67 | static bool pglink_fetch(pglink_cursor *cur, const char *values[]); 68 | static void pglink_close(pglink_cursor *cur); 69 | 70 | static pglink_srvcur *pglink_srvcur_new(void); 71 | static bool pglink_fetch_srvcur(pglink_srvcur *cur, const char *values[]); 72 | static void pglink_close_srvcur(pglink_srvcur *cur); 73 | 74 | static void pglink_error(PGconn *conn, PGresult *res); 75 | static void pglink_makevalue(pglink_cursor *cur, PGresult *res); 76 | static void pglink_freevalue(pglink_cursor *cur); 77 | 78 | /* 79 | * Escaping libpq connect parameter strings. 80 | * 81 | * Replaces "'" with "\'" and "\" with "\\". 82 | */ 83 | static char * 84 | escape_param_str(const char *str) 85 | { 86 | const char *cp; 87 | StringInfo buf = makeStringInfo(); 88 | 89 | for (cp = str; *cp; cp++) 90 | { 91 | if (*cp == '\\' || *cp == '\'') 92 | appendStringInfoChar(buf, '\\'); 93 | appendStringInfoChar(buf, *cp); 94 | } 95 | 96 | return buf->data; 97 | } 98 | 99 | static char * 100 | join_options(List *options) 101 | { 102 | ListCell *cell; 103 | StringInfoData buf; 104 | 105 | initStringInfo(&buf); 106 | foreach(cell, options) 107 | { 108 | DefElem *def = lfirst(cell); 109 | 110 | appendStringInfo(&buf, "%s='%s' ", def->defname, 111 | escape_param_str(strVal(def->arg))); 112 | } 113 | 114 | return buf.data; 115 | } 116 | 117 | Datum 118 | dblink_postgres(PG_FUNCTION_ARGS) 119 | { 120 | dblink_connection **connection; 121 | PGconn *conn; 122 | PGresult *res; 123 | ExecStatusType code; 124 | 125 | if (PG_GETARG_OID(1) != DBLINKOID) 126 | return postgresql_fdw_validator(fcinfo); 127 | 128 | connection = (dblink_connection **) PG_GETARG_POINTER(2); 129 | if (!connection) 130 | PG_RETURN_BOOL(false); 131 | 132 | conn = PQconnectdb(join_options((List *) PG_GETARG_POINTER(3))); 133 | 134 | if (PQstatus(conn) == CONNECTION_BAD) 135 | { 136 | char *detail = pstrdup(PQerrorMessage(conn)); 137 | PQfinish(conn); 138 | 139 | ereport(ERROR, 140 | (errcode(ERRCODE_SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION), 141 | errmsg("could not establish connection"), 142 | errdetail("%s", detail))); 143 | } 144 | 145 | res = PQexec(conn, "SELECT pg_catalog.set_config('search_path', 'pg_catalog, ""$user"", public', false)"); 146 | code = PQresultStatus(res); 147 | if (code != PGRES_COMMAND_OK && code != PGRES_TUPLES_OK) 148 | { 149 | elog(ERROR, "dblink_plus: could not clear search_path: %s", PQerrorMessage(conn)); 150 | pglink_error(conn, res); 151 | } 152 | 153 | PQsetClientEncoding(conn, GetDatabaseEncodingName()); 154 | 155 | *connection = (dblink_connection *) pglink_connection_new(conn); 156 | PG_RETURN_BOOL(true); 157 | } 158 | 159 | static pglink_connection * 160 | pglink_connection_new(PGconn *conn) 161 | { 162 | pglink_connection *p; 163 | 164 | p = malloc(sizeof(pglink_connection)); 165 | p->base.disconnect = (dblink_disconnect_t) pglink_disconnect; 166 | p->base.exec = (dblink_exec_t) pglink_exec; 167 | p->base.open = (dblink_open_t) pglink_open; 168 | p->base.call = (dblink_call_t) pglink_call; 169 | p->base.command = (dblink_command_t) pglink_command; 170 | p->conn = conn; 171 | 172 | return p; 173 | } 174 | 175 | static void 176 | pglink_disconnect(pglink_connection *conn) 177 | { 178 | PQfinish(conn->conn); 179 | free(conn); 180 | } 181 | 182 | static int64 183 | pglink_exec(pglink_connection *conn, const char *sql) 184 | { 185 | PGresult *res; 186 | int64 ntuples; 187 | 188 | res = PQexec(conn->conn, sql); 189 | 190 | switch (PQresultStatus(res)) 191 | { 192 | case PGRES_COMMAND_OK: 193 | ntuples = atoi(PQcmdTuples(res)); 194 | PQclear(res); 195 | break; 196 | case PGRES_TUPLES_OK: 197 | ntuples = PQntuples(res); 198 | PQclear(res); 199 | break; 200 | default: 201 | pglink_error(conn->conn, res); 202 | return 0; /* keep compiler quiet */ 203 | } 204 | 205 | return ntuples; 206 | } 207 | 208 | static bool 209 | fetch_forward(pglink_srvcur *cur) 210 | { 211 | char sql[NAMEDATALEN + 64]; 212 | PGresult *res; 213 | ExecStatusType code; 214 | 215 | pglink_freevalue(&cur->base); 216 | 217 | if (!cur->name[0]) 218 | return false; /* already done */ 219 | 220 | snprintf(sql, lengthof(sql), "FETCH FORWARD %d FROM %s", cur->fetchsize, cur->name); 221 | res = PQexec(cur->conn, sql); 222 | code = PQresultStatus(res); 223 | if (code != PGRES_COMMAND_OK && code != PGRES_TUPLES_OK) 224 | pglink_error(cur->conn, res); 225 | 226 | if (PQntuples(res) <= 0) 227 | { 228 | /* no more tuples */ 229 | PQclear(res); 230 | snprintf(sql, lengthof(sql), "CLOSE %s", cur->name); 231 | PQclear(PQexec(cur->conn, sql)); 232 | cur->name[0] = '\0'; 233 | return false; 234 | } 235 | 236 | cur->base.base.nfields = PQnfields(res); 237 | cur->base.ntuples = PQntuples(res); 238 | pglink_makevalue(&cur->base, res); 239 | PQclear(res); 240 | cur->base.row = 0; 241 | return true; 242 | } 243 | 244 | static pglink_cursor * 245 | pglink_open(pglink_connection *conn, const char *sql, int32 fetchsize, int32 max_value_len) 246 | { 247 | PGresult *res; 248 | ExecStatusType code; 249 | 250 | if (fetchsize > 0) 251 | { 252 | char name[NAMEDATALEN]; 253 | StringInfoData buf; 254 | 255 | initStringInfo(&buf); 256 | 257 | /* TODO: Use less conflict cursor name. */ 258 | snprintf(name, NAMEDATALEN, "dblink_plus_%d", rand()); 259 | appendStringInfo(&buf, "DECLARE %s CURSOR FOR %s", name, sql); 260 | res = PQexec(conn->conn, buf.data); 261 | code = PQresultStatus(res); 262 | 263 | if (code == PGRES_COMMAND_OK) 264 | { 265 | pglink_srvcur *cur; 266 | 267 | PQclear(res); 268 | 269 | cur = pglink_srvcur_new(); 270 | cur->conn = conn->conn; 271 | cur->fetchsize = fetchsize; 272 | strlcpy(cur->name, name, NAMEDATALEN); 273 | if (!fetch_forward(cur)) 274 | { 275 | pglink_close_srvcur(cur); 276 | return NULL; 277 | } 278 | 279 | return (pglink_cursor *) cur; 280 | } 281 | } 282 | else 283 | { 284 | res = PQexec(conn->conn, sql); 285 | code = PQresultStatus(res); 286 | 287 | if (code == PGRES_COMMAND_OK || code == PGRES_TUPLES_OK) 288 | { 289 | pglink_cursor *cur; 290 | 291 | cur = pglink_cursor_new(); 292 | cur->base.nfields = PQnfields(res); 293 | cur->ntuples = PQntuples(res); 294 | pglink_makevalue(cur, res); 295 | PQclear(res); 296 | return cur; 297 | } 298 | } 299 | 300 | pglink_error(conn->conn, res); 301 | return NULL; /* keep compiler quiet */ 302 | } 303 | 304 | static void pglink_makevalue(pglink_cursor *cur, PGresult *res) 305 | { 306 | MemoryContext old_context; 307 | int i; 308 | int n; 309 | 310 | old_context = MemoryContextSwitchTo(TopTransactionContext); 311 | 312 | cur->values = palloc(sizeof(pglink_value) * cur->ntuples); 313 | for (i = 0; i < cur->ntuples; i++) 314 | { 315 | cur->values[i].fields = palloc(sizeof(char*) * cur->base.nfields); 316 | for (n = 0; n < cur->base.nfields; n++) 317 | { 318 | if (PQgetisnull(res, i, n)) 319 | cur->values[i].fields[n] = 0; 320 | else 321 | cur->values[i].fields[n] = pstrdup(PQgetvalue(res, i, n)); 322 | } 323 | } 324 | MemoryContextSwitchTo(old_context); 325 | } 326 | 327 | static void pglink_freevalue(pglink_cursor *cur) 328 | { 329 | int i, n; 330 | 331 | if (!cur->values) 332 | return; 333 | 334 | for (i = 0; i < cur->ntuples; i++) 335 | { 336 | for (n = 0; n < cur->base.nfields; n++) 337 | if (cur->values[i].fields[n]) 338 | pfree(cur->values[i].fields[n]); 339 | 340 | pfree(cur->values[i].fields); 341 | } 342 | pfree(cur->values); 343 | cur->values = 0; 344 | return; 345 | } 346 | 347 | static pglink_cursor * 348 | pglink_call(pglink_connection *conn, const char *func, int32 fetchsize, int32 max_value_len) 349 | { 350 | StringInfoData sql; 351 | 352 | initStringInfo(&sql); 353 | appendStringInfoString(&sql, "SELECT * FROM "); 354 | appendStringInfoString(&sql, func); 355 | 356 | return pglink_open(conn, sql.data, fetchsize, max_value_len); 357 | } 358 | 359 | /* TODO: Use less conflict and more human-readable XA-ID. */ 360 | static bool 361 | pglink_command(pglink_connection *conn, dblink_command type) 362 | { 363 | char sql[256]; 364 | PGresult *res; 365 | bool ok; 366 | 367 | switch (type) 368 | { 369 | case DBLINK_BEGIN: 370 | case DBLINK_XA_START: 371 | strlcpy(sql, "BEGIN", lengthof(sql)); 372 | break; 373 | case DBLINK_COMMIT: 374 | strlcpy(sql, "COMMIT", lengthof(sql)); 375 | break; 376 | case DBLINK_ROLLBACK: 377 | strlcpy(sql, "ROLLBACK", lengthof(sql)); 378 | break; 379 | case DBLINK_XA_PREPARE: 380 | set_dblink_gtrid(sql, lengthof(sql), 381 | "PREPARE TRANSACTION '%s'", conn->base); 382 | break; 383 | case DBLINK_XA_COMMIT: 384 | set_dblink_gtrid(sql, lengthof(sql), 385 | "COMMIT PREPARED '%s'", conn->base); 386 | break; 387 | case DBLINK_XA_ROLLBACK: 388 | set_dblink_gtrid(sql, lengthof(sql), 389 | "ROLLBACK PREPARED '%s'", conn->base); 390 | break; 391 | default: 392 | return false; 393 | } 394 | 395 | res = PQexec(conn->conn, sql); 396 | ok = (PQresultStatus(res) == PGRES_COMMAND_OK); 397 | PQclear(res); 398 | 399 | return ok; 400 | } 401 | 402 | static pglink_cursor * 403 | pglink_cursor_new(void) 404 | { 405 | MemoryContext old_context; 406 | pglink_cursor *p; 407 | 408 | old_context = MemoryContextSwitchTo(TopTransactionContext); 409 | p = palloc(sizeof(pglink_cursor)); 410 | p->base.fetch = (dblink_fetch_t) pglink_fetch; 411 | p->base.close = (dblink_close_t) pglink_close; 412 | p->base.nfields = 0; 413 | p->row = 0; 414 | p->values = NULL; 415 | p->ntuples = 0; 416 | MemoryContextSwitchTo(old_context); 417 | 418 | return p; 419 | } 420 | 421 | static bool 422 | pglink_fetch(pglink_cursor *cur, const char *values[]) 423 | { 424 | int c; 425 | 426 | if (cur->row >= cur->ntuples) 427 | return false; 428 | 429 | for (c = 0; c < cur->base.nfields; c++) 430 | { 431 | values[c] = cur->values[cur->row].fields[c]; 432 | } 433 | cur->row++; 434 | return true; 435 | } 436 | 437 | static void 438 | pglink_close(pglink_cursor *cur) 439 | { 440 | pglink_freevalue(cur); 441 | pfree(cur); 442 | } 443 | 444 | static pglink_srvcur * 445 | pglink_srvcur_new(void) 446 | { 447 | MemoryContext old_context; 448 | pglink_srvcur *p; 449 | 450 | old_context = MemoryContextSwitchTo(TopTransactionContext); 451 | p = palloc(sizeof(pglink_srvcur)); 452 | p->base.base.fetch = (dblink_fetch_t) pglink_fetch_srvcur; 453 | p->base.base.close = (dblink_close_t) pglink_close_srvcur; 454 | p->base.base.nfields = 0; 455 | p->base.row = 0; 456 | p->base.values = NULL; 457 | p->base.ntuples = 0; 458 | p->conn = NULL; 459 | p->name[0] = '\0'; 460 | MemoryContextSwitchTo(old_context); 461 | 462 | return p; 463 | } 464 | 465 | static bool 466 | pglink_fetch_srvcur(pglink_srvcur *cur, const char *values[]) 467 | { 468 | if (cur->base.values == NULL || cur->base.row >= cur->base.ntuples) 469 | { 470 | if (!fetch_forward(cur)) 471 | return false; 472 | } 473 | 474 | return pglink_fetch(&cur->base, values); 475 | } 476 | 477 | static void 478 | pglink_close_srvcur(pglink_srvcur *cur) 479 | { 480 | pglink_freevalue(&cur->base); 481 | if (cur->name[0] && cur->conn) 482 | { 483 | char sql[NAMEDATALEN + 64]; 484 | 485 | snprintf(sql, lengthof(sql), "CLOSE %s", cur->name); 486 | PQclear(PQexec(cur->conn, sql)); 487 | } 488 | pfree(cur); 489 | } 490 | 491 | static void 492 | pglink_error(PGconn *conn, PGresult *res) 493 | { 494 | const char *diag_sqlstate = PQresultErrorField(res, PG_DIAG_SQLSTATE); 495 | const char *message = PQresultErrorField(res, PG_DIAG_MESSAGE_PRIMARY); 496 | const char *detail = PQresultErrorField(res, PG_DIAG_MESSAGE_DETAIL); 497 | const char *hint = PQresultErrorField(res, PG_DIAG_MESSAGE_HINT); 498 | const char *context = PQresultErrorField(res, PG_DIAG_CONTEXT); 499 | int sqlstate; 500 | 501 | if (diag_sqlstate) 502 | sqlstate = MAKE_SQLSTATE(diag_sqlstate[0], 503 | diag_sqlstate[1], 504 | diag_sqlstate[2], 505 | diag_sqlstate[3], 506 | diag_sqlstate[4]); 507 | else 508 | sqlstate = ERRCODE_CONNECTION_FAILURE; 509 | 510 | message = message ? pstrdup(message) : NULL; 511 | detail = detail ? pstrdup(detail) : NULL; 512 | hint = hint ? pstrdup(hint) : NULL; 513 | context = context ? pstrdup(context) : NULL; 514 | 515 | if (res) 516 | PQclear(res); 517 | 518 | ereport(ERROR, (errcode(sqlstate), 519 | message ? errmsg("%s", message) : errmsg("unknown error"), 520 | detail ? errdetail("%s", detail) : 0, 521 | hint ? errhint("%s", hint) : 0, 522 | context ? errcontext("%s", context) : 0)); 523 | } 524 | -------------------------------------------------------------------------------- /dblink_sqlite3.c: -------------------------------------------------------------------------------- 1 | /* 2 | * dblink_sqlite3.c 3 | * 4 | * Copyright (c) 2011-2025, NIPPON TELEGRAPH AND TELEPHONE CORPORATION 5 | */ 6 | #ifdef ENABLE_SQLITE3 7 | 8 | #include "postgres.h" 9 | #include "utils/memutils.h" 10 | #include "lib/stringinfo.h" 11 | #include "mb/pg_wchar.h" 12 | #include "miscadmin.h" 13 | 14 | #include "dblink.h" 15 | #include "dblink_internal.h" 16 | 17 | #include 18 | 19 | typedef struct sq3link_connection 20 | { 21 | dblink_connection base; 22 | sqlite3 *db; 23 | } sq3link_connection; 24 | 25 | typedef struct sq3link_row 26 | { 27 | struct sq3link_row *next; 28 | 29 | char *values[1]; 30 | } sq3link_row; 31 | 32 | typedef struct sq3link_cursor 33 | { 34 | dblink_cursor base; 35 | sq3link_row *head; 36 | sq3link_row *iter; 37 | } sq3link_cursor; 38 | 39 | static sq3link_connection *sq3link_connection_new(sqlite3 *db); 40 | static void sq3link_disconnect(sq3link_connection *conn); 41 | static int64 sq3link_exec(sq3link_connection *conn, const char *sql); 42 | static sq3link_cursor *sq3link_open(sq3link_connection *conn, const char *func, int32 fetchsize, int32 max_value_len); 43 | static sq3link_cursor *sq3link_call(sq3link_connection *conn, const char *sql, int32 fetchsize, int32 max_value_len); 44 | static bool sq3link_command(sq3link_connection *conn, dblink_command type); 45 | 46 | static sq3link_cursor *sq3link_cursor_new(void); 47 | static bool sq3link_fetch(sq3link_cursor *cur, const char *values[]); 48 | static void sq3link_close(sq3link_cursor *cur); 49 | 50 | static int sq3link_callback(void *userdata, int argc, char **argv, char **columns); 51 | static void sq3link_error(sqlite3 *db, const char *message); 52 | 53 | dblink_connection * 54 | sq3link_connect(const char *location) 55 | { 56 | sqlite3 *db = NULL; 57 | int rc; 58 | 59 | rc = sqlite3_open(location, &db); 60 | 61 | if (rc != 0) 62 | { 63 | if (db) 64 | sqlite3_close(db); 65 | dblink_error(ERRCODE_SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION, 66 | "could not establish connection", location, NULL, NULL); 67 | } 68 | 69 | return (dblink_connection *) sq3link_connection_new(db); 70 | } 71 | 72 | static sq3link_connection * 73 | sq3link_connection_new(sqlite3 *db) 74 | { 75 | sq3link_connection *p; 76 | 77 | p = malloc(sizeof(sq3link_connection)); 78 | p->base.disconnect = (dblink_disconnect_t) sq3link_disconnect; 79 | p->base.exec = (dblink_exec_t) sq3link_exec; 80 | p->base.open = (dblink_open_t) sq3link_open; 81 | p->base.call = (dblink_call_t) sq3link_call; 82 | p->base.command = (dblink_command_t) sq3link_command; 83 | p->db = db; 84 | 85 | return p; 86 | } 87 | 88 | static void 89 | sq3link_disconnect(sq3link_connection *conn) 90 | { 91 | if (conn->db) 92 | sqlite3_close(conn->db); 93 | free(conn); 94 | } 95 | 96 | static int64 97 | sq3link_exec(sq3link_connection *conn, const char *sql) 98 | { 99 | char *sql_utf8; 100 | char *message; 101 | int rc; 102 | int64 ntuples; 103 | 104 | sql_utf8 = (char *) pg_do_encoding_conversion((unsigned char *) sql, 105 | strlen(sql), GetDatabaseEncoding(), PG_UTF8); 106 | rc = sqlite3_exec(conn->db, sql_utf8, NULL, NULL, &message); 107 | 108 | switch (rc) 109 | { 110 | case SQLITE_OK: 111 | ntuples = sqlite3_changes(conn->db); 112 | break; 113 | default: 114 | sq3link_error(conn->db, message); 115 | return 0; /* keep compiler quiet */ 116 | } 117 | 118 | return ntuples; 119 | } 120 | 121 | static sq3link_cursor * 122 | sq3link_open(sq3link_connection *conn, const char *sql, int32 fetchsize, int32 max_value_len) 123 | { 124 | sq3link_cursor *cur; 125 | char *sql_utf8; 126 | char *message; 127 | int rc; 128 | 129 | cur = sq3link_cursor_new(); 130 | 131 | sql_utf8 = (char *) pg_do_encoding_conversion((unsigned char *) sql, 132 | strlen(sql), GetDatabaseEncoding(), PG_UTF8); 133 | rc = sqlite3_exec(conn->db, sql_utf8, sq3link_callback, cur, &message); 134 | if (rc == SQLITE_OK) 135 | { 136 | cur->iter = cur->head; 137 | return cur; 138 | } 139 | 140 | sq3link_close(cur); 141 | sq3link_error(conn->db, message); 142 | return NULL; /* keep compiler quiet */ 143 | } 144 | 145 | static sq3link_cursor * 146 | sq3link_call(sq3link_connection *conn, const char *func, int32 fetchsize, int32 max_value_len) 147 | { 148 | StringInfoData sql; 149 | 150 | initStringInfo(&sql); 151 | appendStringInfoString(&sql, "SELECT "); 152 | appendStringInfoString(&sql, func); 153 | 154 | return sq3link_open(conn, sql.data, fetchsize, max_value_len); 155 | } 156 | 157 | static bool 158 | sq3link_command(sq3link_connection *conn, dblink_command type) 159 | { 160 | const char *sql; 161 | int rc; 162 | char *message; 163 | 164 | /* SQLite3 doesn't support 2PC. */ 165 | switch (type) 166 | { 167 | case DBLINK_BEGIN: 168 | case DBLINK_XA_START: 169 | sql = "BEGIN"; 170 | break; 171 | case DBLINK_COMMIT: 172 | case DBLINK_XA_PREPARE: 173 | sql = "COMMIT"; 174 | break; 175 | case DBLINK_ROLLBACK: 176 | sql = "ROLLBACK"; 177 | break; 178 | case DBLINK_XA_COMMIT: 179 | return true; 180 | case DBLINK_XA_ROLLBACK: 181 | default: 182 | return false; 183 | } 184 | 185 | rc = sqlite3_exec(conn->db, sql, NULL, NULL, &message); 186 | 187 | return rc == SQLITE_OK; 188 | } 189 | 190 | static sq3link_cursor * 191 | sq3link_cursor_new(void) 192 | { 193 | MemoryContext old_context; 194 | sq3link_cursor *p; 195 | 196 | old_context = MemoryContextSwitchTo(TopTransactionContext); 197 | p = palloc(sizeof(sq3link_cursor)); 198 | p->base.fetch = (dblink_fetch_t) sq3link_fetch; 199 | p->base.close = (dblink_close_t) sq3link_close; 200 | p->base.nfields = 0; 201 | p->head = p->iter = NULL; 202 | MemoryContextSwitchTo(old_context); 203 | 204 | return p; 205 | } 206 | 207 | static bool 208 | sq3link_fetch(sq3link_cursor *cur, const char *values[]) 209 | { 210 | if (cur->iter == NULL) 211 | return false; 212 | 213 | memcpy(values, cur->iter->values, sizeof(char * ) * cur->base.nfields); 214 | cur->iter = cur->iter->next; 215 | return true; 216 | } 217 | 218 | static void 219 | sq3link_close(sq3link_cursor *cur) 220 | { 221 | sq3link_row *i = cur->head; 222 | 223 | while (i != NULL) 224 | { 225 | int c; 226 | sq3link_row *prev; 227 | 228 | for (c = 0; c < cur->base.nfields; c++) 229 | if (i->values[c]) 230 | pfree(i->values[c]); 231 | 232 | prev = i; 233 | i = i->next; 234 | pfree(prev); 235 | } 236 | pfree(cur); 237 | } 238 | 239 | static char * 240 | strdup_utf8(const char *utf8) 241 | { 242 | if (utf8 == NULL) 243 | return NULL; 244 | else if (GetDatabaseEncoding() == PG_UTF8) 245 | return pstrdup(utf8); 246 | else 247 | { 248 | elog(WARNING, "sq3link: non-utf8 encoding is not supported"); 249 | return pstrdup(utf8); 250 | } 251 | } 252 | 253 | static int 254 | sq3link_callback(void *userdata, int argc, char **argv, char **columns) 255 | { 256 | sq3link_cursor *cur = (sq3link_cursor *) userdata; 257 | sq3link_row *row; 258 | int i; 259 | MemoryContext old_context; 260 | 261 | /* CHECK_FOR_INTERRUPTS */ 262 | #ifdef WIN32 263 | if (UNBLOCKED_SIGNAL_QUEUE()) 264 | pgwin32_dispatch_queued_signals(); 265 | #endif 266 | if (InterruptPending) 267 | return SQLITE_ABORT; 268 | 269 | old_context = MemoryContextSwitchTo(TopTransactionContext); 270 | row = palloc(offsetof(sq3link_row, values) + sizeof(char * ) * argc); 271 | row->next = NULL; 272 | for (i = 0; i < argc; i++) 273 | row->values[i] = strdup_utf8(argv[i]); 274 | 275 | MemoryContextSwitchTo(old_context); 276 | 277 | cur->base.nfields = argc; 278 | if (cur->iter) 279 | { 280 | cur->iter->next = row; 281 | cur->iter = row; 282 | } 283 | else 284 | { 285 | cur->head = cur->iter = row; 286 | } 287 | 288 | return SQLITE_OK; 289 | } 290 | 291 | static void 292 | sq3link_error(sqlite3 *db, const char *message) 293 | { 294 | int code = sqlite3_errcode(db); 295 | int sqlstate; 296 | 297 | switch (code) 298 | { 299 | case SQLITE_ERROR: /* SQL error or missing database */ 300 | sqlstate = ERRCODE_CONNECTION_EXCEPTION; 301 | break; 302 | case SQLITE_INTERNAL: /* Internal logic error in SQLite */ 303 | sqlstate = ERRCODE_INTERNAL_ERROR; 304 | break; 305 | case SQLITE_PERM: /* Access permission denied */ 306 | case SQLITE_ABORT: /* Callback routine requested an abort */ 307 | case SQLITE_BUSY: /* The database file is locked */ 308 | case SQLITE_LOCKED: /* A table in the database is locked */ 309 | case SQLITE_NOMEM: /* A malloc() failed */ 310 | case SQLITE_READONLY: /* Attempt to write a readonly database */ 311 | case SQLITE_INTERRUPT: /* Operation terminated by sqlite3_interrupt()*/ 312 | case SQLITE_IOERR: /* Some kind of disk I/O error occurred */ 313 | case SQLITE_CORRUPT: /* The database disk image is malformed */ 314 | case SQLITE_NOTFOUND: /* NOT USED. Table or record not found */ 315 | case SQLITE_FULL: /* Insertion failed because database is full */ 316 | case SQLITE_CANTOPEN: /* Unable to open the database file */ 317 | case SQLITE_PROTOCOL: /* NOT USED. Database lock protocol error */ 318 | case SQLITE_EMPTY: /* Database is empty */ 319 | case SQLITE_SCHEMA: /* The database schema changed */ 320 | case SQLITE_TOOBIG: /* String or BLOB exceeds size limit */ 321 | case SQLITE_CONSTRAINT: /* Abort due to constraint violation */ 322 | case SQLITE_MISMATCH: /* Data type mismatch */ 323 | case SQLITE_MISUSE: /* Library used incorrectly */ 324 | case SQLITE_NOLFS: /* Uses OS features not supported on host */ 325 | case SQLITE_AUTH: /* Authorization denied */ 326 | case SQLITE_FORMAT: /* Auxiliary database format error */ 327 | case SQLITE_RANGE: /* 2nd parameter to sqlite3_bind out of range */ 328 | case SQLITE_NOTADB: /* File opened that is not a database file */ 329 | case SQLITE_ROW: /* sqlite3_step() has another row ready */ 330 | case SQLITE_DONE: /* sqlite3_step() has finished executing */ 331 | default: 332 | sqlstate = ERRCODE_EXTERNAL_ROUTINE_EXCEPTION; 333 | break; 334 | } 335 | 336 | dblink_error(sqlstate, message, NULL, NULL, NULL); 337 | } 338 | 339 | #endif 340 | -------------------------------------------------------------------------------- /docs/dblink_plus-img.ppt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ossc-db/dblink_plus/045dc1bb7b94e51b725c313ca31e272c6fa9c5e0/docs/dblink_plus-img.ppt -------------------------------------------------------------------------------- /docs/dblink_plus-ja.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ossc-db/dblink_plus/045dc1bb7b94e51b725c313ca31e272c6fa9c5e0/docs/dblink_plus-ja.png -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | dblink_plus 5 | 6 | 7 | 8 | 9 | 10 | 11 |

dblink_plus

12 |
13 | 14 | 29 | 30 |

名前

31 |

dblink_plus -- 外部データベース・サーバに接続し、SQLを実行します。

32 | 33 |

API一覧

34 |

35 | dblink.connect(server, use_xa DEFAULT true) : boolean
36 | dblink.connect(name, server, use_xa DEFAULT true) : boolean
37 | dblink.disconnect(name) : boolean
38 | dblink.query(name, sql, fetchsize DEFAULT 0, max_value_len DEFAULT -1) : SETOF record
39 | dblink.exec(name, sql) : bigint
40 | dblink.open(name, sql, fetchsize DEFAULT 100, max_value_len DEFAULT -1) : dblink.cursor
41 | dblink.fetch(dblink.cursor, howmany DEFAULT 1) : SETOF record
42 | dblink.close(dblink.cursor) : boolean
43 | dblink.call(name, sql, fetchsize DEFAULT 0, max_value_len DEFAULT -1) : SETOF record
44 | dblink.connections() : SETOF record
45 |

46 | 47 | 48 |

概要

49 |

50 | データベースセッション内から他のデータベースへ接続するためのモジュールです。PostgreSQL 以外のデータベース・サーバへの接続をサポートしています。dblink_plus を使うことにより商用データベースからのデータ移行が外部ファイルを介さずに行えるほか、異種RDBMS間のオンラインでのテーブル結合等が実現可能となります。 51 |

52 |

53 | dblink_plus は contrib/dblink と異なり、リモートのトランザクションを含めた2相コミットが可能です。 54 | 即ち、ローカルのトランザクションがコミット / ロールバックした場合、リモートのトランザクションも同様にコミット / ロールバックします。 55 | 特に更新処理にて、一貫性のあるデータベース操作を容易に実現できます。 56 |

57 |

58 | 外部データベース・コネクタにはプラグイン方式を採用しており、PostgreSQL, Oracle Database, MySQL, sqlite3 に接続できます。 59 |

60 | 61 | 62 |

自動トランザクション管理について

63 |

64 | dblink_plus は自動トランザクション管理をサポートしています。これによりユーザーはローカルのトランザクションだけを意識すればよく、リモート側のトランザクションについて意識しなくても、dblink_plus が自動で2相コミットを行ってくれます。 65 |

66 |
 67 | =# BEGIN;
 68 | =# INSERT INTO postgres_table values (...);  -- ローカルテーブルの挿入
 69 | =# SELECT dblink.exec('oracle_conn', 'insert into oracle_table ...'); -- リモート(Oracle)への挿入
 70 | =# COMMIT;  -- ローカルのコミットですが、リモートのOracleも自動的にコミットされます
 71 | 
72 |

73 | 自動トランザクション管理の有効/無効は接続毎に設定でき、 dblink.connect() の引数 use_xa として true/false で指定できます。 74 | また、use_xa は GUC パラメータとして、 postgresql.conf にも設定できます。この値は接続を指定せずに dblink.query(), dblink.exec() などを実行した際に使用されます。詳細は、設定パラメータを参照ください。 75 |

76 | 77 |

コネクタ・プラグインについて

78 |

79 | dblink_plus は外部データベースとの接続コネクタに、プラグイン方式を採用しています。 80 | 現在サポートされているコネクタは PostgreSQL, Oracle です。これらのコネクタは、dblink_plus モジュールのビルド時に指定可能です。 81 |

82 | 83 |

dblink.postgres - PostgreSQLコネクタ

84 |

85 | PostgreSQL用のコネクタです。ビルドおよび実行にはlibpqライブラリが必要です。 86 |

87 | 88 |

dblink.oracle - Oracleコネクタ

89 |

90 | Oracle用のコネクタです。ビルドおよび実行にはOracleクライアントおよびOCIライブラリがインストールされている必要があります。またOracle用コネクタはOracleクライアントとして実行されるため、PostgreSQLを起動するユーザーでOracle用環境変数の設定が必要となります。 91 |

92 |
    93 |
  • ORACLE_HOME - (必須)
  • 94 |
  • LD_LIBRARY_PATH - (必須)
  • 95 |
  • ORACLE_SID - (ローカル接続の場合のみ必要)
  • 96 |
  • NLS_LANG - (オプション)
  • 97 |
  • NLS_DATE_FORMAT - (オプション)
  • 98 |
  • NLS_TIMESTAMP_FORMAT - (オプション)
  • 99 |
  • NLS_TIMESTAMP_TZ_FORMAT - (オプション)
  • 100 |
  • NLS_TIME_FORMAT - (オプション)
  • 101 |
  • NLS_TIME_TZ_FORMAT - (オプション)
  • 102 |
103 |

104 | クライアントキャラクタセットはPostgreSQLデータベースセッションのエンコーディングがOracleに対しても適用されます。取得されたPostgreSQLのキャラクタセットがOracleコネクタで認識できない場合、NLS_LANGの設定に従います。NLS_LANGの設定がない場合はOracleのデフォルトとなります。 105 |

106 |

日付/時刻型の変換は、環境変数NLS_xxxx_FORMATの指定に従います。

107 | 108 | 109 |

dblink.mysql - MYSQLコネクタ

110 |

111 | MYSQL用のコネクタです。ビルドおよび実行にはMYSQLクライアントパッケージがインストールされている必要があります。 112 |

113 | 114 |

dblink.sqlite3 - sqlite3コネクタ

115 |

116 | sqlite3用のコネクタです。ビルドおよび実行にはsqlite3ライブラリが必要です。 117 |

118 | 119 |

使用例

120 |

dblink_plus を使用する前に、外部データラッパ、外部サーバ、ユーザマッピングを作成する必要があります。 121 |

122 | 123 |
=# CREATE FOREIGN DATA WRAPPER postgres VALIDATOR dblink.postgres;
124 | =# CREATE SERVER my_server FOREIGN DATA WRAPPER postgres OPTIONS (dbname 'mydb');
125 | =# CREATE USER MAPPING FOR local_user SERVER my_server OPTIONS (user 'remote_user');
126 | 127 |

接続する先のDBに応じて、CREATE FOREIGN DATA WRAPPER にて VALIDATOR に dblink_plus が提供するコネクタを指定してください。 128 |

129 | 130 |

OracleコネクタでのOPTIONSの指定

131 |

CREATE SERVERでは dbname と max_value_len を、CREATE USER MAPPINGでは user と password を指定します。

132 |
133 |
dbname
134 |
TNS接続文字列です。
135 |
max_value_len
136 |
取り扱う列データの最大長(文字列に変換後のバイト長)を指定します。
137 | この外部サーバ定義を利用して検索される行データの内、最大長となる列データの長さ(文字列に変換後のバイト長)が事前に分かっている場合は、その値を設定することにより検索処理が高速になります(ただし、指定した値より列のデータ長が大きくなる場合はエラーとなります)。このオプションを省略した場合は、内部的に列バッファを自動調整しながら検索処理が行なわれますので、低速になることがあります。
138 |
user
139 |
Oracleデータベースユーザ名です。
140 |
password
141 |
Oracleデータベースユーザのパスワードです。
142 |
143 |
=# CREATE FOREIGN DATA WRAPPER oracle VALIDATOR dblink.oracle;
144 | =# CREATE SERVER server_oracle FOREIGN DATA WRAPPER oracle OPTIONS (dbname 'DBT', max_value_len '200');
145 | =# CREATE USER MAPPING FOR local_user SERVER server_oracle OPTIONS (user 'scott', password 'tiger');
146 | 147 |

最も簡単なSQLの実行

148 |

サーバ名を指定して、dblink.exec()を用い更新系SQLを実行します。実行結果に影響行数が返ります。

149 |
150 | =# SELECT dblink.exec('my_server','insert into tbl values(500, ''remote'')');
151 |  exec
152 | ------
153 |     1
154 | (1 row)
155 | 
156 | 157 |

サーバ名を指定して、dblink.query()を用い参照系SQLを実行します。実行結果に検索された全ての行が返ります。

158 |
159 | =# SELECT * FROM dblink.query('my_server','select * from tbl') as t(c1 integer, c2 text);
160 |  c1  |   c2
161 | -----+--------
162 |  100 | host
163 |  500 | remote
164 | (2 rows)
165 | 
166 | 167 |

接続を指定した実行

168 |

接続をユーザが管理する場合には、dblink.connect(), dblink.disconnect() を使います。

169 |
=# SELECT dblink.connect('my_conn', 'my_server');
170 | =# SELECT dblink.exec('my_conn', 'UPDATE tbl SET col = 999 WHERE id = 1');
171 | =# SELECT dblink.disconnect('my_conn');
172 | 173 |

明示的に接続せずにサーバ名を指定した場合には、トランザクション終了時に自動的に切断されます。

174 |
=# BEGIN;
175 | =# SELECT dblink.exec('my_server', 'UPDATE tbl SET col = 999 WHERE id = 1');
176 | =# COMMIT; -- 切断される
177 | 178 |

open, fetch, closeの実行

179 |

カーソルをオープンし、このカーソルを使いテーブルを検索します。カーソルはトランザクション内で有効です。

180 |
181 | =# BEGIN;
182 | =# select dblink.open('my_server', 'select * from tbl');
183 |  open
184 | ------
185 |     1
186 | (1 row)
187 | 
188 | =# select * from dblink.fetch(1) as t(c1 integer, c2 text);
189 |  c1  |   c2
190 | -----+--------
191 |  100 | host
192 | (1 row)
193 | 
194 | =# select * from dblink.fetch(1, 2) as t(c1 integer, c2 text);
195 |  c1  |   c2
196 | -----+--------
197 |  500 | remote
198 |   10 | dddddd
199 | (2 rows)
200 | 
201 | =# select dblink.close(1);
202 |  close
203 | -------
204 |  t
205 | (1 row)
206 | 
207 | =# COMMIT;
208 | 
209 | 210 |

DDL文の実行

211 |

分散トランザクション中にDDLの実行を許していないDBMSでは、dblink.connect()で use_xa を false で接続し、DDL文を dblink.exec()で実行します。

212 |
213 | =# SELECT dblink.connect('conn_ora', 'server_oracle', false);
214 |  connect
215 | ---------
216 |  t
217 | (1 row)
218 | 
219 | =# SELECT dblink.exec('conn_ora', 'CREATE TABLE dblink_tbl (id NUMBER(4), value VARCHAR2(100))');
220 |  exec
221 | ------
222 |     0
223 | (1 row)
224 | 
225 | =# SELECT dblink.disconnect('conn_ora');
226 |  disconnect
227 | ------------
228 |  t
229 | (1 row)
230 | 
231 | 232 |

関数

233 |

dblink_plus は下記の関数を含みます。

234 | 235 |

dblink.connect(server, use_xa DEFAULT true) : boolean

236 |

237 | 外部サーバ 'server' に接続します。接続の名前は 'server' になります。 238 |

239 |

240 | use_xa が true の場合、自動トランザクション管理が有効になります。 241 | use_xa を指定しなかった場合は、true として動作します。これは GUC パラメータで use_xa を off にしていても変わりません。 242 | use_xa を明示的に引数に指定した場合、GUC パラメータで指定した値を上書きして動作します。 243 |

244 | 245 |

接続は、セッション終了時または dblink.disconnect() を呼ぶまで維持されます。

246 |
247 |
server
248 |
外部サーバ名です。
249 |
use_xa
250 |
自動トランザクションの有効/無効を true/false で指定します。
251 |
252 |

253 |

254 | 返値: 255 | 新規の接続の場合は true が返却されます。 256 | 既に同じ名前の接続があった場合は false が返却されます。 257 |

258 | 259 |

dblink.connect(name, server, use_xa DEFAULT true) : boolean

260 |

261 | 外部サーバ 'server' に接続します。接続の名前は 'name' になります。 262 | それ以外は dblink.connect(server, use_xa) と同じです。 263 |

264 | 265 |

dblink.disconnect(name) : boolean

266 |

267 | 名前 'name' の接続を切断します。 268 | ただし、この関数はすぐには切断しません。 269 | トランザクションの終了時に実際に切断します。 270 |

271 |
name
272 |
切断する接続名です。
273 |
274 |

275 |

276 | 返値: 277 | 指定した名前の接続が存在した場合は true が返却されます。 278 | 見つからなければ false が返却されます。(エラーにはなりません) 279 |

280 | 281 |

dblink.query(name, sql, fetchsize DEFAULT 0, max_value_len DEFAULT -1) : SETOF record

282 |

283 | 参照SQLを実行します。'name'に外部サーバー名を指定した場合、SQLの実行前に該当サーバーへ接続を行います。またSQL実行後に該当サーバーから切断を行います。 284 |

285 |
name
286 |
接続名または外部サーバー名を指定します。
287 |
sql
288 |
実行するSQL文です。
289 |
fetchsize
290 |
内部バッファの行数です。0を指定すると外部データベース・コネクタで最適な値となります。
291 |
max_value_len
292 |
取り扱う列データの最大長(文字列に変換後のバイト長)を指定します。
293 | Oracle外部サーバ定義の 'max_value_len' オプションと同様の効果があり、dblink.query()の実行範囲内で有効となります。0を指定すると外部サーバ定義の 'max_value_len' オプション指定は無視され、内部的に列バッファを自動調整しながら検索処理が行なわれます。引数を省略した場合は、外部サーバ定義の 'max_value_len' オプション指定に従い動作します。この引数はOracleコネクタのみで有効となります。
294 |
295 |

296 |

297 | 返値: 298 | 結果の行セットです。 299 |

300 | 301 |

dblink.exec(name, sql) : bigint

302 |

303 | 更新SQLを実行します。 304 |

305 |
name
306 |
接続名または外部サーバー名を指定します。
307 |
sql
308 |
実行するSQL文です。
309 |
310 |

311 |

312 | 返値: 313 | SQLが影響した行数です。 314 |

315 | 316 |

dblink.cursor [TYPE]

317 |

318 | カーソルハンドルです。 319 |

320 | 321 |

dblink.open(name, sql, fetchsize DEFAULT 100, max_value_len DEFAULT -1) : dblink.cursor

322 |

323 | 参照SQLを実行し、結果を分割して取得するためのカーソルを作成します。その後カーソルをdblink.fetch()とdblink.close()で操作することができます。 324 |

325 |
name
326 |
接続名または外部サーバー名を指定します。
327 |
sql
328 |
実行するSQL文です。
329 |
fetchsize
330 |
内部バッファの行数です。0を指定すると外部データベース・コネクタで最適な値となります。
331 |
max_value_len
332 |
取り扱う列データの最大長(文字列に変換後のバイト長)を指定します。
333 | Oracle外部サーバ定義の 'max_value_len' オプションと同様の効果があり、dblink.open()で作成したカーソルの範囲内で有効となります。0を指定すると外部サーバ定義の 'max_value_len' オプション指定は無視され、内部的に列バッファを自動調整しながら検索処理が行なわれます。引数を省略した場合は、外部サーバ定義の 'max_value_len' オプション指定に従い動作します。この引数はOracleコネクタのみで有効となります。
334 |
335 |

336 |

337 | 返値: 338 | カーソルハンドルです。 339 | PostgreSQLコネクタ利用時において、引数 fetchsize に1以上が指定され、かつクエリの結果が0件となる場合は、カーソルが作成されずにカーソルハンドルは常に0値で返却されます。 340 |

341 | 342 |

dblink.fetch(dblink.cursor, howmany DEFAULT 1)

343 |

344 | カーソルから結果を分割して取得します。オープンされていない無効なカーソルハンドル値を引数 dblink.cursor に指定した場合は、エラーとせずに0件の結果が返却されます。 345 |

346 |
dblink.cursor
347 |
カーソルハンドルです。
348 |
howmany
349 |
フェッチする行数です。
350 |
351 |

352 |

353 | 返値: 354 | 結果の行セットです。 355 |

356 | 357 |

dblink.close(dblink.cursor)

358 |

359 | カーソルを閉じます。オープンされていない無効なカーソルハンドル値を引数 dblink.cursor に指定した場合でもエラーとしません。 360 |

361 |
dblink.cursor
362 |
カーソルハンドルです。
363 |
364 |

365 | 366 |

dblink.call(name, sql, fetchsize DEFAULT 0, max_value_len DEFAULT -1) : SETOF record

367 |

368 | ストアドファンクションを実行します。 369 |

370 |
name
371 |
接続名または外部サーバー名を指定します。
372 |
sql
373 |
実行するストアドファンクション名です。
374 |
fetchsize
375 |
内部バッファの行数です。0を指定すると外部データベース・コネクタで最適な値となります。
376 |
max_value_len
377 |
取り扱う列データの最大長(文字列に変換後のバイト長)を指定します。
378 | Oracle外部サーバ定義の 'max_value_len' オプションと同様の効果があり、dblink.call()の実行範囲内で有効となります。0を指定すると外部サーバ定義の 'max_value_len' オプション指定は無視され、内部的に列バッファを自動調整しながら検索処理が行なわれます。引数を省略した場合は、外部サーバ定義の 'max_value_len' オプション指定に従い動作します。この引数はOracleコネクタのみで有効となります。
379 |
380 |

381 |

382 | 返値: 383 | 結果の行セットです。 384 |

385 | 386 |

dblink.connections [VIEW]

387 |

388 | 現在使用中の接続の一覧を返すビューです。 389 |

390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 |
カラム名データ型説明
nametext接続に付けた名前です。明示的に指定していない場合、外部サーバ名と同じです。
serveroid接続先の外部サーバの oid です。外部サーバの oid は pg_foreign_server カタログで確認できます。
statustext接続の使用状態です。
- idle : 接続されて何も行なっていない状態
- used : 2相コミットのトランザクション中の状態
- prepared : 2相コミットでのPREPARE状態
use_xaboolean接続が2相コミットを利用しているかどうかのフラグです。use_xa にて指定されます。
keepboolean接続が dblink.connect() 経由で行われたものであるかどうかのフラグです。
dblink.connect() が開始した接続は dblink.disconnect() されるまで接続が維持されます。
423 | 424 |

dblink.postgres(text[], oid) RETURNS boolean

425 |

426 | PostgreSQL に接続するためのコネクタです。 427 | 外部データラッパ検証関数として実装されており、以下の形式で使用します。 428 |

429 |
CREATE FOREIGN DATA WRAPPER postgres VALIDATOR dblink.postgres;
430 | 431 |

dblink.mysql(text[], oid) RETURNS boolean

432 |

433 | MySQL に接続するための外部データラッパ検証関数です。 434 | それ以外は dblink.postgres() と同じです。 435 |

436 | 437 |

dblink.oracle(text[], oid) RETURNS boolean

438 |

439 | Oracle に接続するための外部データラッパ検証関数です。 440 | それ以外は dblink.postgres() と同じです。 441 |

442 | 443 |

設定パラメータ

444 |
445 |
dblink_plus.use_xa
446 |
447 | GUC パラメータとして設定した use_xa の値は、dblink.query() や dblink.exec() などを接続を指定せずに実行した場合に使用されます。 448 | dblink.connect() 時に use_xa を明示的に指定した場合はそちらの値が優先されます。 449 | 特に、dblink.connect() において明示的に use_xa を指定しなかった場合は GUC で設定した値は使用されず、 自動トランザクション有効で接続が開始されます。 450 |
451 | 452 |

このパラメータは postgresql.conf に設定します。

453 |
454 | custom_variable_classes = 'dblink_plus'     # PostgreSQL9.2以降ではこの項目は不要
455 | dblink_plus.use_xa = off                    # 記載しなかった場合は on (true)として動作する
456 | 
457 |

なお、shared_preloaded_librariesへの登録は必要ありません。 458 |

459 | 460 |

使用上の注意と制約

461 |

dblink_plus を使用する際には、以下の使用上の注意と制約があります。

462 | 463 | 464 |

Oracleコネクタで制限となる型

465 |

Oracleのデータ型 LONG, LONG RAW, NCLOB, BLOB, BFILE, RAW の検索についてはサポートしていません。 466 | これらのデータ型を検索した場合、"Not supported data type" のエラー、もしくはOracleのエラー(ORA-XXXXX)が発生します。

467 | 468 |

メモリ消費の問題

469 |

大量のデータを検索した場合、ローカルセッションのメモリを大量に消費する恐れがあります。なるべくサーバーサイドで絞込みを行ったSQLを発行してください。

470 |

特にOracleのCLOB型のについては4Gバイトまでのテキストデータを格納できますが、Oracleコネクタは読み込みに必要なバッファを一度に取得するため、メモリ確保エラーでSQLが異常終了する場合があります。

471 |

Oracleコネクタの外部サーバ定義や dblink.query()、dblink.open()、dblink.call()関数において 'max_value_len' の列データ長を指定した場合、フェッチ用のバッファとして消費されるメモリ量は、おおよそ「(max_value_len+1) × 列数 × フェッチ行数」となります。

472 |

また、処理を中断するとメモリの解放が完全に行われないため、メモリリークの原因になるので、注意してください。

473 | 474 |

PostgreSQLの設定

475 |

PostgreSQLコネクタを使って接続する場合、自動トランザクション管理による2相コミットを有効にするため、リモートデータベースのGUCパラメータ max_prepared_transactions を1以上に設定してください。

476 | 477 |

ユーザに必要なオブジェクト権限

478 | dblink_plus を使用する非スーパーユーザには、以下の3種類の権限を付与してください。 479 |
    480 |
  • dblink スキーマの USAGE 権限
  • 481 |
  • dblink スキーマ内に存在する関数の EXECUTE 権限
  • 482 |
  • dblink スキーマ内に存在するテーブルの DELETE 権限
  • 483 |
484 | 485 |

自動トランザクション管理有効時に使用できないコマンド

486 | 自動トランザクションが有効の場合、トランザクションブロックを使用するため、トランザクションブロック内で実行できないコマンドは使用できません。
487 | 例として以下のコマンドがあります。 488 |
    489 |
  • PostgreSQL では、VACUUM 文
  • 490 |
  • ORACLE では、CREATE TABLE 文や TRUNCATE 文等の DDL
  • 491 |
492 | 493 |

log_statement の設定

494 | dblink_plus は、SELECT 文で実行されるため、ローカルのGUCパラメータ log_statement を mod に設定して更新系SQLを実行してもログには残りません。そのため、dblink_plus を使用したログを残すためには、ローカル側の log_statement を all に設定してください。 495 | 496 |

ホットスタンバイサーバでのdblink_plus

497 |

自動トランザクション管理が有効の場合、接続先の DB サーバとの間で2相コミットを行うために内部で DELETE トリガを使用します。そのため、レプリケーション構成でのレプリカ側サーバやホットスタンバイサーバ等の参照専用のDBでは、自動トランザクション管理を有効にして動作させることができません。 498 | 実行しようとした場合、以下のエラーが発生します。 499 |

500 |
501 | postgres=# SELECT * FROM dblink.query('server_oracle', 'SELECT * FROM tbl') AS t(c1 int, c2 text);
502 | ERROR:  cannot execute DELETE in a read-only transaction
503 | CONTEXT:  SQL statement "DELETE FROM dblink.atcommit"
504 | 
505 | 506 |

これらのサーバで dblink_plus を使用する場合、自動トランザクション管理を無効にして外部サーバに接続する必要があります。 507 | use_xa を false にした接続を作成して使用するか、postgresql.conf にて use_xa を off に設定してください。

508 | 以下は、use_xa を false にした接続を明示的に作成する例です。 509 |

510 |
511 | postgres=# SELECT dblink.connect('my_conn', 'server_oracle', false);
512 |  connect
513 | ---------
514 |  t
515 | (1 行)
516 | 
517 | postgres=# SELECT * FROM dblink.connections ;
518 |   name   | server | status | use_xa | keep
519 | ---------+--------+--------+--------+------
520 |  my_conn |  16594 | idle   | f      | t
521 | (1 行)
522 | 
523 | postgres=# SELECT * FROM dblink.query('my_conn', 'SELECT * FROM tbl') AS t(c1 int, c2 text);
524 |  c1  |   c2
525 | -----+--------
526 |  204 | remote
527 |  403 | remote
528 |  104 | remote
529 |  203 | remote
530 | (4 行)
531 | 
532 |

533 | postgresql.conf に use_xa を off で設定した場合、明示的に use_xa を false で開いた接続を用意しなくても各 API が実行できるようになります。ただし、 use_xa を引数として指定せずに dblink.connect() を実行すると、その接続は use_xa が true になります。API 実行時に接続を指定する場合は上記の様に use_xa を false として明示的に指定した接続を使用してください。 534 |

535 | 536 |

なお、自動トランザクション管理を無効にすることで dblink_plus は接続先のDBサーバとの間で2相コミットを行わなくなるため、DBサーバ間でデータの一貫性を保たれるようにクライアント側で考慮する必要があります。 537 |

538 | 539 | 540 |

詳細

541 |

全体構成

542 | 543 | 544 |

dblink.call()で呼出し可能ファンクションの型式

545 |

546 | 呼出し可能のファンクションは、各DBMSによって作成方法に制約があります。 547 |

548 |

Oracleのストアドプロシージャ

549 |

カーソルとの連携のため、パッケージの作成とカーソル型の定義、およびレコードを出力するプロシージャを作成する必要があります。

550 |

下の例は、dblinkというパッケージを作成し、そのパッケージ内プロシージャとしてf_test()を作成しています。

551 |
552 | CREATE OR REPLACE PACKAGE dblink AS
553 | TYPE RT1 IS RECORD ( ret NUMBER );
554 | TYPE RCT1 IS REF CURSOR RETURN RT1;
555 | PROCEDURE f_test(RC IN OUT RCT1, num IN NUMBER);
556 | END;
557 | 
558 | CREATE OR REPLACE PACKAGE BODY dblink AS
559 | PROCEDURE f_test(RC IN OUT RCT1, num IN NUMBER)
560 | AS
561 | BEGIN
562 |     OPEN RC FOR
563 |     select c1 + num from test_tbl;
564 | END f_test;
565 | END;
566 | 
567 |

このプロシージャは、以下のようにcall関数を使って呼び出します。

568 |
569 | =# select * from dblink.call('sv_ora', 'dblink.f_test(100)') as t(id integer);
570 | 
571 | 572 |

PostgreSQLのストアドファンクション

573 | 出力パラメータを指定していない SETOF RECORD を戻すファンクションは呼び出すことができません。これは、PostgreSQL用call関数が、'select * from function()' の型式で関数を呼び出すためです。
574 | なお、'function()'の後ろに別のSQL文を続けて指定すると、後者のSQL文も実行されるので、注意してください。 575 | 576 |

インストール方法

577 |

dblink_plus のインストールは、標準のcontribモジュールと同様です。

578 | 579 |

ビルド

580 |

pgxs を使ってビルドできます。

581 |
$ cd dblink_plus
582 | $ make 
583 | $ make install
584 |

585 | デフォルトでは全てのコネクタがビルド対象となります。特定のコネクタを除外するにはコネクタフラグに0を指定します。 586 |

587 |

588 | $ make MYSQL=0 SQLITE3=0
589 | $ make MYSQL=0 SQLITE3=0 install
590 | 
591 |

592 | コネクタフラグには ORACLE, MYSQL, SQLITE3 があります。またコネクタとして PostgreSQL は除外することはできません。 593 |

594 | 595 |

データベースへの登録

596 |

dblink_plus を利用する場合には、対象のデータベースに対して $PGHOME/share/contrib/dblink_plus.sql を実行してください。

597 |
$ psql -d dbname -f $PGHOME/share/contrib/dblink_plus.sql
598 | 599 |

PostgreSQL 9.1以降であれば、上記の代わりに CREATE EXTENSION コマンドでも登録可能です。

600 |
postgres=# CREATE EXTENSION dblink_plus;
601 | 602 | なお、dblink_plus を使って外部サーバへのアクセスを行うためには、ここからさらに接続先のDBサーバに応じて、外部データラッパ、外部サーバ、ユーザマッピングを作成する必要があります。 603 | 使用例を参照してください。 604 | 605 |

データベースからの登録解除

606 |

dblink_plus が提供するコネクタを使用する全ての外部データラッパを削除下の後に、アンインストールスクリプトを実行してください。

607 |
$ psql -d dbname -f $PGHOME/share/contrib/uninstall_dblink_plus.sql
608 | 609 |

PostgreSQL 9.1以降であれば、上記の代わりに DROP EXTENSION コマンドでも削除可能です。

610 |
postgres=# DROP EXTENSION dblink_plus CASCADE;
611 |

dblink_plus のために作成した外部サーバなどを削除していない場合は、DROP EXTENSION と同時に削除するように CASCADE 句を付ける必要があります。

612 | 613 |

動作環境

614 |
615 |
PostgreSQLバージョン
616 |
PostgreSQL 9.2, 9.3, 9.4, 9.5, 9.6, 10, 11, 12, 13, 14, 15, 16, 17
617 |
Oracleバージョン
618 |
Oracle 11g, 12c, 18c, 19c
619 |
OS
620 |
RHEL 6/7/8/9
621 |
622 | 623 |

関連項目

624 | dblink, 625 | CREATE FOREIGN DATA WRAPPER, 626 | CREATE SERVER, 627 | CREATE USER MAPPING 628 | 629 |
630 | 631 | 632 | 633 | 634 | -------------------------------------------------------------------------------- /docs/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: 3 | Lucida Grande, Verdana, Arial, Helvetica, 4 | 'メイリオ', 5 | 'Meiryo', 6 | 'ヒラギノ角ゴ Pro W3', 7 | 'Hiragino Kaku Gothic Pro', 8 | 'Osaka', 9 | 'MS Pゴシック', 10 | sans-serif; 11 | color: #202020; 12 | } 13 | 14 | /* give the rule a bit of extra space (above and below), since its being used to divide 15 | sections on some pages (project summary) */ 16 | HR { margin: 5px 0px 5px 0px } 17 | 18 | 19 | h2, h3, h4, h5, h6 { 20 | color: Black; 21 | background: none; 22 | padding-top: 0.5em; 23 | padding-bottom: 0.17em; 24 | border-bottom: 1px solid #aaaaaa; 25 | } 26 | H1 { font-size: x-large; font-family: Lucida Grande,verdana,arial,helvetica,sans-serif; } 27 | H2 { font-size: large; font-family: Lucida Grande,verdana,arial,helvetica,sans-serif; } 28 | H3 { padding-left: 1em; font-size: medium; font-family: Lucida Grande,verdana,arial,helvetica,sans-serif; } 29 | H4 { padding-left: 2em; font-size: small; font-family: Lucida Grande,verdana,arial,helvetica,sans-serif; } 30 | H5 { padding-left: 3em; font-size: x-small; font-family: Lucida Grande,verdana,arial,helvetica,sans-serif; } 31 | H6 { padding-left: 4em; font-size: xx-small; font-family: Lucida Grande,verdana,arial,helvetica,sans-serif; } 32 | 33 | pre { 34 | font-family: courier,sans-serif; 35 | background-color: #FBFBFD; 36 | border: 1px dashed #7E7ECB; 37 | color: black; 38 | line-height: 1.1em; padding: 0.5em; 39 | overflow: auto; 40 | } 41 | 42 | li { 43 | line-height: 1.4em; 44 | } 45 | 46 | table { 47 | background: #f9f9f9; 48 | border: 1px solid #aaa; 49 | border-collapse: collapse; 50 | } 51 | 52 | th, td { 53 | border: 1px solid #aaa; 54 | padding: 0.2em; 55 | } 56 | 57 | thead th { 58 | background: #f2f2f2; 59 | text-align: center; 60 | } 61 | 62 | tbody th { 63 | background: #f2f2f2; 64 | text-align: left; 65 | } 66 | 67 | div.index { 68 | float:right; 69 | border:thin solid black; 70 | background-color: white; 71 | padding-top: 0.2em; 72 | padding-bottom: 0.2em; 73 | padding-left: 1em; 74 | padding-right: 1em; 75 | margin-left: 0.5em; 76 | } 77 | 78 | p.footer { 79 | text-align: right; 80 | font-size: small; 81 | } 82 | -------------------------------------------------------------------------------- /expected/init.out: -------------------------------------------------------------------------------- 1 | SET client_min_messages = warning; 2 | \set ECHO none 3 | RESET client_min_messages; 4 | \! sh regress_init.sh 5 | -------------------------------------------------------------------------------- /expected/mysql.out: -------------------------------------------------------------------------------- 1 | CREATE FOREIGN DATA WRAPPER mysql VALIDATOR dblink.mysql; 2 | CREATE SERVER server_mysql FOREIGN DATA WRAPPER mysql OPTIONS (dbname 'mysql'); 3 | CREATE USER MAPPING FOR CURRENT_USER SERVER server_mysql OPTIONS (user 'root', password 'mysql'); 4 | SELECT dblink.connect('conn_mysql', 'server_mysql', false); 5 | connect 6 | --------- 7 | t 8 | (1 row) 9 | 10 | SELECT dblink.exec('conn_mysql', 'DROP TABLE IF EXISTS dblink_tbl'); 11 | exec 12 | ------ 13 | 0 14 | (1 row) 15 | 16 | SELECT dblink.exec('conn_mysql', 'CREATE TABLE dblink_tbl (id integer, value text) ENGINE = InnoDB'); 17 | exec 18 | ------ 19 | 0 20 | (1 row) 21 | 22 | SELECT dblink.disconnect('conn_mysql'); 23 | disconnect 24 | ------------ 25 | t 26 | (1 row) 27 | 28 | -- dblink.connect() 29 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 30 | name | srvname | status | keep 31 | ------+---------+--------+------ 32 | (0 rows) 33 | 34 | SELECT dblink.connect('conn_mysql', 'server_mysql'); 35 | connect 36 | --------- 37 | t 38 | (1 row) 39 | 40 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 41 | name | srvname | status | keep 42 | ------------+--------------+--------+------ 43 | conn_mysql | server_mysql | idle | t 44 | (1 row) 45 | 46 | -- dblink.exec() with an existing connection 47 | SELECT dblink.exec('conn_mysql', 'INSERT INTO dblink_tbl VALUES(1, ''X'')'); 48 | exec 49 | ------ 50 | 1 51 | (1 row) 52 | 53 | SELECT dblink.exec('conn_mysql', 'INSERT INTO dblink_tbl VALUES(2, ''BB'')'); 54 | exec 55 | ------ 56 | 1 57 | (1 row) 58 | 59 | SELECT dblink.exec('conn_mysql', 'INSERT INTO dblink_tbl VALUES(3, ''CCC'')'); 60 | exec 61 | ------ 62 | 1 63 | (1 row) 64 | 65 | SELECT dblink.exec('conn_mysql', 'INSERT INTO dblink_tbl VALUES(4, ''DDDD'')'); 66 | exec 67 | ------ 68 | 1 69 | (1 row) 70 | 71 | SELECT dblink.exec('conn_mysql', 'INSERT INTO dblink_tbl VALUES(5, NULL)'); 72 | exec 73 | ------ 74 | 1 75 | (1 row) 76 | 77 | SELECT * FROM dblink.query('conn_mysql', 'SELECT * FROM dblink_tbl ORDER BY id') AS (id integer, value text); 78 | id | value 79 | ----+------- 80 | 1 | X 81 | 2 | BB 82 | 3 | CCC 83 | 4 | DDDD 84 | 5 | 85 | (5 rows) 86 | 87 | BEGIN; 88 | SELECT dblink.exec('conn_mysql', 'UPDATE dblink_tbl SET value = ''A'' WHERE id = 1'); 89 | exec 90 | ------ 91 | 1 92 | (1 row) 93 | 94 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 95 | name | srvname | status | keep 96 | ------------+--------------+--------+------ 97 | conn_mysql | server_mysql | used | t 98 | (1 row) 99 | 100 | ROLLBACK; 101 | SELECT * FROM dblink.query('conn_mysql', 'SELECT * FROM dblink_tbl ORDER BY id') AS (id integer, value text); 102 | id | value 103 | ----+------- 104 | 1 | X 105 | 2 | BB 106 | 3 | CCC 107 | 4 | DDDD 108 | 5 | 109 | (5 rows) 110 | 111 | BEGIN; 112 | SELECT dblink.exec('conn_mysql', 'UPDATE dblink_tbl SET value = ''A'' WHERE id = 1'); 113 | exec 114 | ------ 115 | 1 116 | (1 row) 117 | 118 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 119 | name | srvname | status | keep 120 | ------------+--------------+--------+------ 121 | conn_mysql | server_mysql | used | t 122 | (1 row) 123 | 124 | COMMIT; 125 | SELECT * FROM dblink.query('conn_mysql', 'SELECT * FROM dblink_tbl ORDER BY id') AS (id integer, value text); 126 | id | value 127 | ----+------- 128 | 1 | A 129 | 2 | BB 130 | 3 | CCC 131 | 4 | DDDD 132 | 5 | 133 | (5 rows) 134 | 135 | -- dblink.query() with an existing connection 136 | BEGIN; 137 | SELECT * FROM dblink.query('conn_mysql', 'SELECT * FROM dblink_tbl ORDER BY id') AS t(id integer, value text) ORDER BY id; 138 | id | value 139 | ----+------- 140 | 1 | A 141 | 2 | BB 142 | 3 | CCC 143 | 4 | DDDD 144 | 5 | 145 | (5 rows) 146 | 147 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 148 | name | srvname | status | keep 149 | ------------+--------------+--------+------ 150 | conn_mysql | server_mysql | used | t 151 | (1 row) 152 | 153 | COMMIT; 154 | -- dblink.open() with an existing connection 155 | SELECT * FROM dblink.cursor_test(dblink.open('conn_mysql', 'SELECT * FROM dblink_tbl ORDER BY id'), 100); 156 | id | value 157 | ----+------- 158 | 1 | A 159 | 2 | BB 160 | 3 | CCC 161 | 4 | DDDD 162 | 5 | 163 | (5 rows) 164 | 165 | SELECT * FROM dblink.cursor_test(dblink.open('conn_mysql', 'SELECT * FROM dblink_tbl ORDER BY id'), 2); 166 | id | value 167 | ----+------- 168 | 1 | A 169 | 2 | BB 170 | 3 | CCC 171 | 4 | DDDD 172 | 5 | 173 | (5 rows) 174 | 175 | SELECT * FROM dblink.cursor_test(dblink.open('conn_mysql', 'SELECT * FROM dblink_tbl ORDER BY id'), 1); 176 | id | value 177 | ----+------- 178 | 1 | A 179 | 2 | BB 180 | 3 | CCC 181 | (3 rows) 182 | 183 | -- dblink.disconnect() 184 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 185 | name | srvname | status | keep 186 | ------------+--------------+--------+------ 187 | conn_mysql | server_mysql | idle | t 188 | (1 row) 189 | 190 | SELECT dblink.disconnect('conn_mysql'); 191 | disconnect 192 | ------------ 193 | t 194 | (1 row) 195 | 196 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 197 | name | srvname | status | keep 198 | ------+---------+--------+------ 199 | (0 rows) 200 | 201 | -- dblink.query() with an anonymous connection 202 | BEGIN; 203 | SELECT * FROM dblink.query('server_mysql', 'SELECT * FROM dblink_tbl ORDER BY id') AS t(id integer, value text) ORDER BY id; 204 | id | value 205 | ----+------- 206 | 1 | A 207 | 2 | BB 208 | 3 | CCC 209 | 4 | DDDD 210 | 5 | 211 | (5 rows) 212 | 213 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 214 | name | srvname | status | keep 215 | --------------+--------------+--------+------ 216 | server_mysql | server_mysql | used | f 217 | (1 row) 218 | 219 | COMMIT; 220 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 221 | name | srvname | status | keep 222 | ------+---------+--------+------ 223 | (0 rows) 224 | 225 | -- dblink.exec() with an anonymous connection 226 | BEGIN; 227 | SELECT dblink.exec('server_mysql', 'UPDATE dblink_tbl SET value = value WHERE id < 3'); 228 | exec 229 | ------ 230 | 0 231 | (1 row) 232 | 233 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 234 | name | srvname | status | keep 235 | --------------+--------------+--------+------ 236 | server_mysql | server_mysql | used | f 237 | (1 row) 238 | 239 | COMMIT; 240 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 241 | name | srvname | status | keep 242 | ------+---------+--------+------ 243 | (0 rows) 244 | 245 | -- dblink.open() with an anonymous connection 246 | SELECT * FROM dblink.cursor_test(dblink.open('server_mysql', 'SELECT * FROM dblink_tbl ORDER BY id'), 100); 247 | id | value 248 | ----+------- 249 | 1 | A 250 | 2 | BB 251 | 3 | CCC 252 | 4 | DDDD 253 | 5 | 254 | (5 rows) 255 | 256 | SELECT * FROM dblink.cursor_test(dblink.open('server_mysql', 'SELECT * FROM dblink_tbl ORDER BY id'), 2); 257 | id | value 258 | ----+------- 259 | 1 | A 260 | 2 | BB 261 | 3 | CCC 262 | 4 | DDDD 263 | 5 | 264 | (5 rows) 265 | 266 | SELECT * FROM dblink.cursor_test(dblink.open('server_mysql', 'SELECT * FROM dblink_tbl ORDER BY id'), 1); 267 | id | value 268 | ----+------- 269 | 1 | A 270 | 2 | BB 271 | 3 | CCC 272 | (3 rows) 273 | 274 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 275 | name | srvname | status | keep 276 | ------+---------+--------+------ 277 | (0 rows) 278 | 279 | -- dblink.query() with max_value_len option 280 | SELECT * FROM dblink.query('server_mysql', 'SELECT * FROM dblink_tbl ORDER BY id', 0, 1) AS (id integer, value text); 281 | id | value 282 | ----+------- 283 | 1 | A 284 | 2 | BB 285 | 3 | CCC 286 | 4 | DDDD 287 | 5 | 288 | (5 rows) 289 | 290 | -- dblink.open() with max_value_len option 291 | SELECT * FROM dblink.cursor_test(dblink.open('server_mysql', 'SELECT * FROM dblink_tbl ORDER BY id', 100, 1), 100); 292 | id | value 293 | ----+------- 294 | 1 | A 295 | 2 | BB 296 | 3 | CCC 297 | 4 | DDDD 298 | 5 | 299 | (5 rows) 300 | 301 | -- connection without automatic transaction management 302 | SELECT dblink.connect('conn_mysql', 'server_mysql', false); 303 | connect 304 | --------- 305 | t 306 | (1 row) 307 | 308 | SELECT dblink.exec('conn_mysql', 'DROP TABLE IF EXISTS dblink_tbl'); 309 | exec 310 | ------ 311 | 0 312 | (1 row) 313 | 314 | SELECT dblink.disconnect('conn_mysql'); 315 | disconnect 316 | ------------ 317 | t 318 | (1 row) 319 | 320 | -------------------------------------------------------------------------------- /expected/oracle.out: -------------------------------------------------------------------------------- 1 | CREATE FOREIGN DATA WRAPPER oracle VALIDATOR dblink.oracle; 2 | CREATE SERVER server_oracle FOREIGN DATA WRAPPER oracle OPTIONS (dbname 'dbt'); 3 | CREATE USER MAPPING FOR CURRENT_USER SERVER server_oracle OPTIONS (user 'scott', password 'tiger'); 4 | CREATE SERVER server_oracle2 FOREIGN DATA WRAPPER oracle OPTIONS (dbname 'dbt', max_value_len '4'); 5 | CREATE USER MAPPING FOR CURRENT_USER SERVER server_oracle2 OPTIONS (user 'scott', password 'tiger'); 6 | CREATE SERVER server_oracle3 FOREIGN DATA WRAPPER oracle OPTIONS (dbname 'dbt', max_value_len '3'); 7 | CREATE USER MAPPING FOR CURRENT_USER SERVER server_oracle3 OPTIONS (user 'scott', password 'tiger'); 8 | SELECT dblink.connect('conn_ora', 'server_oracle', false); 9 | connect 10 | --------- 11 | t 12 | (1 row) 13 | 14 | SELECT dblink.exec('conn_ora', 'CREATE TABLE dblink_tbl (id NUMBER(4), value VARCHAR2(100))'); 15 | exec 16 | ------ 17 | 0 18 | (1 row) 19 | 20 | SELECT dblink.exec('conn_ora', 'CREATE TABLE lob_test (id NUMBER(5), value CLOB, value2 CLOB)'); 21 | exec 22 | ------ 23 | 0 24 | (1 row) 25 | 26 | SELECT dblink.disconnect('conn_ora'); 27 | disconnect 28 | ------------ 29 | t 30 | (1 row) 31 | 32 | SELECT dblink.exec('server_oracle', 'INSERT INTO dblink_tbl VALUES(1, ''X'')'); 33 | exec 34 | ------ 35 | 1 36 | (1 row) 37 | 38 | SELECT dblink.exec('server_oracle', 'INSERT INTO dblink_tbl VALUES(2, ''BB'')'); 39 | exec 40 | ------ 41 | 1 42 | (1 row) 43 | 44 | SELECT dblink.exec('server_oracle', 'INSERT INTO dblink_tbl VALUES(3, ''CCC'')'); 45 | exec 46 | ------ 47 | 1 48 | (1 row) 49 | 50 | SELECT dblink.exec('server_oracle', 'INSERT INTO dblink_tbl VALUES(4, ''DDDD'')'); 51 | exec 52 | ------ 53 | 1 54 | (1 row) 55 | 56 | SELECT dblink.exec('server_oracle', 'INSERT INTO dblink_tbl VALUES(5, NULL)'); 57 | exec 58 | ------ 59 | 1 60 | (1 row) 61 | 62 | CREATE TABLE dblink.dblink_tmp_ora (LIKE dblink.dblink_tbl); 63 | CREATE FUNCTION dblink.cursor_test_ora(cursor integer, howmany integer) 64 | RETURNS SETOF dblink.dblink_tmp_ora AS 65 | $$ 66 | TRUNCATE dblink.dblink_tmp_ora; 67 | INSERT INTO dblink.dblink_tmp_ora SELECT * FROM dblink.fetch($1, $2) AS t(id integer, value text); 68 | INSERT INTO dblink.dblink_tmp_ora SELECT * FROM dblink.fetch($1, $2) AS t(id integer, value text); 69 | INSERT INTO dblink.dblink_tmp_ora SELECT * FROM dblink.fetch($1, $2) AS t(id integer, value text); 70 | SELECT * FROM dblink.dblink_tmp_ora; 71 | $$ 72 | LANGUAGE sql; 73 | -- dblink.connect() 74 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 75 | name | srvname | status | keep 76 | ------+---------+--------+------ 77 | (0 rows) 78 | 79 | SELECT dblink.connect('conn_oracle', 'server_oracle'); 80 | connect 81 | --------- 82 | t 83 | (1 row) 84 | 85 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 86 | name | srvname | status | keep 87 | -------------+---------------+--------+------ 88 | conn_oracle | server_oracle | idle | t 89 | (1 row) 90 | 91 | -- dblink.exec() with an existing connection 92 | SELECT * FROM dblink.query('conn_oracle', 'SELECT * FROM dblink_tbl ORDER BY id') AS t(i int, v text); 93 | i | v 94 | ---+------ 95 | 1 | X 96 | 2 | BB 97 | 3 | CCC 98 | 4 | DDDD 99 | 5 | 100 | (5 rows) 101 | 102 | BEGIN; 103 | SELECT dblink.exec('conn_oracle', 'UPDATE dblink_tbl SET value = ''A'' WHERE id = 1'); 104 | exec 105 | ------ 106 | 1 107 | (1 row) 108 | 109 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 110 | name | srvname | status | keep 111 | -------------+---------------+--------+------ 112 | conn_oracle | server_oracle | used | t 113 | (1 row) 114 | 115 | ROLLBACK; 116 | SELECT * FROM dblink.query('conn_oracle', 'SELECT * FROM dblink_tbl ORDER BY id') AS t(i int, v text); 117 | i | v 118 | ---+------ 119 | 1 | X 120 | 2 | BB 121 | 3 | CCC 122 | 4 | DDDD 123 | 5 | 124 | (5 rows) 125 | 126 | BEGIN; 127 | SELECT dblink.exec('conn_oracle', 'UPDATE dblink_tbl SET value = ''A'' WHERE id = 1'); 128 | exec 129 | ------ 130 | 1 131 | (1 row) 132 | 133 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 134 | name | srvname | status | keep 135 | -------------+---------------+--------+------ 136 | conn_oracle | server_oracle | used | t 137 | (1 row) 138 | 139 | COMMIT; 140 | SELECT * FROM dblink.query('conn_oracle', 'SELECT * FROM dblink_tbl ORDER BY id') AS t(i int, v text); 141 | i | v 142 | ---+------ 143 | 1 | A 144 | 2 | BB 145 | 3 | CCC 146 | 4 | DDDD 147 | 5 | 148 | (5 rows) 149 | 150 | -- dblink.query() with an existing connection 151 | BEGIN; 152 | SELECT * FROM dblink.query('conn_oracle', 'SELECT * FROM dblink_tbl ORDER BY id') AS t(id integer, value text) ORDER BY id; 153 | id | value 154 | ----+------- 155 | 1 | A 156 | 2 | BB 157 | 3 | CCC 158 | 4 | DDDD 159 | 5 | 160 | (5 rows) 161 | 162 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 163 | name | srvname | status | keep 164 | -------------+---------------+--------+------ 165 | conn_oracle | server_oracle | used | t 166 | (1 row) 167 | 168 | COMMIT; 169 | -- dblink.open() with an existing connection 170 | SELECT * FROM dblink.cursor_test_ora(dblink.open('conn_oracle', 'SELECT * FROM dblink_tbl ORDER BY id'), 100); 171 | id | value 172 | ----+------- 173 | 1 | A 174 | 2 | BB 175 | 3 | CCC 176 | 4 | DDDD 177 | 5 | 178 | (5 rows) 179 | 180 | SELECT * FROM dblink.cursor_test_ora(dblink.open('conn_oracle', 'SELECT * FROM dblink_tbl ORDER BY id'), 2); 181 | id | value 182 | ----+------- 183 | 1 | A 184 | 2 | BB 185 | 3 | CCC 186 | 4 | DDDD 187 | 5 | 188 | (5 rows) 189 | 190 | SELECT * FROM dblink.cursor_test_ora(dblink.open('conn_oracle', 'SELECT * FROM dblink_tbl ORDER BY id'), 1); 191 | id | value 192 | ----+------- 193 | 1 | A 194 | 2 | BB 195 | 3 | CCC 196 | (3 rows) 197 | 198 | -- dblink.disconnect() 199 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 200 | name | srvname | status | keep 201 | -------------+---------------+--------+------ 202 | conn_oracle | server_oracle | idle | t 203 | (1 row) 204 | 205 | SELECT dblink.disconnect('conn_oracle'); 206 | disconnect 207 | ------------ 208 | t 209 | (1 row) 210 | 211 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 212 | name | srvname | status | keep 213 | ------+---------+--------+------ 214 | (0 rows) 215 | 216 | -- dblink.query() with an anonymous connection 217 | BEGIN; 218 | SELECT * FROM dblink.query('server_oracle', 'SELECT * FROM dblink_tbl ORDER BY id') AS t(id integer, value text) ORDER BY id; 219 | id | value 220 | ----+------- 221 | 1 | A 222 | 2 | BB 223 | 3 | CCC 224 | 4 | DDDD 225 | 5 | 226 | (5 rows) 227 | 228 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 229 | name | srvname | status | keep 230 | ---------------+---------------+--------+------ 231 | server_oracle | server_oracle | used | f 232 | (1 row) 233 | 234 | COMMIT; 235 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 236 | name | srvname | status | keep 237 | ------+---------+--------+------ 238 | (0 rows) 239 | 240 | -- dblink.exec() with an anonymous connection 241 | BEGIN; 242 | SELECT dblink.exec('server_oracle', 'UPDATE dblink_tbl SET value = value WHERE id < 3'); 243 | exec 244 | ------ 245 | 2 246 | (1 row) 247 | 248 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 249 | name | srvname | status | keep 250 | ---------------+---------------+--------+------ 251 | server_oracle | server_oracle | used | f 252 | (1 row) 253 | 254 | COMMIT; 255 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 256 | name | srvname | status | keep 257 | ------+---------+--------+------ 258 | (0 rows) 259 | 260 | -- dblink.open() with an anonymous connection 261 | SELECT * FROM dblink.cursor_test_ora(dblink.open('server_oracle', 'SELECT * FROM dblink_tbl ORDER BY id'), 100); 262 | id | value 263 | ----+------- 264 | 1 | A 265 | 2 | BB 266 | 3 | CCC 267 | 4 | DDDD 268 | 5 | 269 | (5 rows) 270 | 271 | SELECT * FROM dblink.cursor_test_ora(dblink.open('server_oracle', 'SELECT * FROM dblink_tbl ORDER BY id'), 2); 272 | id | value 273 | ----+------- 274 | 1 | A 275 | 2 | BB 276 | 3 | CCC 277 | 4 | DDDD 278 | 5 | 279 | (5 rows) 280 | 281 | SELECT * FROM dblink.cursor_test_ora(dblink.open('server_oracle', 'SELECT * FROM dblink_tbl ORDER BY id'), 1); 282 | id | value 283 | ----+------- 284 | 1 | A 285 | 2 | BB 286 | 3 | CCC 287 | (3 rows) 288 | 289 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 290 | name | srvname | status | keep 291 | ------+---------+--------+------ 292 | (0 rows) 293 | 294 | -- CLOB data read 295 | SELECT dblink.exec('server_oracle', 'insert into lob_test values(1, lpad(''Z'', 2000, ''Z''), lpad(''z'', 1000, ''z''))'); 296 | exec 297 | ------ 298 | 1 299 | (1 row) 300 | 301 | SELECT dblink.exec('server_oracle', 'insert into lob_test values(2, lpad(''Y'', 4000, ''Y''), lpad(''y'', 3000, ''y''))'); 302 | exec 303 | ------ 304 | 1 305 | (1 row) 306 | 307 | SELECT id, length(value), length(value2) from dblink.query('server_oracle', 'select * from lob_test') as (id integer, value text, value2 text) order by id; 308 | id | length | length 309 | ----+--------+-------- 310 | 1 | 2000 | 1000 311 | 2 | 4000 | 3000 312 | (2 rows) 313 | 314 | -- dblink.query() with max_value_len option 315 | SELECT * FROM dblink.query('server_oracle2', 'SELECT * FROM dblink_tbl ORDER BY id') AS t(id integer, value text) ORDER BY id; 316 | id | value 317 | ----+------- 318 | 1 | A 319 | 2 | BB 320 | 3 | CCC 321 | 4 | DDDD 322 | 5 | 323 | (5 rows) 324 | 325 | SELECT * FROM dblink.query('server_oracle3', 'SELECT * FROM dblink_tbl ORDER BY id', 0, 4) AS t(id integer, value text) ORDER BY id; 326 | id | value 327 | ----+------- 328 | 1 | A 329 | 2 | BB 330 | 3 | CCC 331 | 4 | DDDD 332 | 5 | 333 | (5 rows) 334 | 335 | -- dblink.open() with max_value_len option 336 | SELECT * FROM dblink.cursor_test_ora(dblink.open('server_oracle', 'SELECT * FROM dblink_tbl ORDER BY id', 100, 4), 100); 337 | id | value 338 | ----+------- 339 | 1 | A 340 | 2 | BB 341 | 3 | CCC 342 | 4 | DDDD 343 | 5 | 344 | (5 rows) 345 | 346 | -- connection without automatic transaction management 347 | SELECT dblink.connect('conn_oracle', 'server_oracle', false); 348 | connect 349 | --------- 350 | t 351 | (1 row) 352 | 353 | SELECT dblink.exec('conn_oracle', 'DROP TABLE dblink_tbl'); 354 | exec 355 | ------ 356 | 0 357 | (1 row) 358 | 359 | SELECT dblink.exec('conn_oracle', 'DROP TABLE lob_test'); 360 | exec 361 | ------ 362 | 0 363 | (1 row) 364 | 365 | SELECT dblink.disconnect('conn_oracle'); 366 | disconnect 367 | ------------ 368 | t 369 | (1 row) 370 | 371 | SELECT dblink.connect('conn_oracle', 'server_oracle', false); 372 | connect 373 | --------- 374 | t 375 | (1 row) 376 | 377 | SELECT dblink.exec('conn_oracle', 'CREATE OR REPLACE PACKAGE dblink AS TYPE RT1 IS RECORD ( ret NUMBER ); TYPE RCT1 IS REF CURSOR RETURN RT1; PROCEDURE f_test(RC1 IN OUT RCT1, num IN NUMBER); END;'); 378 | exec 379 | ------ 380 | 0 381 | (1 row) 382 | 383 | SELECT dblink.exec('conn_oracle', 'CREATE OR REPLACE PACKAGE BODY dblink AS PROCEDURE f_test(RC1 IN OUT RCT1, num IN NUMBER) AS BEGIN OPEN RC1 FOR select num + 1000 from dual; END f_test; END;'); 384 | exec 385 | ------ 386 | 0 387 | (1 row) 388 | 389 | SELECT dblink.disconnect('conn_oracle'); 390 | disconnect 391 | ------------ 392 | t 393 | (1 row) 394 | 395 | -- dblink.call() with an anonymous connection 396 | SELECT * FROM dblink.call('server_oracle', 'dblink.f_test(1)') AS t(a integer); 397 | a 398 | ------ 399 | 1001 400 | (1 row) 401 | 402 | -- dblink.call() with oracle standard library function as argument to oracle stored procedure 403 | SELECT * FROM dblink.call('server_oracle', 'dblink.f_test(ceil(3.1))') AS t(a integer); 404 | a 405 | ------ 406 | 1004 407 | (1 row) 408 | 409 | -- dblink.call() with max_value_len option 410 | SELECT * FROM dblink.call('server_oracle2', 'dblink.f_test(1)') AS t(a integer); 411 | a 412 | ------ 413 | 1001 414 | (1 row) 415 | 416 | SELECT * FROM dblink.call('server_oracle3', 'dblink.f_test(1)', 0, 4) AS t(a integer); 417 | a 418 | ------ 419 | 1001 420 | (1 row) 421 | 422 | -------------------------------------------------------------------------------- /expected/postgres.out: -------------------------------------------------------------------------------- 1 | CREATE FOREIGN DATA WRAPPER postgres VALIDATOR dblink.postgres; 2 | CREATE SERVER server_postgres6789012345678901234567890123456789012345678901234567890 FOREIGN DATA WRAPPER postgres OPTIONS (dbname 'contrib_regression'); 3 | NOTICE: identifier "server_postgres6789012345678901234567890123456789012345678901234567890" will be truncated to "server_postgres678901234567890123456789012345678901234567890123" 4 | CREATE USER MAPPING FOR CURRENT_USER SERVER server_postgres678901234567890123456789012345678901234567890123 OPTIONS (user 'postgres'); 5 | CREATE TABLE dblink.dblink_tbl (id integer, value text); 6 | INSERT INTO dblink.dblink_tbl VALUES(1, 'X'); 7 | INSERT INTO dblink.dblink_tbl VALUES(2, 'BB'); 8 | INSERT INTO dblink.dblink_tbl VALUES(3, 'CCC'); 9 | INSERT INTO dblink.dblink_tbl VALUES(4, 'DDDD'); 10 | INSERT INTO dblink.dblink_tbl VALUES(5, NULL); 11 | CREATE TABLE dblink.dblink_tmp (LIKE dblink.dblink_tbl); 12 | CREATE FUNCTION dblink.cursor_test(cursor integer, howmany integer) 13 | RETURNS SETOF dblink.dblink_tmp AS 14 | $$ 15 | TRUNCATE dblink.dblink_tmp; 16 | INSERT INTO dblink.dblink_tmp SELECT * FROM dblink.fetch($1, $2) AS t(id integer, value text); 17 | INSERT INTO dblink.dblink_tmp SELECT * FROM dblink.fetch($1, $2) AS t(id integer, value text); 18 | INSERT INTO dblink.dblink_tmp SELECT * FROM dblink.fetch($1, $2) AS t(id integer, value text); 19 | SELECT dblink.close($1); 20 | SELECT * FROM dblink.dblink_tmp; 21 | $$ 22 | LANGUAGE sql; 23 | -- dblink.connect() 24 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 25 | name | srvname | status | keep 26 | ------+---------+--------+------ 27 | (0 rows) 28 | 29 | SELECT dblink.connect('conn_postgres456789012345678901234567890123456789012345678901234567890', 'server_postgres6789012345678901234567890123456789012345678901234567890'); 30 | NOTICE: identifier "conn_postgres456789012345678901234567890123456789012345678901234567890" will be truncated to "conn_postgres45678901234567890123456789012345678901234567890123" 31 | connect 32 | --------- 33 | t 34 | (1 row) 35 | 36 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 37 | name | srvname | status | keep 38 | -----------------------------------------------------------------+-----------------------------------------------------------------+--------+------ 39 | conn_postgres45678901234567890123456789012345678901234567890123 | server_postgres678901234567890123456789012345678901234567890123 | idle | t 40 | (1 row) 41 | 42 | -- dblink.exec() with an existing connection 43 | SELECT * FROM dblink.dblink_tbl ORDER BY id; 44 | id | value 45 | ----+------- 46 | 1 | X 47 | 2 | BB 48 | 3 | CCC 49 | 4 | DDDD 50 | 5 | 51 | (5 rows) 52 | 53 | BEGIN; 54 | SELECT dblink.exec('conn_postgres456789012345678901234567890123456789012345678901234567890', 'UPDATE dblink.dblink_tbl SET value = ''A'' WHERE id = 1'); 55 | exec 56 | ------ 57 | 1 58 | (1 row) 59 | 60 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 61 | name | srvname | status | keep 62 | -----------------------------------------------------------------+-----------------------------------------------------------------+--------+------ 63 | conn_postgres45678901234567890123456789012345678901234567890123 | server_postgres678901234567890123456789012345678901234567890123 | used | t 64 | (1 row) 65 | 66 | ROLLBACK; 67 | SELECT * FROM dblink.dblink_tbl ORDER BY id; 68 | id | value 69 | ----+------- 70 | 1 | X 71 | 2 | BB 72 | 3 | CCC 73 | 4 | DDDD 74 | 5 | 75 | (5 rows) 76 | 77 | BEGIN; 78 | SELECT dblink.exec('conn_postgres456789012345678901234567890123456789012345678901234567890', 'UPDATE dblink.dblink_tbl SET value = ''A'' WHERE id = 1'); 79 | exec 80 | ------ 81 | 1 82 | (1 row) 83 | 84 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 85 | name | srvname | status | keep 86 | -----------------------------------------------------------------+-----------------------------------------------------------------+--------+------ 87 | conn_postgres45678901234567890123456789012345678901234567890123 | server_postgres678901234567890123456789012345678901234567890123 | used | t 88 | (1 row) 89 | 90 | COMMIT; 91 | SELECT * FROM dblink.dblink_tbl ORDER BY id; 92 | id | value 93 | ----+------- 94 | 1 | A 95 | 2 | BB 96 | 3 | CCC 97 | 4 | DDDD 98 | 5 | 99 | (5 rows) 100 | 101 | -- dblink.query() with an existing connection 102 | BEGIN; 103 | SELECT * FROM dblink.query('conn_postgres456789012345678901234567890123456789012345678901234567890', 'SELECT * FROM dblink.dblink_tbl ORDER BY id') AS t(id integer, value text) ORDER BY id; 104 | id | value 105 | ----+------- 106 | 1 | A 107 | 2 | BB 108 | 3 | CCC 109 | 4 | DDDD 110 | 5 | 111 | (5 rows) 112 | 113 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 114 | name | srvname | status | keep 115 | -----------------------------------------------------------------+-----------------------------------------------------------------+--------+------ 116 | conn_postgres45678901234567890123456789012345678901234567890123 | server_postgres678901234567890123456789012345678901234567890123 | used | t 117 | (1 row) 118 | 119 | COMMIT; 120 | -- dblink.open() with an existing connection 121 | SELECT * FROM dblink.cursor_test(dblink.open('conn_postgres456789012345678901234567890123456789012345678901234567890', 'SELECT * FROM dblink.dblink_tbl ORDER BY id'), 100); 122 | id | value 123 | ----+------- 124 | 1 | A 125 | 2 | BB 126 | 3 | CCC 127 | 4 | DDDD 128 | 5 | 129 | (5 rows) 130 | 131 | SELECT * FROM dblink.cursor_test(dblink.open('conn_postgres456789012345678901234567890123456789012345678901234567890', 'SELECT * FROM dblink.dblink_tbl ORDER BY id'), 2); 132 | id | value 133 | ----+------- 134 | 1 | A 135 | 2 | BB 136 | 3 | CCC 137 | 4 | DDDD 138 | 5 | 139 | (5 rows) 140 | 141 | SELECT * FROM dblink.cursor_test(dblink.open('conn_postgres456789012345678901234567890123456789012345678901234567890', 'SELECT * FROM dblink.dblink_tbl ORDER BY id'), 1); 142 | id | value 143 | ----+------- 144 | 1 | A 145 | 2 | BB 146 | 3 | CCC 147 | (3 rows) 148 | 149 | -- dblink.disconnect() 150 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 151 | name | srvname | status | keep 152 | -----------------------------------------------------------------+-----------------------------------------------------------------+--------+------ 153 | conn_postgres45678901234567890123456789012345678901234567890123 | server_postgres678901234567890123456789012345678901234567890123 | idle | t 154 | (1 row) 155 | 156 | SELECT dblink.disconnect('conn_postgres456789012345678901234567890123456789012345678901234567890'); 157 | disconnect 158 | ------------ 159 | t 160 | (1 row) 161 | 162 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 163 | name | srvname | status | keep 164 | ------+---------+--------+------ 165 | (0 rows) 166 | 167 | -- dblink.query() with an anonymous connection 168 | BEGIN; 169 | SELECT * FROM dblink.query('server_postgres6789012345678901234567890123456789012345678901234567890', 'SELECT * FROM dblink.dblink_tbl ORDER BY id') AS t(id integer, value text) ORDER BY id; 170 | NOTICE: identifier "server_postgres6789012345678901234567890123456789012345678901234567890" will be truncated to "server_postgres678901234567890123456789012345678901234567890123" 171 | id | value 172 | ----+------- 173 | 1 | A 174 | 2 | BB 175 | 3 | CCC 176 | 4 | DDDD 177 | 5 | 178 | (5 rows) 179 | 180 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 181 | name | srvname | status | keep 182 | -----------------------------------------------------------------+-----------------------------------------------------------------+--------+------ 183 | server_postgres678901234567890123456789012345678901234567890123 | server_postgres678901234567890123456789012345678901234567890123 | used | f 184 | (1 row) 185 | 186 | COMMIT; 187 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 188 | name | srvname | status | keep 189 | ------+---------+--------+------ 190 | (0 rows) 191 | 192 | -- dblink.exec() with an anonymous connection 193 | BEGIN; 194 | SELECT dblink.exec('server_postgres6789012345678901234567890123456789012345678901234567890', 'UPDATE dblink.dblink_tbl SET value = value WHERE id < 3'); 195 | NOTICE: identifier "server_postgres6789012345678901234567890123456789012345678901234567890" will be truncated to "server_postgres678901234567890123456789012345678901234567890123" 196 | exec 197 | ------ 198 | 2 199 | (1 row) 200 | 201 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 202 | name | srvname | status | keep 203 | -----------------------------------------------------------------+-----------------------------------------------------------------+--------+------ 204 | server_postgres678901234567890123456789012345678901234567890123 | server_postgres678901234567890123456789012345678901234567890123 | used | f 205 | (1 row) 206 | 207 | COMMIT; 208 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 209 | name | srvname | status | keep 210 | ------+---------+--------+------ 211 | (0 rows) 212 | 213 | -- dblink.open() with an anonymous connection 214 | SELECT * FROM dblink.cursor_test(dblink.open('server_postgres6789012345678901234567890123456789012345678901234567890', 'SELECT * FROM dblink.dblink_tbl ORDER BY id'), 100); 215 | NOTICE: identifier "server_postgres6789012345678901234567890123456789012345678901234567890" will be truncated to "server_postgres678901234567890123456789012345678901234567890123" 216 | id | value 217 | ----+------- 218 | 1 | A 219 | 2 | BB 220 | 3 | CCC 221 | 4 | DDDD 222 | 5 | 223 | (5 rows) 224 | 225 | SELECT * FROM dblink.cursor_test(dblink.open('server_postgres6789012345678901234567890123456789012345678901234567890', 'SELECT * FROM dblink.dblink_tbl ORDER BY id'), 2); 226 | NOTICE: identifier "server_postgres6789012345678901234567890123456789012345678901234567890" will be truncated to "server_postgres678901234567890123456789012345678901234567890123" 227 | id | value 228 | ----+------- 229 | 1 | A 230 | 2 | BB 231 | 3 | CCC 232 | 4 | DDDD 233 | 5 | 234 | (5 rows) 235 | 236 | SELECT * FROM dblink.cursor_test(dblink.open('server_postgres6789012345678901234567890123456789012345678901234567890', 'SELECT * FROM dblink.dblink_tbl ORDER BY id'), 1); 237 | NOTICE: identifier "server_postgres6789012345678901234567890123456789012345678901234567890" will be truncated to "server_postgres678901234567890123456789012345678901234567890123" 238 | id | value 239 | ----+------- 240 | 1 | A 241 | 2 | BB 242 | 3 | CCC 243 | (3 rows) 244 | 245 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 246 | name | srvname | status | keep 247 | ------+---------+--------+------ 248 | (0 rows) 249 | 250 | -- dblink.query() with max_value_len option 251 | SELECT * FROM dblink.query('server_postgres6789012345678901234567890123456789012345678901234567890', 'SELECT * FROM dblink.dblink_tbl ORDER BY id', 0, 1) AS t(id integer, value text) ORDER BY id; 252 | NOTICE: identifier "server_postgres6789012345678901234567890123456789012345678901234567890" will be truncated to "server_postgres678901234567890123456789012345678901234567890123" 253 | id | value 254 | ----+------- 255 | 1 | A 256 | 2 | BB 257 | 3 | CCC 258 | 4 | DDDD 259 | 5 | 260 | (5 rows) 261 | 262 | -- dblink.open() with max_value_len option 263 | SELECT * FROM dblink.cursor_test(dblink.open('server_postgres6789012345678901234567890123456789012345678901234567890', 'SELECT * FROM dblink.dblink_tbl ORDER BY id', 100, 1), 100); 264 | NOTICE: identifier "server_postgres6789012345678901234567890123456789012345678901234567890" will be truncated to "server_postgres678901234567890123456789012345678901234567890123" 265 | id | value 266 | ----+------- 267 | 1 | A 268 | 2 | BB 269 | 3 | CCC 270 | 4 | DDDD 271 | 5 | 272 | (5 rows) 273 | 274 | -- connection without automatic transaction management 275 | SELECT dblink.connect('conn_postgres456789012345678901234567890123456789012345678901234567890', 'server_postgres6789012345678901234567890123456789012345678901234567890', false); 276 | NOTICE: identifier "conn_postgres456789012345678901234567890123456789012345678901234567890" will be truncated to "conn_postgres45678901234567890123456789012345678901234567890123" 277 | connect 278 | --------- 279 | t 280 | (1 row) 281 | 282 | SELECT dblink.exec('conn_postgres456789012345678901234567890123456789012345678901234567890', 'VACUUM dblink.dblink_tbl'); 283 | exec 284 | ------ 285 | 0 286 | (1 row) 287 | 288 | SELECT dblink.disconnect('conn_postgres456789012345678901234567890123456789012345678901234567890'); 289 | disconnect 290 | ------------ 291 | t 292 | (1 row) 293 | 294 | create function dblink.f_test(num integer) 295 | returns integer as 296 | $$ 297 | select $1 + 1000; 298 | $$ 299 | language 'sql'; 300 | -- dblink.call() with an anonymous connection 301 | SELECT * FROM dblink.call('server_postgres6789012345678901234567890123456789012345678901234567890', 'dblink.f_test(1)') AS t(a integer); 302 | NOTICE: identifier "server_postgres6789012345678901234567890123456789012345678901234567890" will be truncated to "server_postgres678901234567890123456789012345678901234567890123" 303 | a 304 | ------ 305 | 1001 306 | (1 row) 307 | 308 | -- dblink.call() with max_value_len option 309 | SELECT * FROM dblink.call('server_postgres6789012345678901234567890123456789012345678901234567890', 'dblink.f_test(1)', 0, 1) AS t(a integer); 310 | NOTICE: identifier "server_postgres6789012345678901234567890123456789012345678901234567890" will be truncated to "server_postgres678901234567890123456789012345678901234567890123" 311 | a 312 | ------ 313 | 1001 314 | (1 row) 315 | 316 | -------------------------------------------------------------------------------- /expected/sqlite3.out: -------------------------------------------------------------------------------- 1 | CREATE FOREIGN DATA WRAPPER sqlite3 VALIDATOR dblink.sqlite3; 2 | CREATE SERVER server_sqlite3 FOREIGN DATA WRAPPER sqlite3 OPTIONS (location ':memory'); 3 | CREATE USER MAPPING FOR CURRENT_USER SERVER server_sqlite3; 4 | -- dblink.connect() 5 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 6 | name | srvname | status | keep 7 | ------+---------+--------+------ 8 | (0 rows) 9 | 10 | SELECT dblink.connect('conn_sqlite3', 'server_sqlite3'); 11 | connect 12 | --------- 13 | t 14 | (1 row) 15 | 16 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 17 | name | srvname | status | keep 18 | --------------+----------------+--------+------ 19 | conn_sqlite3 | server_sqlite3 | idle | t 20 | (1 row) 21 | 22 | -- dblink.exec() with an existing connection 23 | SELECT dblink.exec('conn_sqlite3', 'DROP TABLE IF EXISTS dblink_tbl'); 24 | exec 25 | ------ 26 | 0 27 | (1 row) 28 | 29 | SELECT dblink.exec('conn_sqlite3', 'CREATE TABLE dblink_tbl (id integer, value text)'); 30 | exec 31 | ------ 32 | 0 33 | (1 row) 34 | 35 | SELECT dblink.exec('conn_sqlite3', 'INSERT INTO dblink_tbl VALUES(1, ''X'')'); 36 | exec 37 | ------ 38 | 1 39 | (1 row) 40 | 41 | SELECT dblink.exec('conn_sqlite3', 'INSERT INTO dblink_tbl VALUES(2, ''BB'')'); 42 | exec 43 | ------ 44 | 1 45 | (1 row) 46 | 47 | SELECT dblink.exec('conn_sqlite3', 'INSERT INTO dblink_tbl VALUES(3, ''CCC'')'); 48 | exec 49 | ------ 50 | 1 51 | (1 row) 52 | 53 | SELECT dblink.exec('conn_sqlite3', 'INSERT INTO dblink_tbl VALUES(4, ''DDDD'')'); 54 | exec 55 | ------ 56 | 1 57 | (1 row) 58 | 59 | SELECT dblink.exec('conn_sqlite3', 'INSERT INTO dblink_tbl VALUES(5, NULL)'); 60 | exec 61 | ------ 62 | 1 63 | (1 row) 64 | 65 | SELECT * FROM dblink.query('server_sqlite3', 'SELECT * FROM dblink_tbl ORDER BY id') AS (id integer, value text); 66 | id | value 67 | ----+------- 68 | 1 | X 69 | 2 | BB 70 | 3 | CCC 71 | 4 | DDDD 72 | 5 | 73 | (5 rows) 74 | 75 | BEGIN; 76 | SELECT dblink.exec('conn_sqlite3', 'UPDATE dblink_tbl SET value = ''A'' WHERE id = 1'); 77 | exec 78 | ------ 79 | 1 80 | (1 row) 81 | 82 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 83 | name | srvname | status | keep 84 | --------------+----------------+--------+------ 85 | conn_sqlite3 | server_sqlite3 | used | t 86 | (1 row) 87 | 88 | ROLLBACK; 89 | SELECT * FROM dblink.query('server_sqlite3', 'SELECT * FROM dblink_tbl ORDER BY id') AS (id integer, value text); 90 | id | value 91 | ----+------- 92 | 1 | X 93 | 2 | BB 94 | 3 | CCC 95 | 4 | DDDD 96 | 5 | 97 | (5 rows) 98 | 99 | BEGIN; 100 | SELECT dblink.exec('conn_sqlite3', 'UPDATE dblink_tbl SET value = ''A'' WHERE id = 1'); 101 | exec 102 | ------ 103 | 1 104 | (1 row) 105 | 106 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 107 | name | srvname | status | keep 108 | --------------+----------------+--------+------ 109 | conn_sqlite3 | server_sqlite3 | used | t 110 | (1 row) 111 | 112 | COMMIT; 113 | SELECT * FROM dblink.query('server_sqlite3', 'SELECT * FROM dblink_tbl ORDER BY id') AS (id integer, value text); 114 | id | value 115 | ----+------- 116 | 1 | A 117 | 2 | BB 118 | 3 | CCC 119 | 4 | DDDD 120 | 5 | 121 | (5 rows) 122 | 123 | -- dblink.query() with an existing connection 124 | BEGIN; 125 | SELECT * FROM dblink.query('conn_sqlite3', 'SELECT * FROM dblink_tbl ORDER BY id') AS t(id integer, value text) ORDER BY id; 126 | id | value 127 | ----+------- 128 | 1 | A 129 | 2 | BB 130 | 3 | CCC 131 | 4 | DDDD 132 | 5 | 133 | (5 rows) 134 | 135 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 136 | name | srvname | status | keep 137 | --------------+----------------+--------+------ 138 | conn_sqlite3 | server_sqlite3 | used | t 139 | (1 row) 140 | 141 | COMMIT; 142 | -- dblink.open() with an existing connection 143 | SELECT * FROM dblink.cursor_test(dblink.open('conn_sqlite3', 'SELECT * FROM dblink_tbl ORDER BY id'), 100); 144 | id | value 145 | ----+------- 146 | 1 | A 147 | 2 | BB 148 | 3 | CCC 149 | 4 | DDDD 150 | 5 | 151 | (5 rows) 152 | 153 | SELECT * FROM dblink.cursor_test(dblink.open('conn_sqlite3', 'SELECT * FROM dblink_tbl ORDER BY id'), 2); 154 | id | value 155 | ----+------- 156 | 1 | A 157 | 2 | BB 158 | 3 | CCC 159 | 4 | DDDD 160 | 5 | 161 | (5 rows) 162 | 163 | SELECT * FROM dblink.cursor_test(dblink.open('conn_sqlite3', 'SELECT * FROM dblink_tbl ORDER BY id'), 1); 164 | id | value 165 | ----+------- 166 | 1 | A 167 | 2 | BB 168 | 3 | CCC 169 | (3 rows) 170 | 171 | -- dblink.disconnect() 172 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 173 | name | srvname | status | keep 174 | --------------+----------------+--------+------ 175 | conn_sqlite3 | server_sqlite3 | idle | t 176 | (1 row) 177 | 178 | SELECT dblink.disconnect('conn_sqlite3'); 179 | disconnect 180 | ------------ 181 | t 182 | (1 row) 183 | 184 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 185 | name | srvname | status | keep 186 | ------+---------+--------+------ 187 | (0 rows) 188 | 189 | -- dblink.query() with an anonymous connection 190 | BEGIN; 191 | SELECT * FROM dblink.query('server_sqlite3', 'SELECT * FROM dblink_tbl ORDER BY id') AS t(id integer, value text) ORDER BY id; 192 | id | value 193 | ----+------- 194 | 1 | A 195 | 2 | BB 196 | 3 | CCC 197 | 4 | DDDD 198 | 5 | 199 | (5 rows) 200 | 201 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 202 | name | srvname | status | keep 203 | ----------------+----------------+--------+------ 204 | server_sqlite3 | server_sqlite3 | used | f 205 | (1 row) 206 | 207 | COMMIT; 208 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 209 | name | srvname | status | keep 210 | ------+---------+--------+------ 211 | (0 rows) 212 | 213 | -- dblink.exec() with an anonymous connection 214 | BEGIN; 215 | SELECT dblink.exec('server_sqlite3', 'UPDATE dblink_tbl SET value = value WHERE id < 3'); 216 | exec 217 | ------ 218 | 2 219 | (1 row) 220 | 221 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 222 | name | srvname | status | keep 223 | ----------------+----------------+--------+------ 224 | server_sqlite3 | server_sqlite3 | used | f 225 | (1 row) 226 | 227 | COMMIT; 228 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 229 | name | srvname | status | keep 230 | ------+---------+--------+------ 231 | (0 rows) 232 | 233 | -- dblink.open() with an anonymous connection 234 | SELECT * FROM dblink.cursor_test(dblink.open('server_sqlite3', 'SELECT * FROM dblink_tbl ORDER BY id'), 100); 235 | id | value 236 | ----+------- 237 | 1 | A 238 | 2 | BB 239 | 3 | CCC 240 | 4 | DDDD 241 | 5 | 242 | (5 rows) 243 | 244 | SELECT * FROM dblink.cursor_test(dblink.open('server_sqlite3', 'SELECT * FROM dblink_tbl ORDER BY id'), 2); 245 | id | value 246 | ----+------- 247 | 1 | A 248 | 2 | BB 249 | 3 | CCC 250 | 4 | DDDD 251 | 5 | 252 | (5 rows) 253 | 254 | SELECT * FROM dblink.cursor_test(dblink.open('server_sqlite3', 'SELECT * FROM dblink_tbl ORDER BY id'), 1); 255 | id | value 256 | ----+------- 257 | 1 | A 258 | 2 | BB 259 | 3 | CCC 260 | (3 rows) 261 | 262 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 263 | name | srvname | status | keep 264 | ------+---------+--------+------ 265 | (0 rows) 266 | 267 | -- dblink.query() with max_value_len option 268 | SELECT * FROM dblink.query('server_sqlite3', 'SELECT * FROM dblink_tbl ORDER BY id', 0, 1) AS (id integer, value text); 269 | id | value 270 | ----+------- 271 | 1 | A 272 | 2 | BB 273 | 3 | CCC 274 | 4 | DDDD 275 | 5 | 276 | (5 rows) 277 | 278 | -- dblink.open() with max_value_len option 279 | SELECT * FROM dblink.cursor_test(dblink.open('server_sqlite3', 'SELECT * FROM dblink_tbl ORDER BY id', 100, 1), 100); 280 | id | value 281 | ----+------- 282 | 1 | A 283 | 2 | BB 284 | 3 | CCC 285 | 4 | DDDD 286 | 5 | 287 | (5 rows) 288 | 289 | -- connection without automatic transaction management 290 | SELECT dblink.connect('conn_sqlite3', 'server_sqlite3', false); 291 | connect 292 | --------- 293 | t 294 | (1 row) 295 | 296 | SELECT dblink.exec('conn_sqlite3', 'VACUUM dblink_tbl'); 297 | exec 298 | ------ 299 | 5 300 | (1 row) 301 | 302 | SELECT dblink.disconnect('conn_sqlite3'); 303 | disconnect 304 | ------------ 305 | t 306 | (1 row) 307 | 308 | SELECT dblink.exec('server_sqlite3', 'DROP TABLE IF EXISTS dblink_tbl'); 309 | exec 310 | ------ 311 | 0 312 | (1 row) 313 | 314 | -------------------------------------------------------------------------------- /expected/standby_postgres.out: -------------------------------------------------------------------------------- 1 | -- dblink.query() to test use_xa GUC parameter on Standby Server 2 | BEGIN; 3 | SET dblink_plus.use_xa TO 'on'; 4 | SHOW dblink_plus.use_xa; 5 | dblink_plus.use_xa 6 | -------------------- 7 | on 8 | (1 row) 9 | 10 | SELECT * FROM dblink.query('server_postgres6789012345678901234567890123456789012345678901234567890', 'SELECT * FROM dblink.dblink_tbl ORDER BY id') AS t(id integer, value text) ORDER BY id; 11 | NOTICE: identifier "server_postgres6789012345678901234567890123456789012345678901234567890" will be truncated to "server_postgres678901234567890123456789012345678901234567890123" 12 | ERROR: cannot execute DELETE in a read-only transaction 13 | CONTEXT: SQL statement "DELETE FROM dblink.atcommit" 14 | COMMIT; 15 | BEGIN; 16 | SET dblink_plus.use_xa TO 'off'; 17 | SELECT * FROM dblink.query('server_postgres6789012345678901234567890123456789012345678901234567890', 'SELECT * FROM dblink.dblink_tbl ORDER BY id') AS t(id integer, value text) ORDER BY id; 18 | NOTICE: identifier "server_postgres6789012345678901234567890123456789012345678901234567890" will be truncated to "server_postgres678901234567890123456789012345678901234567890123" 19 | id | value 20 | ----+------- 21 | 1 | A 22 | 2 | BB 23 | 3 | CCC 24 | 4 | DDDD 25 | 5 | 26 | (5 rows) 27 | 28 | COMMIT; 29 | -------------------------------------------------------------------------------- /regress.conf: -------------------------------------------------------------------------------- 1 | #-- PostgreSQL server configuration settings for regression test-- 2 | PG_USER='postgres' 3 | 4 | #-- Oracle server configuration settings for regression test-- 5 | OR_DBNM='dbt' 6 | OR_USER='scott' 7 | OR_PASS='tiger' 8 | -------------------------------------------------------------------------------- /regress_init.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | PARSECOMMAND='grep -v "^#\|^$" | while read n v; do echo $n $v; done' 4 | 5 | PG_USER=`IFS="="; cat regress.conf | eval $PARSECOMMAND | grep "PG_USER" | awk '{print $2}'` 6 | OR_DBNM=`IFS="="; cat regress.conf | eval $PARSECOMMAND | grep "OR_DBNM" | awk '{print $2}'` 7 | OR_USER=`IFS="="; cat regress.conf | eval $PARSECOMMAND | grep "OR_USER" | awk '{print $2}'` 8 | OR_PASS=`IFS="="; cat regress.conf | eval $PARSECOMMAND | grep "OR_PASS" | awk '{print $2}'` 9 | 10 | #-- postgresql parameters setting-- 11 | C_USER=`IFS="="; cat expected/postgres.out | eval $PARSECOMMAND | grep "CREATE USER MAPPING FOR" | awk '{print $10}'` 12 | sed -i "s/user $C_USER/user $PG_USER);/g" expected/postgres.out > /dev/null 13 | 14 | C_USER=`IFS="="; cat sql/postgres.sql | eval $PARSECOMMAND | grep "CREATE USER MAPPING FOR" | awk '{print $10}'` 15 | sed -i "s/user $C_USER/user $PG_USER);/g" sql/postgres.sql > /dev/null 16 | 17 | #-- Oracle parameters setting-- 18 | C_DBNM=`IFS="="; cat expected/oracle.out | eval $PARSECOMMAND | grep "CREATE SERVER server_oracle2" | awk '{print $10}'` 19 | sed -i "s/dbname $C_DBNM/dbname $OR_DBNM,/g" expected/oracle.out > /dev/null 20 | 21 | C_DBNM=`IFS="="; cat sql/oracle.sql | eval $PARSECOMMAND | grep "CREATE SERVER server_oracle2" | awk '{print $10}'` 22 | sed -i "s/dbname $C_DBNM/dbname $OR_DBNM,/g" sql/oracle.sql > /dev/null 23 | 24 | C_DBNM=`IFS="="; cat expected/oracle.out | eval $PARSECOMMAND | grep "CREATE SERVER server_oracle FOREIGN DATA WRAPPER oracle OPTIONS (dbname" | awk '{print $10}'` 25 | sed -i "s/dbname $C_DBNM/dbname $OR_DBNM);/g" expected/oracle.out > /dev/null 26 | 27 | C_DBNM=`IFS="="; cat sql/oracle.sql | eval $PARSECOMMAND | grep "CREATE SERVER server_oracle FOREIGN DATA WRAPPER oracle OPTIONS (dbname" | awk '{print $10}'` 28 | sed -i "s/dbname $C_DBNM/dbname $OR_DBNM);/g" sql/oracle.sql > /dev/null 29 | 30 | C_USER=`IFS="="; cat expected/oracle.out | eval $PARSECOMMAND | grep "CREATE USER MAPPING FOR CURRENT_USER SERVER server_oracle2" | awk '{print $10}'` 31 | sed -i "s/user $C_USER/user $OR_USER,/g" expected/oracle.out > /dev/null 32 | 33 | C_USER=`IFS="="; cat sql/oracle.sql | eval $PARSECOMMAND | grep "CREATE USER MAPPING FOR CURRENT_USER SERVER server_oracle2" | awk '{print $10}'` 34 | sed -i "s/user $C_USER/user $OR_USER,/g" sql/oracle.sql > /dev/null 35 | 36 | C_PASS=`IFS="="; cat expected/oracle.out | eval $PARSECOMMAND | grep "CREATE USER MAPPING FOR CURRENT_USER SERVER server_oracle2" | awk '{print $12}'` 37 | sed -i "s/password $C_PASS/password $OR_PASS);/g" expected/oracle.out > /dev/null 38 | 39 | C_PASS=`IFS="="; cat sql/oracle.sql | eval $PARSECOMMAND | grep "CREATE USER MAPPING FOR CURRENT_USER SERVER server_oracle2" | awk '{print $12}'` 40 | sed -i "s/password $C_PASS/password $OR_PASS);/g" sql/oracle.sql > /dev/null 41 | -------------------------------------------------------------------------------- /sql/init.sql: -------------------------------------------------------------------------------- 1 | SET client_min_messages = warning; 2 | \set ECHO none 3 | \i dblink_plus.sql 4 | \set ECHO all 5 | RESET client_min_messages; 6 | \! sh regress_init.sh 7 | -------------------------------------------------------------------------------- /sql/mysql.sql: -------------------------------------------------------------------------------- 1 | CREATE FOREIGN DATA WRAPPER mysql VALIDATOR dblink.mysql; 2 | CREATE SERVER server_mysql FOREIGN DATA WRAPPER mysql OPTIONS (dbname 'mysql'); 3 | CREATE USER MAPPING FOR CURRENT_USER SERVER server_mysql OPTIONS (user 'root', password 'mysql'); 4 | 5 | SELECT dblink.connect('conn_mysql', 'server_mysql', false); 6 | SELECT dblink.exec('conn_mysql', 'DROP TABLE IF EXISTS dblink_tbl'); 7 | SELECT dblink.exec('conn_mysql', 'CREATE TABLE dblink_tbl (id integer, value text) ENGINE = InnoDB'); 8 | SELECT dblink.disconnect('conn_mysql'); 9 | 10 | -- dblink.connect() 11 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 12 | SELECT dblink.connect('conn_mysql', 'server_mysql'); 13 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 14 | 15 | -- dblink.exec() with an existing connection 16 | SELECT dblink.exec('conn_mysql', 'INSERT INTO dblink_tbl VALUES(1, ''X'')'); 17 | SELECT dblink.exec('conn_mysql', 'INSERT INTO dblink_tbl VALUES(2, ''BB'')'); 18 | SELECT dblink.exec('conn_mysql', 'INSERT INTO dblink_tbl VALUES(3, ''CCC'')'); 19 | SELECT dblink.exec('conn_mysql', 'INSERT INTO dblink_tbl VALUES(4, ''DDDD'')'); 20 | SELECT dblink.exec('conn_mysql', 'INSERT INTO dblink_tbl VALUES(5, NULL)'); 21 | 22 | SELECT * FROM dblink.query('conn_mysql', 'SELECT * FROM dblink_tbl ORDER BY id') AS (id integer, value text); 23 | 24 | BEGIN; 25 | SELECT dblink.exec('conn_mysql', 'UPDATE dblink_tbl SET value = ''A'' WHERE id = 1'); 26 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 27 | ROLLBACK; 28 | 29 | SELECT * FROM dblink.query('conn_mysql', 'SELECT * FROM dblink_tbl ORDER BY id') AS (id integer, value text); 30 | 31 | BEGIN; 32 | SELECT dblink.exec('conn_mysql', 'UPDATE dblink_tbl SET value = ''A'' WHERE id = 1'); 33 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 34 | COMMIT; 35 | 36 | SELECT * FROM dblink.query('conn_mysql', 'SELECT * FROM dblink_tbl ORDER BY id') AS (id integer, value text); 37 | 38 | -- dblink.query() with an existing connection 39 | BEGIN; 40 | SELECT * FROM dblink.query('conn_mysql', 'SELECT * FROM dblink_tbl ORDER BY id') AS t(id integer, value text) ORDER BY id; 41 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 42 | COMMIT; 43 | 44 | -- dblink.open() with an existing connection 45 | SELECT * FROM dblink.cursor_test(dblink.open('conn_mysql', 'SELECT * FROM dblink_tbl ORDER BY id'), 100); 46 | SELECT * FROM dblink.cursor_test(dblink.open('conn_mysql', 'SELECT * FROM dblink_tbl ORDER BY id'), 2); 47 | SELECT * FROM dblink.cursor_test(dblink.open('conn_mysql', 'SELECT * FROM dblink_tbl ORDER BY id'), 1); 48 | 49 | -- dblink.disconnect() 50 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 51 | SELECT dblink.disconnect('conn_mysql'); 52 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 53 | 54 | -- dblink.query() with an anonymous connection 55 | BEGIN; 56 | SELECT * FROM dblink.query('server_mysql', 'SELECT * FROM dblink_tbl ORDER BY id') AS t(id integer, value text) ORDER BY id; 57 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 58 | COMMIT; 59 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 60 | 61 | -- dblink.exec() with an anonymous connection 62 | BEGIN; 63 | SELECT dblink.exec('server_mysql', 'UPDATE dblink_tbl SET value = value WHERE id < 3'); 64 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 65 | COMMIT; 66 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 67 | 68 | -- dblink.open() with an anonymous connection 69 | SELECT * FROM dblink.cursor_test(dblink.open('server_mysql', 'SELECT * FROM dblink_tbl ORDER BY id'), 100); 70 | SELECT * FROM dblink.cursor_test(dblink.open('server_mysql', 'SELECT * FROM dblink_tbl ORDER BY id'), 2); 71 | SELECT * FROM dblink.cursor_test(dblink.open('server_mysql', 'SELECT * FROM dblink_tbl ORDER BY id'), 1); 72 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 73 | 74 | -- dblink.query() with max_value_len option 75 | SELECT * FROM dblink.query('server_mysql', 'SELECT * FROM dblink_tbl ORDER BY id', 0, 1) AS (id integer, value text); 76 | 77 | -- dblink.open() with max_value_len option 78 | SELECT * FROM dblink.cursor_test(dblink.open('server_mysql', 'SELECT * FROM dblink_tbl ORDER BY id', 100, 1), 100); 79 | 80 | -- connection without automatic transaction management 81 | SELECT dblink.connect('conn_mysql', 'server_mysql', false); 82 | SELECT dblink.exec('conn_mysql', 'DROP TABLE IF EXISTS dblink_tbl'); 83 | SELECT dblink.disconnect('conn_mysql'); 84 | 85 | -------------------------------------------------------------------------------- /sql/oracle.sql: -------------------------------------------------------------------------------- 1 | CREATE FOREIGN DATA WRAPPER oracle VALIDATOR dblink.oracle; 2 | CREATE SERVER server_oracle FOREIGN DATA WRAPPER oracle OPTIONS (dbname 'dbt'); 3 | CREATE USER MAPPING FOR CURRENT_USER SERVER server_oracle OPTIONS (user 'scott', password 'tiger'); 4 | 5 | CREATE SERVER server_oracle2 FOREIGN DATA WRAPPER oracle OPTIONS (dbname 'dbt', max_value_len '4'); 6 | CREATE USER MAPPING FOR CURRENT_USER SERVER server_oracle2 OPTIONS (user 'scott', password 'tiger'); 7 | 8 | CREATE SERVER server_oracle3 FOREIGN DATA WRAPPER oracle OPTIONS (dbname 'dbt', max_value_len '3'); 9 | CREATE USER MAPPING FOR CURRENT_USER SERVER server_oracle3 OPTIONS (user 'scott', password 'tiger'); 10 | 11 | SELECT dblink.connect('conn_ora', 'server_oracle', false); 12 | SELECT dblink.exec('conn_ora', 'CREATE TABLE dblink_tbl (id NUMBER(4), value VARCHAR2(100))'); 13 | SELECT dblink.exec('conn_ora', 'CREATE TABLE lob_test (id NUMBER(5), value CLOB, value2 CLOB)'); 14 | SELECT dblink.disconnect('conn_ora'); 15 | SELECT dblink.exec('server_oracle', 'INSERT INTO dblink_tbl VALUES(1, ''X'')'); 16 | SELECT dblink.exec('server_oracle', 'INSERT INTO dblink_tbl VALUES(2, ''BB'')'); 17 | SELECT dblink.exec('server_oracle', 'INSERT INTO dblink_tbl VALUES(3, ''CCC'')'); 18 | SELECT dblink.exec('server_oracle', 'INSERT INTO dblink_tbl VALUES(4, ''DDDD'')'); 19 | SELECT dblink.exec('server_oracle', 'INSERT INTO dblink_tbl VALUES(5, NULL)'); 20 | 21 | CREATE TABLE dblink.dblink_tmp_ora (LIKE dblink.dblink_tbl); 22 | 23 | CREATE FUNCTION dblink.cursor_test_ora(cursor integer, howmany integer) 24 | RETURNS SETOF dblink.dblink_tmp_ora AS 25 | $$ 26 | TRUNCATE dblink.dblink_tmp_ora; 27 | INSERT INTO dblink.dblink_tmp_ora SELECT * FROM dblink.fetch($1, $2) AS t(id integer, value text); 28 | INSERT INTO dblink.dblink_tmp_ora SELECT * FROM dblink.fetch($1, $2) AS t(id integer, value text); 29 | INSERT INTO dblink.dblink_tmp_ora SELECT * FROM dblink.fetch($1, $2) AS t(id integer, value text); 30 | SELECT * FROM dblink.dblink_tmp_ora; 31 | $$ 32 | LANGUAGE sql; 33 | 34 | 35 | -- dblink.connect() 36 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 37 | SELECT dblink.connect('conn_oracle', 'server_oracle'); 38 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 39 | 40 | -- dblink.exec() with an existing connection 41 | SELECT * FROM dblink.query('conn_oracle', 'SELECT * FROM dblink_tbl ORDER BY id') AS t(i int, v text); 42 | 43 | BEGIN; 44 | SELECT dblink.exec('conn_oracle', 'UPDATE dblink_tbl SET value = ''A'' WHERE id = 1'); 45 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 46 | ROLLBACK; 47 | 48 | SELECT * FROM dblink.query('conn_oracle', 'SELECT * FROM dblink_tbl ORDER BY id') AS t(i int, v text); 49 | 50 | BEGIN; 51 | SELECT dblink.exec('conn_oracle', 'UPDATE dblink_tbl SET value = ''A'' WHERE id = 1'); 52 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 53 | COMMIT; 54 | 55 | SELECT * FROM dblink.query('conn_oracle', 'SELECT * FROM dblink_tbl ORDER BY id') AS t(i int, v text); 56 | 57 | -- dblink.query() with an existing connection 58 | BEGIN; 59 | SELECT * FROM dblink.query('conn_oracle', 'SELECT * FROM dblink_tbl ORDER BY id') AS t(id integer, value text) ORDER BY id; 60 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 61 | COMMIT; 62 | 63 | -- dblink.open() with an existing connection 64 | SELECT * FROM dblink.cursor_test_ora(dblink.open('conn_oracle', 'SELECT * FROM dblink_tbl ORDER BY id'), 100); 65 | SELECT * FROM dblink.cursor_test_ora(dblink.open('conn_oracle', 'SELECT * FROM dblink_tbl ORDER BY id'), 2); 66 | SELECT * FROM dblink.cursor_test_ora(dblink.open('conn_oracle', 'SELECT * FROM dblink_tbl ORDER BY id'), 1); 67 | 68 | -- dblink.disconnect() 69 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 70 | SELECT dblink.disconnect('conn_oracle'); 71 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 72 | 73 | -- dblink.query() with an anonymous connection 74 | BEGIN; 75 | SELECT * FROM dblink.query('server_oracle', 'SELECT * FROM dblink_tbl ORDER BY id') AS t(id integer, value text) ORDER BY id; 76 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 77 | COMMIT; 78 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 79 | 80 | -- dblink.exec() with an anonymous connection 81 | BEGIN; 82 | SELECT dblink.exec('server_oracle', 'UPDATE dblink_tbl SET value = value WHERE id < 3'); 83 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 84 | COMMIT; 85 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 86 | 87 | -- dblink.open() with an anonymous connection 88 | SELECT * FROM dblink.cursor_test_ora(dblink.open('server_oracle', 'SELECT * FROM dblink_tbl ORDER BY id'), 100); 89 | SELECT * FROM dblink.cursor_test_ora(dblink.open('server_oracle', 'SELECT * FROM dblink_tbl ORDER BY id'), 2); 90 | SELECT * FROM dblink.cursor_test_ora(dblink.open('server_oracle', 'SELECT * FROM dblink_tbl ORDER BY id'), 1); 91 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 92 | 93 | -- CLOB data read 94 | SELECT dblink.exec('server_oracle', 'insert into lob_test values(1, lpad(''Z'', 2000, ''Z''), lpad(''z'', 1000, ''z''))'); 95 | SELECT dblink.exec('server_oracle', 'insert into lob_test values(2, lpad(''Y'', 4000, ''Y''), lpad(''y'', 3000, ''y''))'); 96 | SELECT id, length(value), length(value2) from dblink.query('server_oracle', 'select * from lob_test') as (id integer, value text, value2 text) order by id; 97 | 98 | -- dblink.query() with max_value_len option 99 | SELECT * FROM dblink.query('server_oracle2', 'SELECT * FROM dblink_tbl ORDER BY id') AS t(id integer, value text) ORDER BY id; 100 | SELECT * FROM dblink.query('server_oracle3', 'SELECT * FROM dblink_tbl ORDER BY id', 0, 4) AS t(id integer, value text) ORDER BY id; 101 | 102 | -- dblink.open() with max_value_len option 103 | SELECT * FROM dblink.cursor_test_ora(dblink.open('server_oracle', 'SELECT * FROM dblink_tbl ORDER BY id', 100, 4), 100); 104 | 105 | -- connection without automatic transaction management 106 | SELECT dblink.connect('conn_oracle', 'server_oracle', false); 107 | SELECT dblink.exec('conn_oracle', 'DROP TABLE dblink_tbl'); 108 | SELECT dblink.exec('conn_oracle', 'DROP TABLE lob_test'); 109 | SELECT dblink.disconnect('conn_oracle'); 110 | 111 | 112 | SELECT dblink.connect('conn_oracle', 'server_oracle', false); 113 | SELECT dblink.exec('conn_oracle', 'CREATE OR REPLACE PACKAGE dblink AS TYPE RT1 IS RECORD ( ret NUMBER ); TYPE RCT1 IS REF CURSOR RETURN RT1; PROCEDURE f_test(RC1 IN OUT RCT1, num IN NUMBER); END;'); 114 | SELECT dblink.exec('conn_oracle', 'CREATE OR REPLACE PACKAGE BODY dblink AS PROCEDURE f_test(RC1 IN OUT RCT1, num IN NUMBER) AS BEGIN OPEN RC1 FOR select num + 1000 from dual; END f_test; END;'); 115 | SELECT dblink.disconnect('conn_oracle'); 116 | 117 | -- dblink.call() with an anonymous connection 118 | SELECT * FROM dblink.call('server_oracle', 'dblink.f_test(1)') AS t(a integer); 119 | 120 | -- dblink.call() with oracle standard library function as argument to oracle stored procedure 121 | SELECT * FROM dblink.call('server_oracle', 'dblink.f_test(ceil(3.1))') AS t(a integer); 122 | 123 | -- dblink.call() with max_value_len option 124 | SELECT * FROM dblink.call('server_oracle2', 'dblink.f_test(1)') AS t(a integer); 125 | SELECT * FROM dblink.call('server_oracle3', 'dblink.f_test(1)', 0, 4) AS t(a integer); 126 | -------------------------------------------------------------------------------- /sql/postgres.sql: -------------------------------------------------------------------------------- 1 | CREATE FOREIGN DATA WRAPPER postgres VALIDATOR dblink.postgres; 2 | CREATE SERVER server_postgres6789012345678901234567890123456789012345678901234567890 FOREIGN DATA WRAPPER postgres OPTIONS (dbname 'contrib_regression'); 3 | CREATE USER MAPPING FOR CURRENT_USER SERVER server_postgres678901234567890123456789012345678901234567890123 OPTIONS (user 'postgres'); 4 | 5 | CREATE TABLE dblink.dblink_tbl (id integer, value text); 6 | INSERT INTO dblink.dblink_tbl VALUES(1, 'X'); 7 | INSERT INTO dblink.dblink_tbl VALUES(2, 'BB'); 8 | INSERT INTO dblink.dblink_tbl VALUES(3, 'CCC'); 9 | INSERT INTO dblink.dblink_tbl VALUES(4, 'DDDD'); 10 | INSERT INTO dblink.dblink_tbl VALUES(5, NULL); 11 | 12 | CREATE TABLE dblink.dblink_tmp (LIKE dblink.dblink_tbl); 13 | 14 | CREATE FUNCTION dblink.cursor_test(cursor integer, howmany integer) 15 | RETURNS SETOF dblink.dblink_tmp AS 16 | $$ 17 | TRUNCATE dblink.dblink_tmp; 18 | INSERT INTO dblink.dblink_tmp SELECT * FROM dblink.fetch($1, $2) AS t(id integer, value text); 19 | INSERT INTO dblink.dblink_tmp SELECT * FROM dblink.fetch($1, $2) AS t(id integer, value text); 20 | INSERT INTO dblink.dblink_tmp SELECT * FROM dblink.fetch($1, $2) AS t(id integer, value text); 21 | SELECT dblink.close($1); 22 | SELECT * FROM dblink.dblink_tmp; 23 | $$ 24 | LANGUAGE sql; 25 | 26 | -- dblink.connect() 27 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 28 | SELECT dblink.connect('conn_postgres456789012345678901234567890123456789012345678901234567890', 'server_postgres6789012345678901234567890123456789012345678901234567890'); 29 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 30 | 31 | -- dblink.exec() with an existing connection 32 | SELECT * FROM dblink.dblink_tbl ORDER BY id; 33 | 34 | BEGIN; 35 | SELECT dblink.exec('conn_postgres456789012345678901234567890123456789012345678901234567890', 'UPDATE dblink.dblink_tbl SET value = ''A'' WHERE id = 1'); 36 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 37 | ROLLBACK; 38 | 39 | SELECT * FROM dblink.dblink_tbl ORDER BY id; 40 | 41 | BEGIN; 42 | SELECT dblink.exec('conn_postgres456789012345678901234567890123456789012345678901234567890', 'UPDATE dblink.dblink_tbl SET value = ''A'' WHERE id = 1'); 43 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 44 | COMMIT; 45 | 46 | SELECT * FROM dblink.dblink_tbl ORDER BY id; 47 | 48 | -- dblink.query() with an existing connection 49 | BEGIN; 50 | SELECT * FROM dblink.query('conn_postgres456789012345678901234567890123456789012345678901234567890', 'SELECT * FROM dblink.dblink_tbl ORDER BY id') AS t(id integer, value text) ORDER BY id; 51 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 52 | COMMIT; 53 | 54 | -- dblink.open() with an existing connection 55 | SELECT * FROM dblink.cursor_test(dblink.open('conn_postgres456789012345678901234567890123456789012345678901234567890', 'SELECT * FROM dblink.dblink_tbl ORDER BY id'), 100); 56 | SELECT * FROM dblink.cursor_test(dblink.open('conn_postgres456789012345678901234567890123456789012345678901234567890', 'SELECT * FROM dblink.dblink_tbl ORDER BY id'), 2); 57 | SELECT * FROM dblink.cursor_test(dblink.open('conn_postgres456789012345678901234567890123456789012345678901234567890', 'SELECT * FROM dblink.dblink_tbl ORDER BY id'), 1); 58 | 59 | -- dblink.disconnect() 60 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 61 | SELECT dblink.disconnect('conn_postgres456789012345678901234567890123456789012345678901234567890'); 62 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 63 | 64 | -- dblink.query() with an anonymous connection 65 | BEGIN; 66 | SELECT * FROM dblink.query('server_postgres6789012345678901234567890123456789012345678901234567890', 'SELECT * FROM dblink.dblink_tbl ORDER BY id') AS t(id integer, value text) ORDER BY id; 67 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 68 | COMMIT; 69 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 70 | 71 | -- dblink.exec() with an anonymous connection 72 | BEGIN; 73 | SELECT dblink.exec('server_postgres6789012345678901234567890123456789012345678901234567890', 'UPDATE dblink.dblink_tbl SET value = value WHERE id < 3'); 74 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 75 | COMMIT; 76 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 77 | 78 | -- dblink.open() with an anonymous connection 79 | SELECT * FROM dblink.cursor_test(dblink.open('server_postgres6789012345678901234567890123456789012345678901234567890', 'SELECT * FROM dblink.dblink_tbl ORDER BY id'), 100); 80 | SELECT * FROM dblink.cursor_test(dblink.open('server_postgres6789012345678901234567890123456789012345678901234567890', 'SELECT * FROM dblink.dblink_tbl ORDER BY id'), 2); 81 | SELECT * FROM dblink.cursor_test(dblink.open('server_postgres6789012345678901234567890123456789012345678901234567890', 'SELECT * FROM dblink.dblink_tbl ORDER BY id'), 1); 82 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 83 | 84 | -- dblink.query() with max_value_len option 85 | SELECT * FROM dblink.query('server_postgres6789012345678901234567890123456789012345678901234567890', 'SELECT * FROM dblink.dblink_tbl ORDER BY id', 0, 1) AS t(id integer, value text) ORDER BY id; 86 | 87 | -- dblink.open() with max_value_len option 88 | SELECT * FROM dblink.cursor_test(dblink.open('server_postgres6789012345678901234567890123456789012345678901234567890', 'SELECT * FROM dblink.dblink_tbl ORDER BY id', 100, 1), 100); 89 | 90 | -- connection without automatic transaction management 91 | SELECT dblink.connect('conn_postgres456789012345678901234567890123456789012345678901234567890', 'server_postgres6789012345678901234567890123456789012345678901234567890', false); 92 | SELECT dblink.exec('conn_postgres456789012345678901234567890123456789012345678901234567890', 'VACUUM dblink.dblink_tbl'); 93 | SELECT dblink.disconnect('conn_postgres456789012345678901234567890123456789012345678901234567890'); 94 | 95 | create function dblink.f_test(num integer) 96 | returns integer as 97 | $$ 98 | select $1 + 1000; 99 | $$ 100 | language 'sql'; 101 | 102 | -- dblink.call() with an anonymous connection 103 | SELECT * FROM dblink.call('server_postgres6789012345678901234567890123456789012345678901234567890', 'dblink.f_test(1)') AS t(a integer); 104 | 105 | -- dblink.call() with max_value_len option 106 | SELECT * FROM dblink.call('server_postgres6789012345678901234567890123456789012345678901234567890', 'dblink.f_test(1)', 0, 1) AS t(a integer); 107 | 108 | -------------------------------------------------------------------------------- /sql/sqlite3.sql: -------------------------------------------------------------------------------- 1 | CREATE FOREIGN DATA WRAPPER sqlite3 VALIDATOR dblink.sqlite3; 2 | CREATE SERVER server_sqlite3 FOREIGN DATA WRAPPER sqlite3 OPTIONS (location ':memory'); 3 | CREATE USER MAPPING FOR CURRENT_USER SERVER server_sqlite3; 4 | 5 | -- dblink.connect() 6 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 7 | SELECT dblink.connect('conn_sqlite3', 'server_sqlite3'); 8 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 9 | 10 | -- dblink.exec() with an existing connection 11 | SELECT dblink.exec('conn_sqlite3', 'DROP TABLE IF EXISTS dblink_tbl'); 12 | SELECT dblink.exec('conn_sqlite3', 'CREATE TABLE dblink_tbl (id integer, value text)'); 13 | SELECT dblink.exec('conn_sqlite3', 'INSERT INTO dblink_tbl VALUES(1, ''X'')'); 14 | SELECT dblink.exec('conn_sqlite3', 'INSERT INTO dblink_tbl VALUES(2, ''BB'')'); 15 | SELECT dblink.exec('conn_sqlite3', 'INSERT INTO dblink_tbl VALUES(3, ''CCC'')'); 16 | SELECT dblink.exec('conn_sqlite3', 'INSERT INTO dblink_tbl VALUES(4, ''DDDD'')'); 17 | SELECT dblink.exec('conn_sqlite3', 'INSERT INTO dblink_tbl VALUES(5, NULL)'); 18 | 19 | SELECT * FROM dblink.query('server_sqlite3', 'SELECT * FROM dblink_tbl ORDER BY id') AS (id integer, value text); 20 | 21 | BEGIN; 22 | SELECT dblink.exec('conn_sqlite3', 'UPDATE dblink_tbl SET value = ''A'' WHERE id = 1'); 23 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 24 | ROLLBACK; 25 | 26 | SELECT * FROM dblink.query('server_sqlite3', 'SELECT * FROM dblink_tbl ORDER BY id') AS (id integer, value text); 27 | 28 | BEGIN; 29 | SELECT dblink.exec('conn_sqlite3', 'UPDATE dblink_tbl SET value = ''A'' WHERE id = 1'); 30 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 31 | COMMIT; 32 | 33 | SELECT * FROM dblink.query('server_sqlite3', 'SELECT * FROM dblink_tbl ORDER BY id') AS (id integer, value text); 34 | 35 | -- dblink.query() with an existing connection 36 | BEGIN; 37 | SELECT * FROM dblink.query('conn_sqlite3', 'SELECT * FROM dblink_tbl ORDER BY id') AS t(id integer, value text) ORDER BY id; 38 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 39 | COMMIT; 40 | 41 | -- dblink.open() with an existing connection 42 | SELECT * FROM dblink.cursor_test(dblink.open('conn_sqlite3', 'SELECT * FROM dblink_tbl ORDER BY id'), 100); 43 | SELECT * FROM dblink.cursor_test(dblink.open('conn_sqlite3', 'SELECT * FROM dblink_tbl ORDER BY id'), 2); 44 | SELECT * FROM dblink.cursor_test(dblink.open('conn_sqlite3', 'SELECT * FROM dblink_tbl ORDER BY id'), 1); 45 | 46 | -- dblink.disconnect() 47 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 48 | SELECT dblink.disconnect('conn_sqlite3'); 49 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 50 | 51 | -- dblink.query() with an anonymous connection 52 | BEGIN; 53 | SELECT * FROM dblink.query('server_sqlite3', 'SELECT * FROM dblink_tbl ORDER BY id') AS t(id integer, value text) ORDER BY id; 54 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 55 | COMMIT; 56 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 57 | 58 | -- dblink.exec() with an anonymous connection 59 | BEGIN; 60 | SELECT dblink.exec('server_sqlite3', 'UPDATE dblink_tbl SET value = value WHERE id < 3'); 61 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 62 | COMMIT; 63 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 64 | 65 | -- dblink.open() with an anonymous connection 66 | SELECT * FROM dblink.cursor_test(dblink.open('server_sqlite3', 'SELECT * FROM dblink_tbl ORDER BY id'), 100); 67 | SELECT * FROM dblink.cursor_test(dblink.open('server_sqlite3', 'SELECT * FROM dblink_tbl ORDER BY id'), 2); 68 | SELECT * FROM dblink.cursor_test(dblink.open('server_sqlite3', 'SELECT * FROM dblink_tbl ORDER BY id'), 1); 69 | SELECT name, srvname, status, keep FROM dblink.connections, pg_foreign_server WHERE server = oid; 70 | 71 | -- dblink.query() with max_value_len option 72 | SELECT * FROM dblink.query('server_sqlite3', 'SELECT * FROM dblink_tbl ORDER BY id', 0, 1) AS (id integer, value text); 73 | 74 | -- dblink.open() with max_value_len option 75 | SELECT * FROM dblink.cursor_test(dblink.open('server_sqlite3', 'SELECT * FROM dblink_tbl ORDER BY id', 100, 1), 100); 76 | 77 | -- connection without automatic transaction management 78 | SELECT dblink.connect('conn_sqlite3', 'server_sqlite3', false); 79 | SELECT dblink.exec('conn_sqlite3', 'VACUUM dblink_tbl'); 80 | SELECT dblink.disconnect('conn_sqlite3'); 81 | 82 | SELECT dblink.exec('server_sqlite3', 'DROP TABLE IF EXISTS dblink_tbl'); 83 | -------------------------------------------------------------------------------- /sql/standby_postgres.sql: -------------------------------------------------------------------------------- 1 | -- dblink.query() to test use_xa GUC parameter on Standby Server 2 | BEGIN; 3 | SET dblink_plus.use_xa TO 'on'; 4 | SHOW dblink_plus.use_xa; 5 | SELECT * FROM dblink.query('server_postgres6789012345678901234567890123456789012345678901234567890', 'SELECT * FROM dblink.dblink_tbl ORDER BY id') AS t(id integer, value text) ORDER BY id; 6 | COMMIT; 7 | BEGIN; 8 | SET dblink_plus.use_xa TO 'off'; 9 | SELECT * FROM dblink.query('server_postgres6789012345678901234567890123456789012345678901234567890', 'SELECT * FROM dblink.dblink_tbl ORDER BY id') AS t(id integer, value text) ORDER BY id; 10 | COMMIT; 11 | -------------------------------------------------------------------------------- /standby_schedule: -------------------------------------------------------------------------------- 1 | test: standby_postgres 2 | -------------------------------------------------------------------------------- /uninstall_dblink_plus.sql: -------------------------------------------------------------------------------- 1 | SET search_path = public; 2 | 3 | DROP SCHEMA dblink CASCADE; 4 | --------------------------------------------------------------------------------