├── .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 | name
399 | text
400 | 接続に付けた名前です。明示的に指定していない場合、外部サーバ名と同じです。
401 |
402 |
403 | server
404 | oid
405 | 接続先の外部サーバの oid です。外部サーバの oid は pg_foreign_server カタログで確認できます。
406 |
407 |
408 | status
409 | text
410 | 接続の使用状態です。 - idle : 接続されて何も行なっていない状態 - used : 2相コミットのトランザクション中の状態 - prepared : 2相コミットでのPREPARE状態
411 |
412 |
413 | use_xa
414 | boolean
415 | 接続が2相コミットを利用しているかどうかのフラグです。use_xa にて指定されます。
416 |
417 |
418 | keep
419 | boolean
420 | 接続が dblink.connect() 経由で行われたものであるかどうかのフラグです。 dblink.connect() が開始した接続は dblink.disconnect() されるまで接続が維持されます。
421 |
422 |
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 |
--------------------------------------------------------------------------------