summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2017-09-20 22:02:04 (GMT)
committerKaren Arutyunov <karen@codesynthesis.com>2017-10-09 16:14:35 (GMT)
commit2a969b7f4bdb223d3626dc14b684701942ccafb2 (patch)
tree5b317acd47e5183b7dd127417cd0017c39f28f94
parent05ac4b76f5d30def9d51815c7f25d54316592f45 (diff)
Make package to be source rather than stub
-rw-r--r--.gitignore19
-rw-r--r--COPYRIGHT23
-rw-r--r--INSTALL7
-rw-r--r--README5
-rw-r--r--README-DEV102
-rw-r--r--TODO14
-rw-r--r--build/bootstrap.build37
-rw-r--r--build/export.build10
-rw-r--r--build/root.build10
-rw-r--r--buildfile17
-rw-r--r--libpq/.gitignore3
-rw-r--r--libpq/buildfile190
-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/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.h280
-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.h20
-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/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
-rw-r--r--manifest12
-rw-r--r--tests/.gitignore3
-rw-r--r--tests/build/.gitignore1
-rw-r--r--tests/build/bootstrap.build9
-rw-r--r--tests/build/root.build21
-rw-r--r--tests/buildfile5
-rw-r--r--tests/conninfo/buildfile13
-rw-r--r--tests/conninfo/driver.c74
-rw-r--r--tests/conninfo/expected.out171
-rw-r--r--tests/conninfo/postgres_fe.h19
-rw-r--r--tests/conninfo/regress.in57
-rw-r--r--tests/conninfo/testscript5
-rw-r--r--tests/conninfo/uri-regress.c84
105 files changed, 36318 insertions, 9 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..3dd23c1
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,19 @@
+# Compiler/linker output.
+#
+*.d
+*.i
+*.o
+*.obj
+*.so
+*.dll
+*.a
+*.lib
+*.exp
+*.exe
+*.exe.dlls/
+*.exe.manifest
+*.pc
+
+# Generated version.
+#
+version
diff --git a/COPYRIGHT b/COPYRIGHT
new file mode 100644
index 0000000..c320ecc
--- /dev/null
+++ b/COPYRIGHT
@@ -0,0 +1,23 @@
+PostgreSQL Database Management System
+(formerly known as Postgres, then as Postgres95)
+
+Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+
+Portions Copyright (c) 1994, The Regents of the University of California
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose, without fee, and without a written agreement
+is hereby granted, provided that the above copyright notice and this
+paragraph and the following two paragraphs appear in all copies.
+
+IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
+DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO
+PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..61ec557
--- /dev/null
+++ b/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/README b/README
index 8db0f8d..4aa73f2 100644
--- a/README
+++ b/README
@@ -5,9 +5,8 @@ using the C programming language. For more information see:
https://www.postgresql.org/
-This package currently contains a build2 package manager (bpkg) stub meaning
-that it can only be "built" as already installed in the underlying system (for
-example, using a system package manager).
+This package contains the original libpq library source code overlaid with the
+build2-based build system and packaged for the build2 package manager (bpkg).
Send questions, bug reports, or any other feedback about the library itself to
the PostgreSQL mailing lists. Send build system and packaging-related feedback
diff --git a/README-DEV b/README-DEV
new file mode 100644
index 0000000..4a56908
--- /dev/null
+++ b/README-DEV
@@ -0,0 +1,102 @@
+This document describes for libpq was packages for build2. In particular,
+this understanding will be useful when ugrading to a new upstream version.
+
+The original libpq library is packaged together with the PostgreSQL server and
+client utilities. Most of the libpq source files are located in the
+src/interfaces/libpq/ directory. Some .c files are symlinked (copied on
+Windows) from other src/ subdirectories during make. So run configure script
+in the package root, run make in src/interfaces/libpq/ and then copy source
+files and symlink targets into libpq/ directory of the build2 package. Note
+that to obtain the full set of source files that includes Windows-specific
+ones, you should perform these steps in the MSYS2/MinGW environment. Copy
+Windows-specific files and strlcpy.c to libpq/win32/ and libpq/non-bsd/
+directories respectively. Also copy src/port/pthread-win32.h to libpq/ (next
+to pthread-win32.c).
+
+Note that the library name in the .def file takes precedence over the one
+specified in the linker command line for both VC and MinGW GCC. So we comment
+it out in libpq/win32/libpqdll.def.
+
+Besides libpq-specific files some of the PostgreSQL common headers are also
+required. All of them except one (see below) are located in src/include/
+subdirectories. Copy them into the libpq/postgresql/ directory, preserving the
+original directory structure, with the following exceptions:
+
+ * src/include/pg_config_os.h
+
+ This is a symlink referencing the target-specific header in the
+ src/include/port/ directory, created by the configure script. Copy these
+ headers under postgresql/port/*/pg_config_os.h path names for the supported
+ target classes. For example, copy src/include/port/linux.h to
+ libpq/postgresql/port/linux/pg_config_os.h. During compilation the
+ '-I.../libpq/postgresql/port/linux' option will be passed to the compiler,
+ so the appropriate pg_config_os.h is picked up.
+
+ * src/port/pg_config_paths.h
+
+ This file is generated by src/port/Makfile and defines several directory
+ path macros. Only SYSCONFDIR macro is used in libpq source files. Make the
+ file empty and place it into libpq/postgresql directory. During compilation
+ the macro will be defined with -DSYSCONFDIR preprocessor option.
+
+ * src/include/pg_config.h.in,
+ src/include/pg_config.h.win32
+ pg_config_ext.h.in
+ pg_config_ext.h.win32
+
+ Use as sources for the manual creation of libpq/postgresql/pg_config.h and
+ libpq/postgresql/pg_config_ext.h, that are used for all target systems.
+ Also add the source headers to the package appending the '.orig' extension
+ to their names.
+
+By default the original package installs the library into the /usr/local/pgsql
+directory. There are also several 'unofficial API' headers installed into the
+/usr/local/pgsql/include/internal directory. If installing at some custom
+location that has no postgresql or pgsql words in its path, then it becomes
+<custom-location>/include/postgresql/internal. We currently do not install
+unofficial API headers.
+
+For the record, the PostgreSQL binary and development packages install
+libraries and headers into the following directories:
+
+Debian/Ubuntu:
+ /usr/lib/x86_64-linux-gnu
+ /usr/include/postgresql
+ /usr/include/postgresql/internal
+
+Fedora/RHEL:
+ /usr/lib64
+ /usr/include
+ /usr/include/pgsql/internal
+
+When merge libpq build2 package with a new version of the original package
+make sure that all the preprocessor include directives reference the packaged
+header files, rather than PostgreSQL headers that are installed into the
+system. It's easy to miss some headers in the package if the PostgreSQL
+development package is installed on the host. To verify the correctness you
+can build the merged project, concatenate the produced .d files, sort the
+resulting file removing duplicates and edit the result, leaving only the
+system headers. Afterwards grep through the remained headers for the
+'PostgreSQL' pattern:
+
+$ cat `find . -name '*.d'` | sort -u >headers
+$ emacs headers # Edit, leaving system headers only.
+$ fgrep PostgreSQL `cat headers`
+
+Also make sure that the macros set in libpq/postgresql/pg_config.h are still
+up to date. For that purpose obtain the macros that are used in the new source
+base, then obtain the macros (un)defined in the current
+libpq/postgresql/pg_config.h and compare the sets. That can be achieved
+running the following commands in the build2 project root directory:
+
+$ for m in `cat libpq/postgresql/pg_config.h.in.orig libpq/postgresql/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 . -name '*.h' -a ! -name 'pg_config.h' -o -name '*.c'`; then
+ echo "$m"
+ fi
+ done >used-macros
+
+$ cat libpq/postgresql/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
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..49528d6
--- /dev/null
+++ b/TODO
@@ -0,0 +1,14 @@
+@@ Should we enable/make configurable Native Language Support? See --enable-nls
+ configure option (off by default, at least for locale with
+ LANG=en_US.UTF-8), ENABLE_NLS macro, libintl.h, src/interfaces/libpq/po/ for
+ details.
+
+@@ Should we make configurable OpenSSL support? See --with-openssl configure
+ option, fe-secure-openssl.c (should also be compiled/linked), USE_OPENSSL
+ macro for details.
+
+@@ Should we make configurable LDAP support? See --with-ldap configure option,
+ USE_LDAP macro.
+
+@@ Should we make configurable GSSAPI support? See --with-gssapi configure
+ option, ENABLE_GSS macro.
diff --git a/build/bootstrap.build b/build/bootstrap.build
index ac03a3d..d379891 100644
--- a/build/bootstrap.build
+++ b/build/bootstrap.build
@@ -1,9 +1,44 @@
# file : build/bootstrap.build
# copyright : Copyright (c) 2016-2017 Code Synthesis Ltd
-# license : PostgreSQL License
+# 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 denote 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/
+#
+# There is no document that describes libpq ABI versioning and compatibility
+# rules, so everything that follows is just a guess.
+#
+# The library naming schema on POSIX is libpq.so.<so_major>.<so_minor> with
+# the so_minor number incremented with each major release unless the so_major
+# number is incremented, in which case it is reset to 0. It is unclear when
+# the so_major is incremented (it, for example, hasn't been in the 9.6 to
+# 10 transition).
+#
+# The <so_major>.<so_minor> pair constitutes the version of the ABI that is
+# backwards-compatible between PostgreSQL minor releases. However, we can not
+# deduce the ABI version from the release major version and will have to check
+# it for each major release by examining the SO_MAJOR_VERSION and
+# SO_MINOR_VERSION variables in src/interfaces/libpq/Makefile.
+#
+if ($version.major == 9 && $version.minor == 6)
+{
+ abi_major = 5
+ abi_minor = 9
+}
+else
+ fail "increment the ABI version?"
+
+abi_version = $abi_major.$abi_minor
diff --git a/build/export.build b/build/export.build
new file mode 100644
index 0000000..447334f
--- /dev/null
+++ b/build/export.build
@@ -0,0 +1,10 @@
+# file : build/export.build
+# copyright : Copyright (c) 2016-2017 Code Synthesis Ltd
+# license : PostgreSQL License; see accompanying COPYRIGHT file
+
+$out_root/:
+{
+ include libpq/
+}
+
+export $out_root/libpq/lib{pq}
diff --git a/build/root.build b/build/root.build
new file mode 100644
index 0000000..6d152ab
--- /dev/null
+++ b/build/root.build
@@ -0,0 +1,10 @@
+# file : build/root.build
+# copyright : Copyright (c) 2016-2017 Code Synthesis Ltd
+# license : PostgreSQL License; see accompanying COPYRIGHT file
+
+c.std = 99
+
+using c
+
+h{*}: extension = h
+c{*}: extension = c
diff --git a/buildfile b/buildfile
index 499ca8d..edf5e7f 100644
--- a/buildfile
+++ b/buildfile
@@ -1,5 +1,18 @@
# file : buildfile
# copyright : Copyright (c) 2016-2017 Code Synthesis Ltd
-# license : PostgreSQL License
+# license : PostgreSQL License; see accompanying COPYRIGHT file
-./: doc{README} file{manifest}
+./: {*/ -build/} doc{COPYRIGHT INSTALL README version} file{manifest}
+
+# The version file is auto-generated (by the version module) from manifest.
+# Include it in distribution and don't remove when cleaning in src (so that
+# clean results in a state identical to distributed).
+#
+doc{version}: file{manifest}
+doc{version}: dist = true
+doc{version}: clean = ($src_root != $out_root)
+
+# Don't install tests or the INSTALL file.
+#
+dir{tests/}: install = false
+doc{INSTALL}@./: install = false
diff --git a/libpq/.gitignore b/libpq/.gitignore
new file mode 100644
index 0000000..620b4c8
--- /dev/null
+++ b/libpq/.gitignore
@@ -0,0 +1,3 @@
+# Generated version.h.
+#
+version.h
diff --git a/libpq/buildfile b/libpq/buildfile
new file mode 100644
index 0000000..35638c8
--- /dev/null
+++ b/libpq/buildfile
@@ -0,0 +1,190 @@
+# file : libpq/buildfile
+# copyright : Copyright (c) 2016-2017 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}} file{win32/libpqdll.def} \
+ file{pg_service.conf.sample}
+
+tclass = $c.target.class
+tsys = $c.target.system
+
+if ($tclass == "windows")
+ lib{pq}: win32/c{*}
+else
+ lib{pq}: win32/file{*.c}
+
+if ($tclass != "bsd" && $tclass != "macos")
+ lib{pq}: non-bsd/c{*}
+else
+ lib{pq}: non-bsd/file{*.c}
+
+# 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"
+
+# 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).
+#
+h{version}: in{version} $src_root/file{manifest}
+
+c.poptions += -DFRONTEND -DUNSAFE_STAT_OK -DSO_MAJOR_VERSION=$abi_major
+
+if ($tclass != "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 = ($tclass == "windows" ? "win32" : \
+ $tclass == "macos" ? "darwin" : \
+ $tclass)
+
+c.poptions =+ "-I$src_base" \
+ "-I$src_base/postgresql/port/$port_dir" \
+ "-I$src_base/postgresql" \
+ "-I$src_root"
+
+if ($tclass == "windows")
+ obj{*}: c.poptions =+ "-I$src_base/win32"
+
+if ($tsys == "win32-msvc")
+{
+ c.poptions =+ "-I$src_base/postgresql/port/win32_msvc"
+
+ # Disable warnings that pop up with /W3.
+ #
+ c.poptions += -D_CRT_SECURE_NO_DEPRECATE -D_CRT_NONSTDC_NO_DEPRECATE
+ c.coptions += /wd4018 /wd4244 /wd4267
+}
+else
+{
+ # Omit -fexcess-precision=standard as -std=9x implies it.
+ #
+ c.coptions += -fno-strict-aliasing -fwrapv
+
+ # Disable warnings that pop up with -W -Wall.
+ #
+ c.coptions += -Wno-unused-parameter -Wno-missing-field-initializers \
+ -Wno-sign-compare -Wno-unused-command-line-argument
+}
+
+# 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 ($tclass == "windows")
+{
+ # win32.mak from the original package does this.
+ #
+ sysconfdir = ""
+}
+else
+{
+ # 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
+}
+
+# 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 ($tclass != "windows")
+{
+ c.libs += -lpthread
+}
+else
+{
+ def = $src_base/win32/libpqdll.def
+
+ if ($tsys == "mingw32")
+ {
+ libs{pq}: c.loptions += $def
+ c.libs += -lsecur32 -lws2_32 -ladvapi32
+ }
+ else
+ {
+ libs{pq}: c.loptions += "/DEF:$def"
+ c.libs += secur32.lib ws2_32.lib advapi32.lib
+ }
+
+ # 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.
+}
+
+lib{pq}: cc.export.poptions = "-I$src_base" "-I$src_base/postgresql"
+
+# Internal header (see above).
+#
+h{version}: install = false
+
+file{pg_service.conf.sample}@./: install = data/
diff --git a/libpq/chklocale.c b/libpq/chklocale.c
new file mode 100644
index 0000000..3c0ef6a
--- /dev/null
+++ b/libpq/chklocale.c
@@ -0,0 +1,445 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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
new file mode 100644
index 0000000..11099b8
--- /dev/null
+++ b/libpq/encnames.c
@@ -0,0 +1,553 @@
+/*
+ * 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
new file mode 100644
index 0000000..ab057e9
--- /dev/null
+++ b/libpq/fe-auth.c
@@ -0,0 +1,841 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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
new file mode 100644
index 0000000..9d11654
--- /dev/null
+++ b/libpq/fe-auth.h
@@ -0,0 +1,24 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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
new file mode 100644
index 0000000..e61ed7d
--- /dev/null
+++ b/libpq/fe-connect.c
@@ -0,0 +1,6002 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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
new file mode 100644
index 0000000..87ff565
--- /dev/null
+++ b/libpq/fe-exec.c
@@ -0,0 +1,3764 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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
new file mode 100644
index 0000000..3b08768
--- /dev/null
+++ b/libpq/fe-lobj.c
@@ -0,0 +1,1103 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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
new file mode 100644
index 0000000..32da8ca
--- /dev/null
+++ b/libpq/fe-misc.c
@@ -0,0 +1,1267 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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
new file mode 100644
index 0000000..e596a51
--- /dev/null
+++ b/libpq/fe-print.c
@@ -0,0 +1,761 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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
new file mode 100644
index 0000000..f1b90f3
--- /dev/null
+++ b/libpq/fe-protocol2.c
@@ -0,0 +1,1623 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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
new file mode 100644
index 0000000..a8a987a
--- /dev/null
+++ b/libpq/fe-protocol3.c
@@ -0,0 +1,2204 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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
new file mode 100644
index 0000000..94e47a5
--- /dev/null
+++ b/libpq/fe-secure.c
@@ -0,0 +1,505 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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
new file mode 100644
index 0000000..5f8c0be
--- /dev/null
+++ b/libpq/getpeereid.c
@@ -0,0 +1,80 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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
new file mode 100644
index 0000000..047895e
--- /dev/null
+++ b/libpq/inet_net_ntop.c
@@ -0,0 +1,298 @@
+/*
+ * 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
new file mode 100644
index 0000000..9591ed2
--- /dev/null
+++ b/libpq/ip.c
@@ -0,0 +1,819 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+/* This is intended to be used in both frontend and backend, so use c.h */
+#include "c.h"
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#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>
+#include <sys/file.h>
+
+#include "libpq/ip.h"
+
+
+static int range_sockaddr_AF_INET(const struct sockaddr_in * addr,
+ const struct sockaddr_in * netaddr,
+ const struct sockaddr_in * netmask);
+
+#ifdef HAVE_IPV6
+static int range_sockaddr_AF_INET6(const struct sockaddr_in6 * addr,
+ const struct sockaddr_in6 * netaddr,
+ const struct sockaddr_in6 * netmask);
+#endif
+
+#ifdef HAVE_UNIX_SOCKETS
+static int getaddrinfo_unix(const char *path,
+ const struct addrinfo * hintsp,
+ struct addrinfo ** result);
+
+static int getnameinfo_unix(const struct sockaddr_un * sa, int salen,
+ char *node, int nodelen,
+ char *service, int servicelen,
+ int flags);
+#endif
+
+
+/*
+ * pg_getaddrinfo_all - get address info for Unix, IPv4 and IPv6 sockets
+ */
+int
+pg_getaddrinfo_all(const char *hostname, const char *servname,
+ const struct addrinfo * hintp, struct addrinfo ** result)
+{
+ int rc;
+
+ /* not all versions of getaddrinfo() zero *result on failure */
+ *result = NULL;
+
+#ifdef HAVE_UNIX_SOCKETS
+ if (hintp->ai_family == AF_UNIX)
+ return getaddrinfo_unix(servname, hintp, result);
+#endif
+
+ /* NULL has special meaning to getaddrinfo(). */
+ rc = getaddrinfo((!hostname || hostname[0] == '\0') ? NULL : hostname,
+ servname, hintp, result);
+
+ return rc;
+}
+
+
+/*
+ * pg_freeaddrinfo_all - free addrinfo structures for IPv4, IPv6, or Unix
+ *
+ * Note: the ai_family field of the original hint structure must be passed
+ * so that we can tell whether the addrinfo struct was built by the system's
+ * getaddrinfo() routine or our own getaddrinfo_unix() routine. Some versions
+ * of getaddrinfo() might be willing to return AF_UNIX addresses, so it's
+ * not safe to look at ai_family in the addrinfo itself.
+ */
+void
+pg_freeaddrinfo_all(int hint_ai_family, struct addrinfo * ai)
+{
+#ifdef HAVE_UNIX_SOCKETS
+ if (hint_ai_family == AF_UNIX)
+ {
+ /* struct was built by getaddrinfo_unix (see pg_getaddrinfo_all) */
+ while (ai != NULL)
+ {
+ struct addrinfo *p = ai;
+
+ ai = ai->ai_next;
+ free(p->ai_addr);
+ free(p);
+ }
+ }
+ else
+#endif /* HAVE_UNIX_SOCKETS */
+ {
+ /* struct was built by getaddrinfo() */
+ if (ai != NULL)
+ freeaddrinfo(ai);
+ }
+}
+
+
+/*
+ * pg_getnameinfo_all - get name info for Unix, IPv4 and IPv6 sockets
+ *
+ * The API of this routine differs from the standard getnameinfo() definition
+ * in two ways: first, the addr parameter is declared as sockaddr_storage
+ * rather than struct sockaddr, and second, the node and service fields are
+ * guaranteed to be filled with something even on failure return.
+ */
+int
+pg_getnameinfo_all(const struct sockaddr_storage * addr, int salen,
+ char *node, int nodelen,
+ char *service, int servicelen,
+ int flags)
+{
+ int rc;
+
+#ifdef HAVE_UNIX_SOCKETS
+ if (addr && addr->ss_family == AF_UNIX)
+ rc = getnameinfo_unix((const struct sockaddr_un *) addr, salen,
+ node, nodelen,
+ service, servicelen,
+ flags);
+ else
+#endif
+ rc = getnameinfo((const struct sockaddr *) addr, salen,
+ node, nodelen,
+ service, servicelen,
+ flags);
+
+ if (rc != 0)
+ {
+ if (node)
+ strlcpy(node, "???", nodelen);
+ if (service)
+ strlcpy(service, "???", servicelen);
+ }
+
+ return rc;
+}
+
+
+#if defined(HAVE_UNIX_SOCKETS)
+
+/* -------
+ * getaddrinfo_unix - get unix socket info using IPv6-compatible API
+ *
+ * Bugs: only one addrinfo is set even though hintsp is NULL or
+ * ai_socktype is 0
+ * AI_CANONNAME is not supported.
+ * -------
+ */
+static int
+getaddrinfo_unix(const char *path, const struct addrinfo * hintsp,
+ struct addrinfo ** result)
+{
+ struct addrinfo hints;
+ struct addrinfo *aip;
+ struct sockaddr_un *unp;
+
+ *result = NULL;
+
+ MemSet(&hints, 0, sizeof(hints));
+
+ if (strlen(path) >= sizeof(unp->sun_path))
+ return EAI_FAIL;
+
+ if (hintsp == NULL)
+ {
+ hints.ai_family = AF_UNIX;
+ hints.ai_socktype = SOCK_STREAM;
+ }
+ else
+ memcpy(&hints, hintsp, sizeof(hints));
+
+ if (hints.ai_socktype == 0)
+ hints.ai_socktype = SOCK_STREAM;
+
+ if (hints.ai_family != AF_UNIX)
+ {
+ /* shouldn't have been called */
+ return EAI_FAIL;
+ }
+
+ aip = calloc(1, sizeof(struct addrinfo));
+ if (aip == NULL)
+ return EAI_MEMORY;
+
+ unp = calloc(1, sizeof(struct sockaddr_un));
+ if (unp == NULL)
+ {
+ free(aip);
+ return EAI_MEMORY;
+ }
+
+ aip->ai_family = AF_UNIX;
+ aip->ai_socktype = hints.ai_socktype;
+ aip->ai_protocol = hints.ai_protocol;
+ aip->ai_next = NULL;
+ aip->ai_canonname = NULL;
+ *result = aip;
+
+ unp->sun_family = AF_UNIX;
+ aip->ai_addr = (struct sockaddr *) unp;
+ aip->ai_addrlen = sizeof(struct sockaddr_un);
+
+ strcpy(unp->sun_path, path);
+
+#ifdef HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN
+ unp->sun_len = sizeof(struct sockaddr_un);
+#endif
+
+ return 0;
+}
+
+/*
+ * Convert an address to a hostname.
+ */
+static int
+getnameinfo_unix(const struct sockaddr_un * sa, int salen,
+ char *node, int nodelen,
+ char *service, int servicelen,
+ int flags)
+{
+ int ret = -1;
+
+ /* Invalid arguments. */
+ if (sa == NULL || sa->sun_family != AF_UNIX ||
+ (node == NULL && service == NULL))
+ return EAI_FAIL;
+
+ if (node)
+ {
+ ret = snprintf(node, nodelen, "%s", "[local]");
+ if (ret == -1 || ret > nodelen)
+ return EAI_MEMORY;
+ }
+
+ if (service)
+ {
+ ret = snprintf(service, servicelen, "%s", sa->sun_path);
+ if (ret == -1 || ret > servicelen)
+ return EAI_MEMORY;
+ }
+
+ return 0;
+}
+#endif /* HAVE_UNIX_SOCKETS */
+
+
+/*
+ * pg_range_sockaddr - is addr within the subnet specified by netaddr/netmask ?
+ *
+ * Note: caller must already have verified that all three addresses are
+ * in the same address family; and AF_UNIX addresses are not supported.
+ */
+int
+pg_range_sockaddr(const struct sockaddr_storage * addr,
+ const struct sockaddr_storage * netaddr,
+ const struct sockaddr_storage * netmask)
+{
+ if (addr->ss_family == AF_INET)
+ return range_sockaddr_AF_INET((const struct sockaddr_in *) addr,
+ (const struct sockaddr_in *) netaddr,
+ (const struct sockaddr_in *) netmask);
+#ifdef HAVE_IPV6
+ else if (addr->ss_family == AF_INET6)
+ return range_sockaddr_AF_INET6((const struct sockaddr_in6 *) addr,
+ (const struct sockaddr_in6 *) netaddr,
+ (const struct sockaddr_in6 *) netmask);
+#endif
+ else
+ return 0;
+}
+
+static int
+range_sockaddr_AF_INET(const struct sockaddr_in * addr,
+ const struct sockaddr_in * netaddr,
+ const struct sockaddr_in * netmask)
+{
+ if (((addr->sin_addr.s_addr ^ netaddr->sin_addr.s_addr) &
+ netmask->sin_addr.s_addr) == 0)
+ return 1;
+ else
+ return 0;
+}
+
+
+#ifdef HAVE_IPV6
+
+static int
+range_sockaddr_AF_INET6(const struct sockaddr_in6 * addr,
+ const struct sockaddr_in6 * netaddr,
+ const struct sockaddr_in6 * netmask)
+{
+ int i;
+
+ for (i = 0; i < 16; i++)
+ {
+ if (((addr->sin6_addr.s6_addr[i] ^ netaddr->sin6_addr.s6_addr[i]) &
+ netmask->sin6_addr.s6_addr[i]) != 0)
+ return 0;
+ }
+
+ return 1;
+}
+#endif /* HAVE_IPV6 */
+
+/*
+ * pg_sockaddr_cidr_mask - make a network mask of the appropriate family
+ * and required number of significant bits
+ *
+ * numbits can be null, in which case the mask is fully set.
+ *
+ * The resulting mask is placed in *mask, which had better be big enough.
+ *
+ * Return value is 0 if okay, -1 if not.
+ */
+int
+pg_sockaddr_cidr_mask(struct sockaddr_storage * mask, char *numbits, int family)
+{
+ long bits;
+ char *endptr;
+
+ if (numbits == NULL)
+ {
+ bits = (family == AF_INET) ? 32 : 128;
+ }
+ else
+ {
+ bits = strtol(numbits, &endptr, 10);
+ if (*numbits == '\0' || *endptr != '\0')
+ return -1;
+ }
+
+ switch (family)
+ {
+ case AF_INET:
+ {
+ struct sockaddr_in mask4;
+ long maskl;
+
+ if (bits < 0 || bits > 32)
+ return -1;
+ memset(&mask4, 0, sizeof(mask4));
+ /* avoid "x << 32", which is not portable */
+ if (bits > 0)
+ maskl = (0xffffffffUL << (32 - (int) bits))
+ & 0xffffffffUL;
+ else
+ maskl = 0;
+ mask4.sin_addr.s_addr = htonl(maskl);
+ memcpy(mask, &mask4, sizeof(mask4));
+ break;
+ }
+
+#ifdef HAVE_IPV6
+ case AF_INET6:
+ {
+ struct sockaddr_in6 mask6;
+ int i;
+
+ if (bits < 0 || bits > 128)
+ return -1;
+ memset(&mask6, 0, sizeof(mask6));
+ for (i = 0; i < 16; i++)
+ {
+ if (bits <= 0)
+ mask6.sin6_addr.s6_addr[i] = 0;
+ else if (bits >= 8)
+ mask6.sin6_addr.s6_addr[i] = 0xff;
+ else
+ {
+ mask6.sin6_addr.s6_addr[i] =
+ (0xff << (8 - (int) bits)) & 0xff;
+ }
+ bits -= 8;
+ }
+ memcpy(mask, &mask6, sizeof(mask6));
+ break;
+ }
+#endif
+ default:
+ return -1;
+ }
+
+ mask->ss_family = family;
+ return 0;
+}
+
+
+/*
+ * Run the callback function for the addr/mask, after making sure the
+ * mask is sane for the addr.
+ */
+static void
+run_ifaddr_callback(PgIfAddrCallback callback, void *cb_data,
+ struct sockaddr * addr, struct sockaddr * mask)
+{
+ struct sockaddr_storage fullmask;
+
+ if (!addr)
+ return;
+
+ /* Check that the mask is valid */
+ if (mask)
+ {
+ if (mask->sa_family != addr->sa_family)
+ {
+ mask = NULL;
+ }
+ else if (mask->sa_family == AF_INET)
+ {
+ if (((struct sockaddr_in *) mask)->sin_addr.s_addr == INADDR_ANY)
+ mask = NULL;
+ }
+#ifdef HAVE_IPV6
+ else if (mask->sa_family == AF_INET6)
+ {
+ if (IN6_IS_ADDR_UNSPECIFIED(&((struct sockaddr_in6 *) mask)->sin6_addr))
+ mask = NULL;
+ }
+#endif
+ }
+
+ /* If mask is invalid, generate our own fully-set mask */
+ if (!mask)
+ {
+ pg_sockaddr_cidr_mask(&fullmask, NULL, addr->sa_family);
+ mask = (struct sockaddr *) & fullmask;
+ }
+
+ (*callback) (addr, mask, cb_data);
+}
+
+#ifdef WIN32
+
+#include <winsock2.h>
+#include <ws2tcpip.h>
+
+/*
+ * Enumerate the system's network interface addresses and call the callback
+ * for each one. Returns 0 if successful, -1 if trouble.
+ *
+ * This version is for Win32. Uses the Winsock 2 functions (ie: ws2_32.dll)
+ */
+int
+pg_foreach_ifaddr(PgIfAddrCallback callback, void *cb_data)
+{
+ INTERFACE_INFO *ptr,
+ *ii = NULL;
+ unsigned long length,
+ i;
+ unsigned long n_ii = 0;
+ SOCKET sock;
+ int error;
+