summaryrefslogtreecommitdiff
path: root/libpq
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2019-11-30 22:37:25 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2019-12-06 15:11:04 +0300
commitf1f39911e0d2d88c98eae96a3eb14a53c664206f (patch)
tree4cf4e3a84d895f59323d3b6ab4bfab38b3cab489 /libpq
parentfc9499b8a7b7a3e350bfabf2cd6ae0bc13f04bea (diff)
Upgrade to 12.1
Diffstat (limited to 'libpq')
-rw-r--r--libpq/.gitignore20
l---------libpq/COPYRIGHT1
-rw-r--r--libpq/INSTALL7
-rw-r--r--libpq/README20
-rw-r--r--libpq/README-DEV77
-rw-r--r--libpq/build/.gitignore3
-rw-r--r--libpq/build/bootstrap.build40
-rw-r--r--libpq/build/export.build10
-rw-r--r--libpq/build/root.build19
-rw-r--r--libpq/buildfile188
-rw-r--r--libpq/chklocale.c445
-rw-r--r--libpq/encnames.c553
-rw-r--r--libpq/fe-auth.c841
-rw-r--r--libpq/fe-auth.h24
-rw-r--r--libpq/fe-connect.c6002
-rw-r--r--libpq/fe-exec.c3764
-rw-r--r--libpq/fe-lobj.c1103
-rw-r--r--libpq/fe-misc.c1267
-rw-r--r--libpq/fe-print.c761
-rw-r--r--libpq/fe-protocol2.c1623
-rw-r--r--libpq/fe-protocol3.c2204
-rw-r--r--libpq/fe-secure.c505
-rw-r--r--libpq/getpeereid.c80
-rw-r--r--libpq/inet_net_ntop.c298
-rw-r--r--libpq/ip.c819
-rw-r--r--libpq/libpq-events.c209
-rw-r--r--libpq/libpq-events.h94
-rw-r--r--libpq/libpq-fe.h607
-rw-r--r--libpq/libpq-int.h675
-rw-r--r--libpq/libpq/.gitignore3
-rw-r--r--libpq/libpq/buildfile233
l---------libpq/libpq/common/base64.c1
l---------libpq/libpq/common/ip.c1
l---------libpq/libpq/common/link-canary.c1
l---------libpq/libpq/common/md5.c1
l---------libpq/libpq/common/saslprep.c1
l---------libpq/libpq/common/scram-common.c1
l---------libpq/libpq/common/sha2_openssl.c1
l---------libpq/libpq/common/unicode_norm.c1
l---------libpq/libpq/include/c.h1
l---------libpq/libpq/include/common1
l---------libpq/libpq/include/getaddrinfo.h1
l---------libpq/libpq/include/libpq/libpq-fs.h1
l---------libpq/libpq/include/libpq/pqcomm.h1
l---------libpq/libpq/include/mb/pg_wchar.h1
l---------libpq/libpq/include/pg_config_manual.h1
l---------libpq/libpq/include/port.h1
l---------libpq/libpq/include/port/darwin.h1
l---------libpq/libpq/include/port/freebsd.h1
l---------libpq/libpq/include/port/linux.h1
l---------libpq/libpq/include/port/pg_bswap.h1
l---------libpq/libpq/include/port/win321
l---------libpq/libpq/include/port/win32.h1
l---------libpq/libpq/include/port/win32_msvc1
l---------libpq/libpq/include/port/win32_port.h1
l---------libpq/libpq/include/postgres_ext.h1
l---------libpq/libpq/include/postgres_fe.h1
-rw-r--r--libpq/libpq/libpqdll.def178
-rw-r--r--libpq/libpq/libpqdll.map178
l---------libpq/libpq/mb/encnames.c1
l---------libpq/libpq/mb/wchar.c1
-rw-r--r--libpq/libpq/pg_config.h (renamed from libpq/postgresql/pg_config.h)107
l---------libpq/libpq/pg_config.h.in.orig1
l---------libpq/libpq/pg_config.h.win32.orig1
-rw-r--r--libpq/libpq/pg_config_ext.h (renamed from libpq/postgresql/pg_config_ext.h)7
l---------libpq/libpq/pg_config_ext.h.in.orig1
l---------libpq/libpq/pg_config_ext.h.win32.orig1
-rw-r--r--libpq/libpq/pg_config_os.h21
-rw-r--r--libpq/libpq/pg_config_paths.h11
l---------libpq/libpq/port/chklocale.c1
l---------libpq/libpq/port/getaddrinfo.c1
l---------libpq/libpq/port/getpeereid.c1
l---------libpq/libpq/port/inet_aton.c1
l---------libpq/libpq/port/inet_net_ntop.c1
l---------libpq/libpq/port/noblock.c1
l---------libpq/libpq/port/open.c1
l---------libpq/libpq/port/pg_strong_random.c1
l---------libpq/libpq/port/pgsleep.c1
l---------libpq/libpq/port/pgstrcasecmp.c1
l---------libpq/libpq/port/pthread-win32.h1
l---------libpq/libpq/port/snprintf.c1
l---------libpq/libpq/port/strerror.c1
l---------libpq/libpq/port/strlcpy.c1
l---------libpq/libpq/port/system.c1
l---------libpq/libpq/port/thread.c1
l---------libpq/libpq/port/win32error.c1
l---------libpq/libpq/port/win32setlocale.c1
l---------libpq/libpq/pq1
-rw-r--r--libpq/libpq/version.h.in17
-rw-r--r--libpq/manifest25
-rw-r--r--libpq/md5.c345
-rw-r--r--libpq/noblock.c66
-rw-r--r--libpq/non-bsd/strlcpy.c71
-rw-r--r--libpq/pg_service.conf.sample17
-rw-r--r--libpq/pgstrcasecmp.c151
-rw-r--r--libpq/postgresql/c.h1107
-rw-r--r--libpq/postgresql/common/fe_memutils.h44
-rw-r--r--libpq/postgresql/getaddrinfo.h164
-rw-r--r--libpq/postgresql/libpq/ip.h51
-rw-r--r--libpq/postgresql/libpq/libpq-fs.h24
-rw-r--r--libpq/postgresql/libpq/md5.h30
-rw-r--r--libpq/postgresql/libpq/pqcomm.h206
-rw-r--r--libpq/postgresql/mb/pg_wchar.h561
-rw-r--r--libpq/postgresql/pg_config.h.in.orig931
-rw-r--r--libpq/postgresql/pg_config.h.win32.orig682
-rw-r--r--libpq/postgresql/pg_config_ext.h.in.orig7
-rw-r--r--libpq/postgresql/pg_config_ext.h.win32.orig7
-rw-r--r--libpq/postgresql/pg_config_manual.h327
-rw-r--r--libpq/postgresql/pg_config_paths.h1
-rw-r--r--libpq/postgresql/port.h477
-rw-r--r--libpq/postgresql/port/bsd/pg_config_os.h0
-rw-r--r--libpq/postgresql/port/darwin/pg_config_os.h8
-rw-r--r--libpq/postgresql/port/linux/pg_config_os.h22
-rw-r--r--libpq/postgresql/port/win32/arpa/inet.h3
-rw-r--r--libpq/postgresql/port/win32/netdb.h1
-rw-r--r--libpq/postgresql/port/win32/netinet/in.h3
-rw-r--r--libpq/postgresql/port/win32/pg_config_os.h486
-rw-r--r--libpq/postgresql/port/win32/pthread-win32.h22
-rw-r--r--libpq/postgresql/port/win32/pwd.h3
-rw-r--r--libpq/postgresql/port/win32/sys/socket.h33
-rw-r--r--libpq/postgresql/port/win32/sys/wait.h3
-rw-r--r--libpq/postgresql/port/win32_msvc/sys/file.h1
-rw-r--r--libpq/postgresql/port/win32_msvc/sys/param.h1
-rw-r--r--libpq/postgresql/port/win32_msvc/sys/time.h1
-rw-r--r--libpq/postgresql/port/win32_msvc/unistd.h1
-rw-r--r--libpq/postgresql/postgres_ext.h70
-rw-r--r--libpq/postgresql/postgres_fe.h29
-rw-r--r--libpq/pqexpbuffer.c430
-rw-r--r--libpq/pqexpbuffer.h182
-rw-r--r--libpq/pqsignal.c90
-rw-r--r--libpq/tests/.gitignore3
-rw-r--r--libpq/tests/build/.gitignore3
-rw-r--r--libpq/tests/build/bootstrap.build9
-rw-r--r--libpq/tests/build/root.build22
-rw-r--r--libpq/tests/buildfile5
-rw-r--r--libpq/tests/conninfo/buildfile13
-rw-r--r--libpq/tests/conninfo/driver.c74
-rw-r--r--libpq/tests/conninfo/expected.out171
-rw-r--r--libpq/tests/conninfo/postgres_fe.h19
-rw-r--r--libpq/tests/conninfo/regress.in57
-rw-r--r--libpq/tests/conninfo/testscript5
l---------libpq/tests/conninfo/uri-regress.c1
-rw-r--r--libpq/thread.c146
-rw-r--r--libpq/version.h.in26
-rw-r--r--libpq/wchar.c2054
-rw-r--r--libpq/win32/crypt.c1085
-rw-r--r--libpq/win32/getaddrinfo.c412
-rw-r--r--libpq/win32/inet_aton.c147
-rw-r--r--libpq/win32/libpq.rc.in31
-rw-r--r--libpq/win32/libpqdll.def174
-rw-r--r--libpq/win32/libpqdll.def.orig174
-rw-r--r--libpq/win32/open.c167
-rw-r--r--libpq/win32/pgsleep.c63
-rw-r--r--libpq/win32/pthread-win32.c61
-rw-r--r--libpq/win32/pthread-win32.h22
-rw-r--r--libpq/win32/snprintf.c1141
-rw-r--r--libpq/win32/system.c119
-rw-r--r--libpq/win32/win32.c327
-rw-r--r--libpq/win32/win32.h40
-rw-r--r--libpq/win32/win32error.c206
-rw-r--r--libpq/win32/win32setlocale.c189
161 files changed, 1400 insertions, 35314 deletions
diff --git a/libpq/.gitignore b/libpq/.gitignore
index 620b4c8..cece09c 100644
--- a/libpq/.gitignore
+++ b/libpq/.gitignore
@@ -1,3 +1,19 @@
-# Generated version.h.
+# Compiler/linker output.
#
-version.h
+*.d
+*.t
+*.i
+*.ii
+*.o
+*.obj
+*.so
+*.dll
+*.a
+*.lib
+*.exp
+*.pdb
+*.ilk
+*.exe
+*.exe.dlls/
+*.exe.manifest
+*.pc
diff --git a/libpq/COPYRIGHT b/libpq/COPYRIGHT
new file mode 120000
index 0000000..945f098
--- /dev/null
+++ b/libpq/COPYRIGHT
@@ -0,0 +1 @@
+../upstream/COPYRIGHT \ No newline at end of file
diff --git a/libpq/INSTALL b/libpq/INSTALL
new file mode 100644
index 0000000..61ec557
--- /dev/null
+++ b/libpq/INSTALL
@@ -0,0 +1,7 @@
+The aim of this package is to make reading the INSTALL file unnecessary. So
+next time try running:
+
+$ bpkg build libpq
+
+But if you don't want to use the package manager, then you can also build this
+package manually using the standard build2 build system.
diff --git a/libpq/README b/libpq/README
new file mode 100644
index 0000000..f57e7a7
--- /dev/null
+++ b/libpq/README
@@ -0,0 +1,20 @@
+PostgreSQL is an object-relational SQL database management system with libpq
+being its C client library. Applications can use this library to pass queries
+to the PostgreSQL backend server and to receive the results of those queries
+using the C programming language. For more information see:
+
+https://www.postgresql.org/
+
+This package contains the original libpq library source code overlaid with the
+build2-based build system and packaged for the build2 package manager (bpkg).
+
+See the INSTALL file for the prerequisites and installation instructions.
+
+Send questions, bug reports, or any other feedback about the library itself to
+the PostgreSQL mailing lists. Send build system and packaging-related feedback
+to the packaging@build2.org mailing list (see https://lists.build2.org for
+posting guidelines, etc).
+
+The packaging of PostgreSQL for build2 is tracked in a Git repository at:
+
+https://git.build2.org/cgit/packaging/postgresql/
diff --git a/libpq/README-DEV b/libpq/README-DEV
new file mode 100644
index 0000000..cadfe63
--- /dev/null
+++ b/libpq/README-DEV
@@ -0,0 +1,77 @@
+This document describes how libpq was packaged for build2. In particular, this
+understanding will be useful when upgrading to a new upstream version. See
+../README-DEV for general notes on PostgreSQL packaging.
+
+The upstream package builds the common and ports static libraries and links
+them to the libpq library. These static libraries are generic enough to fit
+all the binaries built by the package. We will build libpq selecting only the
+required static libraries source files, relying on the linker diagnostics
+(unresolved symbol errors, etc).
+
+Symlink the required upstream components and provide our own implementations
+for auto-generated headers:
+
+$ ln -s ../upstream/COPYRIGHT
+
+Note that we symlink the libpq directory under the different name not to
+confuse the headers auto-generating machinery while it maps the header
+directory prefixes.
+
+$ ln -s ../../upstream/src/interfaces/libpq libpq/pq
+
+Note that while we could symlink the upstream's top source directories, this
+would potentially bloat the distribution, uglify the buildfile, and complicate
+pg_config.h change tracking on upgrade (see below). Thus, we selectively
+symlink only the required files.
+
+$ mkdir libpq/mb
+$ ln -s ../../../upstream/src/backend/utils/mb/{encnames,wchar}.c libpq/mb
+
+$ mkdir -p libpq/include/libpq libpq/include/mb libpq/include/port
+$ ln -s ../../../upstream/src/include/{c,getaddrinfo,pg_config_manual,port,postgres_ext,postgres_fe}.h libpq/include
+$ ln -s ../../../../upstream/src/include/libpq/{libpq-fs,pqcomm}.h libpq/include/libpq
+$ ln -s ../../../../upstream/src/include/mb/pg_wchar.h libpq/include/mb
+$ ln -s ../../../../upstream/src/include/port/{linux,freebsd,darwin,win32,win32_port,pg_bswap}.h libpq/include/port
+$ ln -s ../../../../upstream/src/include/port/{win32,win32_msvc} libpq/include/port
+
+$ mkdir libpq/common
+$ ln -s ../../../upstream/src/common/{md5,scram-common,ip,sha2_openssl,link-canary,base64,saslprep,unicode_norm}.c \
+ libpq/common
+
+$ mkdir libpq/port
+$ ln -s ../../../upstream/src/port/{pgstrcasecmp,snprintf,getpeereid,pg_strong_random,thread,strerror,chklocale,noblock,inet_net_ntop,strlcpy,win32setlocale,getaddrinfo,open,inet_aton,pgsleep,win32error,system}.c libpq/port
+$ ln -s ../../../upstream/src/port/pthread-win32.h libpq/port
+
+$ ln -s ../../upstream/src/include/pg_config_ext.h.in libpq/pg_config_ext.h.in.orig
+$ ln -s ../../upstream/src/include/pg_config_ext.h.win32 libpq/pg_config_ext.h.win32.orig
+$ ln -s ../../upstream/src/include/pg_config.h.in libpq/pg_config.h.in.orig
+$ ln -s ../../upstream/src/include/pg_config.h.win32 libpq/pg_config.h.win32.orig
+
+$ ln -s ../../../upstream/src/interfaces/libpq/test/uri-regress.c tests/conninfo
+
+Create libpq/{pg_config,pg_config_ext,pg_config_os,pg_config_paths}.h using as
+a base the upstream's auto-generated headers and/or the corresponding
+templates, makefiles, and perl/batch/shell scripts.
+
+Note that re-creating libpq/pg_config.h from scratch every time we upgrade to
+a new upstream version would be a real pain. Instead we can only (un)define
+the newly introduced macros, comparing the already defined and currently used
+macro sets:
+
+$ for m in `cat libpq/pg_config.h.in.orig libpq/pg_config.h.win32.orig | \
+ sed -n 's/.*#\s*\(define\|undef\)\s\{1,\}\([_A-Z0-9]\{1,\}\)\(\s.*\)\{0,1\}$/\2/p' | \
+ sort -u`; do
+ if grep -q -e "\b$m\b" `find -L . -name '*.h' -a ! -name 'pg_config.h' -o -name '*.c'`; then
+ echo "$m"
+ fi
+ done >used-macros
+
+$ cat libpq/pg_config.h | \
+ sed -n 's/#\s*\(define\|undef\)\s\{1,\}\([_A-Z0-9]\{1,\}\)\(\s.*\)\{0,1\}$/\2/p' | \
+ sort -u >defined-macros
+
+$ diff defined-macros used-macros
+
+Copy upstream's auto-generated src/interfaces/libpq/{exports.list,libpqdll.def}
+to libpq/{libpqdll.map,libpqdll.def} or create them manually based on
+pq/exports.txt. Comment out the "LIBRARY LIBPQ" line in libpqdll.def.
diff --git a/libpq/build/.gitignore b/libpq/build/.gitignore
new file mode 100644
index 0000000..4a730a3
--- /dev/null
+++ b/libpq/build/.gitignore
@@ -0,0 +1,3 @@
+config.build
+root/
+bootstrap/
diff --git a/libpq/build/bootstrap.build b/libpq/build/bootstrap.build
new file mode 100644
index 0000000..22b180f
--- /dev/null
+++ b/libpq/build/bootstrap.build
@@ -0,0 +1,40 @@
+# file : build/bootstrap.build
+# copyright : Copyright (c) 2016-2019 Code Synthesis Ltd
+# license : PostgreSQL License; see accompanying COPYRIGHT file
+
+project = libpq
+
+using version
+using config
+using dist
+using test
+using install
+
+# PostgreSQL releases (for quite a long time) had the 3-component versions,
+# where the first 2 components denoted a major version and the third one --
+# the minor version. This has changed starting from version 10, with the major
+# version represented by a single component. Minor releases are guaranteed to
+# be backwards-compatible and contain only bug fixes. See also:
+#
+# https://www.postgresql.org/support/versioning/
+#
+# Note that the release version is not a semantic version and we will map it to
+# the standard version as <major>.<minor>.0.
+#
+# There is no document that describes libpq ABI versioning and compatibility
+# rules, so everything that follows is implied from
+# src/interfaces/libpq/Makefile.
+#
+# The library naming schema on Linux is libpq.so.<so_major>.<so_minor>
+# (SO_MAJOR_VERSION and SO_MINOR_VERSION in the Makefile) So presumably
+# so_major is incremented on backwards-incompatible ABI changes (it hasn't
+# been for the several last major version releases). And so_minor is equal to
+# the package major version.
+#
+if ($version.major == 12 && $version.minor == 1)
+{
+ abi_major = 5
+ abi_minor = 12
+}
+else
+ fail "increment the ABI version?"
diff --git a/libpq/build/export.build b/libpq/build/export.build
new file mode 100644
index 0000000..748c3ea
--- /dev/null
+++ b/libpq/build/export.build
@@ -0,0 +1,10 @@
+# file : build/export.build
+# copyright : Copyright (c) 2016-2019 Code Synthesis Ltd
+# license : PostgreSQL License; see accompanying COPYRIGHT file
+
+$out_root/
+{
+ include libpq/
+}
+
+export $out_root/libpq/lib{pq}
diff --git a/libpq/build/root.build b/libpq/build/root.build
new file mode 100644
index 0000000..76b1aa3
--- /dev/null
+++ b/libpq/build/root.build
@@ -0,0 +1,19 @@
+# file : build/root.build
+# copyright : Copyright (c) 2016-2019 Code Synthesis Ltd
+# license : PostgreSQL License; see accompanying COPYRIGHT file
+
+# We rely on this in macros/options deduction (see
+# libpq/{buildfile,pg_config.h} for details).
+#
+c.std = 99
+
+using c
+
+h{*}: extension = h
+c{*}: extension = c
+
+if ($c.target.system == 'win32-msvc')
+ c.poptions += -D_CRT_SECURE_NO_WARNINGS -D_SCL_SECURE_NO_WARNINGS
+
+if ($c.class == 'msvc')
+ c.coptions += /wd4251 /wd4275 /wd4800
diff --git a/libpq/buildfile b/libpq/buildfile
index 5a1732b..e2ceea2 100644
--- a/libpq/buildfile
+++ b/libpq/buildfile
@@ -1,188 +1,10 @@
-# file : libpq/buildfile
+# file : buildfile
# copyright : Copyright (c) 2016-2019 Code Synthesis Ltd
# license : PostgreSQL Licenes; see accompanying COPYRIGHT file
-# Headers other than these are not installed so treat them as files.
-#
-# @@ Make it postgresql/{postgres_ext.h pg_config_ext.h} when name pattern can
-# be represented with a reversible name.
-#
-# @@ TODO: we should be able to redo it with install=true/false
-#
-h = version.h libpq-fe.h libpq-events.h \
- postgresql/postgres_ext.h postgresql/pg_config_ext.h
-
-lib{pq}: c{*} h{$h} file{**.h -{$h}} def{win32/libpqdll} \
- file{pg_service.conf.sample}
-
-tclass = $c.target.class
-
-bsd = ($tclass == 'bsd')
-macos = ($tclass == 'macos')
-windows = ($tclass == 'windows')
-
-msvc_runtime = ($c.target.system == 'win32-msvc')
-
-gcc = ($c.class == 'gcc')
-msvc = ($c.class == 'msvc')
-
-lib{pq}: win32/c{*}: include = $windows
-lib{pq}: non-bsd/c{*}: include = (($bsd || $macos) == false)
+./: {*/ -build/} doc{COPYRIGHT INSTALL README} manifest
-# The version file is an internal one (it is only included from
-# postgresql/pg_config.h) so we don't distribute nor install it (see below).
+# Don't install tests or the INSTALL file.
#
-h{version}: in{version} $src_root/manifest
-
-# Build options.
-#
-c.poptions += -DFRONTEND -DUNSAFE_STAT_OK -DSO_MAJOR_VERSION=$abi_major
-
-if! $windows
- # Note that the original package uses -pthread compiler/linker option. It is
- # currently unsupported by build2, so we use -D_REENTRANT and -lpthread
- # preprocessor/linker options instead. We also omit -D_THREAD_SAFE (synonym
- # for -D_REENTRANT) and Solaris-specific -D_POSIX_PTHREAD_SEMANTICS.
- #
- # @@ Maybe makes sense to support -pthread in build2, adding -lpthread
- # to pkg-config's Libs.private and to cc.export.libs?
- #
- c.poptions += -D_REENTRANT -D_GNU_SOURCE
-else
- # Note that the original package defines the WIN32 macro for VC only, relying
- # on the fact that MinGW GCC defines it by default. However, the macro
- # disappears from the default ones if to compile with -std=c9x (as we do). So
- # we define it for both VC and MinGW GCC.
- #
- # It's tempting to move this definition to libpq/postgresql/pg_config.h.
- # However this header is not included into all files that use the macro, for
- # example, libpq/win32/open.c.
- #
- c.poptions += -DWIN32
-
-port_dir = ($windows ? "win32" : \
- $macos ? "darwin" : \
- $tclass)
-
-# Note that we add "-I$src_root" for the headers auto-generating machinery to
-# work properly.
-#
-c.poptions =+ "-I$out_root" "-I$src_root" "-I$src_base" \
- "-I$src_base/postgresql/port/$port_dir" \
- "-I$src_base/postgresql"
-
-if $windows
- c.poptions =+ "-I$src_base/win32"
-
-if $msvc_runtime
-{
- c.poptions =+ "-I$src_base/postgresql/port/win32_msvc"
-
- c.poptions += -D_CRT_SECURE_NO_DEPRECATE -D_CRT_NONSTDC_NO_DEPRECATE
-
- # Disable warnings that pop up with /W3.
- #
- if $msvc
- c.coptions += /wd4018 /wd4244 /wd4267
-}
-elif $gcc
-{
- # Omit -fexcess-precision=standard as -std=9x implies it.
- #
- c.coptions += -fno-strict-aliasing -fwrapv
-
- # Disable warnings that pop up with -Wall -Wextra. Upstream doesn't seem to
- # care about these and it is not easy to disable specific warnings in a way
- # that works across compilers/version (some -Wno-* options are only
- # recognized in newer versions).
- #
- c.coptions += -Wno-all -Wno-extra
-}
-
-# Define SYSCONFDIR macro. This path is used as a last resort for the
-# pg_service.conf file search (see libpq/fe-connect.c for details).
-#
-# The whole idea feels utterly broken (hello cross-compilation) so we will
-# just do bare minimum and wait and see.
-#
-# @@ We should probably allow to configure this macros via configuration
-# variable config.libpq.sysconfdir.
-#
-if! $windows
-{
- # For the original package if the resulted sysconfdir path doesn't contain
- # the 'postgres' or 'pgsql' substring then the '/postgresql' suffix is
- # automatically appended (see the original INSTALL file for details). Note
- # that the same rule is applied for the datadir and docdir paths. Also if
- # the root directory is /usr, then the resulting sysconfdir path is
- # /etc/postgresql (rather than /usr/etc/postgresql).
- #
- # Let's do the same for the sysconfdir to increase the chance that libpq
- # will find the configuration file. Note that we don't install anything at
- # this path and don't amend the install.data and install.doc path variables.
- # We also use the same default path as the original package.
- #
- if ($install.root != [null])
- {
- root = $install.resolve($install.root)
- sysconfdir = ($root != /usr ? $root/etc : /etc)
-
- if! $regex.match("$sysconfdir", '.*(pgsql|postgresql).*')
- sysconfdir = $sysconfdir/postgresql
- }
- else
- sysconfdir = /usr/local/pgsql/etc
-}
-else
-{
- # win32.mak from the original package does this.
- #
- sysconfdir = ""
-}
-
-# If we ever enable National Language Support (ENABLE_NLS macro) then we will
-# need to define the LOCALEDIR macro as well. It refers to the locale data
-# directory and should be $install.data/locale by default. We will also need
-# to install this directory (see configure script --enable-nls options and the
-# src/interfaces/libpq/po directory in the original package for details).
-#
-obj{fe-connect}: c.poptions += -DSYSCONFDIR="\"$sysconfdir\""
-
-if! $windows
- c.libs += -lpthread
-else
- # The original package also adds the resource file to the library. The file
- # contains only the version information. First, libpq.rc is produced from
- # libpq.rc.in with the following command:
- #
- # sed -e 's/\(VERSION.*\),0 *$/\1,'`date '+%y%j' | \
- # sed 's/^0*//'`'/' libpq.rc.in >libpq.rc
- #
- # Then libpq.rc is compiled with:
- #
- # windres -i libpq.rc -o libpqrc.o
- #
- # Afterwards libpqrc.o is linked to the library.
- #
- # @@ Currently we don't have support for the first two steps.
- #
- c.libs += $regex.apply(secur32 ws2_32 advapi32, \
- '(.+)', \
- $msvc_runtime ? '\1.lib' : '-l\1')
-
-# Export options.
-#
-lib{pq}: cc.export.poptions = "-I$src_base" "-I$src_base/postgresql"
-
-# See bootstrap.build for details.
-#
-if $version.pre_release
- lib{pq}: bin.lib.version = @"-$version.project_id"
-else
- lib{pq}: bin.lib.version = @"-$abi_version"
-
-# Internal header (see above).
-#
-h{version}: install = false
-
-file{pg_service.conf.sample}@./: install = data/
+tests/: install = false
+doc{INSTALL}@./: install = false
diff --git a/libpq/chklocale.c b/libpq/chklocale.c
deleted file mode 100644
index 3c0ef6a..0000000
--- a/libpq/chklocale.c
+++ /dev/null
@@ -1,445 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * chklocale.c
- * Functions for handling locale-related info
- *
- *
- * Copyright (c) 1996-2016, PostgreSQL Global Development Group
- *
- *
- * IDENTIFICATION
- * src/port/chklocale.c
- *
- *-------------------------------------------------------------------------
- */
-
-#ifndef FRONTEND
-#include "postgres.h"
-#else
-#include "postgres_fe.h"
-#endif
-
-#if defined(WIN32) && (_MSC_VER >= 1900)
-#include <windows.h>
-#endif
-
-#include <locale.h>
-#ifdef HAVE_LANGINFO_H
-#include <langinfo.h>
-#endif
-
-#include "mb/pg_wchar.h"
-
-
-/*
- * This table needs to recognize all the CODESET spellings for supported
- * backend encodings, as well as frontend-only encodings where possible
- * (the latter case is currently only needed for initdb to recognize
- * error situations). On Windows, we rely on entries for codepage
- * numbers (CPnnn).
- *
- * Note that we search the table with pg_strcasecmp(), so variant
- * capitalizations don't need their own entries.
- */
-struct encoding_match
-{
- enum pg_enc pg_enc_code;
- const char *system_enc_name;
-};
-
-static const struct encoding_match encoding_match_list[] = {
- {PG_EUC_JP, "EUC-JP"},
- {PG_EUC_JP, "eucJP"},
- {PG_EUC_JP, "IBM-eucJP"},
- {PG_EUC_JP, "sdeckanji"},
- {PG_EUC_JP, "CP20932"},
-
- {PG_EUC_CN, "EUC-CN"},
- {PG_EUC_CN, "eucCN"},
- {PG_EUC_CN, "IBM-eucCN"},
- {PG_EUC_CN, "GB2312"},
- {PG_EUC_CN, "dechanzi"},
- {PG_EUC_CN, "CP20936"},
-
- {PG_EUC_KR, "EUC-KR"},
- {PG_EUC_KR, "eucKR"},
- {PG_EUC_KR, "IBM-eucKR"},
- {PG_EUC_KR, "deckorean"},
- {PG_EUC_KR, "5601"},
- {PG_EUC_KR, "CP51949"},
-
- {PG_EUC_TW, "EUC-TW"},
- {PG_EUC_TW, "eucTW"},
- {PG_EUC_TW, "IBM-eucTW"},
- {PG_EUC_TW, "cns11643"},
- /* No codepage for EUC-TW ? */
-
- {PG_UTF8, "UTF-8"},
- {PG_UTF8, "utf8"},
- {PG_UTF8, "CP65001"},
-
- {PG_LATIN1, "ISO-8859-1"},
- {PG_LATIN1, "ISO8859-1"},
- {PG_LATIN1, "iso88591"},
- {PG_LATIN1, "CP28591"},
-
- {PG_LATIN2, "ISO-8859-2"},
- {PG_LATIN2, "ISO8859-2"},
- {PG_LATIN2, "iso88592"},
- {PG_LATIN2, "CP28592"},
-
- {PG_LATIN3, "ISO-8859-3"},
- {PG_LATIN3, "ISO8859-3"},
- {PG_LATIN3, "iso88593"},
- {PG_LATIN3, "CP28593"},
-
- {PG_LATIN4, "ISO-8859-4"},
- {PG_LATIN4, "ISO8859-4"},
- {PG_LATIN4, "iso88594"},
- {PG_LATIN4, "CP28594"},
-
- {PG_LATIN5, "ISO-8859-9"},
- {PG_LATIN5, "ISO8859-9"},
- {PG_LATIN5, "iso88599"},
- {PG_LATIN5, "CP28599"},
-
- {PG_LATIN6, "ISO-8859-10"},
- {PG_LATIN6, "ISO8859-10"},
- {PG_LATIN6, "iso885910"},
-
- {PG_LATIN7, "ISO-8859-13"},
- {PG_LATIN7, "ISO8859-13"},
- {PG_LATIN7, "iso885913"},
-
- {PG_LATIN8, "ISO-8859-14"},
- {PG_LATIN8, "ISO8859-14"},
- {PG_LATIN8, "iso885914"},
-
- {PG_LATIN9, "ISO-8859-15"},
- {PG_LATIN9, "ISO8859-15"},
- {PG_LATIN9, "iso885915"},
- {PG_LATIN9, "CP28605"},
-
- {PG_LATIN10, "ISO-8859-16"},
- {PG_LATIN10, "ISO8859-16"},
- {PG_LATIN10, "iso885916"},
-
- {PG_KOI8R, "KOI8-R"},
- {PG_KOI8R, "CP20866"},
-
- {PG_KOI8U, "KOI8-U"},
- {PG_KOI8U, "CP21866"},
-
- {PG_WIN866, "CP866"},
- {PG_WIN874, "CP874"},
- {PG_WIN1250, "CP1250"},
- {PG_WIN1251, "CP1251"},
- {PG_WIN1251, "ansi-1251"},
- {PG_WIN1252, "CP1252"},
- {PG_WIN1253, "CP1253"},
- {PG_WIN1254, "CP1254"},
- {PG_WIN1255, "CP1255"},
- {PG_WIN1256, "CP1256"},
- {PG_WIN1257, "CP1257"},
- {PG_WIN1258, "CP1258"},
-
- {PG_ISO_8859_5, "ISO-8859-5"},
- {PG_ISO_8859_5, "ISO8859-5"},
- {PG_ISO_8859_5, "iso88595"},
- {PG_ISO_8859_5, "CP28595"},
-
- {PG_ISO_8859_6, "ISO-8859-6"},
- {PG_ISO_8859_6, "ISO8859-6"},
- {PG_ISO_8859_6, "iso88596"},
- {PG_ISO_8859_6, "CP28596"},
-
- {PG_ISO_8859_7, "ISO-8859-7"},
- {PG_ISO_8859_7, "ISO8859-7"},
- {PG_ISO_8859_7, "iso88597"},
- {PG_ISO_8859_7, "CP28597"},
-
- {PG_ISO_8859_8, "ISO-8859-8"},
- {PG_ISO_8859_8, "ISO8859-8"},
- {PG_ISO_8859_8, "iso88598"},
- {PG_ISO_8859_8, "CP28598"},
-
- {PG_SJIS, "SJIS"},
- {PG_SJIS, "PCK"},
- {PG_SJIS, "CP932"},
- {PG_SJIS, "SHIFT_JIS"},
-
- {PG_BIG5, "BIG5"},
- {PG_BIG5, "BIG5HKSCS"},
- {PG_BIG5, "Big5-HKSCS"},
- {PG_BIG5, "CP950"},
-
- {PG_GBK, "GBK"},
- {PG_GBK, "CP936"},
-
- {PG_UHC, "UHC"},
- {PG_UHC, "CP949"},
-
- {PG_JOHAB, "JOHAB"},
- {PG_JOHAB, "CP1361"},
-
- {PG_GB18030, "GB18030"},
- {PG_GB18030, "CP54936"},
-
- {PG_SHIFT_JIS_2004, "SJIS_2004"},
-
- {PG_SQL_ASCII, "US-ASCII"},
-
- {PG_SQL_ASCII, NULL} /* end marker */
-};
-
-#ifdef WIN32
-/*
- * On Windows, use CP<code page number> instead of the nl_langinfo() result
- *
- * Visual Studio 2012 expanded the set of valid LC_CTYPE values, so have its
- * locale machinery determine the code page. See comments at IsoLocaleName().
- * For other compilers, follow the locale's predictable format.
- *
- * Visual Studio 2015 should still be able to do the same, but the declaration
- * of lc_codepage is missing in _locale_t, causing this code compilation to
- * fail, hence this falls back instead on GetLocaleInfoEx. VS 2015 may be an
- * exception and post-VS2015 versions should be able to handle properly the
- * codepage number using _create_locale(). So, instead of the same logic as
- * VS 2012 and VS 2013, this routine uses GetLocaleInfoEx to parse short
- * locale names like "de-DE", "fr-FR", etc. If those cannot be parsed correctly
- * process falls back to the pre-VS-2010 manual parsing done with
- * using <Language>_<Country>.<CodePage> as a base.
- *
- * Returns a malloc()'d string for the caller to free.
- */
-static char *
-win32_langinfo(const char *ctype)
-{
- char *r = NULL;
-
-#if (_MSC_VER >= 1700) && (_MSC_VER < 1900)
- _locale_t loct = NULL;
-
- loct = _create_locale(LC_CTYPE, ctype);
- if (loct != NULL)
- {
- r = malloc(16); /* excess */
- if (r != NULL)
- sprintf(r, "CP%u", loct->locinfo->lc_codepage);
- _free_locale(loct);
- }
-#else
- char *codepage;
-
-#if (_MSC_VER >= 1900)
- uint32 cp;
- WCHAR wctype[LOCALE_NAME_MAX_LENGTH];
-
- memset(wctype, 0, sizeof(wctype));
- MultiByteToWideChar(CP_ACP, 0, ctype, -1, wctype, LOCALE_NAME_MAX_LENGTH);
-
- if (GetLocaleInfoEx(wctype,
- LOCALE_IDEFAULTANSICODEPAGE | LOCALE_RETURN_NUMBER,
- (LPWSTR) &cp, sizeof(cp) / sizeof(WCHAR)) > 0)
- {
- r = malloc(16); /* excess */
- if (r != NULL)
- sprintf(r, "CP%u", cp);
- }
- else
-#endif
- {
- /*
- * Locale format on Win32 is <Language>_<Country>.<CodePage> . For
- * example, English_United States.1252.
- */
- codepage = strrchr(ctype, '.');
- if (codepage != NULL)
- {
- int ln;
-
- codepage++;
- ln = strlen(codepage);
- r = malloc(ln + 3);
- if (r != NULL)
- sprintf(r, "CP%s", codepage);
- }
-
- }
-#endif
-
- return r;
-}
-
-#ifndef FRONTEND
-/*
- * Given a Windows code page identifier, find the corresponding PostgreSQL
- * encoding. Issue a warning and return -1 if none found.
- */
-int
-pg_codepage_to_encoding(UINT cp)
-{
- char sys[16];
- int i;
-
- sprintf(sys, "CP%u", cp);
-
- /* Check the table */
- for (i = 0; encoding_match_list[i].system_enc_name; i++)
- if (pg_strcasecmp(sys, encoding_match_list[i].system_enc_name) == 0)
- return encoding_match_list[i].pg_enc_code;
-
- ereport(WARNING,
- (errmsg("could not determine encoding for codeset \"%s\"", sys),
- errdetail("Please report this to <pgsql-bugs@postgresql.org>.")));
-
- return -1;
-}
-#endif
-#endif /* WIN32 */
-
-#if (defined(HAVE_LANGINFO_H) && defined(CODESET)) || defined(WIN32)
-
-/*
- * Given a setting for LC_CTYPE, return the Postgres ID of the associated
- * encoding, if we can determine it. Return -1 if we can't determine it.
- *
- * Pass in NULL to get the encoding for the current locale setting.
- * Pass "" to get the encoding selected by the server's environment.
- *
- * If the result is PG_SQL_ASCII, callers should treat it as being compatible
- * with any desired encoding.
- *
- * If running in the backend and write_message is false, this function must
- * cope with the possibility that elog() and palloc() are not yet usable.
- */
-int
-pg_get_encoding_from_locale(const char *ctype, bool write_message)
-{
- char *sys;
- int i;
-
- /* Get the CODESET property, and also LC_CTYPE if not passed in */
- if (ctype)
- {
- char *save;
- char *name;
-
- /* If locale is C or POSIX, we can allow all encodings */
- if (pg_strcasecmp(ctype, "C") == 0 ||
- pg_strcasecmp(ctype, "POSIX") == 0)
- return PG_SQL_ASCII;
-
- save = setlocale(LC_CTYPE, NULL);
- if (!save)
- return -1; /* setlocale() broken? */
- /* must copy result, or it might change after setlocale */
- save = strdup(save);
- if (!save)
- return -1; /* out of memory; unlikely */
-
- name = setlocale(LC_CTYPE, ctype);
- if (!name)
- {
- free(save);
- return -1; /* bogus ctype passed in? */
- }
-
-#ifndef WIN32
- sys = nl_langinfo(CODESET);
- if (sys)
- sys = strdup(sys);
-#else
- sys = win32_langinfo(name);
-#endif
-
- setlocale(LC_CTYPE, save);
- free(save);
- }
- else
- {
- /* much easier... */
- ctype = setlocale(LC_CTYPE, NULL);
- if (!ctype)
- return -1; /* setlocale() broken? */
-
- /* If locale is C or POSIX, we can allow all encodings */
- if (pg_strcasecmp(ctype, "C") == 0 ||
- pg_strcasecmp(ctype, "POSIX") == 0)
- return PG_SQL_ASCII;
-
-#ifndef WIN32
- sys = nl_langinfo(CODESET);
- if (sys)
- sys = strdup(sys);
-#else
- sys = win32_langinfo(ctype);
-#endif
- }
-
- if (!sys)
- return -1; /* out of memory; unlikely */
-
- /* Check the table */
- for (i = 0; encoding_match_list[i].system_enc_name; i++)
- {
- if (pg_strcasecmp(sys, encoding_match_list[i].system_enc_name) == 0)
- {
- free(sys);
- return encoding_match_list[i].pg_enc_code;
- }
- }
-
- /* Special-case kluges for particular platforms go here */
-
-#ifdef __darwin__
-
- /*
- * Current OS X has many locales that report an empty string for CODESET,
- * but they all seem to actually use UTF-8.
- */
- if (strlen(sys) == 0)
- {
- free(sys);
- return PG_UTF8;
- }
-#endif
-
- /*
- * We print a warning if we got a CODESET string but couldn't recognize
- * it. This means we need another entry in the table.
- */
- if (write_message)
- {
-#ifdef FRONTEND
- fprintf(stderr, _("could not determine encoding for locale \"%s\": codeset is \"%s\""),
- ctype, sys);
- /* keep newline separate so there's only one translatable string */
- fputc('\n', stderr);
-#else
- ereport(WARNING,
- (errmsg("could not determine encoding for locale \"%s\": codeset is \"%s\"",
- ctype, sys),
- errdetail("Please report this to <pgsql-bugs@postgresql.org>.")));
-#endif
- }
-
- free(sys);
- return -1;
-}
-#else /* (HAVE_LANGINFO_H && CODESET) || WIN32 */
-
-/*
- * stub if no multi-language platform support
- *
- * Note: we could return -1 here, but that would have the effect of
- * forcing users to specify an encoding to initdb on such platforms.
- * It seems better to silently default to SQL_ASCII.
- */
-int
-pg_get_encoding_from_locale(const char *ctype, bool write_message)
-{
- return PG_SQL_ASCII;
-}
-
-#endif /* (HAVE_LANGINFO_H && CODESET) || WIN32 */
diff --git a/libpq/encnames.c b/libpq/encnames.c
deleted file mode 100644
index 11099b8..0000000
--- a/libpq/encnames.c
+++ /dev/null
@@ -1,553 +0,0 @@
-/*
- * Encoding names and routines for work with it. All
- * in this file is shared between FE and BE.
- *
- * src/backend/utils/mb/encnames.c
- */
-#ifdef FRONTEND
-#include "postgres_fe.h"
-#else
-#include "postgres.h"
-#include "utils/builtins.h"
-#endif
-
-#include <ctype.h>
-#include <unistd.h>
-
-#include "mb/pg_wchar.h"
-
-
-/* ----------
- * All encoding names, sorted: *** A L P H A B E T I C ***
- *
- * All names must be without irrelevant chars, search routines use
- * isalnum() chars only. It means ISO-8859-1, iso_8859-1 and Iso8859_1
- * are always converted to 'iso88591'. All must be lower case.
- *
- * The table doesn't contain 'cs' aliases (like csISOLatin1). It's needed?
- *
- * Karel Zak, Aug 2001
- * ----------
- */
-typedef struct pg_encname
-{
- const char *name;
- pg_enc encoding;
-} pg_encname;
-
-static const pg_encname pg_encname_tbl[] =
-{
- {
- "abc", PG_WIN1258
- }, /* alias for WIN1258 */
- {
- "alt", PG_WIN866
- }, /* IBM866 */
- {
- "big5", PG_BIG5
- }, /* Big5; Chinese for Taiwan multibyte set */
- {
- "euccn", PG_EUC_CN
- }, /* EUC-CN; Extended Unix Code for simplified
- * Chinese */
- {
- "eucjis2004", PG_EUC_JIS_2004
- }, /* EUC-JIS-2004; Extended UNIX Code fixed
- * Width for Japanese, standard JIS X 0213 */
- {
- "eucjp", PG_EUC_JP
- }, /* EUC-JP; Extended UNIX Code fixed Width for
- * Japanese, standard OSF */
- {
- "euckr", PG_EUC_KR
- }, /* EUC-KR; Extended Unix Code for Korean , KS
- * X 1001 standard */
- {
- "euctw", PG_EUC_TW
- }, /* EUC-TW; Extended Unix Code for
- *
- * traditional Chinese */
- {
- "gb18030", PG_GB18030
- }, /* GB18030;GB18030 */
- {
- "gbk", PG_GBK
- }, /* GBK; Chinese Windows CodePage 936
- * simplified Chinese */
- {
- "iso88591", PG_LATIN1
- }, /* ISO-8859-1; RFC1345,KXS2 */
- {
- "iso885910", PG_LATIN6
- }, /* ISO-8859-10; RFC1345,KXS2 */
- {
- "iso885913", PG_LATIN7
- }, /* ISO-8859-13; RFC1345,KXS2 */
- {
- "iso885914", PG_LATIN8
- }, /* ISO-8859-14; RFC1345,KXS2 */
- {
- "iso885915", PG_LATIN9
- }, /* ISO-8859-15; RFC1345,KXS2 */
- {
- "iso885916", PG_LATIN10
- }, /* ISO-8859-16; RFC1345,KXS2 */
- {
- "iso88592", PG_LATIN2
- }, /* ISO-8859-2; RFC1345,KXS2 */
- {
- "iso88593", PG_LATIN3
- }, /* ISO-8859-3; RFC1345,KXS2 */
- {
- "iso88594", PG_LATIN4
- }, /* ISO-8859-4; RFC1345,KXS2 */
- {
- "iso88595", PG_ISO_8859_5
- }, /* ISO-8859-5; RFC1345,KXS2 */
- {
- "iso88596", PG_ISO_8859_6
- }, /* ISO-8859-6; RFC1345,KXS2 */
- {
- "iso88597", PG_ISO_8859_7
- }, /* ISO-8859-7; RFC1345,KXS2 */
- {
- "iso88598", PG_ISO_8859_8
- }, /* ISO-8859-8; RFC1345,KXS2 */
- {
- "iso88599", PG_LATIN5
- }, /* ISO-8859-9; RFC1345,KXS2 */
- {
- "johab", PG_JOHAB
- }, /* JOHAB; Extended Unix Code for simplified
- * Chinese */
- {
- "koi8", PG_KOI8R
- }, /* _dirty_ alias for KOI8-R (backward
- * compatibility) */
- {
- "koi8r", PG_KOI8R
- }, /* KOI8-R; RFC1489 */
- {
- "koi8u", PG_KOI8U
- }, /* KOI8-U; RFC2319 */
- {
- "latin1", PG_LATIN1
- }, /* alias for ISO-8859-1 */
- {
- "latin10", PG_LATIN10
- }, /* alias for ISO-8859-16 */
- {
- "latin2", PG_LATIN2
- }, /* alias for ISO-8859-2 */
- {
- "latin3", PG_LATIN3
- }, /* alias for ISO-8859-3 */
- {
- "latin4", PG_LATIN4
- }, /* alias for ISO-8859-4 */
- {
- "latin5", PG_LATIN5
- }, /* alias for ISO-8859-9 */
- {
- "latin6", PG_LATIN6
- }, /* alias for ISO-8859-10 */
- {
- "latin7", PG_LATIN7
- }, /* alias for ISO-8859-13 */
- {
- "latin8", PG_LATIN8
- }, /* alias for ISO-8859-14 */
- {
- "latin9", PG_LATIN9
- }, /* alias for ISO-8859-15 */
- {
- "mskanji", PG_SJIS
- }, /* alias for Shift_JIS */
- {
- "muleinternal", PG_MULE_INTERNAL
- },
- {
- "shiftjis", PG_SJIS
- }, /* Shift_JIS; JIS X 0202-1991 */
-
- {
- "shiftjis2004", PG_SHIFT_JIS_2004
- }, /* SHIFT-JIS-2004; Shift JIS for Japanese,
- * standard JIS X 0213 */
- {
- "sjis", PG_SJIS
- }, /* alias for Shift_JIS */
- {
- "sqlascii", PG_SQL_ASCII
- },
- {
- "tcvn", PG_WIN1258
- }, /* alias for WIN1258 */
- {
- "tcvn5712", PG_WIN1258
- }, /* alias for WIN1258 */
- {
- "uhc", PG_UHC
- }, /* UHC; Korean Windows CodePage 949 */
- {
- "unicode", PG_UTF8
- }, /* alias for UTF8 */
- {
- "utf8", PG_UTF8
- }, /* alias for UTF8 */
- {
- "vscii", PG_WIN1258
- }, /* alias for WIN1258 */
- {
- "win", PG_WIN1251
- }, /* _dirty_ alias for windows-1251 (backward
- * compatibility) */
- {
- "win1250", PG_WIN1250
- }, /* alias for Windows-1250 */
- {
- "win1251", PG_WIN1251
- }, /* alias for Windows-1251 */
- {
- "win1252", PG_WIN1252
- }, /* alias for Windows-1252 */
- {
- "win1253", PG_WIN1253
- }, /* alias for Windows-1253 */
- {
- "win1254", PG_WIN1254
- }, /* alias for Windows-1254 */
- {
- "win1255", PG_WIN1255
- }, /* alias for Windows-1255 */
- {
- "win1256", PG_WIN1256
- }, /* alias for Windows-1256 */
- {
- "win1257", PG_WIN1257
- }, /* alias for Windows-1257 */
- {
- "win1258", PG_WIN1258
- }, /* alias for Windows-1258 */
- {
- "win866", PG_WIN866
- }, /* IBM866 */
- {
- "win874", PG_WIN874
- }, /* alias for Windows-874 */
- {
- "win932", PG_SJIS
- }, /* alias for Shift_JIS */
- {
- "win936", PG_GBK
- }, /* alias for GBK */
- {
- "win949", PG_UHC
- }, /* alias for UHC */
- {
- "win950", PG_BIG5
- }, /* alias for BIG5 */
- {
- "windows1250", PG_WIN1250
- }, /* Windows-1251; Microsoft */
- {
- "windows1251", PG_WIN1251
- }, /* Windows-1251; Microsoft */
- {
- "windows1252", PG_WIN1252
- }, /* Windows-1252; Microsoft */
- {
- "windows1253", PG_WIN1253
- }, /* Windows-1253; Microsoft */
- {
- "windows1254", PG_WIN1254
- }, /* Windows-1254; Microsoft */
- {
- "windows1255", PG_WIN1255
- }, /* Windows-1255; Microsoft */
- {
- "windows1256", PG_WIN1256
- }, /* Windows-1256; Microsoft */
- {
- "windows1257", PG_WIN1257
- }, /* Windows-1257; Microsoft */
- {
- "windows1258", PG_WIN1258
- }, /* Windows-1258; Microsoft */
- {
- "windows866", PG_WIN866
- }, /* IBM866 */
- {
- "windows874", PG_WIN874
- }, /* Windows-874; Microsoft */
- {
- "windows932", PG_SJIS
- }, /* alias for Shift_JIS */
- {
- "windows936", PG_GBK
- }, /* alias for GBK */
- {
- "windows949", PG_UHC
- }, /* alias for UHC */
- {
- "windows950", PG_BIG5
- } /* alias for BIG5 */
-};
-
-/* ----------
- * These are "official" encoding names.
- * XXX must be sorted by the same order as enum pg_enc (in mb/pg_wchar.h)
- * ----------
- */
-#ifndef WIN32
-#define DEF_ENC2NAME(name, codepage) { #name, PG_##name }
-#else
-#define DEF_ENC2NAME(name, codepage) { #name, PG_##name, codepage }
-#endif
-const pg_enc2name pg_enc2name_tbl[] =
-{
- DEF_ENC2NAME(SQL_ASCII, 0),
- DEF_ENC2NAME(EUC_JP, 20932),
- DEF_ENC2NAME(EUC_CN, 20936),
- DEF_ENC2NAME(EUC_KR, 51949),
- DEF_ENC2NAME(EUC_TW, 0),
- DEF_ENC2NAME(EUC_JIS_2004, 20932),
- DEF_ENC2NAME(UTF8, 65001),
- DEF_ENC2NAME(MULE_INTERNAL, 0),
- DEF_ENC2NAME(LATIN1, 28591),
- DEF_ENC2NAME(LATIN2, 28592),
- DEF_ENC2NAME(LATIN3, 28593),
- DEF_ENC2NAME(LATIN4, 28594),
- DEF_ENC2NAME(LATIN5, 28599),
- DEF_ENC2NAME(LATIN6, 0),
- DEF_ENC2NAME(LATIN7, 0),
- DEF_ENC2NAME(LATIN8, 0),
- DEF_ENC2NAME(LATIN9, 28605),
- DEF_ENC2NAME(LATIN10, 0),
- DEF_ENC2NAME(WIN1256, 1256),
- DEF_ENC2NAME(WIN1258, 1258),
- DEF_ENC2NAME(WIN866, 866),
- DEF_ENC2NAME(WIN874, 874),
- DEF_ENC2NAME(KOI8R, 20866),
- DEF_ENC2NAME(WIN1251, 1251),
- DEF_ENC2NAME(WIN1252, 1252),
- DEF_ENC2NAME(ISO_8859_5, 28595),
- DEF_ENC2NAME(ISO_8859_6, 28596),
- DEF_ENC2NAME(ISO_8859_7, 28597),
- DEF_ENC2NAME(ISO_8859_8, 28598),
- DEF_ENC2NAME(WIN1250, 1250),
- DEF_ENC2NAME(WIN1253, 1253),
- DEF_ENC2NAME(WIN1254, 1254),
- DEF_ENC2NAME(WIN1255, 1255),
- DEF_ENC2NAME(WIN1257, 1257),
- DEF_ENC2NAME(KOI8U, 21866),
- DEF_ENC2NAME(SJIS, 932),
- DEF_ENC2NAME(BIG5, 950),
- DEF_ENC2NAME(GBK, 936),
- DEF_ENC2NAME(UHC, 949),
- DEF_ENC2NAME(GB18030, 54936),
- DEF_ENC2NAME(JOHAB, 0),
- DEF_ENC2NAME(SHIFT_JIS_2004, 932)
-};
-
-/* ----------
- * These are encoding names for gettext.
- *
- * This covers all encodings except MULE_INTERNAL, which is alien to gettext.
- * ----------
- */
-const pg_enc2gettext pg_enc2gettext_tbl[] =
-{
- {PG_SQL_ASCII, "US-ASCII"},
- {PG_UTF8, "UTF-8"},
- {PG_LATIN1, "LATIN1"},
- {PG_LATIN2, "LATIN2"},
- {PG_LATIN3, "LATIN3"},
- {PG_LATIN4, "LATIN4"},
- {PG_ISO_8859_5, "ISO-8859-5"},
- {PG_ISO_8859_6, "ISO_8859-6"},
- {PG_ISO_8859_7, "ISO-8859-7"},
- {PG_ISO_8859_8, "ISO-8859-8"},
- {PG_LATIN5, "LATIN5"},
- {PG_LATIN6, "LATIN6"},
- {PG_LATIN7, "LATIN7"},
- {PG_LATIN8, "LATIN8"},
- {PG_LATIN9, "LATIN-9"},
- {PG_LATIN10, "LATIN10"},
- {PG_KOI8R, "KOI8-R"},
- {PG_KOI8U, "KOI8-U"},
- {PG_WIN1250, "CP1250"},
- {PG_WIN1251, "CP1251"},
- {PG_WIN1252, "CP1252"},
- {PG_WIN1253, "CP1253"},
- {PG_WIN1254, "CP1254"},
- {PG_WIN1255, "CP1255"},
- {PG_WIN1256, "CP1256"},
- {PG_WIN1257, "CP1257"},
- {PG_WIN1258, "CP1258"},
- {PG_WIN866, "CP866"},
- {PG_WIN874, "CP874"},
- {PG_EUC_CN, "EUC-CN"},
- {PG_EUC_JP, "EUC-JP"},
- {PG_EUC_KR, "EUC-KR"},
- {PG_EUC_TW, "EUC-TW"},
- {PG_EUC_JIS_2004, "EUC-JP"},
- {PG_SJIS, "SHIFT-JIS"},
- {PG_BIG5, "BIG5"},
- {PG_GBK, "GBK"},
- {PG_UHC, "UHC"},
- {PG_GB18030, "GB18030"},
- {PG_JOHAB, "JOHAB"},
- {PG_SHIFT_JIS_2004, "SHIFT_JISX0213"},
- {0, NULL}
-};
-
-
-/* ----------
- * Encoding checks, for error returns -1 else encoding id
- * ----------
- */
-int
-pg_valid_client_encoding(const char *name)
-{
- int enc;
-
- if ((enc = pg_char_to_encoding(name)) < 0)
- return -1;
-
- if (!PG_VALID_FE_ENCODING(enc))
- return -1;
-
- return enc;
-}
-
-int
-pg_valid_server_encoding(const char *name)
-{
- int enc;
-
- if ((enc = pg_char_to_encoding(name)) < 0)
- return -1;
-
- if (!PG_VALID_BE_ENCODING(enc))
- return -1;
-
- return enc;
-}
-
-int
-pg_valid_server_encoding_id(int encoding)
-{
- return PG_VALID_BE_ENCODING(encoding);
-}
-
-/* ----------
- * Remove irrelevant chars from encoding name
- * ----------
- */
-static char *
-clean_encoding_name(const char *key, char *newkey)
-{
- const char *p;
- char *np;
-
- for (p = key, np = newkey; *p != '\0'; p++)
- {
- if (isalnum((unsigned char) *p))
- {
- if (*p >= 'A' && *p <= 'Z')
- *np++ = *p + 'a' - 'A';
- else
- *np++ = *p;
- }
- }
- *np = '\0';
- return newkey;
-}
-
-/* ----------
- * Search encoding by encoding name
- *
- * Returns encoding ID, or -1 for error
- * ----------
- */
-int
-pg_char_to_encoding(const char *name)
-{
- unsigned int nel = lengthof(pg_encname_tbl);
- const pg_encname *base = pg_encname_tbl,
- *last = base + nel - 1,
- *position;
- int result;
- char buff[NAMEDATALEN],
- *key;
-
- if (name == NULL || *name == '\0')
- return -1;
-
- if (strlen(name) >= NAMEDATALEN)
- {
-#ifdef FRONTEND
- fprintf(stderr, "encoding name too long\n");
- return -1;
-#else
- ereport(ERROR,
- (errcode(ERRCODE_NAME_TOO_LONG),
- errmsg("encoding name too long")));
-#endif
- }
- key = clean_encoding_name(name, buff);
-
- while (last >= base)
- {
- position = base + ((last - base) >> 1);
- result = key[0] - position->name[0];
-
- if (result == 0)
- {
- result = strcmp(key, position->name);
- if (result == 0)
- return position->encoding;
- }
- if (result < 0)
- last = position - 1;
- else
- base = position + 1;
- }
- return -1;
-}
-
-#ifndef FRONTEND
-Datum
-PG_char_to_encoding(PG_FUNCTION_ARGS)
-{
- Name s = PG_GETARG_NAME(0);
-
- PG_RETURN_INT32(pg_char_to_encoding(NameStr(*s)));
-}
-#endif
-
-const char *
-pg_encoding_to_char(int encoding)
-{
- if (PG_VALID_ENCODING(encoding))
- {
- const pg_enc2name *p = &pg_enc2name_tbl[encoding];
-
- Assert(encoding == p->encoding);
- return p->name;
- }
- return "";
-}
-
-#ifndef FRONTEND
-Datum
-PG_encoding_to_char(PG_FUNCTION_ARGS)
-{
- int32 encoding = PG_GETARG_INT32(0);
- const char *encoding_name = pg_encoding_to_char(encoding);
-
- return DirectFunctionCall1(namein, CStringGetDatum(encoding_name));
-}
-
-#endif
diff --git a/libpq/fe-auth.c b/libpq/fe-auth.c
deleted file mode 100644
index ab057e9..0000000
--- a/libpq/fe-auth.c
+++ /dev/null
@@ -1,841 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * fe-auth.c
- * The front-end (client) authorization routines
- *
- * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- * IDENTIFICATION
- * src/interfaces/libpq/fe-auth.c
- *
- *-------------------------------------------------------------------------
- */
-
-/*
- * INTERFACE ROUTINES
- * frontend (client) routines:
- * pg_fe_sendauth send authentication information
- * pg_fe_getauthname get user's name according to the client side
- * of the authentication system
- */
-
-#include "postgres_fe.h"
-
-#ifdef WIN32
-#include "win32.h"
-#else
-#include <unistd.h>
-#include <fcntl.h>
-#include <sys/param.h> /* for MAXHOSTNAMELEN on most */
-#include <sys/socket.h>
-#ifdef HAVE_SYS_UCRED_H
-#include <sys/ucred.h>
-#endif
-#ifndef MAXHOSTNAMELEN
-#include <netdb.h> /* for MAXHOSTNAMELEN on some */
-#endif
-#include <pwd.h>
-#endif
-
-#include "libpq-fe.h"
-#include "fe-auth.h"
-#include "libpq/md5.h"
-
-
-#ifdef ENABLE_GSS
-/*
- * GSSAPI authentication system.
- */
-
-#if defined(WIN32) && !defined(WIN32_ONLY_COMPILER)
-/*
- * MIT Kerberos GSSAPI DLL doesn't properly export the symbols for MingW
- * that contain the OIDs required. Redefine here, values copied
- * from src/athena/auth/krb5/src/lib/gssapi/generic/gssapi_generic.c
- */
-static const gss_OID_desc GSS_C_NT_HOSTBASED_SERVICE_desc =
-{10, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x04"};
-static GSS_DLLIMP gss_OID GSS_C_NT_HOSTBASED_SERVICE = &GSS_C_NT_HOSTBASED_SERVICE_desc;
-#endif
-
-/*
- * Fetch all errors of a specific type and append to "str".
- */
-static void
-pg_GSS_error_int(PQExpBuffer str, const char *mprefix,
- OM_uint32 stat, int type)
-{
- OM_uint32 lmin_s;
- gss_buffer_desc lmsg;
- OM_uint32 msg_ctx = 0;
-
- do
- {
- gss_display_status(&lmin_s, stat, type,
- GSS_C_NO_OID, &msg_ctx, &lmsg);
- appendPQExpBuffer(str, "%s: %s\n", mprefix, (char *) lmsg.value);
- gss_release_buffer(&lmin_s, &lmsg);
- } while (msg_ctx);
-}
-
-/*
- * GSSAPI errors contain two parts; put both into conn->errorMessage.
- */
-static void
-pg_GSS_error(const char *mprefix, PGconn *conn,
- OM_uint32 maj_stat, OM_uint32 min_stat)
-{
- resetPQExpBuffer(&conn->errorMessage);
-
- /* Fetch major error codes */
- pg_GSS_error_int(&conn->errorMessage, mprefix, maj_stat, GSS_C_GSS_CODE);
-
- /* Add the minor codes as well */
- pg_GSS_error_int(&conn->errorMessage, mprefix, min_stat, GSS_C_MECH_CODE);
-}
-
-/*
- * Continue GSS authentication with next token as needed.
- */
-static int
-pg_GSS_continue(PGconn *conn)
-{
- OM_uint32 maj_stat,
- min_stat,
- lmin_s;
-
- maj_stat = gss_init_sec_context(&min_stat,
- GSS_C_NO_CREDENTIAL,
- &conn->gctx,
- conn->gtarg_nam,
- GSS_C_NO_OID,
- GSS_C_MUTUAL_FLAG,
- 0,
- GSS_C_NO_CHANNEL_BINDINGS,
- (conn->gctx == GSS_C_NO_CONTEXT) ? GSS_C_NO_BUFFER : &conn->ginbuf,
- NULL,
- &conn->goutbuf,
- NULL,
- NULL);
-
- if (conn->gctx != GSS_C_NO_CONTEXT)
- {
- free(conn->ginbuf.value);
- conn->ginbuf.value = NULL;
- conn->ginbuf.length = 0;
- }
-
- if (conn->goutbuf.length != 0)
- {
- /*
- * GSS generated data to send to the server. We don't care if it's the
- * first or subsequent packet, just send the same kind of password
- * packet.
- */
- if (pqPacketSend(conn, 'p',
- conn->goutbuf.value, conn->goutbuf.length)
- != STATUS_OK)
- {
- gss_release_buffer(&lmin_s, &conn->goutbuf);
- return STATUS_ERROR;
- }
- }
- gss_release_buffer(&lmin_s, &conn->goutbuf);
-
- if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED)
- {
- pg_GSS_error(libpq_gettext("GSSAPI continuation error"),
- conn,
- maj_stat, min_stat);
- gss_release_name(&lmin_s, &conn->gtarg_nam);
- if (conn->gctx)
- gss_delete_sec_context(&lmin_s, &conn->gctx, GSS_C_NO_BUFFER);
- return STATUS_ERROR;
- }
-
- if (maj_stat == GSS_S_COMPLETE)
- gss_release_name(&lmin_s, &conn->gtarg_nam);
-
- return STATUS_OK;
-}
-
-/*
- * Send initial GSS authentication token
- */
-static int
-pg_GSS_startup(PGconn *conn)
-{
- OM_uint32 maj_stat,
- min_stat;
- int maxlen;
- gss_buffer_desc temp_gbuf;
-
- if (!(conn->pghost && conn->pghost[0] != '\0'))
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("host name must be specified\n"));
- return STATUS_ERROR;
- }
-
- if (conn->gctx)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("duplicate GSS authentication request\n"));
- return STATUS_ERROR;
- }
-
- /*
- * Import service principal name so the proper ticket can be acquired by
- * the GSSAPI system.
- */
- maxlen = NI_MAXHOST + strlen(conn->krbsrvname) + 2;
- temp_gbuf.value = (char *) malloc(maxlen);
- if (!temp_gbuf.value)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("out of memory\n"));
- return STATUS_ERROR;
- }
- snprintf(temp_gbuf.value, maxlen, "%s@%s",
- conn->krbsrvname, conn->pghost);
- temp_gbuf.length = strlen(temp_gbuf.value);
-
- maj_stat = gss_import_name(&min_stat, &temp_gbuf,
- GSS_C_NT_HOSTBASED_SERVICE, &conn->gtarg_nam);
- free(temp_gbuf.value);
-
- if (maj_stat != GSS_S_COMPLETE)
- {
- pg_GSS_error(libpq_gettext("GSSAPI name import error"),
- conn,
- maj_stat, min_stat);
- return STATUS_ERROR;
- }
-
- /*
- * Initial packet is the same as a continuation packet with no initial
- * context.
- */
- conn->gctx = GSS_C_NO_CONTEXT;
-
- return pg_GSS_continue(conn);
-}
-#endif /* ENABLE_GSS */
-
-
-#ifdef ENABLE_SSPI
-/*
- * SSPI authentication system (Windows only)
- */
-
-static void
-pg_SSPI_error(PGconn *conn, const char *mprefix, SECURITY_STATUS r)
-{
- char sysmsg[256];
-
- if (FormatMessage(FORMAT_MESSAGE_IGNORE_INSERTS |
- FORMAT_MESSAGE_FROM_SYSTEM,
- NULL, r, 0,
- sysmsg, sizeof(sysmsg), NULL) == 0)
- printfPQExpBuffer(&conn->errorMessage, "%s: SSPI error %x\n",
- mprefix, (unsigned int) r);
- else
- printfPQExpBuffer(&conn->errorMessage, "%s: %s (%x)\n",
- mprefix, sysmsg, (unsigned int) r);
-}
-
-/*
- * Continue SSPI authentication with next token as needed.
- */
-static int
-pg_SSPI_continue(PGconn *conn)
-{
- SECURITY_STATUS r;
- CtxtHandle newContext;
- ULONG contextAttr;
- SecBufferDesc inbuf;
- SecBufferDesc outbuf;
- SecBuffer OutBuffers[1];
- SecBuffer InBuffers[1];
-
- if (conn->sspictx != NULL)
- {
- /*
- * On runs other than the first we have some data to send. Put this
- * data in a SecBuffer type structure.
- */
- inbuf.ulVersion = SECBUFFER_VERSION;
- inbuf.cBuffers = 1;
- inbuf.pBuffers = InBuffers;
- InBuffers[0].pvBuffer = conn->ginbuf.value;
- InBuffers[0].cbBuffer = conn->ginbuf.length;
- InBuffers[0].BufferType = SECBUFFER_TOKEN;
- }
-
- OutBuffers[0].pvBuffer = NULL;
- OutBuffers[0].BufferType = SECBUFFER_TOKEN;
- OutBuffers[0].cbBuffer = 0;
- outbuf.cBuffers = 1;
- outbuf.pBuffers = OutBuffers;
- outbuf.ulVersion = SECBUFFER_VERSION;
-
- r = InitializeSecurityContext(conn->sspicred,
- conn->sspictx,
- conn->sspitarget,
- ISC_REQ_ALLOCATE_MEMORY,
- 0,
- SECURITY_NETWORK_DREP,
- (conn->sspictx == NULL) ? NULL : &inbuf,
- 0,
- &newContext,
- &outbuf,
- &contextAttr,
- NULL);
-
- if (r != SEC_E_OK && r != SEC_I_CONTINUE_NEEDED)
- {
- pg_SSPI_error(conn, libpq_gettext("SSPI continuation error"), r);
-
- return STATUS_ERROR;
- }
-
- if (conn->sspictx == NULL)
- {
- /* On first run, transfer retrieved context handle */
- conn->sspictx = malloc(sizeof(CtxtHandle));
- if (conn->sspictx == NULL)
- {
- printfPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory\n"));
- return STATUS_ERROR;
- }
- memcpy(conn->sspictx, &newContext, sizeof(CtxtHandle));
- }
- else
- {
- /*
- * On subsequent runs when we had data to send, free buffers that
- * contained this data.
- */
- free(conn->ginbuf.value);
- conn->ginbuf.value = NULL;
- conn->ginbuf.length = 0;
- }
-
- /*
- * If SSPI returned any data to be sent to the server (as it normally
- * would), send this data as a password packet.
- */
- if (outbuf.cBuffers > 0)
- {
- if (outbuf.cBuffers != 1)
- {
- /*
- * This should never happen, at least not for Kerberos
- * authentication. Keep check in case it shows up with other
- * authentication methods later.
- */
- printfPQExpBuffer(&conn->errorMessage, "SSPI returned invalid number of output buffers\n");
- return STATUS_ERROR;
- }
-
- /*
- * If the negotiation is complete, there may be zero bytes to send.
- * The server is at this point not expecting any more data, so don't
- * send it.
- */
- if (outbuf.pBuffers[0].cbBuffer > 0)
- {
- if (pqPacketSend(conn, 'p',
- outbuf.pBuffers[0].pvBuffer, outbuf.pBuffers[0].cbBuffer))
- {
- FreeContextBuffer(outbuf.pBuffers[0].pvBuffer);
- return STATUS_ERROR;
- }
- }
- FreeContextBuffer(outbuf.pBuffers[0].pvBuffer);
- }
-
- /* Cleanup is handled by the code in freePGconn() */
- return STATUS_OK;
-}
-
-/*
- * Send initial SSPI authentication token.
- * If use_negotiate is 0, use kerberos authentication package which is
- * compatible with Unix. If use_negotiate is 1, use the negotiate package
- * which supports both kerberos and NTLM, but is not compatible with Unix.
- */
-static int
-pg_SSPI_startup(PGconn *conn, int use_negotiate)
-{
- SECURITY_STATUS r;
- TimeStamp expire;
-
- if (conn->sspictx)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("duplicate SSPI authentication request\n"));
- return STATUS_ERROR;
- }
-
- /*
- * Retrieve credentials handle
- */
- conn->sspicred = malloc(sizeof(CredHandle));
- if (conn->sspicred == NULL)
- {
- printfPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory\n"));
- return STATUS_ERROR;
- }
-
- r = AcquireCredentialsHandle(NULL,
- use_negotiate ? "negotiate" : "kerberos",
- SECPKG_CRED_OUTBOUND,
- NULL,
- NULL,
- NULL,
- NULL,
- conn->sspicred,
- &expire);
- if (r != SEC_E_OK)
- {
- pg_SSPI_error(conn, libpq_gettext("could not acquire SSPI credentials"), r);
- free(conn->sspicred);
- conn->sspicred = NULL;
- return STATUS_ERROR;
- }
-
- /*
- * Compute target principal name. SSPI has a different format from GSSAPI,
- * but not more complex. We can skip the @REALM part, because Windows will
- * fill that in for us automatically.
- */
- if (!(conn->pghost && conn->pghost[0] != '\0'))
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("host name must be specified\n"));
- return STATUS_ERROR;
- }
- conn->sspitarget = malloc(strlen(conn->krbsrvname) + strlen(conn->pghost) + 2);
- if (!conn->sspitarget)
- {
- printfPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory\n"));
- return STATUS_ERROR;
- }
- sprintf(conn->sspitarget, "%s/%s", conn->krbsrvname, conn->pghost);
-
- /*
- * Indicate that we're in SSPI authentication mode to make sure that
- * pg_SSPI_continue is called next time in the negotiation.
- */
- conn->usesspi = 1;
-
- return pg_SSPI_continue(conn);
-}
-#endif /* ENABLE_SSPI */
-
-/*
- * Respond to AUTH_REQ_SCM_CREDS challenge.
- *
- * Note: this is dead code as of Postgres 9.1, because current backends will
- * never send this challenge. But we must keep it as long as libpq needs to
- * interoperate with pre-9.1 servers. It is believed to be needed only on
- * Debian/kFreeBSD (ie, FreeBSD kernel with Linux userland, so that the
- * getpeereid() function isn't provided by libc).
- */
-static int
-pg_local_sendauth(PGconn *conn)
-{
-#ifdef HAVE_STRUCT_CMSGCRED
- char buf;
- struct iovec iov;
- struct msghdr msg;
- struct cmsghdr *cmsg;
- union
- {
- struct cmsghdr hdr;
- unsigned char buf[CMSG_SPACE(sizeof(struct cmsgcred))];
- } cmsgbuf;
-
- /*
- * The backend doesn't care what we send here, but it wants exactly one
- * character to force recvmsg() to block and wait for us.
- */
- buf = '\0';
- iov.iov_base = &buf;
- iov.iov_len = 1;
-
- memset(&msg, 0, sizeof(msg));
- msg.msg_iov = &iov;
- msg.msg_iovlen = 1;
-
- /* We must set up a message that will be filled in by kernel */
- memset(&cmsgbuf, 0, sizeof(cmsgbuf));
- msg.msg_control = &cmsgbuf.buf;
- msg.msg_controllen = sizeof(cmsgbuf.buf);
- cmsg = CMSG_FIRSTHDR(&msg);
- cmsg->cmsg_len = CMSG_LEN(sizeof(struct cmsgcred));
- cmsg->cmsg_level = SOL_SOCKET;
- cmsg->cmsg_type = SCM_CREDS;
-
- if (sendmsg(conn->sock, &msg, 0) == -1)
- {
- char sebuf[256];
-
- printfPQExpBuffer(&conn->errorMessage,
- "pg_local_sendauth: sendmsg: %s\n",
- pqStrerror(errno, sebuf, sizeof(sebuf)));
- return STATUS_ERROR;
- }
- return STATUS_OK;
-#else
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("SCM_CRED authentication method not supported\n"));
- return STATUS_ERROR;
-#endif
-}
-
-static int
-pg_password_sendauth(PGconn *conn, const char *password, AuthRequest areq)
-{
- int ret;
- char *crypt_pwd = NULL;
- const char *pwd_to_send;
-
- /* Encrypt the password if needed. */
-
- switch (areq)
- {
- case AUTH_REQ_MD5:
- {
- char *crypt_pwd2;
-
- /* Allocate enough space for two MD5 hashes */
- crypt_pwd = malloc(2 * (MD5_PASSWD_LEN + 1));
- if (!crypt_pwd)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("out of memory\n"));
- return STATUS_ERROR;
- }
-
- crypt_pwd2 = crypt_pwd + MD5_PASSWD_LEN + 1;
- if (!pg_md5_encrypt(password, conn->pguser,
- strlen(conn->pguser), crypt_pwd2))
- {
- free(crypt_pwd);
- return STATUS_ERROR;
- }
- if (!pg_md5_encrypt(crypt_pwd2 + strlen("md5"), conn->md5Salt,
- sizeof(conn->md5Salt), crypt_pwd))
- {
- free(crypt_pwd);
- return STATUS_ERROR;
- }
-
- pwd_to_send = crypt_pwd;
- break;
- }
- case AUTH_REQ_PASSWORD:
- pwd_to_send = password;
- break;
- default:
- return STATUS_ERROR;
- }
- /* Packet has a message type as of protocol 3.0 */
- if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
- ret = pqPacketSend(conn, 'p', pwd_to_send, strlen(pwd_to_send) + 1);
- else
- ret = pqPacketSend(conn, 0, pwd_to_send, strlen(pwd_to_send) + 1);
- if (crypt_pwd)
- free(crypt_pwd);
- return ret;
-}
-
-/*
- * pg_fe_sendauth
- * client demux routine for outgoing authentication information
- */
-int
-pg_fe_sendauth(AuthRequest areq, PGconn *conn)
-{
- switch (areq)
- {
- case AUTH_REQ_OK:
- break;
-
- case AUTH_REQ_KRB4:
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("Kerberos 4 authentication not supported\n"));
- return STATUS_ERROR;
-
- case AUTH_REQ_KRB5:
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("Kerberos 5 authentication not supported\n"));
- return STATUS_ERROR;
-
-#if defined(ENABLE_GSS) || defined(ENABLE_SSPI)
- case AUTH_REQ_GSS:
-#if !defined(ENABLE_SSPI)
- /* no native SSPI, so use GSSAPI library for it */
- case AUTH_REQ_SSPI:
-#endif
- {
- int r;
-
- pglock_thread();
-
- /*
- * If we have both GSS and SSPI support compiled in, use SSPI
- * support by default. This is overridable by a connection
- * string parameter. Note that when using SSPI we still leave
- * the negotiate parameter off, since we want SSPI to use the
- * GSSAPI kerberos protocol. For actual SSPI negotiate
- * protocol, we use AUTH_REQ_SSPI.
- */
-#if defined(ENABLE_GSS) && defined(ENABLE_SSPI)
- if (conn->gsslib && (pg_strcasecmp(conn->gsslib, "gssapi") == 0))
- r = pg_GSS_startup(conn);
- else
- r = pg_SSPI_startup(conn, 0);
-#elif defined(ENABLE_GSS) && !defined(ENABLE_SSPI)
- r = pg_GSS_startup(conn);
-#elif !defined(ENABLE_GSS) && defined(ENABLE_SSPI)
- r = pg_SSPI_startup(conn, 0);
-#endif
- if (r != STATUS_OK)
- {
- /* Error message already filled in. */
- pgunlock_thread();
- return STATUS_ERROR;
- }
- pgunlock_thread();
- }
- break;
-
- case AUTH_REQ_GSS_CONT:
- {
- int r;
-
- pglock_thread();
-#if defined(ENABLE_GSS) && defined(ENABLE_SSPI)
- if (conn->usesspi)
- r = pg_SSPI_continue(conn);
- else
- r = pg_GSS_continue(conn);
-#elif defined(ENABLE_GSS) && !defined(ENABLE_SSPI)
- r = pg_GSS_continue(conn);
-#elif !defined(ENABLE_GSS) && defined(ENABLE_SSPI)
- r = pg_SSPI_continue(conn);
-#endif
- if (r != STATUS_OK)
- {
- /* Error message already filled in. */
- pgunlock_thread();
- return STATUS_ERROR;
- }
- pgunlock_thread();
- }
- break;
-#else /* defined(ENABLE_GSS) || defined(ENABLE_SSPI) */
- /* No GSSAPI *or* SSPI support */
- case AUTH_REQ_GSS:
- case AUTH_REQ_GSS_CONT:
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("GSSAPI authentication not supported\n"));
- return STATUS_ERROR;
-#endif /* defined(ENABLE_GSS) || defined(ENABLE_SSPI) */
-
-#ifdef ENABLE_SSPI
- case AUTH_REQ_SSPI:
-
- /*
- * SSPI has it's own startup message so libpq can decide which
- * method to use. Indicate to pg_SSPI_startup that we want SSPI
- * negotiation instead of Kerberos.
- */
- pglock_thread();
- if (pg_SSPI_startup(conn, 1) != STATUS_OK)
- {
- /* Error message already filled in. */
- pgunlock_thread();
- return STATUS_ERROR;
- }
- pgunlock_thread();
- break;
-#else
-
- /*
- * No SSPI support. However, if we have GSSAPI but not SSPI
- * support, AUTH_REQ_SSPI will have been handled in the codepath
- * for AUTH_REQ_GSSAPI above, so don't duplicate the case label in
- * that case.
- */
-#if !defined(ENABLE_GSS)
- case AUTH_REQ_SSPI:
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("SSPI authentication not supported\n"));
- return STATUS_ERROR;
-#endif /* !define(ENABLE_GSSAPI) */
-#endif /* ENABLE_SSPI */
-
-
- case AUTH_REQ_CRYPT:
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("Crypt authentication not supported\n"));
- return STATUS_ERROR;
-
- case AUTH_REQ_MD5:
- case AUTH_REQ_PASSWORD:
- conn->password_needed = true;
- if (conn->pgpass == NULL || conn->pgpass[0] == '\0')
- {
- printfPQExpBuffer(&conn->errorMessage,
- PQnoPasswordSupplied);
- return STATUS_ERROR;
- }
- if (pg_password_sendauth(conn, conn->pgpass, areq) != STATUS_OK)
- {
- printfPQExpBuffer(&conn->errorMessage,
- "fe_sendauth: error sending password authentication\n");
- return STATUS_ERROR;
- }
- break;
-
- case AUTH_REQ_SCM_CREDS:
- if (pg_local_sendauth(conn) != STATUS_OK)
- return STATUS_ERROR;
- break;
-
- /*
- * SASL authentication was introduced in version 10. Older
- * versions recognize the request only to give a nicer error
- * message. We call it "SCRAM authentication" in the error, rather
- * than SASL, because SCRAM is more familiar to users, and it's
- * the only SASL authentication mechanism that has been
- * implemented as of this writing, anyway.
- */
- case AUTH_REQ_SASL:
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("SCRAM authentication requires libpq version 10 or above\n"));
- return STATUS_ERROR;
-
- default:
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("authentication method %u not supported\n"), areq);
- return STATUS_ERROR;
- }
-
- return STATUS_OK;
-}
-
-
-/*
- * pg_fe_getauthname
- *
- * Returns a pointer to malloc'd space containing whatever name the user
- * has authenticated to the system. If there is an error, return NULL,
- * and put a suitable error message in *errorMessage if that's not NULL.
- */
-char *
-pg_fe_getauthname(PQExpBuffer errorMessage)
-{
- char *result = NULL;
- const char *name = NULL;
-
-#ifdef WIN32
- /* Microsoft recommends buffer size of UNLEN+1, where UNLEN = 256 */
- char username[256 + 1];
- DWORD namesize = sizeof(username);
-#else
- uid_t user_id = geteuid();
- char pwdbuf[BUFSIZ];
- struct passwd pwdstr;
- struct passwd *pw = NULL;
- int pwerr;
-#endif
-
- /*
- * Some users are using configure --enable-thread-safety-force, so we
- * might as well do the locking within our library to protect
- * pqGetpwuid(). In fact, application developers can use getpwuid() in
- * their application if they use the locking call we provide, or install
- * their own locking function using PQregisterThreadLock().
- */
- pglock_thread();
-
-#ifdef WIN32
- if (GetUserName(username, &namesize))
- name = username;
- else if (errorMessage)
- printfPQExpBuffer(errorMessage,
- libpq_gettext("user name lookup failure: error code %lu\n"),
- GetLastError());
-#else
- pwerr = pqGetpwuid(user_id, &pwdstr, pwdbuf, sizeof(pwdbuf), &pw);
- if (pw != NULL)
- name = pw->pw_name;
- else if (errorMessage)
- {
- if (pwerr != 0)
- printfPQExpBuffer(errorMessage,
- libpq_gettext("could not look up local user ID %d: %s\n"),
- (int) user_id,
- pqStrerror(pwerr, pwdbuf, sizeof(pwdbuf)));
- else
- printfPQExpBuffer(errorMessage,
- libpq_gettext("local user with ID %d does not exist\n"),
- (int) user_id);
- }
-#endif
-
- if (name)
- {
- result = strdup(name);
- if (result == NULL && errorMessage)
- printfPQExpBuffer(errorMessage,
- libpq_gettext("out of memory\n"));
- }
-
- pgunlock_thread();
-
- return result;
-}
-
-
-/*
- * PQencryptPassword -- exported routine to encrypt a password
- *
- * This is intended to be used by client applications that wish to send
- * commands like ALTER USER joe PASSWORD 'pwd'. The password need not
- * be sent in cleartext if it is encrypted on the client side. This is
- * good because it ensures the cleartext password won't end up in logs,
- * pg_stat displays, etc. We export the function so that clients won't
- * be dependent on low-level details like whether the encryption is MD5
- * or something else.
- *
- * Arguments are the cleartext password, and the SQL name of the user it
- * is for.
- *
- * Return value is a malloc'd string, or NULL if out-of-memory. The client
- * may assume the string doesn't contain any special characters that would
- * require escaping.
- */
-char *
-PQencryptPassword(const char *passwd, const char *user)
-{
- char *crypt_pwd;
-
- crypt_pwd = malloc(MD5_PASSWD_LEN + 1);
- if (!crypt_pwd)
- return NULL;
-
- if (!pg_md5_encrypt(passwd, user, strlen(user), crypt_pwd))
- {
- free(crypt_pwd);
- return NULL;
- }
-
- return crypt_pwd;
-}
diff --git a/libpq/fe-auth.h b/libpq/fe-auth.h
deleted file mode 100644
index 9d11654..0000000
--- a/libpq/fe-auth.h
+++ /dev/null
@@ -1,24 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * fe-auth.h
- *
- * Definitions for network authentication routines
- *
- * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- * src/interfaces/libpq/fe-auth.h
- *
- *-------------------------------------------------------------------------
- */
-#ifndef FE_AUTH_H
-#define FE_AUTH_H
-
-#include "libpq-fe.h"
-#include "libpq-int.h"
-
-
-extern int pg_fe_sendauth(AuthRequest areq, PGconn *conn);
-extern char *pg_fe_getauthname(PQExpBuffer errorMessage);
-
-#endif /* FE_AUTH_H */
diff --git a/libpq/fe-connect.c b/libpq/fe-connect.c
deleted file mode 100644
index e61ed7d..0000000
--- a/libpq/fe-connect.c
+++ /dev/null
@@ -1,6002 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * fe-connect.c
- * functions related to setting up a connection to the backend
- *
- * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- *
- * IDENTIFICATION
- * src/interfaces/libpq/fe-connect.c
- *
- *-------------------------------------------------------------------------
- */
-
-#include "postgres_fe.h"
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <ctype.h>
-#include <time.h>
-#include <unistd.h>
-
-#include "libpq-fe.h"
-#include "libpq-int.h"
-#include "fe-auth.h"
-#include "pg_config_paths.h"
-
-#ifdef WIN32
-#include "win32.h"
-#ifdef _WIN32_IE
-#undef _WIN32_IE
-#endif
-#define _WIN32_IE 0x0500
-#ifdef near
-#undef near
-#endif
-#define near
-#include <shlobj.h>
-#ifdef WIN32_ONLY_COMPILER /* mstcpip.h is missing on mingw */
-#include <mstcpip.h>
-#endif
-#else
-#include <sys/socket.h>
-#include <netdb.h>
-#include <netinet/in.h>
-#ifdef HAVE_NETINET_TCP_H
-#include <netinet/tcp.h>
-#endif
-#include <arpa/inet.h>
-#endif
-
-#ifdef ENABLE_THREAD_SAFETY
-#ifdef WIN32
-#include "pthread-win32.h"
-#else
-#include <pthread.h>
-#endif
-#endif
-
-#ifdef USE_LDAP
-#ifdef WIN32
-#include <winldap.h>
-#else
-/* OpenLDAP deprecates RFC 1823, but we want standard conformance */
-#define LDAP_DEPRECATED 1
-#include <ldap.h>
-typedef struct timeval LDAP_TIMEVAL;
-#endif
-static int ldapServiceLookup(const char *purl, PQconninfoOption *options,
- PQExpBuffer errorMessage);
-#endif
-
-#include "libpq/ip.h"
-#include "mb/pg_wchar.h"
-
-#ifndef FD_CLOEXEC
-#define FD_CLOEXEC 1
-#endif
-
-
-#ifndef WIN32
-#define PGPASSFILE ".pgpass"
-#else
-#define PGPASSFILE "pgpass.conf"
-#endif
-
-/*
- * Pre-9.0 servers will return this SQLSTATE if asked to set
- * application_name in a startup packet. We hard-wire the value rather
- * than looking into errcodes.h since it reflects historical behavior
- * rather than that of the current code.
- */
-#define ERRCODE_APPNAME_UNKNOWN "42704"
-
-/* This is part of the protocol so just define it */
-#define ERRCODE_INVALID_PASSWORD "28P01"
-/* This too */
-#define ERRCODE_CANNOT_CONNECT_NOW "57P03"
-
-/*
- * Cope with the various platform-specific ways to spell TCP keepalive socket
- * options. This doesn't cover Windows, which as usual does its own thing.
- */
-#if defined(TCP_KEEPIDLE)
-/* TCP_KEEPIDLE is the name of this option on Linux and *BSD */
-#define PG_TCP_KEEPALIVE_IDLE TCP_KEEPIDLE
-#define PG_TCP_KEEPALIVE_IDLE_STR "TCP_KEEPIDLE"
-#elif defined(TCP_KEEPALIVE_THRESHOLD)
-/* TCP_KEEPALIVE_THRESHOLD is the name of this option on Solaris >= 11 */
-#define PG_TCP_KEEPALIVE_IDLE TCP_KEEPALIVE_THRESHOLD
-#define PG_TCP_KEEPALIVE_IDLE_STR "TCP_KEEPALIVE_THRESHOLD"
-#elif defined(TCP_KEEPALIVE) && defined(__darwin__)
-/* TCP_KEEPALIVE is the name of this option on macOS */
-/* Caution: Solaris has this symbol but it means something different */
-#define PG_TCP_KEEPALIVE_IDLE TCP_KEEPALIVE
-#define PG_TCP_KEEPALIVE_IDLE_STR "TCP_KEEPALIVE"
-#endif
-
-/*
- * fall back options if they are not specified by arguments or defined
- * by environment variables
- */
-#define DefaultHost "localhost"
-#define DefaultTty ""
-#define DefaultOption ""
-#define DefaultAuthtype ""
-#define DefaultPassword ""
-#ifdef USE_SSL
-#define DefaultSSLMode "prefer"
-#else
-#define DefaultSSLMode "disable"
-#endif
-
-/* ----------
- * Definition of the conninfo parameters and their fallback resources.
- *
- * If Environment-Var and Compiled-in are specified as NULL, no
- * fallback is available. If after all no value can be determined
- * for an option, an error is returned.
- *
- * The value for the username is treated specially in conninfo_add_defaults.
- * If the value is not obtained any other way, the username is determined
- * by pg_fe_getauthname().
- *
- * The Label and Disp-Char entries are provided for applications that
- * want to use PQconndefaults() to create a generic database connection
- * dialog. Disp-Char is defined as follows:
- * "" Normal input field
- * "*" Password field - hide value
- * "D" Debug option - don't show by default
- *
- * PQconninfoOptions[] is a constant static array that we use to initialize
- * a dynamically allocated working copy. All the "val" fields in
- * PQconninfoOptions[] *must* be NULL. In a working copy, non-null "val"
- * fields point to malloc'd strings that should be freed when the working
- * array is freed (see PQconninfoFree).
- *
- * The first part of each struct is identical to the one in libpq-fe.h,
- * which is required since we memcpy() data between the two!
- * ----------
- */
-typedef struct _internalPQconninfoOption
-{
- char *keyword; /* The keyword of the option */
- char *envvar; /* Fallback environment variable name */
- char *compiled; /* Fallback compiled in default value */
- char *val; /* Option's current value, or NULL */
- char *label; /* Label for field in connect dialog */
- char *dispchar; /* Indicates how to display this field in a
- * connect dialog. Values are: "" Display
- * entered value as is "*" Password field -
- * hide value "D" Debug option - don't show
- * by default */
- int dispsize; /* Field size in characters for dialog */
- /* ---
- * Anything above this comment must be synchronized with
- * PQconninfoOption in libpq-fe.h, since we memcpy() data
- * between them!
- * ---
- */
- off_t connofs; /* Offset into PGconn struct, -1 if not there */
-} internalPQconninfoOption;
-
-static const internalPQconninfoOption PQconninfoOptions[] = {
- /*
- * "authtype" is no longer used, so mark it "don't show". We keep it in
- * the array so as not to reject conninfo strings from old apps that might
- * still try to set it.
- */
- {"authtype", "PGAUTHTYPE", DefaultAuthtype, NULL,
- "Database-Authtype", "D", 20, -1},
-
- {"service", "PGSERVICE", NULL, NULL,
- "Database-Service", "", 20, -1},
-
- {"user", "PGUSER", NULL, NULL,
- "Database-User", "", 20,
- offsetof(struct pg_conn, pguser)},
-
- {"password", "PGPASSWORD", NULL, NULL,
- "Database-Password", "*", 20,
- offsetof(struct pg_conn, pgpass)},
-
- {"connect_timeout", "PGCONNECT_TIMEOUT", NULL, NULL,
- "Connect-timeout", "", 10, /* strlen(INT32_MAX) == 10 */
- offsetof(struct pg_conn, connect_timeout)},
-
- {"dbname", "PGDATABASE", NULL, NULL,
- "Database-Name", "", 20,
- offsetof(struct pg_conn, dbName)},
-
- {"host", "PGHOST", NULL, NULL,
- "Database-Host", "", 40,
- offsetof(struct pg_conn, pghost)},
-
- {"hostaddr", "PGHOSTADDR", NULL, NULL,
- "Database-Host-IP-Address", "", 45,
- offsetof(struct pg_conn, pghostaddr)},
-
- {"port", "PGPORT", DEF_PGPORT_STR, NULL,
- "Database-Port", "", 6,
- offsetof(struct pg_conn, pgport)},
-
- {"client_encoding", "PGCLIENTENCODING", NULL, NULL,
- "Client-Encoding", "", 10,
- offsetof(struct pg_conn, client_encoding_initial)},
-
- /*
- * "tty" is no longer used either, but keep it present for backwards
- * compatibility.
- */
- {"tty", "PGTTY", DefaultTty, NULL,
- "Backend-Debug-TTY", "D", 40,
- offsetof(struct pg_conn, pgtty)},
-
- {"options", "PGOPTIONS", DefaultOption, NULL,
- "Backend-Debug-Options", "D", 40,
- offsetof(struct pg_conn, pgoptions)},
-
- {"application_name", "PGAPPNAME", NULL, NULL,
- "Application-Name", "", 64,
- offsetof(struct pg_conn, appname)},
-
- {"fallback_application_name", NULL, NULL, NULL,
- "Fallback-Application-Name", "", 64,
- offsetof(struct pg_conn, fbappname)},
-
- {"keepalives", NULL, NULL, NULL,
- "TCP-Keepalives", "", 1, /* should be just '0' or '1' */
- offsetof(struct pg_conn, keepalives)},
-
- {"keepalives_idle", NULL, NULL, NULL,
- "TCP-Keepalives-Idle", "", 10, /* strlen(INT32_MAX) == 10 */
- offsetof(struct pg_conn, keepalives_idle)},
-
- {"keepalives_interval", NULL, NULL, NULL,
- "TCP-Keepalives-Interval", "", 10, /* strlen(INT32_MAX) == 10 */
- offsetof(struct pg_conn, keepalives_interval)},
-
- {"keepalives_count", NULL, NULL, NULL,
- "TCP-Keepalives-Count", "", 10, /* strlen(INT32_MAX) == 10 */
- offsetof(struct pg_conn, keepalives_count)},
-
- /*
- * ssl options are allowed even without client SSL support because the
- * client can still handle SSL modes "disable" and "allow". Other
- * parameters have no effect on non-SSL connections, so there is no reason
- * to exclude them since none of them are mandatory.
- */
- {"sslmode", "PGSSLMODE", DefaultSSLMode, NULL,
- "SSL-Mode", "", 12, /* sizeof("verify-full") == 12 */
- offsetof(struct pg_conn, sslmode)},
-
- {"sslcompression", "PGSSLCOMPRESSION", "1", NULL,
- "SSL-Compression", "", 1,
- offsetof(struct pg_conn, sslcompression)},
-
- {"sslcert", "PGSSLCERT", NULL, NULL,
- "SSL-Client-Cert", "", 64,
- offsetof(struct pg_conn, sslcert)},
-
- {"sslkey", "PGSSLKEY", NULL, NULL,
- "SSL-Client-Key", "", 64,
- offsetof(struct pg_conn, sslkey)},
-
- {"sslrootcert", "PGSSLROOTCERT", NULL, NULL,
- "SSL-Root-Certificate", "", 64,
- offsetof(struct pg_conn, sslrootcert)},
-
- {"sslcrl", "PGSSLCRL", NULL, NULL,
- "SSL-Revocation-List", "", 64,
- offsetof(struct pg_conn, sslcrl)},
-
- {"requirepeer", "PGREQUIREPEER", NULL, NULL,
- "Require-Peer", "", 10,
- offsetof(struct pg_conn, requirepeer)},
-
-#if defined(ENABLE_GSS) || defined(ENABLE_SSPI)
- /* Kerberos and GSSAPI authentication support specifying the service name */
- {"krbsrvname", "PGKRBSRVNAME", PG_KRB_SRVNAM, NULL,
- "Kerberos-service-name", "", 20,
- offsetof(struct pg_conn, krbsrvname)},
-#endif
-
-#if defined(ENABLE_GSS) && defined(ENABLE_SSPI)
-
- /*
- * GSSAPI and SSPI both enabled, give a way to override which is used by
- * default
- */
- {"gsslib", "PGGSSLIB", NULL, NULL,
- "GSS-library", "", 7, /* sizeof("gssapi") = 7 */
- offsetof(struct pg_conn, gsslib)},
-#endif
-
- {"replication", NULL, NULL, NULL,
- "Replication", "D", 5,
- offsetof(struct pg_conn, replication)},
-
- /* Terminating entry --- MUST BE LAST */
- {NULL, NULL, NULL, NULL,
- NULL, NULL, 0}
-};
-
-static const PQEnvironmentOption EnvironmentOptions[] =
-{
- /* common user-interface settings */
- {
- "PGDATESTYLE", "datestyle"
- },
- {
- "PGTZ", "timezone"
- },
- /* internal performance-related settings */
- {
- "PGGEQO", "geqo"
- },
- {
- NULL, NULL
- }
-};
-
-/* The connection URI must start with either of the following designators: */
-static const char uri_designator[] = "postgresql://";
-static const char short_uri_designator[] = "postgres://";
-
-static bool connectOptions1(PGconn *conn, const char *conninfo);
-static bool connectOptions2(PGconn *conn);
-static int connectDBStart(PGconn *conn);
-static int connectDBComplete(PGconn *conn);
-static PGPing internal_ping(PGconn *conn);
-static PGconn *makeEmptyPGconn(void);
-static bool fillPGconn(PGconn *conn, PQconninfoOption *connOptions);
-static void freePGconn(PGconn *conn);
-static void closePGconn(PGconn *conn);
-static PQconninfoOption *conninfo_init(PQExpBuffer errorMessage);
-static PQconninfoOption *parse_connection_string(const char *conninfo,
- PQExpBuffer errorMessage, bool use_defaults);
-static int uri_prefix_length(const char *connstr);
-static bool recognized_connection_string(const char *connstr);
-static PQconninfoOption *conninfo_parse(const char *conninfo,
- PQExpBuffer errorMessage, bool use_defaults);
-static PQconninfoOption *conninfo_array_parse(const char *const * keywords,
- const char *const * values, PQExpBuffer errorMessage,
- bool use_defaults, int expand_dbname);
-static bool conninfo_add_defaults(PQconninfoOption *options,
- PQExpBuffer errorMessage);
-static PQconninfoOption *conninfo_uri_parse(const char *uri,
- PQExpBuffer errorMessage, bool use_defaults);
-static bool conninfo_uri_parse_options(PQconninfoOption *options,
- const char *uri, PQExpBuffer errorMessage);
-static bool conninfo_uri_parse_params(char *params,
- PQconninfoOption *connOptions,
- PQExpBuffer errorMessage);
-static char *conninfo_uri_decode(const char *str, PQExpBuffer errorMessage);
-static bool get_hexdigit(char digit, int *value);
-static const char *conninfo_getval(PQconninfoOption *connOptions,
- const char *keyword);
-static PQconninfoOption *conninfo_storeval(PQconninfoOption *connOptions,
- const char *keyword, const char *value,
- PQExpBuffer errorMessage, bool ignoreMissing, bool uri_decode);
-static PQconninfoOption *conninfo_find(PQconninfoOption *connOptions,
- const char *keyword);
-static void defaultNoticeReceiver(void *arg, const PGresult *res);
-static void defaultNoticeProcessor(void *arg, const char *message);
-static int parseServiceInfo(PQconninfoOption *options,
- PQExpBuffer errorMessage);
-static int parseServiceFile(const char *serviceFile,
- const char *service,
- PQconninfoOption *options,
- PQExpBuffer errorMessage,
- bool *group_found);
-static char *pwdfMatchesString(char *buf, char *token);
-static char *PasswordFromFile(char *hostname, char *port, char *dbname,
- char *username);
-static bool getPgPassFilename(char *pgpassfile);
-static void dot_pg_pass_warning(PGconn *conn);
-static void default_threadlock(int acquire);
-
-
-/* global variable because fe-auth.c needs to access it */
-pgthreadlock_t pg_g_threadlock = default_threadlock;
-
-
-/*
- * pqDropConnection
- *
- * Close any physical connection to the server, and reset associated
- * state inside the connection object. We don't release state that
- * would be needed to reconnect, though.
- *
- * We can always flush the output buffer, since there's no longer any hope
- * of sending that data. However, unprocessed input data might still be
- * valuable, so the caller must tell us whether to flush that or not.
- */
-void
-pqDropConnection(PGconn *conn, bool flushInput)
-{
- /* Drop any SSL state */
- pqsecure_close(conn);
-
- /* Close the socket itself */
- if (conn->sock != PGINVALID_SOCKET)
- closesocket(conn->sock);
- conn->sock = PGINVALID_SOCKET;
-
- /* Optionally discard any unread data */
- if (flushInput)
- conn->inStart = conn->inCursor = conn->inEnd = 0;
-
- /* Always discard any unsent data */
- conn->outCount = 0;
-
- /* Free authentication state */
-#ifdef ENABLE_GSS
- {
- OM_uint32 min_s;
-
- if (conn->gctx)
- gss_delete_sec_context(&min_s, &conn->gctx, GSS_C_NO_BUFFER);
- if (conn->gtarg_nam)
- gss_release_name(&min_s, &conn->gtarg_nam);
- if (conn->ginbuf.length)
- gss_release_buffer(&min_s, &conn->ginbuf);
- if (conn->goutbuf.length)
- gss_release_buffer(&min_s, &conn->goutbuf);
- }
-#endif
-#ifdef ENABLE_SSPI
- if (conn->ginbuf.length)
- free(conn->ginbuf.value);
- conn->ginbuf.length = 0;
- conn->ginbuf.value = NULL;
- if (conn->sspitarget)
- free(conn->sspitarget);
- conn->sspitarget = NULL;
- if (conn->sspicred)
- {
- FreeCredentialsHandle(conn->sspicred);
- free(conn->sspicred);
- conn->sspicred = NULL;
- }
- if (conn->sspictx)
- {
- DeleteSecurityContext(conn->sspictx);
- free(conn->sspictx);
- conn->sspictx = NULL;
- }
- conn->usesspi = 0;
-#endif
-}
-
-
-/*
- * Connecting to a Database
- *
- * There are now six different ways a user of this API can connect to the
- * database. Two are not recommended for use in new code, because of their
- * lack of extensibility with respect to the passing of options to the
- * backend. These are PQsetdb and PQsetdbLogin (the former now being a macro
- * to the latter).
- *
- * If it is desired to connect in a synchronous (blocking) manner, use the
- * function PQconnectdb or PQconnectdbParams. The former accepts a string of
- * option = value pairs (or a URI) which must be parsed; the latter takes two
- * NULL terminated arrays instead.
- *
- * To connect in an asynchronous (non-blocking) manner, use the functions
- * PQconnectStart or PQconnectStartParams (which differ in the same way as
- * PQconnectdb and PQconnectdbParams) and PQconnectPoll.
- *
- * Internally, the static functions connectDBStart, connectDBComplete
- * are part of the connection procedure.
- */
-
-/*
- * PQconnectdbParams
- *
- * establishes a connection to a postgres backend through the postmaster
- * using connection information in two arrays.
- *
- * The keywords array is defined as
- *
- * const char *params[] = {"option1", "option2", NULL}
- *
- * The values array is defined as
- *
- * const char *values[] = {"value1", "value2", NULL}
- *
- * Returns a PGconn* which is needed for all subsequent libpq calls, or NULL
- * if a memory allocation failed.
- * If the status field of the connection returned is CONNECTION_BAD,
- * then some fields may be null'ed out instead of having valid values.
- *
- * You should call PQfinish (if conn is not NULL) regardless of whether this
- * call succeeded.
- */
-PGconn *
-PQconnectdbParams(const char *const * keywords,
- const char *const * values,
- int expand_dbname)
-{
- PGconn *conn = PQconnectStartParams(keywords, values, expand_dbname);
-
- if (conn && conn->status != CONNECTION_BAD)
- (void) connectDBComplete(conn);
-
- return conn;
-
-}
-
-/*
- * PQpingParams
- *
- * check server status, accepting parameters identical to PQconnectdbParams
- */
-PGPing
-PQpingParams(const char *const * keywords,
- const char *const * values,
- int expand_dbname)
-{
- PGconn *conn = PQconnectStartParams(keywords, values, expand_dbname);
- PGPing ret;
-
- ret = internal_ping(conn);
- PQfinish(conn);
-
- return ret;
-}
-
-/*
- * PQconnectdb
- *
- * establishes a connection to a postgres backend through the postmaster
- * using connection information in a string.
- *
- * The conninfo string is either a whitespace-separated list of
- *
- * option = value
- *
- * definitions or a URI (refer to the documentation for details.) Value
- * might be a single value containing no whitespaces or a single quoted
- * string. If a single quote should appear anywhere in the value, it must be
- * escaped with a backslash like \'
- *
- * Returns a PGconn* which is needed for all subsequent libpq calls, or NULL
- * if a memory allocation failed.
- * If the status field of the connection returned is CONNECTION_BAD,
- * then some fields may be null'ed out instead of having valid values.
- *
- * You should call PQfinish (if conn is not NULL) regardless of whether this
- * call succeeded.
- */
-PGconn *
-PQconnectdb(const char *conninfo)
-{
- PGconn *conn = PQconnectStart(conninfo);
-
- if (conn && conn->status != CONNECTION_BAD)
- (void) connectDBComplete(conn);
-
- return conn;
-}
-
-/*
- * PQping
- *
- * check server status, accepting parameters identical to PQconnectdb
- */
-PGPing
-PQping(const char *conninfo)
-{
- PGconn *conn = PQconnectStart(conninfo);
- PGPing ret;
-
- ret = internal_ping(conn);
- PQfinish(conn);
-
- return ret;
-}
-
-/*
- * PQconnectStartParams
- *
- * Begins the establishment of a connection to a postgres backend through the
- * postmaster using connection information in a struct.
- *
- * See comment for PQconnectdbParams for the definition of the string format.
- *
- * Returns a PGconn*. If NULL is returned, a malloc error has occurred, and
- * you should not attempt to proceed with this connection. If the status
- * field of the connection returned is CONNECTION_BAD, an error has
- * occurred. In this case you should call PQfinish on the result, (perhaps
- * inspecting the error message first). Other fields of the structure may not
- * be valid if that occurs. If the status field is not CONNECTION_BAD, then
- * this stage has succeeded - call PQconnectPoll, using select(2) to see when
- * this is necessary.
- *
- * See PQconnectPoll for more info.
- */
-PGconn *
-PQconnectStartParams(const char *const * keywords,
- const char *const * values,
- int expand_dbname)
-{
- PGconn *conn;
- PQconninfoOption *connOptions;
-
- /*
- * Allocate memory for the conn structure
- */
- conn = makeEmptyPGconn();
- if (conn == NULL)
- return NULL;
-
- /*
- * Parse the conninfo arrays
- */
- connOptions = conninfo_array_parse(keywords, values,
- &conn->errorMessage,
- true, expand_dbname);
- if (connOptions == NULL)
- {
- conn->status = CONNECTION_BAD;
- /* errorMessage is already set */
- return conn;
- }
-
- /*
- * Move option values into conn structure
- */
- if (!fillPGconn(conn, connOptions))
- {
- PQconninfoFree(connOptions);
- return conn;
- }
-
- /*
- * Free the option info - all is in conn now
- */
- PQconninfoFree(connOptions);
-
- /*
- * Compute derived options
- */
- if (!connectOptions2(conn))
- return conn;
-
- /*
- * Connect to the database
- */
- if (!connectDBStart(conn))
- {
- /* Just in case we failed to set it in connectDBStart */
- conn->status = CONNECTION_BAD;
- }
-
- return conn;
-}
-
-/*
- * PQconnectStart
- *
- * Begins the establishment of a connection to a postgres backend through the
- * postmaster using connection information in a string.
- *
- * See comment for PQconnectdb for the definition of the string format.
- *
- * Returns a PGconn*. If NULL is returned, a malloc error has occurred, and
- * you should not attempt to proceed with this connection. If the status
- * field of the connection returned is CONNECTION_BAD, an error has
- * occurred. In this case you should call PQfinish on the result, (perhaps
- * inspecting the error message first). Other fields of the structure may not
- * be valid if that occurs. If the status field is not CONNECTION_BAD, then
- * this stage has succeeded - call PQconnectPoll, using select(2) to see when
- * this is necessary.
- *
- * See PQconnectPoll for more info.
- */
-PGconn *
-PQconnectStart(const char *conninfo)
-{
- PGconn *conn;
-
- /*
- * Allocate memory for the conn structure
- */
- conn = makeEmptyPGconn();
- if (conn == NULL)
- return NULL;
-
- /*
- * Parse the conninfo string
- */
- if (!connectOptions1(conn, conninfo))
- return conn;
-
- /*
- * Compute derived options
- */
- if (!connectOptions2(conn))
- return conn;
-
- /*
- * Connect to the database
- */
- if (!connectDBStart(conn))
- {
- /* Just in case we failed to set it in connectDBStart */
- conn->status = CONNECTION_BAD;
- }
-
- return conn;
-}
-
-/*
- * Move option values into conn structure
- *
- * Don't put anything cute here --- intelligence should be in
- * connectOptions2 ...
- *
- * Returns true on success. On failure, returns false and sets error message.
- */
-static bool
-fillPGconn(PGconn *conn, PQconninfoOption *connOptions)
-{
- const internalPQconninfoOption *option;
-
- for (option = PQconninfoOptions; option->keyword; option++)
- {
- if (option->connofs >= 0)
- {
- const char *tmp = conninfo_getval(connOptions, option->keyword);
-
- if (tmp)
- {
- char **connmember = (char **) ((char *) conn + option->connofs);
-
- if (*connmember)
- free(*connmember);
- *connmember = strdup(tmp);
- if (*connmember == NULL)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("out of memory\n"));
- return false;
- }
- }
- }
- }
-
- return true;
-}
-
-/*
- * connectOptions1
- *
- * Internal subroutine to set up connection parameters given an already-
- * created PGconn and a conninfo string. Derived settings should be
- * processed by calling connectOptions2 next. (We split them because
- * PQsetdbLogin overrides defaults in between.)
- *
- * Returns true if OK, false if trouble (in which case errorMessage is set
- * and so is conn->status).
- */
-static bool
-connectOptions1(PGconn *conn, const char *conninfo)
-{
- PQconninfoOption *connOptions;
-
- /*
- * Parse the conninfo string
- */
- connOptions = parse_connection_string(conninfo, &conn->errorMessage, true);
- if (connOptions == NULL)
- {
- conn->status = CONNECTION_BAD;
- /* errorMessage is already set */
- return false;
- }
-
- /*
- * Move option values into conn structure
- */
- if (!fillPGconn(conn, connOptions))
- {
- conn->status = CONNECTION_BAD;
- PQconninfoFree(connOptions);
- return false;
- }
-
- /*
- * Free the option info - all is in conn now
- */
- PQconninfoFree(connOptions);
-
- return true;
-}
-
-/*
- * connectOptions2
- *
- * Compute derived connection options after absorbing all user-supplied info.
- *
- * Returns true if OK, false if trouble (in which case errorMessage is set
- * and so is conn->status).
- */
-static bool
-connectOptions2(PGconn *conn)
-{
- /*
- * If user name was not given, fetch it. (Most likely, the fetch will
- * fail, since the only way we get here is if pg_fe_getauthname() failed
- * during conninfo_add_defaults(). But now we want an error message.)
- */
- if (conn->pguser == NULL || conn->pguser[0] == '\0')
- {
- if (conn->pguser)
- free(conn->pguser);
- conn->pguser = pg_fe_getauthname(&conn->errorMessage);
- if (!conn->pguser)
- {
- conn->status = CONNECTION_BAD;
- return false;
- }
- }
-
- /*
- * If database name was not given, default it to equal user name
- */
- if (conn->dbName == NULL || conn->dbName[0] == '\0')
- {
- if (conn->dbName)
- free(conn->dbName);
- conn->dbName = strdup(conn->pguser);
- if (!conn->dbName)
- goto oom_error;
- }
-
- /*
- * Supply default password if none given
- */
- if (conn->pgpass == NULL || conn->pgpass[0] == '\0')
- {
- if (conn->pgpass)
- free(conn->pgpass);
- conn->pgpass = PasswordFromFile(conn->pghost, conn->pgport,
- conn->dbName, conn->pguser);
- if (conn->pgpass == NULL)
- {
- conn->pgpass = strdup(DefaultPassword);
- if (!conn->pgpass)
- goto oom_error;
- }
- else
- conn->dot_pgpass_used = true;
- }
-
- /*
- * Allow unix socket specification in the host name
- */
- if (conn->pghost && is_absolute_path(conn->pghost))
- {
- if (conn->pgunixsocket)
- free(conn->pgunixsocket);
- conn->pgunixsocket = conn->pghost;
- conn->pghost = NULL;
- }
-
- /*
- * validate sslmode option
- */
- if (conn->sslmode)
- {
- if (strcmp(conn->sslmode, "disable") != 0
- && strcmp(conn->sslmode, "allow") != 0
- && strcmp(conn->sslmode, "prefer") != 0
- && strcmp(conn->sslmode, "require") != 0
- && strcmp(conn->sslmode, "verify-ca") != 0
- && strcmp(conn->sslmode, "verify-full") != 0)
- {
- conn->status = CONNECTION_BAD;
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("invalid sslmode value: \"%s\"\n"),
- conn->sslmode);
- return false;
- }
-
-#ifndef USE_SSL
- switch (conn->sslmode[0])
- {
- case 'a': /* "allow" */
- case 'p': /* "prefer" */
-
- /*
- * warn user that an SSL connection will never be negotiated
- * since SSL was not compiled in?
- */
- break;
-
- case 'r': /* "require" */
- case 'v': /* "verify-ca" or "verify-full" */
- conn->status = CONNECTION_BAD;
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("sslmode value \"%s\" invalid when SSL support is not compiled in\n"),
- conn->sslmode);
- return false;
- }
-#endif
- }
- else
- {
- conn->sslmode = strdup(DefaultSSLMode);
- if (!conn->sslmode)
- goto oom_error;
- }
-
- /*
- * Resolve special "auto" client_encoding from the locale
- */
- if (conn->client_encoding_initial &&
- strcmp(conn->client_encoding_initial, "auto") == 0)
- {
- free(conn->client_encoding_initial);
- conn->client_encoding_initial = strdup(pg_encoding_to_char(pg_get_encoding_from_locale(NULL, true)));
- if (!conn->client_encoding_initial)
- goto oom_error;
- }
-
- /*
- * Only if we get this far is it appropriate to try to connect. (We need a
- * state flag, rather than just the boolean result of this function, in
- * case someone tries to PQreset() the PGconn.)
- */
- conn->options_valid = true;
-
- return true;
-
-oom_error:
- conn->status = CONNECTION_BAD;
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("out of memory\n"));
- return false;
-}
-
-/*
- * PQconndefaults
- *
- * Construct a default connection options array, which identifies all the
- * available options and shows any default values that are available from the
- * environment etc. On error (eg out of memory), NULL is returned.
- *
- * Using this function, an application may determine all possible options
- * and their current default values.
- *
- * NOTE: as of PostgreSQL 7.0, the returned array is dynamically allocated
- * and should be freed when no longer needed via PQconninfoFree(). (In prior
- * versions, the returned array was static, but that's not thread-safe.)
- * Pre-7.0 applications that use this function will see a small memory leak
- * until they are updated to call PQconninfoFree.
- */
-PQconninfoOption *
-PQconndefaults(void)
-{
- PQExpBufferData errorBuf;
- PQconninfoOption *connOptions;
-
- /* We don't actually report any errors here, but callees want a buffer */
- initPQExpBuffer(&errorBuf);
- if (PQExpBufferDataBroken(errorBuf))
- return NULL; /* out of memory already :-( */
-
- connOptions = conninfo_init(&errorBuf);
- if (connOptions != NULL)
- {
- /* pass NULL errorBuf to ignore errors */
- if (!conninfo_add_defaults(connOptions, NULL))
- {
- PQconninfoFree(connOptions);
- connOptions = NULL;
- }
- }
-
- termPQExpBuffer(&errorBuf);
- return connOptions;
-}
-
-/* ----------------
- * PQsetdbLogin
- *
- * establishes a connection to a postgres backend through the postmaster
- * at the specified host and port.
- *
- * returns a PGconn* which is needed for all subsequent libpq calls
- *
- * if the status field of the connection returned is CONNECTION_BAD,
- * then only the errorMessage is likely to be useful.
- * ----------------
- */
-PGconn *
-PQsetdbLogin(const char *pghost, const char *pgport, const char *pgoptions,
- const char *pgtty, const char *dbName, const char *login,
- const char *pwd)
-{
- PGconn *conn;
-
- /*
- * Allocate memory for the conn structure
- */
- conn = makeEmptyPGconn();
- if (conn == NULL)
- return NULL;
-
- /*
- * If the dbName parameter contains what looks like a connection string,
- * parse it into conn struct using connectOptions1.
- */
- if (dbName && recognized_connection_string(dbName))
- {
- if (!connectOptions1(conn, dbName))
- return conn;
- }
- else
- {
- /*
- * Old-style path: first, parse an empty conninfo string in order to
- * set up the same defaults that PQconnectdb() would use.
- */
- if (!connectOptions1(conn, ""))
- return conn;
-
- /* Insert dbName parameter value into struct */
- if (dbName && dbName[0] != '\0')
- {
- if (conn->dbName)
- free(conn->dbName);
- conn->dbName = strdup(dbName);
- if (!conn->dbName)
- goto oom_error;
- }
- }
-
- /*
- * Insert remaining parameters into struct, overriding defaults (as well
- * as any conflicting data from dbName taken as a conninfo).
- */
- if (pghost && pghost[0] != '\0')
- {
- if (conn->pghost)
- free(conn->pghost);
- conn->pghost = strdup(pghost);
- if (!conn->pghost)
- goto oom_error;
- }
-
- if (pgport && pgport[0] != '\0')
- {
- if (conn->pgport)
- free(conn->pgport);
- conn->pgport = strdup(pgport);
- if (!conn->pgport)
- goto oom_error;
- }
-
- if (pgoptions && pgoptions[0] != '\0')
- {
- if (conn->pgoptions)
- free(conn->pgoptions);
- conn->pgoptions = strdup(pgoptions);
- if (!conn->pgoptions)
- goto oom_error;
- }
-
- if (pgtty && pgtty[0] != '\0')
- {
- if (conn->pgtty)
- free(conn->pgtty);
- conn->pgtty = strdup(pgtty);
- if (!conn->pgtty)
- goto oom_error;
- }
-
- if (login && login[0] != '\0')
- {
- if (conn->pguser)
- free(conn->pguser);
- conn->pguser = strdup(login);
- if (!conn->pguser)
- goto oom_error;
- }
-
- if (pwd && pwd[0] != '\0')
- {
- if (conn->pgpass)
- free(conn->pgpass);
- conn->pgpass = strdup(pwd);
- if (!conn->pgpass)
- goto oom_error;
- }
-
- /*
- * Compute derived options
- */
- if (!connectOptions2(conn))
- return conn;
-
- /*
- * Connect to the database
- */
- if (connectDBStart(conn))
- (void) connectDBComplete(conn);
-
- return conn;
-
-oom_error:
- conn->status = CONNECTION_BAD;
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("out of memory\n"));
- return conn;
-}
-
-
-/* ----------
- * connectNoDelay -
- * Sets the TCP_NODELAY socket option.
- * Returns 1 if successful, 0 if not.
- * ----------
- */
-static int
-connectNoDelay(PGconn *conn)
-{
-#ifdef TCP_NODELAY
- int on = 1;
-
- if (setsockopt(conn->sock, IPPROTO_TCP, TCP_NODELAY,
- (char *) &on,
- sizeof(on)) < 0)
- {
- char sebuf[256];
-
- appendPQExpBuffer(&conn->errorMessage,
- libpq_gettext("could not set socket to TCP no delay mode: %s\n"),
- SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
- return 0;
- }
-#endif
-
- return 1;
-}
-
-
-/* ----------
- * connectFailureMessage -
- * create a friendly error message on connection failure.
- * ----------
- */
-static void
-connectFailureMessage(PGconn *conn, int errorno)
-{
- char sebuf[256];
-
-#ifdef HAVE_UNIX_SOCKETS
- if (IS_AF_UNIX(conn->raddr.addr.ss_family))
- {
- char service[NI_MAXHOST];
-
- pg_getnameinfo_all(&conn->raddr.addr, conn->raddr.salen,
- NULL, 0,
- service, sizeof(service),
- NI_NUMERICSERV);
- appendPQExpBuffer(&conn->errorMessage,
- libpq_gettext("could not connect to server: %s\n"
- "\tIs the server running locally and accepting\n"
- "\tconnections on Unix domain socket \"%s\"?\n"),
- SOCK_STRERROR(errorno, sebuf, sizeof(sebuf)),
- service);
- }
- else
-#endif /* HAVE_UNIX_SOCKETS */
- {
- char host_addr[NI_MAXHOST];
- const char *displayed_host;
- struct sockaddr_storage *addr = &conn->raddr.addr;
-
- /*
- * Optionally display the network address with the hostname. This is
- * useful to distinguish between IPv4 and IPv6 connections.
- */
- if (conn->pghostaddr != NULL)
- strlcpy(host_addr, conn->pghostaddr, NI_MAXHOST);
- else if (addr->ss_family == AF_INET)
- {
- if (inet_net_ntop(AF_INET,
- &((struct sockaddr_in *) addr)->sin_addr.s_addr,
- 32,
- host_addr, sizeof(host_addr)) == NULL)
- strcpy(host_addr, "???");
- }
-#ifdef HAVE_IPV6
- else if (addr->ss_family == AF_INET6)
- {
- if (inet_net_ntop(AF_INET6,
- &((struct sockaddr_in6 *) addr)->sin6_addr.s6_addr,
- 128,
- host_addr, sizeof(host_addr)) == NULL)
- strcpy(host_addr, "???");
- }
-#endif
- else
- strcpy(host_addr, "???");
-
- if (conn->pghostaddr && conn->pghostaddr[0] != '\0')
- displayed_host = conn->pghostaddr;
- else if (conn->pghost && conn->pghost[0] != '\0')
- displayed_host = conn->pghost;
- else
- displayed_host = DefaultHost;
-
- /*
- * If the user did not supply an IP address using 'hostaddr', and
- * 'host' was missing or does not match our lookup, display the
- * looked-up IP address.
- */
- if ((conn->pghostaddr == NULL) &&
- (conn->pghost == NULL || strcmp(conn->pghost, host_addr) != 0))
- appendPQExpBuffer(&conn->errorMessage,
- libpq_gettext("could not connect to server: %s\n"
- "\tIs the server running on host \"%s\" (%s) and accepting\n"
- "\tTCP/IP connections on port %s?\n"),
- SOCK_STRERROR(errorno, sebuf, sizeof(sebuf)),
- displayed_host,
- host_addr,
- conn->pgport);
- else
- appendPQExpBuffer(&conn->errorMessage,
- libpq_gettext("could not connect to server: %s\n"
- "\tIs the server running on host \"%s\" and accepting\n"
- "\tTCP/IP connections on port %s?\n"),
- SOCK_STRERROR(errorno, sebuf, sizeof(sebuf)),
- displayed_host,
- conn->pgport);
- }
-}
-
-/*
- * Should we use keepalives? Returns 1 if yes, 0 if no, and -1 if
- * conn->keepalives is set to a value which is not parseable as an
- * integer.
- */
-static int
-useKeepalives(PGconn *conn)
-{
- char *ep;
- int val;
-
- if (conn->keepalives == NULL)
- return 1;
- val = strtol(conn->keepalives, &ep, 10);
- if (*ep)
- return -1;
- return val != 0 ? 1 : 0;
-}
-
-#ifndef WIN32
-/*
- * Set the keepalive idle timer.
- */
-static int
-setKeepalivesIdle(PGconn *conn)
-{
- int idle;
-
- if (conn->keepalives_idle == NULL)
- return 1;
-
- idle = atoi(conn->keepalives_idle);
- if (idle < 0)
- idle = 0;
-
-#ifdef PG_TCP_KEEPALIVE_IDLE
- if (setsockopt(conn->sock, IPPROTO_TCP, PG_TCP_KEEPALIVE_IDLE,
- (char *) &idle, sizeof(idle)) < 0)
- {
- char sebuf[256];
-
- appendPQExpBuffer(&conn->errorMessage,
- libpq_gettext("setsockopt(%s) failed: %s\n"),
- PG_TCP_KEEPALIVE_IDLE_STR,
- SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
- return 0;
- }
-#endif
-
- return 1;
-}
-
-/*
- * Set the keepalive interval.
- */
-static int
-setKeepalivesInterval(PGconn *conn)
-{
- int interval;
-
- if (conn->keepalives_interval == NULL)
- return 1;
-
- interval = atoi(conn->keepalives_interval);
- if (interval < 0)
- interval = 0;
-
-#ifdef TCP_KEEPINTVL
- if (setsockopt(conn->sock, IPPROTO_TCP, TCP_KEEPINTVL,
- (char *) &interval, sizeof(interval)) < 0)
- {
- char sebuf[256];
-
- appendPQExpBuffer(&conn->errorMessage,
- libpq_gettext("setsockopt(%s) failed: %s\n"),
- "TCP_KEEPINTVL",
- SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
- return 0;
- }
-#endif
-
- return 1;
-}
-
-/*
- * Set the count of lost keepalive packets that will trigger a connection
- * break.
- */
-static int
-setKeepalivesCount(PGconn *conn)
-{
- int count;
-
- if (conn->keepalives_count == NULL)
- return 1;
-
- count = atoi(conn->keepalives_count);
- if (count < 0)
- count = 0;
-
-#ifdef TCP_KEEPCNT
- if (setsockopt(conn->sock, IPPROTO_TCP, TCP_KEEPCNT,
- (char *) &count, sizeof(count)) < 0)
- {
- char sebuf[256];
-
- appendPQExpBuffer(&conn->errorMessage,
- libpq_gettext("setsockopt(%s) failed: %s\n"),
- "TCP_KEEPCNT",
- SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
- return 0;
- }
-#endif
-
- return 1;
-}
-#else /* WIN32 */
-#ifdef SIO_KEEPALIVE_VALS
-/*
- * Enable keepalives and set the keepalive values on Win32,
- * where they are always set in one batch.
- */
-static int
-setKeepalivesWin32(PGconn *conn)
-{
- struct tcp_keepalive ka;
- DWORD retsize;
- int idle = 0;
- int interval = 0;
-
- if (conn->keepalives_idle)
- idle = atoi(conn->keepalives_idle);
- if (idle <= 0)
- idle = 2 * 60 * 60; /* 2 hours = default */
-
- if (conn->keepalives_interval)
- interval = atoi(conn->keepalives_interval);
- if (interval <= 0)
- interval = 1; /* 1 second = default */
-
- ka.onoff = 1;
- ka.keepalivetime = idle * 1000;
- ka.keepaliveinterval = interval * 1000;
-
- if (WSAIoctl(conn->sock,
- SIO_KEEPALIVE_VALS,
- (LPVOID) &ka,
- sizeof(ka),
- NULL,
- 0,
- &retsize,
- NULL,
- NULL)
- != 0)
- {
- appendPQExpBuffer(&conn->errorMessage,
- libpq_gettext("WSAIoctl(SIO_KEEPALIVE_VALS) failed: %ui\n"),
- WSAGetLastError());
- return 0;
- }
- return 1;
-}
-#endif /* SIO_KEEPALIVE_VALS */
-#endif /* WIN32 */
-
-/* ----------
- * connectDBStart -
- * Begin the process of making a connection to the backend.
- *
- * Returns 1 if successful, 0 if not.
- * ----------
- */
-static int
-connectDBStart(PGconn *conn)
-{
- int portnum;
- char portstr[MAXPGPATH];
- struct addrinfo *addrs = NULL;
- struct addrinfo hint;
- const char *node;
- int ret;
-
- if (!conn)
- return 0;
-
- if (!conn->options_valid)
- goto connect_errReturn;
-
- /* Ensure our buffers are empty */
- conn->inStart = conn->inCursor = conn->inEnd = 0;
- conn->outCount = 0;
-
- /*
- * Determine the parameters to pass to pg_getaddrinfo_all.
- */
-
- /* Initialize hint structure */
- MemSet(&hint, 0, sizeof(hint));
- hint.ai_socktype = SOCK_STREAM;
- hint.ai_family = AF_UNSPEC;
-
- /* Set up port number as a string */
- if (conn->pgport != NULL && conn->pgport[0] != '\0')
- {
- portnum = atoi(conn->pgport);
- if (portnum < 1 || portnum > 65535)
- {
- appendPQExpBuffer(&conn->errorMessage,
- libpq_gettext("invalid port number: \"%s\"\n"),
- conn->pgport);
- conn->options_valid = false;
- goto connect_errReturn;
- }
- }
- else
- portnum = DEF_PGPORT;
- snprintf(portstr, sizeof(portstr), "%d", portnum);
-
- if (conn->pghostaddr != NULL && conn->pghostaddr[0] != '\0')
- {
- /* Using pghostaddr avoids a hostname lookup */
- node = conn->pghostaddr;
- hint.ai_family = AF_UNSPEC;
- hint.ai_flags = AI_NUMERICHOST;
- }
- else if (conn->pghost != NULL && conn->pghost[0] != '\0')
- {
- /* Using pghost, so we have to look-up the hostname */
- node = conn->pghost;
- hint.ai_family = AF_UNSPEC;
- }
- else
- {
-#ifdef HAVE_UNIX_SOCKETS
- /* pghostaddr and pghost are NULL, so use Unix domain socket */
- node = NULL;
- hint.ai_family = AF_UNIX;
- UNIXSOCK_PATH(portstr, portnum, conn->pgunixsocket);
- if (strlen(portstr) >= UNIXSOCK_PATH_BUFLEN)
- {
- appendPQExpBuffer(&conn->errorMessage,
- libpq_gettext("Unix-domain socket path \"%s\" is too long (maximum %d bytes)\n"),
- portstr,
- (int) (UNIXSOCK_PATH_BUFLEN - 1));
- conn->options_valid = false;
- goto connect_errReturn;
- }
-#else
- /* Without Unix sockets, default to localhost instead */
- node = DefaultHost;
- hint.ai_family = AF_UNSPEC;
-#endif /* HAVE_UNIX_SOCKETS */
- }
-
- /* Use pg_getaddrinfo_all() to resolve the address */
- ret = pg_getaddrinfo_all(node, portstr, &hint, &addrs);
- if (ret || !addrs)
- {
- if (node)
- appendPQExpBuffer(&conn->errorMessage,
- libpq_gettext("could not translate host name \"%s\" to address: %s\n"),
- node, gai_strerror(ret));
- else
- appendPQExpBuffer(&conn->errorMessage,
- libpq_gettext("could not translate Unix-domain socket path \"%s\" to address: %s\n"),
- portstr, gai_strerror(ret));
- if (addrs)
- pg_freeaddrinfo_all(hint.ai_family, addrs);
- conn->options_valid = false;
- goto connect_errReturn;
- }
-
-#ifdef USE_SSL
- /* setup values based on SSL mode */
- if (conn->sslmode[0] == 'd') /* "disable" */
- conn->allow_ssl_try = false;
- else if (conn->sslmode[0] == 'a') /* "allow" */
- conn->wait_ssl_try = true;
-#endif
-
- /*
- * Set up to try to connect, with protocol 3.0 as the first attempt.
- */
- conn->addrlist = addrs;
- conn->addr_cur = addrs;
- conn->addrlist_family = hint.ai_family;
- conn->pversion = PG_PROTOCOL(3, 0);
- conn->send_appname = true;
- conn->status = CONNECTION_NEEDED;
-
- /*
- * The code for processing CONNECTION_NEEDED state is in PQconnectPoll(),
- * so that it can easily be re-executed if needed again during the
- * asynchronous startup process. However, we must run it once here,
- * because callers expect a success return from this routine to mean that
- * we are in PGRES_POLLING_WRITING connection state.
- */
- if (PQconnectPoll(conn) == PGRES_POLLING_WRITING)
- return 1;
-
-connect_errReturn:
- pqDropConnection(conn, true);
- conn->status = CONNECTION_BAD;
- return 0;
-}
-
-
-/*
- * connectDBComplete
- *
- * Block and complete a connection.
- *
- * Returns 1 on success, 0 on failure.
- */
-static int
-connectDBComplete(PGconn *conn)
-{
- PostgresPollingStatusType flag = PGRES_POLLING_WRITING;
- time_t finish_time = ((time_t) -1);
-
- if (conn == NULL || conn->status == CONNECTION_BAD)
- return 0;
-
- /*
- * Set up a time limit, if connect_timeout isn't zero.
- */
- if (conn->connect_timeout != NULL)
- {
- int timeout = atoi(conn->connect_timeout);
-
- if (timeout > 0)
- {
- /*
- * Rounding could cause connection to fail; need at least 2 secs
- */
- if (timeout < 2)
- timeout = 2;
- /* calculate the finish time based on start + timeout */
- finish_time = time(NULL) + timeout;
- }
- }
-
- for (;;)
- {
- /*
- * Wait, if necessary. Note that the initial state (just after
- * PQconnectStart) is to wait for the socket to select for writing.
- */
- switch (flag)
- {
- case PGRES_POLLING_OK:
-
- /*
- * Reset stored error messages since we now have a working
- * connection
- */
- resetPQExpBuffer(&conn->errorMessage);
- return 1; /* success! */
-
- case PGRES_POLLING_READING:
- if (pqWaitTimed(1, 0, conn, finish_time))
- {
- conn->status = CONNECTION_BAD;
- return 0;
- }
- break;
-
- case PGRES_POLLING_WRITING:
- if (pqWaitTimed(0, 1, conn, finish_time))
- {
- conn->status = CONNECTION_BAD;
- return 0;
- }
- break;
-
- default:
- /* Just in case we failed to set it in PQconnectPoll */
- conn->status = CONNECTION_BAD;
- return 0;
- }
-
- /*
- * Now try to advance the state machine.
- */
- flag = PQconnectPoll(conn);
- }
-}
-
-/* ----------------
- * PQconnectPoll
- *
- * Poll an asynchronous connection.
- *
- * Returns a PostgresPollingStatusType.
- * Before calling this function, use select(2) to determine when data
- * has arrived..
- *
- * You must call PQfinish whether or not this fails.
- *
- * This function and PQconnectStart are intended to allow connections to be
- * made without blocking the execution of your program on remote I/O. However,
- * there are a number of caveats:
- *
- * o If you call PQtrace, ensure that the stream object into which you trace
- * will not block.
- * o If you do not supply an IP address for the remote host (i.e. you
- * supply a host name instead) then PQconnectStart will block on
- * gethostbyname. You will be fine if using Unix sockets (i.e. by
- * supplying neither a host name nor a host address).
- * o If your backend wants to use Kerberos authentication then you must
- * supply both a host name and a host address, otherwise this function
- * may block on gethostname.
- *
- * ----------------
- */
-PostgresPollingStatusType
-PQconnectPoll(PGconn *conn)
-{
- PGresult *res;
- char sebuf[256];
- int optval;
-
- if (conn == NULL)
- return PGRES_POLLING_FAILED;
-
- /* Get the new data */
- switch (conn->status)
- {
- /*
- * We really shouldn't have been polled in these two cases, but we
- * can handle it.
- */
- case CONNECTION_BAD:
- return PGRES_POLLING_FAILED;
- case CONNECTION_OK:
- return PGRES_POLLING_OK;
-
- /* These are reading states */
- case CONNECTION_AWAITING_RESPONSE:
- case CONNECTION_AUTH_OK:
- {
- /* Load waiting data */
- int n = pqReadData(conn);
-
- if (n < 0)
- goto error_return;
- if (n == 0)
- return PGRES_POLLING_READING;
-
- break;
- }
-
- /* These are writing states, so we just proceed. */
- case CONNECTION_STARTED:
- case CONNECTION_MADE:
- break;
-
- /* We allow pqSetenvPoll to decide whether to proceed. */
- case CONNECTION_SETENV:
- break;
-
- /* Special cases: proceed without waiting. */
- case CONNECTION_SSL_STARTUP:
- case CONNECTION_NEEDED:
- break;
-
- default:
- appendPQExpBufferStr(&conn->errorMessage,
- libpq_gettext(
- "invalid connection state, "
- "probably indicative of memory corruption\n"
- ));
- goto error_return;
- }
-
-
-keep_going: /* We will come back to here until there is
- * nothing left to do. */
- switch (conn->status)
- {
- case CONNECTION_NEEDED:
- {
- /*
- * Try to initiate a connection to one of the addresses
- * returned by pg_getaddrinfo_all(). conn->addr_cur is the
- * next one to try. We fail when we run out of addresses.
- */
- while (conn->addr_cur != NULL)
- {
- struct addrinfo *addr_cur = conn->addr_cur;
-
- /* Remember current address for possible error msg */
- memcpy(&conn->raddr.addr, addr_cur->ai_addr,
- addr_cur->ai_addrlen);
- conn->raddr.salen = addr_cur->ai_addrlen;
-
- conn->sock = socket(addr_cur->ai_family, SOCK_STREAM, 0);
- if (conn->sock == PGINVALID_SOCKET)
- {
- /*
- * ignore socket() failure if we have more addresses
- * to try
- */
- if (addr_cur->ai_next != NULL)
- {
- conn->addr_cur = addr_cur->ai_next;
- continue;
- }
- appendPQExpBuffer(&conn->errorMessage,
- libpq_gettext("could not create socket: %s\n"),
- SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
- break;
- }
-
- /*
- * Select socket options: no delay of outgoing data for
- * TCP sockets, nonblock mode, close-on-exec. Fail if any
- * of this fails.
- */
- if (!IS_AF_UNIX(addr_cur->ai_family))
- {
- if (!connectNoDelay(conn))
- {
- pqDropConnection(conn, true);
- conn->addr_cur = addr_cur->ai_next;
- continue;
- }
- }
- if (!pg_set_noblock(conn->sock))
- {
- appendPQExpBuffer(&conn->errorMessage,
- libpq_gettext("could not set socket to nonblocking mode: %s\n"),
- SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
- pqDropConnection(conn, true);
- conn->addr_cur = addr_cur->ai_next;
- continue;
- }
-
-#ifdef F_SETFD
- if (fcntl(conn->sock, F_SETFD, FD_CLOEXEC) == -1)
- {
- appendPQExpBuffer(&conn->errorMessage,
- libpq_gettext("could not set socket to close-on-exec mode: %s\n"),
- SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
- pqDropConnection(conn, true);
- conn->addr_cur = addr_cur->ai_next;
- continue;
- }
-#endif /* F_SETFD */
-
- if (!IS_AF_UNIX(addr_cur->ai_family))
- {
-#ifndef WIN32
- int on = 1;
-#endif
- int usekeepalives = useKeepalives(conn);
- int err = 0;
-
- if (usekeepalives < 0)
- {
- appendPQExpBufferStr(&conn->errorMessage,
- libpq_gettext("keepalives parameter must be an integer\n"));
- err = 1;
- }
- else if (usekeepalives == 0)
- {
- /* Do nothing */
- }
-#ifndef WIN32
- else if (setsockopt(conn->sock,
- SOL_SOCKET, SO_KEEPALIVE,
- (char *) &on, sizeof(on)) < 0)
- {
- appendPQExpBuffer(&conn->errorMessage,
- libpq_gettext("setsockopt(%s) failed: %s\n"),
- "SO_KEEPALIVE",
- SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
- err = 1;
- }
- else if (!setKeepalivesIdle(conn)
- || !setKeepalivesInterval(conn)
- || !setKeepalivesCount(conn))
- err = 1;
-#else /* WIN32 */
-#ifdef SIO_KEEPALIVE_VALS
- else if (!setKeepalivesWin32(conn))
- err = 1;
-#endif /* SIO_KEEPALIVE_VALS */
-#endif /* WIN32 */
-
- if (err)
- {
- pqDropConnection(conn, true);
- conn->addr_cur = addr_cur->ai_next;
- continue;
- }
- }
-
- /*----------
- * We have three methods of blocking SIGPIPE during
- * send() calls to this socket:
- *
- * - setsockopt(sock, SO_NOSIGPIPE)
- * - send(sock, ..., MSG_NOSIGNAL)
- * - setting the signal mask to SIG_IGN during send()
- *
- * The third method requires three syscalls per send,
- * so we prefer either of the first two, but they are
- * less portable. The state is tracked in the following
- * members of PGconn:
- *
- * conn->sigpipe_so - we have set up SO_NOSIGPIPE
- * conn->sigpipe_flag - we're specifying MSG_NOSIGNAL
- *
- * If we can use SO_NOSIGPIPE, then set sigpipe_so here
- * and we're done. Otherwise, set sigpipe_flag so that
- * we will try MSG_NOSIGNAL on sends. If we get an error
- * with MSG_NOSIGNAL, we'll clear that flag and revert to
- * signal masking.
- *----------
- */
- conn->sigpipe_so = false;
-#ifdef MSG_NOSIGNAL
- conn->sigpipe_flag = true;
-#else
- conn->sigpipe_flag = false;
-#endif /* MSG_NOSIGNAL */
-
-#ifdef SO_NOSIGPIPE
- optval = 1;
- if (setsockopt(conn->sock, SOL_SOCKET, SO_NOSIGPIPE,
- (char *) &optval, sizeof(optval)) == 0)
- {
- conn->sigpipe_so = true;
- conn->sigpipe_flag = false;
- }
-#endif /* SO_NOSIGPIPE */
-
- /*
- * Start/make connection. This should not block, since we
- * are in nonblock mode. If it does, well, too bad.
- */
- if (connect(conn->sock, addr_cur->ai_addr,
- addr_cur->ai_addrlen) < 0)
- {
- if (SOCK_ERRNO == EINPROGRESS ||
-#ifdef WIN32
- SOCK_ERRNO == EWOULDBLOCK ||
-#endif
- SOCK_ERRNO == EINTR)
- {
- /*
- * This is fine - we're in non-blocking mode, and
- * the connection is in progress. Tell caller to
- * wait for write-ready on socket.
- */
- conn->status = CONNECTION_STARTED;
- return PGRES_POLLING_WRITING;
- }
- /* otherwise, trouble */
- }
- else
- {
- /*
- * Hm, we're connected already --- seems the "nonblock
- * connection" wasn't. Advance the state machine and
- * go do the next stuff.
- */
- conn->status = CONNECTION_STARTED;
- goto keep_going;
- }
-
- /*
- * This connection failed --- set up error report, then
- * close socket (do it this way in case close() affects
- * the value of errno...). We will ignore the connect()
- * failure and keep going if there are more addresses.
- */
- connectFailureMessage(conn, SOCK_ERRNO);
- pqDropConnection(conn, true);
-
- /*
- * Try the next address, if any.
- */
- conn->addr_cur = addr_cur->ai_next;
- } /* loop over addresses */
-
- /*
- * Ooops, no more addresses. An appropriate error message is
- * already set up, so just set the right status.
- */
- goto error_return;
- }
-
- case CONNECTION_STARTED:
- {
- ACCEPT_TYPE_ARG3 optlen = sizeof(optval);
-
- /*
- * Write ready, since we've made it here, so the connection
- * has been made ... or has failed.
- */
-
- /*
- * Now check (using getsockopt) that there is not an error
- * state waiting for us on the socket.
- */
-
- if (getsockopt(conn->sock, SOL_SOCKET, SO_ERROR,
- (char *) &optval, &optlen) == -1)
- {
- appendPQExpBuffer(&conn->errorMessage,
- libpq_gettext("could not get socket error status: %s\n"),
- SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
- goto error_return;
- }
- else if (optval != 0)
- {
- /*
- * When using a nonblocking connect, we will typically see
- * connect failures at this point, so provide a friendly
- * error message.
- */
- connectFailureMessage(conn, optval);
- pqDropConnection(conn, true);
-
- /*
- * If more addresses remain, keep trying, just as in the
- * case where connect() returned failure immediately.
- */
- if (conn->addr_cur->ai_next != NULL)
- {
- conn->addr_cur = conn->addr_cur->ai_next;
- conn->status = CONNECTION_NEEDED;
- goto keep_going;
- }
- goto error_return;
- }
-
- /* Fill in the client address */
- conn->laddr.salen = sizeof(conn->laddr.addr);
- if (getsockname(conn->sock,
- (struct sockaddr *) & conn->laddr.addr,
- &conn->laddr.salen) < 0)
- {
- appendPQExpBuffer(&conn->errorMessage,
- libpq_gettext("could not get client address from socket: %s\n"),
- SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
- goto error_return;
- }
-
- /*
- * Make sure we can write before advancing to next step.
- */
- conn->status = CONNECTION_MADE;
- return PGRES_POLLING_WRITING;
- }
-
- case CONNECTION_MADE:
- {
- char *startpacket;
- int packetlen;
-
-#ifdef HAVE_UNIX_SOCKETS
-
- /*
- * Implement requirepeer check, if requested and it's a
- * Unix-domain socket.
- */
- if (conn->requirepeer && conn->requirepeer[0] &&
- IS_AF_UNIX(conn->raddr.addr.ss_family))
- {
- char pwdbuf[BUFSIZ];
- struct passwd pass_buf;
- struct passwd *pass;
- int passerr;
- uid_t uid;
- gid_t gid;
-
- errno = 0;
- if (getpeereid(conn->sock, &uid, &gid) != 0)
- {
- /*
- * Provide special error message if getpeereid is a
- * stub
- */
- if (errno == ENOSYS)
- appendPQExpBufferStr(&conn->errorMessage,
- libpq_gettext("requirepeer parameter is not supported on this platform\n"));
- else
- appendPQExpBuffer(&conn->errorMessage,
- libpq_gettext("could not get peer credentials: %s\n"),
- pqStrerror(errno, sebuf, sizeof(sebuf)));
- goto error_return;
- }
-
- passerr = pqGetpwuid(uid, &pass_buf, pwdbuf, sizeof(pwdbuf), &pass);
- if (pass == NULL)
- {
- if (passerr != 0)
- appendPQExpBuffer(&conn->errorMessage,
- libpq_gettext("could not look up local user ID %d: %s\n"),
- (int) uid,
- pqStrerror(passerr, sebuf, sizeof(sebuf)));
- else
- appendPQExpBuffer(&conn->errorMessage,
- libpq_gettext("local user with ID %d does not exist\n"),
- (int) uid);
- goto error_return;
- }
-
- if (strcmp(pass->pw_name, conn->requirepeer) != 0)
- {
- appendPQExpBuffer(&conn->errorMessage,
- libpq_gettext("requirepeer specifies \"%s\", but actual peer user name is \"%s\"\n"),
- conn->requirepeer, pass->pw_name);
- goto error_return;
- }
- }
-#endif /* HAVE_UNIX_SOCKETS */
-
-#ifdef USE_SSL
-
- /*
- * If SSL is enabled and we haven't already got it running,
- * request it instead of sending the startup message.
- */
- if (IS_AF_UNIX(conn->raddr.addr.ss_family))
- {
- /* Don't bother requesting SSL over a Unix socket */
- conn->allow_ssl_try = false;
- }
- if (conn->allow_ssl_try && !conn->wait_ssl_try &&
- !conn->ssl_in_use)
- {
- ProtocolVersion pv;
-
- /*
- * Send the SSL request packet.
- *
- * Theoretically, this could block, but it really
- * shouldn't since we only got here if the socket is
- * write-ready.
- */
- pv = htonl(NEGOTIATE_SSL_CODE);
- if (pqPacketSend(conn, 0, &pv, sizeof(pv)) != STATUS_OK)
- {
- appendPQExpBuffer(&conn->errorMessage,
- libpq_gettext("could not send SSL negotiation packet: %s\n"),
- SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
- goto error_return;
- }
- /* Ok, wait for response */
- conn->status = CONNECTION_SSL_STARTUP;
- return PGRES_POLLING_READING;
- }
-#endif /* USE_SSL */
-
- /*
- * Build the startup packet.
- */
- if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
- startpacket = pqBuildStartupPacket3(conn, &packetlen,
- EnvironmentOptions);
- else
- startpacket = pqBuildStartupPacket2(conn, &packetlen,
- EnvironmentOptions);
- if (!startpacket)
- {
- /*
- * will not appendbuffer here, since it's likely to also
- * run out of memory
- */
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("out of memory\n"));
- goto error_return;
- }
-
- /*
- * Send the startup packet.
- *
- * Theoretically, this could block, but it really shouldn't
- * since we only got here if the socket is write-ready.
- */
- if (pqPacketSend(conn, 0, startpacket, packetlen) != STATUS_OK)
- {
- appendPQExpBuffer(&conn->errorMessage,
- libpq_gettext("could not send startup packet: %s\n"),
- SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
- free(startpacket);
- goto error_return;
- }
-
- free(startpacket);
-
- conn->status = CONNECTION_AWAITING_RESPONSE;
- return PGRES_POLLING_READING;
- }
-
- /*
- * Handle SSL negotiation: wait for postmaster messages and
- * respond as necessary.
- */
- case CONNECTION_SSL_STARTUP:
- {
-#ifdef USE_SSL
- PostgresPollingStatusType pollres;
-
- /*
- * On first time through, get the postmaster's response to our
- * SSL negotiation packet.
- */
- if (!conn->ssl_in_use)
- {
- /*
- * We use pqReadData here since it has the logic to
- * distinguish no-data-yet from connection closure. Since
- * conn->ssl isn't set, a plain recv() will occur.
- */
- char SSLok;
- int rdresult;
-
- rdresult = pqReadData(conn);
- if (rdresult < 0)
- {
- /* errorMessage is already filled in */
- goto error_return;
- }
- if (rdresult == 0)
- {
- /* caller failed to wait for data */
- return PGRES_POLLING_READING;
- }
- if (pqGetc(&SSLok, conn) < 0)
- {
- /* should not happen really */
- return PGRES_POLLING_READING;
- }
- if (SSLok == 'S')
- {
- /* mark byte consumed */
- conn->inStart = conn->inCursor;
- /* Set up global SSL state if required */
- if (pqsecure_initialize(conn) != 0)
- goto error_return;
- }
- else if (SSLok == 'N')
- {
- /* mark byte consumed */
- conn->inStart = conn->inCursor;
- /* OK to do without SSL? */
- if (conn->sslmode[0] == 'r' || /* "require" */
- conn->sslmode[0] == 'v') /* "verify-ca" or
- * "verify-full" */
- {
- /* Require SSL, but server does not want it */
- appendPQExpBufferStr(&conn->errorMessage,
- libpq_gettext("server does not support SSL, but SSL was required\n"));
- goto error_return;
- }
- /* Otherwise, proceed with normal startup */
- conn->allow_ssl_try = false;
- conn->status = CONNECTION_MADE;
- return PGRES_POLLING_WRITING;
- }
- else if (SSLok == 'E')
- {
- /*
- * Server failure of some sort, such as failure to
- * fork a backend process. We need to process and
- * report the error message, which might be formatted
- * according to either protocol 2 or protocol 3.
- * Rather than duplicate the code for that, we flip
- * into AWAITING_RESPONSE state and let the code there
- * deal with it. Note we have *not* consumed the "E"
- * byte here.
- */
- conn->status = CONNECTION_AWAITING_RESPONSE;
- goto keep_going;
- }
- else
- {
- appendPQExpBuffer(&conn->errorMessage,
- libpq_gettext("received invalid response to SSL negotiation: %c\n"),
- SSLok);
- goto error_return;
- }
- }
-
- /*
- * Begin or continue the SSL negotiation process.
- */
- pollres = pqsecure_open_client(conn);
- if (pollres == PGRES_POLLING_OK)
- {
- /* SSL handshake done, ready to send startup packet */
- conn->status = CONNECTION_MADE;
- return PGRES_POLLING_WRITING;
- }
- if (pollres == PGRES_POLLING_FAILED)
- {
- /*
- * Failed ... if sslmode is "prefer" then do a non-SSL
- * retry
- */
- if (conn->sslmode[0] == 'p' /* "prefer" */
- && conn->allow_ssl_try /* redundant? */
- && !conn->wait_ssl_try) /* redundant? */
- {
- /* only retry once */
- conn->allow_ssl_try = false;
- /* Must drop the old connection */
- pqDropConnection(conn, true);
- conn->status = CONNECTION_NEEDED;
- goto keep_going;
- }
- }
- return pollres;
-#else /* !USE_SSL */
- /* can't get here */
- goto error_return;
-#endif /* USE_SSL */
- }
-
- /*
- * Handle authentication exchange: wait for postmaster messages
- * and respond as necessary.
- */
- case CONNECTION_AWAITING_RESPONSE:
- {
- char beresp;
- int msgLength;
- int avail;
- AuthRequest areq;
-
- /*
- * Scan the message from current point (note that if we find
- * the message is incomplete, we will return without advancing
- * inStart, and resume here next time).
- */
- conn->inCursor = conn->inStart;
-
- /* Read type byte */
- if (pqGetc(&beresp, conn))
- {
- /* We'll come back when there is more data */
- return PGRES_POLLING_READING;
- }
-
- /*
- * Validate message type: we expect only an authentication
- * request or an error here. Anything else probably means
- * it's not Postgres on the other end at all.
- */
- if (!(beresp == 'R' || beresp == 'E'))
- {
- appendPQExpBuffer(&conn->errorMessage,
- libpq_gettext(
- "expected authentication request from "
- "server, but received %c\n"),
- beresp);
- goto error_return;
- }
-
- if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
- {
- /* Read message length word */
- if (pqGetInt(&msgLength, 4, conn))
- {
- /* We'll come back when there is more data */
- return PGRES_POLLING_READING;
- }
- }
- else
- {
- /* Set phony message length to disable checks below */
- msgLength = 8;
- }
-
- /*
- * Try to validate message length before using it.
- * Authentication requests can't be very large, although GSS
- * auth requests may not be that small. Errors can be a
- * little larger, but not huge. If we see a large apparent
- * length in an error, it means we're really talking to a
- * pre-3.0-protocol server; cope.
- */
- if (beresp == 'R' && (msgLength < 8 || msgLength > 2000))
- {
- appendPQExpBuffer(&conn->errorMessage,
- libpq_gettext(
- "expected authentication request from "
- "server, but received %c\n"),
- beresp);
- goto error_return;
- }
-
- if (beresp == 'E' && (msgLength < 8 || msgLength > 30000))
- {
- /* Handle error from a pre-3.0 server */
- conn->inCursor = conn->inStart + 1; /* reread data */
- if (pqGets_append(&conn->errorMessage, conn))
- {
- /* We'll come back when there is more data */
- return PGRES_POLLING_READING;
- }
- /* OK, we read the message; mark data consumed */
- conn->inStart = conn->inCursor;
-
- /*
- * The postmaster typically won't end its message with a
- * newline, so add one to conform to libpq conventions.
- */
- appendPQExpBufferChar(&conn->errorMessage, '\n');
-
- /*
- * If we tried to open the connection in 3.0 protocol,
- * fall back to 2.0 protocol.
- */
- if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
- {
- conn->pversion = PG_PROTOCOL(2, 0);
- /* Must drop the old connection */
- pqDropConnection(conn, true);
- conn->status = CONNECTION_NEEDED;
- goto keep_going;
- }
-
- goto error_return;
- }
-
- /*
- * Can't process if message body isn't all here yet.
- *
- * (In protocol 2.0 case, we are assuming messages carry at
- * least 4 bytes of data.)
- */
- msgLength -= 4;
- avail = conn->inEnd - conn->inCursor;
- if (avail < msgLength)
- {
- /*
- * Before returning, try to enlarge the input buffer if
- * needed to hold the whole message; see notes in
- * pqParseInput3.
- */
- if (pqCheckInBufferSpace(conn->inCursor + (size_t) msgLength,
- conn))
- goto error_return;
- /* We'll come back when there is more data */
- return PGRES_POLLING_READING;
- }
-
- /* Handle errors. */
- if (beresp == 'E')
- {
- if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
- {
- if (pqGetErrorNotice3(conn, true))
- {
- /* We'll come back when there is more data */
- return PGRES_POLLING_READING;
- }
- }
- else
- {
- if (pqGets_append(&conn->errorMessage, conn))
- {
- /* We'll come back when there is more data */
- return PGRES_POLLING_READING;
- }
- }
- /* OK, we read the message; mark data consumed */
- conn->inStart = conn->inCursor;
-
-#ifdef USE_SSL
-
- /*
- * if sslmode is "allow" and we haven't tried an SSL
- * connection already, then retry with an SSL connection
- */
- if (conn->sslmode[0] == 'a' /* "allow" */
- && !conn->ssl_in_use
- && conn->allow_ssl_try
- && conn->wait_ssl_try)
- {
- /* only retry once */
- conn->wait_ssl_try = false;
- /* Must drop the old connection */
- pqDropConnection(conn, true);
- conn->status = CONNECTION_NEEDED;
- goto keep_going;
- }
-
- /*
- * if sslmode is "prefer" and we're in an SSL connection,
- * then do a non-SSL retry
- */
- if (conn->sslmode[0] == 'p' /* "prefer" */
- && conn->allow_ssl_try
- && !conn->wait_ssl_try) /* redundant? */
- {
- /* only retry once */
- conn->allow_ssl_try = false;
- /* Must drop the old connection */
- pqDropConnection(conn, true);
- conn->status = CONNECTION_NEEDED;
- goto keep_going;
- }
-#endif
-
- goto error_return;
- }
-
- /* It is an authentication request. */
- conn->auth_req_received = true;
-
- /* Get the type of request. */
- if (pqGetInt((int *) &areq, 4, conn))
- {
- /* We'll come back when there are more data */
- return PGRES_POLLING_READING;
- }
-
- /* Get the password salt if there is one. */
- if (areq == AUTH_REQ_MD5)
- {
- if (pqGetnchar(conn->md5Salt,
- sizeof(conn->md5Salt), conn))
- {
- /* We'll come back when there are more data */
- return PGRES_POLLING_READING;
- }
- }
-#if defined(ENABLE_GSS) || defined(ENABLE_SSPI)
-
- /*
- * Continue GSSAPI/SSPI authentication
- */
- if (areq == AUTH_REQ_GSS_CONT)
- {
- int llen = msgLength - 4;
-
- /*
- * We can be called repeatedly for the same buffer. Avoid
- * re-allocating the buffer in this case - just re-use the
- * old buffer.
- */
- if (llen != conn->ginbuf.length)
- {
- if (conn->ginbuf.value)
- free(conn->ginbuf.value);
-
- conn->ginbuf.length = llen;
- conn->ginbuf.value = malloc(llen);
- if (!conn->ginbuf.value)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("out of memory allocating GSSAPI buffer (%d)"),
- llen);
- goto error_return;
- }
- }
-
- if (pqGetnchar(conn->ginbuf.value, llen, conn))
- {
- /* We'll come back when there is more data. */
- return PGRES_POLLING_READING;
- }
- }
-#endif
-
- /*
- * OK, we successfully read the message; mark data consumed
- */
- conn->inStart = conn->inCursor;
-
- /* Respond to the request if necessary. */
-
- /*
- * Note that conn->pghost must be non-NULL if we are going to
- * avoid the Kerberos code doing a hostname look-up.
- */
-
- if (pg_fe_sendauth(areq, conn) != STATUS_OK)
- {
- conn->errorMessage.len = strlen(conn->errorMessage.data);
- goto error_return;
- }
- conn->errorMessage.len = strlen(conn->errorMessage.data);
-
- /*
- * Just make sure that any data sent by pg_fe_sendauth is
- * flushed out. Although this theoretically could block, it
- * really shouldn't since we don't send large auth responses.
- */
- if (pqFlush(conn))
- goto error_return;
-
- if (areq == AUTH_REQ_OK)
- {
- /* We are done with authentication exchange */
- conn->status = CONNECTION_AUTH_OK;
-
- /*
- * Set asyncStatus so that PQgetResult will think that
- * what comes back next is the result of a query. See
- * below.
- */
- conn->asyncStatus = PGASYNC_BUSY;
- }
-
- /* Look to see if we have more data yet. */
- goto keep_going;
- }
-
- case CONNECTION_AUTH_OK:
- {
- /*
- * Now we expect to hear from the backend. A ReadyForQuery
- * message indicates that startup is successful, but we might
- * also get an Error message indicating failure. (Notice
- * messages indicating nonfatal warnings are also allowed by
- * the protocol, as are ParameterStatus and BackendKeyData
- * messages.) Easiest way to handle this is to let
- * PQgetResult() read the messages. We just have to fake it
- * out about the state of the connection, by setting
- * asyncStatus = PGASYNC_BUSY (done above).
- */
-
- if (PQisBusy(conn))
- return PGRES_POLLING_READING;
-
- res = PQgetResult(conn);
-
- /*
- * NULL return indicating we have gone to IDLE state is
- * expected
- */
- if (res)
- {
- if (res->resultStatus != PGRES_FATAL_ERROR)
- appendPQExpBufferStr(&conn->errorMessage,
- libpq_gettext("unexpected message from server during startup\n"));
- else if (conn->send_appname &&
- (conn->appname || conn->fbappname))
- {
- /*
- * If we tried to send application_name, check to see
- * if the error is about that --- pre-9.0 servers will
- * reject it at this stage of the process. If so,
- * close the connection and retry without sending
- * application_name. We could possibly get a false
- * SQLSTATE match here and retry uselessly, but there
- * seems no great harm in that; we'll just get the
- * same error again if it's unrelated.
- */
- const char *sqlstate;
-
- sqlstate = PQresultErrorField(res, PG_DIAG_SQLSTATE);
- if (sqlstate &&
- strcmp(sqlstate, ERRCODE_APPNAME_UNKNOWN) == 0)
- {
- PQclear(res);
- conn->send_appname = false;
- /* Must drop the old connection */
- pqDropConnection(conn, true);
- conn->status = CONNECTION_NEEDED;
- goto keep_going;
- }
- }
-
- /*
- * if the resultStatus is FATAL, then conn->errorMessage
- * already has a copy of the error; needn't copy it back.
- * But add a newline if it's not there already, since
- * postmaster error messages may not have one.
- */
- if (conn->errorMessage.len <= 0 ||
- conn->errorMessage.data[conn->errorMessage.len - 1] != '\n')
- appendPQExpBufferChar(&conn->errorMessage, '\n');
- PQclear(res);
- goto error_return;
- }
-
- /* We can release the address list now. */
- pg_freeaddrinfo_all(conn->addrlist_family, conn->addrlist);
- conn->addrlist = NULL;
- conn->addr_cur = NULL;
-
- /* Fire up post-connection housekeeping if needed */
- if (PG_PROTOCOL_MAJOR(conn->pversion) < 3)
- {
- conn->status = CONNECTION_SETENV;
- conn->setenv_state = SETENV_STATE_CLIENT_ENCODING_SEND;
- conn->next_eo = EnvironmentOptions;
- return PGRES_POLLING_WRITING;
- }
-
- /* Otherwise, we are open for business! */
- conn->status = CONNECTION_OK;
- return PGRES_POLLING_OK;
- }
-
- case CONNECTION_SETENV:
-
- /*
- * Do post-connection housekeeping (only needed in protocol 2.0).
- *
- * We pretend that the connection is OK for the duration of these
- * queries.
- */
- conn->status = CONNECTION_OK;
-
- switch (pqSetenvPoll(conn))
- {
- case PGRES_POLLING_OK: /* Success */
- break;
-
- case PGRES_POLLING_READING: /* Still going */
- conn->status = CONNECTION_SETENV;
- return PGRES_POLLING_READING;
-
- case PGRES_POLLING_WRITING: /* Still going */
- conn->status = CONNECTION_SETENV;
- return PGRES_POLLING_WRITING;
-
- default:
- goto error_return;
- }
-
- /* We are open for business! */
- conn->status = CONNECTION_OK;
- return PGRES_POLLING_OK;
-
- default:
- appendPQExpBuffer(&conn->errorMessage,
- libpq_gettext("invalid connection state %d, "
- "probably indicative of memory corruption\n"),
- conn->status);
- goto error_return;
- }
-
- /* Unreachable */
-
-error_return:
-
- dot_pg_pass_warning(conn);
-
- /*
- * We used to close the socket at this point, but that makes it awkward
- * for those above us if they wish to remove this socket from their own
- * records (an fd_set for example). We'll just have this socket closed
- * when PQfinish is called (which is compulsory even after an error, since
- * the connection structure must be freed).
- */
- conn->status = CONNECTION_BAD;
- return PGRES_POLLING_FAILED;
-}
-
-
-/*
- * internal_ping
- * Determine if a server is running and if we can connect to it.
- *
- * The argument is a connection that's been started, but not completed.
- */
-static PGPing
-internal_ping(PGconn *conn)
-{
- /* Say "no attempt" if we never got to PQconnectPoll */
- if (!conn || !conn->options_valid)
- return PQPING_NO_ATTEMPT;
-
- /* Attempt to complete the connection */
- if (conn->status != CONNECTION_BAD)
- (void) connectDBComplete(conn);
-
- /* Definitely OK if we succeeded */
- if (conn->status != CONNECTION_BAD)
- return PQPING_OK;
-
- /*
- * Here begins the interesting part of "ping": determine the cause of the
- * failure in sufficient detail to decide what to return. We do not want
- * to report that the server is not up just because we didn't have a valid
- * password, for example. In fact, any sort of authentication request
- * implies the server is up. (We need this check since the libpq side of
- * things might have pulled the plug on the connection before getting an
- * error as such from the postmaster.)
- */
- if (conn->auth_req_received)
- return PQPING_OK;
-
- /*
- * If we failed to get any ERROR response from the postmaster, report
- * PQPING_NO_RESPONSE. This result could be somewhat misleading for a
- * pre-7.4 server, since it won't send back a SQLSTATE, but those are long
- * out of support. Another corner case where the server could return a
- * failure without a SQLSTATE is fork failure, but NO_RESPONSE isn't
- * totally unreasonable for that anyway. We expect that every other
- * failure case in a modern server will produce a report with a SQLSTATE.
- *
- * NOTE: whenever we get around to making libpq generate SQLSTATEs for
- * client-side errors, we should either not store those into
- * last_sqlstate, or add an extra flag so we can tell client-side errors
- * apart from server-side ones.
- */
- if (strlen(conn->last_sqlstate) != 5)
- return PQPING_NO_RESPONSE;
-
- /*
- * Report PQPING_REJECT if server says it's not accepting connections. (We
- * distinguish this case mainly for the convenience of pg_ctl.)
- */
- if (strcmp(conn->last_sqlstate, ERRCODE_CANNOT_CONNECT_NOW) == 0)
- return PQPING_REJECT;
-
- /*
- * Any other SQLSTATE can be taken to indicate that the server is up.
- * Presumably it didn't like our username, password, or database name; or
- * perhaps it had some transient failure, but that should not be taken as
- * meaning "it's down".
- */
- return PQPING_OK;
-}
-
-
-/*
- * makeEmptyPGconn
- * - create a PGconn data structure with (as yet) no interesting data
- */
-static PGconn *
-makeEmptyPGconn(void)
-{
- PGconn *conn;
-
-#ifdef WIN32
-
- /*
- * Make sure socket support is up and running.
- */
- WSADATA wsaData;
-
- if (WSAStartup(MAKEWORD(1, 1), &wsaData))
- return NULL;
- WSASetLastError(0);
-#endif
-
- conn = (PGconn *) malloc(sizeof(PGconn));
- if (conn == NULL)
- {
-#ifdef WIN32
- WSACleanup();
-#endif
- return conn;
- }
-
- /* Zero all pointers and booleans */
- MemSet(conn, 0, sizeof(PGconn));
-
- /* install default notice hooks */
- conn->noticeHooks.noticeRec = defaultNoticeReceiver;
- conn->noticeHooks.noticeProc = defaultNoticeProcessor;
-
- conn->status = CONNECTION_BAD;
- conn->asyncStatus = PGASYNC_IDLE;
- conn->xactStatus = PQTRANS_IDLE;
- conn->options_valid = false;
- conn->nonblocking = false;
- conn->setenv_state = SETENV_STATE_IDLE;
- conn->client_encoding = PG_SQL_ASCII;
- conn->std_strings = false; /* unless server says differently */
- conn->verbosity = PQERRORS_DEFAULT;
- conn->show_context = PQSHOW_CONTEXT_ERRORS;
- conn->sock = PGINVALID_SOCKET;
- conn->auth_req_received = false;
- conn->password_needed = false;
- conn->dot_pgpass_used = false;
-#ifdef USE_SSL
- conn->allow_ssl_try = true;
- conn->wait_ssl_try = false;
- conn->ssl_in_use = false;
-#endif
-
- /*
- * We try to send at least 8K at a time, which is the usual size of pipe
- * buffers on Unix systems. That way, when we are sending a large amount
- * of data, we avoid incurring extra kernel context swaps for partial
- * bufferloads. The output buffer is initially made 16K in size, and we
- * try to dump it after accumulating 8K.
- *
- * With the same goal of minimizing context swaps, the input buffer will
- * be enlarged anytime it has less than 8K free, so we initially allocate
- * twice that.
- */
- conn->inBufSize = 16 * 1024;
- conn->inBuffer = (char *) malloc(conn->inBufSize);
- conn->outBufSize = 16 * 1024;
- conn->outBuffer = (char *) malloc(conn->outBufSize);
- conn->rowBufLen = 32;
- conn->rowBuf = (PGdataValue *) malloc(conn->rowBufLen * sizeof(PGdataValue));
- initPQExpBuffer(&conn->errorMessage);
- initPQExpBuffer(&conn->workBuffer);
-
- if (conn->inBuffer == NULL ||
- conn->outBuffer == NULL ||
- conn->rowBuf == NULL ||
- PQExpBufferBroken(&conn->errorMessage) ||
- PQExpBufferBroken(&conn->workBuffer))
- {
- /* out of memory already :-( */
- freePGconn(conn);
- conn = NULL;
- }
-
- return conn;
-}
-
-/*
- * freePGconn
- * - free an idle (closed) PGconn data structure
- *
- * NOTE: this should not overlap any functionality with closePGconn().
- * Clearing/resetting of transient state belongs there; what we do here is
- * release data that is to be held for the life of the PGconn structure.
- * If a value ought to be cleared/freed during PQreset(), do it there not here.
- */
-static void
-freePGconn(PGconn *conn)
-{
- int i;
-
- /* let any event procs clean up their state data */
- for (i = 0; i < conn->nEvents; i++)
- {
- PGEventConnDestroy evt;
-
- evt.conn = conn;
- (void) conn->events[i].proc(PGEVT_CONNDESTROY, &evt,
- conn->events[i].passThrough);
- free(conn->events[i].name);
- }
-
- if (conn->client_encoding_initial)
- free(conn->client_encoding_initial);
- if (conn->events)
- free(conn->events);
- if (conn->pghost)
- free(conn->pghost);
- if (conn->pghostaddr)
- free(conn->pghostaddr);
- if (conn->pgport)
- free(conn->pgport);
- if (conn->pgunixsocket)
- free(conn->pgunixsocket);
- if (conn->pgtty)
- free(conn->pgtty);
- if (conn->connect_timeout)
- free(conn->connect_timeout);
- if (conn->pgoptions)
- free(conn->pgoptions);
- if (conn->appname)
- free(conn->appname);
- if (conn->fbappname)
- free(conn->fbappname);
- if (conn->dbName)
- free(conn->dbName);
- if (conn->replication)
- free(conn->replication);
- if (conn->pguser)
- free(conn->pguser);
- if (conn->pgpass)
- free(conn->pgpass);
- if (conn->keepalives)
- free(conn->keepalives);
- if (conn->keepalives_idle)
- free(conn->keepalives_idle);
- if (conn->keepalives_interval)
- free(conn->keepalives_interval);
- if (conn->keepalives_count)
- free(conn->keepalives_count);
- if (conn->sslmode)
- free(conn->sslmode);
- if (conn->sslcert)
- free(conn->sslcert);
- if (conn->sslkey)
- free(conn->sslkey);
- if (conn->sslrootcert)
- free(conn->sslrootcert);
- if (conn->sslcrl)
- free(conn->sslcrl);
- if (conn->sslcompression)
- free(conn->sslcompression);
- if (conn->requirepeer)
- free(conn->requirepeer);
-#if defined(ENABLE_GSS) || defined(ENABLE_SSPI)
- if (conn->krbsrvname)
- free(conn->krbsrvname);
-#endif
-#if defined(ENABLE_GSS) && defined(ENABLE_SSPI)
- if (conn->gsslib)
- free(conn->gsslib);
-#endif
- /* Note that conn->Pfdebug is not ours to close or free */
- if (conn->last_query)
- free(conn->last_query);
- if (conn->inBuffer)
- free(conn->inBuffer);
- if (conn->outBuffer)
- free(conn->outBuffer);
- if (conn->rowBuf)
- free(conn->rowBuf);
- termPQExpBuffer(&conn->errorMessage);
- termPQExpBuffer(&conn->workBuffer);
-
- free(conn);
-
-#ifdef WIN32
- WSACleanup();
-#endif
-}
-
-/*
- * closePGconn
- * - properly close a connection to the backend
- *
- * This should reset or release all transient state, but NOT the connection
- * parameters. On exit, the PGconn should be in condition to start a fresh
- * connection with the same parameters (see PQreset()).
- */
-static void
-closePGconn(PGconn *conn)
-{
- PGnotify *notify;
- pgParameterStatus *pstatus;
-
- /*
- * Note that the protocol doesn't allow us to send Terminate messages
- * during the startup phase.
- */
- if (conn->sock != PGINVALID_SOCKET && conn->status == CONNECTION_OK)
- {
- /*
- * Try to send "close connection" message to backend. Ignore any
- * error.
- */
- pqPutMsgStart('X', false, conn);
- pqPutMsgEnd(conn);
- (void) pqFlush(conn);
- }
-
- /*
- * Must reset the blocking status so a possible reconnect will work.
- *
- * Don't call PQsetnonblocking() because it will fail if it's unable to
- * flush the connection.
- */
- conn->nonblocking = FALSE;
-
- /*
- * Close the connection, reset all transient state, flush I/O buffers.
- */
- pqDropConnection(conn, true);
- conn->status = CONNECTION_BAD; /* Well, not really _bad_ - just
- * absent */
- conn->asyncStatus = PGASYNC_IDLE;
- pqClearAsyncResult(conn); /* deallocate result */
- resetPQExpBuffer(&conn->errorMessage);
- pg_freeaddrinfo_all(conn->addrlist_family, conn->addrlist);
- conn->addrlist = NULL;
- conn->addr_cur = NULL;
- notify = conn->notifyHead;
- while (notify != NULL)
- {
- PGnotify *prev = notify;
-
- notify = notify->next;
- free(prev);
- }
- conn->notifyHead = conn->notifyTail = NULL;
- pstatus = conn->pstatus;
- while (pstatus != NULL)
- {
- pgParameterStatus *prev = pstatus;
-
- pstatus = pstatus->next;
- free(prev);
- }
- conn->pstatus = NULL;
- if (conn->lobjfuncs)
- free(conn->lobjfuncs);
- conn->lobjfuncs = NULL;
-}
-
-/*
- * PQfinish: properly close a connection to the backend. Also frees
- * the PGconn data structure so it shouldn't be re-used after this.
- */
-void
-PQfinish(PGconn *conn)
-{
- if (conn)
- {
- closePGconn(conn);
- freePGconn(conn);
- }
-}
-
-/*
- * PQreset: resets the connection to the backend by closing the
- * existing connection and creating a new one.
- */
-void
-PQreset(PGconn *conn)
-{
- if (conn)
- {
- closePGconn(conn);
-
- if (connectDBStart(conn) && connectDBComplete(conn))
- {
- /*
- * Notify event procs of successful reset. We treat an event proc
- * failure as disabling the connection ... good idea?
- */
- int i;
-
- for (i = 0; i < conn->nEvents; i++)
- {
- PGEventConnReset evt;
-
- evt.conn = conn;
- if (!conn->events[i].proc(PGEVT_CONNRESET, &evt,
- conn->events[i].passThrough))
- {
- conn->status = CONNECTION_BAD;
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("PGEventProc \"%s\" failed during PGEVT_CONNRESET event\n"),
- conn->events[i].name);
- break;
- }
- }
- }
- }
-}
-
-
-/*
- * PQresetStart:
- * resets the connection to the backend
- * closes the existing connection and makes a new one
- * Returns 1 on success, 0 on failure.
- */
-int
-PQresetStart(PGconn *conn)
-{
- if (conn)
- {
- closePGconn(conn);
-
- return connectDBStart(conn);
- }
-
- return 0;
-}
-
-
-/*
- * PQresetPoll:
- * resets the connection to the backend
- * closes the existing connection and makes a new one
- */
-PostgresPollingStatusType
-PQresetPoll(PGconn *conn)
-{
- if (conn)
- {
- PostgresPollingStatusType status = PQconnectPoll(conn);
-
- if (status == PGRES_POLLING_OK)
- {
- /*
- * Notify event procs of successful reset. We treat an event proc
- * failure as disabling the connection ... good idea?
- */
- int i;
-
- for (i = 0; i < conn->nEvents; i++)
- {
- PGEventConnReset evt;
-
- evt.conn = conn;
- if (!conn->events[i].proc(PGEVT_CONNRESET, &evt,
- conn->events[i].passThrough))
- {
- conn->status = CONNECTION_BAD;
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("PGEventProc \"%s\" failed during PGEVT_CONNRESET event\n"),
- conn->events[i].name);
- return PGRES_POLLING_FAILED;
- }
- }
- }
-
- return status;
- }
-
- return PGRES_POLLING_FAILED;
-}
-
-/*
- * PQgetCancel: get a PGcancel structure corresponding to a connection.
- *
- * A copy is needed to be able to cancel a running query from a different
- * thread. If the same structure is used all structure members would have
- * to be individually locked (if the entire structure was locked, it would
- * be impossible to cancel a synchronous query because the structure would
- * have to stay locked for the duration of the query).
- */
-PGcancel *
-PQgetCancel(PGconn *conn)
-{
- PGcancel *cancel;
-
- if (!conn)
- return NULL;
-
- if (conn->sock == PGINVALID_SOCKET)
- return NULL;
-
- cancel = malloc(sizeof(PGcancel));
- if (cancel == NULL)
- return NULL;
-
- memcpy(&cancel->raddr, &conn->raddr, sizeof(SockAddr));
- cancel->be_pid = conn->be_pid;
- cancel->be_key = conn->be_key;
-
- return cancel;
-}
-
-/* PQfreeCancel: free a cancel structure */
-void
-PQfreeCancel(PGcancel *cancel)
-{
- if (cancel)
- free(cancel);
-}
-
-
-/*
- * PQcancel and PQrequestCancel: attempt to request cancellation of the
- * current operation.
- *
- * The return value is TRUE if the cancel request was successfully
- * dispatched, FALSE if not (in which case an error message is available).
- * Note: successful dispatch is no guarantee that there will be any effect at
- * the backend. The application must read the operation result as usual.
- *
- * CAUTION: we want this routine to be safely callable from a signal handler
- * (for example, an application might want to call it in a SIGINT handler).
- * This means we cannot use any C library routine that might be non-reentrant.
- * malloc/free are often non-reentrant, and anything that might call them is
- * just as dangerous. We avoid sprintf here for that reason. Building up
- * error messages with strcpy/strcat is tedious but should be quite safe.
- * We also save/restore errno in case the signal handler support doesn't.
- *
- * internal_cancel() is an internal helper function to make code-sharing
- * between the two versions of the cancel function possible.
- */
-static int
-internal_cancel(SockAddr *raddr, int be_pid, int be_key,
- char *errbuf, int errbufsize)
-{
- int save_errno = SOCK_ERRNO;
- pgsocket tmpsock = PGINVALID_SOCKET;
- char sebuf[256];
- int maxlen;
- struct
- {
- uint32 packetlen;
- CancelRequestPacket cp;
- } crp;
-
- /*
- * We need to open a temporary connection to the postmaster. Do this with
- * only kernel calls.
- */
- if ((tmpsock = socket(raddr->addr.ss_family, SOCK_STREAM, 0)) == PGINVALID_SOCKET)
- {
- strlcpy(errbuf, "PQcancel() -- socket() failed: ", errbufsize);
- goto cancel_errReturn;
- }
-retry3:
- if (connect(tmpsock, (struct sockaddr *) & raddr->addr,
- raddr->salen) < 0)
- {
- if (SOCK_ERRNO == EINTR)
- /* Interrupted system call - we'll just try again */
- goto retry3;
- strlcpy(errbuf, "PQcancel() -- connect() failed: ", errbufsize);
- goto cancel_errReturn;
- }
-
- /*
- * We needn't set nonblocking I/O or NODELAY options here.
- */
-
- /* Create and send the cancel request packet. */
-
- crp.packetlen = htonl((uint32) sizeof(crp));
- crp.cp.cancelRequestCode = (MsgType) htonl(CANCEL_REQUEST_CODE);
- crp.cp.backendPID = htonl(be_pid);
- crp.cp.cancelAuthCode = htonl(be_key);
-
-retry4:
- if (send(tmpsock, (char *) &crp, sizeof(crp), 0) != (int) sizeof(crp))
- {
- if (SOCK_ERRNO == EINTR)
- /* Interrupted system call - we'll just try again */
- goto retry4;
- strlcpy(errbuf, "PQcancel() -- send() failed: ", errbufsize);
- goto cancel_errReturn;
- }
-
- /*
- * Wait for the postmaster to close the connection, which indicates that
- * it's processed the request. Without this delay, we might issue another
- * command only to find that our cancel zaps that command instead of the
- * one we thought we were canceling. Note we don't actually expect this
- * read to obtain any data, we are just waiting for EOF to be signaled.
- */
-retry5:
- if (recv(tmpsock, (char *) &crp, 1, 0) < 0)
- {
- if (SOCK_ERRNO == EINTR)
- /* Interrupted system call - we'll just try again */
- goto retry5;
- /* we ignore other error conditions */
- }
-
- /* All done */
- closesocket(tmpsock);
- SOCK_ERRNO_SET(save_errno);
- return TRUE;
-
-cancel_errReturn:
-
- /*
- * Make sure we don't overflow the error buffer. Leave space for the \n at
- * the end, and for the terminating zero.
- */
- maxlen = errbufsize - strlen(errbuf) - 2;
- if (maxlen >= 0)
- {
- strncat(errbuf, SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)),
- maxlen);
- strcat(errbuf, "\n");
- }
- if (tmpsock != PGINVALID_SOCKET)
- closesocket(tmpsock);
- SOCK_ERRNO_SET(save_errno);
- return FALSE;
-}
-
-/*
- * PQcancel: request query cancel
- *
- * Returns TRUE if able to send the cancel request, FALSE if not.
- *
- * On failure, an error message is stored in *errbuf, which must be of size
- * errbufsize (recommended size is 256 bytes). *errbuf is not changed on
- * success return.
- */
-int
-PQcancel(PGcancel *cancel, char *errbuf, int errbufsize)
-{
- if (!cancel)
- {
- strlcpy(errbuf, "PQcancel() -- no cancel object supplied", errbufsize);
- return FALSE;
- }
-
- return internal_cancel(&cancel->raddr, cancel->be_pid, cancel->be_key,
- errbuf, errbufsize);
-}
-
-/*
- * PQrequestCancel: old, not thread-safe function for requesting query cancel
- *
- * Returns TRUE if able to send the cancel request, FALSE if not.
- *
- * On failure, the error message is saved in conn->errorMessage; this means
- * that this can't be used when there might be other active operations on
- * the connection object.
- *
- * NOTE: error messages will be cut off at the current size of the
- * error message buffer, since we dare not try to expand conn->errorMessage!
- */
-int
-PQrequestCancel(PGconn *conn)
-{
- int r;
-
- /* Check we have an open connection */
- if (!conn)
- return FALSE;
-
- if (conn->sock == PGINVALID_SOCKET)
- {
- strlcpy(conn->errorMessage.data,
- "PQrequestCancel() -- connection is not open\n",
- conn->errorMessage.maxlen);
- conn->errorMessage.len = strlen(conn->errorMessage.data);
-
- return FALSE;
- }
-
- r = internal_cancel(&conn->raddr, conn->be_pid, conn->be_key,
- conn->errorMessage.data, conn->errorMessage.maxlen);
-
- if (!r)
- conn->errorMessage.len = strlen(conn->errorMessage.data);
-
- return r;
-}
-
-
-/*
- * pqPacketSend() -- convenience routine to send a message to server.
- *
- * pack_type: the single-byte message type code. (Pass zero for startup
- * packets, which have no message type code.)
- *
- * buf, buf_len: contents of message. The given length includes only what
- * is in buf; the message type and message length fields are added here.
- *
- * RETURNS: STATUS_ERROR if the write fails, STATUS_OK otherwise.
- * SIDE_EFFECTS: may block.
- *
- * Note: all messages sent with this routine have a length word, whether
- * it's protocol 2.0 or 3.0.
- */
-int
-pqPacketSend(PGconn *conn, char pack_type,
- const void *buf, size_t buf_len)
-{
- /* Start the message. */
- if (pqPutMsgStart(pack_type, true, conn))
- return STATUS_ERROR;
-
- /* Send the message body. */
- if (pqPutnchar(buf, buf_len, conn))
- return STATUS_ERROR;
-
- /* Finish the message. */
- if (pqPutMsgEnd(conn))
- return STATUS_ERROR;
-
- /* Flush to ensure backend gets it. */
- if (pqFlush(conn))
- return STATUS_ERROR;
-
- return STATUS_OK;
-}
-
-#ifdef USE_LDAP
-
-#define LDAP_URL "ldap://"
-#define LDAP_DEF_PORT 389
-#define PGLDAP_TIMEOUT 2
-
-#define ld_is_sp_tab(x) ((x) == ' ' || (x) == '\t')
-#define ld_is_nl_cr(x) ((x) == '\r' || (x) == '\n')
-
-
-/*
- * ldapServiceLookup
- *
- * Search the LDAP URL passed as first argument, treat the result as a
- * string of connection options that are parsed and added to the array of
- * options passed as second argument.
- *
- * LDAP URLs must conform to RFC 1959 without escape sequences.
- * ldap://host:port/dn?attributes?scope?filter?extensions
- *
- * Returns
- * 0 if the lookup was successful,
- * 1 if the connection to the LDAP server could be established but
- * the search was unsuccessful,
- * 2 if a connection could not be established, and
- * 3 if a fatal error occurred.
- *
- * An error message is returned in the third argument for return codes 1 and 3.
- */
-static int
-ldapServiceLookup(const char *purl, PQconninfoOption *options,
- PQExpBuffer errorMessage)
-{
- int port = LDAP_DEF_PORT,
- scope,
- rc,
- size,
- state,
- oldstate,
- i;
-#ifndef WIN32
- int msgid;
-#endif
- bool found_keyword;
- char *url,
- *hostname,
- *portstr,
- *endptr,
- *dn,
- *scopestr,
- *filter,
- *result,
- *p,
- *p1 = NULL,
- *optname = NULL,
- *optval = NULL;
- char *attrs[2] = {NULL, NULL};
- LDAP *ld = NULL;
- LDAPMessage *res,
- *entry;
- struct berval **values;
- LDAP_TIMEVAL time = {PGLDAP_TIMEOUT, 0};
-
- if ((url = strdup(purl)) == NULL)
- {
- printfPQExpBuffer(errorMessage, libpq_gettext("out of memory\n"));
- return 3;
- }
-
- /*
- * Parse URL components, check for correctness. Basically, url has '\0'
- * placed at component boundaries and variables are pointed at each
- * component.
- */
-
- if (pg_strncasecmp(url, LDAP_URL, strlen(LDAP_URL)) != 0)
- {
- printfPQExpBuffer(errorMessage,
- libpq_gettext("invalid LDAP URL \"%s\": scheme must be ldap://\n"), purl);
- free(url);
- return 3;
- }
-
- /* hostname */
- hostname = url + strlen(LDAP_URL);
- if (*hostname == '/') /* no hostname? */
- hostname = DefaultHost; /* the default */
-
- /* dn, "distinguished name" */
- p = strchr(url + strlen(LDAP_URL), '/');
- if (p == NULL || *(p + 1) == '\0' || *(p + 1) == '?')
- {
- printfPQExpBuffer(errorMessage, libpq_gettext(
- "invalid LDAP URL \"%s\": missing distinguished name\n"), purl);
- free(url);
- return 3;
- }
- *p = '\0'; /* terminate hostname */
- dn = p + 1;
-
- /* attribute */
- if ((p = strchr(dn, '?')) == NULL || *(p + 1) == '\0' || *(p + 1) == '?')
- {
- printfPQExpBuffer(errorMessage, libpq_gettext(
- "invalid LDAP URL \"%s\": must have exactly one attribute\n"), purl);
- free(url);
- return 3;
- }
- *p = '\0';
- attrs[0] = p + 1;
-
- /* scope */
- if ((p = strchr(attrs[0], '?')) == NULL || *(p + 1) == '\0' || *(p + 1) == '?')
- {
- printfPQExpBuffer(errorMessage, libpq_gettext("invalid LDAP URL \"%s\": must have search scope (base/one/sub)\n"), purl);
- free(url);
- return 3;
- }
- *p = '\0';
- scopestr = p + 1;
-
- /* filter */
- if ((p = strchr(scopestr, '?')) == NULL || *(p + 1) == '\0' || *(p + 1) == '?')
- {
- printfPQExpBuffer(errorMessage,
- libpq_gettext("invalid LDAP URL \"%s\": no filter\n"), purl);
- free(url);
- return 3;
- }
- *p = '\0';
- filter = p + 1;
- if ((p = strchr(filter, '?')) != NULL)
- *p = '\0';
-
- /* port number? */
- if ((p1 = strchr(hostname, ':')) != NULL)
- {
- long lport;
-
- *p1 = '\0';
- portstr = p1 + 1;
- errno = 0;
- lport = strtol(portstr, &endptr, 10);
- if (*portstr == '\0' || *endptr != '\0' || errno || lport < 0 || lport > 65535)
- {
- printfPQExpBuffer(errorMessage, libpq_gettext(
- "invalid LDAP URL \"%s\": invalid port number\n"), purl);
- free(url);
- return 3;
- }
- port = (int) lport;
- }
-
- /* Allow only one attribute */
- if (strchr(attrs[0], ',') != NULL)
- {
- printfPQExpBuffer(errorMessage, libpq_gettext(
- "invalid LDAP URL \"%s\": must have exactly one attribute\n"), purl);
- free(url);
- return 3;
- }
-
- /* set scope */
- if (pg_strcasecmp(scopestr, "base") == 0)
- scope = LDAP_SCOPE_BASE;
- else if (pg_strcasecmp(scopestr, "one") == 0)
- scope = LDAP_SCOPE_ONELEVEL;
- else if (pg_strcasecmp(scopestr, "sub") == 0)
- scope = LDAP_SCOPE_SUBTREE;
- else
- {
- printfPQExpBuffer(errorMessage, libpq_gettext("invalid LDAP URL \"%s\": must have search scope (base/one/sub)\n"), purl);
- free(url);
- return 3;
- }
-
- /* initialize LDAP structure */
- if ((ld = ldap_init(hostname, port)) == NULL)
- {
- printfPQExpBuffer(errorMessage,
- libpq_gettext("could not create LDAP structure\n"));
- free(url);
- return 3;
- }
-
- /*
- * Perform an explicit anonymous bind.
- *
- * LDAP does not require that an anonymous bind is performed explicitly,
- * but we want to distinguish between the case where LDAP bind does not
- * succeed within PGLDAP_TIMEOUT seconds (return 2 to continue parsing the
- * service control file) and the case where querying the LDAP server fails
- * (return 1 to end parsing).
- *
- * Unfortunately there is no way of setting a timeout that works for both
- * Windows and OpenLDAP.
- */
-#ifdef WIN32
- /* the nonstandard ldap_connect function performs an anonymous bind */
- if (ldap_connect(ld, &time) != LDAP_SUCCESS)
- {
- /* error or timeout in ldap_connect */
- free(url);
- ldap_unbind(ld);
- return 2;
- }
-#else /* !WIN32 */
- /* in OpenLDAP, use the LDAP_OPT_NETWORK_TIMEOUT option */
- if (ldap_set_option(ld, LDAP_OPT_NETWORK_TIMEOUT, &time) != LDAP_SUCCESS)
- {
- free(url);
- ldap_unbind(ld);
- return 3;
- }
-
- /* anonymous bind */
- if ((msgid = ldap_simple_bind(ld, NULL, NULL)) == -1)
- {
- /* error or network timeout */
- free(url);
- ldap_unbind(ld);
- return 2;
- }
-
- /* wait some time for the connection to succeed */
- res = NULL;
- if ((rc = ldap_result(ld, msgid, LDAP_MSG_ALL, &time, &res)) == -1 ||
- res == NULL)
- {
- /* error or timeout */
- if (res != NULL)
- ldap_msgfree(res);
- free(url);
- ldap_unbind(ld);
- return 2;
- }
- ldap_msgfree(res);
-
- /* reset timeout */
- time.tv_sec = -1;
- if (ldap_set_option(ld, LDAP_OPT_NETWORK_TIMEOUT, &time) != LDAP_SUCCESS)
- {
- free(url);
- ldap_unbind(ld);
- return 3;
- }
-#endif /* WIN32 */
-
- /* search */
- res = NULL;
- if ((rc = ldap_search_st(ld, dn, scope, filter, attrs, 0, &time, &res))
- != LDAP_SUCCESS)
- {
- if (res != NULL)
- ldap_msgfree(res);
- printfPQExpBuffer(errorMessage,
- libpq_gettext("lookup on LDAP server failed: %s\n"),
- ldap_err2string(rc));
- ldap_unbind(ld);
- free(url);
- return 1;
- }
-
- /* complain if there was not exactly one result */
- if ((rc = ldap_count_entries(ld, res)) != 1)
- {
- printfPQExpBuffer(errorMessage,
- rc ? libpq_gettext("more than one entry found on LDAP lookup\n")
- : libpq_gettext("no entry found on LDAP lookup\n"));
- ldap_msgfree(res);
- ldap_unbind(ld);
- free(url);
- return 1;
- }
-
- /* get entry */
- if ((entry = ldap_first_entry(ld, res)) == NULL)
- {
- /* should never happen */
- printfPQExpBuffer(errorMessage,
- libpq_gettext("no entry found on LDAP lookup\n"));
- ldap_msgfree(res);
- ldap_unbind(ld);
- free(url);
- return 1;
- }
-
- /* get values */
- if ((values = ldap_get_values_len(ld, entry, attrs[0])) == NULL)
- {
- printfPQExpBuffer(errorMessage,
- libpq_gettext("attribute has no values on LDAP lookup\n"));
- ldap_msgfree(res);
- ldap_unbind(ld);
- free(url);
- return 1;
- }
-
- ldap_msgfree(res);
- free(url);
-
- if (values[0] == NULL)
- {
- printfPQExpBuffer(errorMessage,
- libpq_gettext("attribute has no values on LDAP lookup\n"));
- ldap_value_free_len(values);
- ldap_unbind(ld);
- return 1;
- }
-
- /* concatenate values into a single string with newline terminators */
- size = 1; /* for the trailing null */
- for (i = 0; values[i] != NULL; i++)
- size += values[i]->bv_len + 1;
- if ((result = malloc(size)) == NULL)
- {
- printfPQExpBuffer(errorMessage,
- libpq_gettext("out of memory\n"));
- ldap_value_free_len(values);
- ldap_unbind(ld);
- return 3;
- }
- p = result;
- for (i = 0; values[i] != NULL; i++)
- {
- memcpy(p, values[i]->bv_val, values[i]->bv_len);
- p += values[i]->bv_len;
- *(p++) = '\n';
- }
- *p = '\0';
-
- ldap_value_free_len(values);
- ldap_unbind(ld);
-
- /* parse result string */
- oldstate = state = 0;
- for (p = result; *p != '\0'; ++p)
- {
- switch (state)
- {
- case 0: /* between entries */
- if (!ld_is_sp_tab(*p) && !ld_is_nl_cr(*p))
- {
- optname = p;
- state = 1;
- }
- break;
- case 1: /* in option name */
- if (ld_is_sp_tab(*p))
- {
- *p = '\0';
- state = 2;
- }
- else if (ld_is_nl_cr(*p))
- {
- printfPQExpBuffer(errorMessage, libpq_gettext(
- "missing \"=\" after \"%s\" in connection info string\n"),
- optname);
- free(result);
- return 3;
- }
- else if (*p == '=')
- {
- *p = '\0';
- state = 3;
- }
- break;
- case 2: /* after option name */
- if (*p == '=')
- {
- state = 3;
- }
- else if (!ld_is_sp_tab(*p))
- {
- printfPQExpBuffer(errorMessage, libpq_gettext(
- "missing \"=\" after \"%s\" in connection info string\n"),
- optname);
- free(result);
- return 3;
- }
- break;
- case 3: /* before option value */
- if (*p == '\'')
- {
- optval = p + 1;
- p1 = p + 1;
- state = 5;
- }
- else if (ld_is_nl_cr(*p))
- {
- optval = optname + strlen(optname); /* empty */
- state = 0;
- }
- else if (!ld_is_sp_tab(*p))
- {
- optval = p;
- state = 4;
- }
- break;
- case 4: /* in unquoted option value */
- if (ld_is_sp_tab(*p) || ld_is_nl_cr(*p))
- {
- *p = '\0';
- state = 0;
- }
- break;
- case 5: /* in quoted option value */
- if (*p == '\'')
- {
- *p1 = '\0';
- state = 0;
- }
- else if (*p == '\\')
- state = 6;
- else
- *(p1++) = *p;
- break;
- case 6: /* in quoted option value after escape */
- *(p1++) = *p;
- state = 5;
- break;
- }
-
- if (state == 0 && oldstate != 0)
- {
- found_keyword = false;
- for (i = 0; options[i].keyword; i++)
- {
- if (strcmp(options[i].keyword, optname) == 0)
- {
- if (options[i].val == NULL)
- {
- options[i].val = strdup(optval);
- if (!options[i].val)
- {
- printfPQExpBuffer(errorMessage,
- libpq_gettext("out of memory\n"));
- free(result);
- return 3;
- }
- }
- found_keyword = true;
- break;
- }
- }
- if (!found_keyword)
- {
- printfPQExpBuffer(errorMessage,
- libpq_gettext("invalid connection option \"%s\"\n"),
- optname);
- free(result);
- return 1;
- }
- optname = NULL;
- optval = NULL;
- }
- oldstate = state;
- }
-
- free(result);
-
- if (state == 5 || state == 6)
- {
- printfPQExpBuffer(errorMessage, libpq_gettext(
- "unterminated quoted string in connection info string\n"));
- return 3;
- }
-
- return 0;
-}
-
-#endif /* USE_LDAP */
-
-#define MAXBUFSIZE 256
-
-static int
-parseServiceInfo(PQconninfoOption *options, PQExpBuffer errorMessage)
-{
- const char *service = conninfo_getval(options, "service");
- char serviceFile[MAXPGPATH];
- char *env;
- bool group_found = false;
- int status;
- struct stat stat_buf;
-
- /*
- * We have to special-case the environment variable PGSERVICE here, since
- * this is and should be called before inserting environment defaults for
- * other connection options.
- */
- if (service == NULL)
- service = getenv("PGSERVICE");
-
- if (service == NULL)
- return 0;
-
- if ((env = getenv("PGSERVICEFILE")) != NULL)
- strlcpy(serviceFile, env, sizeof(serviceFile));
- else
- {
- char homedir[MAXPGPATH];
-
- if (!pqGetHomeDirectory(homedir, sizeof(homedir)))
- {
- printfPQExpBuffer(errorMessage, libpq_gettext("could not get home directory to locate service definition file"));
- return 1;
- }
- snprintf(serviceFile, MAXPGPATH, "%s/%s", homedir, ".pg_service.conf");
- errno = 0;
- if (stat(serviceFile, &stat_buf) != 0 && errno == ENOENT)
- goto next_file;
- }
-
- status = parseServiceFile(serviceFile, service, options, errorMessage, &group_found);
- if (group_found || status != 0)
- return status;
-
-next_file:
-
- /*
- * This could be used by any application so we can't use the binary
- * location to find our config files.
- */
- snprintf(serviceFile, MAXPGPATH, "%s/pg_service.conf",
- getenv("PGSYSCONFDIR") ? getenv("PGSYSCONFDIR") : SYSCONFDIR);
- errno = 0;
- if (stat(serviceFile, &stat_buf) != 0 && errno == ENOENT)
- goto last_file;
-
- status = parseServiceFile(serviceFile, service, options, errorMessage, &group_found);
- if (status != 0)
- return status;
-
-last_file:
- if (!group_found)
- {
- printfPQExpBuffer(errorMessage,
- libpq_gettext("definition of service \"%s\" not found\n"), service);
- return 3;
- }
-
- return 0;
-}
-
-static int
-parseServiceFile(const char *serviceFile,
- const char *service,
- PQconninfoOption *options,
- PQExpBuffer errorMessage,
- bool *group_found)
-{
- int linenr = 0,
- i;
- FILE *f;
- char buf[MAXBUFSIZE],
- *line;
-
- f = fopen(serviceFile, "r");
- if (f == NULL)
- {
- printfPQExpBuffer(errorMessage, libpq_gettext("service file \"%s\" not found\n"),
- serviceFile);
- return 1;
- }
-
- while ((line = fgets(buf, sizeof(buf), f)) != NULL)
- {
- linenr++;
-
- if (strlen(line) >= sizeof(buf) - 1)
- {
- fclose(f);
- printfPQExpBuffer(errorMessage,
- libpq_gettext("line %d too long in service file \"%s\"\n"),
- linenr,
- serviceFile);
- return 2;
- }
-
- /* ignore EOL at end of line */
- if (strlen(line) && line[strlen(line) - 1] == '\n')
- line[strlen(line) - 1] = 0;
-
- /* ignore leading blanks */
- while (*line && isspace((unsigned char) line[0]))
- line++;
-
- /* ignore comments and empty lines */
- if (strlen(line) == 0 || line[0] == '#')
- continue;
-
- /* Check for right groupname */
- if (line[0] == '[')
- {
- if (*group_found)
- {
- /* group info already read */
- fclose(f);
- return 0;
- }
-
- if (strncmp(line + 1, service, strlen(service)) == 0 &&
- line[strlen(service) + 1] == ']')
- *group_found = true;
- else
- *group_found = false;
- }
- else
- {
- if (*group_found)
- {
- /*
- * Finally, we are in the right group and can parse the line
- */
- char *key,
- *val;
- bool found_keyword;
-
-#ifdef USE_LDAP
- if (strncmp(line, "ldap", 4) == 0)
- {
- int rc = ldapServiceLookup(line, options, errorMessage);
-
- /* if rc = 2, go on reading for fallback */
- switch (rc)
- {
- case 0:
- fclose(f);
- return 0;
- case 1:
- case 3:
- fclose(f);
- return 3;
- case 2:
- continue;
- }
- }
-#endif
-
- key = line;
- val = strchr(line, '=');
- if (val == NULL)
- {
- printfPQExpBuffer(errorMessage,
- libpq_gettext("syntax error in service file \"%s\", line %d\n"),
- serviceFile,
- linenr);
- fclose(f);
- return 3;
- }
- *val++ = '\0';
-
- if (strcmp(key, "service") == 0)
- {
- printfPQExpBuffer(errorMessage,
- libpq_gettext("nested service specifications not supported in service file \"%s\", line %d\n"),
- serviceFile,
- linenr);
- fclose(f);
- return 3;
- }
-
- /*
- * Set the parameter --- but don't override any previous
- * explicit setting.
- */
- found_keyword = false;
- for (i = 0; options[i].keyword; i++)
- {
- if (strcmp(options[i].keyword, key) == 0)
- {
- if (options[i].val == NULL)
- options[i].val = strdup(val);
- if (!options[i].val)
- {
- printfPQExpBuffer(errorMessage,
- libpq_gettext("out of memory\n"));
- fclose(f);
- return 3;
- }
- found_keyword = true;
- break;
- }
- }
-
- if (!found_keyword)
- {
- printfPQExpBuffer(errorMessage,
- libpq_gettext("syntax error in service file \"%s\", line %d\n"),
- serviceFile,
- linenr);
- fclose(f);
- return 3;
- }
- }
- }
- }
-
- fclose(f);
-
- return 0;
-}
-
-
-/*
- * PQconninfoParse
- *
- * Parse a string like PQconnectdb() would do and return the
- * resulting connection options array. NULL is returned on failure.
- * The result contains only options specified directly in the string,
- * not any possible default values.
- *
- * If errmsg isn't NULL, *errmsg is set to NULL on success, or a malloc'd
- * string on failure (use PQfreemem to free it). In out-of-memory conditions
- * both *errmsg and the result could be NULL.
- *
- * NOTE: the returned array is dynamically allocated and should
- * be freed when no longer needed via PQconninfoFree().
- */
-PQconninfoOption *
-PQconninfoParse(const char *conninfo, char **errmsg)
-{
- PQExpBufferData errorBuf;
- PQconninfoOption *connOptions;
-
- if (errmsg)
- *errmsg = NULL; /* default */
- initPQExpBuffer(&errorBuf);
- if (PQExpBufferDataBroken(errorBuf))
- return NULL; /* out of memory already :-( */
- connOptions = parse_connection_string(conninfo, &errorBuf, false);
- if (connOptions == NULL && errmsg)
- *errmsg = errorBuf.data;
- else
- termPQExpBuffer(&errorBuf);
- return connOptions;
-}
-
-/*
- * Build a working copy of the constant PQconninfoOptions array.
- */
-static PQconninfoOption *
-conninfo_init(PQExpBuffer errorMessage)
-{
- PQconninfoOption *options;
- PQconninfoOption *opt_dest;
- const internalPQconninfoOption *cur_opt;
-
- /*
- * Get enough memory for all options in PQconninfoOptions, even if some
- * end up being filtered out.
- */
- options = (PQconninfoOption *) malloc(sizeof(PQconninfoOption) * sizeof(PQconninfoOptions) / sizeof(PQconninfoOptions[0]));
- if (options == NULL)
- {
- printfPQExpBuffer(errorMessage,
- libpq_gettext("out of memory\n"));
- return NULL;
- }
- opt_dest = options;
-
- for (cur_opt = PQconninfoOptions; cur_opt->keyword; cur_opt++)
- {
- /* Only copy the public part of the struct, not the full internal */
- memcpy(opt_dest, cur_opt, sizeof(PQconninfoOption));
- opt_dest++;
- }
- MemSet(opt_dest, 0, sizeof(PQconninfoOption));
-
- return options;
-}
-
-/*
- * Connection string parser
- *
- * Returns a malloc'd PQconninfoOption array, if parsing is successful.
- * Otherwise, NULL is returned and an error message is left in errorMessage.
- *
- * If use_defaults is TRUE, default values are filled in (from a service file,
- * environment variables, etc).
- */
-static PQconninfoOption *
-parse_connection_string(const char *connstr, PQExpBuffer errorMessage,
- bool use_defaults)
-{
- /* Parse as URI if connection string matches URI prefix */
- if (uri_prefix_length(connstr) != 0)
- return conninfo_uri_parse(connstr, errorMessage, use_defaults);
-
- /* Parse as default otherwise */
- return conninfo_parse(connstr, errorMessage, use_defaults);
-}
-
-/*
- * Checks if connection string starts with either of the valid URI prefix
- * designators.
- *
- * Returns the URI prefix length, 0 if the string doesn't contain a URI prefix.
- *
- * XXX this is duplicated in psql/common.c.
- */
-static int
-uri_prefix_length(const char *connstr)
-{
- if (strncmp(connstr, uri_designator,
- sizeof(uri_designator) - 1) == 0)
- return sizeof(uri_designator) - 1;
-
- if (strncmp(connstr, short_uri_designator,
- sizeof(short_uri_designator) - 1) == 0)
- return sizeof(short_uri_designator) - 1;
-
- return 0;
-}
-
-/*
- * Recognized connection string either starts with a valid URI prefix or
- * contains a "=" in it.
- *
- * Must be consistent with parse_connection_string: anything for which this
- * returns true should at least look like it's parseable by that routine.
- *
- * XXX this is duplicated in psql/common.c
- */
-static bool
-recognized_connection_string(const char *connstr)
-{
- return uri_prefix_length(connstr) != 0 || strchr(connstr, '=') != NULL;
-}
-
-/*
- * Subroutine for parse_connection_string
- *
- * Deal with a string containing key=value pairs.
- */
-static PQconninfoOption *
-conninfo_parse(const char *conninfo, PQExpBuffer errorMessage,
- bool use_defaults)
-{
- char *pname;
- char *pval;
- char *buf;
- char *cp;
- char *cp2;
- PQconninfoOption *options;
-
- /* Make a working copy of PQconninfoOptions */
- options = conninfo_init(errorMessage);
- if (options == NULL)
- return NULL;
-
- /* Need a modifiable copy of the input string */
- if ((buf = strdup(conninfo)) == NULL)
- {
- printfPQExpBuffer(errorMessage,
- libpq_gettext("out of memory\n"));
- PQconninfoFree(options);
- return NULL;
- }
- cp = buf;
-
- while (*cp)
- {
- /* Skip blanks before the parameter name */
- if (isspace((unsigned char) *cp))
- {
- cp++;
- continue;
- }
-
- /* Get the parameter name */
- pname = cp;
- while (*cp)
- {
- if (*cp == '=')
- break;
- if (isspace((unsigned char) *cp))
- {
- *cp++ = '\0';
- while (*cp)
- {
- if (!isspace((unsigned char) *cp))
- break;
- cp++;
- }
- break;
- }
- cp++;
- }
-
- /* Check that there is a following '=' */
- if (*cp != '=')
- {
- printfPQExpBuffer(errorMessage,
- libpq_gettext("missing \"=\" after \"%s\" in connection info string\n"),
- pname);
- PQconninfoFree(options);
- free(buf);
- return NULL;
- }
- *cp++ = '\0';
-
- /* Skip blanks after the '=' */
- while (*cp)
- {
- if (!isspace((unsigned char) *cp))
- break;
- cp++;
- }
-
- /* Get the parameter value */
- pval = cp;
-
- if (*cp != '\'')
- {
- cp2 = pval;
- while (*cp)
- {
- if (isspace((unsigned char) *cp))
- {
- *cp++ = '\0';
- break;
- }
- if (*cp == '\\')
- {
- cp++;
- if (*cp != '\0')
- *cp2++ = *cp++;
- }
- else
- *cp2++ = *cp++;
- }
- *cp2 = '\0';
- }
- else
- {
- cp2 = pval;
- cp++;
- for (;;)
- {
- if (*cp == '\0')
- {
- printfPQExpBuffer(errorMessage,
- libpq_gettext("unterminated quoted string in connection info string\n"));
- PQconninfoFree(options);
- free(buf);
- return NULL;
- }
- if (*cp == '\\')
- {
- cp++;
- if (*cp != '\0')
- *cp2++ = *cp++;
- continue;
- }
- if (*cp == '\'')
- {
- *cp2 = '\0';
- cp++;
- break;
- }
- *cp2++ = *cp++;
- }
- }
-
- /*
- * Now that we have the name and the value, store the record.
- */
- if (!conninfo_storeval(options, pname, pval, errorMessage, false, false))
- {
- PQconninfoFree(options);
- free(buf);
- return NULL;
- }
- }
-
- /* Done with the modifiable input string */
- free(buf);
-
- /*
- * Add in defaults if the caller wants that.
- */
- if (use_defaults)
- {
- if (!conninfo_add_defaults(options, errorMessage))
- {
- PQconninfoFree(options);
- return NULL;
- }
- }
-
- return options;
-}
-
-/*
- * Conninfo array parser routine
- *
- * If successful, a malloc'd PQconninfoOption array is returned.
- * If not successful, NULL is returned and an error message is
- * left in errorMessage.
- * Defaults are supplied (from a service file, environment variables, etc)
- * for unspecified options, but only if use_defaults is TRUE.
- *
- * If expand_dbname is non-zero, and the value passed for the first occurrence
- * of "dbname" keyword is a connection string (as indicated by
- * recognized_connection_string) then parse and process it, overriding any
- * previously processed conflicting keywords. Subsequent keywords will take
- * precedence, however. In-tree programs generally specify expand_dbname=true,
- * so command-line arguments naming a database can use a connection string.
- * Some code acquires arbitrary database names from known-literal sources like
- * PQdb(), PQconninfoParse() and pg_database.datname. When connecting to such
- * a database, in-tree code first wraps the name in a connection string.
- */
-static PQconninfoOption *
-conninfo_array_parse(const char *const * keywords, const char *const * values,
- PQExpBuffer errorMessage, bool use_defaults,
- int expand_dbname)
-{
- PQconninfoOption *options;
- PQconninfoOption *dbname_options = NULL;
- PQconninfoOption *option;
- int i = 0;
-
- /*
- * If expand_dbname is non-zero, check keyword "dbname" to see if val is
- * actually a recognized connection string.
- */
- while (expand_dbname && keywords[i])
- {
- const char *pname = keywords[i];
- const char *pvalue = values[i];
-
- /* first find "dbname" if any */
- if (strcmp(pname, "dbname") == 0 && pvalue)
- {
- /*
- * If value is a connection string, parse it, but do not use
- * defaults here -- those get picked up later. We only want to
- * override for those parameters actually passed.
- */
- if (recognized_connection_string(pvalue))
- {
- dbname_options = parse_connection_string(pvalue, errorMessage, false);
- if (dbname_options == NULL)
- return NULL;
- }
- break;
- }
- ++i;
- }
-
- /* Make a working copy of PQconninfoOptions */
- options = conninfo_init(errorMessage);
- if (options == NULL)
- {
- PQconninfoFree(dbname_options);
- return NULL;
- }
-
- /* Parse the keywords/values arrays */
- i = 0;
- while (keywords[i])
- {
- const char *pname = keywords[i];
- const char *pvalue = values[i];
-
- if (pvalue != NULL && pvalue[0] != '\0')
- {
- /* Search for the param record */
- for (option = options; option->keyword != NULL; option++)
- {
- if (strcmp(option->keyword, pname) == 0)
- break;
- }
-
- /* Check for invalid connection option */
- if (option->keyword == NULL)
- {
- printfPQExpBuffer(errorMessage,
- libpq_gettext("invalid connection option \"%s\"\n"),
- pname);
- PQconninfoFree(options);
- PQconninfoFree(dbname_options);
- return NULL;
- }
-
- /*
- * If we are on the first dbname parameter, and we have a parsed
- * connection string, copy those parameters across, overriding any
- * existing previous settings.
- */
- if (strcmp(pname, "dbname") == 0 && dbname_options)
- {
- PQconninfoOption *str_option;
-
- for (str_option = dbname_options; str_option->keyword != NULL; str_option++)
- {
- if (str_option->val != NULL)
- {
- int k;
-
- for (k = 0; options[k].keyword; k++)
- {
- if (strcmp(options[k].keyword, str_option->keyword) == 0)
- {
- if (options[k].val)
- free(options[k].val);
- options[k].val = strdup(str_option->val);
- if (!options[k].val)
- {
- printfPQExpBuffer(errorMessage,
- libpq_gettext("out of memory\n"));
- PQconninfoFree(options);
- PQconninfoFree(dbname_options);
- return NULL;
- }
- break;
- }
- }
- }
- }
-
- /*
- * Forget the parsed connection string, so that any subsequent
- * dbname parameters will not be expanded.
- */
- PQconninfoFree(dbname_options);
- dbname_options = NULL;
- }
- else
- {
- /*
- * Store the value, overriding previous settings
- */
- if (option->val)
- free(option->val);
- option->val = strdup(pvalue);
- if (!option->val)
- {
- printfPQExpBuffer(errorMessage,
- libpq_gettext("out of memory\n"));
- PQconninfoFree(options);
- PQconninfoFree(dbname_options);
- return NULL;
- }
- }
- }
- ++i;
- }
- PQconninfoFree(dbname_options);
-
- /*
- * Add in defaults if the caller wants that.
- */
- if (use_defaults)
- {
- if (!conninfo_add_defaults(options, errorMessage))
- {
- PQconninfoFree(options);
- return NULL;
- }
- }
-
- return options;
-}
-
-/*
- * Add the default values for any unspecified options to the connection
- * options array.
- *
- * Defaults are obtained from a service file, environment variables, etc.
- *
- * Returns TRUE if successful, otherwise FALSE; errorMessage, if supplied,
- * is filled in upon failure. Note that failure to locate a default value
- * is not an error condition here --- we just leave the option's value as
- * NULL.
- */
-static bool
-conninfo_add_defaults(PQconninfoOption *options, PQExpBuffer errorMessage)
-{
- PQconninfoOption *option;
- char *tmp;
-
- /*
- * If there's a service spec, use it to obtain any not-explicitly-given
- * parameters. Ignore error if no error message buffer is passed because
- * there is no way to pass back the failure message.
- */
- if (parseServiceInfo(options, errorMessage) != 0 && errorMessage)
- return false;
-
- /*
- * Get the fallback resources for parameters not specified in the conninfo
- * string nor the service.
- */
- for (option = options; option->keyword != NULL; option++)
- {
- if (option->val != NULL)
- continue; /* Value was in conninfo or service */
-
- /*
- * Try to get the environment variable fallback
- */
- if (option->envvar != NULL)
- {
- if ((tmp = getenv(option->envvar)) != NULL)
- {
- option->val = strdup(tmp);
- if (!option->val)
- {
- if (errorMessage)
- printfPQExpBuffer(errorMessage,
- libpq_gettext("out of memory\n"));
- return false;
- }
- continue;
- }
- }
-
- /*
- * Interpret the deprecated PGREQUIRESSL environment variable. Per
- * tradition, translate values starting with "1" to sslmode=require,
- * and ignore other values. Given both PGREQUIRESSL=1 and PGSSLMODE,
- * PGSSLMODE takes precedence; the opposite was true before v9.3.
- */
- if (strcmp(option->keyword, "sslmode") == 0)
- {
- const char *requiresslenv = getenv("PGREQUIRESSL");
-
- if (requiresslenv != NULL && requiresslenv[0] == '1')
- {
- option->val = strdup("require");
- if (!option->val)
- {
- if (errorMessage)
- printfPQExpBuffer(errorMessage,
- libpq_gettext("out of memory\n"));
- return false;
- }
- continue;
- }
- }
-
- /*
- * No environment variable specified or the variable isn't set - try
- * compiled-in default
- */
- if (option->compiled != NULL)
- {
- option->val = strdup(option->compiled);
- if (!option->val)
- {
- if (errorMessage)
- printfPQExpBuffer(errorMessage,
- libpq_gettext("out of memory\n"));
- return false;
- }
- continue;
- }
-
- /*
- * Special handling for "user" option. Note that if pg_fe_getauthname
- * fails, we just leave the value as NULL; there's no need for this to
- * be an error condition if the caller provides a user name. The only
- * reason we do this now at all is so that callers of PQconndefaults
- * will see a correct default (barring error, of course).
- */
- if (strcmp(option->keyword, "user") == 0)
- {
- option->val = pg_fe_getauthname(NULL);
- continue;
- }
- }
-
- return true;
-}
-
-/*
- * Subroutine for parse_connection_string
- *
- * Deal with a URI connection string.
- */
-static PQconninfoOption *
-conninfo_uri_parse(const char *uri, PQExpBuffer errorMessage,
- bool use_defaults)
-{
- PQconninfoOption *options;
-
- /* Make a working copy of PQconninfoOptions */
- options = conninfo_init(errorMessage);
- if (options == NULL)
- return NULL;
-
- if (!conninfo_uri_parse_options(options, uri, errorMessage))
- {
- PQconninfoFree(options);
- return NULL;
- }
-
- /*
- * Add in defaults if the caller wants that.
- */
- if (use_defaults)
- {
- if (!conninfo_add_defaults(options, errorMessage))
- {
- PQconninfoFree(options);
- return NULL;
- }
- }
-
- return options;
-}
-
-/*
- * conninfo_uri_parse_options
- * Actual URI parser.
- *
- * If successful, returns true while the options array is filled with parsed
- * options from the URI.
- * If not successful, returns false and fills errorMessage accordingly.
- *
- * Parses the connection URI string in 'uri' according to the URI syntax (RFC
- * 3986):
- *
- * postgresql://[user[:password]@][netloc][:port][/dbname][?param1=value1&...]
- *
- * where "netloc" is a hostname, an IPv4 address, or an IPv6 address surrounded
- * by literal square brackets.
- *
- * Any of the URI parts might use percent-encoding (%xy).
- */
-static bool
-conninfo_uri_parse_options(PQconninfoOption *options, const char *uri,
- PQExpBuffer errorMessage)
-{
- int prefix_len;
- char *p;
- char *buf;
- char *start;
- char prevchar = '\0';
- char *user = NULL;
- char *host = NULL;
- bool retval = false;
-
- /* need a modifiable copy of the input URI */
- buf = strdup(uri);
- if (buf == NULL)
- {
- printfPQExpBuffer(errorMessage,
- libpq_gettext("out of memory\n"));
- return false;
- }
- start = buf;
-
- /* Skip the URI prefix */
- prefix_len = uri_prefix_length(uri);
- if (prefix_len == 0)
- {
- /* Should never happen */
- printfPQExpBuffer(errorMessage,
- libpq_gettext("invalid URI propagated to internal parser routine: \"%s\"\n"),
- uri);
- goto cleanup;
- }
- start += prefix_len;
- p = start;
-
- /* Look ahead for possible user credentials designator */
- while (*p && *p != '@' && *p != '/')
- ++p;
- if (*p == '@')
- {
- /*
- * Found username/password designator, so URI should be of the form
- * "scheme://user[:password]@[netloc]".
- */
- user = start;
-
- p = user;
- while (*p != ':' && *p != '@')
- ++p;
-
- /* Save last char and cut off at end of user name */
- prevchar = *p;
- *p = '\0';
-
- if (*user &&
- !conninfo_storeval(options, "user", user,
- errorMessage, false, true))
- goto cleanup;
-
- if (prevchar == ':')
- {
- const char *password = p + 1;
-
- while (*p != '@')
- ++p;
- *p = '\0';
-
- if (*password &&
- !conninfo_storeval(options, "password", password,
- errorMessage, false, true))
- goto cleanup;
- }
-
- /* Advance past end of parsed user name or password token */
- ++p;
- }
- else
- {
- /*
- * No username/password designator found. Reset to start of URI.
- */
- p = start;
- }
-
- /*
- * "p" has been incremented past optional URI credential information at
- * this point and now points at the "netloc" part of the URI.
- *
- * Look for IPv6 address.
- */
- if (*p == '[')
- {
- host = ++p;
- while (*p && *p != ']')
- ++p;
- if (!*p)
- {
- printfPQExpBuffer(errorMessage,
- libpq_gettext("end of string reached when looking for matching \"]\" in IPv6 host address in URI: \"%s\"\n"),
- uri);
- goto cleanup;
- }
- if (p == host)
- {
- printfPQExpBuffer(errorMessage,
- libpq_gettext("IPv6 host address may not be empty in URI: \"%s\"\n"),
- uri);
- goto cleanup;
- }
-
- /* Cut off the bracket and advance */
- *(p++) = '\0';
-
- /*
- * The address may be followed by a port specifier or a slash or a
- * query.
- */
- if (*p && *p != ':' && *p != '/' && *p != '?')
- {
- printfPQExpBuffer(errorMessage,
- libpq_gettext("unexpected character \"%c\" at position %d in URI (expected \":\" or \"/\"): \"%s\"\n"),
- *p, (int) (p - buf + 1), uri);
- goto cleanup;
- }
- }
- else
- {
- /* not an IPv6 address: DNS-named or IPv4 netloc */
- host = p;
-
- /*
- * Look for port specifier (colon) or end of host specifier (slash),
- * or query (question mark).
- */
- while (*p && *p != ':' && *p != '/' && *p != '?')
- ++p;
- }
-
- /* Save the hostname terminator before we null it */
- prevchar = *p;
- *p = '\0';
-
- if (*host &&
- !conninfo_storeval(options, "host", host,
- errorMessage, false, true))
- goto cleanup;
-
-
- if (prevchar == ':')
- {
- const char *port = ++p; /* advance past host terminator */
-
- while (*p && *p != '/' && *p != '?')
- ++p;
-
- prevchar = *p;
- *p = '\0';
-
- if (*port &&
- !conninfo_storeval(options, "port", port,
- errorMessage, false, true))
- goto cleanup;
- }
-
- if (prevchar && prevchar != '?')
- {
- const char *dbname = ++p; /* advance past host terminator */
-
- /* Look for query parameters */
- while (*p && *p != '?')
- ++p;
-
- prevchar = *p;
- *p = '\0';
-
- /*
- * Avoid setting dbname to an empty string, as it forces the default
- * value (username) and ignores $PGDATABASE, as opposed to not setting
- * it at all.
- */
- if (*dbname &&
- !conninfo_storeval(options, "dbname", dbname,
- errorMessage, false, true))
- goto cleanup;
- }
-
- if (prevchar)
- {
- ++p; /* advance past terminator */
-
- if (!conninfo_uri_parse_params(p, options, errorMessage))
- goto cleanup;
- }
-
- /* everything parsed okay */
- retval = true;
-
-cleanup:
- free(buf);
- return retval;
-}
-
-/*
- * Connection URI parameters parser routine
- *
- * If successful, returns true while connOptions is filled with parsed
- * parameters. Otherwise, returns false and fills errorMessage appropriately.
- *
- * Destructively modifies 'params' buffer.
- */
-static bool
-conninfo_uri_parse_params(char *params,
- PQconninfoOption *connOptions,
- PQExpBuffer errorMessage)
-{
- while (*params)
- {
- char *keyword = params;
- char *value = NULL;
- char *p = params;
- bool malloced = false;
-
- /*
- * Scan the params string for '=' and '&', marking the end of keyword
- * and value respectively.
- */
- for (;;)
- {
- if (*p == '=')
- {
- /* Was there '=' already? */
- if (value != NULL)
- {
- printfPQExpBuffer(errorMessage,
- libpq_gettext("extra key/value separator \"=\" in URI query parameter: \"%s\"\n"),
- keyword);
- return false;
- }
- /* Cut off keyword, advance to value */
- *p++ = '\0';
- value = p;
- }
- else if (*p == '&' || *p == '\0')
- {
- /*
- * If not at the end, cut off value and advance; leave p
- * pointing to start of the next parameter, if any.
- */
- if (*p != '\0')
- *p++ = '\0';
- /* Was there '=' at all? */
- if (value == NULL)
- {
- printfPQExpBuffer(errorMessage,
- libpq_gettext("missing key/value separator \"=\" in URI query parameter: \"%s\"\n"),
- keyword);
- return false;
- }
- /* Got keyword and value, go process them. */
- break;
- }
- else
- ++p; /* Advance over all other bytes. */
- }
-
- keyword = conninfo_uri_decode(keyword, errorMessage);
- if (keyword == NULL)
- {
- /* conninfo_uri_decode already set an error message */
- return false;
- }
- value = conninfo_uri_decode(value, errorMessage);
- if (value == NULL)
- {
- /* conninfo_uri_decode already set an error message */
- free(keyword);
- return false;
- }
- malloced = true;
-
- /*
- * Special keyword handling for improved JDBC compatibility.
- */
- if (strcmp(keyword, "ssl") == 0 &&
- strcmp(value, "true") == 0)
- {
- free(keyword);
- free(value);
- malloced = false;
-
- keyword = "sslmode";
- value = "require";
- }
-
- /*
- * Store the value if the corresponding option exists; ignore
- * otherwise. At this point both keyword and value are not
- * URI-encoded.
- */
- if (!conninfo_storeval(connOptions, keyword, value,
- errorMessage, true, false))
- {
- /* Insert generic message if conninfo_storeval didn't give one. */
- if (errorMessage->len == 0)
- printfPQExpBuffer(errorMessage,
- libpq_gettext("invalid URI query parameter: \"%s\"\n"),
- keyword);
- /* And fail. */
- if (malloced)
- {
- free(keyword);
- free(value);
- }
- return false;
- }
-
- if (malloced)
- {
- free(keyword);
- free(value);
- }
-
- /* Proceed to next key=value pair, if any */
- params = p;
- }
-
- return true;
-}
-
-/*
- * Connection URI decoder routine
- *
- * If successful, returns the malloc'd decoded string.
- * If not successful, returns NULL and fills errorMessage accordingly.
- *
- * The string is decoded by replacing any percent-encoded tokens with
- * corresponding characters, while preserving any non-encoded characters. A
- * percent-encoded token is a character triplet: a percent sign, followed by a
- * pair of hexadecimal digits (0-9A-F), where lower- and upper-case letters are
- * treated identically.
- */
-static char *
-conninfo_uri_decode(const char *str, PQExpBuffer errorMessage)
-{
- char *buf;
- char *p;
- const char *q = str;
-
- buf = malloc(strlen(str) + 1);
- if (buf == NULL)
- {
- printfPQExpBuffer(errorMessage, libpq_gettext("out of memory\n"));
- return NULL;
- }
- p = buf;
-
- for (;;)
- {
- if (*q != '%')
- {
- /* copy and check for NUL terminator */
- if (!(*(p++) = *(q++)))
- break;
- }
- else
- {
- int hi;
- int lo;
- int c;
-
- ++q; /* skip the percent sign itself */
-
- /*
- * Possible EOL will be caught by the first call to
- * get_hexdigit(), so we never dereference an invalid q pointer.
- */
- if (!(get_hexdigit(*q++, &hi) && get_hexdigit(*q++, &lo)))
- {
- printfPQExpBuffer(errorMessage,
- libpq_gettext("invalid percent-encoded token: \"%s\"\n"),
- str);
- free(buf);
- return NULL;
- }
-
- c = (hi << 4) | lo;
- if (c == 0)
- {
- printfPQExpBuffer(errorMessage,
- libpq_gettext("forbidden value %%00 in percent-encoded value: \"%s\"\n"),
- str);
- free(buf);
- return NULL;
- }
- *(p++) = c;
- }
- }
-
- return buf;
-}
-
-/*
- * Convert hexadecimal digit character to its integer value.
- *
- * If successful, returns true and value is filled with digit's base 16 value.
- * If not successful, returns false.
- *
- * Lower- and upper-case letters in the range A-F are treated identically.
- */
-static bool
-get_hexdigit(char digit, int *value)
-{
- if ('0' <= digit && digit <= '9')
- *value = digit - '0';
- else if ('A' <= digit && digit <= 'F')
- *value = digit - 'A' + 10;
- else if ('a' <= digit && digit <= 'f')
- *value = digit - 'a' + 10;
- else
- return false;
-
- return true;
-}
-
-/*
- * Find an option value corresponding to the keyword in the connOptions array.
- *
- * If successful, returns a pointer to the corresponding option's value.
- * If not successful, returns NULL.
- */
-static const char *
-conninfo_getval(PQconninfoOption *connOptions,
- const char *keyword)
-{
- PQconninfoOption *option;
-
- option = conninfo_find(connOptions, keyword);
-
- return option ? option->val : NULL;
-}
-
-/*
- * Store a (new) value for an option corresponding to the keyword in
- * connOptions array.
- *
- * If uri_decode is true, the value is URI-decoded. The keyword is always
- * assumed to be non URI-encoded.
- *
- * If successful, returns a pointer to the corresponding PQconninfoOption,
- * which value is replaced with a strdup'd copy of the passed value string.
- * The existing value for the option is free'd before replacing, if any.
- *
- * If not successful, returns NULL and fills errorMessage accordingly.
- * However, if the reason of failure is an invalid keyword being passed and
- * ignoreMissing is TRUE, errorMessage will be left untouched.
- */
-static PQconninfoOption *
-conninfo_storeval(PQconninfoOption *connOptions,
- const char *keyword, const char *value,
- PQExpBuffer errorMessage, bool ignoreMissing,
- bool uri_decode)
-{
- PQconninfoOption *option;
- char *value_copy;
-
- /*
- * For backwards compatibility, requiressl=1 gets translated to
- * sslmode=require, and requiressl=0 gets translated to sslmode=prefer
- * (which is the default for sslmode).
- */
- if (strcmp(keyword, "requiressl") == 0)
- {
- keyword = "sslmode";
- if (value[0] == '1')
- value = "require";
- else
- value = "prefer";
- }
-
- option = conninfo_find(connOptions, keyword);
- if (option == NULL)
- {
- if (!ignoreMissing)
- printfPQExpBuffer(errorMessage,
- libpq_gettext("invalid connection option \"%s\"\n"),
- keyword);
- return NULL;
- }
-
- if (uri_decode)
- {
- value_copy = conninfo_uri_decode(value, errorMessage);
- if (value_copy == NULL)
- /* conninfo_uri_decode already set an error message */
- return NULL;
- }
- else
- {
- value_copy = strdup(value);
- if (value_copy == NULL)
- {
- printfPQExpBuffer(errorMessage, libpq_gettext("out of memory\n"));
- return NULL;
- }
- }
-
- if (option->val)
- free(option->val);
- option->val = value_copy;
-
- return option;
-}
-
-/*
- * Find a PQconninfoOption option corresponding to the keyword in the
- * connOptions array.
- *
- * If successful, returns a pointer to the corresponding PQconninfoOption
- * structure.
- * If not successful, returns NULL.
- */
-static PQconninfoOption *
-conninfo_find(PQconninfoOption *connOptions, const char *keyword)
-{
- PQconninfoOption *option;
-
- for (option = connOptions; option->keyword != NULL; option++)
- {
- if (strcmp(option->keyword, keyword) == 0)
- return option;
- }
-
- return NULL;
-}
-
-
-/*
- * Return the connection options used for the connection
- */
-PQconninfoOption *
-PQconninfo(PGconn *conn)
-{
- PQExpBufferData errorBuf;
- PQconninfoOption *connOptions;
-
- if (conn == NULL)
- return NULL;
-
- /* We don't actually report any errors here, but callees want a buffer */
- initPQExpBuffer(&errorBuf);
- if (PQExpBufferDataBroken(errorBuf))
- return NULL; /* out of memory already :-( */
-
- connOptions = conninfo_init(&errorBuf);
-
- if (connOptions != NULL)
- {
- const internalPQconninfoOption *option;
-
- for (option = PQconninfoOptions; option->keyword; option++)
- {
- char **connmember;
-
- if (option->connofs < 0)
- continue;
-
- connmember = (char **) ((char *) conn + option->connofs);
-
- if (*connmember)
- conninfo_storeval(connOptions, option->keyword, *connmember,
- &errorBuf, true, false);
- }
- }
-
- termPQExpBuffer(&errorBuf);
-
- return connOptions;
-}
-
-
-void
-PQconninfoFree(PQconninfoOption *connOptions)
-{
- PQconninfoOption *option;
-
- if (connOptions == NULL)
- return;
-
- for (option = connOptions; option->keyword != NULL; option++)
- {
- if (option->val != NULL)
- free(option->val);
- }
- free(connOptions);
-}
-
-
-/* =========== accessor functions for PGconn ========= */
-char *
-PQdb(const PGconn *conn)
-{
- if (!conn)
- return NULL;
- return conn->dbName;
-}
-
-char *
-PQuser(const PGconn *conn)
-{
- if (!conn)
- return NULL;
- return conn->pguser;
-}
-
-char *
-PQpass(const PGconn *conn)
-{
- if (!conn)
- return NULL;
- return conn->pgpass;
-}
-
-char *
-PQhost(const PGconn *conn)
-{
- if (!conn)
- return NULL;
- if (conn->pghost != NULL && conn->pghost[0] != '\0')
- return conn->pghost;
- else
- {
-#ifdef HAVE_UNIX_SOCKETS
- if (conn->pgunixsocket != NULL && conn->pgunixsocket[0] != '\0')
- return conn->pgunixsocket;
- else
- return DEFAULT_PGSOCKET_DIR;
-#else
- return DefaultHost;
-#endif
- }
-}
-
-char *
-PQport(const PGconn *conn)
-{
- if (!conn)
- return NULL;
- return conn->pgport;
-}
-
-char *
-PQtty(const PGconn *conn)
-{
- if (!conn)
- return NULL;
- return conn->pgtty;
-}
-
-char *
-PQoptions(const PGconn *conn)
-{
- if (!conn)
- return NULL;
- return conn->pgoptions;
-}
-
-ConnStatusType
-PQstatus(const PGconn *conn)
-{
- if (!conn)
- return CONNECTION_BAD;
- return conn->status;
-}
-
-PGTransactionStatusType
-PQtransactionStatus(const PGconn *conn)
-{
- if (!conn || conn->status != CONNECTION_OK)
- return PQTRANS_UNKNOWN;
- if (conn->asyncStatus != PGASYNC_IDLE)
- return PQTRANS_ACTIVE;
- return conn->xactStatus;
-}
-
-const char *
-PQparameterStatus(const PGconn *conn, const char *paramName)
-{
- const pgParameterStatus *pstatus;
-
- if (!conn || !paramName)
- return NULL;
- for (pstatus = conn->pstatus; pstatus != NULL; pstatus = pstatus->next)
- {
- if (strcmp(pstatus->name, paramName) == 0)
- return pstatus->value;
- }
- return NULL;
-}
-
-int
-PQprotocolVersion(const PGconn *conn)
-{
- if (!conn)
- return 0;
- if (conn->status == CONNECTION_BAD)
- return 0;
- return PG_PROTOCOL_MAJOR(conn->pversion);
-}
-
-int
-PQserverVersion(const PGconn *conn)
-{
- if (!conn)
- return 0;
- if (conn->status == CONNECTION_BAD)
- return 0;
- return conn->sversion;
-}
-
-char *
-PQerrorMessage(const PGconn *conn)
-{
- if (!conn)
- return libpq_gettext("connection pointer is NULL\n");
-
- return conn->errorMessage.data;
-}
-
-/*
- * In Windows, socket values are unsigned, and an invalid socket value
- * (INVALID_SOCKET) is ~0, which equals -1 in comparisons (with no compiler
- * warning). Ideally we would return an unsigned value for PQsocket() on
- * Windows, but that would cause the function's return value to differ from
- * Unix, so we just return -1 for invalid sockets.
- * http://msdn.microsoft.com/en-us/library/windows/desktop/cc507522%28v=vs.85%29.aspx
- * http://stackoverflow.com/questions/10817252/why-is-invalid-socket-defined-as-0-in-winsock2-h-c
- */
-int
-PQsocket(const PGconn *conn)
-{
- if (!conn)
- return -1;
- return (conn->sock != PGINVALID_SOCKET) ? conn->sock : -1;
-}
-
-int
-PQbackendPID(const PGconn *conn)
-{
- if (!conn || conn->status != CONNECTION_OK)
- return 0;
- return conn->be_pid;
-}
-
-int
-PQconnectionNeedsPassword(const PGconn *conn)
-{
- if (!conn)
- return false;
- if (conn->password_needed &&
- (conn->pgpass == NULL || conn->pgpass[0] == '\0'))
- return true;
- else
- return false;
-}
-
-int
-PQconnectionUsedPassword(const PGconn *conn)
-{
- if (!conn)
- return false;
- if (conn->password_needed)
- return true;
- else
- return false;
-}
-
-int
-PQclientEncoding(const PGconn *conn)
-{
- if (!conn || conn->status != CONNECTION_OK)
- return -1;
- return conn->client_encoding;
-}
-
-int
-PQsetClientEncoding(PGconn *conn, const char *encoding)
-{
- char qbuf[128];
- static const char query[] = "set client_encoding to '%s'";
- PGresult *res;
- int status;
-
- if (!conn || conn->status != CONNECTION_OK)
- return -1;
-
- if (!encoding)
- return -1;
-
- /* Resolve special "auto" value from the locale */
- if (strcmp(encoding, "auto") == 0)
- encoding = pg_encoding_to_char(pg_get_encoding_from_locale(NULL, true));
-
- /* check query buffer overflow */
- if (sizeof(qbuf) < (sizeof(query) + strlen(encoding)))
- return -1;
-
- /* ok, now send a query */
- sprintf(qbuf, query, encoding);
- res = PQexec(conn, qbuf);
-
- if (res == NULL)
- return -1;
- if (res->resultStatus != PGRES_COMMAND_OK)
- status = -1;
- else
- {
- /*
- * In protocol 2 we have to assume the setting will stick, and adjust
- * our state immediately. In protocol 3 and up we can rely on the
- * backend to report the parameter value, and we'll change state at
- * that time.
- */
- if (PG_PROTOCOL_MAJOR(conn->pversion) < 3)
- pqSaveParameterStatus(conn, "client_encoding", encoding);
- status = 0; /* everything is ok */
- }
- PQclear(res);
- return status;
-}
-
-PGVerbosity
-PQsetErrorVerbosity(PGconn *conn, PGVerbosity verbosity)
-{
- PGVerbosity old;
-
- if (!conn)
- return PQERRORS_DEFAULT;
- old = conn->verbosity;
- conn->verbosity = verbosity;
- return old;
-}
-
-PGContextVisibility
-PQsetErrorContextVisibility(PGconn *conn, PGContextVisibility show_context)
-{
- PGContextVisibility old;
-
- if (!conn)
- return PQSHOW_CONTEXT_ERRORS;
- old = conn->show_context;
- conn->show_context = show_context;
- return old;
-}
-
-void
-PQtrace(PGconn *conn, FILE *debug_port)
-{
- if (conn == NULL)
- return;
- PQuntrace(conn);
- conn->Pfdebug = debug_port;
-}
-
-void
-PQuntrace(PGconn *conn)
-{
- if (conn == NULL)
- return;
- if (conn->Pfdebug)
- {
- fflush(conn->Pfdebug);
- conn->Pfdebug = NULL;
- }
-}
-
-PQnoticeReceiver
-PQsetNoticeReceiver(PGconn *conn, PQnoticeReceiver proc, void *arg)
-{
- PQnoticeReceiver old;
-
- if (conn == NULL)
- return NULL;
-
- old = conn->noticeHooks.noticeRec;
- if (proc)
- {
- conn->noticeHooks.noticeRec = proc;
- conn->noticeHooks.noticeRecArg = arg;
- }
- return old;
-}
-
-PQnoticeProcessor
-PQsetNoticeProcessor(PGconn *conn, PQnoticeProcessor proc, void *arg)
-{
- PQnoticeProcessor old;
-
- if (conn == NULL)
- return NULL;
-
- old = conn->noticeHooks.noticeProc;
- if (proc)
- {
- conn->noticeHooks.noticeProc = proc;
- conn->noticeHooks.noticeProcArg = arg;
- }
- return old;
-}
-
-/*
- * The default notice message receiver just gets the standard notice text
- * and sends it to the notice processor. This two-level setup exists
- * mostly for backwards compatibility; perhaps we should deprecate use of
- * PQsetNoticeProcessor?
- */
-static void
-defaultNoticeReceiver(void *arg, const PGresult *res)
-{
- (void) arg; /* not used */
- if (res->noticeHooks.noticeProc != NULL)
- (*res->noticeHooks.noticeProc) (res->noticeHooks.noticeProcArg,
- PQresultErrorMessage(res));
-}
-
-/*
- * The default notice message processor just prints the
- * message on stderr. Applications can override this if they
- * want the messages to go elsewhere (a window, for example).
- * Note that simply discarding notices is probably a bad idea.
- */
-static void
-defaultNoticeProcessor(void *arg, const char *message)
-{
- (void) arg; /* not used */
- /* Note: we expect the supplied string to end with a newline already. */
- fprintf(stderr, "%s", message);
-}
-
-/*
- * returns a pointer to the next token or NULL if the current
- * token doesn't match
- */
-static char *
-pwdfMatchesString(char *buf, char *token)
-{
- char *tbuf,
- *ttok;
- bool bslash = false;
-
- if (buf == NULL || token == NULL)
- return NULL;
- tbuf = buf;
- ttok = token;
- if (tbuf[0] == '*' && tbuf[1] == ':')
- return tbuf + 2;
- while (*tbuf != 0)
- {
- if (*tbuf == '\\' && !bslash)
- {
- tbuf++;
- bslash = true;
- }
- if (*tbuf == ':' && *ttok == 0 && !bslash)
- return tbuf + 1;
- bslash = false;
- if (*ttok == 0)
- return NULL;
- if (*tbuf == *ttok)
- {
- tbuf++;
- ttok++;
- }
- else
- return NULL;
- }
- return NULL;
-}
-
-/* Get a password from the password file. Return value is malloc'd. */
-static char *
-PasswordFromFile(char *hostname, char *port, char *dbname, char *username)
-{
- FILE *fp;
- char pgpassfile[MAXPGPATH];
- struct stat stat_buf;
-
-#define LINELEN NAMEDATALEN*5
- char buf[LINELEN];
-
- if (dbname == NULL || strlen(dbname) == 0)
- return NULL;
-
- if (username == NULL || strlen(username) == 0)
- return NULL;
-
- /* 'localhost' matches pghost of '' or the default socket directory */
- if (hostname == NULL)
- hostname = DefaultHost;
- else if (is_absolute_path(hostname))
-
- /*
- * We should probably use canonicalize_path(), but then we have to
- * bring path.c into libpq, and it doesn't seem worth it.
- */
- if (strcmp(hostname, DEFAULT_PGSOCKET_DIR) == 0)
- hostname = DefaultHost;
-
- if (port == NULL)
- port = DEF_PGPORT_STR;
-
- if (!getPgPassFilename(pgpassfile))
- return NULL;
-
- /* If password file cannot be opened, ignore it. */
- if (stat(pgpassfile, &stat_buf) != 0)
- return NULL;
-
-#ifndef WIN32
- if (!S_ISREG(stat_buf.st_mode))
- {
- fprintf(stderr,
- libpq_gettext("WARNING: password file \"%s\" is not a plain file\n"),
- pgpassfile);
- return NULL;
- }
-
- /* If password file is insecure, alert the user and ignore it. */
- if (stat_buf.st_mode & (S_IRWXG | S_IRWXO))
- {
- fprintf(stderr,
- libpq_gettext("WARNING: password file \"%s\" has group or world access; permissions should be u=rw (0600) or less\n"),
- pgpassfile);
- return NULL;
- }
-#else
-
- /*
- * On Win32, the directory is protected, so we don't have to check the
- * file.
- */
-#endif
-
- fp = fopen(pgpassfile, "r");
- if (fp == NULL)
- return NULL;
-
- while (!feof(fp) && !ferror(fp))
- {
- char *t = buf,
- *ret,
- *p1,
- *p2;
- int len;
-
- if (fgets(buf, sizeof(buf), fp) == NULL)
- break;
-
- len = strlen(buf);
-
- /* Remove trailing newline */
- if (len > 0 && buf[len - 1] == '\n')
- {
- buf[--len] = '\0';
- /* Handle DOS-style line endings, too, even when not on Windows */
- if (len > 0 && buf[len - 1] == '\r')
- buf[--len] = '\0';
- }
-
- if (len == 0)
- continue;
-
- if ((t = pwdfMatchesString(t, hostname)) == NULL ||
- (t = pwdfMatchesString(t, port)) == NULL ||
- (t = pwdfMatchesString(t, dbname)) == NULL ||
- (t = pwdfMatchesString(t, username)) == NULL)
- continue;
-
- /* Found a match. */
- ret = strdup(t);
- fclose(fp);
-
- if (!ret)
- {
- /* Out of memory. XXX: an error message would be nice. */
- return NULL;
- }
-
- /* De-escape password. */
- for (p1 = p2 = ret; *p1 != ':' && *p1 != '\0'; ++p1, ++p2)
- {
- if (*p1 == '\\' && p1[1] != '\0')
- ++p1;
- *p2 = *p1;
- }
- *p2 = '\0';
-
- return ret;
- }
-
- fclose(fp);
- return NULL;
-
-#undef LINELEN
-}
-
-
-static bool
-getPgPassFilename(char *pgpassfile)
-{
- char *passfile_env;
-
- if ((passfile_env = getenv("PGPASSFILE")) != NULL)
- /* use the literal path from the environment, if set */
- strlcpy(pgpassfile, passfile_env, MAXPGPATH);
- else
- {
- char homedir[MAXPGPATH];
-
- if (!pqGetHomeDirectory(homedir, sizeof(homedir)))
- return false;
- snprintf(pgpassfile, MAXPGPATH, "%s/%s", homedir, PGPASSFILE);
- }
- return true;
-}
-
-/*
- * If the connection failed, we should mention if
- * we got the password from .pgpass in case that
- * password is wrong.
- */
-static void
-dot_pg_pass_warning(PGconn *conn)
-{
- /* If it was 'invalid authorization', add .pgpass mention */
- /* only works with >= 9.0 servers */
- if (conn->dot_pgpass_used && conn->password_needed && conn->result &&
- strcmp(PQresultErrorField(conn->result, PG_DIAG_SQLSTATE),
- ERRCODE_INVALID_PASSWORD) == 0)
- {
- char pgpassfile[MAXPGPATH];
-
- if (!getPgPassFilename(pgpassfile))
- return;
- appendPQExpBuffer(&conn->errorMessage,
- libpq_gettext("password retrieved from file \"%s\"\n"),
- pgpassfile);
- }
-}
-
-
-/*
- * Obtain user's home directory, return in given buffer
- *
- * On Unix, this actually returns the user's home directory. On Windows
- * it returns the PostgreSQL-specific application data folder.
- *
- * This is essentially the same as get_home_path(), but we don't use that
- * because we don't want to pull path.c into libpq (it pollutes application
- * namespace)
- */
-bool
-pqGetHomeDirectory(char *buf, int bufsize)
-{
-#ifndef WIN32
- char pwdbuf[BUFSIZ];
- struct passwd pwdstr;
- struct passwd *pwd = NULL;
-
- (void) pqGetpwuid(geteuid(), &pwdstr, pwdbuf, sizeof(pwdbuf), &pwd);
- if (pwd == NULL)
- return false;
- strlcpy(buf, pwd->pw_dir, bufsize);
- return true;
-#else
- char tmppath[MAX_PATH];
-
- ZeroMemory(tmppath, sizeof(tmppath));
- if (SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, 0, tmppath) != S_OK)
- return false;
- snprintf(buf, bufsize, "%s/postgresql", tmppath);
- return true;
-#endif
-}
-
-/*
- * To keep the API consistent, the locking stubs are always provided, even
- * if they are not required.
- */
-
-static void
-default_threadlock(int acquire)
-{
-#ifdef ENABLE_THREAD_SAFETY
-#ifndef WIN32
- static pthread_mutex_t singlethread_lock = PTHREAD_MUTEX_INITIALIZER;
-#else
- static pthread_mutex_t singlethread_lock = NULL;
- static long mutex_initlock = 0;
-
- if (singlethread_lock == NULL)
- {
- while (InterlockedExchange(&mutex_initlock, 1) == 1)
- /* loop, another thread own the lock */ ;
- if (singlethread_lock == NULL)
- {
- if (pthread_mutex_init(&singlethread_lock, NULL))
- PGTHREAD_ERROR("failed to initialize mutex");
- }
- InterlockedExchange(&mutex_initlock, 0);
- }
-#endif
- if (acquire)
- {
- if (pthread_mutex_lock(&singlethread_lock))
- PGTHREAD_ERROR("failed to lock mutex");
- }
- else
- {
- if (pthread_mutex_unlock(&singlethread_lock))
- PGTHREAD_ERROR("failed to unlock mutex");
- }
-#endif
-}
-
-pgthreadlock_t
-PQregisterThreadLock(pgthreadlock_t newhandler)
-{
- pgthreadlock_t prev = pg_g_threadlock;
-
- if (newhandler)
- pg_g_threadlock = newhandler;
- else
- pg_g_threadlock = default_threadlock;
-
- return prev;
-}
diff --git a/libpq/fe-exec.c b/libpq/fe-exec.c
deleted file mode 100644
index 87ff565..0000000
--- a/libpq/fe-exec.c
+++ /dev/null
@@ -1,3764 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * fe-exec.c
- * functions related to sending a query down to the backend
- *
- * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- *
- * IDENTIFICATION
- * src/interfaces/libpq/fe-exec.c
- *
- *-------------------------------------------------------------------------
- */
-#include "postgres_fe.h"
-
-#include <ctype.h>
-#include <fcntl.h>
-
-#include "libpq-fe.h"
-#include "libpq-int.h"
-
-#include "mb/pg_wchar.h"
-
-#ifdef WIN32
-#include "win32.h"
-#else
-#include <unistd.h>
-#endif
-
-/* keep this in same order as ExecStatusType in libpq-fe.h */
-char *const pgresStatus[] = {
- "PGRES_EMPTY_QUERY",
- "PGRES_COMMAND_OK",
- "PGRES_TUPLES_OK",
- "PGRES_COPY_OUT",
- "PGRES_COPY_IN",
- "PGRES_BAD_RESPONSE",
- "PGRES_NONFATAL_ERROR",
- "PGRES_FATAL_ERROR",
- "PGRES_COPY_BOTH",
- "PGRES_SINGLE_TUPLE"
-};
-
-/*
- * static state needed by PQescapeString and PQescapeBytea; initialize to
- * values that result in backward-compatible behavior
- */
-static int static_client_encoding = PG_SQL_ASCII;
-static bool static_std_strings = false;
-
-
-static PGEvent *dupEvents(PGEvent *events, int count);
-static bool pqAddTuple(PGresult *res, PGresAttValue *tup);
-static bool PQsendQueryStart(PGconn *conn);
-static int PQsendQueryGuts(PGconn *conn,
- const char *command,
- const char *stmtName,
- int nParams,
- const Oid *paramTypes,
- const char *const * paramValues,
- const int *paramLengths,
- const int *paramFormats,
- int resultFormat);
-static void parseInput(PGconn *conn);
-static PGresult *getCopyResult(PGconn *conn, ExecStatusType copytype);
-static bool PQexecStart(PGconn *conn);
-static PGresult *PQexecFinish(PGconn *conn);
-static int PQsendDescribe(PGconn *conn, char desc_type,
- const char *desc_target);
-static int check_field_number(const PGresult *res, int field_num);
-
-
-/* ----------------
- * Space management for PGresult.
- *
- * Formerly, libpq did a separate malloc() for each field of each tuple
- * returned by a query. This was remarkably expensive --- malloc/free
- * consumed a sizable part of the application's runtime. And there is
- * no real need to keep track of the fields separately, since they will
- * all be freed together when the PGresult is released. So now, we grab
- * large blocks of storage from malloc and allocate space for query data
- * within these blocks, using a trivially simple allocator. This reduces
- * the number of malloc/free calls dramatically, and it also avoids
- * fragmentation of the malloc storage arena.
- * The PGresult structure itself is still malloc'd separately. We could
- * combine it with the first allocation block, but that would waste space
- * for the common case that no extra storage is actually needed (that is,
- * the SQL command did not return tuples).
- *
- * We also malloc the top-level array of tuple pointers separately, because
- * we need to be able to enlarge it via realloc, and our trivial space
- * allocator doesn't handle that effectively. (Too bad the FE/BE protocol
- * doesn't tell us up front how many tuples will be returned.)
- * All other subsidiary storage for a PGresult is kept in PGresult_data blocks
- * of size PGRESULT_DATA_BLOCKSIZE. The overhead at the start of each block
- * is just a link to the next one, if any. Free-space management info is
- * kept in the owning PGresult.
- * A query returning a small amount of data will thus require three malloc
- * calls: one for the PGresult, one for the tuples pointer array, and one
- * PGresult_data block.
- *
- * Only the most recently allocated PGresult_data block is a candidate to
- * have more stuff added to it --- any extra space left over in older blocks
- * is wasted. We could be smarter and search the whole chain, but the point
- * here is to be simple and fast. Typical applications do not keep a PGresult
- * around very long anyway, so some wasted space within one is not a problem.
- *
- * Tuning constants for the space allocator are:
- * PGRESULT_DATA_BLOCKSIZE: size of a standard allocation block, in bytes
- * PGRESULT_ALIGN_BOUNDARY: assumed alignment requirement for binary data
- * PGRESULT_SEP_ALLOC_THRESHOLD: objects bigger than this are given separate
- * blocks, instead of being crammed into a regular allocation block.
- * Requirements for correct function are:
- * PGRESULT_ALIGN_BOUNDARY must be a multiple of the alignment requirements
- * of all machine data types. (Currently this is set from configure
- * tests, so it should be OK automatically.)
- * PGRESULT_SEP_ALLOC_THRESHOLD + PGRESULT_BLOCK_OVERHEAD <=
- * PGRESULT_DATA_BLOCKSIZE
- * pqResultAlloc assumes an object smaller than the threshold will fit
- * in a new block.
- * The amount of space wasted at the end of a block could be as much as
- * PGRESULT_SEP_ALLOC_THRESHOLD, so it doesn't pay to make that too large.
- * ----------------
- */
-
-#define PGRESULT_DATA_BLOCKSIZE 2048
-#define PGRESULT_ALIGN_BOUNDARY MAXIMUM_ALIGNOF /* from configure */
-#define PGRESULT_BLOCK_OVERHEAD Max(sizeof(PGresult_data), PGRESULT_ALIGN_BOUNDARY)
-#define PGRESULT_SEP_ALLOC_THRESHOLD (PGRESULT_DATA_BLOCKSIZE / 2)
-
-
-/*
- * PQmakeEmptyPGresult
- * returns a newly allocated, initialized PGresult with given status.
- * If conn is not NULL and status indicates an error, the conn's
- * errorMessage is copied. Also, any PGEvents are copied from the conn.
- */
-PGresult *
-PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status)
-{
- PGresult *result;
-
- result = (PGresult *) malloc(sizeof(PGresult));
- if (!result)
- return NULL;
-
- result->ntups = 0;
- result->numAttributes = 0;
- result->attDescs = NULL;
- result->tuples = NULL;
- result->tupArrSize = 0;
- result->numParameters = 0;
- result->paramDescs = NULL;
- result->resultStatus = status;
- result->cmdStatus[0] = '\0';
- result->binary = 0;
- result->events = NULL;
- result->nEvents = 0;
- result->errMsg = NULL;
- result->errFields = NULL;
- result->errQuery = NULL;
- result->null_field[0] = '\0';
- result->curBlock = NULL;
- result->curOffset = 0;
- result->spaceLeft = 0;
-
- if (conn)
- {
- /* copy connection data we might need for operations on PGresult */
- result->noticeHooks = conn->noticeHooks;
- result->client_encoding = conn->client_encoding;
-
- /* consider copying conn's errorMessage */
- switch (status)
- {
- case PGRES_EMPTY_QUERY:
- case PGRES_COMMAND_OK:
- case PGRES_TUPLES_OK:
- case PGRES_COPY_OUT:
- case PGRES_COPY_IN:
- case PGRES_COPY_BOTH:
- case PGRES_SINGLE_TUPLE:
- /* non-error cases */
- break;
- default:
- pqSetResultError(result, conn->errorMessage.data);
- break;
- }
-
- /* copy events last; result must be valid if we need to PQclear */
- if (conn->nEvents > 0)
- {
- result->events = dupEvents(conn->events, conn->nEvents);
- if (!result->events)
- {
- PQclear(result);
- return NULL;
- }
- result->nEvents = conn->nEvents;
- }
- }
- else
- {
- /* defaults... */
- result->noticeHooks.noticeRec = NULL;
- result->noticeHooks.noticeRecArg = NULL;
- result->noticeHooks.noticeProc = NULL;
- result->noticeHooks.noticeProcArg = NULL;
- result->client_encoding = PG_SQL_ASCII;
- }
-
- return result;
-}
-
-/*
- * PQsetResultAttrs
- *
- * Set the attributes for a given result. This function fails if there are
- * already attributes contained in the provided result. The call is
- * ignored if numAttributes is zero or attDescs is NULL. If the
- * function fails, it returns zero. If the function succeeds, it
- * returns a non-zero value.
- */
-int
-PQsetResultAttrs(PGresult *res, int numAttributes, PGresAttDesc *attDescs)
-{
- int i;
-
- /* If attrs already exist, they cannot be overwritten. */
- if (!res || res->numAttributes > 0)
- return FALSE;
-
- /* ignore no-op request */
- if (numAttributes <= 0 || !attDescs)
- return TRUE;
-
- res->attDescs = (PGresAttDesc *)
- PQresultAlloc(res, numAttributes * sizeof(PGresAttDesc));
-
- if (!res->attDescs)
- return FALSE;
-
- res->numAttributes = numAttributes;
- memcpy(res->attDescs, attDescs, numAttributes * sizeof(PGresAttDesc));
-
- /* deep-copy the attribute names, and determine format */
- res->binary = 1;
- for (i = 0; i < res->numAttributes; i++)
- {
- if (res->attDescs[i].name)
- res->attDescs[i].name = pqResultStrdup(res, res->attDescs[i].name);
- else
- res->attDescs[i].name = res->null_field;
-
- if (!res->attDescs[i].name)
- return FALSE;
-
- if (res->attDescs[i].format == 0)
- res->binary = 0;
- }
-
- return TRUE;
-}
-
-/*
- * PQcopyResult
- *
- * Returns a deep copy of the provided 'src' PGresult, which cannot be NULL.
- * The 'flags' argument controls which portions of the result will or will
- * NOT be copied. The created result is always put into the
- * PGRES_TUPLES_OK status. The source result error message is not copied,
- * although cmdStatus is.
- *
- * To set custom attributes, use PQsetResultAttrs. That function requires
- * that there are no attrs contained in the result, so to use that
- * function you cannot use the PG_COPYRES_ATTRS or PG_COPYRES_TUPLES
- * options with this function.
- *
- * Options:
- * PG_COPYRES_ATTRS - Copy the source result's attributes
- *
- * PG_COPYRES_TUPLES - Copy the source result's tuples. This implies
- * copying the attrs, seeing how the attrs are needed by the tuples.
- *
- * PG_COPYRES_EVENTS - Copy the source result's events.
- *
- * PG_COPYRES_NOTICEHOOKS - Copy the source result's notice hooks.
- */
-PGresult *
-PQcopyResult(const PGresult *src, int flags)
-{
- PGresult *dest;
- int i;
-
- if (!src)
- return NULL;
-
- dest = PQmakeEmptyPGresult(NULL, PGRES_TUPLES_OK);
- if (!dest)
- return NULL;
-
- /* Always copy these over. Is cmdStatus really useful here? */
- dest->client_encoding = src->client_encoding;
- strcpy(dest->cmdStatus, src->cmdStatus);
-
- /* Wants attrs? */
- if (flags & (PG_COPYRES_ATTRS | PG_COPYRES_TUPLES))
- {
- if (!PQsetResultAttrs(dest, src->numAttributes, src->attDescs))
- {
- PQclear(dest);
- return NULL;
- }
- }
-
- /* Wants to copy tuples? */
- if (flags & PG_COPYRES_TUPLES)
- {
- int tup,
- field;
-
- for (tup = 0; tup < src->ntups; tup++)
- {
- for (field = 0; field < src->numAttributes; field++)
- {
- if (!PQsetvalue(dest, tup, field,
- src->tuples[tup][field].value,
- src->tuples[tup][field].len))
- {
- PQclear(dest);
- return NULL;
- }
- }
- }
- }
-
- /* Wants to copy notice hooks? */
- if (flags & PG_COPYRES_NOTICEHOOKS)
- dest->noticeHooks = src->noticeHooks;
-
- /* Wants to copy PGEvents? */
- if ((flags & PG_COPYRES_EVENTS) && src->nEvents > 0)
- {
- dest->events = dupEvents(src->events, src->nEvents);
- if (!dest->events)
- {
- PQclear(dest);
- return NULL;
- }
- dest->nEvents = src->nEvents;
- }
-
- /* Okay, trigger PGEVT_RESULTCOPY event */
- for (i = 0; i < dest->nEvents; i++)
- {
- if (src->events[i].resultInitialized)
- {
- PGEventResultCopy evt;
-
- evt.src = src;
- evt.dest = dest;
- if (!dest->events[i].proc(PGEVT_RESULTCOPY, &evt,
- dest->events[i].passThrough))
- {
- PQclear(dest);
- return NULL;
- }
- dest->events[i].resultInitialized = TRUE;
- }
- }
-
- return dest;
-}
-
-/*
- * Copy an array of PGEvents (with no extra space for more).
- * Does not duplicate the event instance data, sets this to NULL.
- * Also, the resultInitialized flags are all cleared.
- */
-static PGEvent *
-dupEvents(PGEvent *events, int count)
-{
- PGEvent *newEvents;
- int i;
-
- if (!events || count <= 0)
- return NULL;
-
- newEvents = (PGEvent *) malloc(count * sizeof(PGEvent));
- if (!newEvents)
- return NULL;
-
- for (i = 0; i < count; i++)
- {
- newEvents[i].proc = events[i].proc;
- newEvents[i].passThrough = events[i].passThrough;
- newEvents[i].data = NULL;
- newEvents[i].resultInitialized = FALSE;
- newEvents[i].name = strdup(events[i].name);
- if (!newEvents[i].name)
- {
- while (--i >= 0)
- free(newEvents[i].name);
- free(newEvents);
- return NULL;
- }
- }
-
- return newEvents;
-}
-
-
-/*
- * Sets the value for a tuple field. The tup_num must be less than or
- * equal to PQntuples(res). If it is equal, a new tuple is created and
- * added to the result.
- * Returns a non-zero value for success and zero for failure.
- */
-int
-PQsetvalue(PGresult *res, int tup_num, int field_num, char *value, int len)
-{
- PGresAttValue *attval;
-
- if (!check_field_number(res, field_num))
- return FALSE;
-
- /* Invalid tup_num, must be <= ntups */
- if (tup_num < 0 || tup_num > res->ntups)
- return FALSE;
-
- /* need to allocate a new tuple? */
- if (tup_num == res->ntups)
- {
- PGresAttValue *tup;
- int i;
-
- tup = (PGresAttValue *)
- pqResultAlloc(res, res->numAttributes * sizeof(PGresAttValue),
- TRUE);
-
- if (!tup)
- return FALSE;
-
- /* initialize each column to NULL */
- for (i = 0; i < res->numAttributes; i++)
- {
- tup[i].len = NULL_LEN;
- tup[i].value = res->null_field;
- }
-
- /* add it to the array */
- if (!pqAddTuple(res, tup))
- return FALSE;
- }
-
- attval = &res->tuples[tup_num][field_num];
-
- /* treat either NULL_LEN or NULL value pointer as a NULL field */
- if (len == NULL_LEN || value == NULL)
- {
- attval->len = NULL_LEN;
- attval->value = res->null_field;
- }
- else if (len <= 0)
- {
- attval->len = 0;
- attval->value = res->null_field;
- }
- else
- {
- attval->value = (char *) pqResultAlloc(res, len + 1, TRUE);
- if (!attval->value)
- return FALSE;
- attval->len = len;
- memcpy(attval->value, value, len);
- attval->value[len] = '\0';
- }
-
- return TRUE;
-}
-
-/*
- * pqResultAlloc - exported routine to allocate local storage in a PGresult.
- *
- * We force all such allocations to be maxaligned, since we don't know
- * whether the value might be binary.
- */
-void *
-PQresultAlloc(PGresult *res, size_t nBytes)
-{
- return pqResultAlloc(res, nBytes, TRUE);
-}
-
-/*
- * pqResultAlloc -
- * Allocate subsidiary storage for a PGresult.
- *
- * nBytes is the amount of space needed for the object.
- * If isBinary is true, we assume that we need to align the object on
- * a machine allocation boundary.
- * If isBinary is false, we assume the object is a char string and can
- * be allocated on any byte boundary.
- */
-void *
-pqResultAlloc(PGresult *res, size_t nBytes, bool isBinary)
-{
- char *space;
- PGresult_data *block;
-
- if (!res)
- return NULL;
-
- if (nBytes <= 0)
- return res->null_field;
-
- /*
- * If alignment is needed, round up the current position to an alignment
- * boundary.
- */
- if (isBinary)
- {
- int offset = res->curOffset % PGRESULT_ALIGN_BOUNDARY;
-
- if (offset)
- {
- res->curOffset += PGRESULT_ALIGN_BOUNDARY - offset;
- res->spaceLeft -= PGRESULT_ALIGN_BOUNDARY - offset;
- }
- }
-
- /* If there's enough space in the current block, no problem. */
- if (nBytes <= (size_t) res->spaceLeft)
- {
- space = res->curBlock->space + res->curOffset;
- res->curOffset += nBytes;
- res->spaceLeft -= nBytes;
- return space;
- }
-
- /*
- * If the requested object is very large, give it its own block; this
- * avoids wasting what might be most of the current block to start a new
- * block. (We'd have to special-case requests bigger than the block size
- * anyway.) The object is always given binary alignment in this case.
- */
- if (nBytes >= PGRESULT_SEP_ALLOC_THRESHOLD)
- {
- block = (PGresult_data *) malloc(nBytes + PGRESULT_BLOCK_OVERHEAD);
- if (!block)
- return NULL;
- space = block->space + PGRESULT_BLOCK_OVERHEAD;
- if (res->curBlock)
- {
- /*
- * Tuck special block below the active block, so that we don't
- * have to waste the free space in the active block.
- */
- block->next = res->curBlock->next;
- res->curBlock->next = block;
- }
- else
- {
- /* Must set up the new block as the first active block. */
- block->next = NULL;
- res->curBlock = block;
- res->spaceLeft = 0; /* be sure it's marked full */
- }
- return space;
- }
-
- /* Otherwise, start a new block. */
- block = (PGresult_data *) malloc(PGRESULT_DATA_BLOCKSIZE);
- if (!block)
- return NULL;
- block->next = res->curBlock;
- res->curBlock = block;
- if (isBinary)
- {
- /* object needs full alignment */
- res->curOffset = PGRESULT_BLOCK_OVERHEAD;
- res->spaceLeft = PGRESULT_DATA_BLOCKSIZE - PGRESULT_BLOCK_OVERHEAD;
- }
- else
- {
- /* we can cram it right after the overhead pointer */
- res->curOffset = sizeof(PGresult_data);
- res->spaceLeft = PGRESULT_DATA_BLOCKSIZE - sizeof(PGresult_data);
- }
-
- space = block->space + res->curOffset;
- res->curOffset += nBytes;
- res->spaceLeft -= nBytes;
- return space;
-}
-
-/*
- * pqResultStrdup -
- * Like strdup, but the space is subsidiary PGresult space.
- */
-char *
-pqResultStrdup(PGresult *res, const char *str)
-{
- char *space = (char *) pqResultAlloc(res, strlen(str) + 1, FALSE);
-
- if (space)
- strcpy(space, str);
- return space;
-}
-
-/*
- * pqSetResultError -
- * assign a new error message to a PGresult
- */
-void
-pqSetResultError(PGresult *res, const char *msg)
-{
- if (!res)
- return;
- if (msg && *msg)
- res->errMsg = pqResultStrdup(res, msg);
- else
- res->errMsg = NULL;
-}
-
-/*
- * pqCatenateResultError -
- * concatenate a new error message to the one already in a PGresult
- */
-void
-pqCatenateResultError(PGresult *res, const char *msg)
-{
- PQExpBufferData errorBuf;
-
- if (!res || !msg)
- return;
- initPQExpBuffer(&errorBuf);
- if (res->errMsg)
- appendPQExpBufferStr(&errorBuf, res->errMsg);
- appendPQExpBufferStr(&errorBuf, msg);
- pqSetResultError(res, errorBuf.data);
- termPQExpBuffer(&errorBuf);
-}
-
-/*
- * PQclear -
- * free's the memory associated with a PGresult
- */
-void
-PQclear(PGresult *res)
-{
- PGresult_data *block;
- int i;
-
- if (!res)
- return;
-
- for (i = 0; i < res->nEvents; i++)
- {
- /* only send DESTROY to successfully-initialized event procs */
- if (res->events[i].resultInitialized)
- {
- PGEventResultDestroy evt;
-
- evt.result = res;
- (void) res->events[i].proc(PGEVT_RESULTDESTROY, &evt,
- res->events[i].passThrough);
- }
- free(res->events[i].name);
- }
-
- if (res->events)
- free(res->events);
-
- /* Free all the subsidiary blocks */
- while ((block = res->curBlock) != NULL)
- {
- res->curBlock = block->next;
- free(block);
- }
-
- /* Free the top-level tuple pointer array */
- if (res->tuples)
- free(res->tuples);
-
- /* zero out the pointer fields to catch programming errors */
- res->attDescs = NULL;
- res->tuples = NULL;
- res->paramDescs = NULL;
- res->errFields = NULL;
- res->events = NULL;
- res->nEvents = 0;
- /* res->curBlock was zeroed out earlier */
-
- /* Free the PGresult structure itself */
- free(res);
-}
-
-/*
- * Handy subroutine to deallocate any partially constructed async result.
- *
- * Any "next" result gets cleared too.
- */
-void
-pqClearAsyncResult(PGconn *conn)
-{
- if (conn->result)
- PQclear(conn->result);
- conn->result = NULL;
- if (conn->next_result)
- PQclear(conn->next_result);
- conn->next_result = NULL;
-}
-
-/*
- * This subroutine deletes any existing async result, sets conn->result
- * to a PGresult with status PGRES_FATAL_ERROR, and stores the current
- * contents of conn->errorMessage into that result. It differs from a
- * plain call on PQmakeEmptyPGresult() in that if there is already an
- * async result with status PGRES_FATAL_ERROR, the current error message
- * is APPENDED to the old error message instead of replacing it. This
- * behavior lets us report multiple error conditions properly, if necessary.
- * (An example where this is needed is when the backend sends an 'E' message
- * and immediately closes the connection --- we want to report both the
- * backend error and the connection closure error.)
- */
-void
-pqSaveErrorResult(PGconn *conn)
-{
- /*
- * If no old async result, just let PQmakeEmptyPGresult make one. Likewise
- * if old result is not an error message.
- */
- if (conn->result == NULL ||
- conn->result->resultStatus != PGRES_FATAL_ERROR ||
- conn->result->errMsg == NULL)
- {
- pqClearAsyncResult(conn);
- conn->result = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR);
- }
- else
- {
- /* Else, concatenate error message to existing async result. */
- pqCatenateResultError(conn->result, conn->errorMessage.data);
- }
-}
-
-/*
- * This subroutine prepares an async result object for return to the caller.
- * If there is not already an async result object, build an error object
- * using whatever is in conn->errorMessage. In any case, clear the async
- * result storage and make sure PQerrorMessage will agree with the result's
- * error string.
- */
-PGresult *
-pqPrepareAsyncResult(PGconn *conn)
-{
- PGresult *res;
-
- /*
- * conn->result is the PGresult to return. If it is NULL (which probably
- * shouldn't happen) we assume there is an appropriate error message in
- * conn->errorMessage.
- */
- res = conn->result;
- if (!res)
- res = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR);
- else
- {
- /*
- * Make sure PQerrorMessage agrees with result; it could be different
- * if we have concatenated messages.
- */
- resetPQExpBuffer(&conn->errorMessage);
- appendPQExpBufferStr(&conn->errorMessage,
- PQresultErrorMessage(res));
- }
-
- /*
- * Replace conn->result with next_result, if any. In the normal case
- * there isn't a next result and we're just dropping ownership of the
- * current result. In single-row mode this restores the situation to what
- * it was before we created the current single-row result.
- */
- conn->result = conn->next_result;
- conn->next_result = NULL;
-
- return res;
-}
-
-/*
- * pqInternalNotice - produce an internally-generated notice message
- *
- * A format string and optional arguments can be passed. Note that we do
- * libpq_gettext() here, so callers need not.
- *
- * The supplied text is taken as primary message (ie., it should not include
- * a trailing newline, and should not be more than one line).
- */
-void
-pqInternalNotice(const PGNoticeHooks *hooks, const char *fmt,...)
-{
- char msgBuf[1024];
- va_list args;
- PGresult *res;
-
- if (hooks->noticeRec == NULL)
- return; /* nobody home to receive notice? */
-
- /* Format the message */
- va_start(args, fmt);
- vsnprintf(msgBuf, sizeof(msgBuf), libpq_gettext(fmt), args);
- va_end(args);
- msgBuf[sizeof(msgBuf) - 1] = '\0'; /* make real sure it's terminated */
-
- /* Make a PGresult to pass to the notice receiver */
- res = PQmakeEmptyPGresult(NULL, PGRES_NONFATAL_ERROR);
- if (!res)
- return;
- res->noticeHooks = *hooks;
-
- /*
- * Set up fields of notice.
- */
- pqSaveMessageField(res, PG_DIAG_MESSAGE_PRIMARY, msgBuf);
- pqSaveMessageField(res, PG_DIAG_SEVERITY, libpq_gettext("NOTICE"));
- pqSaveMessageField(res, PG_DIAG_SEVERITY_NONLOCALIZED, "NOTICE");
- /* XXX should provide a SQLSTATE too? */
-
- /*
- * Result text is always just the primary message + newline. If we can't
- * allocate it, don't bother invoking the receiver.
- */
- res->errMsg = (char *) pqResultAlloc(res, strlen(msgBuf) + 2, FALSE);
- if (res->errMsg)
- {
- sprintf(res->errMsg, "%s\n", msgBuf);
-
- /*
- * Pass to receiver, then free it.
- */
- (*res->noticeHooks.noticeRec) (res->noticeHooks.noticeRecArg, res);
- }
- PQclear(res);
-}
-
-/*
- * pqAddTuple
- * add a row pointer to the PGresult structure, growing it if necessary
- * Returns TRUE if OK, FALSE if not enough memory to add the row
- */
-static bool
-pqAddTuple(PGresult *res, PGresAttValue *tup)
-{
- if (res->ntups >= res->tupArrSize)
- {
- /*
- * Try to grow the array.
- *
- * We can use realloc because shallow copying of the structure is
- * okay. Note that the first time through, res->tuples is NULL. While
- * ANSI says that realloc() should act like malloc() in that case,
- * some old C libraries (like SunOS 4.1.x) coredump instead. On
- * failure realloc is supposed to return NULL without damaging the
- * existing allocation. Note that the positions beyond res->ntups are
- * garbage, not necessarily NULL.
- */
- int newSize = (res->tupArrSize > 0) ? res->tupArrSize * 2 : 128;
- PGresAttValue **newTuples;
-
- if (res->tuples == NULL)
- newTuples = (PGresAttValue **)
- malloc(newSize * sizeof(PGresAttValue *));
- else
- newTuples = (PGresAttValue **)
- realloc(res->tuples, newSize * sizeof(PGresAttValue *));
- if (!newTuples)
- return FALSE; /* malloc or realloc failed */
- res->tupArrSize = newSize;
- res->tuples = newTuples;
- }
- res->tuples[res->ntups] = tup;
- res->ntups++;
- return TRUE;
-}
-
-/*
- * pqSaveMessageField - save one field of an error or notice message
- */
-void
-pqSaveMessageField(PGresult *res, char code, const char *value)
-{
- PGMessageField *pfield;
-
- pfield = (PGMessageField *)
- pqResultAlloc(res,
- offsetof(PGMessageField, contents) +
- strlen(value) + 1,
- TRUE);
- if (!pfield)
- return; /* out of memory? */
- pfield->code = code;
- strcpy(pfield->contents, value);
- pfield->next = res->errFields;
- res->errFields = pfield;
-}
-
-/*
- * pqSaveParameterStatus - remember parameter status sent by backend
- */
-void
-pqSaveParameterStatus(PGconn *conn, const char *name, const char *value)
-{
- pgParameterStatus *pstatus;
- pgParameterStatus *prev;
-
- if (conn->Pfdebug)
- fprintf(conn->Pfdebug, "pqSaveParameterStatus: '%s' = '%s'\n",
- name, value);
-
- /*
- * Forget any old information about the parameter
- */
- for (pstatus = conn->pstatus, prev = NULL;
- pstatus != NULL;
- prev = pstatus, pstatus = pstatus->next)
- {
- if (strcmp(pstatus->name, name) == 0)
- {
- if (prev)
- prev->next = pstatus->next;
- else
- conn->pstatus = pstatus->next;
- free(pstatus); /* frees name and value strings too */
- break;
- }
- }
-
- /*
- * Store new info as a single malloc block
- */
- pstatus = (pgParameterStatus *) malloc(sizeof(pgParameterStatus) +
- strlen(name) +strlen(value) + 2);
- if (pstatus)
- {
- char *ptr;
-
- ptr = ((char *) pstatus) + sizeof(pgParameterStatus);
- pstatus->name = ptr;
- strcpy(ptr, name);
- ptr += strlen(name) + 1;
- pstatus->value = ptr;
- strcpy(ptr, value);
- pstatus->next = conn->pstatus;
- conn->pstatus = pstatus;
- }
-
- /*
- * Special hacks: remember client_encoding and
- * standard_conforming_strings, and convert server version to a numeric
- * form. We keep the first two of these in static variables as well, so
- * that PQescapeString and PQescapeBytea can behave somewhat sanely (at
- * least in single-connection-using programs).
- */
- if (strcmp(name, "client_encoding") == 0)
- {
- conn->client_encoding = pg_char_to_encoding(value);
- /* if we don't recognize the encoding name, fall back to SQL_ASCII */
- if (conn->client_encoding < 0)
- conn->client_encoding = PG_SQL_ASCII;
- static_client_encoding = conn->client_encoding;
- }
- else if (strcmp(name, "standard_conforming_strings") == 0)
- {
- conn->std_strings = (strcmp(value, "on") == 0);
- static_std_strings = conn->std_strings;
- }
- else if (strcmp(name, "server_version") == 0)
- {
- int cnt;
- int vmaj,
- vmin,
- vrev;
-
- cnt = sscanf(value, "%d.%d.%d", &vmaj, &vmin, &vrev);
-
- if (cnt == 3)
- {
- /* old style, e.g. 9.6.1 */
- conn->sversion = (100 * vmaj + vmin) * 100 + vrev;
- }
- else if (cnt == 2)
- {
- if (vmaj >= 10)
- {
- /* new style, e.g. 10.1 */
- conn->sversion = 100 * 100 * vmaj + vmin;
- }
- else
- {
- /* old style without minor version, e.g. 9.6devel */
- conn->sversion = (100 * vmaj + vmin) * 100;
- }
- }
- else if (cnt == 1)
- {
- /* new style without minor version, e.g. 10devel */
- conn->sversion = 100 * 100 * vmaj;
- }
- else
- conn->sversion = 0; /* unknown */
- }
-}
-
-
-/*
- * pqRowProcessor
- * Add the received row to the current async result (conn->result).
- * Returns 1 if OK, 0 if error occurred.
- *
- * On error, *errmsgp can be set to an error string to be returned.
- * If it is left NULL, the error is presumed to be "out of memory".
- *
- * In single-row mode, we create a new result holding just the current row,
- * stashing the previous result in conn->next_result so that it becomes
- * active again after pqPrepareAsyncResult(). This allows the result metadata
- * (column descriptions) to be carried forward to each result row.
- */
-int
-pqRowProcessor(PGconn *conn, const char **errmsgp)
-{
- PGresult *res = conn->result;
- int nfields = res->numAttributes;
- const PGdataValue *columns = conn->rowBuf;
- PGresAttValue *tup;
- int i;
-
- /*
- * In single-row mode, make a new PGresult that will hold just this one
- * row; the original conn->result is left unchanged so that it can be used
- * again as the template for future rows.
- */
- if (conn->singleRowMode)
- {
- /* Copy everything that should be in the result at this point */
- res = PQcopyResult(res,
- PG_COPYRES_ATTRS | PG_COPYRES_EVENTS |
- PG_COPYRES_NOTICEHOOKS);
- if (!res)
- return 0;
- }
-
- /*
- * Basically we just allocate space in the PGresult for each field and
- * copy the data over.
- *
- * Note: on malloc failure, we return 0 leaving *errmsgp still NULL, which
- * caller will take to mean "out of memory". This is preferable to trying
- * to set up such a message here, because evidently there's not enough
- * memory for gettext() to do anything.
- */
- tup = (PGresAttValue *)
- pqResultAlloc(res, nfields * sizeof(PGresAttValue), TRUE);
- if (tup == NULL)
- goto fail;
-
- for (i = 0; i < nfields; i++)
- {
- int clen = columns[i].len;
-
- if (clen < 0)
- {
- /* null field */
- tup[i].len = NULL_LEN;
- tup[i].value = res->null_field;
- }
- else
- {
- bool isbinary = (res->attDescs[i].format != 0);
- char *val;
-
- val = (char *) pqResultAlloc(res, clen + 1, isbinary);
- if (val == NULL)
- goto fail;
-
- /* copy and zero-terminate the data (even if it's binary) */
- memcpy(val, columns[i].value, clen);
- val[clen] = '\0';
-
- tup[i].len = clen;
- tup[i].value = val;
- }
- }
-
- /* And add the tuple to the PGresult's tuple array */
- if (!pqAddTuple(res, tup))
- goto fail;
-
- /*
- * Success. In single-row mode, make the result available to the client
- * immediately.
- */
- if (conn->singleRowMode)
- {
- /* Change result status to special single-row value */
- res->resultStatus = PGRES_SINGLE_TUPLE;
- /* Stash old result for re-use later */
- conn->next_result = conn->result;
- conn->result = res;
- /* And mark the result ready to return */
- conn->asyncStatus = PGASYNC_READY;
- }
-
- return 1;
-
-fail:
- /* release locally allocated PGresult, if we made one */
- if (res != conn->result)
- PQclear(res);
- return 0;
-}
-
-
-/*
- * PQsendQuery
- * Submit a query, but don't wait for it to finish
- *
- * Returns: 1 if successfully submitted
- * 0 if error (conn->errorMessage is set)
- */
-int
-PQsendQuery(PGconn *conn, const char *query)
-{
- if (!PQsendQueryStart(conn))
- return 0;
-
- /* check the argument */
- if (!query)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("command string is a null pointer\n"));
- return 0;
- }
-
- /* construct the outgoing Query message */
- if (pqPutMsgStart('Q', false, conn) < 0 ||
- pqPuts(query, conn) < 0 ||
- pqPutMsgEnd(conn) < 0)
- {
- pqHandleSendFailure(conn);
- return 0;
- }
-
- /* remember we are using simple query protocol */
- conn->queryclass = PGQUERY_SIMPLE;
-
- /* and remember the query text too, if possible */
- /* if insufficient memory, last_query just winds up NULL */
- if (conn->last_query)
- free(conn->last_query);
- conn->last_query = strdup(query);
-
- /*
- * Give the data a push. In nonblock mode, don't complain if we're unable
- * to send it all; PQgetResult() will do any additional flushing needed.
- */
- if (pqFlush(conn) < 0)
- {
- pqHandleSendFailure(conn);
- return 0;
- }
-
- /* OK, it's launched! */
- conn->asyncStatus = PGASYNC_BUSY;
- return 1;
-}
-
-/*
- * PQsendQueryParams
- * Like PQsendQuery, but use protocol 3.0 so we can pass parameters
- */
-int
-PQsendQueryParams(PGconn *conn,
- const char *command,
- int nParams,
- const Oid *paramTypes,
- const char *const * paramValues,
- const int *paramLengths,
- const int *paramFormats,
- int resultFormat)
-{
- if (!PQsendQueryStart(conn))
- return 0;
-
- /* check the arguments */
- if (!command)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("command string is a null pointer\n"));
- return 0;
- }
- if (nParams < 0 || nParams > 65535)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("number of parameters must be between 0 and 65535\n"));
- return 0;
- }
-
- return PQsendQueryGuts(conn,
- command,
- "", /* use unnamed statement */
- nParams,
- paramTypes,
- paramValues,
- paramLengths,
- paramFormats,
- resultFormat);
-}
-
-/*
- * PQsendPrepare
- * Submit a Parse message, but don't wait for it to finish
- *
- * Returns: 1 if successfully submitted
- * 0 if error (conn->errorMessage is set)
- */
-int
-PQsendPrepare(PGconn *conn,
- const char *stmtName, const char *query,
- int nParams, const Oid *paramTypes)
-{
- if (!PQsendQueryStart(conn))
- return 0;
-
- /* check the arguments */
- if (!stmtName)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("statement name is a null pointer\n"));
- return 0;
- }
- if (!query)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("command string is a null pointer\n"));
- return 0;
- }
- if (nParams < 0 || nParams > 65535)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("number of parameters must be between 0 and 65535\n"));
- return 0;
- }
-
- /* This isn't gonna work on a 2.0 server */
- if (PG_PROTOCOL_MAJOR(conn->pversion) < 3)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("function requires at least protocol version 3.0\n"));
- return 0;
- }
-
- /* construct the Parse message */
- if (pqPutMsgStart('P', false, conn) < 0 ||
- pqPuts(stmtName, conn) < 0 ||
- pqPuts(query, conn) < 0)
- goto sendFailed;
-
- if (nParams > 0 && paramTypes)
- {
- int i;
-
- if (pqPutInt(nParams, 2, conn) < 0)
- goto sendFailed;
- for (i = 0; i < nParams; i++)
- {
- if (pqPutInt(paramTypes[i], 4, conn) < 0)
- goto sendFailed;
- }
- }
- else
- {
- if (pqPutInt(0, 2, conn) < 0)
- goto sendFailed;
- }
- if (pqPutMsgEnd(conn) < 0)
- goto sendFailed;
-
- /* construct the Sync message */
- if (pqPutMsgStart('S', false, conn) < 0 ||
- pqPutMsgEnd(conn) < 0)
- goto sendFailed;
-
- /* remember we are doing just a Parse */
- conn->queryclass = PGQUERY_PREPARE;
-
- /* and remember the query text too, if possible */
- /* if insufficient memory, last_query just winds up NULL */
- if (conn->last_query)
- free(conn->last_query);
- conn->last_query = strdup(query);
-
- /*
- * Give the data a push. In nonblock mode, don't complain if we're unable
- * to send it all; PQgetResult() will do any additional flushing needed.
- */
- if (pqFlush(conn) < 0)
- goto sendFailed;
-
- /* OK, it's launched! */
- conn->asyncStatus = PGASYNC_BUSY;
- return 1;
-
-sendFailed:
- pqHandleSendFailure(conn);
- return 0;
-}
-
-/*
- * PQsendQueryPrepared
- * Like PQsendQuery, but execute a previously prepared statement,
- * using protocol 3.0 so we can pass parameters
- */
-int
-PQsendQueryPrepared(PGconn *conn,
- const char *stmtName,
- int nParams,
- const char *const * paramValues,
- const int *paramLengths,
- const int *paramFormats,
- int resultFormat)
-{
- if (!PQsendQueryStart(conn))
- return 0;
-
- /* check the arguments */
- if (!stmtName)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("statement name is a null pointer\n"));
- return 0;
- }
- if (nParams < 0 || nParams > 65535)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("number of parameters must be between 0 and 65535\n"));
- return 0;
- }
-
- return PQsendQueryGuts(conn,
- NULL, /* no command to parse */
- stmtName,
- nParams,
- NULL, /* no param types */
- paramValues,
- paramLengths,
- paramFormats,
- resultFormat);
-}
-
-/*
- * Common startup code for PQsendQuery and sibling routines
- */
-static bool
-PQsendQueryStart(PGconn *conn)
-{
- if (!conn)
- return false;
-
- /* clear the error string */
- resetPQExpBuffer(&conn->errorMessage);
-
- /* Don't try to send if we know there's no live connection. */
- if (conn->status != CONNECTION_OK)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("no connection to the server\n"));
- return false;
- }
- /* Can't send while already busy, either. */
- if (conn->asyncStatus != PGASYNC_IDLE)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("another command is already in progress\n"));
- return false;
- }
-
- /* initialize async result-accumulation state */
- pqClearAsyncResult(conn);
-
- /* reset single-row processing mode */
- conn->singleRowMode = false;
-
- /* ready to send command message */
- return true;
-}
-
-/*
- * PQsendQueryGuts
- * Common code for protocol-3.0 query sending
- * PQsendQueryStart should be done already
- *
- * command may be NULL to indicate we use an already-prepared statement
- */
-static int
-PQsendQueryGuts(PGconn *conn,
- const char *command,
- const char *stmtName,
- int nParams,
- const Oid *paramTypes,
- const char *const * paramValues,
- const int *paramLengths,
- const int *paramFormats,
- int resultFormat)
-{
- int i;
-
- /* This isn't gonna work on a 2.0 server */
- if (PG_PROTOCOL_MAJOR(conn->pversion) < 3)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("function requires at least protocol version 3.0\n"));
- return 0;
- }
-
- /*
- * We will send Parse (if needed), Bind, Describe Portal, Execute, Sync,
- * using specified statement name and the unnamed portal.
- */
-
- if (command)
- {
- /* construct the Parse message */
- if (pqPutMsgStart('P', false, conn) < 0 ||
- pqPuts(stmtName, conn) < 0 ||
- pqPuts(command, conn) < 0)
- goto sendFailed;
- if (nParams > 0 && paramTypes)
- {
- if (pqPutInt(nParams, 2, conn) < 0)
- goto sendFailed;
- for (i = 0; i < nParams; i++)
- {
- if (pqPutInt(paramTypes[i], 4, conn) < 0)
- goto sendFailed;
- }
- }
- else
- {
- if (pqPutInt(0, 2, conn) < 0)
- goto sendFailed;
- }
- if (pqPutMsgEnd(conn) < 0)
- goto sendFailed;
- }
-
- /* Construct the Bind message */
- if (pqPutMsgStart('B', false, conn) < 0 ||
- pqPuts("", conn) < 0 ||
- pqPuts(stmtName, conn) < 0)
- goto sendFailed;
-
- /* Send parameter formats */
- if (nParams > 0 && paramFormats)
- {
- if (pqPutInt(nParams, 2, conn) < 0)
- goto sendFailed;
- for (i = 0; i < nParams; i++)
- {
- if (pqPutInt(paramFormats[i], 2, conn) < 0)
- goto sendFailed;
- }
- }
- else
- {
- if (pqPutInt(0, 2, conn) < 0)
- goto sendFailed;
- }
-
- if (pqPutInt(nParams, 2, conn) < 0)
- goto sendFailed;
-
- /* Send parameters */
- for (i = 0; i < nParams; i++)
- {
- if (paramValues && paramValues[i])
- {
- int nbytes;
-
- if (paramFormats && paramFormats[i] != 0)
- {
- /* binary parameter */
- if (paramLengths)
- nbytes = paramLengths[i];
- else
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("length must be given for binary parameter\n"));
- goto sendFailed;
- }
- }
- else
- {
- /* text parameter, do not use paramLengths */
- nbytes = strlen(paramValues[i]);
- }
- if (pqPutInt(nbytes, 4, conn) < 0 ||
- pqPutnchar(paramValues[i], nbytes, conn) < 0)
- goto sendFailed;
- }
- else
- {
- /* take the param as NULL */
- if (pqPutInt(-1, 4, conn) < 0)
- goto sendFailed;
- }
- }
- if (pqPutInt(1, 2, conn) < 0 ||
- pqPutInt(resultFormat, 2, conn))
- goto sendFailed;
- if (pqPutMsgEnd(conn) < 0)
- goto sendFailed;
-
- /* construct the Describe Portal message */
- if (pqPutMsgStart('D', false, conn) < 0 ||
- pqPutc('P', conn) < 0 ||
- pqPuts("", conn) < 0 ||
- pqPutMsgEnd(conn) < 0)
- goto sendFailed;
-
- /* construct the Execute message */
- if (pqPutMsgStart('E', false, conn) < 0 ||
- pqPuts("", conn) < 0 ||
- pqPutInt(0, 4, conn) < 0 ||
- pqPutMsgEnd(conn) < 0)
- goto sendFailed;
-
- /* construct the Sync message */
- if (pqPutMsgStart('S', false, conn) < 0 ||
- pqPutMsgEnd(conn) < 0)
- goto sendFailed;
-
- /* remember we are using extended query protocol */
- conn->queryclass = PGQUERY_EXTENDED;
-
- /* and remember the query text too, if possible */
- /* if insufficient memory, last_query just winds up NULL */
- if (conn->last_query)
- free(conn->last_query);
- if (command)
- conn->last_query = strdup(command);
- else
- conn->last_query = NULL;
-
- /*
- * Give the data a push. In nonblock mode, don't complain if we're unable
- * to send it all; PQgetResult() will do any additional flushing needed.
- */
- if (pqFlush(conn) < 0)
- goto sendFailed;
-
- /* OK, it's launched! */
- conn->asyncStatus = PGASYNC_BUSY;
- return 1;
-
-sendFailed:
- pqHandleSendFailure(conn);
- return 0;
-}
-
-/*
- * pqHandleSendFailure: try to clean up after failure to send command.
- *
- * Primarily, what we want to accomplish here is to process any ERROR or
- * NOTICE messages that the backend might have sent just before it died.
- * Since we're in IDLE state, all such messages will get sent to the notice
- * processor.
- *
- * NOTE: this routine should only be called in PGASYNC_IDLE state.
- */
-void
-pqHandleSendFailure(PGconn *conn)
-{
- /*
- * Accept and parse any available input data, ignoring I/O errors. Note
- * that if pqReadData decides the backend has closed the channel, it will
- * close our side of the socket --- that's just what we want here.
- */
- while (pqReadData(conn) > 0)
- parseInput(conn);
-
- /*
- * Be sure to parse available input messages even if we read no data.
- * (Note: calling parseInput within the above loop isn't really necessary,
- * but it prevents buffer bloat if there's a lot of data available.)
- */
- parseInput(conn);
-}
-
-/*
- * Select row-by-row processing mode
- */
-int
-PQsetSingleRowMode(PGconn *conn)
-{
- /*
- * Only allow setting the flag when we have launched a query and not yet
- * received any results.
- */
- if (!conn)
- return 0;
- if (conn->asyncStatus != PGASYNC_BUSY)
- return 0;
- if (conn->queryclass != PGQUERY_SIMPLE &&
- conn->queryclass != PGQUERY_EXTENDED)
- return 0;
- if (conn->result)
- return 0;
-
- /* OK, set flag */
- conn->singleRowMode = true;
- return 1;
-}
-
-/*
- * Consume any available input from the backend
- * 0 return: some kind of trouble
- * 1 return: no problem
- */
-int
-PQconsumeInput(PGconn *conn)
-{
- if (!conn)
- return 0;
-
- /*
- * for non-blocking connections try to flush the send-queue, otherwise we
- * may never get a response for something that may not have already been
- * sent because it's in our write buffer!
- */
- if (pqIsnonblocking(conn))
- {
- if (pqFlush(conn) < 0)
- return 0;
- }
-
- /*
- * Load more data, if available. We do this no matter what state we are
- * in, since we are probably getting called because the application wants
- * to get rid of a read-select condition. Note that we will NOT block
- * waiting for more input.
- */
- if (pqReadData(conn) < 0)
- return 0;
-
- /* Parsing of the data waits till later. */
- return 1;
-}
-
-
-/*
- * parseInput: if appropriate, parse input data from backend
- * until input is exhausted or a stopping state is reached.
- * Note that this function will NOT attempt to read more data from the backend.
- */
-static void
-parseInput(PGconn *conn)
-{
- if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
- pqParseInput3(conn);
- else
- pqParseInput2(conn);
-}
-
-/*
- * PQisBusy
- * Return TRUE if PQgetResult would block waiting for input.
- */
-
-int
-PQisBusy(PGconn *conn)
-{
- if (!conn)
- return FALSE;
-
- /* Parse any available data, if our state permits. */
- parseInput(conn);
-
- /* PQgetResult will return immediately in all states except BUSY. */
- return conn->asyncStatus == PGASYNC_BUSY;
-}
-
-
-/*
- * PQgetResult
- * Get the next PGresult produced by a query. Returns NULL if no
- * query work remains or an error has occurred (e.g. out of
- * memory).
- */
-
-PGresult *
-PQgetResult(PGconn *conn)
-{
- PGresult *res;
-
- if (!conn)
- return NULL;
-
- /* Parse any available data, if our state permits. */
- parseInput(conn);
-
- /* If not ready to return something, block until we are. */
- while (conn->asyncStatus == PGASYNC_BUSY)
- {
- int flushResult;
-
- /*
- * If data remains unsent, send it. Else we might be waiting for the
- * result of a command the backend hasn't even got yet.
- */
- while ((flushResult = pqFlush(conn)) > 0)
- {
- if (pqWait(FALSE, TRUE, conn))
- {
- flushResult = -1;
- break;
- }
- }
-
- /* Wait for some more data, and load it. */
- if (flushResult ||
- pqWait(TRUE, FALSE, conn) ||
- pqReadData(conn) < 0)
- {
- /*
- * conn->errorMessage has been set by pqWait or pqReadData. We
- * want to append it to any already-received error message.
- */
- pqSaveErrorResult(conn);
- conn->asyncStatus = PGASYNC_IDLE;
- return pqPrepareAsyncResult(conn);
- }
-
- /* Parse it. */
- parseInput(conn);
- }
-
- /* Return the appropriate thing. */
- switch (conn->asyncStatus)
- {
- case PGASYNC_IDLE:
- res = NULL; /* query is complete */
- break;
- case PGASYNC_READY:
- res = pqPrepareAsyncResult(conn);
- /* Set the state back to BUSY, allowing parsing to proceed. */
- conn->asyncStatus = PGASYNC_BUSY;
- break;
- case PGASYNC_COPY_IN:
- res = getCopyResult(conn, PGRES_COPY_IN);
- break;
- case PGASYNC_COPY_OUT:
- res = getCopyResult(conn, PGRES_COPY_OUT);
- break;
- case PGASYNC_COPY_BOTH:
- res = getCopyResult(conn, PGRES_COPY_BOTH);
- break;
- default:
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("unexpected asyncStatus: %d\n"),
- (int) conn->asyncStatus);
- res = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR);
- break;
- }
-
- if (res)
- {
- int i;
-
- for (i = 0; i < res->nEvents; i++)
- {
- PGEventResultCreate evt;
-
- evt.conn = conn;
- evt.result = res;
- if (!res->events[i].proc(PGEVT_RESULTCREATE, &evt,
- res->events[i].passThrough))
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("PGEventProc \"%s\" failed during PGEVT_RESULTCREATE event\n"),
- res->events[i].name);
- pqSetResultError(res, conn->errorMessage.data);
- res->resultStatus = PGRES_FATAL_ERROR;
- break;
- }
- res->events[i].resultInitialized = TRUE;
- }
- }
-
- return res;
-}
-
-/*
- * getCopyResult
- * Helper for PQgetResult: generate result for COPY-in-progress cases
- */
-static PGresult *
-getCopyResult(PGconn *conn, ExecStatusType copytype)
-{
- /*
- * If the server connection has been lost, don't pretend everything is
- * hunky-dory; instead return a PGRES_FATAL_ERROR result, and reset the
- * asyncStatus to idle (corresponding to what we'd do if we'd detected I/O
- * error in the earlier steps in PQgetResult). The text returned in the
- * result is whatever is in conn->errorMessage; we hope that was filled
- * with something relevant when the lost connection was detected.
- */
- if (conn->status != CONNECTION_OK)
- {
- pqSaveErrorResult(conn);
- conn->asyncStatus = PGASYNC_IDLE;
- return pqPrepareAsyncResult(conn);
- }
-
- /* If we have an async result for the COPY, return that */
- if (conn->result && conn->result->resultStatus == copytype)
- return pqPrepareAsyncResult(conn);
-
- /* Otherwise, invent a suitable PGresult */
- return PQmakeEmptyPGresult(conn, copytype);
-}
-
-
-/*
- * PQexec
- * send a query to the backend and package up the result in a PGresult
- *
- * If the query was not even sent, return NULL; conn->errorMessage is set to
- * a relevant message.
- * If the query was sent, a new PGresult is returned (which could indicate
- * either success or failure).
- * The user is responsible for freeing the PGresult via PQclear()
- * when done with it.
- */
-PGresult *
-PQexec(PGconn *conn, const char *query)
-{
- if (!PQexecStart(conn))
- return NULL;
- if (!PQsendQuery(conn, query))
- return NULL;
- return PQexecFinish(conn);
-}
-
-/*
- * PQexecParams
- * Like PQexec, but use protocol 3.0 so we can pass parameters
- */
-PGresult *
-PQexecParams(PGconn *conn,
- const char *command,
- int nParams,
- const Oid *paramTypes,
- const char *const * paramValues,
- const int *paramLengths,
- const int *paramFormats,
- int resultFormat)
-{
- if (!PQexecStart(conn))
- return NULL;
- if (!PQsendQueryParams(conn, command,
- nParams, paramTypes, paramValues, paramLengths,
- paramFormats, resultFormat))
- return NULL;
- return PQexecFinish(conn);
-}
-
-/*
- * PQprepare
- * Creates a prepared statement by issuing a v3.0 parse message.
- *
- * If the query was not even sent, return NULL; conn->errorMessage is set to
- * a relevant message.
- * If the query was sent, a new PGresult is returned (which could indicate
- * either success or failure).
- * The user is responsible for freeing the PGresult via PQclear()
- * when done with it.
- */
-PGresult *
-PQprepare(PGconn *conn,
- const char *stmtName, const char *query,
- int nParams, const Oid *paramTypes)
-{
- if (!PQexecStart(conn))
- return NULL;
- if (!PQsendPrepare(conn, stmtName, query, nParams, paramTypes))
- return NULL;
- return PQexecFinish(conn);
-}
-
-/*
- * PQexecPrepared
- * Like PQexec, but execute a previously prepared statement,
- * using protocol 3.0 so we can pass parameters
- */
-PGresult *
-PQexecPrepared(PGconn *conn,
- const char *stmtName,
- int nParams,
- const char *const * paramValues,
- const int *paramLengths,
- const int *paramFormats,
- int resultFormat)
-{
- if (!PQexecStart(conn))
- return NULL;
- if (!PQsendQueryPrepared(conn, stmtName,
- nParams, paramValues, paramLengths,
- paramFormats, resultFormat))
- return NULL;
- return PQexecFinish(conn);
-}
-
-/*
- * Common code for PQexec and sibling routines: prepare to send command
- */
-static bool
-PQexecStart(PGconn *conn)
-{
- PGresult *result;
-
- if (!conn)
- return false;
-
- /*
- * Silently discard any prior query result that application didn't eat.
- * This is probably poor design, but it's here for backward compatibility.
- */
- while ((result = PQgetResult(conn)) != NULL)
- {
- ExecStatusType resultStatus = result->resultStatus;
-
- PQclear(result); /* only need its status */
- if (resultStatus == PGRES_COPY_IN)
- {
- if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
- {
- /* In protocol 3, we can get out of a COPY IN state */
- if (PQputCopyEnd(conn,
- libpq_gettext("COPY terminated by new PQexec")) < 0)
- return false;
- /* keep waiting to swallow the copy's failure message */
- }
- else
- {
- /* In older protocols we have to punt */
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("COPY IN state must be terminated first\n"));
- return false;
- }
- }
- else if (resultStatus == PGRES_COPY_OUT)
- {
- if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
- {
- /*
- * In protocol 3, we can get out of a COPY OUT state: we just
- * switch back to BUSY and allow the remaining COPY data to be
- * dropped on the floor.
- */
- conn->asyncStatus = PGASYNC_BUSY;
- /* keep waiting to swallow the copy's completion message */
- }
- else
- {
- /* In older protocols we have to punt */
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("COPY OUT state must be terminated first\n"));
- return false;
- }
- }
- else if (resultStatus == PGRES_COPY_BOTH)
- {
- /* We don't allow PQexec during COPY BOTH */
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("PQexec not allowed during COPY BOTH\n"));
- return false;
- }
- /* check for loss of connection, too */
- if (conn->status == CONNECTION_BAD)
- return false;
- }
-
- /* OK to send a command */
- return true;
-}
-
-/*
- * Common code for PQexec and sibling routines: wait for command result
- */
-static PGresult *
-PQexecFinish(PGconn *conn)
-{
- PGresult *result;
- PGresult *lastResult;
-
- /*
- * For backwards compatibility, return the last result if there are more
- * than one --- but merge error messages if we get more than one error
- * result.
- *
- * We have to stop if we see copy in/out/both, however. We will resume
- * parsing after application performs the data transfer.
- *
- * Also stop if the connection is lost (else we'll loop infinitely).
- */
- lastResult = NULL;
- while ((result = PQgetResult(conn)) != NULL)
- {
- if (lastResult)
- {
- if (lastResult->resultStatus == PGRES_FATAL_ERROR &&
- result->resultStatus == PGRES_FATAL_ERROR)
- {
- pqCatenateResultError(lastResult, result->errMsg);
- PQclear(result);
- result = lastResult;
-
- /*
- * Make sure PQerrorMessage agrees with concatenated result
- */
- resetPQExpBuffer(&conn->errorMessage);
- appendPQExpBufferStr(&conn->errorMessage, result->errMsg);
- }
- else
- PQclear(lastResult);
- }
- lastResult = result;
- if (result->resultStatus == PGRES_COPY_IN ||
- result->resultStatus == PGRES_COPY_OUT ||
- result->resultStatus == PGRES_COPY_BOTH ||
- conn->status == CONNECTION_BAD)
- break;
- }
-
- return lastResult;
-}
-
-/*
- * PQdescribePrepared
- * Obtain information about a previously prepared statement
- *
- * If the query was not even sent, return NULL; conn->errorMessage is set to
- * a relevant message.
- * If the query was sent, a new PGresult is returned (which could indicate
- * either success or failure). On success, the PGresult contains status
- * PGRES_COMMAND_OK, and its parameter and column-heading fields describe
- * the statement's inputs and outputs respectively.
- * The user is responsible for freeing the PGresult via PQclear()
- * when done with it.
- */
-PGresult *
-PQdescribePrepared(PGconn *conn, const char *stmt)
-{
- if (!PQexecStart(conn))
- return NULL;
- if (!PQsendDescribe(conn, 'S', stmt))
- return NULL;
- return PQexecFinish(conn);
-}
-
-/*
- * PQdescribePortal
- * Obtain information about a previously created portal
- *
- * This is much like PQdescribePrepared, except that no parameter info is
- * returned. Note that at the moment, libpq doesn't really expose portals
- * to the client; but this can be used with a portal created by a SQL
- * DECLARE CURSOR command.
- */
-PGresult *
-PQdescribePortal(PGconn *conn, const char *portal)
-{
- if (!PQexecStart(conn))
- return NULL;
- if (!PQsendDescribe(conn, 'P', portal))
- return NULL;
- return PQexecFinish(conn);
-}
-
-/*
- * PQsendDescribePrepared
- * Submit a Describe Statement command, but don't wait for it to finish
- *
- * Returns: 1 if successfully submitted
- * 0 if error (conn->errorMessage is set)
- */
-int
-PQsendDescribePrepared(PGconn *conn, const char *stmt)
-{
- return PQsendDescribe(conn, 'S', stmt);
-}
-
-/*
- * PQsendDescribePortal
- * Submit a Describe Portal command, but don't wait for it to finish
- *
- * Returns: 1 if successfully submitted
- * 0 if error (conn->errorMessage is set)
- */
-int
-PQsendDescribePortal(PGconn *conn, const char *portal)
-{
- return PQsendDescribe(conn, 'P', portal);
-}
-
-/*
- * PQsendDescribe
- * Common code to send a Describe command
- *
- * Available options for desc_type are
- * 'S' to describe a prepared statement; or
- * 'P' to describe a portal.
- * Returns 1 on success and 0 on failure.
- */
-static int
-PQsendDescribe(PGconn *conn, char desc_type, const char *desc_target)
-{
- /* Treat null desc_target as empty string */
- if (!desc_target)
- desc_target = "";
-
- if (!PQsendQueryStart(conn))
- return 0;
-
- /* This isn't gonna work on a 2.0 server */
- if (PG_PROTOCOL_MAJOR(conn->pversion) < 3)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("function requires at least protocol version 3.0\n"));
- return 0;
- }
-
- /* construct the Describe message */
- if (pqPutMsgStart('D', false, conn) < 0 ||
- pqPutc(desc_type, conn) < 0 ||
- pqPuts(desc_target, conn) < 0 ||
- pqPutMsgEnd(conn) < 0)
- goto sendFailed;
-
- /* construct the Sync message */
- if (pqPutMsgStart('S', false, conn) < 0 ||
- pqPutMsgEnd(conn) < 0)
- goto sendFailed;
-
- /* remember we are doing a Describe */
- conn->queryclass = PGQUERY_DESCRIBE;
-
- /* reset last-query string (not relevant now) */
- if (conn->last_query)
- {
- free(conn->last_query);
- conn->last_query = NULL;
- }
-
- /*
- * Give the data a push. In nonblock mode, don't complain if we're unable
- * to send it all; PQgetResult() will do any additional flushing needed.
- */
- if (pqFlush(conn) < 0)
- goto sendFailed;
-
- /* OK, it's launched! */
- conn->asyncStatus = PGASYNC_BUSY;
- return 1;
-
-sendFailed:
- pqHandleSendFailure(conn);
- return 0;
-}
-
-/*
- * PQnotifies
- * returns a PGnotify* structure of the latest async notification
- * that has not yet been handled
- *
- * returns NULL, if there is currently
- * no unhandled async notification from the backend
- *
- * the CALLER is responsible for FREE'ing the structure returned
- */
-PGnotify *
-PQnotifies(PGconn *conn)
-{
- PGnotify *event;
-
- if (!conn)
- return NULL;
-
- /* Parse any available data to see if we can extract NOTIFY messages. */
- parseInput(conn);
-
- event = conn->notifyHead;
- if (event)
- {
- conn->notifyHead = event->next;
- if (!conn->notifyHead)
- conn->notifyTail = NULL;
- event->next = NULL; /* don't let app see the internal state */
- }
- return event;
-}
-
-/*
- * PQputCopyData - send some data to the backend during COPY IN or COPY BOTH
- *
- * Returns 1 if successful, 0 if data could not be sent (only possible
- * in nonblock mode), or -1 if an error occurs.
- */
-int
-PQputCopyData(PGconn *conn, const char *buffer, int nbytes)
-{
- if (!conn)
- return -1;
- if (conn->asyncStatus != PGASYNC_COPY_IN &&
- conn->asyncStatus != PGASYNC_COPY_BOTH)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("no COPY in progress\n"));
- return -1;
- }
-
- /*
- * Process any NOTICE or NOTIFY messages that might be pending in the
- * input buffer. Since the server might generate many notices during the
- * COPY, we want to clean those out reasonably promptly to prevent
- * indefinite expansion of the input buffer. (Note: the actual read of
- * input data into the input buffer happens down inside pqSendSome, but
- * it's not authorized to get rid of the data again.)
- */
- parseInput(conn);
-
- if (nbytes > 0)
- {
- /*
- * Try to flush any previously sent data in preference to growing the
- * output buffer. If we can't enlarge the buffer enough to hold the
- * data, return 0 in the nonblock case, else hard error. (For
- * simplicity, always assume 5 bytes of overhead even in protocol 2.0
- * case.)
- */
- if ((conn->outBufSize - conn->outCount - 5) < nbytes)
- {
- if (pqFlush(conn) < 0)
- return -1;
- if (pqCheckOutBufferSpace(conn->outCount + 5 + (size_t) nbytes,
- conn))
- return pqIsnonblocking(conn) ? 0 : -1;
- }
- /* Send the data (too simple to delegate to fe-protocol files) */
- if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
- {
- if (pqPutMsgStart('d', false, conn) < 0 ||
- pqPutnchar(buffer, nbytes, conn) < 0 ||
- pqPutMsgEnd(conn) < 0)
- return -1;
- }
- else
- {
- if (pqPutMsgStart(0, false, conn) < 0 ||
- pqPutnchar(buffer, nbytes, conn) < 0 ||
- pqPutMsgEnd(conn) < 0)
- return -1;
- }
- }
- return 1;
-}
-
-/*
- * PQputCopyEnd - send EOF indication to the backend during COPY IN
- *
- * After calling this, use PQgetResult() to check command completion status.
- *
- * Returns 1 if successful, 0 if data could not be sent (only possible
- * in nonblock mode), or -1 if an error occurs.
- */
-int
-PQputCopyEnd(PGconn *conn, const char *errormsg)
-{
- if (!conn)
- return -1;
- if (conn->asyncStatus != PGASYNC_COPY_IN &&
- conn->asyncStatus != PGASYNC_COPY_BOTH)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("no COPY in progress\n"));
- return -1;
- }
-
- /*
- * Send the COPY END indicator. This is simple enough that we don't
- * bother delegating it to the fe-protocol files.
- */
- if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
- {
- if (errormsg)
- {
- /* Send COPY FAIL */
- if (pqPutMsgStart('f', false, conn) < 0 ||
- pqPuts(errormsg, conn) < 0 ||
- pqPutMsgEnd(conn) < 0)
- return -1;
- }
- else
- {
- /* Send COPY DONE */
- if (pqPutMsgStart('c', false, conn) < 0 ||
- pqPutMsgEnd(conn) < 0)
- return -1;
- }
-
- /*
- * If we sent the COPY command in extended-query mode, we must issue a
- * Sync as well.
- */
- if (conn->queryclass != PGQUERY_SIMPLE)
- {
- if (pqPutMsgStart('S', false, conn) < 0 ||
- pqPutMsgEnd(conn) < 0)
- return -1;
- }
- }
- else
- {
- if (errormsg)
- {
- /* Ooops, no way to do this in 2.0 */
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("function requires at least protocol version 3.0\n"));
- return -1;
- }
- else
- {
- /* Send old-style end-of-data marker */
- if (pqPutMsgStart(0, false, conn) < 0 ||
- pqPutnchar("\\.\n", 3, conn) < 0 ||
- pqPutMsgEnd(conn) < 0)
- return -1;
- }
- }
-
- /* Return to active duty */
- if (conn->asyncStatus == PGASYNC_COPY_BOTH)
- conn->asyncStatus = PGASYNC_COPY_OUT;
- else
- conn->asyncStatus = PGASYNC_BUSY;
- resetPQExpBuffer(&conn->errorMessage);
-
- /* Try to flush data */
- if (pqFlush(conn) < 0)
- return -1;
-
- return 1;
-}
-
-/*
- * PQgetCopyData - read a row of data from the backend during COPY OUT
- * or COPY BOTH
- *
- * If successful, sets *buffer to point to a malloc'd row of data, and
- * returns row length (always > 0) as result.
- * Returns 0 if no row available yet (only possible if async is true),
- * -1 if end of copy (consult PQgetResult), or -2 if error (consult
- * PQerrorMessage).
- */
-int
-PQgetCopyData(PGconn *conn, char **buffer, int async)
-{
- *buffer = NULL; /* for all failure cases */
- if (!conn)
- return -2;
- if (conn->asyncStatus != PGASYNC_COPY_OUT &&
- conn->asyncStatus != PGASYNC_COPY_BOTH)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("no COPY in progress\n"));
- return -2;
- }
- if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
- return pqGetCopyData3(conn, buffer, async);
- else
- return pqGetCopyData2(conn, buffer, async);
-}
-
-/*
- * PQgetline - gets a newline-terminated string from the backend.
- *
- * Chiefly here so that applications can use "COPY <rel> to stdout"
- * and read the output string. Returns a null-terminated string in s.
- *
- * XXX this routine is now deprecated, because it can't handle binary data.
- * If called during a COPY BINARY we return EOF.
- *
- * PQgetline reads up to maxlen-1 characters (like fgets(3)) but strips
- * the terminating \n (like gets(3)).
- *
- * CAUTION: the caller is responsible for detecting the end-of-copy signal
- * (a line containing just "\.") when using this routine.
- *
- * RETURNS:
- * EOF if error (eg, invalid arguments are given)
- * 0 if EOL is reached (i.e., \n has been read)
- * (this is required for backward-compatibility -- this
- * routine used to always return EOF or 0, assuming that
- * the line ended within maxlen bytes.)
- * 1 in other cases (i.e., the buffer was filled before \n is reached)
- */
-int
-PQgetline(PGconn *conn, char *s, int maxlen)
-{
- if (!s || maxlen <= 0)
- return EOF;
- *s = '\0';
- /* maxlen must be at least 3 to hold the \. terminator! */
- if (maxlen < 3)
- return EOF;
-
- if (!conn)
- return EOF;
-
- if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
- return pqGetline3(conn, s, maxlen);
- else
- return pqGetline2(conn, s, maxlen);
-}
-
-/*
- * PQgetlineAsync - gets a COPY data row without blocking.
- *
- * This routine is for applications that want to do "COPY <rel> to stdout"
- * asynchronously, that is without blocking. Having issued the COPY command
- * and gotten a PGRES_COPY_OUT response, the app should call PQconsumeInput
- * and this routine until the end-of-data signal is detected. Unlike
- * PQgetline, this routine takes responsibility for detecting end-of-data.
- *
- * On each call, PQgetlineAsync will return data if a complete data row
- * is available in libpq's input buffer. Otherwise, no data is returned
- * until the rest of the row arrives.
- *
- * If -1 is returned, the end-of-data signal has been recognized (and removed
- * from libpq's input buffer). The caller *must* next call PQendcopy and
- * then return to normal processing.
- *
- * RETURNS:
- * -1 if the end-of-copy-data marker has been recognized
- * 0 if no data is available
- * >0 the number of bytes returned.
- *
- * The data returned will not extend beyond a data-row boundary. If possible
- * a whole row will be returned at one time. But if the buffer offered by
- * the caller is too small to hold a row sent by the backend, then a partial
- * data row will be returned. In text mode this can be detected by testing
- * whether the last returned byte is '\n' or not.
- *
- * The returned data is *not* null-terminated.
- */
-
-int
-PQgetlineAsync(PGconn *conn, char *buffer, int bufsize)
-{
- if (!conn)
- return -1;
-
- if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
- return pqGetlineAsync3(conn, buffer, bufsize);
- else
- return pqGetlineAsync2(conn, buffer, bufsize);
-}
-
-/*
- * PQputline -- sends a string to the backend during COPY IN.
- * Returns 0 if OK, EOF if not.
- *
- * This is deprecated primarily because the return convention doesn't allow
- * caller to tell the difference between a hard error and a nonblock-mode
- * send failure.
- */
-int
-PQputline(PGconn *conn, const char *s)
-{
- return PQputnbytes(conn, s, strlen(s));
-}
-
-/*
- * PQputnbytes -- like PQputline, but buffer need not be null-terminated.
- * Returns 0 if OK, EOF if not.
- */
-int
-PQputnbytes(PGconn *conn, const char *buffer, int nbytes)
-{
- if (PQputCopyData(conn, buffer, nbytes) > 0)
- return 0;
- else
- return EOF;
-}
-
-/*
- * PQendcopy
- * After completing the data transfer portion of a copy in/out,
- * the application must call this routine to finish the command protocol.
- *
- * When using protocol 3.0 this is deprecated; it's cleaner to use PQgetResult
- * to get the transfer status. Note however that when using 2.0 protocol,
- * recovering from a copy failure often requires a PQreset. PQendcopy will
- * take care of that, PQgetResult won't.
- *
- * RETURNS:
- * 0 on success
- * 1 on failure
- */
-int
-PQendcopy(PGconn *conn)
-{
- if (!conn)
- return 0;
-
- if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
- return pqEndcopy3(conn);
- else
- return pqEndcopy2(conn);
-}
-
-
-/* ----------------
- * PQfn - Send a function call to the POSTGRES backend.
- *
- * conn : backend connection
- * fnid : OID of function to be called
- * result_buf : pointer to result buffer
- * result_len : actual length of result is returned here
- * result_is_int : If the result is an integer, this must be 1,
- * otherwise this should be 0
- * args : pointer to an array of function arguments
- * (each has length, if integer, and value/pointer)
- * nargs : # of arguments in args array.
- *
- * RETURNS
- * PGresult with status = PGRES_COMMAND_OK if successful.
- * *result_len is > 0 if there is a return value, 0 if not.
- * PGresult with status = PGRES_FATAL_ERROR if backend returns an error.
- * NULL on communications failure. conn->errorMessage will be set.
- * ----------------
- */
-
-PGresult *
-PQfn(PGconn *conn,
- int fnid,
- int *result_buf,
- int *result_len,
- int result_is_int,
- const PQArgBlock *args,
- int nargs)
-{
- *result_len = 0;
-
- if (!conn)
- return NULL;
-
- /* clear the error string */
- resetPQExpBuffer(&conn->errorMessage);
-
- if (conn->sock == PGINVALID_SOCKET || conn->asyncStatus != PGASYNC_IDLE ||
- conn->result != NULL)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("connection in wrong state\n"));
- return NULL;
- }
-
- if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
- return pqFunctionCall3(conn, fnid,
- result_buf, result_len,
- result_is_int,
- args, nargs);
- else
- return pqFunctionCall2(conn, fnid,
- result_buf, result_len,
- result_is_int,
- args, nargs);
-}
-
-
-/* ====== accessor funcs for PGresult ======== */
-
-ExecStatusType
-PQresultStatus(const PGresult *res)
-{
- if (!res)
- return PGRES_FATAL_ERROR;
- return res->resultStatus;
-}
-
-char *
-PQresStatus(ExecStatusType status)
-{
- if ((unsigned int) status >= sizeof pgresStatus / sizeof pgresStatus[0])
- return libpq_gettext("invalid ExecStatusType code");
- return pgresStatus[status];
-}
-
-char *
-PQresultErrorMessage(const PGresult *res)
-{
- if (!res || !res->errMsg)
- return "";
- return res->errMsg;
-}
-
-char *
-PQresultVerboseErrorMessage(const PGresult *res,
- PGVerbosity verbosity,
- PGContextVisibility show_context)
-{
- PQExpBufferData workBuf;
-
- /*
- * Because the caller is expected to free the result string, we must
- * strdup any constant result. We use plain strdup and document that
- * callers should expect NULL if out-of-memory.
- */
- if (!res ||
- (res->resultStatus != PGRES_FATAL_ERROR &&
- res->resultStatus != PGRES_NONFATAL_ERROR))
- return strdup(libpq_gettext("PGresult is not an error result\n"));
-
- initPQExpBuffer(&workBuf);
-
- /*
- * Currently, we pass this off to fe-protocol3.c in all cases; it will
- * behave reasonably sanely with an error reported by fe-protocol2.c as
- * well. If necessary, we could record the protocol version in PGresults
- * so as to be able to invoke a version-specific message formatter, but
- * for now there's no need.
- */
- pqBuildErrorMessage3(&workBuf, res, verbosity, show_context);
-
- /* If insufficient memory to format the message, fail cleanly */
- if (PQExpBufferDataBroken(workBuf))
- {
- termPQExpBuffer(&workBuf);
- return strdup(libpq_gettext("out of memory\n"));
- }
-
- return workBuf.data;
-}
-
-char *
-PQresultErrorField(const PGresult *res, int fieldcode)
-{
- PGMessageField *pfield;
-
- if (!res)
- return NULL;
- for (pfield = res->errFields; pfield != NULL; pfield = pfield->next)
- {
- if (pfield->code == fieldcode)
- return pfield->contents;
- }
- return NULL;
-}
-
-int
-PQntuples(const PGresult *res)
-{
- if (!res)
- return 0;
- return res->ntups;
-}
-
-int
-PQnfields(const PGresult *res)
-{
- if (!res)
- return 0;
- return res->numAttributes;
-}
-
-int
-PQbinaryTuples(const PGresult *res)
-{
- if (!res)
- return 0;
- return res->binary;
-}
-
-/*
- * Helper routines to range-check field numbers and tuple numbers.
- * Return TRUE if OK, FALSE if not
- */
-
-static int
-check_field_number(const PGresult *res, int field_num)
-{
- if (!res)
- return FALSE; /* no way to display error message... */
- if (field_num < 0 || field_num >= res->numAttributes)
- {
- pqInternalNotice(&res->noticeHooks,
- "column number %d is out of range 0..%d",
- field_num, res->numAttributes - 1);
- return FALSE;
- }
- return TRUE;
-}
-
-static int
-check_tuple_field_number(const PGresult *res,
- int tup_num, int field_num)
-{
- if (!res)
- return FALSE; /* no way to display error message... */
- if (tup_num < 0 || tup_num >= res->ntups)
- {
- pqInternalNotice(&res->noticeHooks,
- "row number %d is out of range 0..%d",
- tup_num, res->ntups - 1);
- return FALSE;
- }
- if (field_num < 0 || field_num >= res->numAttributes)
- {
- pqInternalNotice(&res->noticeHooks,
- "column number %d is out of range 0..%d",
- field_num, res->numAttributes - 1);
- return FALSE;
- }
- return TRUE;
-}
-
-static int
-check_param_number(const PGresult *res, int param_num)
-{
- if (!res)
- return FALSE; /* no way to display error message... */
- if (param_num < 0 || param_num >= res->numParameters)
- {
- pqInternalNotice(&res->noticeHooks,
- "parameter number %d is out of range 0..%d",
- param_num, res->numParameters - 1);
- return FALSE;
- }
-
- return TRUE;
-}
-
-/*
- * returns NULL if the field_num is invalid
- */
-char *
-PQfname(const PGresult *res, int field_num)
-{
- if (!check_field_number(res, field_num))
- return NULL;
- if (res->attDescs)
- return res->attDescs[field_num].name;
- else
- return NULL;
-}
-
-/*
- * PQfnumber: find column number given column name
- *
- * The column name is parsed as if it were in a SQL statement, including
- * case-folding and double-quote processing. But note a possible gotcha:
- * downcasing in the frontend might follow different locale rules than
- * downcasing in the backend...
- *
- * Returns -1 if no match. In the present backend it is also possible
- * to have multiple matches, in which case the first one is found.
- */
-int
-PQfnumber(const PGresult *res, const char *field_name)
-{
- char *field_case;
- bool in_quotes;
- bool all_lower = true;
- const char *iptr;
- char *optr;
- int i;
-
- if (!res)
- return -1;
-
- /*
- * Note: it is correct to reject a zero-length input string; the proper
- * input to match a zero-length field name would be "".
- */
- if (field_name == NULL ||
- field_name[0] == '\0' ||
- res->attDescs == NULL)
- return -1;
-
- /*
- * Check if we can avoid the strdup() and related work because the
- * passed-in string wouldn't be changed before we do the check anyway.
- */
- for (iptr = field_name; *iptr; iptr++)
- {
- char c = *iptr;
-
- if (c == '"' || c != pg_tolower((unsigned char) c))
- {
- all_lower = false;
- break;
- }
- }
-
- if (all_lower)
- for (i = 0; i < res->numAttributes; i++)
- if (strcmp(field_name, res->attDescs[i].name) == 0)
- return i;
-
- /* Fall through to the normal check if that didn't work out. */
-
- /*
- * Note: this code will not reject partially quoted strings, eg
- * foo"BAR"foo will become fooBARfoo when it probably ought to be an error
- * condition.
- */
- field_case = strdup(field_name);
- if (field_case == NULL)
- return -1; /* grotty */
-
- in_quotes = false;
- optr = field_case;
- for (iptr = field_case; *iptr; iptr++)
- {
- char c = *iptr;
-
- if (in_quotes)
- {
- if (c == '"')
- {
- if (iptr[1] == '"')
- {
- /* doubled quotes become a single quote */
- *optr++ = '"';
- iptr++;
- }
- else
- in_quotes = false;
- }
- else
- *optr++ = c;
- }
- else if (c == '"')
- in_quotes = true;
- else
- {
- c = pg_tolower((unsigned char) c);
- *optr++ = c;
- }
- }
- *optr = '\0';
-
- for (i = 0; i < res->numAttributes; i++)
- {
- if (strcmp(field_case, res->attDescs[i].name) == 0)
- {
- free(field_case);
- return i;
- }
- }
- free(field_case);
- return -1;
-}
-
-Oid
-PQftable(const PGresult *res, int field_num)
-{
- if (!check_field_number(res, field_num))
- return InvalidOid;
- if (res->attDescs)
- return res->attDescs[field_num].tableid;
- else
- return InvalidOid;
-}
-
-int
-PQftablecol(const PGresult *res, int field_num)
-{
- if (!check_field_number(res, field_num))
- return 0;
- if (res->attDescs)
- return res->attDescs[field_num].columnid;
- else
- return 0;
-}
-
-int
-PQfformat(const PGresult *res, int field_num)
-{
- if (!check_field_number(res, field_num))
- return 0;
- if (res->attDescs)
- return res->attDescs[field_num].format;
- else
- return 0;
-}
-
-Oid
-PQftype(const PGresult *res, int field_num)
-{
- if (!check_field_number(res, field_num))
- return InvalidOid;
- if (res->attDescs)
- return res->attDescs[field_num].typid;
- else
- return InvalidOid;
-}
-
-int
-PQfsize(const PGresult *res, int field_num)
-{
- if (!check_field_number(res, field_num))
- return 0;
- if (res->attDescs)
- return res->attDescs[field_num].typlen;
- else
- return 0;
-}
-
-int
-PQfmod(const PGresult *res, int field_num)
-{
- if (!check_field_number(res, field_num))
- return 0;
- if (res->attDescs)
- return res->attDescs[field_num].atttypmod;
- else
- return 0;
-}
-
-char *
-PQcmdStatus(PGresult *res)
-{
- if (!res)
- return NULL;
- return res->cmdStatus;
-}
-
-/*
- * PQoidStatus -
- * if the last command was an INSERT, return the oid string
- * if not, return ""
- */
-char *
-PQoidStatus(const PGresult *res)
-{
- /*
- * This must be enough to hold the result. Don't laugh, this is better
- * than what this function used to do.
- */
- static char buf[24];
-
- size_t len;
-
- if (!res || strncmp(res->cmdStatus, "INSERT ", 7) != 0)
- return "";
-
- len = strspn(res->cmdStatus + 7, "0123456789");
- if (len > sizeof(buf) - 1)
- len = sizeof(buf) - 1;
- memcpy(buf, res->cmdStatus + 7, len);
- buf[len] = '\0';
-
- return buf;
-}
-
-/*
- * PQoidValue -
- * a perhaps preferable form of the above which just returns
- * an Oid type
- */
-Oid
-PQoidValue(const PGresult *res)
-{
- char *endptr = NULL;
- unsigned long result;
-
- if (!res ||
- strncmp(res->cmdStatus, "INSERT ", 7) != 0 ||
- res->cmdStatus[7] < '0' ||
- res->cmdStatus[7] > '9')
- return InvalidOid;
-
- result = strtoul(res->cmdStatus + 7, &endptr, 10);
-
- if (!endptr || (*endptr != ' ' && *endptr != '\0'))
- return InvalidOid;
- else
- return (Oid) result;
-}
-
-
-/*
- * PQcmdTuples -
- * If the last command was INSERT/UPDATE/DELETE/MOVE/FETCH/COPY, return
- * a string containing the number of inserted/affected tuples. If not,
- * return "".
- *
- * XXX: this should probably return an int
- */
-char *
-PQcmdTuples(PGresult *res)
-{
- char *p,
- *c;
-
- if (!res)
- return "";
-
- if (strncmp(res->cmdStatus, "INSERT ", 7) == 0)
- {
- p = res->cmdStatus + 7;
- /* INSERT: skip oid and space */
- while (*p && *p != ' ')
- p++;
- if (*p == 0)
- goto interpret_error; /* no space? */
- p++;
- }
- else if (strncmp(res->cmdStatus, "SELECT ", 7) == 0 ||
- strncmp(res->cmdStatus, "DELETE ", 7) == 0 ||
- strncmp(res->cmdStatus, "UPDATE ", 7) == 0)
- p = res->cmdStatus + 7;
- else if (strncmp(res->cmdStatus, "FETCH ", 6) == 0)
- p = res->cmdStatus + 6;
- else if (strncmp(res->cmdStatus, "MOVE ", 5) == 0 ||
- strncmp(res->cmdStatus, "COPY ", 5) == 0)
- p = res->cmdStatus + 5;
- else
- return "";
-
- /* check that we have an integer (at least one digit, nothing else) */
- for (c = p; *c; c++)
- {
- if (!isdigit((unsigned char) *c))
- goto interpret_error;
- }
- if (c == p)
- goto interpret_error;
-
- return p;
-
-interpret_error:
- pqInternalNotice(&res->noticeHooks,
- "could not interpret result from server: %s",
- res->cmdStatus);
- return "";
-}
-
-/*
- * PQgetvalue:
- * return the value of field 'field_num' of row 'tup_num'
- */
-char *
-PQgetvalue(const PGresult *res, int tup_num, int field_num)
-{
- if (!check_tuple_field_number(res, tup_num, field_num))
- return NULL;
- return res->tuples[tup_num][field_num].value;
-}
-
-/* PQgetlength:
- * returns the actual length of a field value in bytes.
- */
-int
-PQgetlength(const PGresult *res, int tup_num, int field_num)
-{
- if (!check_tuple_field_number(res, tup_num, field_num))
- return 0;
- if (res->tuples[tup_num][field_num].len != NULL_LEN)
- return res->tuples[tup_num][field_num].len;
- else
- return 0;
-}
-
-/* PQgetisnull:
- * returns the null status of a field value.
- */
-int
-PQgetisnull(const PGresult *res, int tup_num, int field_num)
-{
- if (!check_tuple_field_number(res, tup_num, field_num))
- return 1; /* pretend it is null */
- if (res->tuples[tup_num][field_num].len == NULL_LEN)
- return 1;
- else
- return 0;
-}
-
-/* PQnparams:
- * returns the number of input parameters of a prepared statement.
- */
-int
-PQnparams(const PGresult *res)
-{
- if (!res)
- return 0;
- return res->numParameters;
-}
-
-/* PQparamtype:
- * returns type Oid of the specified statement parameter.
- */
-Oid
-PQparamtype(const PGresult *res, int param_num)
-{
- if (!check_param_number(res, param_num))
- return InvalidOid;
- if (res->paramDescs)
- return res->paramDescs[param_num].typid;
- else
- return InvalidOid;
-}
-
-
-/* PQsetnonblocking:
- * sets the PGconn's database connection non-blocking if the arg is TRUE
- * or makes it blocking if the arg is FALSE, this will not protect
- * you from PQexec(), you'll only be safe when using the non-blocking API.
- * Needs to be called only on a connected database connection.
- */
-int
-PQsetnonblocking(PGconn *conn, int arg)
-{
- bool barg;
-
- if (!conn || conn->status == CONNECTION_BAD)
- return -1;
-
- barg = (arg ? TRUE : FALSE);
-
- /* early out if the socket is already in the state requested */
- if (barg == conn->nonblocking)
- return 0;
-
- /*
- * to guarantee constancy for flushing/query/result-polling behavior we
- * need to flush the send queue at this point in order to guarantee proper
- * behavior. this is ok because either they are making a transition _from_
- * or _to_ blocking mode, either way we can block them.
- */
- /* if we are going from blocking to non-blocking flush here */
- if (pqFlush(conn))
- return -1;
-
- conn->nonblocking = barg;
-
- return 0;
-}
-
-/*
- * return the blocking status of the database connection
- * TRUE == nonblocking, FALSE == blocking
- */
-int
-PQisnonblocking(const PGconn *conn)
-{
- return pqIsnonblocking(conn);
-}
-
-/* libpq is thread-safe? */
-int
-PQisthreadsafe(void)
-{
-#ifdef ENABLE_THREAD_SAFETY
- return true;
-#else
- return false;
-#endif
-}
-
-
-/* try to force data out, really only useful for non-blocking users */
-int
-PQflush(PGconn *conn)
-{
- return pqFlush(conn);
-}
-
-
-/*
- * PQfreemem - safely frees memory allocated
- *
- * Needed mostly by Win32, unless multithreaded DLL (/MD in VC6)
- * Used for freeing memory from PQescapeByte()a/PQunescapeBytea()
- */
-void
-PQfreemem(void *ptr)
-{
- free(ptr);
-}
-
-/*
- * PQfreeNotify - free's the memory associated with a PGnotify
- *
- * This function is here only for binary backward compatibility.
- * New code should use PQfreemem(). A macro will automatically map
- * calls to PQfreemem. It should be removed in the future. bjm 2003-03-24
- */
-
-#undef PQfreeNotify
-void PQfreeNotify(PGnotify *notify);
-
-void
-PQfreeNotify(PGnotify *notify)
-{
- PQfreemem(notify);
-}
-
-
-/*
- * Escaping arbitrary strings to get valid SQL literal strings.
- *
- * Replaces "'" with "''", and if not std_strings, replaces "\" with "\\".
- *
- * length is the length of the source string. (Note: if a terminating NUL
- * is encountered sooner, PQescapeString stops short of "length"; the behavior
- * is thus rather like strncpy.)
- *
- * For safety the buffer at "to" must be at least 2*length + 1 bytes long.
- * A terminating NUL character is added to the output string, whether the
- * input is NUL-terminated or not.
- *
- * Returns the actual length of the output (not counting the terminating NUL).
- */
-static size_t
-PQescapeStringInternal(PGconn *conn,
- char *to, const char *from, size_t length,
- int *error,
- int encoding, bool std_strings)
-{
- const char *source = from;
- char *target = to;
- size_t remaining = length;
-
- if (error)
- *error = 0;
-
- while (remaining > 0 && *source != '\0')
- {
- char c = *source;
- int len;
- int i;
-
- /* Fast path for plain ASCII */
- if (!IS_HIGHBIT_SET(c))
- {
- /* Apply quoting if needed */
- if (SQL_STR_DOUBLE(c, !std_strings))
- *target++ = c;
- /* Copy the character */
- *target++ = c;
- source++;
- remaining--;
- continue;
- }
-
- /* Slow path for possible multibyte characters */
- len = pg_encoding_mblen(encoding, source);
-
- /* Copy the character */
- for (i = 0; i < len; i++)
- {
- if (remaining == 0 || *source == '\0')
- break;
- *target++ = *source++;
- remaining--;
- }
-
- /*
- * If we hit premature end of string (ie, incomplete multibyte
- * character), try to pad out to the correct length with spaces. We
- * may not be able to pad completely, but we will always be able to
- * insert at least one pad space (since we'd not have quoted a
- * multibyte character). This should be enough to make a string that
- * the server will error out on.
- */
- if (i < len)
- {
- if (error)
- *error = 1;
- if (conn)
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("incomplete multibyte character\n"));
- for (; i < len; i++)
- {
- if (((size_t) (target - to)) / 2 >= length)
- break;
- *target++ = ' ';
- }
- break;
- }
- }
-
- /* Write the terminating NUL character. */
- *target = '\0';
-
- return target - to;
-}
-
-size_t
-PQescapeStringConn(PGconn *conn,
- char *to, const char *from, size_t length,
- int *error)
-{
- if (!conn)
- {
- /* force empty-string result */
- *to = '\0';
- if (error)
- *error = 1;
- return 0;
- }
- return PQescapeStringInternal(conn, to, from, length, error,
- conn->client_encoding,
- conn->std_strings);
-}
-
-size_t
-PQescapeString(char *to, const char *from, size_t length)
-{
- return PQescapeStringInternal(NULL, to, from, length, NULL,
- static_client_encoding,
- static_std_strings);
-}
-
-
-/*
- * Escape arbitrary strings. If as_ident is true, we escape the result
- * as an identifier; if false, as a literal. The result is returned in
- * a newly allocated buffer. If we fail due to an encoding violation or out
- * of memory condition, we return NULL, storing an error message into conn.
- */
-static char *
-PQescapeInternal(PGconn *conn, const char *str, size_t len, bool as_ident)
-{
- const char *s;
- char *result;
- char *rp;
- int num_quotes = 0; /* single or double, depending on as_ident */
- int num_backslashes = 0;
- int input_len;
- int result_size;
- char quote_char = as_ident ? '"' : '\'';
-
- /* We must have a connection, else fail immediately. */
- if (!conn)
- return NULL;
-
- /* Scan the string for characters that must be escaped. */
- for (s = str; (s - str) < len && *s != '\0'; ++s)
- {
- if (*s == quote_char)
- ++num_quotes;
- else if (*s == '\\')
- ++num_backslashes;
- else if (IS_HIGHBIT_SET(*s))
- {
- int charlen;
-
- /* Slow path for possible multibyte characters */
- charlen = pg_encoding_mblen(conn->client_encoding, s);
-
- /* Multibyte character overruns allowable length. */
- if ((s - str) + charlen > len || memchr(s, 0, charlen) != NULL)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("incomplete multibyte character\n"));
- return NULL;
- }
-
- /* Adjust s, bearing in mind that for loop will increment it. */
- s += charlen - 1;
- }
- }
-
- /* Allocate output buffer. */
- input_len = s - str;
- result_size = input_len + num_quotes + 3; /* two quotes, plus a NUL */
- if (!as_ident && num_backslashes > 0)
- result_size += num_backslashes + 2;
- result = rp = (char *) malloc(result_size);
- if (rp == NULL)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("out of memory\n"));
- return NULL;
- }
-
- /*
- * If we are escaping a literal that contains backslashes, we use the
- * escape string syntax so that the result is correct under either value
- * of standard_conforming_strings. We also emit a leading space in this
- * case, to guard against the possibility that the result might be
- * interpolated immediately following an identifier.
- */
- if (!as_ident && num_backslashes > 0)
- {
- *rp++ = ' ';
- *rp++ = 'E';
- }
-
- /* Opening quote. */
- *rp++ = quote_char;
-
- /*
- * Use fast path if possible.
- *
- * We've already verified that the input string is well-formed in the
- * current encoding. If it contains no quotes and, in the case of
- * literal-escaping, no backslashes, then we can just copy it directly to
- * the output buffer, adding the necessary quotes.
- *
- * If not, we must rescan the input and process each character
- * individually.
- */
- if (num_quotes == 0 && (num_backslashes == 0 || as_ident))
- {
- memcpy(rp, str, input_len);
- rp += input_len;
- }
- else
- {
- for (s = str; s - str < input_len; ++s)
- {
- if (*s == quote_char || (!as_ident && *s == '\\'))
- {
- *rp++ = *s;
- *rp++ = *s;
- }
- else if (!IS_HIGHBIT_SET(*s))
- *rp++ = *s;
- else
- {
- int i = pg_encoding_mblen(conn->client_encoding, s);
-
- while (1)
- {
- *rp++ = *s;
- if (--i == 0)
- break;
- ++s; /* for loop will provide the final increment */
- }
- }
- }
- }
-
- /* Closing quote and terminating NUL. */
- *rp++ = quote_char;
- *rp = '\0';
-
- return result;
-}
-
-char *
-PQescapeLiteral(PGconn *conn, const char *str, size_t len)
-{
- return PQescapeInternal(conn, str, len, false);
-}
-
-char *
-PQescapeIdentifier(PGconn *conn, const char *str, size_t len)
-{
- return PQescapeInternal(conn, str, len, true);
-}
-
-/* HEX encoding support for bytea */
-static const char hextbl[] = "0123456789abcdef";
-
-static const int8 hexlookup[128] = {
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
- -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-};
-
-static inline char
-get_hex(char c)
-{
- int res = -1;
-
- if (c > 0 && c < 127)
- res = hexlookup[(unsigned char) c];
-
- return (char) res;
-}
-
-
-/*
- * PQescapeBytea - converts from binary string to the
- * minimal encoding necessary to include the string in an SQL
- * INSERT statement with a bytea type column as the target.
- *
- * We can use either hex or escape (traditional) encoding.
- * In escape mode, the following transformations are applied:
- * '\0' == ASCII 0 == \000
- * '\'' == ASCII 39 == ''
- * '\\' == ASCII 92 == \\
- * anything < 0x20, or > 0x7e ---> \ooo
- * (where ooo is an octal expression)
- *
- * If not std_strings, all backslashes sent to the output are doubled.
- */
-static unsigned char *
-PQescapeByteaInternal(PGconn *conn,
- const unsigned char *from, size_t from_length,
- size_t *to_length, bool std_strings, bool use_hex)
-{
- const unsigned char *vp;
- unsigned char *rp;
- unsigned char *result;
- size_t i;
- size_t len;
- size_t bslash_len = (std_strings ? 1 : 2);
-
- /*
- * empty string has 1 char ('\0')
- */
- len = 1;
-
- if (use_hex)
- {
- len += bslash_len + 1 + 2 * from_length;
- }
- else
- {
- vp = from;
- for (i = from_length; i > 0; i--, vp++)
- {
- if (*vp < 0x20 || *vp > 0x7e)
- len += bslash_len + 3;
- else if (*vp == '\'')
- len += 2;
- else if (*vp == '\\')
- len += bslash_len + bslash_len;
- else
- len++;
- }
- }
-
- *to_length = len;
- rp = result = (unsigned char *) malloc(len);
- if (rp == NULL)
- {
- if (conn)
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("out of memory\n"));
- return NULL;
- }
-
- if (use_hex)
- {
- if (!std_strings)
- *rp++ = '\\';
- *rp++ = '\\';
- *rp++ = 'x';
- }
-
- vp = from;
- for (i = from_length; i > 0; i--, vp++)
- {
- unsigned char c = *vp;
-
- if (use_hex)
- {
- *rp++ = hextbl[(c >> 4) & 0xF];
- *rp++ = hextbl[c & 0xF];
- }
- else if (c < 0x20 || c > 0x7e)
- {
- if (!std_strings)
- *rp++ = '\\';
- *rp++ = '\\';
- *rp++ = (c >> 6) + '0';
- *rp++ = ((c >> 3) & 07) + '0';
- *rp++ = (c & 07) + '0';
- }
- else if (c == '\'')
- {
- *rp++ = '\'';
- *rp++ = '\'';
- }
- else if (c == '\\')
- {
- if (!std_strings)
- {
- *rp++ = '\\';
- *rp++ = '\\';
- }
- *rp++ = '\\';
- *rp++ = '\\';
- }
- else
- *rp++ = c;
- }
- *rp = '\0';
-
- return result;
-}
-
-unsigned char *
-PQescapeByteaConn(PGconn *conn,
- const unsigned char *from, size_t from_length,
- size_t *to_length)
-{
- if (!conn)
- return NULL;
- return PQescapeByteaInternal(conn, from, from_length, to_length,
- conn->std_strings,
- (conn->sversion >= 90000));
-}
-
-unsigned char *
-PQescapeBytea(const unsigned char *from, size_t from_length, size_t *to_length)
-{
- return PQescapeByteaInternal(NULL, from, from_length, to_length,
- static_std_strings,
- false /* can't use hex */ );
-}
-
-
-#define ISFIRSTOCTDIGIT(CH) ((CH) >= '0' && (CH) <= '3')
-#define ISOCTDIGIT(CH) ((CH) >= '0' && (CH) <= '7')
-#define OCTVAL(CH) ((CH) - '0')
-
-/*
- * PQunescapeBytea - converts the null terminated string representation
- * of a bytea, strtext, into binary, filling a buffer. It returns a
- * pointer to the buffer (or NULL on error), and the size of the
- * buffer in retbuflen. The pointer may subsequently be used as an
- * argument to the function PQfreemem.
- *
- * The following transformations are made:
- * \\ == ASCII 92 == \
- * \ooo == a byte whose value = ooo (ooo is an octal number)
- * \x == x (x is any character not matched by the above transformations)
- */
-unsigned char *
-PQunescapeBytea(const unsigned char *strtext, size_t *retbuflen)
-{
- size_t strtextlen,
- buflen;
- unsigned char *buffer,
- *tmpbuf;
- size_t i,
- j;
-
- if (strtext == NULL)
- return NULL;
-
- strtextlen = strlen((const char *) strtext);
-
- if (strtext[0] == '\\' && strtext[1] == 'x')
- {
- const unsigned char *s;
- unsigned char *p;
-
- buflen = (strtextlen - 2) / 2;
- /* Avoid unportable malloc(0) */
- buffer = (unsigned char *) malloc(buflen > 0 ? buflen : 1);
- if (buffer == NULL)
- return NULL;
-
- s = strtext + 2;
- p = buffer;
- while (*s)
- {
- char v1,
- v2;
-
- /*
- * Bad input is silently ignored. Note that this includes
- * whitespace between hex pairs, which is allowed by byteain.
- */
- v1 = get_hex(*s++);
- if (!*s || v1 == (char) -1)
- continue;
- v2 = get_hex(*s++);
- if (v2 != (char) -1)
- *p++ = (v1 << 4) | v2;
- }
-
- buflen = p - buffer;
- }
- else
- {
- /*
- * Length of input is max length of output, but add one to avoid
- * unportable malloc(0) if input is zero-length.
- */
- buffer = (unsigned char *) malloc(strtextlen + 1);
- if (buffer == NULL)
- return NULL;
-
- for (i = j = 0; i < strtextlen;)
- {
- switch (strtext[i])
- {
- case '\\':
- i++;
- if (strtext[i] == '\\')
- buffer[j++] = strtext[i++];
- else
- {
- if ((ISFIRSTOCTDIGIT(strtext[i])) &&
- (ISOCTDIGIT(strtext[i + 1])) &&
- (ISOCTDIGIT(strtext[i + 2])))
- {
- int byte;
-
- byte = OCTVAL(strtext[i++]);
- byte = (byte << 3) + OCTVAL(strtext[i++]);
- byte = (byte << 3) + OCTVAL(strtext[i++]);
- buffer[j++] = byte;
- }
- }
-
- /*
- * Note: if we see '\' followed by something that isn't a
- * recognized escape sequence, we loop around having done
- * nothing except advance i. Therefore the something will
- * be emitted as ordinary data on the next cycle. Corner
- * case: '\' at end of string will just be discarded.
- */
- break;
-
- default:
- buffer[j++] = strtext[i++];
- break;
- }
- }
- buflen = j; /* buflen is the length of the dequoted data */
- }
-
- /* Shrink the buffer to be no larger than necessary */
- /* +1 avoids unportable behavior when buflen==0 */
- tmpbuf = realloc(buffer, buflen + 1);
-
- /* It would only be a very brain-dead realloc that could fail, but... */
- if (!tmpbuf)
- {
- free(buffer);
- return NULL;
- }
-
- *retbuflen = buflen;
- return tmpbuf;
-}
diff --git a/libpq/fe-lobj.c b/libpq/fe-lobj.c
deleted file mode 100644
index 3b08768..0000000
--- a/libpq/fe-lobj.c
+++ /dev/null
@@ -1,1103 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * fe-lobj.c
- * Front-end large object interface
- *
- * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- *
- * IDENTIFICATION
- * src/interfaces/libpq/fe-lobj.c
- *
- *-------------------------------------------------------------------------
- */
-
-#ifdef WIN32
-/*
- * As unlink/rename are #define'd in port.h (via postgres_fe.h), io.h
- * must be included first on MS C. Might as well do it for all WIN32's
- * here.
- */
-#include <io.h>
-#endif
-
-#include "postgres_fe.h"
-
-#ifdef WIN32
-#include "win32.h"
-#else
-#include <unistd.h>
-#endif
-
-#include <fcntl.h>
-#include <limits.h>
-#include <sys/stat.h>
-#include <netinet/in.h> /* for ntohl/htonl */
-#include <arpa/inet.h>
-
-#include "libpq-fe.h"
-#include "libpq-int.h"
-#include "libpq/libpq-fs.h" /* must come after sys/stat.h */
-
-#define LO_BUFSIZE 8192
-
-static int lo_initialize(PGconn *conn);
-static Oid lo_import_internal(PGconn *conn, const char *filename, Oid oid);
-static pg_int64 lo_hton64(pg_int64 host64);
-static pg_int64 lo_ntoh64(pg_int64 net64);
-
-/*
- * lo_open
- * opens an existing large object
- *
- * returns the file descriptor for use in later lo_* calls
- * return -1 upon failure.
- */
-int
-lo_open(PGconn *conn, Oid lobjId, int mode)
-{
- int fd;
- int result_len;
- PQArgBlock argv[2];
- PGresult *res;
-
- if (conn == NULL || conn->lobjfuncs == NULL)
- {
- if (lo_initialize(conn) < 0)
- return -1;
- }
-
- argv[0].isint = 1;
- argv[0].len = 4;
- argv[0].u.integer = lobjId;
-
- argv[1].isint = 1;
- argv[1].len = 4;
- argv[1].u.integer = mode;
-
- res = PQfn(conn, conn->lobjfuncs->fn_lo_open, &fd, &result_len, 1, argv, 2);
- if (PQresultStatus(res) == PGRES_COMMAND_OK)
- {
- PQclear(res);
- return fd;
- }
- else
- {
- PQclear(res);
- return -1;
- }
-}
-
-/*
- * lo_close
- * closes an existing large object
- *
- * returns 0 upon success
- * returns -1 upon failure.
- */
-int
-lo_close(PGconn *conn, int fd)
-{
- PQArgBlock argv[1];
- PGresult *res;
- int retval;
- int result_len;
-
- if (conn == NULL || conn->lobjfuncs == NULL)
- {
- if (lo_initialize(conn) < 0)
- return -1;
- }
-
- argv[0].isint = 1;
- argv[0].len = 4;
- argv[0].u.integer = fd;
- res = PQfn(conn, conn->lobjfuncs->fn_lo_close,
- &retval, &result_len, 1, argv, 1);
- if (PQresultStatus(res) == PGRES_COMMAND_OK)
- {
- PQclear(res);
- return retval;
- }
- else
- {
- PQclear(res);
- return -1;
- }
-}
-
-/*
- * lo_truncate
- * truncates an existing large object to the given size
- *
- * returns 0 upon success
- * returns -1 upon failure
- */
-int
-lo_truncate(PGconn *conn, int fd, size_t len)
-{
- PQArgBlock argv[2];
- PGresult *res;
- int retval;
- int result_len;
-
- if (conn == NULL || conn->lobjfuncs == NULL)
- {
- if (lo_initialize(conn) < 0)
- return -1;
- }
-
- /* Must check this on-the-fly because it's not there pre-8.3 */
- if (conn->lobjfuncs->fn_lo_truncate == 0)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("cannot determine OID of function lo_truncate\n"));
- return -1;
- }
-
- /*
- * Long ago, somebody thought it'd be a good idea to declare this function
- * as taking size_t ... but the underlying backend function only accepts a
- * signed int32 length. So throw error if the given value overflows
- * int32. (A possible alternative is to automatically redirect the call
- * to lo_truncate64; but if the caller wanted to rely on that backend
- * function being available, he could have called lo_truncate64 for
- * himself.)
- */
- if (len > (size_t) INT_MAX)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("argument of lo_truncate exceeds integer range\n"));
- return -1;
- }
-
- argv[0].isint = 1;
- argv[0].len = 4;
- argv[0].u.integer = fd;
-
- argv[1].isint = 1;
- argv[1].len = 4;
- argv[1].u.integer = (int) len;
-
- res = PQfn(conn, conn->lobjfuncs->fn_lo_truncate,
- &retval, &result_len, 1, argv, 2);
-
- if (PQresultStatus(res) == PGRES_COMMAND_OK)
- {
- PQclear(res);
- return retval;
- }
- else
- {
- PQclear(res);
- return -1;
- }
-}
-
-/*
- * lo_truncate64
- * truncates an existing large object to the given size
- *
- * returns 0 upon success
- * returns -1 upon failure
- */
-int
-lo_truncate64(PGconn *conn, int fd, pg_int64 len)
-{
- PQArgBlock argv[2];
- PGresult *res;
- int retval;
- int result_len;
-
- if (conn == NULL || conn->lobjfuncs == NULL)
- {
- if (lo_initialize(conn) < 0)
- return -1;
- }
-
- if (conn->lobjfuncs->fn_lo_truncate64 == 0)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("cannot determine OID of function lo_truncate64\n"));
- return -1;
- }
-
- argv[0].isint = 1;
- argv[0].len = 4;
- argv[0].u.integer = fd;
-
- len = lo_hton64(len);
- argv[1].isint = 0;
- argv[1].len = 8;
- argv[1].u.ptr = (int *) &len;
-
- res = PQfn(conn, conn->lobjfuncs->fn_lo_truncate64,
- &retval, &result_len, 1, argv, 2);
-
- if (PQresultStatus(res) == PGRES_COMMAND_OK)
- {
- PQclear(res);
- return retval;
- }
- else
- {
- PQclear(res);
- return -1;
- }
-}
-
-/*
- * lo_read
- * read len bytes of the large object into buf
- *
- * returns the number of bytes read, or -1 on failure.
- * the CALLER must have allocated enough space to hold the result returned
- */
-
-int
-lo_read(PGconn *conn, int fd, char *buf, size_t len)
-{
- PQArgBlock argv[2];
- PGresult *res;
- int result_len;
-
- if (conn == NULL || conn->lobjfuncs == NULL)
- {
- if (lo_initialize(conn) < 0)
- return -1;
- }
-
- /*
- * Long ago, somebody thought it'd be a good idea to declare this function
- * as taking size_t ... but the underlying backend function only accepts a
- * signed int32 length. So throw error if the given value overflows
- * int32.
- */
- if (len > (size_t) INT_MAX)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("argument of lo_read exceeds integer range\n"));
- return -1;
- }
-
- argv[0].isint = 1;
- argv[0].len = 4;
- argv[0].u.integer = fd;
-
- argv[1].isint = 1;
- argv[1].len = 4;
- argv[1].u.integer = (int) len;
-
- res = PQfn(conn, conn->lobjfuncs->fn_lo_read,
- (void *) buf, &result_len, 0, argv, 2);
- if (PQresultStatus(res) == PGRES_COMMAND_OK)
- {
- PQclear(res);
- return result_len;
- }
- else
- {
- PQclear(res);
- return -1;
- }
-}
-
-/*
- * lo_write
- * write len bytes of buf into the large object fd
- *
- * returns the number of bytes written, or -1 on failure.
- */
-int
-lo_write(PGconn *conn, int fd, const char *buf, size_t len)
-{
- PQArgBlock argv[2];
- PGresult *res;
- int result_len;
- int retval;
-
- if (conn == NULL || conn->lobjfuncs == NULL)
- {
- if (lo_initialize(conn) < 0)
- return -1;
- }
-
- /*
- * Long ago, somebody thought it'd be a good idea to declare this function
- * as taking size_t ... but the underlying backend function only accepts a
- * signed int32 length. So throw error if the given value overflows
- * int32.
- */
- if (len > (size_t) INT_MAX)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("argument of lo_write exceeds integer range\n"));
- return -1;
- }
-
- argv[0].isint = 1;
- argv[0].len = 4;
- argv[0].u.integer = fd;
-
- argv[1].isint = 0;
- argv[1].len = (int) len;
- argv[1].u.ptr = (int *) buf;
-
- res = PQfn(conn, conn->lobjfuncs->fn_lo_write,
- &retval, &result_len, 1, argv, 2);
- if (PQresultStatus(res) == PGRES_COMMAND_OK)
- {
- PQclear(res);
- return retval;
- }
- else
- {
- PQclear(res);
- return -1;
- }
-}
-
-/*
- * lo_lseek
- * change the current read or write location on a large object
- */
-int
-lo_lseek(PGconn *conn, int fd, int offset, int whence)
-{
- PQArgBlock argv[3];
- PGresult *res;
- int retval;
- int result_len;
-
- if (conn == NULL || conn->lobjfuncs == NULL)
- {
- if (lo_initialize(conn) < 0)
- return -1;
- }
-
- argv[0].isint = 1;
- argv[0].len = 4;
- argv[0].u.integer = fd;
-
- argv[1].isint = 1;
- argv[1].len = 4;
- argv[1].u.integer = offset;
-
- argv[2].isint = 1;
- argv[2].len = 4;
- argv[2].u.integer = whence;
-
- res = PQfn(conn, conn->lobjfuncs->fn_lo_lseek,
- &retval, &result_len, 1, argv, 3);
- if (PQresultStatus(res) == PGRES_COMMAND_OK)
- {
- PQclear(res);
- return retval;
- }
- else
- {
- PQclear(res);
- return -1;
- }
-}
-
-/*
- * lo_lseek64
- * change the current read or write location on a large object
- */
-pg_int64
-lo_lseek64(PGconn *conn, int fd, pg_int64 offset, int whence)
-{
- PQArgBlock argv[3];
- PGresult *res;
- pg_int64 retval;
- int result_len;
-
- if (conn == NULL || conn->lobjfuncs == NULL)
- {
- if (lo_initialize(conn) < 0)
- return -1;
- }
-
- if (conn->lobjfuncs->fn_lo_lseek64 == 0)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("cannot determine OID of function lo_lseek64\n"));
- return -1;
- }
-
- argv[0].isint = 1;
- argv[0].len = 4;
- argv[0].u.integer = fd;
-
- offset = lo_hton64(offset);
- argv[1].isint = 0;
- argv[1].len = 8;
- argv[1].u.ptr = (int *) &offset;
-
- argv[2].isint = 1;
- argv[2].len = 4;
- argv[2].u.integer = whence;
-
- res = PQfn(conn, conn->lobjfuncs->fn_lo_lseek64,
- (void *) &retval, &result_len, 0, argv, 3);
- if (PQresultStatus(res) == PGRES_COMMAND_OK && result_len == 8)
- {
- PQclear(res);
- return lo_ntoh64(retval);
- }
- else
- {
- PQclear(res);
- return -1;
- }
-}
-
-/*
- * lo_creat
- * create a new large object
- * the mode is ignored (once upon a time it had a use)
- *
- * returns the oid of the large object created or
- * InvalidOid upon failure
- */
-Oid
-lo_creat(PGconn *conn, int mode)
-{
- PQArgBlock argv[1];
- PGresult *res;
- int retval;
- int result_len;
-
- if (conn == NULL || conn->lobjfuncs == NULL)
- {
- if (lo_initialize(conn) < 0)
- return InvalidOid;
- }
-
- argv[0].isint = 1;
- argv[0].len = 4;
- argv[0].u.integer = mode;
- res = PQfn(conn, conn->lobjfuncs->fn_lo_creat,
- &retval, &result_len, 1, argv, 1);
- if (PQresultStatus(res) == PGRES_COMMAND_OK)
- {
- PQclear(res);
- return (Oid) retval;
- }
- else
- {
- PQclear(res);
- return InvalidOid;
- }
-}
-
-/*
- * lo_create
- * create a new large object
- * if lobjId isn't InvalidOid, it specifies the OID to (attempt to) create
- *
- * returns the oid of the large object created or
- * InvalidOid upon failure
- */
-Oid
-lo_create(PGconn *conn, Oid lobjId)
-{
- PQArgBlock argv[1];
- PGresult *res;
- int retval;
- int result_len;
-
- if (conn == NULL || conn->lobjfuncs == NULL)
- {
- if (lo_initialize(conn) < 0)
- return InvalidOid;
- }
-
- /* Must check this on-the-fly because it's not there pre-8.1 */
- if (conn->lobjfuncs->fn_lo_create == 0)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("cannot determine OID of function lo_create\n"));
- return InvalidOid;
- }
-
- argv[0].isint = 1;
- argv[0].len = 4;
- argv[0].u.integer = lobjId;
- res = PQfn(conn, conn->lobjfuncs->fn_lo_create,
- &retval, &result_len, 1, argv, 1);
- if (PQresultStatus(res) == PGRES_COMMAND_OK)
- {
- PQclear(res);
- return (Oid) retval;
- }
- else
- {
- PQclear(res);
- return InvalidOid;
- }
-}
-
-
-/*
- * lo_tell
- * returns the current seek location of the large object
- */
-int
-lo_tell(PGconn *conn, int fd)
-{
- int retval;
- PQArgBlock argv[1];
- PGresult *res;
- int result_len;
-
- if (conn == NULL || conn->lobjfuncs == NULL)
- {
- if (lo_initialize(conn) < 0)
- return -1;
- }
-
- argv[0].isint = 1;
- argv[0].len = 4;
- argv[0].u.integer = fd;
-
- res = PQfn(conn, conn->lobjfuncs->fn_lo_tell,
- &retval, &result_len, 1, argv, 1);
- if (PQresultStatus(res) == PGRES_COMMAND_OK)
- {
- PQclear(res);
- return retval;
- }
- else
- {
- PQclear(res);
- return -1;
- }
-}
-
-/*
- * lo_tell64
- * returns the current seek location of the large object
- */
-pg_int64
-lo_tell64(PGconn *conn, int fd)
-{
- pg_int64 retval;
- PQArgBlock argv[1];
- PGresult *res;
- int result_len;
-
- if (conn == NULL || conn->lobjfuncs == NULL)
- {
- if (lo_initialize(conn) < 0)
- return -1;
- }
-
- if (conn->lobjfuncs->fn_lo_tell64 == 0)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("cannot determine OID of function lo_tell64\n"));
- return -1;
- }
-
- argv[0].isint = 1;
- argv[0].len = 4;
- argv[0].u.integer = fd;
-
- res = PQfn(conn, conn->lobjfuncs->fn_lo_tell64,
- (void *) &retval, &result_len, 0, argv, 1);
- if (PQresultStatus(res) == PGRES_COMMAND_OK && result_len == 8)
- {
- PQclear(res);
- return lo_ntoh64(retval);
- }
- else
- {
- PQclear(res);
- return -1;
- }
-}
-
-/*
- * lo_unlink
- * delete a file
- */
-
-int
-lo_unlink(PGconn *conn, Oid lobjId)
-{
- PQArgBlock argv[1];
- PGresult *res;
- int result_len;
- int retval;
-
- if (conn == NULL || conn->lobjfuncs == NULL)
- {
- if (lo_initialize(conn) < 0)
- return -1;
- }
-
- argv[0].isint = 1;
- argv[0].len = 4;
- argv[0].u.integer = lobjId;
-
- res = PQfn(conn, conn->lobjfuncs->fn_lo_unlink,
- &retval, &result_len, 1, argv, 1);
- if (PQresultStatus(res) == PGRES_COMMAND_OK)
- {
- PQclear(res);
- return retval;
- }
- else
- {
- PQclear(res);
- return -1;
- }
-}
-
-/*
- * lo_import -
- * imports a file as an (inversion) large object.
- *
- * returns the oid of that object upon success,
- * returns InvalidOid upon failure
- */
-
-Oid
-lo_import(PGconn *conn, const char *filename)
-{
- return lo_import_internal(conn, filename, InvalidOid);
-}
-
-/*
- * lo_import_with_oid -
- * imports a file as an (inversion) large object.
- * large object id can be specified.
- *
- * returns the oid of that object upon success,
- * returns InvalidOid upon failure
- */
-
-Oid
-lo_import_with_oid(PGconn *conn, const char *filename, Oid lobjId)
-{
- return lo_import_internal(conn, filename, lobjId);
-}
-
-static Oid
-lo_import_internal(PGconn *conn, const char *filename, Oid oid)
-{
- int fd;
- int nbytes,
- tmp;
- char buf[LO_BUFSIZE];
- Oid lobjOid;
- int lobj;
- char sebuf[256];
-
- /*
- * open the file to be read in
- */
- fd = open(filename, O_RDONLY | PG_BINARY, 0666);
- if (fd < 0)
- { /* error */
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("could not open file \"%s\": %s\n"),
- filename, pqStrerror(errno, sebuf, sizeof(sebuf)));
- return InvalidOid;
- }
-
- /*
- * create an inversion object
- */
- if (oid == InvalidOid)
- lobjOid = lo_creat(conn, INV_READ | INV_WRITE);
- else
- lobjOid = lo_create(conn, oid);
-
- if (lobjOid == InvalidOid)
- {
- /* we assume lo_create() already set a suitable error message */
- (void) close(fd);
- return InvalidOid;
- }
-
- lobj = lo_open(conn, lobjOid, INV_WRITE);
- if (lobj == -1)
- {
- /* we assume lo_open() already set a suitable error message */
- (void) close(fd);
- return InvalidOid;
- }
-
- /*
- * read in from the file and write to the large object
- */
- while ((nbytes = read(fd, buf, LO_BUFSIZE)) > 0)
- {
- tmp = lo_write(conn, lobj, buf, nbytes);
- if (tmp != nbytes)
- {
- /*
- * If lo_write() failed, we are now in an aborted transaction so
- * there's no need for lo_close(); furthermore, if we tried it
- * we'd overwrite the useful error result with a useless one. So
- * just nail the doors shut and get out of town.
- */
- (void) close(fd);
- return InvalidOid;
- }
- }
-
- if (nbytes < 0)
- {
- /* We must do lo_close before setting the errorMessage */
- int save_errno = errno;
-
- (void) lo_close(conn, lobj);
- (void) close(fd);
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("could not read from file \"%s\": %s\n"),
- filename,
- pqStrerror(save_errno, sebuf, sizeof(sebuf)));
- return InvalidOid;
- }
-
- (void) close(fd);
-
- if (lo_close(conn, lobj) != 0)
- {
- /* we assume lo_close() already set a suitable error message */
- return InvalidOid;
- }
-
- return lobjOid;
-}
-
-/*
- * lo_export -
- * exports an (inversion) large object.
- * returns -1 upon failure, 1 if OK
- */
-int
-lo_export(PGconn *conn, Oid lobjId, const char *filename)
-{
- int result = 1;
- int fd;
- int nbytes,
- tmp;
- char buf[LO_BUFSIZE];
- int lobj;
- char sebuf[256];
-
- /*
- * open the large object.
- */
- lobj = lo_open(conn, lobjId, INV_READ);
- if (lobj == -1)
- {
- /* we assume lo_open() already set a suitable error message */
- return -1;
- }
-
- /*
- * create the file to be written to
- */
- fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC | PG_BINARY, 0666);
- if (fd < 0)
- {
- /* We must do lo_close before setting the errorMessage */
- int save_errno = errno;
-
- (void) lo_close(conn, lobj);
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("could not open file \"%s\": %s\n"),
- filename,
- pqStrerror(save_errno, sebuf, sizeof(sebuf)));
- return -1;
- }
-
- /*
- * read in from the large object and write to the file
- */
- while ((nbytes = lo_read(conn, lobj, buf, LO_BUFSIZE)) > 0)
- {
- tmp = write(fd, buf, nbytes);
- if (tmp != nbytes)
- {
- /* We must do lo_close before setting the errorMessage */
- int save_errno = errno;
-
- (void) lo_close(conn, lobj);
- (void) close(fd);
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("could not write to file \"%s\": %s\n"),
- filename,
- pqStrerror(save_errno, sebuf, sizeof(sebuf)));
- return -1;
- }
- }
-
- /*
- * If lo_read() failed, we are now in an aborted transaction so there's no
- * need for lo_close(); furthermore, if we tried it we'd overwrite the
- * useful error result with a useless one. So skip lo_close() if we got a
- * failure result.
- */
- if (nbytes < 0 ||
- lo_close(conn, lobj) != 0)
- {
- /* assume lo_read() or lo_close() left a suitable error message */
- result = -1;
- }
-
- /* if we already failed, don't overwrite that msg with a close error */
- if (close(fd) && result >= 0)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("could not write to file \"%s\": %s\n"),
- filename, pqStrerror(errno, sebuf, sizeof(sebuf)));
- result = -1;
- }
-
- return result;
-}
-
-
-/*
- * lo_initialize
- *
- * Initialize the large object interface for an existing connection.
- * We ask the backend about the functions OID's in pg_proc for all
- * functions that are required for large object operations.
- */
-static int
-lo_initialize(PGconn *conn)
-{
- PGresult *res;
- PGlobjfuncs *lobjfuncs;
- int n;
- const char *query;
- const char *fname;
- Oid foid;
-
- if (!conn)
- return -1;
-
- /*
- * Allocate the structure to hold the functions OID's
- */
- lobjfuncs = (PGlobjfuncs *) malloc(sizeof(PGlobjfuncs));
- if (lobjfuncs == NULL)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("out of memory\n"));
- return -1;
- }
- MemSet((char *) lobjfuncs, 0, sizeof(PGlobjfuncs));
-
- /*
- * Execute the query to get all the functions at once. In 7.3 and later
- * we need to be schema-safe. lo_create only exists in 8.1 and up.
- * lo_truncate only exists in 8.3 and up.
- */
- if (conn->sversion >= 70300)
- query = "select proname, oid from pg_catalog.pg_proc "
- "where proname in ("
- "'lo_open', "
- "'lo_close', "
- "'lo_creat', "
- "'lo_create', "
- "'lo_unlink', "
- "'lo_lseek', "
- "'lo_lseek64', "
- "'lo_tell', "
- "'lo_tell64', "
- "'lo_truncate', "
- "'lo_truncate64', "
- "'loread', "
- "'lowrite') "
- "and pronamespace = (select oid from pg_catalog.pg_namespace "
- "where nspname = 'pg_catalog')";
- else
- query = "select proname, oid from pg_proc "
- "where proname = 'lo_open' "
- "or proname = 'lo_close' "
- "or proname = 'lo_creat' "
- "or proname = 'lo_unlink' "
- "or proname = 'lo_lseek' "
- "or proname = 'lo_tell' "
- "or proname = 'loread' "
- "or proname = 'lowrite'";
-
- res = PQexec(conn, query);
- if (res == NULL)
- {
- free(lobjfuncs);
- return -1;
- }
-
- if (res->resultStatus != PGRES_TUPLES_OK)
- {
- free(lobjfuncs);
- PQclear(res);
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("query to initialize large object functions did not return data\n"));
- return -1;
- }
-
- /*
- * Examine the result and put the OID's into the struct
- */
- for (n = 0; n < PQntuples(res); n++)
- {
- fname = PQgetvalue(res, n, 0);
- foid = (Oid) atoi(PQgetvalue(res, n, 1));
- if (strcmp(fname, "lo_open") == 0)
- lobjfuncs->fn_lo_open = foid;
- else if (strcmp(fname, "lo_close") == 0)
- lobjfuncs->fn_lo_close = foid;
- else if (strcmp(fname, "lo_creat") == 0)
- lobjfuncs->fn_lo_creat = foid;
- else if (strcmp(fname, "lo_create") == 0)
- lobjfuncs->fn_lo_create = foid;
- else if (strcmp(fname, "lo_unlink") == 0)
- lobjfuncs->fn_lo_unlink = foid;
- else if (strcmp(fname, "lo_lseek") == 0)
- lobjfuncs->fn_lo_lseek = foid;
- else if (strcmp(fname, "lo_lseek64") == 0)
- lobjfuncs->fn_lo_lseek64 = foid;
- else if (strcmp(fname, "lo_tell") == 0)
- lobjfuncs->fn_lo_tell = foid;
- else if (strcmp(fname, "lo_tell64") == 0)
- lobjfuncs->fn_lo_tell64 = foid;
- else if (strcmp(fname, "lo_truncate") == 0)
- lobjfuncs->fn_lo_truncate = foid;
- else if (strcmp(fname, "lo_truncate64") == 0)
- lobjfuncs->fn_lo_truncate64 = foid;
- else if (strcmp(fname, "loread") == 0)
- lobjfuncs->fn_lo_read = foid;
- else if (strcmp(fname, "lowrite") == 0)
- lobjfuncs->fn_lo_write = foid;
- }
-
- PQclear(res);
-
- /*
- * Finally check that we got all required large object interface functions
- * (ones that have been added later than the stone age are instead checked
- * only if used)
- */
- if (lobjfuncs->fn_lo_open == 0)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("cannot determine OID of function lo_open\n"));
- free(lobjfuncs);
- return -1;
- }
- if (lobjfuncs->fn_lo_close == 0)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("cannot determine OID of function lo_close\n"));
- free(lobjfuncs);
- return -1;
- }
- if (lobjfuncs->fn_lo_creat == 0)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("cannot determine OID of function lo_creat\n"));
- free(lobjfuncs);
- return -1;
- }
- if (lobjfuncs->fn_lo_unlink == 0)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("cannot determine OID of function lo_unlink\n"));
- free(lobjfuncs);
- return -1;
- }
- if (lobjfuncs->fn_lo_lseek == 0)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("cannot determine OID of function lo_lseek\n"));
- free(lobjfuncs);
- return -1;
- }
- if (lobjfuncs->fn_lo_tell == 0)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("cannot determine OID of function lo_tell\n"));
- free(lobjfuncs);
- return -1;
- }
- if (lobjfuncs->fn_lo_read == 0)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("cannot determine OID of function loread\n"));
- free(lobjfuncs);
- return -1;
- }
- if (lobjfuncs->fn_lo_write == 0)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("cannot determine OID of function lowrite\n"));
- free(lobjfuncs);
- return -1;
- }
-
- /*
- * Put the structure into the connection control
- */
- conn->lobjfuncs = lobjfuncs;
- return 0;
-}
-
-/*
- * lo_hton64
- * converts a 64-bit integer from host byte order to network byte order
- */
-static pg_int64
-lo_hton64(pg_int64 host64)
-{
- union
- {
- pg_int64 i64;
- uint32 i32[2];
- } swap;
- uint32 t;
-
- /* High order half first, since we're doing MSB-first */
- t = (uint32) (host64 >> 32);
- swap.i32[0] = htonl(t);
-
- /* Now the low order half */
- t = (uint32) host64;
- swap.i32[1] = htonl(t);
-
- return swap.i64;
-}
-
-/*
- * lo_ntoh64
- * converts a 64-bit integer from network byte order to host byte order
- */
-static pg_int64
-lo_ntoh64(pg_int64 net64)
-{
- union
- {
- pg_int64 i64;
- uint32 i32[2];
- } swap;
- pg_int64 result;
-
- swap.i64 = net64;
-
- result = (uint32) ntohl(swap.i32[0]);
- result <<= 32;
- result |= (uint32) ntohl(swap.i32[1]);
-
- return result;
-}
diff --git a/libpq/fe-misc.c b/libpq/fe-misc.c
deleted file mode 100644
index 32da8ca..0000000
--- a/libpq/fe-misc.c
+++ /dev/null
@@ -1,1267 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * FILE
- * fe-misc.c
- *
- * DESCRIPTION
- * miscellaneous useful functions
- *
- * The communication routines here are analogous to the ones in
- * backend/libpq/pqcomm.c and backend/libpq/pqcomprim.c, but operate
- * in the considerably different environment of the frontend libpq.
- * In particular, we work with a bare nonblock-mode socket, rather than
- * a stdio stream, so that we can avoid unwanted blocking of the application.
- *
- * XXX: MOVE DEBUG PRINTOUT TO HIGHER LEVEL. As is, block and restart
- * will cause repeat printouts.
- *
- * We must speak the same transmitted data representations as the backend
- * routines.
- *
- *
- * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- * IDENTIFICATION
- * src/interfaces/libpq/fe-misc.c
- *
- *-------------------------------------------------------------------------
- */
-
-#include "postgres_fe.h"
-
-#include <signal.h>
-#include <time.h>
-
-#include <netinet/in.h>
-#include <arpa/inet.h>
-
-#ifdef WIN32
-#include "win32.h"
-#else
-#include <unistd.h>
-#include <sys/time.h>
-#endif
-
-#ifdef HAVE_POLL_H
-#include <poll.h>
-#endif
-#ifdef HAVE_SYS_POLL_H
-#include <sys/poll.h>
-#endif
-#ifdef HAVE_SYS_SELECT_H
-#include <sys/select.h>
-#endif
-
-#include "libpq-fe.h"
-#include "libpq-int.h"
-#include "mb/pg_wchar.h"
-#include "pg_config_paths.h"
-
-
-static int pqPutMsgBytes(const void *buf, size_t len, PGconn *conn);
-static int pqSendSome(PGconn *conn, int len);
-static int pqSocketCheck(PGconn *conn, int forRead, int forWrite,
- time_t end_time);
-static int pqSocketPoll(int sock, int forRead, int forWrite, time_t end_time);
-
-/*
- * PQlibVersion: return the libpq version number
- */
-int
-PQlibVersion(void)
-{
- return PG_VERSION_NUM;
-}
-
-/*
- * fputnbytes: print exactly N bytes to a file
- *
- * We avoid using %.*s here because it can misbehave if the data
- * is not valid in what libc thinks is the prevailing encoding.
- */
-static void
-fputnbytes(FILE *f, const char *str, size_t n)
-{
- while (n-- > 0)
- fputc(*str++, f);
-}
-
-
-/*
- * pqGetc: get 1 character from the connection
- *
- * All these routines return 0 on success, EOF on error.
- * Note that for the Get routines, EOF only means there is not enough
- * data in the buffer, not that there is necessarily a hard error.
- */
-int
-pqGetc(char *result, PGconn *conn)
-{
- if (conn->inCursor >= conn->inEnd)
- return EOF;
-
- *result = conn->inBuffer[conn->inCursor++];
-
- if (conn->Pfdebug)
- fprintf(conn->Pfdebug, "From backend> %c\n", *result);
-
- return 0;
-}
-
-
-/*
- * pqPutc: write 1 char to the current message
- */
-int
-pqPutc(char c, PGconn *conn)
-{
- if (pqPutMsgBytes(&c, 1, conn))
- return EOF;
-
- if (conn->Pfdebug)
- fprintf(conn->Pfdebug, "To backend> %c\n", c);
-
- return 0;
-}
-
-
-/*
- * pqGets[_append]:
- * get a null-terminated string from the connection,
- * and store it in an expansible PQExpBuffer.
- * If we run out of memory, all of the string is still read,
- * but the excess characters are silently discarded.
- */
-static int
-pqGets_internal(PQExpBuffer buf, PGconn *conn, bool resetbuffer)
-{
- /* Copy conn data to locals for faster search loop */
- char *inBuffer = conn->inBuffer;
- int inCursor = conn->inCursor;
- int inEnd = conn->inEnd;
- int slen;
-
- while (inCursor < inEnd && inBuffer[inCursor])
- inCursor++;
-
- if (inCursor >= inEnd)
- return EOF;
-
- slen = inCursor - conn->inCursor;
-
- if (resetbuffer)
- resetPQExpBuffer(buf);
-
- appendBinaryPQExpBuffer(buf, inBuffer + conn->inCursor, slen);
-
- conn->inCursor = ++inCursor;
-
- if (conn->Pfdebug)
- fprintf(conn->Pfdebug, "From backend> \"%s\"\n",
- buf->data);
-
- return 0;
-}
-
-int
-pqGets(PQExpBuffer buf, PGconn *conn)
-{
- return pqGets_internal(buf, conn, true);
-}
-
-int
-pqGets_append(PQExpBuffer buf, PGconn *conn)
-{
- return pqGets_internal(buf, conn, false);
-}
-
-
-/*
- * pqPuts: write a null-terminated string to the current message
- */
-int
-pqPuts(const char *s, PGconn *conn)
-{
- if (pqPutMsgBytes(s, strlen(s) + 1, conn))
- return EOF;
-
- if (conn->Pfdebug)
- fprintf(conn->Pfdebug, "To backend> \"%s\"\n", s);
-
- return 0;
-}
-
-/*
- * pqGetnchar:
- * get a string of exactly len bytes in buffer s, no null termination
- */
-int
-pqGetnchar(char *s, size_t len, PGconn *conn)
-{
- if (len > (size_t) (conn->inEnd - conn->inCursor))
- return EOF;
-
- memcpy(s, conn->inBuffer + conn->inCursor, len);
- /* no terminating null */
-
- conn->inCursor += len;
-
- if (conn->Pfdebug)
- {
- fprintf(conn->Pfdebug, "From backend (%lu)> ", (unsigned long) len);
- fputnbytes(conn->Pfdebug, s, len);
- fprintf(conn->Pfdebug, "\n");
- }
-
- return 0;
-}
-
-/*
- * pqSkipnchar:
- * skip over len bytes in input buffer.
- *
- * Note: this is primarily useful for its debug output, which should
- * be exactly the same as for pqGetnchar. We assume the data in question
- * will actually be used, but just isn't getting copied anywhere as yet.
- */
-int
-pqSkipnchar(size_t len, PGconn *conn)
-{
- if (len > (size_t) (conn->inEnd - conn->inCursor))
- return EOF;
-
- if (conn->Pfdebug)
- {
- fprintf(conn->Pfdebug, "From backend (%lu)> ", (unsigned long) len);
- fputnbytes(conn->Pfdebug, conn->inBuffer + conn->inCursor, len);
- fprintf(conn->Pfdebug, "\n");
- }
-
- conn->inCursor += len;
-
- return 0;
-}
-
-/*
- * pqPutnchar:
- * write exactly len bytes to the current message
- */
-int
-pqPutnchar(const char *s, size_t len, PGconn *conn)
-{
- if (pqPutMsgBytes(s, len, conn))
- return EOF;
-
- if (conn->Pfdebug)
- {
- fprintf(conn->Pfdebug, "To backend> ");
- fputnbytes(conn->Pfdebug, s, len);
- fprintf(conn->Pfdebug, "\n");
- }
-
- return 0;
-}
-
-/*
- * pqGetInt
- * read a 2 or 4 byte integer and convert from network byte order
- * to local byte order
- */
-int
-pqGetInt(int *result, size_t bytes, PGconn *conn)
-{
- uint16 tmp2;
- uint32 tmp4;
-
- switch (bytes)
- {
- case 2:
- if (conn->inCursor + 2 > conn->inEnd)
- return EOF;
- memcpy(&tmp2, conn->inBuffer + conn->inCursor, 2);
- conn->inCursor += 2;
- *result = (int) ntohs(tmp2);
- break;
- case 4:
- if (conn->inCursor + 4 > conn->inEnd)
- return EOF;
- memcpy(&tmp4, conn->inBuffer + conn->inCursor, 4);
- conn->inCursor += 4;
- *result = (int) ntohl(tmp4);
- break;
- default:
- pqInternalNotice(&conn->noticeHooks,
- "integer of size %lu not supported by pqGetInt",
- (unsigned long) bytes);
- return EOF;
- }
-
- if (conn->Pfdebug)
- fprintf(conn->Pfdebug, "From backend (#%lu)> %d\n", (unsigned long) bytes, *result);
-
- return 0;
-}
-
-/*
- * pqPutInt
- * write an integer of 2 or 4 bytes, converting from host byte order
- * to network byte order.
- */
-int
-pqPutInt(int value, size_t bytes, PGconn *conn)
-{
- uint16 tmp2;
- uint32 tmp4;
-
- switch (bytes)
- {
- case 2:
- tmp2 = htons((uint16) value);
- if (pqPutMsgBytes((const char *) &tmp2, 2, conn))
- return EOF;
- break;
- case 4:
- tmp4 = htonl((uint32) value);
- if (pqPutMsgBytes((const char *) &tmp4, 4, conn))
- return EOF;
- break;
- default:
- pqInternalNotice(&conn->noticeHooks,
- "integer of size %lu not supported by pqPutInt",
- (unsigned long) bytes);
- return EOF;
- }
-
- if (conn->Pfdebug)
- fprintf(conn->Pfdebug, "To backend (%lu#)> %d\n", (unsigned long) bytes, value);
-
- return 0;
-}
-
-/*
- * Make sure conn's output buffer can hold bytes_needed bytes (caller must
- * include already-stored data into the value!)
- *
- * Returns 0 on success, EOF if failed to enlarge buffer
- */
-int
-pqCheckOutBufferSpace(size_t bytes_needed, PGconn *conn)
-{
- int newsize = conn->outBufSize;
- char *newbuf;
-
- /* Quick exit if we have enough space */
- if (bytes_needed <= (size_t) newsize)
- return 0;
-
- /*
- * If we need to enlarge the buffer, we first try to double it in size; if
- * that doesn't work, enlarge in multiples of 8K. This avoids thrashing
- * the malloc pool by repeated small enlargements.
- *
- * Note: tests for newsize > 0 are to catch integer overflow.
- */
- do
- {
- newsize *= 2;
- } while (newsize > 0 && bytes_needed > (size_t) newsize);
-
- if (newsize > 0 && bytes_needed <= (size_t) newsize)
- {
- newbuf = realloc(conn->outBuffer, newsize);
- if (newbuf)
- {
- /* realloc succeeded */
- conn->outBuffer = newbuf;
- conn->outBufSize = newsize;
- return 0;
- }
- }
-
- newsize = conn->outBufSize;
- do
- {
- newsize += 8192;
- } while (newsize > 0 && bytes_needed > (size_t) newsize);
-
- if (newsize > 0 && bytes_needed <= (size_t) newsize)
- {
- newbuf = realloc(conn->outBuffer, newsize);
- if (newbuf)
- {
- /* realloc succeeded */
- conn->outBuffer = newbuf;
- conn->outBufSize = newsize;
- return 0;
- }
- }
-
- /* realloc failed. Probably out of memory */
- printfPQExpBuffer(&conn->errorMessage,
- "cannot allocate memory for output buffer\n");
- return EOF;
-}
-
-/*
- * Make sure conn's input buffer can hold bytes_needed bytes (caller must
- * include already-stored data into the value!)
- *
- * Returns 0 on success, EOF if failed to enlarge buffer
- */
-int
-pqCheckInBufferSpace(size_t bytes_needed, PGconn *conn)
-{
- int newsize = conn->inBufSize;
- char *newbuf;
-
- /* Quick exit if we have enough space */
- if (bytes_needed <= (size_t) newsize)
- return 0;
-
- /*
- * Before concluding that we need to enlarge the buffer, left-justify
- * whatever is in it and recheck. The caller's value of bytes_needed
- * includes any data to the left of inStart, but we can delete that in
- * preference to enlarging the buffer. It's slightly ugly to have this
- * function do this, but it's better than making callers worry about it.
- */
- bytes_needed -= conn->inStart;
-
- if (conn->inStart < conn->inEnd)
- {
- if (conn->inStart > 0)
- {
- memmove(conn->inBuffer, conn->inBuffer + conn->inStart,
- conn->inEnd - conn->inStart);
- conn->inEnd -= conn->inStart;
- conn->inCursor -= conn->inStart;
- conn->inStart = 0;
- }
- }
- else
- {
- /* buffer is logically empty, reset it */
- conn->inStart = conn->inCursor = conn->inEnd = 0;
- }
-
- /* Recheck whether we have enough space */
- if (bytes_needed <= (size_t) newsize)
- return 0;
-
- /*
- * If we need to enlarge the buffer, we first try to double it in size; if
- * that doesn't work, enlarge in multiples of 8K. This avoids thrashing
- * the malloc pool by repeated small enlargements.
- *
- * Note: tests for newsize > 0 are to catch integer overflow.
- */
- do
- {
- newsize *= 2;
- } while (newsize > 0 && bytes_needed > (size_t) newsize);
-
- if (newsize > 0 && bytes_needed <= (size_t) newsize)
- {
- newbuf = realloc(conn->inBuffer, newsize);
- if (newbuf)
- {
- /* realloc succeeded */
- conn->inBuffer = newbuf;
- conn->inBufSize = newsize;
- return 0;
- }
- }
-
- newsize = conn->inBufSize;
- do
- {
- newsize += 8192;
- } while (newsize > 0 && bytes_needed > (size_t) newsize);
-
- if (newsize > 0 && bytes_needed <= (size_t) newsize)
- {
- newbuf = realloc(conn->inBuffer, newsize);
- if (newbuf)
- {
- /* realloc succeeded */
- conn->inBuffer = newbuf;
- conn->inBufSize = newsize;
- return 0;
- }
- }
-
- /* realloc failed. Probably out of memory */
- printfPQExpBuffer(&conn->errorMessage,
- "cannot allocate memory for input buffer\n");
- return EOF;
-}
-
-/*
- * pqPutMsgStart: begin construction of a message to the server
- *
- * msg_type is the message type byte, or 0 for a message without type byte
- * (only startup messages have no type byte)
- *
- * force_len forces the message to have a length word; otherwise, we add
- * a length word if protocol 3.
- *
- * Returns 0 on success, EOF on error
- *
- * The idea here is that we construct the message in conn->outBuffer,
- * beginning just past any data already in outBuffer (ie, at
- * outBuffer+outCount). We enlarge the buffer as needed to hold the message.
- * When the message is complete, we fill in the length word (if needed) and
- * then advance outCount past the message, making it eligible to send.
- *
- * The state variable conn->outMsgStart points to the incomplete message's
- * length word: it is either outCount or outCount+1 depending on whether
- * there is a type byte. If we are sending a message without length word
- * (pre protocol 3.0 only), then outMsgStart is -1. The state variable
- * conn->outMsgEnd is the end of the data collected so far.
- */
-int
-pqPutMsgStart(char msg_type, bool force_len, PGconn *conn)
-{
- int lenPos;
- int endPos;
-
- /* allow room for message type byte */
- if (msg_type)
- endPos = conn->outCount + 1;
- else
- endPos = conn->outCount;
-
- /* do we want a length word? */
- if (force_len || PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
- {
- lenPos = endPos;
- /* allow room for message length */
- endPos += 4;
- }
- else
- lenPos = -1;
-
- /* make sure there is room for message header */
- if (pqCheckOutBufferSpace(endPos, conn))
- return EOF;
- /* okay, save the message type byte if any */
- if (msg_type)
- conn->outBuffer[conn->outCount] = msg_type;
- /* set up the message pointers */
- conn->outMsgStart = lenPos;
- conn->outMsgEnd = endPos;
- /* length word, if needed, will be filled in by pqPutMsgEnd */
-
- if (conn->Pfdebug)
- fprintf(conn->Pfdebug, "To backend> Msg %c\n",
- msg_type ? msg_type : ' ');
-
- return 0;
-}
-
-/*
- * pqPutMsgBytes: add bytes to a partially-constructed message
- *
- * Returns 0 on success, EOF on error
- */
-static int
-pqPutMsgBytes(const void *buf, size_t len, PGconn *conn)
-{
- /* make sure there is room for it */
- if (pqCheckOutBufferSpace(conn->outMsgEnd + len, conn))
- return EOF;
- /* okay, save the data */
- memcpy(conn->outBuffer + conn->outMsgEnd, buf, len);
- conn->outMsgEnd += len;
- /* no Pfdebug call here, caller should do it */
- return 0;
-}
-
-/*
- * pqPutMsgEnd: finish constructing a message and possibly send it
- *
- * Returns 0 on success, EOF on error
- *
- * We don't actually send anything here unless we've accumulated at least
- * 8K worth of data (the typical size of a pipe buffer on Unix systems).
- * This avoids sending small partial packets. The caller must use pqFlush
- * when it's important to flush all the data out to the server.
- */
-int
-pqPutMsgEnd(PGconn *conn)
-{
- if (conn->Pfdebug)
- fprintf(conn->Pfdebug, "To backend> Msg complete, length %u\n",
- conn->outMsgEnd - conn->outCount);
-
- /* Fill in length word if needed */
- if (conn->outMsgStart >= 0)
- {
- uint32 msgLen = conn->outMsgEnd - conn->outMsgStart;
-
- msgLen = htonl(msgLen);
- memcpy(conn->outBuffer + conn->outMsgStart, &msgLen, 4);
- }
-
- /* Make message eligible to send */
- conn->outCount = conn->outMsgEnd;
-
- if (conn->outCount >= 8192)
- {
- int toSend = conn->outCount - (conn->outCount % 8192);
-
- if (pqSendSome(conn, toSend) < 0)
- return EOF;
- /* in nonblock mode, don't complain if unable to send it all */
- }
-
- return 0;
-}
-
-/* ----------
- * pqReadData: read more data, if any is available
- * Possible return values:
- * 1: successfully loaded at least one more byte
- * 0: no data is presently available, but no error detected
- * -1: error detected (including EOF = connection closure);
- * conn->errorMessage set
- * NOTE: callers must not assume that pointers or indexes into conn->inBuffer
- * remain valid across this call!
- * ----------
- */
-int
-pqReadData(PGconn *conn)
-{
- int someread = 0;
- int nread;
-
- if (conn->sock == PGINVALID_SOCKET)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("connection not open\n"));
- return -1;
- }
-
- /* Left-justify any data in the buffer to make room */
- if (conn->inStart < conn->inEnd)
- {
- if (conn->inStart > 0)
- {
- memmove(conn->inBuffer, conn->inBuffer + conn->inStart,
- conn->inEnd - conn->inStart);
- conn->inEnd -= conn->inStart;
- conn->inCursor -= conn->inStart;
- conn->inStart = 0;
- }
- }
- else
- {
- /* buffer is logically empty, reset it */
- conn->inStart = conn->inCursor = conn->inEnd = 0;
- }
-
- /*
- * If the buffer is fairly full, enlarge it. We need to be able to enlarge
- * the buffer in case a single message exceeds the initial buffer size. We
- * enlarge before filling the buffer entirely so as to avoid asking the
- * kernel for a partial packet. The magic constant here should be large
- * enough for a TCP packet or Unix pipe bufferload. 8K is the usual pipe
- * buffer size, so...
- */
- if (conn->inBufSize - conn->inEnd < 8192)
- {
- if (pqCheckInBufferSpace(conn->inEnd + (size_t) 8192, conn))
- {
- /*
- * We don't insist that the enlarge worked, but we need some room
- */
- if (conn->inBufSize - conn->inEnd < 100)
- return -1; /* errorMessage already set */
- }
- }
-
- /* OK, try to read some data */
-retry3:
- nread = pqsecure_read(conn, conn->inBuffer + conn->inEnd,
- conn->inBufSize - conn->inEnd);
- if (nread < 0)
- {
- if (SOCK_ERRNO == EINTR)
- goto retry3;
- /* Some systems return EAGAIN/EWOULDBLOCK for no data */
-#ifdef EAGAIN
- if (SOCK_ERRNO == EAGAIN)
- return someread;
-#endif
-#if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
- if (SOCK_ERRNO == EWOULDBLOCK)
- return someread;
-#endif
- /* We might get ECONNRESET here if using TCP and backend died */
-#ifdef ECONNRESET
- if (SOCK_ERRNO == ECONNRESET)
- goto definitelyFailed;
-#endif
- /* pqsecure_read set the error message for us */
- return -1;
- }
- if (nread > 0)
- {
- conn->inEnd += nread;
-
- /*
- * Hack to deal with the fact that some kernels will only give us back
- * 1 packet per recv() call, even if we asked for more and there is
- * more available. If it looks like we are reading a long message,
- * loop back to recv() again immediately, until we run out of data or
- * buffer space. Without this, the block-and-restart behavior of
- * libpq's higher levels leads to O(N^2) performance on long messages.
- *
- * Since we left-justified the data above, conn->inEnd gives the
- * amount of data already read in the current message. We consider
- * the message "long" once we have acquired 32k ...
- */
- if (conn->inEnd > 32768 &&
- (conn->inBufSize - conn->inEnd) >= 8192)
- {
- someread = 1;
- goto retry3;
- }
- return 1;
- }
-
- if (someread)
- return 1; /* got a zero read after successful tries */
-
- /*
- * A return value of 0 could mean just that no data is now available, or
- * it could mean EOF --- that is, the server has closed the connection.
- * Since we have the socket in nonblock mode, the only way to tell the
- * difference is to see if select() is saying that the file is ready.
- * Grumble. Fortunately, we don't expect this path to be taken much,
- * since in normal practice we should not be trying to read data unless
- * the file selected for reading already.
- *
- * In SSL mode it's even worse: SSL_read() could say WANT_READ and then
- * data could arrive before we make the pqReadReady() test, but the second
- * SSL_read() could still say WANT_READ because the data received was not
- * a complete SSL record. So we must play dumb and assume there is more
- * data, relying on the SSL layer to detect true EOF.
- */
-
-#ifdef USE_SSL
- if (conn->ssl_in_use)
- return 0;
-#endif
-
- switch (pqReadReady(conn))
- {
- case 0:
- /* definitely no data available */
- return 0;
- case 1:
- /* ready for read */
- break;
- default:
- /* we override pqReadReady's message with something more useful */
- goto definitelyEOF;
- }
-
- /*
- * Still not sure that it's EOF, because some data could have just
- * arrived.
- */
-retry4:
- nread = pqsecure_read(conn, conn->inBuffer + conn->inEnd,
- conn->inBufSize - conn->inEnd);
- if (nread < 0)
- {
- if (SOCK_ERRNO == EINTR)
- goto retry4;
- /* Some systems return EAGAIN/EWOULDBLOCK for no data */
-#ifdef EAGAIN
- if (SOCK_ERRNO == EAGAIN)
- return 0;
-#endif
-#if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
- if (SOCK_ERRNO == EWOULDBLOCK)
- return 0;
-#endif
- /* We might get ECONNRESET here if using TCP and backend died */
-#ifdef ECONNRESET
- if (SOCK_ERRNO == ECONNRESET)
- goto definitelyFailed;
-#endif
- /* pqsecure_read set the error message for us */
- return -1;
- }
- if (nread > 0)
- {
- conn->inEnd += nread;
- return 1;
- }
-
- /*
- * OK, we are getting a zero read even though select() says ready. This
- * means the connection has been closed. Cope.
- */
-definitelyEOF:
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext(
- "server closed the connection unexpectedly\n"
- "\tThis probably means the server terminated abnormally\n"
- "\tbefore or while processing the request.\n"));
-
- /* Come here if lower-level code already set a suitable errorMessage */
-definitelyFailed:
- /* Do *not* drop any already-read data; caller still wants it */
- pqDropConnection(conn, false);
- conn->status = CONNECTION_BAD; /* No more connection to backend */
- return -1;
-}
-
-/*
- * pqSendSome: send data waiting in the output buffer.
- *
- * len is how much to try to send (typically equal to outCount, but may
- * be less).
- *
- * Return 0 on success, -1 on failure and 1 when not all data could be sent
- * because the socket would block and the connection is non-blocking.
- */
-static int
-pqSendSome(PGconn *conn, int len)
-{
- char *ptr = conn->outBuffer;
- int remaining = conn->outCount;
- int result = 0;
-
- if (conn->sock == PGINVALID_SOCKET)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("connection not open\n"));
- /* Discard queued data; no chance it'll ever be sent */
- conn->outCount = 0;
- return -1;
- }
-
- /* while there's still data to send */
- while (len > 0)
- {
- int sent;
-
-#ifndef WIN32
- sent = pqsecure_write(conn, ptr, len);
-#else
-
- /*
- * Windows can fail on large sends, per KB article Q201213. The
- * failure-point appears to be different in different versions of
- * Windows, but 64k should always be safe.
- */
- sent = pqsecure_write(conn, ptr, Min(len, 65536));
-#endif
-
- if (sent < 0)
- {
- /* Anything except EAGAIN/EWOULDBLOCK/EINTR is trouble */
- switch (SOCK_ERRNO)
- {
-#ifdef EAGAIN
- case EAGAIN:
- break;
-#endif
-#if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
- case EWOULDBLOCK:
- break;
-#endif
- case EINTR:
- continue;
-
- default:
- /* pqsecure_write set the error message for us */
-
- /*
- * We used to close the socket here, but that's a bad idea
- * since there might be unread data waiting (typically, a
- * NOTICE message from the backend telling us it's
- * committing hara-kiri...). Leave the socket open until
- * pqReadData finds no more data can be read. But abandon
- * attempt to send data.
- */
- conn->outCount = 0;
- return -1;
- }
- }
- else
- {
- ptr += sent;
- len -= sent;
- remaining -= sent;
- }
-
- if (len > 0)
- {
- /*
- * We didn't send it all, wait till we can send more.
- *
- * There are scenarios in which we can't send data because the
- * communications channel is full, but we cannot expect the server
- * to clear the channel eventually because it's blocked trying to
- * send data to us. (This can happen when we are sending a large
- * amount of COPY data, and the server has generated lots of
- * NOTICE responses.) To avoid a deadlock situation, we must be
- * prepared to accept and buffer incoming data before we try
- * again. Furthermore, it is possible that such incoming data
- * might not arrive until after we've gone to sleep. Therefore,
- * we wait for either read ready or write ready.
- *
- * In non-blocking mode, we don't wait here directly, but return 1
- * to indicate that data is still pending. The caller should wait
- * for both read and write ready conditions, and call
- * PQconsumeInput() on read ready, but just in case it doesn't, we
- * call pqReadData() ourselves before returning. That's not
- * enough if the data has not arrived yet, but it's the best we
- * can do, and works pretty well in practice. (The documentation
- * used to say that you only need to wait for write-ready, so
- * there are still plenty of applications like that out there.)
- */
- if (pqReadData(conn) < 0)
- {
- result = -1; /* error message already set up */
- break;
- }
-
- if (pqIsnonblocking(conn))
- {
- result = 1;
- break;
- }
-
- if (pqWait(TRUE, TRUE, conn))
- {
- result = -1;
- break;
- }
- }
- }
-
- /* shift the remaining contents of the buffer */
- if (remaining > 0)
- memmove(conn->outBuffer, ptr, remaining);
- conn->outCount = remaining;
-
- return result;
-}
-
-
-/*
- * pqFlush: send any data waiting in the output buffer
- *
- * Return 0 on success, -1 on failure and 1 when not all data could be sent
- * because the socket would block and the connection is non-blocking.
- */
-int
-pqFlush(PGconn *conn)
-{
- if (conn->Pfdebug)
- fflush(conn->Pfdebug);
-
- if (conn->outCount > 0)
- return pqSendSome(conn, conn->outCount);
-
- return 0;
-}
-
-
-/*
- * pqWait: wait until we can read or write the connection socket
- *
- * JAB: If SSL enabled and used and forRead, buffered bytes short-circuit the
- * call to select().
- *
- * We also stop waiting and return if the kernel flags an exception condition
- * on the socket. The actual error condition will be detected and reported
- * when the caller tries to read or write the socket.
- */
-int
-pqWait(int forRead, int forWrite, PGconn *conn)
-{
- return pqWaitTimed(forRead, forWrite, conn, (time_t) -1);
-}
-
-/*
- * pqWaitTimed: wait, but not past finish_time.
- *
- * If finish_time is exceeded then we return failure (EOF). This is like
- * the response for a kernel exception because we don't want the caller
- * to try to read/write in that case.
- *
- * finish_time = ((time_t) -1) disables the wait limit.
- */
-int
-pqWaitTimed(int forRead, int forWrite, PGconn *conn, time_t finish_time)
-{
- int result;
-
- result = pqSocketCheck(conn, forRead, forWrite, finish_time);
-
- if (result < 0)
- return EOF; /* errorMessage is already set */
-
- if (result == 0)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("timeout expired\n"));
- return EOF;
- }
-
- return 0;
-}
-
-/*
- * pqReadReady: is select() saying the file is ready to read?
- * Returns -1 on failure, 0 if not ready, 1 if ready.
- */
-int
-pqReadReady(PGconn *conn)
-{
- return pqSocketCheck(conn, 1, 0, (time_t) 0);
-}
-
-/*
- * pqWriteReady: is select() saying the file is ready to write?
- * Returns -1 on failure, 0 if not ready, 1 if ready.
- */
-int
-pqWriteReady(PGconn *conn)
-{
- return pqSocketCheck(conn, 0, 1, (time_t) 0);
-}
-
-/*
- * Checks a socket, using poll or select, for data to be read, written,
- * or both. Returns >0 if one or more conditions are met, 0 if it timed
- * out, -1 if an error occurred.
- *
- * If SSL is in use, the SSL buffer is checked prior to checking the socket
- * for read data directly.
- */
-static int
-pqSocketCheck(PGconn *conn, int forRead, int forWrite, time_t end_time)
-{
- int result;
-
- if (!conn)
- return -1;
- if (conn->sock == PGINVALID_SOCKET)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("invalid socket\n"));
- return -1;
- }
-
-#ifdef USE_SSL
- /* Check for SSL library buffering read bytes */
- if (forRead && conn->ssl_in_use && pgtls_read_pending(conn) > 0)
- {
- /* short-circuit the select */
- return 1;
- }
-#endif
-
- /* We will retry as long as we get EINTR */
- do
- result = pqSocketPoll(conn->sock, forRead, forWrite, end_time);
- while (result < 0 && SOCK_ERRNO == EINTR);
-
- if (result < 0)
- {
- char sebuf[256];
-
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("select() failed: %s\n"),
- SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
- }
-
- return result;
-}
-
-
-/*
- * Check a file descriptor for read and/or write data, possibly waiting.
- * If neither forRead nor forWrite are set, immediately return a timeout
- * condition (without waiting). Return >0 if condition is met, 0
- * if a timeout occurred, -1 if an error or interrupt occurred.
- *
- * Timeout is infinite if end_time is -1. Timeout is immediate (no blocking)
- * if end_time is 0 (or indeed, any time before now).
- */
-static int
-pqSocketPoll(int sock, int forRead, int forWrite, time_t end_time)
-{
- /* We use poll(2) if available, otherwise select(2) */
-#ifdef HAVE_POLL
- struct pollfd input_fd;
- int timeout_ms;
-
- if (!forRead && !forWrite)
- return 0;
-
- input_fd.fd = sock;
- input_fd.events = POLLERR;
- input_fd.revents = 0;
-
- if (forRead)
- input_fd.events |= POLLIN;
- if (forWrite)
- input_fd.events |= POLLOUT;
-
- /* Compute appropriate timeout interval */
- if (end_time == ((time_t) -1))
- timeout_ms = -1;
- else
- {
- time_t now = time(NULL);
-
- if (end_time > now)
- timeout_ms = (end_time - now) * 1000;
- else
- timeout_ms = 0;
- }
-
- return poll(&input_fd, 1, timeout_ms);
-#else /* !HAVE_POLL */
-
- fd_set input_mask;
- fd_set output_mask;
- fd_set except_mask;
- struct timeval timeout;
- struct timeval *ptr_timeout;
-
- if (!forRead && !forWrite)
- return 0;
-
- FD_ZERO(&input_mask);
- FD_ZERO(&output_mask);
- FD_ZERO(&except_mask);
- if (forRead)
- FD_SET(sock, &input_mask);
-
- if (forWrite)
- FD_SET(sock, &output_mask);
- FD_SET(sock, &except_mask);
-
- /* Compute appropriate timeout interval */
- if (end_time == ((time_t) -1))
- ptr_timeout = NULL;
- else
- {
- time_t now = time(NULL);
-
- if (end_time > now)
- timeout.tv_sec = end_time - now;
- else
- timeout.tv_sec = 0;
- timeout.tv_usec = 0;
- ptr_timeout = &timeout;
- }
-
- return select(sock + 1, &input_mask, &output_mask,
- &except_mask, ptr_timeout);
-#endif /* HAVE_POLL */
-}
-
-
-/*
- * A couple of "miscellaneous" multibyte related functions. They used
- * to be in fe-print.c but that file is doomed.
- */
-
-/*
- * returns the byte length of the character beginning at s, using the
- * specified encoding.
- */
-int
-PQmblen(const char *s, int encoding)
-{
- return pg_encoding_mblen(encoding, s);
-}
-
-/*
- * returns the display length of the character beginning at s, using the
- * specified encoding.
- */
-int
-PQdsplen(const char *s, int encoding)
-{
- return pg_encoding_dsplen(encoding, s);
-}
-
-/*
- * Get encoding id from environment variable PGCLIENTENCODING.
- */
-int
-PQenv2encoding(void)
-{
- char *str;
- int encoding = PG_SQL_ASCII;
-
- str = getenv("PGCLIENTENCODING");
- if (str && *str != '\0')
- {
- encoding = pg_char_to_encoding(str);
- if (encoding < 0)
- encoding = PG_SQL_ASCII;
- }
- return encoding;
-}
-
-
-#ifdef ENABLE_NLS
-
-static void
-libpq_binddomain()
-{
- static bool already_bound = false;
-
- if (!already_bound)
- {
- /* bindtextdomain() does not preserve errno */
-#ifdef WIN32
- int save_errno = GetLastError();
-#else
- int save_errno = errno;
-#endif
- const char *ldir;
-
- already_bound = true;
- /* No relocatable lookup here because the binary could be anywhere */
- ldir = getenv("PGLOCALEDIR");
- if (!ldir)
- ldir = LOCALEDIR;
- bindtextdomain(PG_TEXTDOMAIN("libpq"), ldir);
-#ifdef WIN32
- SetLastError(save_errno);
-#else
- errno = save_errno;
-#endif
- }
-}
-
-char *
-libpq_gettext(const char *msgid)
-{
- libpq_binddomain();
- return dgettext(PG_TEXTDOMAIN("libpq"), msgid);
-}
-
-char *
-libpq_ngettext(const char *msgid, const char *msgid_plural, unsigned long n)
-{
- libpq_binddomain();
- return dngettext(PG_TEXTDOMAIN("libpq"), msgid, msgid_plural, n);
-}
-
-#endif /* ENABLE_NLS */
diff --git a/libpq/fe-print.c b/libpq/fe-print.c
deleted file mode 100644
index e596a51..0000000
--- a/libpq/fe-print.c
+++ /dev/null
@@ -1,761 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * fe-print.c
- * functions for pretty-printing query results
- *
- * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- * These functions were formerly part of fe-exec.c, but they
- * didn't really belong there.
- *
- * IDENTIFICATION
- * src/interfaces/libpq/fe-print.c
- *
- *-------------------------------------------------------------------------
- */
-#include "postgres_fe.h"
-
-#include <signal.h>
-
-#ifdef WIN32
-#include "win32.h"
-#else
-#include <unistd.h>
-#include <sys/ioctl.h>
-#endif
-
-#ifdef HAVE_TERMIOS_H
-#include <termios.h>
-#else
-#ifndef WIN32
-#include <sys/termios.h>
-#endif
-#endif
-
-#include "libpq-fe.h"
-#include "libpq-int.h"
-
-
-static void do_field(const PQprintOpt *po, const PGresult *res,
- const int i, const int j, const int fs_len,
- char **fields,
- const int nFields, const char **fieldNames,
- unsigned char *fieldNotNum, int *fieldMax,
- const int fieldMaxLen, FILE *fout);
-static char *do_header(FILE *fout, const PQprintOpt *po, const int nFields,
- int *fieldMax, const char **fieldNames, unsigned char *fieldNotNum,
- const int fs_len, const PGresult *res);
-static void output_row(FILE *fout, const PQprintOpt *po, const int nFields, char **fields,
- unsigned char *fieldNotNum, int *fieldMax, char *border,
- const int row_index);
-static void fill(int length, int max, char filler, FILE *fp);
-
-/*
- * PQprint()
- *
- * Format results of a query for printing.
- *
- * PQprintOpt is a typedef (structure) that contains
- * various flags and options. consult libpq-fe.h for
- * details
- *
- * This function should probably be removed sometime since psql
- * doesn't use it anymore. It is unclear to what extent this is used
- * by external clients, however.
- */
-void
-PQprint(FILE *fout, const PGresult *res, const PQprintOpt *po)
-{
- int nFields;
-
- nFields = PQnfields(res);
-
- if (nFields > 0)
- { /* only print rows with at least 1 field. */
- int i,
- j;
- int nTups;
- int *fieldMax = NULL; /* in case we don't use them */
- unsigned char *fieldNotNum = NULL;
- char *border = NULL;
- char **fields = NULL;
- const char **fieldNames;
- int fieldMaxLen = 0;
- int numFieldName;
- int fs_len = strlen(po->fieldSep);
- int total_line_length = 0;
- int usePipe = 0;
- char *pagerenv;
-
-#if defined(ENABLE_THREAD_SAFETY) && !defined(WIN32)
- sigset_t osigset;
- bool sigpipe_masked = false;
- bool sigpipe_pending;
-#endif
-#if !defined(ENABLE_THREAD_SAFETY) && !defined(WIN32)
- pqsigfunc oldsigpipehandler = NULL;
-#endif
-
-#ifdef TIOCGWINSZ
- struct winsize screen_size;
-#else
- struct winsize
- {
- int ws_row;
- int ws_col;
- } screen_size;
-#endif
-
- nTups = PQntuples(res);
- if (!(fieldNames = (const char **) calloc(nFields, sizeof(char *))))
- {
- fprintf(stderr, libpq_gettext("out of memory\n"));
- abort();
- }
- if (!(fieldNotNum = (unsigned char *) calloc(nFields, 1)))
- {
- fprintf(stderr, libpq_gettext("out of memory\n"));
- abort();
- }
- if (!(fieldMax = (int *) calloc(nFields, sizeof(int))))
- {
- fprintf(stderr, libpq_gettext("out of memory\n"));
- abort();
- }
- for (numFieldName = 0;
- po->fieldName && po->fieldName[numFieldName];
- numFieldName++)
- ;
- for (j = 0; j < nFields; j++)
- {
- int len;
- const char *s = (j < numFieldName && po->fieldName[j][0]) ?
- po->fieldName[j] : PQfname(res, j);
-
- fieldNames[j] = s;
- len = s ? strlen(s) : 0;
- fieldMax[j] = len;
- len += fs_len;
- if (len > fieldMaxLen)
- fieldMaxLen = len;
- total_line_length += len;
- }
-
- total_line_length += nFields * strlen(po->fieldSep) + 1;
-
- if (fout == NULL)
- fout = stdout;
- if (po->pager && fout == stdout && isatty(fileno(stdin)) &&
- isatty(fileno(stdout)))
- {
- /*
- * If we think there'll be more than one screen of output, try to
- * pipe to the pager program.
- */
-#ifdef TIOCGWINSZ
- if (ioctl(fileno(stdout), TIOCGWINSZ, &screen_size) == -1 ||
- screen_size.ws_col == 0 ||
- screen_size.ws_row == 0)
- {
- screen_size.ws_row = 24;
- screen_size.ws_col = 80;
- }
-#else
- screen_size.ws_row = 24;
- screen_size.ws_col = 80;
-#endif
- pagerenv = getenv("PAGER");
- /* if PAGER is unset, empty or all-white-space, don't use pager */
- if (pagerenv != NULL &&
- strspn(pagerenv, " \t\r\n") != strlen(pagerenv) &&
- !po->html3 &&
- ((po->expanded &&
- nTups * (nFields + 1) >= screen_size.ws_row) ||
- (!po->expanded &&
- nTups * (total_line_length / screen_size.ws_col + 1) *
- (1 + (po->standard != 0)) >= screen_size.ws_row -
- (po->header != 0) *
- (total_line_length / screen_size.ws_col + 1) * 2
- - (po->header != 0) * 2 /* row count and newline */
- )))
- {
- fout = popen(pagerenv, "w");
- if (fout)
- {
- usePipe = 1;
-#ifndef WIN32
-#ifdef ENABLE_THREAD_SAFETY
- if (pq_block_sigpipe(&osigset, &sigpipe_pending) == 0)
- sigpipe_masked = true;
-#else
- oldsigpipehandler = pqsignal(SIGPIPE, SIG_IGN);
-#endif /* ENABLE_THREAD_SAFETY */
-#endif /* WIN32 */
- }
- else
- fout = stdout;
- }
- }
-
- if (!po->expanded && (po->align || po->html3))
- {
- if (!(fields = (char **) calloc(nFields * (nTups + 1), sizeof(char *))))
- {
- fprintf(stderr, libpq_gettext("out of memory\n"));
- abort();
- }
- }
- else if (po->header && !po->html3)
- {
- if (po->expanded)
- {
- if (po->align)
- fprintf(fout, libpq_gettext("%-*s%s Value\n"),
- fieldMaxLen - fs_len, libpq_gettext("Field"), po->fieldSep);
- else
- fprintf(fout, libpq_gettext("%s%sValue\n"), libpq_gettext("Field"), po->fieldSep);
- }
- else
- {
- int len = 0;
-
- for (j = 0; j < nFields; j++)
- {
- const char *s = fieldNames[j];
-
- fputs(s, fout);
- len += strlen(s) + fs_len;
- if ((j + 1) < nFields)
- fputs(po->fieldSep, fout);
- }
- fputc('\n', fout);
- for (len -= fs_len; len--; fputc('-', fout));
- fputc('\n', fout);
- }
- }
- if (po->expanded && po->html3)
- {
- if (po->caption)
- fprintf(fout, "<center><h2>%s</h2></center>\n", po->caption);
- else
- fprintf(fout,
- "<center><h2>"
- "Query retrieved %d rows * %d fields"
- "</h2></center>\n",
- nTups, nFields);
- }
- for (i = 0; i < nTups; i++)
- {
- if (po->expanded)
- {
- if (po->html3)
- fprintf(fout,
- "<table %s><caption align=\"top\">%d</caption>\n",
- po->tableOpt ? po->tableOpt : "", i);
- else
- fprintf(fout, libpq_gettext("-- RECORD %d --\n"), i);
- }
- for (j = 0; j < nFields; j++)
- do_field(po, res, i, j, fs_len, fields, nFields,
- fieldNames, fieldNotNum,
- fieldMax, fieldMaxLen, fout);
- if (po->html3 && po->expanded)
- fputs("</table>\n", fout);
- }
- if (!po->expanded && (po->align || po->html3))
- {
- if (po->html3)
- {
- if (po->header)
- {
- if (po->caption)
- fprintf(fout,
- "<table %s><caption align=\"top\">%s</caption>\n",
- po->tableOpt ? po->tableOpt : "",
- po->caption);
- else
- fprintf(fout,
- "<table %s><caption align=\"top\">"
- "Retrieved %d rows * %d fields"
- "</caption>\n",
- po->tableOpt ? po->tableOpt : "", nTups, nFields);
- }
- else
- fprintf(fout, "<table %s>", po->tableOpt ? po->tableOpt : "");
- }
- if (po->header)
- border = do_header(fout, po, nFields, fieldMax, fieldNames,
- fieldNotNum, fs_len, res);
- for (i = 0; i < nTups; i++)
- output_row(fout, po, nFields, fields,
- fieldNotNum, fieldMax, border, i);
- free(fields);
- if (border)
- free(border);
- }
- if (po->header && !po->html3)
- fprintf(fout, "(%d row%s)\n\n", PQntuples(res),
- (PQntuples(res) == 1) ? "" : "s");
- free(fieldMax);
- free(fieldNotNum);
- free((void *) fieldNames);
- if (usePipe)
- {
-#ifdef WIN32
- _pclose(fout);
-#else
- pclose(fout);
-
-#ifdef ENABLE_THREAD_SAFETY
- /* we can't easily verify if EPIPE occurred, so say it did */
- if (sigpipe_masked)
- pq_reset_sigpipe(&osigset, sigpipe_pending, true);
-#else
- pqsignal(SIGPIPE, oldsigpipehandler);
-#endif /* ENABLE_THREAD_SAFETY */
-#endif /* WIN32 */
- }
- if (po->html3 && !po->expanded)
- fputs("</table>\n", fout);
- }
-}
-
-
-static void
-do_field(const PQprintOpt *po, const PGresult *res,
- const int i, const int j, const int fs_len,
- char **fields,
- const int nFields, char const ** fieldNames,
- unsigned char *fieldNotNum, int *fieldMax,
- const int fieldMaxLen, FILE *fout)
-{
- const char *pval,
- *p;
- int plen;
- bool skipit;
-
- plen = PQgetlength(res, i, j);
- pval = PQgetvalue(res, i, j);
-
- if (plen < 1 || !pval || !*pval)
- {
- if (po->align || po->expanded)
- skipit = true;
- else
- {
- skipit = false;
- goto efield;
- }
- }
- else
- skipit = false;
-
- if (!skipit)
- {
- if (po->align && !fieldNotNum[j])
- {
- /* Detect whether field contains non-numeric data */
- char ch = '0';
-
- for (p = pval; *p; p += PQmblen(p, res->client_encoding))
- {
- ch = *p;
- if (!((ch >= '0' && ch <= '9') ||
- ch == '.' ||
- ch == 'E' ||
- ch == 'e' ||
- ch == ' ' ||
- ch == '-'))
- {
- fieldNotNum[j] = 1;
- break;
- }
- }
-
- /*
- * Above loop will believe E in first column is numeric; also, we
- * insist on a digit in the last column for a numeric. This test
- * is still not bulletproof but it handles most cases.
- */
- if (*pval == 'E' || *pval == 'e' ||
- !(ch >= '0' && ch <= '9'))
- fieldNotNum[j] = 1;
- }
-
- if (!po->expanded && (po->align || po->html3))
- {
- if (plen > fieldMax[j])
- fieldMax[j] = plen;
- if (!(fields[i * nFields + j] = (char *) malloc(plen + 1)))
- {
- fprintf(stderr, libpq_gettext("out of memory\n"));
- abort();
- }
- strcpy(fields[i * nFields + j], pval);
- }
- else
- {
- if (po->expanded)
- {
- if (po->html3)
- fprintf(fout,
- "<tr><td align=\"left\"><b>%s</b></td>"
- "<td align=\"%s\">%s</td></tr>\n",
- fieldNames[j],
- fieldNotNum[j] ? "left" : "right",
- pval);
- else
- {
- if (po->align)
- fprintf(fout,
- "%-*s%s %s\n",
- fieldMaxLen - fs_len, fieldNames[j],
- po->fieldSep,
- pval);
- else
- fprintf(fout,
- "%s%s%s\n",
- fieldNames[j], po->fieldSep, pval);
- }
- }
- else
- {
- if (!po->html3)
- {
- fputs(pval, fout);
- efield:
- if ((j + 1) < nFields)
- fputs(po->fieldSep, fout);
- else
- fputc('\n', fout);
- }
- }
- }
- }
-}
-
-
-static char *
-do_header(FILE *fout, const PQprintOpt *po, const int nFields, int *fieldMax,
- const char **fieldNames, unsigned char *fieldNotNum,
- const int fs_len, const PGresult *res)
-{
- int j; /* for loop index */
- char *border = NULL;
-
- if (po->html3)
- fputs("<tr>", fout);
- else
- {
- int tot = 0;
- int n = 0;
- char *p = NULL;
-
- for (; n < nFields; n++)
- tot += fieldMax[n] + fs_len + (po->standard ? 2 : 0);
- if (po->standard)
- tot += fs_len * 2 + 2;
- border = malloc(tot + 1);
- if (!border)
- {
- fprintf(stderr, libpq_gettext("out of memory\n"));
- abort();
- }
- p = border;
- if (po->standard)
- {
- char *fs = po->fieldSep;
-
- while (*fs++)
- *p++ = '+';
- }
- for (j = 0; j < nFields; j++)
- {
- int len;
-
- for (len = fieldMax[j] + (po->standard ? 2 : 0); len--; *p++ = '-');
- if (po->standard || (j + 1) < nFields)
- {
- char *fs = po->fieldSep;
-
- while (*fs++)
- *p++ = '+';
- }
- }
- *p = '\0';
- if (po->standard)
- fprintf(fout, "%s\n", border);
- }
- if (po->standard)
- fputs(po->fieldSep, fout);
- for (j = 0; j < nFields; j++)
- {
- const char *s = PQfname(res, j);
-
- if (po->html3)
- {
- fprintf(fout, "<th align=\"%s\">%s</th>",
- fieldNotNum[j] ? "left" : "right", fieldNames[j]);
- }
- else
- {
- int n = strlen(s);
-
- if (n > fieldMax[j])
- fieldMax[j] = n;
- if (po->standard)
- fprintf(fout,
- fieldNotNum[j] ? " %-*s " : " %*s ",
- fieldMax[j], s);
- else
- fprintf(fout, fieldNotNum[j] ? "%-*s" : "%*s", fieldMax[j], s);
- if (po->standard || (j + 1) < nFields)
- fputs(po->fieldSep, fout);
- }
- }
- if (po->html3)
- fputs("</tr>\n", fout);
- else
- fprintf(fout, "\n%s\n", border);
- return border;
-}
-
-
-static void
-output_row(FILE *fout, const PQprintOpt *po, const int nFields, char **fields,
- unsigned char *fieldNotNum, int *fieldMax, char *border,
- const int row_index)
-{
- int field_index; /* for loop index */
-
- if (po->html3)
- fputs("<tr>", fout);
- else if (po->standard)
- fputs(po->fieldSep, fout);
- for (field_index = 0; field_index < nFields; field_index++)
- {
- char *p = fields[row_index * nFields + field_index];
-
- if (po->html3)
- fprintf(fout, "<td align=\"%s\">%s</td>",
- fieldNotNum[field_index] ? "left" : "right", p ? p : "");
- else
- {
- fprintf(fout,
- fieldNotNum[field_index] ?
- (po->standard ? " %-*s " : "%-*s") :
- (po->standard ? " %*s " : "%*s"),
- fieldMax[field_index],
- p ? p : "");
- if (po->standard || field_index + 1 < nFields)
- fputs(po->fieldSep, fout);
- }
- if (p)
- free(p);
- }
- if (po->html3)
- fputs("</tr>", fout);
- else if (po->standard)
- fprintf(fout, "\n%s", border);
- fputc('\n', fout);
-}
-
-
-
-/*
- * really old printing routines
- */
-
-void
-PQdisplayTuples(const PGresult *res,
- FILE *fp, /* where to send the output */
- int fillAlign, /* pad the fields with spaces */
- const char *fieldSep, /* field separator */
- int printHeader, /* display headers? */
- int quiet
-)
-{
-#define DEFAULT_FIELD_SEP " "
-
- int i,
- j;
- int nFields;
- int nTuples;
- int *fLength = NULL;
-
- if (fieldSep == NULL)
- fieldSep = DEFAULT_FIELD_SEP;
-
- /* Get some useful info about the results */
- nFields = PQnfields(res);
- nTuples = PQntuples(res);
-
- if (fp == NULL)
- fp = stdout;
-
- /* Figure the field lengths to align to */
- /* will be somewhat time consuming for very large results */
- if (fillAlign)
- {
- fLength = (int *) malloc(nFields * sizeof(int));
- if (!fLength)
- {
- fprintf(stderr, libpq_gettext("out of memory\n"));
- abort();
- }
-
- for (j = 0; j < nFields; j++)
- {
- fLength[j] = strlen(PQfname(res, j));
- for (i = 0; i < nTuples; i++)
- {
- int flen = PQgetlength(res, i, j);
-
- if (flen > fLength[j])
- fLength[j] = flen;
- }
- }
- }
-
- if (printHeader)
- {
- /* first, print out the attribute names */
- for (i = 0; i < nFields; i++)
- {
- fputs(PQfname(res, i), fp);
- if (fillAlign)
- fill(strlen(PQfname(res, i)), fLength[i], ' ', fp);
- fputs(fieldSep, fp);
- }
- fprintf(fp, "\n");
-
- /* Underline the attribute names */
- for (i = 0; i < nFields; i++)
- {
- if (fillAlign)
- fill(0, fLength[i], '-', fp);
- fputs(fieldSep, fp);
- }
- fprintf(fp, "\n");
- }
-
- /* next, print out the instances */
- for (i = 0; i < nTuples; i++)
- {
- for (j = 0; j < nFields; j++)
- {
- fprintf(fp, "%s", PQgetvalue(res, i, j));
- if (fillAlign)
- fill(strlen(PQgetvalue(res, i, j)), fLength[j], ' ', fp);
- fputs(fieldSep, fp);
- }
- fprintf(fp, "\n");
- }
-
- if (!quiet)
- fprintf(fp, "\nQuery returned %d row%s.\n", PQntuples(res),
- (PQntuples(res) == 1) ? "" : "s");
-
- fflush(fp);
-
- if (fLength)
- free(fLength);
-}
-
-
-
-void
-PQprintTuples(const PGresult *res,
- FILE *fout, /* output stream */
- int PrintAttNames, /* print attribute names or not */
- int TerseOutput, /* delimiter bars or not? */
- int colWidth /* width of column, if 0, use variable width */
-)
-{
- int nFields;
- int nTups;
- int i,
- j;
- char formatString[80];
- char *tborder = NULL;
-
- nFields = PQnfields(res);
- nTups = PQntuples(res);
-
- if (colWidth > 0)
- sprintf(formatString, "%%s %%-%ds", colWidth);
- else
- sprintf(formatString, "%%s %%s");
-
- if (nFields > 0)
- { /* only print rows with at least 1 field. */
-
- if (!TerseOutput)
- {
- int width;
-
- width = nFields * 14;
- tborder = (char *) malloc(width + 1);
- if (!tborder)
- {
- fprintf(stderr, libpq_gettext("out of memory\n"));
- abort();
- }
- for (i = 0; i < width; i++)
- tborder[i] = '-';
- tborder[width] = '\0';
- fprintf(fout, "%s\n", tborder);
- }
-
- for (i = 0; i < nFields; i++)
- {
- if (PrintAttNames)
- {
- fprintf(fout, formatString,
- TerseOutput ? "" : "|",
- PQfname(res, i));
- }
- }
-
- if (PrintAttNames)
- {
- if (TerseOutput)
- fprintf(fout, "\n");
- else
- fprintf(fout, "|\n%s\n", tborder);
- }
-
- for (i = 0; i < nTups; i++)
- {
- for (j = 0; j < nFields; j++)
- {
- const char *pval = PQgetvalue(res, i, j);
-
- fprintf(fout, formatString,
- TerseOutput ? "" : "|",
- pval ? pval : "");
- }
- if (TerseOutput)
- fprintf(fout, "\n");
- else
- fprintf(fout, "|\n%s\n", tborder);
- }
- }
-
- if (tborder)
- free(tborder);
-}
-
-
-/* simply send out max-length number of filler characters to fp */
-
-static void
-fill(int length, int max, char filler, FILE *fp)
-{
- int count;
-
- count = max - length;
- while (count-- >= 0)
- putc(filler, fp);
-}
diff --git a/libpq/fe-protocol2.c b/libpq/fe-protocol2.c
deleted file mode 100644
index f1b90f3..0000000
--- a/libpq/fe-protocol2.c
+++ /dev/null
@@ -1,1623 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * fe-protocol2.c
- * functions that are specific to frontend/backend protocol version 2
- *
- * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- *
- * IDENTIFICATION
- * src/interfaces/libpq/fe-protocol2.c
- *
- *-------------------------------------------------------------------------
- */
-#include "postgres_fe.h"
-
-#include <ctype.h>
-#include <fcntl.h>
-
-#include "libpq-fe.h"
-#include "libpq-int.h"
-
-
-#ifdef WIN32
-#include "win32.h"
-#else
-#include <unistd.h>
-#include <netinet/in.h>
-#ifdef HAVE_NETINET_TCP_H
-#include <netinet/tcp.h>
-#endif
-#include <arpa/inet.h>
-#endif
-
-
-static int getRowDescriptions(PGconn *conn);
-static int getAnotherTuple(PGconn *conn, bool binary);
-static int pqGetErrorNotice2(PGconn *conn, bool isError);
-static void checkXactStatus(PGconn *conn, const char *cmdTag);
-static int getNotify(PGconn *conn);
-
-
-/*
- * pqSetenvPoll
- *
- * Polls the process of passing the values of a standard set of environment
- * variables to the backend.
- */
-PostgresPollingStatusType
-pqSetenvPoll(PGconn *conn)
-{
- PGresult *res;
-
- if (conn == NULL || conn->status == CONNECTION_BAD)
- return PGRES_POLLING_FAILED;
-
- /* Check whether there are any data for us */
- switch (conn->setenv_state)
- {
- /* These are reading states */
- case SETENV_STATE_CLIENT_ENCODING_WAIT:
- case SETENV_STATE_OPTION_WAIT:
- case SETENV_STATE_QUERY1_WAIT:
- case SETENV_STATE_QUERY2_WAIT:
- {
- /* Load waiting data */
- int n = pqReadData(conn);
-
- if (n < 0)
- goto error_return;
- if (n == 0)
- return PGRES_POLLING_READING;
-
- break;
- }
-
- /* These are writing states, so we just proceed. */
- case SETENV_STATE_CLIENT_ENCODING_SEND:
- case SETENV_STATE_OPTION_SEND:
- case SETENV_STATE_QUERY1_SEND:
- case SETENV_STATE_QUERY2_SEND:
- break;
-
- /* Should we raise an error if called when not active? */
- case SETENV_STATE_IDLE:
- return PGRES_POLLING_OK;
-
- default:
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext(
- "invalid setenv state %c, "
- "probably indicative of memory corruption\n"
- ),
- conn->setenv_state);
- goto error_return;
- }
-
- /* We will loop here until there is nothing left to do in this call. */
- for (;;)
- {
- switch (conn->setenv_state)
- {
- /*
- * The _CLIENT_ENCODING_SEND code is slightly different from
- * _OPTION_SEND below (e.g., no getenv() call), which is why a
- * different state is used.
- */
- case SETENV_STATE_CLIENT_ENCODING_SEND:
- {
- char setQuery[100]; /* note length limit in
- * sprintf below */
- const char *val = conn->client_encoding_initial;
-
- if (val)
- {
- if (pg_strcasecmp(val, "default") == 0)
- sprintf(setQuery, "SET client_encoding = DEFAULT");
- else
- sprintf(setQuery, "SET client_encoding = '%.60s'",
- val);
-#ifdef CONNECTDEBUG
- fprintf(stderr,
- "Sending client_encoding with %s\n",
- setQuery);
-#endif
- if (!PQsendQuery(conn, setQuery))
- goto error_return;
-
- conn->setenv_state = SETENV_STATE_CLIENT_ENCODING_WAIT;
- }
- else
- conn->setenv_state = SETENV_STATE_OPTION_SEND;
- break;
- }
-
- case SETENV_STATE_OPTION_SEND:
- {
- /*
- * Send SET commands for stuff directed by Environment
- * Options. Note: we assume that SET commands won't start
- * transaction blocks, even in a 7.3 server with
- * autocommit off.
- */
- char setQuery[100]; /* note length limit in
- * sprintf below */
-
- if (conn->next_eo->envName)
- {
- const char *val;
-
- if ((val = getenv(conn->next_eo->envName)))
- {
- if (pg_strcasecmp(val, "default") == 0)
- sprintf(setQuery, "SET %s = DEFAULT",
- conn->next_eo->pgName);
- else
- sprintf(setQuery, "SET %s = '%.60s'",
- conn->next_eo->pgName, val);
-#ifdef CONNECTDEBUG
- fprintf(stderr,
- "Use environment variable %s to send %s\n",
- conn->next_eo->envName, setQuery);
-#endif
- if (!PQsendQuery(conn, setQuery))
- goto error_return;
-
- conn->setenv_state = SETENV_STATE_OPTION_WAIT;
- }
- else
- conn->next_eo++;
- }
- else
- {
- /* No more options to send, so move on to querying */
- conn->setenv_state = SETENV_STATE_QUERY1_SEND;
- }
- break;
- }
-
- case SETENV_STATE_CLIENT_ENCODING_WAIT:
- {
- if (PQisBusy(conn))
- return PGRES_POLLING_READING;
-
- res = PQgetResult(conn);
-
- if (res)
- {
- if (PQresultStatus(res) != PGRES_COMMAND_OK)
- {
- PQclear(res);
- goto error_return;
- }
- PQclear(res);
- /* Keep reading until PQgetResult returns NULL */
- }
- else
- {
- /* Query finished, so send the next option */
- conn->setenv_state = SETENV_STATE_OPTION_SEND;
- }
- break;
- }
-
- case SETENV_STATE_OPTION_WAIT:
- {
- if (PQisBusy(conn))
- return PGRES_POLLING_READING;
-
- res = PQgetResult(conn);
-
- if (res)
- {
- if (PQresultStatus(res) != PGRES_COMMAND_OK)
- {
- PQclear(res);
- goto error_return;
- }
- PQclear(res);
- /* Keep reading until PQgetResult returns NULL */
- }
- else
- {
- /* Query finished, so send the next option */
- conn->next_eo++;
- conn->setenv_state = SETENV_STATE_OPTION_SEND;
- }
- break;
- }
-
- case SETENV_STATE_QUERY1_SEND:
- {
- /*
- * Issue query to get information we need. Here we must
- * use begin/commit in case autocommit is off by default
- * in a 7.3 server.
- *
- * Note: version() exists in all protocol-2.0-supporting
- * backends. In 7.3 it would be safer to write
- * pg_catalog.version(), but we can't do that without
- * causing problems on older versions.
- */
- if (!PQsendQuery(conn, "begin; select version(); end"))
- goto error_return;
-
- conn->setenv_state = SETENV_STATE_QUERY1_WAIT;
- return PGRES_POLLING_READING;
- }
-
- case SETENV_STATE_QUERY1_WAIT:
- {
- if (PQisBusy(conn))
- return PGRES_POLLING_READING;
-
- res = PQgetResult(conn);
-
- if (res)
- {
- char *val;
-
- if (PQresultStatus(res) == PGRES_COMMAND_OK)
- {
- /* ignore begin/commit command results */
- PQclear(res);
- continue;
- }
-
- if (PQresultStatus(res) != PGRES_TUPLES_OK ||
- PQntuples(res) != 1)
- {
- PQclear(res);
- goto error_return;
- }
-
- /*
- * Extract server version and save as if
- * ParameterStatus
- */
- val = PQgetvalue(res, 0, 0);
- if (val && strncmp(val, "PostgreSQL ", 11) == 0)
- {
- char *ptr;
-
- /* strip off PostgreSQL part */
- val += 11;
-
- /*
- * strip off platform part (scribbles on result,
- * naughty naughty)
- */
- ptr = strchr(val, ' ');
- if (ptr)
- *ptr = '\0';
-
- pqSaveParameterStatus(conn, "server_version",
- val);
- }
-
- PQclear(res);
- /* Keep reading until PQgetResult returns NULL */
- }
- else
- {
- /* Query finished, move to next */
- conn->setenv_state = SETENV_STATE_QUERY2_SEND;
- }
- break;
- }
-
- case SETENV_STATE_QUERY2_SEND:
- {
- const char *query;
-
- /*
- * pg_client_encoding does not exist in pre-7.2 servers.
- * So we need to be prepared for an error here. Do *not*
- * start a transaction block, except in 7.3 servers where
- * we need to prevent autocommit-off from starting a
- * transaction anyway.
- */
- if (conn->sversion >= 70300 &&
- conn->sversion < 70400)
- query = "begin; select pg_catalog.pg_client_encoding(); end";
- else
- query = "select pg_client_encoding()";
- if (!PQsendQuery(conn, query))
- goto error_return;
-
- conn->setenv_state = SETENV_STATE_QUERY2_WAIT;
- return PGRES_POLLING_READING;
- }
-
- case SETENV_STATE_QUERY2_WAIT:
- {
- if (PQisBusy(conn))
- return PGRES_POLLING_READING;
-
- res = PQgetResult(conn);
-
- if (res)
- {
- const char *val;
-
- if (PQresultStatus(res) == PGRES_COMMAND_OK)
- {
- /* ignore begin/commit command results */
- PQclear(res);
- continue;
- }
-
- if (PQresultStatus(res) == PGRES_TUPLES_OK &&
- PQntuples(res) == 1)
- {
- /* Extract client encoding and save it */
- val = PQgetvalue(res, 0, 0);
- if (val && *val) /* null should not happen, but */
- pqSaveParameterStatus(conn, "client_encoding",
- val);
- }
- else
- {
- /*
- * Error: presumably function not available, so
- * use PGCLIENTENCODING or SQL_ASCII as the
- * fallback.
- */
- val = getenv("PGCLIENTENCODING");
- if (val && *val)
- pqSaveParameterStatus(conn, "client_encoding",
- val);
- else
- pqSaveParameterStatus(conn, "client_encoding",
- "SQL_ASCII");
- }
-
- PQclear(res);
- /* Keep reading until PQgetResult returns NULL */
- }
- else
- {
- /* Query finished, so we're done */
- conn->setenv_state = SETENV_STATE_IDLE;
- return PGRES_POLLING_OK;
- }
- break;
- }
-
- default:
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("invalid state %c, "
- "probably indicative of memory corruption\n"),
- conn->setenv_state);
- goto error_return;
- }
- }
-
- /* Unreachable */
-
-error_return:
- conn->setenv_state = SETENV_STATE_IDLE;
- return PGRES_POLLING_FAILED;
-}
-
-
-/*
- * parseInput: if appropriate, parse input data from backend
- * until input is exhausted or a stopping state is reached.
- * Note that this function will NOT attempt to read more data from the backend.
- */
-void
-pqParseInput2(PGconn *conn)
-{
- char id;
-
- /*
- * Loop to parse successive complete messages available in the buffer.
- */
- for (;;)
- {
- /*
- * Quit if in COPY_OUT state: we expect raw data from the server until
- * PQendcopy is called. Don't try to parse it according to the normal
- * protocol. (This is bogus. The data lines ought to be part of the
- * protocol and have identifying leading characters.)
- */
- if (conn->asyncStatus == PGASYNC_COPY_OUT)
- return;
-
- /*
- * OK to try to read a message type code.
- */
- conn->inCursor = conn->inStart;
- if (pqGetc(&id, conn))
- return;
-
- /*
- * NOTIFY and NOTICE messages can happen in any state besides COPY
- * OUT; always process them right away.
- *
- * Most other messages should only be processed while in BUSY state.
- * (In particular, in READY state we hold off further parsing until
- * the application collects the current PGresult.)
- *
- * However, if the state is IDLE then we got trouble; we need to deal
- * with the unexpected message somehow.
- */
- if (id == 'A')
- {
- if (getNotify(conn))
- return;
- }
- else if (id == 'N')
- {
- if (pqGetErrorNotice2(conn, false))
- return;
- }
- else if (conn->asyncStatus != PGASYNC_BUSY)
- {
- /* If not IDLE state, just wait ... */
- if (conn->asyncStatus != PGASYNC_IDLE)
- return;
-
- /*
- * Unexpected message in IDLE state; need to recover somehow.
- * ERROR messages are displayed using the notice processor;
- * anything else is just dropped on the floor after displaying a
- * suitable warning notice. (An ERROR is very possibly the
- * backend telling us why it is about to close the connection, so
- * we don't want to just discard it...)
- */
- if (id == 'E')
- {
- if (pqGetErrorNotice2(conn, false /* treat as notice */ ))
- return;
- }
- else
- {
- pqInternalNotice(&conn->noticeHooks,
- "message type 0x%02x arrived from server while idle",
- id);
- /* Discard the unexpected message; good idea?? */
- conn->inStart = conn->inEnd;
- break;
- }
- }
- else
- {
- /*
- * In BUSY state, we can process everything.
- */
- switch (id)
- {
- case 'C': /* command complete */
- if (pqGets(&conn->workBuffer, conn))
- return;
- if (conn->result == NULL)
- {
- conn->result = PQmakeEmptyPGresult(conn,
- PGRES_COMMAND_OK);
- if (!conn->result)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("out of memory"));
- pqSaveErrorResult(conn);
- }
- }
- if (conn->result)
- {
- strlcpy(conn->result->cmdStatus, conn->workBuffer.data,
- CMDSTATUS_LEN);
- }
- checkXactStatus(conn, conn->workBuffer.data);
- conn->asyncStatus = PGASYNC_READY;
- break;
- case 'E': /* error return */
- if (pqGetErrorNotice2(conn, true))
- return;
- conn->asyncStatus = PGASYNC_READY;
- break;
- case 'Z': /* backend is ready for new query */
- conn->asyncStatus = PGASYNC_IDLE;
- break;
- case 'I': /* empty query */
- /* read and throw away the closing '\0' */
- if (pqGetc(&id, conn))
- return;
- if (id != '\0')
- pqInternalNotice(&conn->noticeHooks,
- "unexpected character %c following empty query response (\"I\" message)",
- id);
- if (conn->result == NULL)
- {
- conn->result = PQmakeEmptyPGresult(conn,
- PGRES_EMPTY_QUERY);
- if (!conn->result)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("out of memory"));
- pqSaveErrorResult(conn);
- }
- }
- conn->asyncStatus = PGASYNC_READY;
- break;
- case 'K': /* secret key data from the backend */
-
- /*
- * This is expected only during backend startup, but it's
- * just as easy to handle it as part of the main loop.
- * Save the data and continue processing.
- */
- if (pqGetInt(&(conn->be_pid), 4, conn))
- return;
- if (pqGetInt(&(conn->be_key), 4, conn))
- return;
- break;
- case 'P': /* synchronous (normal) portal */
- if (pqGets(&conn->workBuffer, conn))
- return;
- /* We pretty much ignore this message type... */
- break;
- case 'T': /* row descriptions (start of query results) */
- if (conn->result == NULL)
- {
- /* First 'T' in a query sequence */
- if (getRowDescriptions(conn))
- return;
- /* getRowDescriptions() moves inStart itself */
- continue;
- }
- else
- {
- /*
- * A new 'T' message is treated as the start of
- * another PGresult. (It is not clear that this is
- * really possible with the current backend.) We stop
- * parsing until the application accepts the current
- * result.
- */
- conn->asyncStatus = PGASYNC_READY;
- return;
- }
- break;
- case 'D': /* ASCII data tuple */
- if (conn->result != NULL)
- {
- /* Read another tuple of a normal query response */
- if (getAnotherTuple(conn, FALSE))
- return;
- /* getAnotherTuple() moves inStart itself */
- continue;
- }
- else
- {
- pqInternalNotice(&conn->noticeHooks,
- "server sent data (\"D\" message) without prior row description (\"T\" message)");
- /* Discard the unexpected message; good idea?? */
- conn->inStart = conn->inEnd;
- return;
- }
- break;
- case 'B': /* Binary data tuple */
- if (conn->result != NULL)
- {
- /* Read another tuple of a normal query response */
- if (getAnotherTuple(conn, TRUE))
- return;
- /* getAnotherTuple() moves inStart itself */
- continue;
- }
- else
- {
- pqInternalNotice(&conn->noticeHooks,
- "server sent binary data (\"B\" message) without prior row description (\"T\" message)");
- /* Discard the unexpected message; good idea?? */
- conn->inStart = conn->inEnd;
- return;
- }
- break;
- case 'G': /* Start Copy In */
- conn->asyncStatus = PGASYNC_COPY_IN;
- break;
- case 'H': /* Start Copy Out */
- conn->asyncStatus = PGASYNC_COPY_OUT;
- break;
-
- /*
- * Don't need to process CopyBothResponse here because it
- * never arrives from the server during protocol 2.0.
- */
- default:
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext(
- "unexpected response from server; first received character was \"%c\"\n"),
- id);
- /* build an error result holding the error message */
- pqSaveErrorResult(conn);
- /* Discard the unexpected message; good idea?? */
- conn->inStart = conn->inEnd;
- conn->asyncStatus = PGASYNC_READY;
- return;
- } /* switch on protocol character */
- }
- /* Successfully consumed this message */
- conn->inStart = conn->inCursor;
- }
-}
-
-/*
- * parseInput subroutine to read a 'T' (row descriptions) message.
- * We build a PGresult structure containing the attribute data.
- * Returns: 0 if completed message, EOF if error or not enough data
- * received yet.
- *
- * Note that if we run out of data, we have to suspend and reprocess
- * the message after more data is received. Otherwise, conn->inStart
- * must get advanced past the processed data.
- */
-static int
-getRowDescriptions(PGconn *conn)
-{
- PGresult *result;
- int nfields;
- const char *errmsg;
- int i;
-
- result = PQmakeEmptyPGresult(conn, PGRES_TUPLES_OK);
- if (!result)
- {
- errmsg = NULL; /* means "out of memory", see below */
- goto advance_and_error;
- }
-
- /* parseInput already read the 'T' label. */
- /* the next two bytes are the number of fields */
- if (pqGetInt(&(result->numAttributes), 2, conn))
- goto EOFexit;
- nfields = result->numAttributes;
-
- /* allocate space for the attribute descriptors */
- if (nfields > 0)
- {
- result->attDescs = (PGresAttDesc *)
- pqResultAlloc(result, nfields * sizeof(PGresAttDesc), TRUE);
- if (!result->attDescs)
- {
- errmsg = NULL; /* means "out of memory", see below */
- goto advance_and_error;
- }
- MemSet(result->attDescs, 0, nfields * sizeof(PGresAttDesc));
- }
-
- /* get type info */
- for (i = 0; i < nfields; i++)
- {
- int typid;
- int typlen;
- int atttypmod;
-
- if (pqGets(&conn->workBuffer, conn) ||
- pqGetInt(&typid, 4, conn) ||
- pqGetInt(&typlen, 2, conn) ||
- pqGetInt(&atttypmod, 4, conn))
- goto EOFexit;
-
- /*
- * Since pqGetInt treats 2-byte integers as unsigned, we need to
- * coerce the result to signed form.
- */
- typlen = (int) ((int16) typlen);
-
- result->attDescs[i].name = pqResultStrdup(result,
- conn->workBuffer.data);
- if (!result->attDescs[i].name)
- {
- errmsg = NULL; /* means "out of memory", see below */
- goto advance_and_error;
- }
- result->attDescs[i].tableid = 0;
- result->attDescs[i].columnid = 0;
- result->attDescs[i].format = 0;
- result->attDescs[i].typid = typid;
- result->attDescs[i].typlen = typlen;
- result->attDescs[i].atttypmod = atttypmod;
- }
-
- /* Success! */
- conn->result = result;
-
- /* Advance inStart to show that the "T" message has been processed. */
- conn->inStart = conn->inCursor;
-
- /*
- * We could perform additional setup for the new result set here, but for
- * now there's nothing else to do.
- */
-
- /* And we're done. */
- return 0;
-
-advance_and_error:
-
- /*
- * Discard the failed message. Unfortunately we don't know for sure where
- * the end is, so just throw away everything in the input buffer. This is
- * not very desirable but it's the best we can do in protocol v2.
- */
- conn->inStart = conn->inEnd;
-
- /*
- * Replace partially constructed result with an error result. First
- * discard the old result to try to win back some memory.
- */
- pqClearAsyncResult(conn);
-
- /*
- * If preceding code didn't provide an error message, assume "out of
- * memory" was meant. The advantage of having this special case is that
- * freeing the old result first greatly improves the odds that gettext()
- * will succeed in providing a translation.
- */
- if (!errmsg)
- errmsg = libpq_gettext("out of memory for query result");
-
- printfPQExpBuffer(&conn->errorMessage, "%s\n", errmsg);
-
- /*
- * XXX: if PQmakeEmptyPGresult() fails, there's probably not much we can
- * do to recover...
- */
- conn->result = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR);
- conn->asyncStatus = PGASYNC_READY;
-
-EOFexit:
- if (result && result != conn->result)
- PQclear(result);
- return EOF;
-}
-
-/*
- * parseInput subroutine to read a 'B' or 'D' (row data) message.
- * We fill rowbuf with column pointers and then call the row processor.
- * Returns: 0 if completed message, EOF if error or not enough data
- * received yet.
- *
- * Note that if we run out of data, we have to suspend and reprocess
- * the message after more data is received. Otherwise, conn->inStart
- * must get advanced past the processed data.
- */
-static int
-getAnotherTuple(PGconn *conn, bool binary)
-{
- PGresult *result = conn->result;
- int nfields = result->numAttributes;
- const char *errmsg;
- PGdataValue *rowbuf;
-
- /* the backend sends us a bitmap of which attributes are null */
- char std_bitmap[64]; /* used unless it doesn't fit */
- char *bitmap = std_bitmap;
- int i;
- size_t nbytes; /* the number of bytes in bitmap */
- char bmap; /* One byte of the bitmap */
- int bitmap_index; /* Its index */
- int bitcnt; /* number of bits examined in current byte */
- int vlen; /* length of the current field value */
-
- /* Resize row buffer if needed */
- rowbuf = conn->rowBuf;
- if (nfields > conn->rowBufLen)
- {
- rowbuf = (PGdataValue *) realloc(rowbuf,
- nfields * sizeof(PGdataValue));
- if (!rowbuf)
- {
- errmsg = NULL; /* means "out of memory", see below */
- goto advance_and_error;
- }
- conn->rowBuf = rowbuf;
- conn->rowBufLen = nfields;
- }
-
- /* Save format specifier */
- result->binary = binary;
-
- /*
- * If it's binary, fix the column format indicators. We assume the
- * backend will consistently send either B or D, not a mix.
- */
- if (binary)
- {
- for (i = 0; i < nfields; i++)
- result->attDescs[i].format = 1;
- }
-
- /* Get the null-value bitmap */
- nbytes = (nfields + BITS_PER_BYTE - 1) / BITS_PER_BYTE;
- /* malloc() only for unusually large field counts... */
- if (nbytes > sizeof(std_bitmap))
- {
- bitmap = (char *) malloc(nbytes);
- if (!bitmap)
- {
- errmsg = NULL; /* means "out of memory", see below */
- goto advance_and_error;
- }
- }
-
- if (pqGetnchar(bitmap, nbytes, conn))
- goto EOFexit;
-
- /* Scan the fields */
- bitmap_index = 0;
- bmap = bitmap[bitmap_index];
- bitcnt = 0;
-
- for (i = 0; i < nfields; i++)
- {
- /* get the value length */
- if (!(bmap & 0200))
- vlen = NULL_LEN;
- else if (pqGetInt(&vlen, 4, conn))
- goto EOFexit;
- else
- {
- if (!binary)
- vlen = vlen - 4;
- if (vlen < 0)
- vlen = 0;
- }
- rowbuf[i].len = vlen;
-
- /*
- * rowbuf[i].value always points to the next address in the data
- * buffer even if the value is NULL. This allows row processors to
- * estimate data sizes more easily.
- */
- rowbuf[i].value = conn->inBuffer + conn->inCursor;
-
- /* Skip over the data value */
- if (vlen > 0)
- {
- if (pqSkipnchar(vlen, conn))
- goto EOFexit;
- }
-
- /* advance the bitmap stuff */
- bitcnt++;
- if (bitcnt == BITS_PER_BYTE)
- {
- bitmap_index++;
- bmap = bitmap[bitmap_index];
- bitcnt = 0;
- }
- else
- bmap <<= 1;
- }
-
- /* Release bitmap now if we allocated it */
- if (bitmap != std_bitmap)
- free(bitmap);
- bitmap = NULL;
-
- /* Advance inStart to show that the "D" message has been processed. */
- conn->inStart = conn->inCursor;
-
- /* Process the collected row */
- errmsg = NULL;
- if (pqRowProcessor(conn, &errmsg))
- return 0; /* normal, successful exit */
-
- goto set_error_result; /* pqRowProcessor failed, report it */
-
-advance_and_error:
-
- /*
- * Discard the failed message. Unfortunately we don't know for sure where
- * the end is, so just throw away everything in the input buffer. This is
- * not very desirable but it's the best we can do in protocol v2.
- */
- conn->inStart = conn->inEnd;
-
-set_error_result:
-
- /*
- * Replace partially constructed result with an error result. First
- * discard the old result to try to win back some memory.
- */
- pqClearAsyncResult(conn);
-
- /*
- * If preceding code didn't provide an error message, assume "out of
- * memory" was meant. The advantage of having this special case is that
- * freeing the old result first greatly improves the odds that gettext()
- * will succeed in providing a translation.
- */
- if (!errmsg)
- errmsg = libpq_gettext("out of memory for query result");
-
- printfPQExpBuffer(&conn->errorMessage, "%s\n", errmsg);
-
- /*
- * XXX: if PQmakeEmptyPGresult() fails, there's probably not much we can
- * do to recover...
- */
- conn->result = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR);
- conn->asyncStatus = PGASYNC_READY;
-
-EOFexit:
- if (bitmap != NULL && bitmap != std_bitmap)
- free(bitmap);
- return EOF;
-}
-
-
-/*
- * Attempt to read an Error or Notice response message.
- * This is possible in several places, so we break it out as a subroutine.
- * Entry: 'E' or 'N' message type has already been consumed.
- * Exit: returns 0 if successfully consumed message.
- * returns EOF if not enough data.
- */
-static int
-pqGetErrorNotice2(PGconn *conn, bool isError)
-{
- PGresult *res = NULL;
- PQExpBufferData workBuf;
- char *startp;
- char *splitp;
-
- /*
- * Since the message might be pretty long, we create a temporary
- * PQExpBuffer rather than using conn->workBuffer. workBuffer is intended
- * for stuff that is expected to be short.
- */
- initPQExpBuffer(&workBuf);
- if (pqGets(&workBuf, conn))
- goto failure;
-
- /*
- * Make a PGresult to hold the message. We temporarily lie about the
- * result status, so that PQmakeEmptyPGresult doesn't uselessly copy
- * conn->errorMessage.
- *
- * NB: This allocation can fail, if you run out of memory. The rest of the
- * function handles that gracefully, and we still try to set the error
- * message as the connection's error message.
- */
- res = PQmakeEmptyPGresult(conn, PGRES_EMPTY_QUERY);
- if (res)
- {
- res->resultStatus = isError ? PGRES_FATAL_ERROR : PGRES_NONFATAL_ERROR;
- res->errMsg = pqResultStrdup(res, workBuf.data);
- }
-
- /*
- * Break the message into fields. We can't do very much here, but we can
- * split the severity code off, and remove trailing newlines. Also, we use
- * the heuristic that the primary message extends only to the first
- * newline --- anything after that is detail message. (In some cases it'd
- * be better classed as hint, but we can hardly be expected to guess that
- * here.)
- */
- while (workBuf.len > 0 && workBuf.data[workBuf.len - 1] == '\n')
- workBuf.data[--workBuf.len] = '\0';
- splitp = strstr(workBuf.data, ": ");
- if (splitp)
- {
- /* what comes before the colon is severity */
- *splitp = '\0';
- pqSaveMessageField(res, PG_DIAG_SEVERITY, workBuf.data);
- startp = splitp + 3;
- }
- else
- {
- /* can't find a colon? oh well... */
- startp = workBuf.data;
- }
- splitp = strchr(startp, '\n');
- if (splitp)
- {
- /* what comes before the newline is primary message */
- *splitp++ = '\0';
- pqSaveMessageField(res, PG_DIAG_MESSAGE_PRIMARY, startp);
- /* the rest is detail; strip any leading whitespace */
- while (*splitp && isspace((unsigned char) *splitp))
- splitp++;
- pqSaveMessageField(res, PG_DIAG_MESSAGE_DETAIL, splitp);
- }
- else
- {
- /* single-line message, so all primary */
- pqSaveMessageField(res, PG_DIAG_MESSAGE_PRIMARY, startp);
- }
-
- /*
- * Either save error as current async result, or just emit the notice.
- * Also, if it's an error and we were in a transaction block, assume the
- * server has now gone to error-in-transaction state.
- */
- if (isError)
- {
- pqClearAsyncResult(conn);
- conn->result = res;
- resetPQExpBuffer(&conn->errorMessage);
- if (res && !PQExpBufferDataBroken(workBuf) && res->errMsg)
- appendPQExpBufferStr(&conn->errorMessage, res->errMsg);
- else
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("out of memory"));
- if (conn->xactStatus == PQTRANS_INTRANS)
- conn->xactStatus = PQTRANS_INERROR;
- }
- else
- {
- if (res)
- {
- if (res->noticeHooks.noticeRec != NULL)
- (*res->noticeHooks.noticeRec) (res->noticeHooks.noticeRecArg, res);
- PQclear(res);
- }
- }
-
- termPQExpBuffer(&workBuf);
- return 0;
-
-failure:
- if (res)
- PQclear(res);
- termPQExpBuffer(&workBuf);
- return EOF;
-}
-
-/*
- * checkXactStatus - attempt to track transaction-block status of server
- *
- * This is called each time we receive a command-complete message. By
- * watching for messages from BEGIN/COMMIT/ROLLBACK commands, we can do
- * a passable job of tracking the server's xact status. BUT: this does
- * not work at all on 7.3 servers with AUTOCOMMIT OFF. (Man, was that
- * feature ever a mistake.) Caveat user.
- *
- * The tags known here are all those used as far back as 7.0; is it worth
- * adding those from even-older servers?
- */
-static void
-checkXactStatus(PGconn *conn, const char *cmdTag)
-{
- if (strcmp(cmdTag, "BEGIN") == 0)
- conn->xactStatus = PQTRANS_INTRANS;
- else if (strcmp(cmdTag, "COMMIT") == 0)
- conn->xactStatus = PQTRANS_IDLE;
- else if (strcmp(cmdTag, "ROLLBACK") == 0)
- conn->xactStatus = PQTRANS_IDLE;
- else if (strcmp(cmdTag, "START TRANSACTION") == 0) /* 7.3 only */
- conn->xactStatus = PQTRANS_INTRANS;
-
- /*
- * Normally we get into INERROR state by detecting an Error message.
- * However, if we see one of these tags then we know for sure the server
- * is in abort state ...
- */
- else if (strcmp(cmdTag, "*ABORT STATE*") == 0) /* pre-7.3 only */
- conn->xactStatus = PQTRANS_INERROR;
-}
-
-/*
- * Attempt to read a Notify response message.
- * This is possible in several places, so we break it out as a subroutine.
- * Entry: 'A' message type and length have already been consumed.
- * Exit: returns 0 if successfully consumed Notify message.
- * returns EOF if not enough data.
- */
-static int
-getNotify(PGconn *conn)
-{
- int be_pid;
- int nmlen;
- PGnotify *newNotify;
-
- if (pqGetInt(&be_pid, 4, conn))
- return EOF;
- if (pqGets(&conn->workBuffer, conn))
- return EOF;
-
- /*
- * Store the relation name right after the PQnotify structure so it can
- * all be freed at once. We don't use NAMEDATALEN because we don't want
- * to tie this interface to a specific server name length.
- */
- nmlen = strlen(conn->workBuffer.data);
- newNotify = (PGnotify *) malloc(sizeof(PGnotify) + nmlen + 1);
- if (newNotify)
- {
- newNotify->relname = (char *) newNotify + sizeof(PGnotify);
- strcpy(newNotify->relname, conn->workBuffer.data);
- /* fake up an empty-string extra field */
- newNotify->extra = newNotify->relname + nmlen;
- newNotify->be_pid = be_pid;
- newNotify->next = NULL;
- if (conn->notifyTail)
- conn->notifyTail->next = newNotify;
- else
- conn->notifyHead = newNotify;
- conn->notifyTail = newNotify;
- }
-
- return 0;
-}
-
-
-/*
- * PQgetCopyData - read a row of data from the backend during COPY OUT
- *
- * If successful, sets *buffer to point to a malloc'd row of data, and
- * returns row length (always > 0) as result.
- * Returns 0 if no row available yet (only possible if async is true),
- * -1 if end of copy (consult PQgetResult), or -2 if error (consult
- * PQerrorMessage).
- */
-int
-pqGetCopyData2(PGconn *conn, char **buffer, int async)
-{
- bool found;
- int msgLength;
-
- for (;;)
- {
- /*
- * Do we have a complete line of data?
- */
- conn->inCursor = conn->inStart;
- found = false;
- while (conn->inCursor < conn->inEnd)
- {
- char c = conn->inBuffer[conn->inCursor++];
-
- if (c == '\n')
- {
- found = true;
- break;
- }
- }
- if (!found)
- goto nodata;
- msgLength = conn->inCursor - conn->inStart;
-
- /*
- * If it's the end-of-data marker, consume it, exit COPY_OUT mode, and
- * let caller read status with PQgetResult().
- */
- if (msgLength == 3 &&
- strncmp(&conn->inBuffer[conn->inStart], "\\.\n", 3) == 0)
- {
- conn->inStart = conn->inCursor;
- conn->asyncStatus = PGASYNC_BUSY;
- return -1;
- }
-
- /*
- * Pass the line back to the caller.
- */
- *buffer = (char *) malloc(msgLength + 1);
- if (*buffer == NULL)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("out of memory\n"));
- return -2;
- }
- memcpy(*buffer, &conn->inBuffer[conn->inStart], msgLength);
- (*buffer)[msgLength] = '\0'; /* Add terminating null */
-
- /* Mark message consumed */
- conn->inStart = conn->inCursor;
-
- return msgLength;
-
-nodata:
- /* Don't block if async read requested */
- if (async)
- return 0;
- /* Need to load more data */
- if (pqWait(TRUE, FALSE, conn) ||
- pqReadData(conn) < 0)
- return -2;
- }
-}
-
-
-/*
- * PQgetline - gets a newline-terminated string from the backend.
- *
- * See fe-exec.c for documentation.
- */
-int
-pqGetline2(PGconn *conn, char *s, int maxlen)
-{
- int result = 1; /* return value if buffer overflows */
-
- if (conn->sock == PGINVALID_SOCKET ||
- conn->asyncStatus != PGASYNC_COPY_OUT)
- {
- *s = '\0';
- return EOF;
- }
-
- /*
- * Since this is a purely synchronous routine, we don't bother to maintain
- * conn->inCursor; there is no need to back up.
- */
- while (maxlen > 1)
- {
- if (conn->inStart < conn->inEnd)
- {
- char c = conn->inBuffer[conn->inStart++];
-
- if (c == '\n')
- {
- result = 0; /* success exit */
- break;
- }
- *s++ = c;
- maxlen--;
- }
- else
- {
- /* need to load more data */
- if (pqWait(TRUE, FALSE, conn) ||
- pqReadData(conn) < 0)
- {
- result = EOF;
- break;
- }
- }
- }
- *s = '\0';
-
- return result;
-}
-
-/*
- * PQgetlineAsync - gets a COPY data row without blocking.
- *
- * See fe-exec.c for documentation.
- */
-int
-pqGetlineAsync2(PGconn *conn, char *buffer, int bufsize)
-{
- int avail;
-
- if (conn->asyncStatus != PGASYNC_COPY_OUT)
- return -1; /* we are not doing a copy... */
-
- /*
- * Move data from libpq's buffer to the caller's. We want to accept data
- * only in units of whole lines, not partial lines. This ensures that we
- * can recognize the terminator line "\\.\n". (Otherwise, if it happened
- * to cross a packet/buffer boundary, we might hand the first one or two
- * characters off to the caller, which we shouldn't.)
- */
-
- conn->inCursor = conn->inStart;
-
- avail = bufsize;
- while (avail > 0 && conn->inCursor < conn->inEnd)
- {
- char c = conn->inBuffer[conn->inCursor++];
-
- *buffer++ = c;
- --avail;
- if (c == '\n')
- {
- /* Got a complete line; mark the data removed from libpq */
- conn->inStart = conn->inCursor;
- /* Is it the endmarker line? */
- if (bufsize - avail == 3 && buffer[-3] == '\\' && buffer[-2] == '.')
- return -1;
- /* No, return the data line to the caller */
- return bufsize - avail;
- }
- }
-
- /*
- * We don't have a complete line. We'd prefer to leave it in libpq's
- * buffer until the rest arrives, but there is a special case: what if the
- * line is longer than the buffer the caller is offering us? In that case
- * we'd better hand over a partial line, else we'd get into an infinite
- * loop. Do this in a way that ensures we can't misrecognize a terminator
- * line later: leave last 3 characters in libpq buffer.
- */
- if (avail == 0 && bufsize > 3)
- {
- conn->inStart = conn->inCursor - 3;
- return bufsize - 3;
- }
- return 0;
-}
-
-/*
- * PQendcopy
- *
- * See fe-exec.c for documentation.
- */
-int
-pqEndcopy2(PGconn *conn)
-{
- PGresult *result;
-
- if (conn->asyncStatus != PGASYNC_COPY_IN &&
- conn->asyncStatus != PGASYNC_COPY_OUT)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("no COPY in progress\n"));
- return 1;
- }
-
- /*
- * make sure no data is waiting to be sent, abort if we are non-blocking
- * and the flush fails
- */
- if (pqFlush(conn) && pqIsnonblocking(conn))
- return 1;
-
- /* non blocking connections may have to abort at this point. */
- if (pqIsnonblocking(conn) && PQisBusy(conn))
- return 1;
-
- /* Return to active duty */
- conn->asyncStatus = PGASYNC_BUSY;
- resetPQExpBuffer(&conn->errorMessage);
-
- /* Wait for the completion response */
- result = PQgetResult(conn);
-
- /* Expecting a successful result */
- if (result && result->resultStatus == PGRES_COMMAND_OK)
- {
- PQclear(result);
- return 0;
- }
-
- /*
- * Trouble. For backwards-compatibility reasons, we issue the error
- * message as if it were a notice (would be nice to get rid of this
- * silliness, but too many apps probably don't handle errors from
- * PQendcopy reasonably). Note that the app can still obtain the error
- * status from the PGconn object.
- */
- if (conn->errorMessage.len > 0)
- {
- /* We have to strip the trailing newline ... pain in neck... */
- char svLast = conn->errorMessage.data[conn->errorMessage.len - 1];
-
- if (svLast == '\n')
- conn->errorMessage.data[conn->errorMessage.len - 1] = '\0';
- pqInternalNotice(&conn->noticeHooks, "%s", conn->errorMessage.data);
- conn->errorMessage.data[conn->errorMessage.len - 1] = svLast;
- }
-
- PQclear(result);
-
- /*
- * The worst case is that we've lost sync with the backend entirely due to
- * application screwup of the copy in/out protocol. To recover, reset the
- * connection (talk about using a sledgehammer...)
- */
- pqInternalNotice(&conn->noticeHooks,
- "lost synchronization with server, resetting connection");
-
- /*
- * Users doing non-blocking connections need to handle the reset
- * themselves, they'll need to check the connection status if we return an
- * error.
- */
- if (pqIsnonblocking(conn))
- PQresetStart(conn);
- else
- PQreset(conn);
-
- return 1;
-}
-
-
-/*
- * PQfn - Send a function call to the POSTGRES backend.
- *
- * See fe-exec.c for documentation.
- */
-PGresult *
-pqFunctionCall2(PGconn *conn, Oid fnid,
- int *result_buf, int *actual_result_len,
- int result_is_int,
- const PQArgBlock *args, int nargs)
-{
- bool needInput = false;
- ExecStatusType status = PGRES_FATAL_ERROR;
- char id;
- int i;
-
- /* PQfn already validated connection state */
-
- if (pqPutMsgStart('F', false, conn) < 0 || /* function call msg */
- pqPuts(" ", conn) < 0 || /* dummy string */
- pqPutInt(fnid, 4, conn) != 0 || /* function id */
- pqPutInt(nargs, 4, conn) != 0) /* # of args */
- {
- pqHandleSendFailure(conn);
- return NULL;
- }
-
- for (i = 0; i < nargs; ++i)
- { /* len.int4 + contents */
- if (pqPutInt(args[i].len, 4, conn))
- {
- pqHandleSendFailure(conn);
- return NULL;
- }
-
- if (args[i].isint)
- {
- if (pqPutInt(args[i].u.integer, 4, conn))
- {
- pqHandleSendFailure(conn);
- return NULL;
- }
- }
- else
- {
- if (pqPutnchar((char *) args[i].u.ptr, args[i].len, conn))
- {
- pqHandleSendFailure(conn);
- return NULL;
- }
- }
- }
-
- if (pqPutMsgEnd(conn) < 0 ||
- pqFlush(conn))
- {
- pqHandleSendFailure(conn);
- return NULL;
- }
-
- for (;;)
- {
- if (needInput)
- {
- /* Wait for some data to arrive (or for the channel to close) */
- if (pqWait(TRUE, FALSE, conn) ||
- pqReadData(conn) < 0)
- break;
- }
-
- /*
- * Scan the message. If we run out of data, loop around to try again.
- */
- conn->inCursor = conn->inStart;
- needInput = true;
-
- if (pqGetc(&id, conn))
- continue;
-
- /*
- * We should see V or E response to the command, but might get N
- * and/or A notices first. We also need to swallow the final Z before
- * returning.
- */
- switch (id)
- {
- case 'V': /* function result */
- if (pqGetc(&id, conn))
- continue;
- if (id == 'G')
- {
- /* function returned nonempty value */
- if (pqGetInt(actual_result_len, 4, conn))
- continue;
- if (result_is_int)
- {
- if (pqGetInt(result_buf, 4, conn))
- continue;
- }
- else
- {
- if (pqGetnchar((char *) result_buf,
- *actual_result_len,
- conn))
- continue;
- }
- if (pqGetc(&id, conn)) /* get the last '0' */
- continue;
- }
- if (id == '0')
- {
- /* correctly finished function result message */
- status = PGRES_COMMAND_OK;
- }
- else
- {
- /* The backend violates the protocol. */
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("protocol error: id=0x%x\n"),
- id);
- pqSaveErrorResult(conn);
- conn->inStart = conn->inCursor;
- return pqPrepareAsyncResult(conn);
- }
- break;
- case 'E': /* error return */
- if (pqGetErrorNotice2(conn, true))
- continue;
- status = PGRES_FATAL_ERROR;
- break;
- case 'A': /* notify message */
- /* handle notify and go back to processing return values */
- if (getNotify(conn))
- continue;
- break;
- case 'N': /* notice */
- /* handle notice and go back to processing return values */
- if (pqGetErrorNotice2(conn, false))
- continue;
- break;
- case 'Z': /* backend is ready for new query */
- /* consume the message and exit */
- conn->inStart = conn->inCursor;
- /* if we saved a result object (probably an error), use it */
- if (conn->result)
- return pqPrepareAsyncResult(conn);
- return PQmakeEmptyPGresult(conn, status);
- default:
- /* The backend violates the protocol. */
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("protocol error: id=0x%x\n"),
- id);
- pqSaveErrorResult(conn);
- conn->inStart = conn->inCursor;
- return pqPrepareAsyncResult(conn);
- }
- /* Completed this message, keep going */
- conn->inStart = conn->inCursor;
- needInput = false;
- }
-
- /*
- * We fall out of the loop only upon failing to read data.
- * conn->errorMessage has been set by pqWait or pqReadData. We want to
- * append it to any already-received error message.
- */
- pqSaveErrorResult(conn);
- return pqPrepareAsyncResult(conn);
-}
-
-
-/*
- * Construct startup packet
- *
- * Returns a malloc'd packet buffer, or NULL if out of memory
- */
-char *
-pqBuildStartupPacket2(PGconn *conn, int *packetlen,
- const PQEnvironmentOption *options)
-{
- StartupPacket *startpacket;
-
- *packetlen = sizeof(StartupPacket);
- startpacket = (StartupPacket *) malloc(sizeof(StartupPacket));
- if (!startpacket)
- return NULL;
-
- MemSet(startpacket, 0, sizeof(StartupPacket));
-
- startpacket->protoVersion = htonl(conn->pversion);
-
- /* strncpy is safe here: postmaster will handle full fields correctly */
- strncpy(startpacket->user, conn->pguser, SM_USER);
- strncpy(startpacket->database, conn->dbName, SM_DATABASE);
- strncpy(startpacket->tty, conn->pgtty, SM_TTY);
-
- if (conn->pgoptions)
- strncpy(startpacket->options, conn->pgoptions, SM_OPTIONS);
-
- return (char *) startpacket;
-}
diff --git a/libpq/fe-protocol3.c b/libpq/fe-protocol3.c
deleted file mode 100644
index a8a987a..0000000
--- a/libpq/fe-protocol3.c
+++ /dev/null
@@ -1,2204 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * fe-protocol3.c
- * functions that are specific to frontend/backend protocol version 3
- *
- * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- *
- * IDENTIFICATION
- * src/interfaces/libpq/fe-protocol3.c
- *
- *-------------------------------------------------------------------------
- */
-#include "postgres_fe.h"
-
-#include <ctype.h>
-#include <fcntl.h>
-
-#include "libpq-fe.h"
-#include "libpq-int.h"
-
-#include "mb/pg_wchar.h"
-
-#ifdef WIN32
-#include "win32.h"
-#else
-#include <unistd.h>
-#include <netinet/in.h>
-#ifdef HAVE_NETINET_TCP_H
-#include <netinet/tcp.h>
-#endif
-#include <arpa/inet.h>
-#endif
-
-
-/*
- * This macro lists the backend message types that could be "long" (more
- * than a couple of kilobytes).
- */
-#define VALID_LONG_MESSAGE_TYPE(id) \
- ((id) == 'T' || (id) == 'D' || (id) == 'd' || (id) == 'V' || \
- (id) == 'E' || (id) == 'N' || (id) == 'A')
-
-
-static void handleSyncLoss(PGconn *conn, char id, int msgLength);
-static int getRowDescriptions(PGconn *conn, int msgLength);
-static int getParamDescriptions(PGconn *conn, int msgLength);
-static int getAnotherTuple(PGconn *conn, int msgLength);
-static int getParameterStatus(PGconn *conn);
-static int getNotify(PGconn *conn);
-static int getCopyStart(PGconn *conn, ExecStatusType copytype);
-static int getReadyForQuery(PGconn *conn);
-static void reportErrorPosition(PQExpBuffer msg, const char *query,
- int loc, int encoding);
-static int build_startup_packet(const PGconn *conn, char *packet,
- const PQEnvironmentOption *options);
-
-
-/*
- * parseInput: if appropriate, parse input data from backend
- * until input is exhausted or a stopping state is reached.
- * Note that this function will NOT attempt to read more data from the backend.
- */
-void
-pqParseInput3(PGconn *conn)
-{
- char id;
- int msgLength;
- int avail;
-
- /*
- * Loop to parse successive complete messages available in the buffer.
- */
- for (;;)
- {
- /*
- * Try to read a message. First get the type code and length. Return
- * if not enough data.
- */
- conn->inCursor = conn->inStart;
- if (pqGetc(&id, conn))
- return;
- if (pqGetInt(&msgLength, 4, conn))
- return;
-
- /*
- * Try to validate message type/length here. A length less than 4 is
- * definitely broken. Large lengths should only be believed for a few
- * message types.
- */
- if (msgLength < 4)
- {
- handleSyncLoss(conn, id, msgLength);
- return;
- }
- if (msgLength > 30000 && !VALID_LONG_MESSAGE_TYPE(id))
- {
- handleSyncLoss(conn, id, msgLength);
- return;
- }
-
- /*
- * Can't process if message body isn't all here yet.
- */
- msgLength -= 4;
- avail = conn->inEnd - conn->inCursor;
- if (avail < msgLength)
- {
- /*
- * Before returning, enlarge the input buffer if needed to hold
- * the whole message. This is better than leaving it to
- * pqReadData because we can avoid multiple cycles of realloc()
- * when the message is large; also, we can implement a reasonable
- * recovery strategy if we are unable to make the buffer big
- * enough.
- */
- if (pqCheckInBufferSpace(conn->inCursor + (size_t) msgLength,
- conn))
- {
- /*
- * XXX add some better recovery code... plan is to skip over
- * the message using its length, then report an error. For the
- * moment, just treat this like loss of sync (which indeed it
- * might be!)
- */
- handleSyncLoss(conn, id, msgLength);
- }
- return;
- }
-
- /*
- * NOTIFY and NOTICE messages can happen in any state; always process
- * them right away.
- *
- * Most other messages should only be processed while in BUSY state.
- * (In particular, in READY state we hold off further parsing until
- * the application collects the current PGresult.)
- *
- * However, if the state is IDLE then we got trouble; we need to deal
- * with the unexpected message somehow.
- *
- * ParameterStatus ('S') messages are a special case: in IDLE state we
- * must process 'em (this case could happen if a new value was adopted
- * from config file due to SIGHUP), but otherwise we hold off until
- * BUSY state.
- */
- if (id == 'A')
- {
- if (getNotify(conn))
- return;
- }
- else if (id == 'N')
- {
- if (pqGetErrorNotice3(conn, false))
- return;
- }
- else if (conn->asyncStatus != PGASYNC_BUSY)
- {
- /* If not IDLE state, just wait ... */
- if (conn->asyncStatus != PGASYNC_IDLE)
- return;
-
- /*
- * Unexpected message in IDLE state; need to recover somehow.
- * ERROR messages are handled using the notice processor;
- * ParameterStatus is handled normally; anything else is just
- * dropped on the floor after displaying a suitable warning
- * notice. (An ERROR is very possibly the backend telling us why
- * it is about to close the connection, so we don't want to just
- * discard it...)
- */
- if (id == 'E')
- {
- if (pqGetErrorNotice3(conn, false /* treat as notice */ ))
- return;
- }
- else if (id == 'S')
- {
- if (getParameterStatus(conn))
- return;
- }
- else
- {
- pqInternalNotice(&conn->noticeHooks,
- "message type 0x%02x arrived from server while idle",
- id);
- /* Discard the unexpected message */
- conn->inCursor += msgLength;
- }
- }
- else
- {
- /*
- * In BUSY state, we can process everything.
- */
- switch (id)
- {
- case 'C': /* command complete */
- if (pqGets(&conn->workBuffer, conn))
- return;
- if (conn->result == NULL)
- {
- conn->result = PQmakeEmptyPGresult(conn,
- PGRES_COMMAND_OK);
- if (!conn->result)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("out of memory"));
- pqSaveErrorResult(conn);
- }
- }
- if (conn->result)
- strlcpy(conn->result->cmdStatus, conn->workBuffer.data,
- CMDSTATUS_LEN);
- conn->asyncStatus = PGASYNC_READY;
- break;
- case 'E': /* error return */
- if (pqGetErrorNotice3(conn, true))
- return;
- conn->asyncStatus = PGASYNC_READY;
- break;
- case 'Z': /* backend is ready for new query */
- if (getReadyForQuery(conn))
- return;
- conn->asyncStatus = PGASYNC_IDLE;
- break;
- case 'I': /* empty query */
- if (conn->result == NULL)
- {
- conn->result = PQmakeEmptyPGresult(conn,
- PGRES_EMPTY_QUERY);
- if (!conn->result)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("out of memory"));
- pqSaveErrorResult(conn);
- }
- }
- conn->asyncStatus = PGASYNC_READY;
- break;
- case '1': /* Parse Complete */
- /* If we're doing PQprepare, we're done; else ignore */
- if (conn->queryclass == PGQUERY_PREPARE)
- {
- if (conn->result == NULL)
- {
- conn->result = PQmakeEmptyPGresult(conn,
- PGRES_COMMAND_OK);
- if (!conn->result)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("out of memory"));
- pqSaveErrorResult(conn);
- }
- }
- conn->asyncStatus = PGASYNC_READY;
- }
- break;
- case '2': /* Bind Complete */
- case '3': /* Close Complete */
- /* Nothing to do for these message types */
- break;
- case 'S': /* parameter status */
- if (getParameterStatus(conn))
- return;
- break;
- case 'K': /* secret key data from the backend */
-
- /*
- * This is expected only during backend startup, but it's
- * just as easy to handle it as part of the main loop.
- * Save the data and continue processing.
- */
- if (pqGetInt(&(conn->be_pid), 4, conn))
- return;
- if (pqGetInt(&(conn->be_key), 4, conn))
- return;
- break;
- case 'T': /* Row Description */
- if (conn->result != NULL &&
- conn->result->resultStatus == PGRES_FATAL_ERROR)
- {
- /*
- * We've already choked for some reason. Just discard
- * the data till we get to the end of the query.
- */
- conn->inCursor += msgLength;
- }
- else if (conn->result == NULL ||
- conn->queryclass == PGQUERY_DESCRIBE)
- {
- /* First 'T' in a query sequence */
- if (getRowDescriptions(conn, msgLength))
- return;
- /* getRowDescriptions() moves inStart itself */
- continue;
- }
- else
- {
- /*
- * A new 'T' message is treated as the start of
- * another PGresult. (It is not clear that this is
- * really possible with the current backend.) We stop
- * parsing until the application accepts the current
- * result.
- */
- conn->asyncStatus = PGASYNC_READY;
- return;
- }
- break;
- case 'n': /* No Data */
-
- /*
- * NoData indicates that we will not be seeing a
- * RowDescription message because the statement or portal
- * inquired about doesn't return rows.
- *
- * If we're doing a Describe, we have to pass something
- * back to the client, so set up a COMMAND_OK result,
- * instead of TUPLES_OK. Otherwise we can just ignore
- * this message.
- */
- if (conn->queryclass == PGQUERY_DESCRIBE)
- {
- if (conn->result == NULL)
- {
- conn->result = PQmakeEmptyPGresult(conn,
- PGRES_COMMAND_OK);
- if (!conn->result)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("out of memory"));
- pqSaveErrorResult(conn);
- }
- }
- conn->asyncStatus = PGASYNC_READY;
- }
- break;
- case 't': /* Parameter Description */
- if (getParamDescriptions(conn, msgLength))
- return;
- /* getParamDescriptions() moves inStart itself */
- continue;
- case 'D': /* Data Row */
- if (conn->result != NULL &&
- conn->result->resultStatus == PGRES_TUPLES_OK)
- {
- /* Read another tuple of a normal query response */
- if (getAnotherTuple(conn, msgLength))
- return;
- /* getAnotherTuple() moves inStart itself */
- continue;
- }
- else if (conn->result != NULL &&
- conn->result->resultStatus == PGRES_FATAL_ERROR)
- {
- /*
- * We've already choked for some reason. Just discard
- * tuples till we get to the end of the query.
- */
- conn->inCursor += msgLength;
- }
- else
- {
- /* Set up to report error at end of query */
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("server sent data (\"D\" message) without prior row description (\"T\" message)\n"));
- pqSaveErrorResult(conn);
- /* Discard the unexpected message */
- conn->inCursor += msgLength;
- }
- break;
- case 'G': /* Start Copy In */
- if (getCopyStart(conn, PGRES_COPY_IN))
- return;
- conn->asyncStatus = PGASYNC_COPY_IN;
- break;
- case 'H': /* Start Copy Out */
- if (getCopyStart(conn, PGRES_COPY_OUT))
- return;
- conn->asyncStatus = PGASYNC_COPY_OUT;
- conn->copy_already_done = 0;
- break;
- case 'W': /* Start Copy Both */
- if (getCopyStart(conn, PGRES_COPY_BOTH))
- return;
- conn->asyncStatus = PGASYNC_COPY_BOTH;
- conn->copy_already_done = 0;
- break;
- case 'd': /* Copy Data */
-
- /*
- * If we see Copy Data, just silently drop it. This would
- * only occur if application exits COPY OUT mode too
- * early.
- */
- conn->inCursor += msgLength;
- break;
- case 'c': /* Copy Done */
-
- /*
- * If we see Copy Done, just silently drop it. This is
- * the normal case during PQendcopy. We will keep
- * swallowing data, expecting to see command-complete for
- * the COPY command.
- */
- break;
- default:
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext(
- "unexpected response from server; first received character was \"%c\"\n"),
- id);
- /* build an error result holding the error message */
- pqSaveErrorResult(conn);
- /* not sure if we will see more, so go to ready state */
- conn->asyncStatus = PGASYNC_READY;
- /* Discard the unexpected message */
- conn->inCursor += msgLength;
- break;
- } /* switch on protocol character */
- }
- /* Successfully consumed this message */
- if (conn->inCursor == conn->inStart + 5 + msgLength)
- {
- /* Normal case: parsing agrees with specified length */
- conn->inStart = conn->inCursor;
- }
- else
- {
- /* Trouble --- report it */
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("message contents do not agree with length in message type \"%c\"\n"),
- id);
- /* build an error result holding the error message */
- pqSaveErrorResult(conn);
- conn->asyncStatus = PGASYNC_READY;
- /* trust the specified message length as what to skip */
- conn->inStart += 5 + msgLength;
- }
- }
-}
-
-/*
- * handleSyncLoss: clean up after loss of message-boundary sync
- *
- * There isn't really a lot we can do here except abandon the connection.
- */
-static void
-handleSyncLoss(PGconn *conn, char id, int msgLength)
-{
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext(
- "lost synchronization with server: got message type \"%c\", length %d\n"),
- id, msgLength);
- /* build an error result holding the error message */
- pqSaveErrorResult(conn);
- conn->asyncStatus = PGASYNC_READY; /* drop out of GetResult wait loop */
- /* flush input data since we're giving up on processing it */
- pqDropConnection(conn, true);
- conn->status = CONNECTION_BAD; /* No more connection to backend */
-}
-
-/*
- * parseInput subroutine to read a 'T' (row descriptions) message.
- * We'll build a new PGresult structure (unless called for a Describe
- * command for a prepared statement) containing the attribute data.
- * Returns: 0 if processed message successfully, EOF to suspend parsing
- * (the latter case is not actually used currently).
- * In the former case, conn->inStart has been advanced past the message.
- */
-static int
-getRowDescriptions(PGconn *conn, int msgLength)
-{
- PGresult *result;
- int nfields;
- const char *errmsg;
- int i;
-
- /*
- * When doing Describe for a prepared statement, there'll already be a
- * PGresult created by getParamDescriptions, and we should fill data into
- * that. Otherwise, create a new, empty PGresult.
- */
- if (conn->queryclass == PGQUERY_DESCRIBE)
- {
- if (conn->result)
- result = conn->result;
- else
- result = PQmakeEmptyPGresult(conn, PGRES_COMMAND_OK);
- }
- else
- result = PQmakeEmptyPGresult(conn, PGRES_TUPLES_OK);
- if (!result)
- {
- errmsg = NULL; /* means "out of memory", see below */
- goto advance_and_error;
- }
-
- /* parseInput already read the 'T' label and message length. */
- /* the next two bytes are the number of fields */
- if (pqGetInt(&(result->numAttributes), 2, conn))
- {
- /* We should not run out of data here, so complain */
- errmsg = libpq_gettext("insufficient data in \"T\" message");
- goto advance_and_error;
- }
- nfields = result->numAttributes;
-
- /* allocate space for the attribute descriptors */
- if (nfields > 0)
- {
- result->attDescs = (PGresAttDesc *)
- pqResultAlloc(result, nfields * sizeof(PGresAttDesc), TRUE);
- if (!result->attDescs)
- {
- errmsg = NULL; /* means "out of memory", see below */
- goto advance_and_error;
- }
- MemSet(result->attDescs, 0, nfields * sizeof(PGresAttDesc));
- }
-
- /* result->binary is true only if ALL columns are binary */
- result->binary = (nfields > 0) ? 1 : 0;
-
- /* get type info */
- for (i = 0; i < nfields; i++)
- {
- int tableid;
- int columnid;
- int typid;
- int typlen;
- int atttypmod;
- int format;
-
- if (pqGets(&conn->workBuffer, conn) ||
- pqGetInt(&tableid, 4, conn) ||
- pqGetInt(&columnid, 2, conn) ||
- pqGetInt(&typid, 4, conn) ||
- pqGetInt(&typlen, 2, conn) ||
- pqGetInt(&atttypmod, 4, conn) ||
- pqGetInt(&format, 2, conn))
- {
- /* We should not run out of data here, so complain */
- errmsg = libpq_gettext("insufficient data in \"T\" message");
- goto advance_and_error;
- }
-
- /*
- * Since pqGetInt treats 2-byte integers as unsigned, we need to
- * coerce these results to signed form.
- */
- columnid = (int) ((int16) columnid);
- typlen = (int) ((int16) typlen);
- format = (int) ((int16) format);
-
- result->attDescs[i].name = pqResultStrdup(result,
- conn->workBuffer.data);
- if (!result->attDescs[i].name)
- {
- errmsg = NULL; /* means "out of memory", see below */
- goto advance_and_error;
- }
- result->attDescs[i].tableid = tableid;
- result->attDescs[i].columnid = columnid;
- result->attDescs[i].format = format;
- result->attDescs[i].typid = typid;
- result->attDescs[i].typlen = typlen;
- result->attDescs[i].atttypmod = atttypmod;
-
- if (format != 1)
- result->binary = 0;
- }
-
- /* Sanity check that we absorbed all the data */
- if (conn->inCursor != conn->inStart + 5 + msgLength)
- {
- errmsg = libpq_gettext("extraneous data in \"T\" message");
- goto advance_and_error;
- }
-
- /* Success! */
- conn->result = result;
-
- /* Advance inStart to show that the "T" message has been processed. */
- conn->inStart = conn->inCursor;
-
- /*
- * If we're doing a Describe, we're done, and ready to pass the result
- * back to the client.
- */
- if (conn->queryclass == PGQUERY_DESCRIBE)
- {
- conn->asyncStatus = PGASYNC_READY;
- return 0;
- }
-
- /*
- * We could perform additional setup for the new result set here, but for
- * now there's nothing else to do.
- */
-
- /* And we're done. */
- return 0;
-
-advance_and_error:
- /* Discard unsaved result, if any */
- if (result && result != conn->result)
- PQclear(result);
-
- /* Discard the failed message by pretending we read it */
- conn->inStart += 5 + msgLength;
-
- /*
- * Replace partially constructed result with an error result. First
- * discard the old result to try to win back some memory.
- */
- pqClearAsyncResult(conn);
-
- /*
- * If preceding code didn't provide an error message, assume "out of
- * memory" was meant. The advantage of having this special case is that
- * freeing the old result first greatly improves the odds that gettext()
- * will succeed in providing a translation.
- */
- if (!errmsg)
- errmsg = libpq_gettext("out of memory for query result");
-
- printfPQExpBuffer(&conn->errorMessage, "%s\n", errmsg);
- pqSaveErrorResult(conn);
-
- /*
- * Return zero to allow input parsing to continue. Subsequent "D"
- * messages will be ignored until we get to end of data, since an error
- * result is already set up.
- */
- return 0;
-}
-
-/*
- * parseInput subroutine to read a 't' (ParameterDescription) message.
- * We'll build a new PGresult structure containing the parameter data.
- * Returns: 0 if completed message, EOF if not enough data yet.
- * In the former case, conn->inStart has been advanced past the message.
- *
- * Note that if we run out of data, we have to release the partially
- * constructed PGresult, and rebuild it again next time. Fortunately,
- * that shouldn't happen often, since 't' messages usually fit in a packet.
- */
-static int
-getParamDescriptions(PGconn *conn, int msgLength)
-{
- PGresult *result;
- const char *errmsg = NULL; /* means "out of memory", see below */
- int nparams;
- int i;
-
- result = PQmakeEmptyPGresult(conn, PGRES_COMMAND_OK);
- if (!result)
- goto advance_and_error;
-
- /* parseInput already read the 't' label and message length. */
- /* the next two bytes are the number of parameters */
- if (pqGetInt(&(result->numParameters), 2, conn))
- goto not_enough_data;
- nparams = result->numParameters;
-
- /* allocate space for the parameter descriptors */
- if (nparams > 0)
- {
- result->paramDescs = (PGresParamDesc *)
- pqResultAlloc(result, nparams * sizeof(PGresParamDesc), TRUE);
- if (!result->paramDescs)
- goto advance_and_error;
- MemSet(result->paramDescs, 0, nparams * sizeof(PGresParamDesc));
- }
-
- /* get parameter info */
- for (i = 0; i < nparams; i++)
- {
- int typid;
-
- if (pqGetInt(&typid, 4, conn))
- goto not_enough_data;
- result->paramDescs[i].typid = typid;
- }
-
- /* Sanity check that we absorbed all the data */
- if (conn->inCursor != conn->inStart + 5 + msgLength)
- {
- errmsg = libpq_gettext("extraneous data in \"t\" message");
- goto advance_and_error;
- }
-
- /* Success! */
- conn->result = result;
-
- /* Advance inStart to show that the "t" message has been processed. */
- conn->inStart = conn->inCursor;
-
- return 0;
-
-not_enough_data:
- PQclear(result);
- return EOF;
-
-advance_and_error:
- /* Discard unsaved result, if any */
- if (result && result != conn->result)
- PQclear(result);
-
- /* Discard the failed message by pretending we read it */
- conn->inStart += 5 + msgLength;
-
- /*
- * Replace partially constructed result with an error result. First
- * discard the old result to try to win back some memory.
- */
- pqClearAsyncResult(conn);
-
- /*
- * If preceding code didn't provide an error message, assume "out of
- * memory" was meant. The advantage of having this special case is that
- * freeing the old result first greatly improves the odds that gettext()
- * will succeed in providing a translation.
- */
- if (!errmsg)
- errmsg = libpq_gettext("out of memory");
- printfPQExpBuffer(&conn->errorMessage, "%s\n", errmsg);
- pqSaveErrorResult(conn);
-
- /*
- * Return zero to allow input parsing to continue. Essentially, we've
- * replaced the COMMAND_OK result with an error result, but since this
- * doesn't affect the protocol state, it's fine.
- */
- return 0;
-}
-
-/*
- * parseInput subroutine to read a 'D' (row data) message.
- * We fill rowbuf with column pointers and then call the row processor.
- * Returns: 0 if processed message successfully, EOF to suspend parsing
- * (the latter case is not actually used currently).
- * In the former case, conn->inStart has been advanced past the message.
- */
-static int
-getAnotherTuple(PGconn *conn, int msgLength)
-{
- PGresult *result = conn->result;
- int nfields = result->numAttributes;
- const char *errmsg;
- PGdataValue *rowbuf;
- int tupnfields; /* # fields from tuple */
- int vlen; /* length of the current field value */
- int i;
-
- /* Get the field count and make sure it's what we expect */
- if (pqGetInt(&tupnfields, 2, conn))
- {
- /* We should not run out of data here, so complain */
- errmsg = libpq_gettext("insufficient data in \"D\" message");
- goto advance_and_error;
- }
-
- if (tupnfields != nfields)
- {
- errmsg = libpq_gettext("unexpected field count in \"D\" message");
- goto advance_and_error;
- }
-
- /* Resize row buffer if needed */
- rowbuf = conn->rowBuf;
- if (nfields > conn->rowBufLen)
- {
- rowbuf = (PGdataValue *) realloc(rowbuf,
- nfields * sizeof(PGdataValue));
- if (!rowbuf)
- {
- errmsg = NULL; /* means "out of memory", see below */
- goto advance_and_error;
- }
- conn->rowBuf = rowbuf;
- conn->rowBufLen = nfields;
- }
-
- /* Scan the fields */
- for (i = 0; i < nfields; i++)
- {
- /* get the value length */
- if (pqGetInt(&vlen, 4, conn))
- {
- /* We should not run out of data here, so complain */
- errmsg = libpq_gettext("insufficient data in \"D\" message");
- goto advance_and_error;
- }
- rowbuf[i].len = vlen;
-
- /*
- * rowbuf[i].value always points to the next address in the data
- * buffer even if the value is NULL. This allows row processors to
- * estimate data sizes more easily.
- */
- rowbuf[i].value = conn->inBuffer + conn->inCursor;
-
- /* Skip over the data value */
- if (vlen > 0)
- {
- if (pqSkipnchar(vlen, conn))
- {
- /* We should not run out of data here, so complain */
- errmsg = libpq_gettext("insufficient data in \"D\" message");
- goto advance_and_error;
- }
- }
- }
-
- /* Sanity check that we absorbed all the data */
- if (conn->inCursor != conn->inStart + 5 + msgLength)
- {
- errmsg = libpq_gettext("extraneous data in \"D\" message");
- goto advance_and_error;
- }
-
- /* Advance inStart to show that the "D" message has been processed. */
- conn->inStart = conn->inCursor;
-
- /* Process the collected row */
- errmsg = NULL;
- if (pqRowProcessor(conn, &errmsg))
- return 0; /* normal, successful exit */
-
- goto set_error_result; /* pqRowProcessor failed, report it */
-
-advance_and_error:
- /* Discard the failed message by pretending we read it */
- conn->inStart += 5 + msgLength;
-
-set_error_result:
-
- /*
- * Replace partially constructed result with an error result. First
- * discard the old result to try to win back some memory.
- */
- pqClearAsyncResult(conn);
-
- /*
- * If preceding code didn't provide an error message, assume "out of
- * memory" was meant. The advantage of having this special case is that
- * freeing the old result first greatly improves the odds that gettext()
- * will succeed in providing a translation.
- */
- if (!errmsg)
- errmsg = libpq_gettext("out of memory for query result");
-
- printfPQExpBuffer(&conn->errorMessage, "%s\n", errmsg);
- pqSaveErrorResult(conn);
-
- /*
- * Return zero to allow input parsing to continue. Subsequent "D"
- * messages will be ignored until we get to end of data, since an error
- * result is already set up.
- */
- return 0;
-}
-
-
-/*
- * Attempt to read an Error or Notice response message.
- * This is possible in several places, so we break it out as a subroutine.
- * Entry: 'E' or 'N' message type and length have already been consumed.
- * Exit: returns 0 if successfully consumed message.
- * returns EOF if not enough data.
- */
-int
-pqGetErrorNotice3(PGconn *conn, bool isError)
-{
- PGresult *res = NULL;
- bool have_position = false;
- PQExpBufferData workBuf;
- char id;
-
- /*
- * Since the fields might be pretty long, we create a temporary
- * PQExpBuffer rather than using conn->workBuffer. workBuffer is intended
- * for stuff that is expected to be short. We shouldn't use
- * conn->errorMessage either, since this might be only a notice.
- */
- initPQExpBuffer(&workBuf);
-
- /*
- * Make a PGresult to hold the accumulated fields. We temporarily lie
- * about the result status, so that PQmakeEmptyPGresult doesn't uselessly
- * copy conn->errorMessage.
- *
- * NB: This allocation can fail, if you run out of memory. The rest of the
- * function handles that gracefully, and we still try to set the error
- * message as the connection's error message.
- */
- res = PQmakeEmptyPGresult(conn, PGRES_EMPTY_QUERY);
- if (res)
- res->resultStatus = isError ? PGRES_FATAL_ERROR : PGRES_NONFATAL_ERROR;
-
- /*
- * Read the fields and save into res.
- *
- * While at it, save the SQLSTATE in conn->last_sqlstate, and note whether
- * we saw a PG_DIAG_STATEMENT_POSITION field.
- */
- for (;;)
- {
- if (pqGetc(&id, conn))
- goto fail;
- if (id == '\0')
- break; /* terminator found */
- if (pqGets(&workBuf, conn))
- goto fail;
- pqSaveMessageField(res, id, workBuf.data);
- if (id == PG_DIAG_SQLSTATE)
- strlcpy(conn->last_sqlstate, workBuf.data,
- sizeof(conn->last_sqlstate));
- else if (id == PG_DIAG_STATEMENT_POSITION)
- have_position = true;
- }
-
- /*
- * Save the active query text, if any, into res as well; but only if we
- * might need it for an error cursor display, which is only true if there
- * is a PG_DIAG_STATEMENT_POSITION field.
- */
- if (have_position && conn->last_query && res)
- res->errQuery = pqResultStrdup(res, conn->last_query);
-
- /*
- * Now build the "overall" error message for PQresultErrorMessage.
- */
- resetPQExpBuffer(&workBuf);
- pqBuildErrorMessage3(&workBuf, res, conn->verbosity, conn->show_context);
-
- /*
- * Either save error as current async result, or just emit the notice.
- */
- if (isError)
- {
- if (res)
- res->errMsg = pqResultStrdup(res, workBuf.data);
- pqClearAsyncResult(conn);
- conn->result = res;
- if (PQExpBufferDataBroken(workBuf))
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("out of memory"));
- else
- appendPQExpBufferStr(&conn->errorMessage, workBuf.data);
- }
- else
- {
- /* if we couldn't allocate the result set, just discard the NOTICE */
- if (res)
- {
- /* We can cheat a little here and not copy the message. */
- res->errMsg = workBuf.data;
- if (res->noticeHooks.noticeRec != NULL)
- (*res->noticeHooks.noticeRec) (res->noticeHooks.noticeRecArg, res);
- PQclear(res);
- }
- }
-
- termPQExpBuffer(&workBuf);
- return 0;
-
-fail:
- PQclear(res);
- termPQExpBuffer(&workBuf);
- return EOF;
-}
-
-/*
- * Construct an error message from the fields in the given PGresult,
- * appending it to the contents of "msg".
- */
-void
-pqBuildErrorMessage3(PQExpBuffer msg, const PGresult *res,
- PGVerbosity verbosity, PGContextVisibility show_context)
-{
- const char *val;
- const char *querytext = NULL;
- int querypos = 0;
-
- /* If we couldn't allocate a PGresult, just say "out of memory" */
- if (res == NULL)
- {
- appendPQExpBuffer(msg, libpq_gettext("out of memory\n"));
- return;
- }
-
- /*
- * If we don't have any broken-down fields, just return the base message.
- * This mainly applies if we're given a libpq-generated error result.
- */
- if (res->errFields == NULL)
- {
- if (res->errMsg && res->errMsg[0])
- appendPQExpBufferStr(msg, res->errMsg);
- else
- appendPQExpBuffer(msg, libpq_gettext("no error message available\n"));
- return;
- }
-
- /* Else build error message from relevant fields */
- val = PQresultErrorField(res, PG_DIAG_SEVERITY);
- if (val)
- appendPQExpBuffer(msg, "%s: ", val);
- if (verbosity == PQERRORS_VERBOSE)
- {
- val = PQresultErrorField(res, PG_DIAG_SQLSTATE);
- if (val)
- appendPQExpBuffer(msg, "%s: ", val);
- }
- val = PQresultErrorField(res, PG_DIAG_MESSAGE_PRIMARY);
- if (val)
- appendPQExpBufferStr(msg, val);
- val = PQresultErrorField(res, PG_DIAG_STATEMENT_POSITION);
- if (val)
- {
- if (verbosity != PQERRORS_TERSE && res->errQuery != NULL)
- {
- /* emit position as a syntax cursor display */
- querytext = res->errQuery;
- querypos = atoi(val);
- }
- else
- {
- /* emit position as text addition to primary message */
- /* translator: %s represents a digit string */
- appendPQExpBuffer(msg, libpq_gettext(" at character %s"),
- val);
- }
- }
- else
- {
- val = PQresultErrorField(res, PG_DIAG_INTERNAL_POSITION);
- if (val)
- {
- querytext = PQresultErrorField(res, PG_DIAG_INTERNAL_QUERY);
- if (verbosity != PQERRORS_TERSE && querytext != NULL)
- {
- /* emit position as a syntax cursor display */
- querypos = atoi(val);
- }
- else
- {
- /* emit position as text addition to primary message */
- /* translator: %s represents a digit string */
- appendPQExpBuffer(msg, libpq_gettext(" at character %s"),
- val);
- }
- }
- }
- appendPQExpBufferChar(msg, '\n');
- if (verbosity != PQERRORS_TERSE)
- {
- if (querytext && querypos > 0)
- reportErrorPosition(msg, querytext, querypos,
- res->client_encoding);
- val = PQresultErrorField(res, PG_DIAG_MESSAGE_DETAIL);
- if (val)
- appendPQExpBuffer(msg, libpq_gettext("DETAIL: %s\n"), val);
- val = PQresultErrorField(res, PG_DIAG_MESSAGE_HINT);
- if (val)
- appendPQExpBuffer(msg, libpq_gettext("HINT: %s\n"), val);
- val = PQresultErrorField(res, PG_DIAG_INTERNAL_QUERY);
- if (val)
- appendPQExpBuffer(msg, libpq_gettext("QUERY: %s\n"), val);
- if (show_context == PQSHOW_CONTEXT_ALWAYS ||
- (show_context == PQSHOW_CONTEXT_ERRORS &&
- res->resultStatus == PGRES_FATAL_ERROR))
- {
- val = PQresultErrorField(res, PG_DIAG_CONTEXT);
- if (val)
- appendPQExpBuffer(msg, libpq_gettext("CONTEXT: %s\n"),
- val);
- }
- }
- if (verbosity == PQERRORS_VERBOSE)
- {
- val = PQresultErrorField(res, PG_DIAG_SCHEMA_NAME);
- if (val)
- appendPQExpBuffer(msg,
- libpq_gettext("SCHEMA NAME: %s\n"), val);
- val = PQresultErrorField(res, PG_DIAG_TABLE_NAME);
- if (val)
- appendPQExpBuffer(msg,
- libpq_gettext("TABLE NAME: %s\n"), val);
- val = PQresultErrorField(res, PG_DIAG_COLUMN_NAME);
- if (val)
- appendPQExpBuffer(msg,
- libpq_gettext("COLUMN NAME: %s\n"), val);
- val = PQresultErrorField(res, PG_DIAG_DATATYPE_NAME);
- if (val)
- appendPQExpBuffer(msg,
- libpq_gettext("DATATYPE NAME: %s\n"), val);
- val = PQresultErrorField(res, PG_DIAG_CONSTRAINT_NAME);
- if (val)
- appendPQExpBuffer(msg,
- libpq_gettext("CONSTRAINT NAME: %s\n"), val);
- }
- if (verbosity == PQERRORS_VERBOSE)
- {
- const char *valf;
- const char *vall;
-
- valf = PQresultErrorField(res, PG_DIAG_SOURCE_FILE);
- vall = PQresultErrorField(res, PG_DIAG_SOURCE_LINE);
- val = PQresultErrorField(res, PG_DIAG_SOURCE_FUNCTION);
- if (val || valf || vall)
- {
- appendPQExpBufferStr(msg, libpq_gettext("LOCATION: "));
- if (val)
- appendPQExpBuffer(msg, libpq_gettext("%s, "), val);
- if (valf && vall) /* unlikely we'd have just one */
- appendPQExpBuffer(msg, libpq_gettext("%s:%s"),
- valf, vall);
- appendPQExpBufferChar(msg, '\n');
- }
- }
-}
-
-/*
- * Add an error-location display to the error message under construction.
- *
- * The cursor location is measured in logical characters; the query string
- * is presumed to be in the specified encoding.
- */
-static void
-reportErrorPosition(PQExpBuffer msg, const char *query, int loc, int encoding)
-{
-#define DISPLAY_SIZE 60 /* screen width limit, in screen cols */
-#define MIN_RIGHT_CUT 10 /* try to keep this far away from EOL */
-
- char *wquery;
- int slen,
- cno,
- i,
- *qidx,
- *scridx,
- qoffset,
- scroffset,
- ibeg,
- iend,
- loc_line;
- bool mb_encoding,
- beg_trunc,
- end_trunc;
-
- /* Convert loc from 1-based to 0-based; no-op if out of range */
- loc--;
- if (loc < 0)
- return;
-
- /* Need a writable copy of the query */
- wquery = strdup(query);
- if (wquery == NULL)
- return; /* fail silently if out of memory */
-
- /*
- * Each character might occupy multiple physical bytes in the string, and
- * in some Far Eastern character sets it might take more than one screen
- * column as well. We compute the starting byte offset and starting
- * screen column of each logical character, and store these in qidx[] and
- * scridx[] respectively.
- */
-
- /* we need a safe allocation size... */
- slen = strlen(wquery) + 1;
-
- qidx = (int *) malloc(slen * sizeof(int));
- if (qidx == NULL)
- {
- free(wquery);
- return;
- }
- scridx = (int *) malloc(slen * sizeof(int));
- if (scridx == NULL)
- {
- free(qidx);
- free(wquery);
- return;
- }
-
- /* We can optimize a bit if it's a single-byte encoding */
- mb_encoding = (pg_encoding_max_length(encoding) != 1);
-
- /*
- * Within the scanning loop, cno is the current character's logical
- * number, qoffset is its offset in wquery, and scroffset is its starting
- * logical screen column (all indexed from 0). "loc" is the logical
- * character number of the error location. We scan to determine loc_line
- * (the 1-based line number containing loc) and ibeg/iend (first character
- * number and last+1 character number of the line containing loc). Note
- * that qidx[] and scridx[] are filled only as far as iend.
- */
- qoffset = 0;
- scroffset = 0;
- loc_line = 1;
- ibeg = 0;
- iend = -1; /* -1 means not set yet */
-
- for (cno = 0; wquery[qoffset] != '\0'; cno++)
- {
- char ch = wquery[qoffset];
-
- qidx[cno] = qoffset;
- scridx[cno] = scroffset;
-
- /*
- * Replace tabs with spaces in the writable copy. (Later we might
- * want to think about coping with their variable screen width, but
- * not today.)
- */
- if (ch == '\t')
- wquery[qoffset] = ' ';
-
- /*
- * If end-of-line, count lines and mark positions. Each \r or \n
- * counts as a line except when \r \n appear together.
- */
- else if (ch == '\r' || ch == '\n')
- {
- if (cno < loc)
- {
- if (ch == '\r' ||
- cno == 0 ||
- wquery[qidx[cno - 1]] != '\r')
- loc_line++;
- /* extract beginning = last line start before loc. */
- ibeg = cno + 1;
- }
- else
- {
- /* set extract end. */
- iend = cno;
- /* done scanning. */
- break;
- }
- }
-
- /* Advance */
- if (mb_encoding)
- {
- int w;
-
- w = pg_encoding_dsplen(encoding, &wquery[qoffset]);
- /* treat any non-tab control chars as width 1 */
- if (w <= 0)
- w = 1;
- scroffset += w;
- qoffset += pg_encoding_mblen(encoding, &wquery[qoffset]);
- }
- else
- {
- /* We assume wide chars only exist in multibyte encodings */
- scroffset++;
- qoffset++;
- }
- }
- /* Fix up if we didn't find an end-of-line after loc */
- if (iend < 0)
- {
- iend = cno; /* query length in chars, +1 */
- qidx[iend] = qoffset;
- scridx[iend] = scroffset;
- }
-
- /* Print only if loc is within computed query length */
- if (loc <= cno)
- {
- /* If the line extracted is too long, we truncate it. */
- beg_trunc = false;
- end_trunc = false;
- if (scridx[iend] - scridx[ibeg] > DISPLAY_SIZE)
- {
- /*
- * We first truncate right if it is enough. This code might be
- * off a space or so on enforcing MIN_RIGHT_CUT if there's a wide
- * character right there, but that should be okay.
- */
- if (scridx[ibeg] + DISPLAY_SIZE >= scridx[loc] + MIN_RIGHT_CUT)
- {
- while (scridx[iend] - scridx[ibeg] > DISPLAY_SIZE)
- iend--;
- end_trunc = true;
- }
- else
- {
- /* Truncate right if not too close to loc. */
- while (scridx[loc] + MIN_RIGHT_CUT < scridx[iend])
- {
- iend--;
- end_trunc = true;
- }
-
- /* Truncate left if still too long. */
- while (scridx[iend] - scridx[ibeg] > DISPLAY_SIZE)
- {
- ibeg++;
- beg_trunc = true;
- }
- }
- }
-
- /* truncate working copy at desired endpoint */
- wquery[qidx[iend]] = '\0';
-
- /* Begin building the finished message. */
- i = msg->len;
- appendPQExpBuffer(msg, libpq_gettext("LINE %d: "), loc_line);
- if (beg_trunc)
- appendPQExpBufferStr(msg, "...");
-
- /*
- * While we have the prefix in the msg buffer, compute its screen
- * width.
- */
- scroffset = 0;
- for (; i < msg->len; i += pg_encoding_mblen(encoding, &msg->data[i]))
- {
- int w = pg_encoding_dsplen(encoding, &msg->data[i]);
-
- if (w <= 0)
- w = 1;
- scroffset += w;
- }
-
- /* Finish up the LINE message line. */
- appendPQExpBufferStr(msg, &wquery[qidx[ibeg]]);
- if (end_trunc)
- appendPQExpBufferStr(msg, "...");
- appendPQExpBufferChar(msg, '\n');
-
- /* Now emit the cursor marker line. */
- scroffset += scridx[loc] - scridx[ibeg];
- for (i = 0; i < scroffset; i++)
- appendPQExpBufferChar(msg, ' ');
- appendPQExpBufferChar(msg, '^');
- appendPQExpBufferChar(msg, '\n');
- }
-
- /* Clean up. */
- free(scridx);
- free(qidx);
- free(wquery);
-}
-
-
-/*
- * Attempt to read a ParameterStatus message.
- * This is possible in several places, so we break it out as a subroutine.
- * Entry: 'S' message type and length have already been consumed.
- * Exit: returns 0 if successfully consumed message.
- * returns EOF if not enough data.
- */
-static int
-getParameterStatus(PGconn *conn)
-{
- PQExpBufferData valueBuf;
-
- /* Get the parameter name */
- if (pqGets(&conn->workBuffer, conn))
- return EOF;
- /* Get the parameter value (could be large) */
- initPQExpBuffer(&valueBuf);
- if (pqGets(&valueBuf, conn))
- {
- termPQExpBuffer(&valueBuf);
- return EOF;
- }
- /* And save it */
- pqSaveParameterStatus(conn, conn->workBuffer.data, valueBuf.data);
- termPQExpBuffer(&valueBuf);
- return 0;
-}
-
-
-/*
- * Attempt to read a Notify response message.
- * This is possible in several places, so we break it out as a subroutine.
- * Entry: 'A' message type and length have already been consumed.
- * Exit: returns 0 if successfully consumed Notify message.
- * returns EOF if not enough data.
- */
-static int
-getNotify(PGconn *conn)
-{
- int be_pid;
- char *svname;
- int nmlen;
- int extralen;
- PGnotify *newNotify;
-
- if (pqGetInt(&be_pid, 4, conn))
- return EOF;
- if (pqGets(&conn->workBuffer, conn))
- return EOF;
- /* must save name while getting extra string */
- svname = strdup(conn->workBuffer.data);
- if (!svname)
- return EOF;
- if (pqGets(&conn->workBuffer, conn))
- {
- free(svname);
- return EOF;
- }
-
- /*
- * Store the strings right after the PQnotify structure so it can all be
- * freed at once. We don't use NAMEDATALEN because we don't want to tie
- * this interface to a specific server name length.
- */
- nmlen = strlen(svname);
- extralen = strlen(conn->workBuffer.data);
- newNotify = (PGnotify *) malloc(sizeof(PGnotify) + nmlen + extralen + 2);
- if (newNotify)
- {
- newNotify->relname = (char *) newNotify + sizeof(PGnotify);
- strcpy(newNotify->relname, svname);
- newNotify->extra = newNotify->relname + nmlen + 1;
- strcpy(newNotify->extra, conn->workBuffer.data);
- newNotify->be_pid = be_pid;
- newNotify->next = NULL;
- if (conn->notifyTail)
- conn->notifyTail->next = newNotify;
- else
- conn->notifyHead = newNotify;
- conn->notifyTail = newNotify;
- }
-
- free(svname);
- return 0;
-}
-
-/*
- * getCopyStart - process CopyInResponse, CopyOutResponse or
- * CopyBothResponse message
- *
- * parseInput already read the message type and length.
- */
-static int
-getCopyStart(PGconn *conn, ExecStatusType copytype)
-{
- PGresult *result;
- int nfields;
- int i;
-
- result = PQmakeEmptyPGresult(conn, copytype);
- if (!result)
- goto failure;
-
- if (pqGetc(&conn->copy_is_binary, conn))
- goto failure;
- result->binary = conn->copy_is_binary;
- /* the next two bytes are the number of fields */
- if (pqGetInt(&(result->numAttributes), 2, conn))
- goto failure;
- nfields = result->numAttributes;
-
- /* allocate space for the attribute descriptors */
- if (nfields > 0)
- {
- result->attDescs = (PGresAttDesc *)
- pqResultAlloc(result, nfields * sizeof(PGresAttDesc), TRUE);
- if (!result->attDescs)
- goto failure;
- MemSet(result->attDescs, 0, nfields * sizeof(PGresAttDesc));
- }
-
- for (i = 0; i < nfields; i++)
- {
- int format;
-
- if (pqGetInt(&format, 2, conn))
- goto failure;
-
- /*
- * Since pqGetInt treats 2-byte integers as unsigned, we need to
- * coerce these results to signed form.
- */
- format = (int) ((int16) format);
- result->attDescs[i].format = format;
- }
-
- /* Success! */
- conn->result = result;
- return 0;
-
-failure:
- PQclear(result);
- return EOF;
-}
-
-/*
- * getReadyForQuery - process ReadyForQuery message
- */
-static int
-getReadyForQuery(PGconn *conn)
-{
- char xact_status;
-
- if (pqGetc(&xact_status, conn))
- return EOF;
- switch (xact_status)
- {
- case 'I':
- conn->xactStatus = PQTRANS_IDLE;
- break;
- case 'T':
- conn->xactStatus = PQTRANS_INTRANS;
- break;
- case 'E':
- conn->xactStatus = PQTRANS_INERROR;
- break;
- default:
- conn->xactStatus = PQTRANS_UNKNOWN;
- break;
- }
-
- return 0;
-}
-
-/*
- * getCopyDataMessage - fetch next CopyData message, process async messages
- *
- * Returns length word of CopyData message (> 0), or 0 if no complete
- * message available, -1 if end of copy, -2 if error.
- */
-static int
-getCopyDataMessage(PGconn *conn)
-{
- char id;
- int msgLength;
- int avail;
-
- for (;;)
- {
- /*
- * Do we have the next input message? To make life simpler for async
- * callers, we keep returning 0 until the next message is fully
- * available, even if it is not Copy Data.
- */
- conn->inCursor = conn->inStart;
- if (pqGetc(&id, conn))
- return 0;
- if (pqGetInt(&msgLength, 4, conn))
- return 0;
- if (msgLength < 4)
- {
- handleSyncLoss(conn, id, msgLength);
- return -2;
- }
- avail = conn->inEnd - conn->inCursor;
- if (avail < msgLength - 4)
- {
- /*
- * Before returning, enlarge the input buffer if needed to hold
- * the whole message. See notes in parseInput.
- */
- if (pqCheckInBufferSpace(conn->inCursor + (size_t) msgLength - 4,
- conn))
- {
- /*
- * XXX add some better recovery code... plan is to skip over
- * the message using its length, then report an error. For the
- * moment, just treat this like loss of sync (which indeed it
- * might be!)
- */
- handleSyncLoss(conn, id, msgLength);
- return -2;
- }
- return 0;
- }
-
- /*
- * If it's a legitimate async message type, process it. (NOTIFY
- * messages are not currently possible here, but we handle them for
- * completeness.) Otherwise, if it's anything except Copy Data,
- * report end-of-copy.
- */
- switch (id)
- {
- case 'A': /* NOTIFY */
- if (getNotify(conn))
- return 0;
- break;
- case 'N': /* NOTICE */
- if (pqGetErrorNotice3(conn, false))
- return 0;
- break;
- case 'S': /* ParameterStatus */
- if (getParameterStatus(conn))
- return 0;
- break;
- case 'd': /* Copy Data, pass it back to caller */
- return msgLength;
- case 'c':
-
- /*
- * If this is a CopyDone message, exit COPY_OUT mode and let
- * caller read status with PQgetResult(). If we're in
- * COPY_BOTH mode, return to COPY_IN mode.
- */
- if (conn->asyncStatus == PGASYNC_COPY_BOTH)
- conn->asyncStatus = PGASYNC_COPY_IN;
- else
- conn->asyncStatus = PGASYNC_BUSY;
- return -1;
- default: /* treat as end of copy */
-
- /*
- * Any other message terminates either COPY_IN or COPY_BOTH
- * mode.
- */
- conn->asyncStatus = PGASYNC_BUSY;
- return -1;
- }
-
- /* Drop the processed message and loop around for another */
- conn->inStart = conn->inCursor;
- }
-}
-
-/*
- * PQgetCopyData - read a row of data from the backend during COPY OUT
- * or COPY BOTH
- *
- * If successful, sets *buffer to point to a malloc'd row of data, and
- * returns row length (always > 0) as result.
- * Returns 0 if no row available yet (only possible if async is true),
- * -1 if end of copy (consult PQgetResult), or -2 if error (consult
- * PQerrorMessage).
- */
-int
-pqGetCopyData3(PGconn *conn, char **buffer, int async)
-{
- int msgLength;
-
- for (;;)
- {
- /*
- * Collect the next input message. To make life simpler for async
- * callers, we keep returning 0 until the next message is fully
- * available, even if it is not Copy Data.
- */
- msgLength = getCopyDataMessage(conn);
- if (msgLength < 0)
- return msgLength; /* end-of-copy or error */
- if (msgLength == 0)
- {
- /* Don't block if async read requested */
- if (async)
- return 0;
- /* Need to load more data */
- if (pqWait(TRUE, FALSE, conn) ||
- pqReadData(conn) < 0)
- return -2;
- continue;
- }
-
- /*
- * Drop zero-length messages (shouldn't happen anyway). Otherwise
- * pass the data back to the caller.
- */
- msgLength -= 4;
- if (msgLength > 0)
- {
- *buffer = (char *) malloc(msgLength + 1);
- if (*buffer == NULL)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("out of memory\n"));
- return -2;
- }
- memcpy(*buffer, &conn->inBuffer[conn->inCursor], msgLength);
- (*buffer)[msgLength] = '\0'; /* Add terminating null */
-
- /* Mark message consumed */
- conn->inStart = conn->inCursor + msgLength;
-
- return msgLength;
- }
-
- /* Empty, so drop it and loop around for another */
- conn->inStart = conn->inCursor;
- }
-}
-
-/*
- * PQgetline - gets a newline-terminated string from the backend.
- *
- * See fe-exec.c for documentation.
- */
-int
-pqGetline3(PGconn *conn, char *s, int maxlen)
-{
- int status;
-
- if (conn->sock == PGINVALID_SOCKET ||
- (conn->asyncStatus != PGASYNC_COPY_OUT &&
- conn->asyncStatus != PGASYNC_COPY_BOTH) ||
- conn->copy_is_binary)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("PQgetline: not doing text COPY OUT\n"));
- *s = '\0';
- return EOF;
- }
-
- while ((status = PQgetlineAsync(conn, s, maxlen - 1)) == 0)
- {
- /* need to load more data */
- if (pqWait(TRUE, FALSE, conn) ||
- pqReadData(conn) < 0)
- {
- *s = '\0';
- return EOF;
- }
- }
-
- if (status < 0)
- {
- /* End of copy detected; gin up old-style terminator */
- strcpy(s, "\\.");
- return 0;
- }
-
- /* Add null terminator, and strip trailing \n if present */
- if (s[status - 1] == '\n')
- {
- s[status - 1] = '\0';
- return 0;
- }
- else
- {
- s[status] = '\0';
- return 1;
- }
-}
-
-/*
- * PQgetlineAsync - gets a COPY data row without blocking.
- *
- * See fe-exec.c for documentation.
- */
-int
-pqGetlineAsync3(PGconn *conn, char *buffer, int bufsize)
-{
- int msgLength;
- int avail;
-
- if (conn->asyncStatus != PGASYNC_COPY_OUT
- && conn->asyncStatus != PGASYNC_COPY_BOTH)
- return -1; /* we are not doing a copy... */
-
- /*
- * Recognize the next input message. To make life simpler for async
- * callers, we keep returning 0 until the next message is fully available
- * even if it is not Copy Data. This should keep PQendcopy from blocking.
- * (Note: unlike pqGetCopyData3, we do not change asyncStatus here.)
- */
- msgLength = getCopyDataMessage(conn);
- if (msgLength < 0)
- return -1; /* end-of-copy or error */
- if (msgLength == 0)
- return 0; /* no data yet */
-
- /*
- * Move data from libpq's buffer to the caller's. In the case where a
- * prior call found the caller's buffer too small, we use
- * conn->copy_already_done to remember how much of the row was already
- * returned to the caller.
- */
- conn->inCursor += conn->copy_already_done;
- avail = msgLength - 4 - conn->copy_already_done;
- if (avail <= bufsize)
- {
- /* Able to consume the whole message */
- memcpy(buffer, &conn->inBuffer[conn->inCursor], avail);
- /* Mark message consumed */
- conn->inStart = conn->inCursor + avail;
- /* Reset state for next time */
- conn->copy_already_done = 0;
- return avail;
- }
- else
- {
- /* We must return a partial message */
- memcpy(buffer, &conn->inBuffer[conn->inCursor], bufsize);
- /* The message is NOT consumed from libpq's buffer */
- conn->copy_already_done += bufsize;
- return bufsize;
- }
-}
-
-/*
- * PQendcopy
- *
- * See fe-exec.c for documentation.
- */
-int
-pqEndcopy3(PGconn *conn)
-{
- PGresult *result;
-
- if (conn->asyncStatus != PGASYNC_COPY_IN &&
- conn->asyncStatus != PGASYNC_COPY_OUT &&
- conn->asyncStatus != PGASYNC_COPY_BOTH)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("no COPY in progress\n"));
- return 1;
- }
-
- /* Send the CopyDone message if needed */
- if (conn->asyncStatus == PGASYNC_COPY_IN ||
- conn->asyncStatus == PGASYNC_COPY_BOTH)
- {
- if (pqPutMsgStart('c', false, conn) < 0 ||
- pqPutMsgEnd(conn) < 0)
- return 1;
-
- /*
- * If we sent the COPY command in extended-query mode, we must issue a
- * Sync as well.
- */
- if (conn->queryclass != PGQUERY_SIMPLE)
- {
- if (pqPutMsgStart('S', false, conn) < 0 ||
- pqPutMsgEnd(conn) < 0)
- return 1;
- }
- }
-
- /*
- * make sure no data is waiting to be sent, abort if we are non-blocking
- * and the flush fails
- */
- if (pqFlush(conn) && pqIsnonblocking(conn))
- return 1;
-
- /* Return to active duty */
- conn->asyncStatus = PGASYNC_BUSY;
- resetPQExpBuffer(&conn->errorMessage);
-
- /*
- * Non blocking connections may have to abort at this point. If everyone
- * played the game there should be no problem, but in error scenarios the
- * expected messages may not have arrived yet. (We are assuming that the
- * backend's packetizing will ensure that CommandComplete arrives along
- * with the CopyDone; are there corner cases where that doesn't happen?)
- */
- if (pqIsnonblocking(conn) && PQisBusy(conn))
- return 1;
-
- /* Wait for the completion response */
- result = PQgetResult(conn);
-
- /* Expecting a successful result */
- if (result && result->resultStatus == PGRES_COMMAND_OK)
- {
- PQclear(result);
- return 0;
- }
-
- /*
- * Trouble. For backwards-compatibility reasons, we issue the error
- * message as if it were a notice (would be nice to get rid of this
- * silliness, but too many apps probably don't handle errors from
- * PQendcopy reasonably). Note that the app can still obtain the error
- * status from the PGconn object.
- */
- if (conn->errorMessage.len > 0)
- {
- /* We have to strip the trailing newline ... pain in neck... */
- char svLast = conn->errorMessage.data[conn->errorMessage.len - 1];
-
- if (svLast == '\n')
- conn->errorMessage.data[conn->errorMessage.len - 1] = '\0';
- pqInternalNotice(&conn->noticeHooks, "%s", conn->errorMessage.data);
- conn->errorMessage.data[conn->errorMessage.len - 1] = svLast;
- }
-
- PQclear(result);
-
- return 1;
-}
-
-
-/*
- * PQfn - Send a function call to the POSTGRES backend.
- *
- * See fe-exec.c for documentation.
- */
-PGresult *
-pqFunctionCall3(PGconn *conn, Oid fnid,
- int *result_buf, int *actual_result_len,
- int result_is_int,
- const PQArgBlock *args, int nargs)
-{
- bool needInput = false;
- ExecStatusType status = PGRES_FATAL_ERROR;
- char id;
- int msgLength;
- int avail;
- int i;
-
- /* PQfn already validated connection state */
-
- if (pqPutMsgStart('F', false, conn) < 0 || /* function call msg */
- pqPutInt(fnid, 4, conn) < 0 || /* function id */
- pqPutInt(1, 2, conn) < 0 || /* # of format codes */
- pqPutInt(1, 2, conn) < 0 || /* format code: BINARY */
- pqPutInt(nargs, 2, conn) < 0) /* # of args */
- {
- pqHandleSendFailure(conn);
- return NULL;
- }
-
- for (i = 0; i < nargs; ++i)
- { /* len.int4 + contents */
- if (pqPutInt(args[i].len, 4, conn))
- {
- pqHandleSendFailure(conn);
- return NULL;
- }
- if (args[i].len == -1)
- continue; /* it's NULL */
-
- if (args[i].isint)
- {
- if (pqPutInt(args[i].u.integer, args[i].len, conn))
- {
- pqHandleSendFailure(conn);
- return NULL;
- }
- }
- else
- {
- if (pqPutnchar((char *) args[i].u.ptr, args[i].len, conn))
- {
- pqHandleSendFailure(conn);
- return NULL;
- }
- }
- }
-
- if (pqPutInt(1, 2, conn) < 0) /* result format code: BINARY */
- {
- pqHandleSendFailure(conn);
- return NULL;
- }
-
- if (pqPutMsgEnd(conn) < 0 ||
- pqFlush(conn))
- {
- pqHandleSendFailure(conn);
- return NULL;
- }
-
- for (;;)
- {
- if (needInput)
- {
- /* Wait for some data to arrive (or for the channel to close) */
- if (pqWait(TRUE, FALSE, conn) ||
- pqReadData(conn) < 0)
- break;
- }
-
- /*
- * Scan the message. If we run out of data, loop around to try again.
- */
- needInput = true;
-
- conn->inCursor = conn->inStart;
- if (pqGetc(&id, conn))
- continue;
- if (pqGetInt(&msgLength, 4, conn))
- continue;
-
- /*
- * Try to validate message type/length here. A length less than 4 is
- * definitely broken. Large lengths should only be believed for a few
- * message types.
- */
- if (msgLength < 4)
- {
- handleSyncLoss(conn, id, msgLength);
- break;
- }
- if (msgLength > 30000 && !VALID_LONG_MESSAGE_TYPE(id))
- {
- handleSyncLoss(conn, id, msgLength);
- break;
- }
-
- /*
- * Can't process if message body isn't all here yet.
- */
- msgLength -= 4;
- avail = conn->inEnd - conn->inCursor;
- if (avail < msgLength)
- {
- /*
- * Before looping, enlarge the input buffer if needed to hold the
- * whole message. See notes in parseInput.
- */
- if (pqCheckInBufferSpace(conn->inCursor + (size_t) msgLength,
- conn))
- {
- /*
- * XXX add some better recovery code... plan is to skip over
- * the message using its length, then report an error. For the
- * moment, just treat this like loss of sync (which indeed it
- * might be!)
- */
- handleSyncLoss(conn, id, msgLength);
- break;
- }
- continue;
- }
-
- /*
- * We should see V or E response to the command, but might get N
- * and/or A notices first. We also need to swallow the final Z before
- * returning.
- */
- switch (id)
- {
- case 'V': /* function result */
- if (pqGetInt(actual_result_len, 4, conn))
- continue;
- if (*actual_result_len != -1)
- {
- if (result_is_int)
- {
- if (pqGetInt(result_buf, *actual_result_len, conn))
- continue;
- }
- else
- {
- if (pqGetnchar((char *) result_buf,
- *actual_result_len,
- conn))
- continue;
- }
- }
- /* correctly finished function result message */
- status = PGRES_COMMAND_OK;
- break;
- case 'E': /* error return */
- if (pqGetErrorNotice3(conn, true))
- continue;
- status = PGRES_FATAL_ERROR;
- break;
- case 'A': /* notify message */
- /* handle notify and go back to processing return values */
- if (getNotify(conn))
- continue;
- break;
- case 'N': /* notice */
- /* handle notice and go back to processing return values */
- if (pqGetErrorNotice3(conn, false))
- continue;
- break;
- case 'Z': /* backend is ready for new query */
- if (getReadyForQuery(conn))
- continue;
- /* consume the message and exit */
- conn->inStart += 5 + msgLength;
- /* if we saved a result object (probably an error), use it */
- if (conn->result)
- return pqPrepareAsyncResult(conn);
- return PQmakeEmptyPGresult(conn, status);
- case 'S': /* parameter status */
- if (getParameterStatus(conn))
- continue;
- break;
- default:
- /* The backend violates the protocol. */
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("protocol error: id=0x%x\n"),
- id);
- pqSaveErrorResult(conn);
- /* trust the specified message length as what to skip */
- conn->inStart += 5 + msgLength;
- return pqPrepareAsyncResult(conn);
- }
- /* Completed this message, keep going */
- /* trust the specified message length as what to skip */
- conn->inStart += 5 + msgLength;
- needInput = false;
- }
-
- /*
- * We fall out of the loop only upon failing to read data.
- * conn->errorMessage has been set by pqWait or pqReadData. We want to
- * append it to any already-received error message.
- */
- pqSaveErrorResult(conn);
- return pqPrepareAsyncResult(conn);
-}
-
-
-/*
- * Construct startup packet
- *
- * Returns a malloc'd packet buffer, or NULL if out of memory
- */
-char *
-pqBuildStartupPacket3(PGconn *conn, int *packetlen,
- const PQEnvironmentOption *options)
-{
- char *startpacket;
-
- *packetlen = build_startup_packet(conn, NULL, options);
- startpacket = (char *) malloc(*packetlen);
- if (!startpacket)
- return NULL;
- *packetlen = build_startup_packet(conn, startpacket, options);
- return startpacket;
-}
-
-/*
- * Build a startup packet given a filled-in PGconn structure.
- *
- * We need to figure out how much space is needed, then fill it in.
- * To avoid duplicate logic, this routine is called twice: the first time
- * (with packet == NULL) just counts the space needed, the second time
- * (with packet == allocated space) fills it in. Return value is the number
- * of bytes used.
- */
-static int
-build_startup_packet(const PGconn *conn, char *packet,
- const PQEnvironmentOption *options)
-{
- int packet_len = 0;
- const PQEnvironmentOption *next_eo;
- const char *val;
-
- /* Protocol version comes first. */
- if (packet)
- {
- ProtocolVersion pv = htonl(conn->pversion);
-
- memcpy(packet + packet_len, &pv, sizeof(ProtocolVersion));
- }
- packet_len += sizeof(ProtocolVersion);
-
- /* Add user name, database name, options */
-
-#define ADD_STARTUP_OPTION(optname, optval) \
- do { \
- if (packet) \
- strcpy(packet + packet_len, optname); \
- packet_len += strlen(optname) + 1; \
- if (packet) \
- strcpy(packet + packet_len, optval); \
- packet_len += strlen(optval) + 1; \
- } while(0)
-
- if (conn->pguser && conn->pguser[0])
- ADD_STARTUP_OPTION("user", conn->pguser);
- if (conn->dbName && conn->dbName[0])
- ADD_STARTUP_OPTION("database", conn->dbName);
- if (conn->replication && conn->replication[0])
- ADD_STARTUP_OPTION("replication", conn->replication);
- if (conn->pgoptions && conn->pgoptions[0])
- ADD_STARTUP_OPTION("options", conn->pgoptions);
- if (conn->send_appname)
- {
- /* Use appname if present, otherwise use fallback */
- val = conn->appname ? conn->appname : conn->fbappname;
- if (val && val[0])
- ADD_STARTUP_OPTION("application_name", val);
- }
-
- if (conn->client_encoding_initial && conn->client_encoding_initial[0])
- ADD_STARTUP_OPTION("client_encoding", conn->client_encoding_initial);
-
- /* Add any environment-driven GUC settings needed */
- for (next_eo = options; next_eo->envName; next_eo++)
- {
- if ((val = getenv(next_eo->envName)) != NULL)
- {
- if (pg_strcasecmp(val, "default") != 0)
- ADD_STARTUP_OPTION(next_eo->pgName, val);
- }
- }
-
- /* Add trailing terminator */
- if (packet)
- packet[packet_len] = '\0';
- packet_len++;
-
- return packet_len;
-}
diff --git a/libpq/fe-secure.c b/libpq/fe-secure.c
deleted file mode 100644
index 94e47a5..0000000
--- a/libpq/fe-secure.c
+++ /dev/null
@@ -1,505 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * fe-secure.c
- * functions related to setting up a secure connection to the backend.
- * Secure connections are expected to provide confidentiality,
- * message integrity and endpoint authentication.
- *
- *
- * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- *
- * IDENTIFICATION
- * src/interfaces/libpq/fe-secure.c
- *
- * NOTES
- *
- * We don't provide informational callbacks here (like
- * info_cb() in be-secure.c), since there's no good mechanism to
- * display such information to the user.
- *
- *-------------------------------------------------------------------------
- */
-
-#include "postgres_fe.h"
-
-#include <signal.h>
-#include <fcntl.h>
-#include <ctype.h>
-
-#include "libpq-fe.h"
-#include "fe-auth.h"
-#include "libpq-int.h"
-
-#ifdef WIN32
-#include "win32.h"
-#else
-#include <sys/socket.h>
-#include <unistd.h>
-#include <netdb.h>
-#include <netinet/in.h>
-#ifdef HAVE_NETINET_TCP_H
-#include <netinet/tcp.h>
-#endif
-#include <arpa/inet.h>
-#endif
-
-#include <sys/stat.h>
-
-#ifdef ENABLE_THREAD_SAFETY
-#ifdef WIN32
-#include "pthread-win32.h"
-#else
-#include <pthread.h>
-#endif
-#endif
-
-/*
- * Macros to handle disabling and then restoring the state of SIGPIPE handling.
- * On Windows, these are all no-ops since there's no SIGPIPEs.
- */
-
-#ifndef WIN32
-
-#define SIGPIPE_MASKED(conn) ((conn)->sigpipe_so || (conn)->sigpipe_flag)
-
-#ifdef ENABLE_THREAD_SAFETY
-
-struct sigpipe_info
-{
- sigset_t oldsigmask;
- bool sigpipe_pending;
- bool got_epipe;
-};
-
-#define DECLARE_SIGPIPE_INFO(spinfo) struct sigpipe_info spinfo
-
-#define DISABLE_SIGPIPE(conn, spinfo, failaction) \
- do { \
- (spinfo).got_epipe = false; \
- if (!SIGPIPE_MASKED(conn)) \
- { \
- if (pq_block_sigpipe(&(spinfo).oldsigmask, \
- &(spinfo).sigpipe_pending) < 0) \
- failaction; \
- } \
- } while (0)
-
-#define REMEMBER_EPIPE(spinfo, cond) \
- do { \
- if (cond) \
- (spinfo).got_epipe = true; \
- } while (0)
-
-#define RESTORE_SIGPIPE(conn, spinfo) \
- do { \
- if (!SIGPIPE_MASKED(conn)) \
- pq_reset_sigpipe(&(spinfo).oldsigmask, (spinfo).sigpipe_pending, \
- (spinfo).got_epipe); \
- } while (0)
-#else /* !ENABLE_THREAD_SAFETY */
-
-#define DECLARE_SIGPIPE_INFO(spinfo) pqsigfunc spinfo = NULL
-
-#define DISABLE_SIGPIPE(conn, spinfo, failaction) \
- do { \
- if (!SIGPIPE_MASKED(conn)) \
- spinfo = pqsignal(SIGPIPE, SIG_IGN); \
- } while (0)
-
-#define REMEMBER_EPIPE(spinfo, cond)
-
-#define RESTORE_SIGPIPE(conn, spinfo) \
- do { \
- if (!SIGPIPE_MASKED(conn)) \
- pqsignal(SIGPIPE, spinfo); \
- } while (0)
-#endif /* ENABLE_THREAD_SAFETY */
-#else /* WIN32 */
-
-#define DECLARE_SIGPIPE_INFO(spinfo)
-#define DISABLE_SIGPIPE(conn, spinfo, failaction)
-#define REMEMBER_EPIPE(spinfo, cond)
-#define RESTORE_SIGPIPE(conn, spinfo)
-#endif /* WIN32 */
-
-/* ------------------------------------------------------------ */
-/* Procedures common to all secure sessions */
-/* ------------------------------------------------------------ */
-
-
-/*
- * Exported function to allow application to tell us it's already
- * initialized OpenSSL.
- */
-void
-PQinitSSL(int do_init)
-{
-#ifdef USE_SSL
- pgtls_init_library(do_init, do_init);
-#endif
-}
-
-/*
- * Exported function to allow application to tell us it's already
- * initialized OpenSSL and/or libcrypto.
- */
-void
-PQinitOpenSSL(int do_ssl, int do_crypto)
-{
-#ifdef USE_SSL
- pgtls_init_library(do_ssl, do_crypto);
-#endif
-}
-
-/*
- * Initialize global SSL context
- */
-int
-pqsecure_initialize(PGconn *conn)
-{
- int r = 0;
-
-#ifdef USE_SSL
- r = pgtls_init(conn);
-#endif
-
- return r;
-}
-
-/*
- * Begin or continue negotiating a secure session.
- */
-PostgresPollingStatusType
-pqsecure_open_client(PGconn *conn)
-{
-#ifdef USE_SSL
- return pgtls_open_client(conn);
-#else
- /* shouldn't get here */
- return PGRES_POLLING_FAILED;
-#endif
-}
-
-/*
- * Close secure session.
- */
-void
-pqsecure_close(PGconn *conn)
-{
-#ifdef USE_SSL
- if (conn->ssl_in_use)
- pgtls_close(conn);
-#endif
-}
-
-/*
- * Read data from a secure connection.
- *
- * On failure, this function is responsible for putting a suitable message
- * into conn->errorMessage. The caller must still inspect errno, but only
- * to determine whether to continue/retry after error.
- */
-ssize_t
-pqsecure_read(PGconn *conn, void *ptr, size_t len)
-{
- ssize_t n;
-
-#ifdef USE_SSL
- if (conn->ssl_in_use)
- {
- n = pgtls_read(conn, ptr, len);
- }
- else
-#endif
- {
- n = pqsecure_raw_read(conn, ptr, len);
- }
-
- return n;
-}
-
-ssize_t
-pqsecure_raw_read(PGconn *conn, void *ptr, size_t len)
-{
- ssize_t n;
- int result_errno = 0;
- char sebuf[256];
-
- n = recv(conn->sock, ptr, len, 0);
-
- if (n < 0)
- {
- result_errno = SOCK_ERRNO;
-
- /* Set error message if appropriate */
- switch (result_errno)
- {
-#ifdef EAGAIN
- case EAGAIN:
-#endif
-#if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
- case EWOULDBLOCK:
-#endif
- case EINTR:
- /* no error message, caller is expected to retry */
- break;
-
-#ifdef ECONNRESET
- case ECONNRESET:
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext(
- "server closed the connection unexpectedly\n"
- "\tThis probably means the server terminated abnormally\n"
- "\tbefore or while processing the request.\n"));
- break;
-#endif
-
- default:
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("could not receive data from server: %s\n"),
- SOCK_STRERROR(result_errno,
- sebuf, sizeof(sebuf)));
- break;
- }
- }
-
- /* ensure we return the intended errno to caller */
- SOCK_ERRNO_SET(result_errno);
-
- return n;
-}
-
-/*
- * Write data to a secure connection.
- *
- * On failure, this function is responsible for putting a suitable message
- * into conn->errorMessage. The caller must still inspect errno, but only
- * to determine whether to continue/retry after error.
- */
-ssize_t
-pqsecure_write(PGconn *conn, const void *ptr, size_t len)
-{
- ssize_t n;
-
-#ifdef USE_SSL
- if (conn->ssl_in_use)
- {
- n = pgtls_write(conn, ptr, len);
- }
- else
-#endif
- {
- n = pqsecure_raw_write(conn, ptr, len);
- }
-
- return n;
-}
-
-ssize_t
-pqsecure_raw_write(PGconn *conn, const void *ptr, size_t len)
-{
- ssize_t n;
- int flags = 0;
- int result_errno = 0;
- char sebuf[256];
-
- DECLARE_SIGPIPE_INFO(spinfo);
-
-#ifdef MSG_NOSIGNAL
- if (conn->sigpipe_flag)
- flags |= MSG_NOSIGNAL;
-
-retry_masked:
-#endif /* MSG_NOSIGNAL */
-
- DISABLE_SIGPIPE(conn, spinfo, return -1);
-
- n = send(conn->sock, ptr, len, flags);
-
- if (n < 0)
- {
- result_errno = SOCK_ERRNO;
-
- /*
- * If we see an EINVAL, it may be because MSG_NOSIGNAL isn't available
- * on this machine. So, clear sigpipe_flag so we don't try the flag
- * again, and retry the send().
- */
-#ifdef MSG_NOSIGNAL
- if (flags != 0 && result_errno == EINVAL)
- {
- conn->sigpipe_flag = false;
- flags = 0;
- goto retry_masked;
- }
-#endif /* MSG_NOSIGNAL */
-
- /* Set error message if appropriate */
- switch (result_errno)
- {
-#ifdef EAGAIN
- case EAGAIN:
-#endif
-#if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
- case EWOULDBLOCK:
-#endif
- case EINTR:
- /* no error message, caller is expected to retry */
- break;
-
- case EPIPE:
- /* Set flag for EPIPE */
- REMEMBER_EPIPE(spinfo, true);
- /* FALL THRU */
-
-#ifdef ECONNRESET
- case ECONNRESET:
-#endif
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext(
- "server closed the connection unexpectedly\n"
- "\tThis probably means the server terminated abnormally\n"
- "\tbefore or while processing the request.\n"));
- break;
-
- default:
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("could not send data to server: %s\n"),
- SOCK_STRERROR(result_errno,
- sebuf, sizeof(sebuf)));
- break;
- }
- }
-
- RESTORE_SIGPIPE(conn, spinfo);
-
- /* ensure we return the intended errno to caller */
- SOCK_ERRNO_SET(result_errno);
-
- return n;
-}
-
-/* Dummy versions of SSL info functions, when built without SSL support */
-#ifndef USE_SSL
-
-int
-PQsslInUse(PGconn *conn)
-{
- return 0;
-}
-
-void *
-PQgetssl(PGconn *conn)
-{
- return NULL;
-}
-
-void *
-PQsslStruct(PGconn *conn, const char *struct_name)
-{
- return NULL;
-}
-
-const char *
-PQsslAttribute(PGconn *conn, const char *attribute_name)
-{
- return NULL;
-}
-
-const char *const *
-PQsslAttributeNames(PGconn *conn)
-{
- static const char *const result[] = {NULL};
-
- return result;
-}
-#endif /* USE_SSL */
-
-
-#if defined(ENABLE_THREAD_SAFETY) && !defined(WIN32)
-
-/*
- * Block SIGPIPE for this thread. This prevents send()/write() from exiting
- * the application.
- */
-int
-pq_block_sigpipe(sigset_t *osigset, bool *sigpipe_pending)
-{
- sigset_t sigpipe_sigset;
- sigset_t sigset;
-
- sigemptyset(&sigpipe_sigset);
- sigaddset(&sigpipe_sigset, SIGPIPE);
-
- /* Block SIGPIPE and save previous mask for later reset */
- SOCK_ERRNO_SET(pthread_sigmask(SIG_BLOCK, &sigpipe_sigset, osigset));
- if (SOCK_ERRNO)
- return -1;
-
- /* We can have a pending SIGPIPE only if it was blocked before */
- if (sigismember(osigset, SIGPIPE))
- {
- /* Is there a pending SIGPIPE? */
- if (sigpending(&sigset) != 0)
- return -1;
-
- if (sigismember(&sigset, SIGPIPE))
- *sigpipe_pending = true;
- else
- *sigpipe_pending = false;
- }
- else
- *sigpipe_pending = false;
-
- return 0;
-}
-
-/*
- * Discard any pending SIGPIPE and reset the signal mask.
- *
- * Note: we are effectively assuming here that the C library doesn't queue
- * up multiple SIGPIPE events. If it did, then we'd accidentally leave
- * ours in the queue when an event was already pending and we got another.
- * As long as it doesn't queue multiple events, we're OK because the caller
- * can't tell the difference.
- *
- * The caller should say got_epipe = FALSE if it is certain that it
- * didn't get an EPIPE error; in that case we'll skip the clear operation
- * and things are definitely OK, queuing or no. If it got one or might have
- * gotten one, pass got_epipe = TRUE.
- *
- * We do not want this to change errno, since if it did that could lose
- * the error code from a preceding send(). We essentially assume that if
- * we were able to do pq_block_sigpipe(), this can't fail.
- */
-void
-pq_reset_sigpipe(sigset_t *osigset, bool sigpipe_pending, bool got_epipe)
-{
- int save_errno = SOCK_ERRNO;
- int signo;
- sigset_t sigset;
-
- /* Clear SIGPIPE only if none was pending */
- if (got_epipe && !sigpipe_pending)
- {
- if (sigpending(&sigset) == 0 &&
- sigismember(&sigset, SIGPIPE))
- {
- sigset_t sigpipe_sigset;
-
- sigemptyset(&sigpipe_sigset);
- sigaddset(&sigpipe_sigset, SIGPIPE);
-
- sigwait(&sigpipe_sigset, &signo);
- }
- }
-
- /* Restore saved block mask */
- pthread_sigmask(SIG_SETMASK, osigset, NULL);
-
- SOCK_ERRNO_SET(save_errno);
-}
-
-#endif /* ENABLE_THREAD_SAFETY && !WIN32 */
diff --git a/libpq/getpeereid.c b/libpq/getpeereid.c
deleted file mode 100644
index 5f8c0be..0000000
--- a/libpq/getpeereid.c
+++ /dev/null
@@ -1,80 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * getpeereid.c
- * get peer userid for UNIX-domain socket connection
- *
- * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
- *
- *
- * IDENTIFICATION
- * src/port/getpeereid.c
- *
- *-------------------------------------------------------------------------
- */
-
-#include "c.h"
-
-#include <sys/param.h>
-#include <sys/socket.h>
-#include <unistd.h>
-#ifdef HAVE_SYS_UN_H
-#include <sys/un.h>
-#endif
-#ifdef HAVE_UCRED_H
-#include <ucred.h>
-#endif
-#ifdef HAVE_SYS_UCRED_H
-#include <sys/ucred.h>
-#endif
-
-
-/*
- * BSD-style getpeereid() for platforms that lack it.
- */
-int
-getpeereid(int sock, uid_t *uid, gid_t *gid)
-{
-#if defined(SO_PEERCRED)
- /* Linux: use getsockopt(SO_PEERCRED) */
- struct ucred peercred;
- ACCEPT_TYPE_ARG3 so_len = sizeof(peercred);
-
- if (getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &peercred, &so_len) != 0 ||
- so_len != sizeof(peercred))
- return -1;
- *uid = peercred.uid;
- *gid = peercred.gid;
- return 0;
-#elif defined(LOCAL_PEERCRED)
- /* Debian with FreeBSD kernel: use getsockopt(LOCAL_PEERCRED) */
- struct xucred peercred;
- ACCEPT_TYPE_ARG3 so_len = sizeof(peercred);
-
- if (getsockopt(sock, 0, LOCAL_PEERCRED, &peercred, &so_len) != 0 ||
- so_len != sizeof(peercred) ||
- peercred.cr_version != XUCRED_VERSION)
- return -1;
- *uid = peercred.cr_uid;
- *gid = peercred.cr_gid;
- return 0;
-#elif defined(HAVE_GETPEERUCRED)
- /* Solaris: use getpeerucred() */
- ucred_t *ucred;
-
- ucred = NULL; /* must be initialized to NULL */
- if (getpeerucred(sock, &ucred) == -1)
- return -1;
-
- *uid = ucred_geteuid(ucred);
- *gid = ucred_getegid(ucred);
- ucred_free(ucred);
-
- if (*uid == (uid_t) (-1) || *gid == (gid_t) (-1))
- return -1;
- return 0;
-#else
- /* No implementation available on this platform */
- errno = ENOSYS;
- return -1;
-#endif
-}
diff --git a/libpq/inet_net_ntop.c b/libpq/inet_net_ntop.c
deleted file mode 100644
index 047895e..0000000
--- a/libpq/inet_net_ntop.c
+++ /dev/null
@@ -1,298 +0,0 @@
-/*
- * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
- * Copyright (c) 1996,1999 by Internet Software Consortium.
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
- * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- * src/backend/utils/adt/inet_net_ntop.c
- */
-
-#if defined(LIBC_SCCS) && !defined(lint)
-static const char rcsid[] = "Id: inet_net_ntop.c,v 1.1.2.2 2004/03/09 09:17:27 marka Exp $";
-#endif
-
-#ifndef FRONTEND
-#include "postgres.h"
-#else
-#include "postgres_fe.h"
-#endif
-
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-
-#ifndef FRONTEND
-#include "utils/inet.h"
-#else
-/*
- * In a frontend build, we can't include inet.h, but we still need to have
- * sensible definitions of these two constants. Note that inet_net_ntop()
- * assumes that PGSQL_AF_INET is equal to AF_INET.
- */
-#define PGSQL_AF_INET (AF_INET + 0)
-#define PGSQL_AF_INET6 (AF_INET + 1)
-#endif
-
-
-#define NS_IN6ADDRSZ 16
-#define NS_INT16SZ 2
-
-#ifdef SPRINTF_CHAR
-#define SPRINTF(x) strlen(sprintf/**/x)
-#else
-#define SPRINTF(x) ((size_t)sprintf x)
-#endif
-
-static char *inet_net_ntop_ipv4(const u_char *src, int bits,
- char *dst, size_t size);
-static char *inet_net_ntop_ipv6(const u_char *src, int bits,
- char *dst, size_t size);
-
-
-/*
- * char *
- * inet_net_ntop(af, src, bits, dst, size)
- * convert host/network address from network to presentation format.
- * "src"'s size is determined from its "af".
- * return:
- * pointer to dst, or NULL if an error occurred (check errno).
- * note:
- * 192.5.5.1/28 has a nonzero host part, which means it isn't a network
- * as called for by inet_net_pton() but it can be a host address with
- * an included netmask.
- * author:
- * Paul Vixie (ISC), October 1998
- */
-char *
-inet_net_ntop(int af, const void *src, int bits, char *dst, size_t size)
-{
- /*
- * We need to cover both the address family constants used by the PG inet
- * type (PGSQL_AF_INET and PGSQL_AF_INET6) and those used by the system
- * libraries (AF_INET and AF_INET6). We can safely assume PGSQL_AF_INET
- * == AF_INET, but the INET6 constants are very likely to be different. If
- * AF_INET6 isn't defined, silently ignore it.
- */
- switch (af)
- {
- case PGSQL_AF_INET:
- return (inet_net_ntop_ipv4(src, bits, dst, size));
- case PGSQL_AF_INET6:
-#if defined(AF_INET6) && AF_INET6 != PGSQL_AF_INET6
- case AF_INET6:
-#endif
- return (inet_net_ntop_ipv6(src, bits, dst, size));
- default:
- errno = EAFNOSUPPORT;
- return (NULL);
- }
-}
-
-/*
- * static char *
- * inet_net_ntop_ipv4(src, bits, dst, size)
- * convert IPv4 network address from network to presentation format.
- * "src"'s size is determined from its "af".
- * return:
- * pointer to dst, or NULL if an error occurred (check errno).
- * note:
- * network byte order assumed. this means 192.5.5.240/28 has
- * 0b11110000 in its fourth octet.
- * author:
- * Paul Vixie (ISC), October 1998
- */
-static char *
-inet_net_ntop_ipv4(const u_char *src, int bits, char *dst, size_t size)
-{
- char *odst = dst;
- char *t;
- int len = 4;
- int b;
-
- if (bits < 0 || bits > 32)
- {
- errno = EINVAL;
- return (NULL);
- }
-
- /* Always format all four octets, regardless of mask length. */
- for (b = len; b > 0; b--)
- {
- if (size <= sizeof ".255")
- goto emsgsize;
- t = dst;
- if (dst != odst)
- *dst++ = '.';
- dst += SPRINTF((dst, "%u", *src++));
- size -= (size_t) (dst - t);
- }
-
- /* don't print masklen if 32 bits */
- if (bits != 32)
- {
- if (size <= sizeof "/32")
- goto emsgsize;
- dst += SPRINTF((dst, "/%u", bits));
- }
-
- return (odst);
-
-emsgsize:
- errno = EMSGSIZE;
- return (NULL);
-}
-
-static int
-decoct(const u_char *src, int bytes, char *dst, size_t size)
-{
- char *odst = dst;
- char *t;
- int b;
-
- for (b = 1; b <= bytes; b++)
- {
- if (size <= sizeof "255.")
- return (0);
- t = dst;
- dst += SPRINTF((dst, "%u", *src++));
- if (b != bytes)
- {
- *dst++ = '.';
- *dst = '\0';
- }
- size -= (size_t) (dst - t);
- }
- return (dst - odst);
-}
-
-static char *
-inet_net_ntop_ipv6(const u_char *src, int bits, char *dst, size_t size)
-{
- /*
- * Note that int32_t and int16_t need only be "at least" large enough to
- * contain a value of the specified size. On some systems, like Crays,
- * there is no such thing as an integer variable with 16 bits. Keep this
- * in mind if you think this function should have been coded to use
- * pointer overlays. All the world's not a VAX.
- */
- char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255/128"];
- char *tp;
- struct
- {
- int base,
- len;
- } best, cur;
- u_int words[NS_IN6ADDRSZ / NS_INT16SZ];
- int i;
-
- if ((bits < -1) || (bits > 128))
- {
- errno = EINVAL;
- return (NULL);
- }
-
- /*
- * Preprocess: Copy the input (bytewise) array into a wordwise array. Find
- * the longest run of 0x00's in src[] for :: shorthanding.
- */
- memset(words, '\0', sizeof words);
- for (i = 0; i < NS_IN6ADDRSZ; i++)
- words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3));
- best.base = -1;
- cur.base = -1;
- best.len = 0;
- cur.len = 0;
- for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++)
- {
- if (words[i] == 0)
- {
- if (cur.base == -1)
- cur.base = i, cur.len = 1;
- else
- cur.len++;
- }
- else
- {
- if (cur.base != -1)
- {
- if (best.base == -1 || cur.len > best.len)
- best = cur;
- cur.base = -1;
- }
- }
- }
- if (cur.base != -1)
- {
- if (best.base == -1 || cur.len > best.len)
- best = cur;
- }
- if (best.base != -1 && best.len < 2)
- best.base = -1;
-
- /*
- * Format the result.
- */
- tp = tmp;
- for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++)
- {
- /* Are we inside the best run of 0x00's? */
- if (best.base != -1 && i >= best.base &&
- i < (best.base + best.len))
- {
- if (i == best.base)
- *tp++ = ':';
- continue;
- }
- /* Are we following an initial run of 0x00s or any real hex? */
- if (i != 0)
- *tp++ = ':';
- /* Is this address an encapsulated IPv4? */
- if (i == 6 && best.base == 0 && (best.len == 6 ||
- (best.len == 7 && words[7] != 0x0001) ||
- (best.len == 5 && words[5] == 0xffff)))
- {
- int n;
-
- n = decoct(src + 12, 4, tp, sizeof tmp - (tp - tmp));
- if (n == 0)
- {
- errno = EMSGSIZE;
- return (NULL);
- }
- tp += strlen(tp);
- break;
- }
- tp += SPRINTF((tp, "%x", words[i]));
- }
-
- /* Was it a trailing run of 0x00's? */
- if (best.base != -1 && (best.base + best.len) ==
- (NS_IN6ADDRSZ / NS_INT16SZ))
- *tp++ = ':';
- *tp = '\0';
-
- if (bits != -1 && bits != 128)
- tp += SPRINTF((tp, "/%u", bits));
-
- /*
- * Check for overflow, copy, and we're done.
- */
- if ((size_t) (tp - tmp) > size)
- {
- errno = EMSGSIZE;
- return (NULL);
- }
- strcpy(dst, tmp);
- return (dst);
-}
diff --git a/libpq/ip.c b/libpq/ip.c
deleted file mode 100644
index 9591ed2..0000000
--- a/libpq/ip.c
+++ /dev/null
@@ -1,819 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * ip.c
- * IPv6-aware network access.
- *
- * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- *
- * IDENTIFICATION
- * src/backend/libpq/ip.c
- *
- * This file and the IPV6 implementation were initially provided by
- * Nigel Kukard <nkukard@lbsd.net>, Linux Based Systems Design
- * http://www.lbsd.net.
- *
- *---------------------------------------------------------