From 2a969b7f4bdb223d3626dc14b684701942ccafb2 Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Thu, 21 Sep 2017 01:02:04 +0300 Subject: Make package to be source rather than stub --- .gitignore | 19 + COPYRIGHT | 23 + INSTALL | 7 + README | 5 +- README-DEV | 102 + TODO | 14 + build/bootstrap.build | 37 +- build/export.build | 10 + build/root.build | 10 + buildfile | 17 +- libpq/.gitignore | 3 + libpq/buildfile | 190 + libpq/chklocale.c | 445 ++ libpq/encnames.c | 553 +++ libpq/fe-auth.c | 841 ++++ libpq/fe-auth.h | 24 + libpq/fe-connect.c | 6002 ++++++++++++++++++++++++++ libpq/fe-exec.c | 3764 ++++++++++++++++ libpq/fe-lobj.c | 1103 +++++ libpq/fe-misc.c | 1267 ++++++ libpq/fe-print.c | 761 ++++ libpq/fe-protocol2.c | 1623 +++++++ libpq/fe-protocol3.c | 2204 ++++++++++ libpq/fe-secure.c | 505 +++ libpq/getpeereid.c | 80 + libpq/inet_net_ntop.c | 298 ++ libpq/ip.c | 819 ++++ libpq/libpq-events.c | 209 + libpq/libpq-events.h | 94 + libpq/libpq-fe.h | 607 +++ libpq/libpq-int.h | 675 +++ libpq/md5.c | 345 ++ libpq/noblock.c | 66 + libpq/non-bsd/strlcpy.c | 71 + libpq/pg_service.conf.sample | 17 + libpq/pgstrcasecmp.c | 151 + libpq/postgresql/c.h | 1107 +++++ libpq/postgresql/common/fe_memutils.h | 44 + libpq/postgresql/getaddrinfo.h | 164 + libpq/postgresql/libpq/ip.h | 51 + libpq/postgresql/libpq/libpq-fs.h | 24 + libpq/postgresql/libpq/md5.h | 30 + libpq/postgresql/libpq/pqcomm.h | 206 + libpq/postgresql/mb/pg_wchar.h | 561 +++ libpq/postgresql/pg_config.h | 280 ++ libpq/postgresql/pg_config.h.in.orig | 931 ++++ libpq/postgresql/pg_config.h.win32.orig | 682 +++ libpq/postgresql/pg_config_ext.h | 20 + libpq/postgresql/pg_config_ext.h.in.orig | 7 + libpq/postgresql/pg_config_ext.h.win32.orig | 7 + libpq/postgresql/pg_config_manual.h | 327 ++ libpq/postgresql/pg_config_paths.h | 1 + libpq/postgresql/port.h | 477 ++ libpq/postgresql/port/bsd/pg_config_os.h | 0 libpq/postgresql/port/darwin/pg_config_os.h | 8 + libpq/postgresql/port/linux/pg_config_os.h | 22 + libpq/postgresql/port/win32/arpa/inet.h | 3 + libpq/postgresql/port/win32/netdb.h | 1 + libpq/postgresql/port/win32/netinet/in.h | 3 + libpq/postgresql/port/win32/pg_config_os.h | 486 +++ libpq/postgresql/port/win32/pthread-win32.h | 22 + libpq/postgresql/port/win32/pwd.h | 3 + libpq/postgresql/port/win32/sys/socket.h | 33 + libpq/postgresql/port/win32/sys/wait.h | 3 + libpq/postgresql/port/win32_msvc/sys/file.h | 1 + libpq/postgresql/port/win32_msvc/sys/param.h | 1 + libpq/postgresql/port/win32_msvc/sys/time.h | 1 + libpq/postgresql/port/win32_msvc/unistd.h | 1 + libpq/postgresql/postgres_ext.h | 70 + libpq/postgresql/postgres_fe.h | 29 + libpq/pqexpbuffer.c | 430 ++ libpq/pqexpbuffer.h | 182 + libpq/pqsignal.c | 90 + libpq/thread.c | 146 + libpq/version.h.in | 26 + libpq/wchar.c | 2054 +++++++++ libpq/win32/crypt.c | 1085 +++++ libpq/win32/getaddrinfo.c | 412 ++ libpq/win32/inet_aton.c | 147 + libpq/win32/libpq.rc.in | 31 + libpq/win32/libpqdll.def | 174 + libpq/win32/libpqdll.def.orig | 174 + libpq/win32/open.c | 167 + libpq/win32/pgsleep.c | 63 + libpq/win32/pthread-win32.c | 61 + libpq/win32/pthread-win32.h | 22 + libpq/win32/snprintf.c | 1141 +++++ libpq/win32/system.c | 119 + libpq/win32/win32.c | 327 ++ libpq/win32/win32.h | 40 + libpq/win32/win32error.c | 206 + libpq/win32/win32setlocale.c | 189 + manifest | 12 +- tests/.gitignore | 3 + tests/build/.gitignore | 1 + tests/build/bootstrap.build | 9 + tests/build/root.build | 21 + tests/buildfile | 5 + tests/conninfo/buildfile | 13 + tests/conninfo/driver.c | 74 + tests/conninfo/expected.out | 171 + tests/conninfo/postgres_fe.h | 19 + tests/conninfo/regress.in | 57 + tests/conninfo/testscript | 5 + tests/conninfo/uri-regress.c | 84 + 105 files changed, 36318 insertions(+), 9 deletions(-) create mode 100644 .gitignore create mode 100644 COPYRIGHT create mode 100644 INSTALL create mode 100644 README-DEV create mode 100644 TODO create mode 100644 build/export.build create mode 100644 build/root.build create mode 100644 libpq/.gitignore create mode 100644 libpq/buildfile create mode 100644 libpq/chklocale.c create mode 100644 libpq/encnames.c create mode 100644 libpq/fe-auth.c create mode 100644 libpq/fe-auth.h create mode 100644 libpq/fe-connect.c create mode 100644 libpq/fe-exec.c create mode 100644 libpq/fe-lobj.c create mode 100644 libpq/fe-misc.c create mode 100644 libpq/fe-print.c create mode 100644 libpq/fe-protocol2.c create mode 100644 libpq/fe-protocol3.c create mode 100644 libpq/fe-secure.c create mode 100644 libpq/getpeereid.c create mode 100644 libpq/inet_net_ntop.c create mode 100644 libpq/ip.c create mode 100644 libpq/libpq-events.c create mode 100644 libpq/libpq-events.h create mode 100644 libpq/libpq-fe.h create mode 100644 libpq/libpq-int.h create mode 100644 libpq/md5.c create mode 100644 libpq/noblock.c create mode 100644 libpq/non-bsd/strlcpy.c create mode 100644 libpq/pg_service.conf.sample create mode 100644 libpq/pgstrcasecmp.c create mode 100644 libpq/postgresql/c.h create mode 100644 libpq/postgresql/common/fe_memutils.h create mode 100644 libpq/postgresql/getaddrinfo.h create mode 100644 libpq/postgresql/libpq/ip.h create mode 100644 libpq/postgresql/libpq/libpq-fs.h create mode 100644 libpq/postgresql/libpq/md5.h create mode 100644 libpq/postgresql/libpq/pqcomm.h create mode 100644 libpq/postgresql/mb/pg_wchar.h create mode 100644 libpq/postgresql/pg_config.h create mode 100644 libpq/postgresql/pg_config.h.in.orig create mode 100644 libpq/postgresql/pg_config.h.win32.orig create mode 100644 libpq/postgresql/pg_config_ext.h create mode 100644 libpq/postgresql/pg_config_ext.h.in.orig create mode 100644 libpq/postgresql/pg_config_ext.h.win32.orig create mode 100644 libpq/postgresql/pg_config_manual.h create mode 100644 libpq/postgresql/pg_config_paths.h create mode 100644 libpq/postgresql/port.h create mode 100644 libpq/postgresql/port/bsd/pg_config_os.h create mode 100644 libpq/postgresql/port/darwin/pg_config_os.h create mode 100644 libpq/postgresql/port/linux/pg_config_os.h create mode 100644 libpq/postgresql/port/win32/arpa/inet.h create mode 100644 libpq/postgresql/port/win32/netdb.h create mode 100644 libpq/postgresql/port/win32/netinet/in.h create mode 100644 libpq/postgresql/port/win32/pg_config_os.h create mode 100644 libpq/postgresql/port/win32/pthread-win32.h create mode 100644 libpq/postgresql/port/win32/pwd.h create mode 100644 libpq/postgresql/port/win32/sys/socket.h create mode 100644 libpq/postgresql/port/win32/sys/wait.h create mode 100644 libpq/postgresql/port/win32_msvc/sys/file.h create mode 100644 libpq/postgresql/port/win32_msvc/sys/param.h create mode 100644 libpq/postgresql/port/win32_msvc/sys/time.h create mode 100644 libpq/postgresql/port/win32_msvc/unistd.h create mode 100644 libpq/postgresql/postgres_ext.h create mode 100644 libpq/postgresql/postgres_fe.h create mode 100644 libpq/pqexpbuffer.c create mode 100644 libpq/pqexpbuffer.h create mode 100644 libpq/pqsignal.c create mode 100644 libpq/thread.c create mode 100644 libpq/version.h.in create mode 100644 libpq/wchar.c create mode 100644 libpq/win32/crypt.c create mode 100644 libpq/win32/getaddrinfo.c create mode 100644 libpq/win32/inet_aton.c create mode 100644 libpq/win32/libpq.rc.in create mode 100644 libpq/win32/libpqdll.def create mode 100644 libpq/win32/libpqdll.def.orig create mode 100644 libpq/win32/open.c create mode 100644 libpq/win32/pgsleep.c create mode 100644 libpq/win32/pthread-win32.c create mode 100644 libpq/win32/pthread-win32.h create mode 100644 libpq/win32/snprintf.c create mode 100644 libpq/win32/system.c create mode 100644 libpq/win32/win32.c create mode 100644 libpq/win32/win32.h create mode 100644 libpq/win32/win32error.c create mode 100644 libpq/win32/win32setlocale.c create mode 100644 tests/.gitignore create mode 100644 tests/build/.gitignore create mode 100644 tests/build/bootstrap.build create mode 100644 tests/build/root.build create mode 100644 tests/buildfile create mode 100644 tests/conninfo/buildfile create mode 100644 tests/conninfo/driver.c create mode 100644 tests/conninfo/expected.out create mode 100644 tests/conninfo/postgres_fe.h create mode 100644 tests/conninfo/regress.in create mode 100644 tests/conninfo/testscript create mode 100644 tests/conninfo/uri-regress.c 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 +/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.. 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 . 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 +#endif + +#include +#ifdef HAVE_LANGINFO_H +#include +#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 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 _. 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 _. . 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 ."))); + + 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 ."))); +#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 +#include + +#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 +#include +#include /* for MAXHOSTNAMELEN on most */ +#include +#ifdef HAVE_SYS_UCRED_H +#include +#endif +#ifndef MAXHOSTNAMELEN +#include /* for MAXHOSTNAMELEN on some */ +#endif +#include +#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 +#include +#include +#include +#include +#include + +#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 +#ifdef WIN32_ONLY_COMPILER /* mstcpip.h is missing on mingw */ +#include +#endif +#else +#include +#include +#include +#ifdef HAVE_NETINET_TCP_H +#include +#endif +#include +#endif + +#ifdef ENABLE_THREAD_SAFETY +#ifdef WIN32 +#include "pthread-win32.h" +#else +#include +#endif +#endif + +#ifdef USE_LDAP +#ifdef WIN32 +#include +#else +/* OpenLDAP deprecates RFC 1823, but we want standard conformance */ +#define LDAP_DEPRECATED 1 +#include +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 +#include + +#include "libpq-fe.h" +#include "libpq-int.h" + +#include "mb/pg_wchar.h" + +#ifdef WIN32 +#include "win32.h" +#else +#include +#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 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 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 +#endif + +#include "postgres_fe.h" + +#ifdef WIN32 +#include "win32.h" +#else +#include +#endif + +#include +#include +#include +#include /* for ntohl/htonl */ +#include + +#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 +#include + +#include +#include + +#ifdef WIN32 +#include "win32.h" +#else +#include +#include +#endif + +#ifdef HAVE_POLL_H +#include +#endif +#ifdef HAVE_SYS_POLL_H +#include +#endif +#ifdef HAVE_SYS_SELECT_H +#include +#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 + +#ifdef WIN32 +#include "win32.h" +#else +#include +#include +#endif + +#ifdef HAVE_TERMIOS_H +#include +#else +#ifndef WIN32 +#include +#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, "

%s

\n", po->caption); + else + fprintf(fout, + "

" + "Query retrieved %d rows * %d fields" + "

\n", + nTups, nFields); + } + for (i = 0; i < nTups; i++) + { + if (po->expanded) + { + if (po->html3) + fprintf(fout, + "\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("
%d
\n", fout); + } + if (!po->expanded && (po->align || po->html3)) + { + if (po->html3) + { + if (po->header) + { + if (po->caption) + fprintf(fout, + "\n", + po->tableOpt ? po->tableOpt : "", + po->caption); + else + fprintf(fout, + "
%s
\n", + po->tableOpt ? po->tableOpt : "", nTups, nFields); + } + else + fprintf(fout, "
" + "Retrieved %d rows * %d fields" + "
", 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("
\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, + "%s" + "%s\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("", 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, "%s", + 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("\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("", 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, "%s", + 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("", 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 +#include + +#include "libpq-fe.h" +#include "libpq-int.h" + + +#ifdef WIN32 +#include "win32.h" +#else +#include +#include +#ifdef HAVE_NETINET_TCP_H +#include +#endif +#include +#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 +#include + +#include "libpq-fe.h" +#include "libpq-int.h" + +#include "mb/pg_wchar.h" + +#ifdef WIN32 +#include "win32.h" +#else +#include +#include +#ifdef HAVE_NETINET_TCP_H +#include +#endif +#include +#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 +#include +#include + +#include "libpq-fe.h" +#include "fe-auth.h" +#include "libpq-int.h" + +#ifdef WIN32 +#include "win32.h" +#else +#include +#include +#include +#include +#ifdef HAVE_NETINET_TCP_H +#include +#endif +#include +#endif + +#include + +#ifdef ENABLE_THREAD_SAFETY +#ifdef WIN32 +#include "pthread-win32.h" +#else +#include +#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 +#include +#include +#ifdef HAVE_SYS_UN_H +#include +#endif +#ifdef HAVE_UCRED_H +#include +#endif +#ifdef HAVE_SYS_UCRED_H +#include +#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 +#include +#include +#include + +#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 , 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 +#include +#include +#include +#include +#include +#ifdef HAVE_NETINET_TCP_H +#include +#endif +#include +#include + +#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 +#include + +/* + * 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; + + sock = WSASocket(AF_INET, SOCK_DGRAM, 0, 0, 0, 0); + if (sock == INVALID_SOCKET) + return -1; + + while (n_ii < 1024) + { + n_ii += 64; + ptr = realloc(ii, sizeof(INTERFACE_INFO) * n_ii); + if (!ptr) + { + free(ii); + closesocket(sock); + errno = ENOMEM; + return -1; + } + + ii = ptr; + if (WSAIoctl(sock, SIO_GET_INTERFACE_LIST, 0, 0, + ii, n_ii * sizeof(INTERFACE_INFO), + &length, 0, 0) == SOCKET_ERROR) + { + error = WSAGetLastError(); + if (error == WSAEFAULT || error == WSAENOBUFS) + continue; /* need to make the buffer bigger */ + closesocket(sock); + free(ii); + return -1; + } + + break; + } + + for (i = 0; i < length / sizeof(INTERFACE_INFO); ++i) + run_ifaddr_callback(callback, cb_data, + (struct sockaddr *) & ii[i].iiAddress, + (struct sockaddr *) & ii[i].iiNetmask); + + closesocket(sock); + free(ii); + return 0; +} +#elif HAVE_GETIFADDRS /* && !WIN32 */ + +#ifdef HAVE_IFADDRS_H +#include +#endif + +/* + * Enumerate the system's network interface addresses and call the callback + * for each one. Returns 0 if successful, -1 if trouble. + * + * This version uses the getifaddrs() interface, which is available on + * BSDs, AIX, and modern Linux. + */ +int +pg_foreach_ifaddr(PgIfAddrCallback callback, void *cb_data) +{ + struct ifaddrs *ifa, + *l; + + if (getifaddrs(&ifa) < 0) + return -1; + + for (l = ifa; l; l = l->ifa_next) + run_ifaddr_callback(callback, cb_data, + l->ifa_addr, l->ifa_netmask); + + freeifaddrs(ifa); + return 0; +} +#else /* !HAVE_GETIFADDRS && !WIN32 */ + +#ifdef HAVE_SYS_IOCTL_H +#include +#endif + +#ifdef HAVE_NET_IF_H +#include +#endif + +#ifdef HAVE_SYS_SOCKIO_H +#include +#endif + +/* + * SIOCGIFCONF does not return IPv6 addresses on Solaris + * and HP/UX. So we prefer SIOCGLIFCONF if it's available. + * + * On HP/UX, however, it *only* returns IPv6 addresses, + * and the structs are named slightly differently too. + * We'd have to do another call with SIOCGIFCONF to get the + * IPv4 addresses as well. We don't currently bother, just + * fall back to SIOCGIFCONF on HP/UX. + */ + +#if defined(SIOCGLIFCONF) && !defined(__hpux) + +/* + * Enumerate the system's network interface addresses and call the callback + * for each one. Returns 0 if successful, -1 if trouble. + * + * This version uses ioctl(SIOCGLIFCONF). + */ +int +pg_foreach_ifaddr(PgIfAddrCallback callback, void *cb_data) +{ + struct lifconf lifc; + struct lifreq *lifr, + lmask; + struct sockaddr *addr, + *mask; + char *ptr, + *buffer = NULL; + size_t n_buffer = 1024; + pgsocket sock, + fd; + +#ifdef HAVE_IPV6 + pgsocket sock6; +#endif + int i, + total; + + sock = socket(AF_INET, SOCK_DGRAM, 0); + if (sock == PGINVALID_SOCKET) + return -1; + + while (n_buffer < 1024 * 100) + { + n_buffer += 1024; + ptr = realloc(buffer, n_buffer); + if (!ptr) + { + free(buffer); + close(sock); + errno = ENOMEM; + return -1; + } + + memset(&lifc, 0, sizeof(lifc)); + lifc.lifc_family = AF_UNSPEC; + lifc.lifc_buf = buffer = ptr; + lifc.lifc_len = n_buffer; + + if (ioctl(sock, SIOCGLIFCONF, &lifc) < 0) + { + if (errno == EINVAL) + continue; + free(buffer); + close(sock); + return -1; + } + + /* + * Some Unixes try to return as much data as possible, with no + * indication of whether enough space allocated. Don't believe we have + * it all unless there's lots of slop. + */ + if (lifc.lifc_len < n_buffer - 1024) + break; + } + +#ifdef HAVE_IPV6 + /* We'll need an IPv6 socket too for the SIOCGLIFNETMASK ioctls */ + sock6 = socket(AF_INET6, SOCK_DGRAM, 0); + if (sock6 == PGINVALID_SOCKET) + { + free(buffer); + close(sock); + return -1; + } +#endif + + total = lifc.lifc_len / sizeof(struct lifreq); + lifr = lifc.lifc_req; + for (i = 0; i < total; ++i) + { + addr = (struct sockaddr *) & lifr[i].lifr_addr; + memcpy(&lmask, &lifr[i], sizeof(struct lifreq)); +#ifdef HAVE_IPV6 + fd = (addr->sa_family == AF_INET6) ? sock6 : sock; +#else + fd = sock; +#endif + if (ioctl(fd, SIOCGLIFNETMASK, &lmask) < 0) + mask = NULL; + else + mask = (struct sockaddr *) & lmask.lifr_addr; + run_ifaddr_callback(callback, cb_data, addr, mask); + } + + free(buffer); + close(sock); +#ifdef HAVE_IPV6 + close(sock6); +#endif + return 0; +} +#elif defined(SIOCGIFCONF) + +/* + * Remaining Unixes use SIOCGIFCONF. Some only return IPv4 information + * here, so this is the least preferred method. Note that there is no + * standard way to iterate the struct ifreq returned in the array. + * On some OSs the structures are padded large enough for any address, + * on others you have to calculate the size of the struct ifreq. + */ + +/* Some OSs have _SIZEOF_ADDR_IFREQ, so just use that */ +#ifndef _SIZEOF_ADDR_IFREQ + +/* Calculate based on sockaddr.sa_len */ +#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN +#define _SIZEOF_ADDR_IFREQ(ifr) \ + ((ifr).ifr_addr.sa_len > sizeof(struct sockaddr) ? \ + (sizeof(struct ifreq) - sizeof(struct sockaddr) + \ + (ifr).ifr_addr.sa_len) : sizeof(struct ifreq)) + +/* Padded ifreq structure, simple */ +#else +#define _SIZEOF_ADDR_IFREQ(ifr) \ + sizeof (struct ifreq) +#endif +#endif /* !_SIZEOF_ADDR_IFREQ */ + +/* + * Enumerate the system's network interface addresses and call the callback + * for each one. Returns 0 if successful, -1 if trouble. + * + * This version uses ioctl(SIOCGIFCONF). + */ +int +pg_foreach_ifaddr(PgIfAddrCallback callback, void *cb_data) +{ + struct ifconf ifc; + struct ifreq *ifr, + *end, + addr, + mask; + char *ptr, + *buffer = NULL; + size_t n_buffer = 1024; + pgsocket sock; + + sock = socket(AF_INET, SOCK_DGRAM, 0); + if (sock == PGINVALID_SOCKET) + return -1; + + while (n_buffer < 1024 * 100) + { + n_buffer += 1024; + ptr = realloc(buffer, n_buffer); + if (!ptr) + { + free(buffer); + close(sock); + errno = ENOMEM; + return -1; + } + + memset(&ifc, 0, sizeof(ifc)); + ifc.ifc_buf = buffer = ptr; + ifc.ifc_len = n_buffer; + + if (ioctl(sock, SIOCGIFCONF, &ifc) < 0) + { + if (errno == EINVAL) + continue; + free(buffer); + close(sock); + return -1; + } + + /* + * Some Unixes try to return as much data as possible, with no + * indication of whether enough space allocated. Don't believe we have + * it all unless there's lots of slop. + */ + if (ifc.ifc_len < n_buffer - 1024) + break; + } + + end = (struct ifreq *) (buffer + ifc.ifc_len); + for (ifr = ifc.ifc_req; ifr < end;) + { + memcpy(&addr, ifr, sizeof(addr)); + memcpy(&mask, ifr, sizeof(mask)); + if (ioctl(sock, SIOCGIFADDR, &addr, sizeof(addr)) == 0 && + ioctl(sock, SIOCGIFNETMASK, &mask, sizeof(mask)) == 0) + run_ifaddr_callback(callback, cb_data, + &addr.ifr_addr, &mask.ifr_addr); + ifr = (struct ifreq *) ((char *) ifr + _SIZEOF_ADDR_IFREQ(*ifr)); + } + + free(buffer); + close(sock); + return 0; +} +#else /* !defined(SIOCGIFCONF) */ + +/* + * Enumerate the system's network interface addresses and call the callback + * for each one. Returns 0 if successful, -1 if trouble. + * + * This version is our fallback if there's no known way to get the + * interface addresses. Just return the standard loopback addresses. + */ +int +pg_foreach_ifaddr(PgIfAddrCallback callback, void *cb_data) +{ + struct sockaddr_in addr; + struct sockaddr_storage mask; + +#ifdef HAVE_IPV6 + struct sockaddr_in6 addr6; +#endif + + /* addr 127.0.0.1/8 */ + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = ntohl(0x7f000001); + memset(&mask, 0, sizeof(mask)); + pg_sockaddr_cidr_mask(&mask, "8", AF_INET); + run_ifaddr_callback(callback, cb_data, + (struct sockaddr *) & addr, + (struct sockaddr *) & mask); + +#ifdef HAVE_IPV6 + /* addr ::1/128 */ + memset(&addr6, 0, sizeof(addr6)); + addr6.sin6_family = AF_INET6; + addr6.sin6_addr.s6_addr[15] = 1; + memset(&mask, 0, sizeof(mask)); + pg_sockaddr_cidr_mask(&mask, "128", AF_INET6); + run_ifaddr_callback(callback, cb_data, + (struct sockaddr *) & addr6, + (struct sockaddr *) & mask); +#endif + + return 0; +} +#endif /* !defined(SIOCGIFCONF) */ + +#endif /* !HAVE_GETIFADDRS */ diff --git a/libpq/libpq-events.c b/libpq/libpq-events.c new file mode 100644 index 0000000..6db3e94 --- /dev/null +++ b/libpq/libpq-events.c @@ -0,0 +1,209 @@ +/*------------------------------------------------------------------------- + * + * libpq-events.c + * functions for supporting the libpq "events" API + * + * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/interfaces/libpq/libpq-events.c + * + *------------------------------------------------------------------------- + */ +#include "postgres_fe.h" + +#include "libpq-fe.h" +#include "libpq-int.h" + + +/* + * Registers an event proc with the given PGconn. + * + * The same proc can't be registered more than once in a PGconn. This + * restriction is required because we use the proc address to identify + * the event for purposes such as PQinstanceData(). + * + * The name argument is used within error messages to aid in debugging. + * A name must be supplied, but it needn't be unique. The string is + * copied, so the passed value needn't be long-lived. + * + * The passThrough argument is an application specific pointer and can be set + * to NULL if not required. It is passed through to the event proc whenever + * the event proc is called, and is not otherwise touched by libpq. + * + * The function returns a non-zero if successful. If the function fails, + * zero is returned. + */ +int +PQregisterEventProc(PGconn *conn, PGEventProc proc, + const char *name, void *passThrough) +{ + int i; + PGEventRegister regevt; + + if (!proc || !conn || !name || !*name) + return FALSE; /* bad arguments */ + + for (i = 0; i < conn->nEvents; i++) + { + if (conn->events[i].proc == proc) + return FALSE; /* already registered */ + } + + if (conn->nEvents >= conn->eventArraySize) + { + PGEvent *e; + int newSize; + + newSize = conn->eventArraySize ? conn->eventArraySize * 2 : 8; + if (conn->events) + e = (PGEvent *) realloc(conn->events, newSize * sizeof(PGEvent)); + else + e = (PGEvent *) malloc(newSize * sizeof(PGEvent)); + + if (!e) + return FALSE; + + conn->eventArraySize = newSize; + conn->events = e; + } + + conn->events[conn->nEvents].proc = proc; + conn->events[conn->nEvents].name = strdup(name); + if (!conn->events[conn->nEvents].name) + return FALSE; + conn->events[conn->nEvents].passThrough = passThrough; + conn->events[conn->nEvents].data = NULL; + conn->events[conn->nEvents].resultInitialized = FALSE; + conn->nEvents++; + + regevt.conn = conn; + if (!proc(PGEVT_REGISTER, ®evt, passThrough)) + { + conn->nEvents--; + free(conn->events[conn->nEvents].name); + return FALSE; + } + + return TRUE; +} + +/* + * Set some "instance data" for an event within a PGconn. + * Returns nonzero on success, zero on failure. + */ +int +PQsetInstanceData(PGconn *conn, PGEventProc proc, void *data) +{ + int i; + + if (!conn || !proc) + return FALSE; + + for (i = 0; i < conn->nEvents; i++) + { + if (conn->events[i].proc == proc) + { + conn->events[i].data = data; + return TRUE; + } + } + + return FALSE; +} + +/* + * Obtain the "instance data", if any, for the event. + */ +void * +PQinstanceData(const PGconn *conn, PGEventProc proc) +{ + int i; + + if (!conn || !proc) + return NULL; + + for (i = 0; i < conn->nEvents; i++) + { + if (conn->events[i].proc == proc) + return conn->events[i].data; + } + + return NULL; +} + +/* + * Set some "instance data" for an event within a PGresult. + * Returns nonzero on success, zero on failure. + */ +int +PQresultSetInstanceData(PGresult *result, PGEventProc proc, void *data) +{ + int i; + + if (!result || !proc) + return FALSE; + + for (i = 0; i < result->nEvents; i++) + { + if (result->events[i].proc == proc) + { + result->events[i].data = data; + return TRUE; + } + } + + return FALSE; +} + +/* + * Obtain the "instance data", if any, for the event. + */ +void * +PQresultInstanceData(const PGresult *result, PGEventProc proc) +{ + int i; + + if (!result || !proc) + return NULL; + + for (i = 0; i < result->nEvents; i++) + if (result->events[i].proc == proc) + return result->events[i].data; + + return NULL; +} + +/* + * Fire RESULTCREATE events for an application-created PGresult. + * + * The conn argument can be NULL if event procedures won't use it. + */ +int +PQfireResultCreateEvents(PGconn *conn, PGresult *res) +{ + int i; + + if (!res) + return FALSE; + + for (i = 0; i < res->nEvents; i++) + { + if (!res->events[i].resultInitialized) + { + PGEventResultCreate evt; + + evt.conn = conn; + evt.result = res; + if (!res->events[i].proc(PGEVT_RESULTCREATE, &evt, + res->events[i].passThrough)) + return FALSE; + + res->events[i].resultInitialized = TRUE; + } + } + + return TRUE; +} diff --git a/libpq/libpq-events.h b/libpq/libpq-events.h new file mode 100644 index 0000000..e2bdb28 --- /dev/null +++ b/libpq/libpq-events.h @@ -0,0 +1,94 @@ +/*------------------------------------------------------------------------- + * + * libpq-events.h + * This file contains definitions that are useful to applications + * that invoke the libpq "events" API, but are not interesting to + * ordinary users of libpq. + * + * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/interfaces/libpq/libpq-events.h + * + *------------------------------------------------------------------------- + */ + +#ifndef LIBPQ_EVENTS_H +#define LIBPQ_EVENTS_H + +#include "libpq-fe.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* Callback Event Ids */ +typedef enum +{ + PGEVT_REGISTER, + PGEVT_CONNRESET, + PGEVT_CONNDESTROY, + PGEVT_RESULTCREATE, + PGEVT_RESULTCOPY, + PGEVT_RESULTDESTROY +} PGEventId; + +typedef struct +{ + PGconn *conn; +} PGEventRegister; + +typedef struct +{ + PGconn *conn; +} PGEventConnReset; + +typedef struct +{ + PGconn *conn; +} PGEventConnDestroy; + +typedef struct +{ + PGconn *conn; + PGresult *result; +} PGEventResultCreate; + +typedef struct +{ + const PGresult *src; + PGresult *dest; +} PGEventResultCopy; + +typedef struct +{ + PGresult *result; +} PGEventResultDestroy; + +typedef int (*PGEventProc) (PGEventId evtId, void *evtInfo, void *passThrough); + +/* Registers an event proc with the given PGconn. */ +extern int PQregisterEventProc(PGconn *conn, PGEventProc proc, + const char *name, void *passThrough); + +/* Sets the PGconn instance data for the provided proc to data. */ +extern int PQsetInstanceData(PGconn *conn, PGEventProc proc, void *data); + +/* Gets the PGconn instance data for the provided proc. */ +extern void *PQinstanceData(const PGconn *conn, PGEventProc proc); + +/* Sets the PGresult instance data for the provided proc to data. */ +extern int PQresultSetInstanceData(PGresult *result, PGEventProc proc, void *data); + +/* Gets the PGresult instance data for the provided proc. */ +extern void *PQresultInstanceData(const PGresult *result, PGEventProc proc); + +/* Fires RESULTCREATE events for an application-created PGresult. */ +extern int PQfireResultCreateEvents(PGconn *conn, PGresult *res); + +#ifdef __cplusplus +} +#endif + +#endif /* LIBPQ_EVENTS_H */ diff --git a/libpq/libpq-fe.h b/libpq/libpq-fe.h new file mode 100644 index 0000000..9ca0756 --- /dev/null +++ b/libpq/libpq-fe.h @@ -0,0 +1,607 @@ +/*------------------------------------------------------------------------- + * + * libpq-fe.h + * This file contains definitions for structures and + * externs for functions used by frontend postgres applications. + * + * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/interfaces/libpq/libpq-fe.h + * + *------------------------------------------------------------------------- + */ + +#ifndef LIBPQ_FE_H +#define LIBPQ_FE_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include + +/* + * postgres_ext.h defines the backend's externally visible types, + * such as Oid. + */ +#include "postgres_ext.h" + +/* + * Option flags for PQcopyResult + */ +#define PG_COPYRES_ATTRS 0x01 +#define PG_COPYRES_TUPLES 0x02 /* Implies PG_COPYRES_ATTRS */ +#define PG_COPYRES_EVENTS 0x04 +#define PG_COPYRES_NOTICEHOOKS 0x08 + +/* Application-visible enum types */ + +/* + * Although it is okay to add to these lists, values which become unused + * should never be removed, nor should constants be redefined - that would + * break compatibility with existing code. + */ + +typedef enum +{ + CONNECTION_OK, + CONNECTION_BAD, + /* Non-blocking mode only below here */ + + /* + * The existence of these should never be relied upon - they should only + * be used for user feedback or similar purposes. + */ + CONNECTION_STARTED, /* Waiting for connection to be made. */ + CONNECTION_MADE, /* Connection OK; waiting to send. */ + CONNECTION_AWAITING_RESPONSE, /* Waiting for a response from the + * postmaster. */ + CONNECTION_AUTH_OK, /* Received authentication; waiting for + * backend startup. */ + CONNECTION_SETENV, /* Negotiating environment. */ + CONNECTION_SSL_STARTUP, /* Negotiating SSL. */ + CONNECTION_NEEDED /* Internal state: connect() needed */ +} ConnStatusType; + +typedef enum +{ + PGRES_POLLING_FAILED = 0, + PGRES_POLLING_READING, /* These two indicate that one may */ + PGRES_POLLING_WRITING, /* use select before polling again. */ + PGRES_POLLING_OK, + PGRES_POLLING_ACTIVE /* unused; keep for awhile for backwards + * compatibility */ +} PostgresPollingStatusType; + +typedef enum +{ + PGRES_EMPTY_QUERY = 0, /* empty query string was executed */ + PGRES_COMMAND_OK, /* a query command that doesn't return + * anything was executed properly by the + * backend */ + PGRES_TUPLES_OK, /* a query command that returns tuples was + * executed properly by the backend, PGresult + * contains the result tuples */ + PGRES_COPY_OUT, /* Copy Out data transfer in progress */ + PGRES_COPY_IN, /* Copy In data transfer in progress */ + PGRES_BAD_RESPONSE, /* an unexpected response was recv'd from the + * backend */ + PGRES_NONFATAL_ERROR, /* notice or warning message */ + PGRES_FATAL_ERROR, /* query failed */ + PGRES_COPY_BOTH, /* Copy In/Out data transfer in progress */ + PGRES_SINGLE_TUPLE /* single tuple from larger resultset */ +} ExecStatusType; + +typedef enum +{ + PQTRANS_IDLE, /* connection idle */ + PQTRANS_ACTIVE, /* command in progress */ + PQTRANS_INTRANS, /* idle, within transaction block */ + PQTRANS_INERROR, /* idle, within failed transaction */ + PQTRANS_UNKNOWN /* cannot determine status */ +} PGTransactionStatusType; + +typedef enum +{ + PQERRORS_TERSE, /* single-line error messages */ + PQERRORS_DEFAULT, /* recommended style */ + PQERRORS_VERBOSE /* all the facts, ma'am */ +} PGVerbosity; + +typedef enum +{ + PQSHOW_CONTEXT_NEVER, /* never show CONTEXT field */ + PQSHOW_CONTEXT_ERRORS, /* show CONTEXT for errors only (default) */ + PQSHOW_CONTEXT_ALWAYS /* always show CONTEXT field */ +} PGContextVisibility; + +/* + * PGPing - The ordering of this enum should not be altered because the + * values are exposed externally via pg_isready. + */ + +typedef enum +{ + PQPING_OK, /* server is accepting connections */ + PQPING_REJECT, /* server is alive but rejecting connections */ + PQPING_NO_RESPONSE, /* could not establish connection */ + PQPING_NO_ATTEMPT /* connection not attempted (bad params) */ +} PGPing; + +/* PGconn encapsulates a connection to the backend. + * The contents of this struct are not supposed to be known to applications. + */ +typedef struct pg_conn PGconn; + +/* PGresult encapsulates the result of a query (or more precisely, of a single + * SQL command --- a query string given to PQsendQuery can contain multiple + * commands and thus return multiple PGresult objects). + * The contents of this struct are not supposed to be known to applications. + */ +typedef struct pg_result PGresult; + +/* PGcancel encapsulates the information needed to cancel a running + * query on an existing connection. + * The contents of this struct are not supposed to be known to applications. + */ +typedef struct pg_cancel PGcancel; + +/* PGnotify represents the occurrence of a NOTIFY message. + * Ideally this would be an opaque typedef, but it's so simple that it's + * unlikely to change. + * NOTE: in Postgres 6.4 and later, the be_pid is the notifying backend's, + * whereas in earlier versions it was always your own backend's PID. + */ +typedef struct pgNotify +{ + char *relname; /* notification condition name */ + int be_pid; /* process ID of notifying server process */ + char *extra; /* notification parameter */ + /* Fields below here are private to libpq; apps should not use 'em */ + struct pgNotify *next; /* list link */ +} PGnotify; + +/* Function types for notice-handling callbacks */ +typedef void (*PQnoticeReceiver) (void *arg, const PGresult *res); +typedef void (*PQnoticeProcessor) (void *arg, const char *message); + +/* Print options for PQprint() */ +typedef char pqbool; + +typedef struct _PQprintOpt +{ + pqbool header; /* print output field headings and row count */ + pqbool align; /* fill align the fields */ + pqbool standard; /* old brain dead format */ + pqbool html3; /* output html tables */ + pqbool expanded; /* expand tables */ + pqbool pager; /* use pager for output if needed */ + char *fieldSep; /* field separator */ + char *tableOpt; /* insert to HTML */ + char *caption; /* HTML
*/ + char **fieldName; /* null terminated array of replacement field + * names */ +} PQprintOpt; + +/* ---------------- + * Structure for the conninfo parameter definitions returned by PQconndefaults + * or PQconninfoParse. + * + * All fields except "val" point at static strings which must not be altered. + * "val" is either NULL or a malloc'd current-value string. PQconninfoFree() + * will release both the val strings and the PQconninfoOption array itself. + * ---------------- + */ +typedef struct _PQconninfoOption +{ + 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 */ +} PQconninfoOption; + +/* ---------------- + * PQArgBlock -- structure for PQfn() arguments + * ---------------- + */ +typedef struct +{ + int len; + int isint; + union + { + int *ptr; /* can't use void (dec compiler barfs) */ + int integer; + } u; +} PQArgBlock; + +/* ---------------- + * PGresAttDesc -- Data about a single attribute (column) of a query result + * ---------------- + */ +typedef struct pgresAttDesc +{ + char *name; /* column name */ + Oid tableid; /* source table, if known */ + int columnid; /* source column, if known */ + int format; /* format code for value (text/binary) */ + Oid typid; /* type id */ + int typlen; /* type size */ + int atttypmod; /* type-specific modifier info */ +} PGresAttDesc; + +/* ---------------- + * Exported functions of libpq + * ---------------- + */ + +/* === in fe-connect.c === */ + +/* make a new client connection to the backend */ +/* Asynchronous (non-blocking) */ +extern PGconn *PQconnectStart(const char *conninfo); +extern PGconn *PQconnectStartParams(const char *const * keywords, + const char *const * values, int expand_dbname); +extern PostgresPollingStatusType PQconnectPoll(PGconn *conn); + +/* Synchronous (blocking) */ +extern PGconn *PQconnectdb(const char *conninfo); +extern PGconn *PQconnectdbParams(const char *const * keywords, + const char *const * values, int expand_dbname); +extern PGconn *PQsetdbLogin(const char *pghost, const char *pgport, + const char *pgoptions, const char *pgtty, + const char *dbName, + const char *login, const char *pwd); + +#define PQsetdb(M_PGHOST,M_PGPORT,M_PGOPT,M_PGTTY,M_DBNAME) \ + PQsetdbLogin(M_PGHOST, M_PGPORT, M_PGOPT, M_PGTTY, M_DBNAME, NULL, NULL) + +/* close the current connection and free the PGconn data structure */ +extern void PQfinish(PGconn *conn); + +/* get info about connection options known to PQconnectdb */ +extern PQconninfoOption *PQconndefaults(void); + +/* parse connection options in same way as PQconnectdb */ +extern PQconninfoOption *PQconninfoParse(const char *conninfo, char **errmsg); + +/* return the connection options used by a live connection */ +extern PQconninfoOption *PQconninfo(PGconn *conn); + +/* free the data structure returned by PQconndefaults() or PQconninfoParse() */ +extern void PQconninfoFree(PQconninfoOption *connOptions); + +/* + * close the current connection and restablish a new one with the same + * parameters + */ +/* Asynchronous (non-blocking) */ +extern int PQresetStart(PGconn *conn); +extern PostgresPollingStatusType PQresetPoll(PGconn *conn); + +/* Synchronous (blocking) */ +extern void PQreset(PGconn *conn); + +/* request a cancel structure */ +extern PGcancel *PQgetCancel(PGconn *conn); + +/* free a cancel structure */ +extern void PQfreeCancel(PGcancel *cancel); + +/* issue a cancel request */ +extern int PQcancel(PGcancel *cancel, char *errbuf, int errbufsize); + +/* backwards compatible version of PQcancel; not thread-safe */ +extern int PQrequestCancel(PGconn *conn); + +/* Accessor functions for PGconn objects */ +extern char *PQdb(const PGconn *conn); +extern char *PQuser(const PGconn *conn); +extern char *PQpass(const PGconn *conn); +extern char *PQhost(const PGconn *conn); +extern char *PQport(const PGconn *conn); +extern char *PQtty(const PGconn *conn); +extern char *PQoptions(const PGconn *conn); +extern ConnStatusType PQstatus(const PGconn *conn); +extern PGTransactionStatusType PQtransactionStatus(const PGconn *conn); +extern const char *PQparameterStatus(const PGconn *conn, + const char *paramName); +extern int PQprotocolVersion(const PGconn *conn); +extern int PQserverVersion(const PGconn *conn); +extern char *PQerrorMessage(const PGconn *conn); +extern int PQsocket(const PGconn *conn); +extern int PQbackendPID(const PGconn *conn); +extern int PQconnectionNeedsPassword(const PGconn *conn); +extern int PQconnectionUsedPassword(const PGconn *conn); +extern int PQclientEncoding(const PGconn *conn); +extern int PQsetClientEncoding(PGconn *conn, const char *encoding); + +/* SSL information functions */ +extern int PQsslInUse(PGconn *conn); +extern void *PQsslStruct(PGconn *conn, const char *struct_name); +extern const char *PQsslAttribute(PGconn *conn, const char *attribute_name); +extern const char *const * PQsslAttributeNames(PGconn *conn); + +/* Get the OpenSSL structure associated with a connection. Returns NULL for + * unencrypted connections or if any other TLS library is in use. */ +extern void *PQgetssl(PGconn *conn); + +/* Tell libpq whether it needs to initialize OpenSSL */ +extern void PQinitSSL(int do_init); + +/* More detailed way to tell libpq whether it needs to initialize OpenSSL */ +extern void PQinitOpenSSL(int do_ssl, int do_crypto); + +/* Set verbosity for PQerrorMessage and PQresultErrorMessage */ +extern PGVerbosity PQsetErrorVerbosity(PGconn *conn, PGVerbosity verbosity); + +/* Set CONTEXT visibility for PQerrorMessage and PQresultErrorMessage */ +extern PGContextVisibility PQsetErrorContextVisibility(PGconn *conn, + PGContextVisibility show_context); + +/* Enable/disable tracing */ +extern void PQtrace(PGconn *conn, FILE *debug_port); +extern void PQuntrace(PGconn *conn); + +/* Override default notice handling routines */ +extern PQnoticeReceiver PQsetNoticeReceiver(PGconn *conn, + PQnoticeReceiver proc, + void *arg); +extern PQnoticeProcessor PQsetNoticeProcessor(PGconn *conn, + PQnoticeProcessor proc, + void *arg); + +/* + * Used to set callback that prevents concurrent access to + * non-thread safe functions that libpq needs. + * The default implementation uses a libpq internal mutex. + * Only required for multithreaded apps that use kerberos + * both within their app and for postgresql connections. + */ +typedef void (*pgthreadlock_t) (int acquire); + +extern pgthreadlock_t PQregisterThreadLock(pgthreadlock_t newhandler); + +/* === in fe-exec.c === */ + +/* Simple synchronous query */ +extern PGresult *PQexec(PGconn *conn, const char *query); +extern PGresult *PQexecParams(PGconn *conn, + const char *command, + int nParams, + const Oid *paramTypes, + const char *const * paramValues, + const int *paramLengths, + const int *paramFormats, + int resultFormat); +extern PGresult *PQprepare(PGconn *conn, const char *stmtName, + const char *query, int nParams, + const Oid *paramTypes); +extern PGresult *PQexecPrepared(PGconn *conn, + const char *stmtName, + int nParams, + const char *const * paramValues, + const int *paramLengths, + const int *paramFormats, + int resultFormat); + +/* Interface for multiple-result or asynchronous queries */ +extern int PQsendQuery(PGconn *conn, const char *query); +extern int PQsendQueryParams(PGconn *conn, + const char *command, + int nParams, + const Oid *paramTypes, + const char *const * paramValues, + const int *paramLengths, + const int *paramFormats, + int resultFormat); +extern int PQsendPrepare(PGconn *conn, const char *stmtName, + const char *query, int nParams, + const Oid *paramTypes); +extern int PQsendQueryPrepared(PGconn *conn, + const char *stmtName, + int nParams, + const char *const * paramValues, + const int *paramLengths, + const int *paramFormats, + int resultFormat); +extern int PQsetSingleRowMode(PGconn *conn); +extern PGresult *PQgetResult(PGconn *conn); + +/* Routines for managing an asynchronous query */ +extern int PQisBusy(PGconn *conn); +extern int PQconsumeInput(PGconn *conn); + +/* LISTEN/NOTIFY support */ +extern PGnotify *PQnotifies(PGconn *conn); + +/* Routines for copy in/out */ +extern int PQputCopyData(PGconn *conn, const char *buffer, int nbytes); +extern int PQputCopyEnd(PGconn *conn, const char *errormsg); +extern int PQgetCopyData(PGconn *conn, char **buffer, int async); + +/* Deprecated routines for copy in/out */ +extern int PQgetline(PGconn *conn, char *string, int length); +extern int PQputline(PGconn *conn, const char *string); +extern int PQgetlineAsync(PGconn *conn, char *buffer, int bufsize); +extern int PQputnbytes(PGconn *conn, const char *buffer, int nbytes); +extern int PQendcopy(PGconn *conn); + +/* Set blocking/nonblocking connection to the backend */ +extern int PQsetnonblocking(PGconn *conn, int arg); +extern int PQisnonblocking(const PGconn *conn); +extern int PQisthreadsafe(void); +extern PGPing PQping(const char *conninfo); +extern PGPing PQpingParams(const char *const * keywords, + const char *const * values, int expand_dbname); + +/* Force the write buffer to be written (or at least try) */ +extern int PQflush(PGconn *conn); + +/* + * "Fast path" interface --- not really recommended for application + * use + */ +extern PGresult *PQfn(PGconn *conn, + int fnid, + int *result_buf, + int *result_len, + int result_is_int, + const PQArgBlock *args, + int nargs); + +/* Accessor functions for PGresult objects */ +extern ExecStatusType PQresultStatus(const PGresult *res); +extern char *PQresStatus(ExecStatusType status); +extern char *PQresultErrorMessage(const PGresult *res); +extern char *PQresultVerboseErrorMessage(const PGresult *res, + PGVerbosity verbosity, + PGContextVisibility show_context); +extern char *PQresultErrorField(const PGresult *res, int fieldcode); +extern int PQntuples(const PGresult *res); +extern int PQnfields(const PGresult *res); +extern int PQbinaryTuples(const PGresult *res); +extern char *PQfname(const PGresult *res, int field_num); +extern int PQfnumber(const PGresult *res, const char *field_name); +extern Oid PQftable(const PGresult *res, int field_num); +extern int PQftablecol(const PGresult *res, int field_num); +extern int PQfformat(const PGresult *res, int field_num); +extern Oid PQftype(const PGresult *res, int field_num); +extern int PQfsize(const PGresult *res, int field_num); +extern int PQfmod(const PGresult *res, int field_num); +extern char *PQcmdStatus(PGresult *res); +extern char *PQoidStatus(const PGresult *res); /* old and ugly */ +extern Oid PQoidValue(const PGresult *res); /* new and improved */ +extern char *PQcmdTuples(PGresult *res); +extern char *PQgetvalue(const PGresult *res, int tup_num, int field_num); +extern int PQgetlength(const PGresult *res, int tup_num, int field_num); +extern int PQgetisnull(const PGresult *res, int tup_num, int field_num); +extern int PQnparams(const PGresult *res); +extern Oid PQparamtype(const PGresult *res, int param_num); + +/* Describe prepared statements and portals */ +extern PGresult *PQdescribePrepared(PGconn *conn, const char *stmt); +extern PGresult *PQdescribePortal(PGconn *conn, const char *portal); +extern int PQsendDescribePrepared(PGconn *conn, const char *stmt); +extern int PQsendDescribePortal(PGconn *conn, const char *portal); + +/* Delete a PGresult */ +extern void PQclear(PGresult *res); + +/* For freeing other alloc'd results, such as PGnotify structs */ +extern void PQfreemem(void *ptr); + +/* Exists for backward compatibility. bjm 2003-03-24 */ +#define PQfreeNotify(ptr) PQfreemem(ptr) + +/* Error when no password was given. */ +/* Note: depending on this is deprecated; use PQconnectionNeedsPassword(). */ +#define PQnoPasswordSupplied "fe_sendauth: no password supplied\n" + +/* Create and manipulate PGresults */ +extern PGresult *PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status); +extern PGresult *PQcopyResult(const PGresult *src, int flags); +extern int PQsetResultAttrs(PGresult *res, int numAttributes, PGresAttDesc *attDescs); +extern void *PQresultAlloc(PGresult *res, size_t nBytes); +extern int PQsetvalue(PGresult *res, int tup_num, int field_num, char *value, int len); + +/* Quoting strings before inclusion in queries. */ +extern size_t PQescapeStringConn(PGconn *conn, + char *to, const char *from, size_t length, + int *error); +extern char *PQescapeLiteral(PGconn *conn, const char *str, size_t len); +extern char *PQescapeIdentifier(PGconn *conn, const char *str, size_t len); +extern unsigned char *PQescapeByteaConn(PGconn *conn, + const unsigned char *from, size_t from_length, + size_t *to_length); +extern unsigned char *PQunescapeBytea(const unsigned char *strtext, + size_t *retbuflen); + +/* These forms are deprecated! */ +extern size_t PQescapeString(char *to, const char *from, size_t length); +extern unsigned char *PQescapeBytea(const unsigned char *from, size_t from_length, + size_t *to_length); + + + +/* === in fe-print.c === */ + +extern void PQprint(FILE *fout, /* output stream */ + const PGresult *res, + const PQprintOpt *ps); /* option structure */ + +/* + * really old printing routines + */ +extern 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); + +extern void PQprintTuples(const PGresult *res, + FILE *fout, /* output stream */ + int printAttName, /* print attribute names */ + int terseOutput, /* delimiter bars */ + int width); /* width of column, if 0, use variable width */ + + +/* === in fe-lobj.c === */ + +/* Large-object access routines */ +extern int lo_open(PGconn *conn, Oid lobjId, int mode); +extern int lo_close(PGconn *conn, int fd); +extern int lo_read(PGconn *conn, int fd, char *buf, size_t len); +extern int lo_write(PGconn *conn, int fd, const char *buf, size_t len); +extern int lo_lseek(PGconn *conn, int fd, int offset, int whence); +extern pg_int64 lo_lseek64(PGconn *conn, int fd, pg_int64 offset, int whence); +extern Oid lo_creat(PGconn *conn, int mode); +extern Oid lo_create(PGconn *conn, Oid lobjId); +extern int lo_tell(PGconn *conn, int fd); +extern pg_int64 lo_tell64(PGconn *conn, int fd); +extern int lo_truncate(PGconn *conn, int fd, size_t len); +extern int lo_truncate64(PGconn *conn, int fd, pg_int64 len); +extern int lo_unlink(PGconn *conn, Oid lobjId); +extern Oid lo_import(PGconn *conn, const char *filename); +extern Oid lo_import_with_oid(PGconn *conn, const char *filename, Oid lobjId); +extern int lo_export(PGconn *conn, Oid lobjId, const char *filename); + +/* === in fe-misc.c === */ + +/* Get the version of the libpq library in use */ +extern int PQlibVersion(void); + +/* Determine length of multibyte encoded char at *s */ +extern int PQmblen(const char *s, int encoding); + +/* Determine display length of multibyte encoded char at *s */ +extern int PQdsplen(const char *s, int encoding); + +/* Get encoding id from environment variable PGCLIENTENCODING */ +extern int PQenv2encoding(void); + +/* === in fe-auth.c === */ + +extern char *PQencryptPassword(const char *passwd, const char *user); + +/* === in encnames.c === */ + +extern int pg_char_to_encoding(const char *name); +extern const char *pg_encoding_to_char(int encoding); +extern int pg_valid_server_encoding_id(int encoding); + +#ifdef __cplusplus +} +#endif + +#endif /* LIBPQ_FE_H */ diff --git a/libpq/libpq-int.h b/libpq/libpq-int.h new file mode 100644 index 0000000..9e4e625 --- /dev/null +++ b/libpq/libpq-int.h @@ -0,0 +1,675 @@ +/*------------------------------------------------------------------------- + * + * libpq-int.h + * This file contains internal definitions meant to be used only by + * the frontend libpq library, not by applications that call it. + * + * An application can include this file if it wants to bypass the + * official API defined by libpq-fe.h, but code that does so is much + * more likely to break across PostgreSQL releases than code that uses + * only the official API. + * + * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/interfaces/libpq/libpq-int.h + * + *------------------------------------------------------------------------- + */ + +#ifndef LIBPQ_INT_H +#define LIBPQ_INT_H + +/* We assume libpq-fe.h has already been included. */ +#include "postgres_fe.h" +#include "libpq-events.h" + +#include +#include +#ifndef WIN32 +#include +#endif + +#ifdef ENABLE_THREAD_SAFETY +#ifdef WIN32 +#include "pthread-win32.h" +#else +#include +#endif +#include +#endif + +/* include stuff common to fe and be */ +#include "getaddrinfo.h" +#include "libpq/pqcomm.h" +/* include stuff found in fe only */ +#include "pqexpbuffer.h" + +#ifdef ENABLE_GSS +#if defined(HAVE_GSSAPI_H) +#include +#else +#include +#endif +#endif + +#ifdef ENABLE_SSPI +#define SECURITY_WIN32 +#if defined(WIN32) && !defined(WIN32_ONLY_COMPILER) +#include +#endif +#include +#undef SECURITY_WIN32 + +#ifndef ENABLE_GSS +/* + * Define a fake structure compatible with GSSAPI on Unix. + */ +typedef struct +{ + void *value; + int length; +} gss_buffer_desc; +#endif +#endif /* ENABLE_SSPI */ + +#ifdef USE_OPENSSL +#include +#include + +#if (OPENSSL_VERSION_NUMBER >= 0x00907000L) && !defined(OPENSSL_NO_ENGINE) +#define USE_SSL_ENGINE +#endif +#endif /* USE_OPENSSL */ + +/* + * POSTGRES backend dependent Constants. + */ +#define CMDSTATUS_LEN 64 /* should match COMPLETION_TAG_BUFSIZE */ + +/* + * PGresult and the subsidiary types PGresAttDesc, PGresAttValue + * represent the result of a query (or more precisely, of a single SQL + * command --- a query string given to PQexec can contain multiple commands). + * Note we assume that a single command can return at most one tuple group, + * hence there is no need for multiple descriptor sets. + */ + +/* Subsidiary-storage management structure for PGresult. + * See space management routines in fe-exec.c for details. + * Note that space[k] refers to the k'th byte starting from the physical + * head of the block --- it's a union, not a struct! + */ +typedef union pgresult_data PGresult_data; + +union pgresult_data +{ + PGresult_data *next; /* link to next block, or NULL */ + char space[1]; /* dummy for accessing block as bytes */ +}; + +/* Data about a single parameter of a prepared statement */ +typedef struct pgresParamDesc +{ + Oid typid; /* type id */ +} PGresParamDesc; + +/* + * Data for a single attribute of a single tuple + * + * We use char* for Attribute values. + * + * The value pointer always points to a null-terminated area; we add a + * null (zero) byte after whatever the backend sends us. This is only + * particularly useful for text values ... with a binary value, the + * value might have embedded nulls, so the application can't use C string + * operators on it. But we add a null anyway for consistency. + * Note that the value itself does not contain a length word. + * + * A NULL attribute is a special case in two ways: its len field is NULL_LEN + * and its value field points to null_field in the owning PGresult. All the + * NULL attributes in a query result point to the same place (there's no need + * to store a null string separately for each one). + */ + +#define NULL_LEN (-1) /* pg_result len for NULL value */ + +typedef struct pgresAttValue +{ + int len; /* length in bytes of the value */ + char *value; /* actual value, plus terminating zero byte */ +} PGresAttValue; + +/* Typedef for message-field list entries */ +typedef struct pgMessageField +{ + struct pgMessageField *next; /* list link */ + char code; /* field code */ + char contents[FLEXIBLE_ARRAY_MEMBER]; /* value, nul-terminated */ +} PGMessageField; + +/* Fields needed for notice handling */ +typedef struct +{ + PQnoticeReceiver noticeRec; /* notice message receiver */ + void *noticeRecArg; + PQnoticeProcessor noticeProc; /* notice message processor */ + void *noticeProcArg; +} PGNoticeHooks; + +typedef struct PGEvent +{ + PGEventProc proc; /* the function to call on events */ + char *name; /* used only for error messages */ + void *passThrough; /* pointer supplied at registration time */ + void *data; /* optional state (instance) data */ + bool resultInitialized; /* T if RESULTCREATE/COPY succeeded */ +} PGEvent; + +struct pg_result +{ + int ntups; + int numAttributes; + PGresAttDesc *attDescs; + PGresAttValue **tuples; /* each PGresTuple is an array of + * PGresAttValue's */ + int tupArrSize; /* allocated size of tuples array */ + int numParameters; + PGresParamDesc *paramDescs; + ExecStatusType resultStatus; + char cmdStatus[CMDSTATUS_LEN]; /* cmd status from the query */ + int binary; /* binary tuple values if binary == 1, + * otherwise text */ + + /* + * These fields are copied from the originating PGconn, so that operations + * on the PGresult don't have to reference the PGconn. + */ + PGNoticeHooks noticeHooks; + PGEvent *events; + int nEvents; + int client_encoding; /* encoding id */ + + /* + * Error information (all NULL if not an error result). errMsg is the + * "overall" error message returned by PQresultErrorMessage. If we have + * per-field info then it is stored in a linked list. + */ + char *errMsg; /* error message, or NULL if no error */ + PGMessageField *errFields; /* message broken into fields */ + char *errQuery; /* text of triggering query, if available */ + + /* All NULL attributes in the query result point to this null string */ + char null_field[1]; + + /* + * Space management information. Note that attDescs and error stuff, if + * not null, point into allocated blocks. But tuples points to a + * separately malloc'd block, so that we can realloc it. + */ + PGresult_data *curBlock; /* most recently allocated block */ + int curOffset; /* start offset of free space in block */ + int spaceLeft; /* number of free bytes remaining in block */ +}; + +/* PGAsyncStatusType defines the state of the query-execution state machine */ +typedef enum +{ + PGASYNC_IDLE, /* nothing's happening, dude */ + PGASYNC_BUSY, /* query in progress */ + PGASYNC_READY, /* result ready for PQgetResult */ + PGASYNC_COPY_IN, /* Copy In data transfer in progress */ + PGASYNC_COPY_OUT, /* Copy Out data transfer in progress */ + PGASYNC_COPY_BOTH /* Copy In/Out data transfer in progress */ +} PGAsyncStatusType; + +/* PGQueryClass tracks which query protocol we are now executing */ +typedef enum +{ + PGQUERY_SIMPLE, /* simple Query protocol (PQexec) */ + PGQUERY_EXTENDED, /* full Extended protocol (PQexecParams) */ + PGQUERY_PREPARE, /* Parse only (PQprepare) */ + PGQUERY_DESCRIBE /* Describe Statement or Portal */ +} PGQueryClass; + +/* PGSetenvStatusType defines the state of the PQSetenv state machine */ +/* (this is used only for 2.0-protocol connections) */ +typedef enum +{ + SETENV_STATE_CLIENT_ENCODING_SEND, /* About to send an Environment Option */ + SETENV_STATE_CLIENT_ENCODING_WAIT, /* Waiting for above send to complete */ + SETENV_STATE_OPTION_SEND, /* About to send an Environment Option */ + SETENV_STATE_OPTION_WAIT, /* Waiting for above send to complete */ + SETENV_STATE_QUERY1_SEND, /* About to send a status query */ + SETENV_STATE_QUERY1_WAIT, /* Waiting for query to complete */ + SETENV_STATE_QUERY2_SEND, /* About to send a status query */ + SETENV_STATE_QUERY2_WAIT, /* Waiting for query to complete */ + SETENV_STATE_IDLE +} PGSetenvStatusType; + +/* Typedef for the EnvironmentOptions[] array */ +typedef struct PQEnvironmentOption +{ + const char *envName, /* name of an environment variable */ + *pgName; /* name of corresponding SET variable */ +} PQEnvironmentOption; + +/* Typedef for parameter-status list entries */ +typedef struct pgParameterStatus +{ + struct pgParameterStatus *next; /* list link */ + char *name; /* parameter name */ + char *value; /* parameter value */ + /* Note: name and value are stored in same malloc block as struct is */ +} pgParameterStatus; + +/* large-object-access data ... allocated only if large-object code is used. */ +typedef struct pgLobjfuncs +{ + Oid fn_lo_open; /* OID of backend function lo_open */ + Oid fn_lo_close; /* OID of backend function lo_close */ + Oid fn_lo_creat; /* OID of backend function lo_creat */ + Oid fn_lo_create; /* OID of backend function lo_create */ + Oid fn_lo_unlink; /* OID of backend function lo_unlink */ + Oid fn_lo_lseek; /* OID of backend function lo_lseek */ + Oid fn_lo_lseek64; /* OID of backend function lo_lseek64 */ + Oid fn_lo_tell; /* OID of backend function lo_tell */ + Oid fn_lo_tell64; /* OID of backend function lo_tell64 */ + Oid fn_lo_truncate; /* OID of backend function lo_truncate */ + Oid fn_lo_truncate64; /* OID of function lo_truncate64 */ + Oid fn_lo_read; /* OID of backend function LOread */ + Oid fn_lo_write; /* OID of backend function LOwrite */ +} PGlobjfuncs; + +/* PGdataValue represents a data field value being passed to a row processor. + * It could be either text or binary data; text data is not zero-terminated. + * A SQL NULL is represented by len < 0; then value is still valid but there + * are no data bytes there. + */ +typedef struct pgDataValue +{ + int len; /* data length in bytes, or <0 if NULL */ + const char *value; /* data value, without zero-termination */ +} PGdataValue; + +/* + * PGconn stores all the state data associated with a single connection + * to a backend. + */ +struct pg_conn +{ + /* Saved values of connection options */ + char *pghost; /* the machine on which the server is running */ + char *pghostaddr; /* the numeric IP address of the machine on + * which the server is running. Takes + * precedence over above. */ + char *pgport; /* the server's communication port number */ + char *pgunixsocket; /* the directory of the server's Unix-domain + * socket; if NULL, use DEFAULT_PGSOCKET_DIR */ + char *pgtty; /* tty on which the backend messages is + * displayed (OBSOLETE, NOT USED) */ + char *connect_timeout; /* connection timeout (numeric string) */ + char *client_encoding_initial; /* encoding to use */ + char *pgoptions; /* options to start the backend with */ + char *appname; /* application name */ + char *fbappname; /* fallback application name */ + char *dbName; /* database name */ + char *replication; /* connect as the replication standby? */ + char *pguser; /* Postgres username and password, if any */ + char *pgpass; + char *keepalives; /* use TCP keepalives? */ + char *keepalives_idle; /* time between TCP keepalives */ + char *keepalives_interval; /* time between TCP keepalive + * retransmits */ + char *keepalives_count; /* maximum number of TCP keepalive + * retransmits */ + char *sslmode; /* SSL mode (require,prefer,allow,disable) */ + char *sslcompression; /* SSL compression (0 or 1) */ + char *sslkey; /* client key filename */ + char *sslcert; /* client certificate filename */ + char *sslrootcert; /* root certificate filename */ + char *sslcrl; /* certificate revocation list filename */ + char *requirepeer; /* required peer credentials for local sockets */ + +#if defined(ENABLE_GSS) || defined(ENABLE_SSPI) + char *krbsrvname; /* Kerberos service name */ +#endif + + /* Optional file to write trace info to */ + FILE *Pfdebug; + + /* Callback procedures for notice message processing */ + PGNoticeHooks noticeHooks; + + /* Event procs registered via PQregisterEventProc */ + PGEvent *events; /* expandable array of event data */ + int nEvents; /* number of active events */ + int eventArraySize; /* allocated array size */ + + /* Status indicators */ + ConnStatusType status; + PGAsyncStatusType asyncStatus; + PGTransactionStatusType xactStatus; /* never changes to ACTIVE */ + PGQueryClass queryclass; + char *last_query; /* last SQL command, or NULL if unknown */ + char last_sqlstate[6]; /* last reported SQLSTATE */ + bool options_valid; /* true if OK to attempt connection */ + bool nonblocking; /* whether this connection is using nonblock + * sending semantics */ + bool singleRowMode; /* return current query result row-by-row? */ + char copy_is_binary; /* 1 = copy binary, 0 = copy text */ + int copy_already_done; /* # bytes already returned in COPY + * OUT */ + PGnotify *notifyHead; /* oldest unreported Notify msg */ + PGnotify *notifyTail; /* newest unreported Notify msg */ + + /* Connection data */ + /* See PQconnectPoll() for how we use 'int' and not 'pgsocket'. */ + pgsocket sock; /* FD for socket, PGINVALID_SOCKET if + * unconnected */ + SockAddr laddr; /* Local address */ + SockAddr raddr; /* Remote address */ + ProtocolVersion pversion; /* FE/BE protocol version in use */ + int sversion; /* server version, e.g. 70401 for 7.4.1 */ + bool auth_req_received; /* true if any type of auth req + * received */ + bool password_needed; /* true if server demanded a password */ + bool dot_pgpass_used; /* true if used .pgpass */ + bool sigpipe_so; /* have we masked SIGPIPE via SO_NOSIGPIPE? */ + bool sigpipe_flag; /* can we mask SIGPIPE via MSG_NOSIGNAL? */ + + /* Transient state needed while establishing connection */ + struct addrinfo *addrlist; /* list of possible backend addresses */ + struct addrinfo *addr_cur; /* the one currently being tried */ + int addrlist_family; /* needed to know how to free addrlist */ + PGSetenvStatusType setenv_state; /* for 2.0 protocol only */ + const PQEnvironmentOption *next_eo; + bool send_appname; /* okay to send application_name? */ + + /* Miscellaneous stuff */ + int be_pid; /* PID of backend --- needed for cancels */ + int be_key; /* key of backend --- needed for cancels */ + char md5Salt[4]; /* password salt received from backend */ + pgParameterStatus *pstatus; /* ParameterStatus data */ + int client_encoding; /* encoding id */ + bool std_strings; /* standard_conforming_strings */ + PGVerbosity verbosity; /* error/notice message verbosity */ + PGContextVisibility show_context; /* whether to show CONTEXT field */ + PGlobjfuncs *lobjfuncs; /* private state for large-object access fns */ + + /* Buffer for data received from backend and not yet processed */ + char *inBuffer; /* currently allocated buffer */ + int inBufSize; /* allocated size of buffer */ + int inStart; /* offset to first unconsumed data in buffer */ + int inCursor; /* next byte to tentatively consume */ + int inEnd; /* offset to first position after avail data */ + + /* Buffer for data not yet sent to backend */ + char *outBuffer; /* currently allocated buffer */ + int outBufSize; /* allocated size of buffer */ + int outCount; /* number of chars waiting in buffer */ + + /* State for constructing messages in outBuffer */ + int outMsgStart; /* offset to msg start (length word); if -1, + * msg has no length word */ + int outMsgEnd; /* offset to msg end (so far) */ + + /* Row processor interface workspace */ + PGdataValue *rowBuf; /* array for passing values to rowProcessor */ + int rowBufLen; /* number of entries allocated in rowBuf */ + + /* Status for asynchronous result construction */ + PGresult *result; /* result being constructed */ + PGresult *next_result; /* next result (used in single-row mode) */ + + /* Assorted state for SSL, GSS, etc */ + +#ifdef USE_SSL + bool allow_ssl_try; /* Allowed to try SSL negotiation */ + bool wait_ssl_try; /* Delay SSL negotiation until after + * attempting normal connection */ + bool ssl_in_use; +#ifdef USE_OPENSSL + SSL *ssl; /* SSL status, if have SSL connection */ + X509 *peer; /* X509 cert of server */ +#ifdef USE_SSL_ENGINE + ENGINE *engine; /* SSL engine, if any */ +#else + void *engine; /* dummy field to keep struct the same if + * OpenSSL version changes */ +#endif +#endif /* USE_OPENSSL */ +#endif /* USE_SSL */ + +#ifdef ENABLE_GSS + gss_ctx_id_t gctx; /* GSS context */ + gss_name_t gtarg_nam; /* GSS target name */ + gss_buffer_desc ginbuf; /* GSS input token */ + gss_buffer_desc goutbuf; /* GSS output token */ +#endif + +#ifdef ENABLE_SSPI +#ifndef ENABLE_GSS + gss_buffer_desc ginbuf; /* GSS input token */ +#else + char *gsslib; /* What GSS librart to use ("gssapi" or + * "sspi") */ +#endif + CredHandle *sspicred; /* SSPI credentials handle */ + CtxtHandle *sspictx; /* SSPI context */ + char *sspitarget; /* SSPI target name */ + int usesspi; /* Indicate if SSPI is in use on the + * connection */ +#endif + + /* Buffer for current error message */ + PQExpBufferData errorMessage; /* expansible string */ + + /* Buffer for receiving various parts of messages */ + PQExpBufferData workBuffer; /* expansible string */ +}; + +/* PGcancel stores all data necessary to cancel a connection. A copy of this + * data is required to safely cancel a connection running on a different + * thread. + */ +struct pg_cancel +{ + SockAddr raddr; /* Remote address */ + int be_pid; /* PID of backend --- needed for cancels */ + int be_key; /* key of backend --- needed for cancels */ +}; + + +/* String descriptions of the ExecStatusTypes. + * direct use of this array is deprecated; call PQresStatus() instead. + */ +extern char *const pgresStatus[]; + + +#ifdef USE_SSL + +#ifndef WIN32 +#define USER_CERT_FILE ".postgresql/postgresql.crt" +#define USER_KEY_FILE ".postgresql/postgresql.key" +#define ROOT_CERT_FILE ".postgresql/root.crt" +#define ROOT_CRL_FILE ".postgresql/root.crl" +#else +/* On Windows, the "home" directory is already PostgreSQL-specific */ +#define USER_CERT_FILE "postgresql.crt" +#define USER_KEY_FILE "postgresql.key" +#define ROOT_CERT_FILE "root.crt" +#define ROOT_CRL_FILE "root.crl" +#endif + +#endif /* USE_SSL */ + +/* ---------------- + * Internal functions of libpq + * Functions declared here need to be visible across files of libpq, + * but are not intended to be called by applications. We use the + * convention "pqXXX" for internal functions, vs. the "PQxxx" names + * used for application-visible routines. + * ---------------- + */ + +/* === in fe-connect.c === */ + +extern void pqDropConnection(PGconn *conn, bool flushInput); +extern int pqPacketSend(PGconn *conn, char pack_type, + const void *buf, size_t buf_len); +extern bool pqGetHomeDirectory(char *buf, int bufsize); + +#ifdef ENABLE_THREAD_SAFETY +extern pgthreadlock_t pg_g_threadlock; + +#define PGTHREAD_ERROR(msg) \ + do { \ + fprintf(stderr, "%s\n", msg); \ + abort(); \ + } while (0) + + +#define pglock_thread() pg_g_threadlock(true) +#define pgunlock_thread() pg_g_threadlock(false) +#else +#define pglock_thread() ((void) 0) +#define pgunlock_thread() ((void) 0) +#endif + +/* === in fe-exec.c === */ + +extern void pqSetResultError(PGresult *res, const char *msg); +extern void pqCatenateResultError(PGresult *res, const char *msg); +extern void *pqResultAlloc(PGresult *res, size_t nBytes, bool isBinary); +extern char *pqResultStrdup(PGresult *res, const char *str); +extern void pqClearAsyncResult(PGconn *conn); +extern void pqSaveErrorResult(PGconn *conn); +extern PGresult *pqPrepareAsyncResult(PGconn *conn); +extern void pqInternalNotice(const PGNoticeHooks *hooks, const char *fmt,...) pg_attribute_printf(2, 3); +extern void pqSaveMessageField(PGresult *res, char code, + const char *value); +extern void pqSaveParameterStatus(PGconn *conn, const char *name, + const char *value); +extern int pqRowProcessor(PGconn *conn, const char **errmsgp); +extern void pqHandleSendFailure(PGconn *conn); + +/* === in fe-protocol2.c === */ + +extern PostgresPollingStatusType pqSetenvPoll(PGconn *conn); + +extern char *pqBuildStartupPacket2(PGconn *conn, int *packetlen, + const PQEnvironmentOption *options); +extern void pqParseInput2(PGconn *conn); +extern int pqGetCopyData2(PGconn *conn, char **buffer, int async); +extern int pqGetline2(PGconn *conn, char *s, int maxlen); +extern int pqGetlineAsync2(PGconn *conn, char *buffer, int bufsize); +extern int pqEndcopy2(PGconn *conn); +extern PGresult *pqFunctionCall2(PGconn *conn, Oid fnid, + int *result_buf, int *actual_result_len, + int result_is_int, + const PQArgBlock *args, int nargs); + +/* === in fe-protocol3.c === */ + +extern char *pqBuildStartupPacket3(PGconn *conn, int *packetlen, + const PQEnvironmentOption *options); +extern void pqParseInput3(PGconn *conn); +extern int pqGetErrorNotice3(PGconn *conn, bool isError); +extern void pqBuildErrorMessage3(PQExpBuffer msg, const PGresult *res, + PGVerbosity verbosity, PGContextVisibility show_context); +extern int pqGetCopyData3(PGconn *conn, char **buffer, int async); +extern int pqGetline3(PGconn *conn, char *s, int maxlen); +extern int pqGetlineAsync3(PGconn *conn, char *buffer, int bufsize); +extern int pqEndcopy3(PGconn *conn); +extern PGresult *pqFunctionCall3(PGconn *conn, Oid fnid, + int *result_buf, int *actual_result_len, + int result_is_int, + const PQArgBlock *args, int nargs); + +/* === in fe-misc.c === */ + + /* + * "Get" and "Put" routines return 0 if successful, EOF if not. Note that for + * Get, EOF merely means the buffer is exhausted, not that there is + * necessarily any error. + */ +extern int pqCheckOutBufferSpace(size_t bytes_needed, PGconn *conn); +extern int pqCheckInBufferSpace(size_t bytes_needed, PGconn *conn); +extern int pqGetc(char *result, PGconn *conn); +extern int pqPutc(char c, PGconn *conn); +extern int pqGets(PQExpBuffer buf, PGconn *conn); +extern int pqGets_append(PQExpBuffer buf, PGconn *conn); +extern int pqPuts(const char *s, PGconn *conn); +extern int pqGetnchar(char *s, size_t len, PGconn *conn); +extern int pqSkipnchar(size_t len, PGconn *conn); +extern int pqPutnchar(const char *s, size_t len, PGconn *conn); +extern int pqGetInt(int *result, size_t bytes, PGconn *conn); +extern int pqPutInt(int value, size_t bytes, PGconn *conn); +extern int pqPutMsgStart(char msg_type, bool force_len, PGconn *conn); +extern int pqPutMsgEnd(PGconn *conn); +extern int pqReadData(PGconn *conn); +extern int pqFlush(PGconn *conn); +extern int pqWait(int forRead, int forWrite, PGconn *conn); +extern int pqWaitTimed(int forRead, int forWrite, PGconn *conn, + time_t finish_time); +extern int pqReadReady(PGconn *conn); +extern int pqWriteReady(PGconn *conn); + +/* === in fe-secure.c === */ + +extern int pqsecure_initialize(PGconn *); +extern void pqsecure_destroy(void); +extern PostgresPollingStatusType pqsecure_open_client(PGconn *); +extern void pqsecure_close(PGconn *); +extern ssize_t pqsecure_read(PGconn *, void *ptr, size_t len); +extern ssize_t pqsecure_write(PGconn *, const void *ptr, size_t len); +extern ssize_t pqsecure_raw_read(PGconn *, void *ptr, size_t len); +extern ssize_t pqsecure_raw_write(PGconn *, const void *ptr, size_t len); + +#if defined(ENABLE_THREAD_SAFETY) && !defined(WIN32) +extern int pq_block_sigpipe(sigset_t *osigset, bool *sigpipe_pending); +extern void pq_reset_sigpipe(sigset_t *osigset, bool sigpipe_pending, + bool got_epipe); +#endif + +/* + * The SSL implementation provides these functions (fe-secure-openssl.c) + */ +extern void pgtls_init_library(bool do_ssl, int do_crypto); +extern int pgtls_init(PGconn *conn); +extern PostgresPollingStatusType pgtls_open_client(PGconn *conn); +extern void pgtls_close(PGconn *conn); +extern ssize_t pgtls_read(PGconn *conn, void *ptr, size_t len); +extern bool pgtls_read_pending(PGconn *conn); +extern ssize_t pgtls_write(PGconn *conn, const void *ptr, size_t len); + +/* + * this is so that we can check if a connection is non-blocking internally + * without the overhead of a function call + */ +#define pqIsnonblocking(conn) ((conn)->nonblocking) + +#ifdef ENABLE_NLS +extern char *libpq_gettext(const char *msgid) pg_attribute_format_arg(1); +extern char *libpq_ngettext(const char *msgid, const char *msgid_plural, unsigned long n) pg_attribute_format_arg(1) pg_attribute_format_arg(2); +#else +#define libpq_gettext(x) (x) +#define libpq_ngettext(s, p, n) ((n) == 1 ? (s) : (p)) +#endif + +/* + * These macros are needed to let error-handling code be portable between + * Unix and Windows. (ugh) + */ +#ifdef WIN32 +#define SOCK_ERRNO (WSAGetLastError()) +#define SOCK_STRERROR winsock_strerror +#define SOCK_ERRNO_SET(e) WSASetLastError(e) +#else +#define SOCK_ERRNO errno +#define SOCK_STRERROR pqStrerror +#define SOCK_ERRNO_SET(e) (errno = (e)) +#endif + +#endif /* LIBPQ_INT_H */ diff --git a/libpq/md5.c b/libpq/md5.c new file mode 100644 index 0000000..5af54e6 --- /dev/null +++ b/libpq/md5.c @@ -0,0 +1,345 @@ +/* + * md5.c + * + * Implements the MD5 Message-Digest Algorithm as specified in + * RFC 1321. This implementation is a simple one, in that it + * needs every input byte to be buffered before doing any + * calculations. I do not expect this file to be used for + * general purpose MD5'ing of large amounts of data, only for + * generating hashed passwords from limited input. + * + * Sverre H. Huseby + * + * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * IDENTIFICATION + * src/backend/libpq/md5.c + */ + +/* This is intended to be used in both frontend and backend, so use c.h */ +#include "c.h" + +#include "libpq/md5.h" + + +/* + * PRIVATE FUNCTIONS + */ + + +/* + * The returned array is allocated using malloc. the caller should free it + * when it is no longer needed. + */ +static uint8 * +createPaddedCopyWithLength(const uint8 *b, uint32 *l) +{ + uint8 *ret; + uint32 q; + uint32 len, + newLen448; + uint32 len_high, + len_low; /* 64-bit value split into 32-bit sections */ + + len = ((b == NULL) ? 0 : *l); + newLen448 = len + 64 - (len % 64) - 8; + if (newLen448 <= len) + newLen448 += 64; + + *l = newLen448 + 8; + if ((ret = (uint8 *) malloc(sizeof(uint8) * *l)) == NULL) + return NULL; + + if (b != NULL) + memcpy(ret, b, sizeof(uint8) * len); + + /* pad */ + ret[len] = 0x80; + for (q = len + 1; q < newLen448; q++) + ret[q] = 0x00; + + /* append length as a 64 bit bitcount */ + len_low = len; + /* split into two 32-bit values */ + /* we only look at the bottom 32-bits */ + len_high = len >> 29; + len_low <<= 3; + q = newLen448; + ret[q++] = (len_low & 0xff); + len_low >>= 8; + ret[q++] = (len_low & 0xff); + len_low >>= 8; + ret[q++] = (len_low & 0xff); + len_low >>= 8; + ret[q++] = (len_low & 0xff); + ret[q++] = (len_high & 0xff); + len_high >>= 8; + ret[q++] = (len_high & 0xff); + len_high >>= 8; + ret[q++] = (len_high & 0xff); + len_high >>= 8; + ret[q] = (len_high & 0xff); + + return ret; +} + +#define F(x, y, z) (((x) & (y)) | (~(x) & (z))) +#define G(x, y, z) (((x) & (z)) | ((y) & ~(z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | ~(z))) +#define ROT_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) + +static void +doTheRounds(uint32 X[16], uint32 state[4]) +{ + uint32 a, + b, + c, + d; + + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + + /* round 1 */ + a = b + ROT_LEFT((a + F(b, c, d) + X[0] + 0xd76aa478), 7); /* 1 */ + d = a + ROT_LEFT((d + F(a, b, c) + X[1] + 0xe8c7b756), 12); /* 2 */ + c = d + ROT_LEFT((c + F(d, a, b) + X[2] + 0x242070db), 17); /* 3 */ + b = c + ROT_LEFT((b + F(c, d, a) + X[3] + 0xc1bdceee), 22); /* 4 */ + a = b + ROT_LEFT((a + F(b, c, d) + X[4] + 0xf57c0faf), 7); /* 5 */ + d = a + ROT_LEFT((d + F(a, b, c) + X[5] + 0x4787c62a), 12); /* 6 */ + c = d + ROT_LEFT((c + F(d, a, b) + X[6] + 0xa8304613), 17); /* 7 */ + b = c + ROT_LEFT((b + F(c, d, a) + X[7] + 0xfd469501), 22); /* 8 */ + a = b + ROT_LEFT((a + F(b, c, d) + X[8] + 0x698098d8), 7); /* 9 */ + d = a + ROT_LEFT((d + F(a, b, c) + X[9] + 0x8b44f7af), 12); /* 10 */ + c = d + ROT_LEFT((c + F(d, a, b) + X[10] + 0xffff5bb1), 17); /* 11 */ + b = c + ROT_LEFT((b + F(c, d, a) + X[11] + 0x895cd7be), 22); /* 12 */ + a = b + ROT_LEFT((a + F(b, c, d) + X[12] + 0x6b901122), 7); /* 13 */ + d = a + ROT_LEFT((d + F(a, b, c) + X[13] + 0xfd987193), 12); /* 14 */ + c = d + ROT_LEFT((c + F(d, a, b) + X[14] + 0xa679438e), 17); /* 15 */ + b = c + ROT_LEFT((b + F(c, d, a) + X[15] + 0x49b40821), 22); /* 16 */ + + /* round 2 */ + a = b + ROT_LEFT((a + G(b, c, d) + X[1] + 0xf61e2562), 5); /* 17 */ + d = a + ROT_LEFT((d + G(a, b, c) + X[6] + 0xc040b340), 9); /* 18 */ + c = d + ROT_LEFT((c + G(d, a, b) + X[11] + 0x265e5a51), 14); /* 19 */ + b = c + ROT_LEFT((b + G(c, d, a) + X[0] + 0xe9b6c7aa), 20); /* 20 */ + a = b + ROT_LEFT((a + G(b, c, d) + X[5] + 0xd62f105d), 5); /* 21 */ + d = a + ROT_LEFT((d + G(a, b, c) + X[10] + 0x02441453), 9); /* 22 */ + c = d + ROT_LEFT((c + G(d, a, b) + X[15] + 0xd8a1e681), 14); /* 23 */ + b = c + ROT_LEFT((b + G(c, d, a) + X[4] + 0xe7d3fbc8), 20); /* 24 */ + a = b + ROT_LEFT((a + G(b, c, d) + X[9] + 0x21e1cde6), 5); /* 25 */ + d = a + ROT_LEFT((d + G(a, b, c) + X[14] + 0xc33707d6), 9); /* 26 */ + c = d + ROT_LEFT((c + G(d, a, b) + X[3] + 0xf4d50d87), 14); /* 27 */ + b = c + ROT_LEFT((b + G(c, d, a) + X[8] + 0x455a14ed), 20); /* 28 */ + a = b + ROT_LEFT((a + G(b, c, d) + X[13] + 0xa9e3e905), 5); /* 29 */ + d = a + ROT_LEFT((d + G(a, b, c) + X[2] + 0xfcefa3f8), 9); /* 30 */ + c = d + ROT_LEFT((c + G(d, a, b) + X[7] + 0x676f02d9), 14); /* 31 */ + b = c + ROT_LEFT((b + G(c, d, a) + X[12] + 0x8d2a4c8a), 20); /* 32 */ + + /* round 3 */ + a = b + ROT_LEFT((a + H(b, c, d) + X[5] + 0xfffa3942), 4); /* 33 */ + d = a + ROT_LEFT((d + H(a, b, c) + X[8] + 0x8771f681), 11); /* 34 */ + c = d + ROT_LEFT((c + H(d, a, b) + X[11] + 0x6d9d6122), 16); /* 35 */ + b = c + ROT_LEFT((b + H(c, d, a) + X[14] + 0xfde5380c), 23); /* 36 */ + a = b + ROT_LEFT((a + H(b, c, d) + X[1] + 0xa4beea44), 4); /* 37 */ + d = a + ROT_LEFT((d + H(a, b, c) + X[4] + 0x4bdecfa9), 11); /* 38 */ + c = d + ROT_LEFT((c + H(d, a, b) + X[7] + 0xf6bb4b60), 16); /* 39 */ + b = c + ROT_LEFT((b + H(c, d, a) + X[10] + 0xbebfbc70), 23); /* 40 */ + a = b + ROT_LEFT((a + H(b, c, d) + X[13] + 0x289b7ec6), 4); /* 41 */ + d = a + ROT_LEFT((d + H(a, b, c) + X[0] + 0xeaa127fa), 11); /* 42 */ + c = d + ROT_LEFT((c + H(d, a, b) + X[3] + 0xd4ef3085), 16); /* 43 */ + b = c + ROT_LEFT((b + H(c, d, a) + X[6] + 0x04881d05), 23); /* 44 */ + a = b + ROT_LEFT((a + H(b, c, d) + X[9] + 0xd9d4d039), 4); /* 45 */ + d = a + ROT_LEFT((d + H(a, b, c) + X[12] + 0xe6db99e5), 11); /* 46 */ + c = d + ROT_LEFT((c + H(d, a, b) + X[15] + 0x1fa27cf8), 16); /* 47 */ + b = c + ROT_LEFT((b + H(c, d, a) + X[2] + 0xc4ac5665), 23); /* 48 */ + + /* round 4 */ + a = b + ROT_LEFT((a + I(b, c, d) + X[0] + 0xf4292244), 6); /* 49 */ + d = a + ROT_LEFT((d + I(a, b, c) + X[7] + 0x432aff97), 10); /* 50 */ + c = d + ROT_LEFT((c + I(d, a, b) + X[14] + 0xab9423a7), 15); /* 51 */ + b = c + ROT_LEFT((b + I(c, d, a) + X[5] + 0xfc93a039), 21); /* 52 */ + a = b + ROT_LEFT((a + I(b, c, d) + X[12] + 0x655b59c3), 6); /* 53 */ + d = a + ROT_LEFT((d + I(a, b, c) + X[3] + 0x8f0ccc92), 10); /* 54 */ + c = d + ROT_LEFT((c + I(d, a, b) + X[10] + 0xffeff47d), 15); /* 55 */ + b = c + ROT_LEFT((b + I(c, d, a) + X[1] + 0x85845dd1), 21); /* 56 */ + a = b + ROT_LEFT((a + I(b, c, d) + X[8] + 0x6fa87e4f), 6); /* 57 */ + d = a + ROT_LEFT((d + I(a, b, c) + X[15] + 0xfe2ce6e0), 10); /* 58 */ + c = d + ROT_LEFT((c + I(d, a, b) + X[6] + 0xa3014314), 15); /* 59 */ + b = c + ROT_LEFT((b + I(c, d, a) + X[13] + 0x4e0811a1), 21); /* 60 */ + a = b + ROT_LEFT((a + I(b, c, d) + X[4] + 0xf7537e82), 6); /* 61 */ + d = a + ROT_LEFT((d + I(a, b, c) + X[11] + 0xbd3af235), 10); /* 62 */ + c = d + ROT_LEFT((c + I(d, a, b) + X[2] + 0x2ad7d2bb), 15); /* 63 */ + b = c + ROT_LEFT((b + I(c, d, a) + X[9] + 0xeb86d391), 21); /* 64 */ + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; +} + +static int +calculateDigestFromBuffer(const uint8 *b, uint32 len, uint8 sum[16]) +{ + register uint32 i, + j, + k, + newI; + uint32 l; + uint8 *input; + register uint32 *wbp; + uint32 workBuff[16], + state[4]; + + l = len; + + state[0] = 0x67452301; + state[1] = 0xEFCDAB89; + state[2] = 0x98BADCFE; + state[3] = 0x10325476; + + if ((input = createPaddedCopyWithLength(b, &l)) == NULL) + return 0; + + for (i = 0;;) + { + if ((newI = i + 16 * 4) > l) + break; + k = i + 3; + for (j = 0; j < 16; j++) + { + wbp = (workBuff + j); + *wbp = input[k--]; + *wbp <<= 8; + *wbp |= input[k--]; + *wbp <<= 8; + *wbp |= input[k--]; + *wbp <<= 8; + *wbp |= input[k]; + k += 7; + } + doTheRounds(workBuff, state); + i = newI; + } + free(input); + + j = 0; + for (i = 0; i < 4; i++) + { + k = state[i]; + sum[j++] = (k & 0xff); + k >>= 8; + sum[j++] = (k & 0xff); + k >>= 8; + sum[j++] = (k & 0xff); + k >>= 8; + sum[j++] = (k & 0xff); + } + return 1; +} + +static void +bytesToHex(uint8 b[16], char *s) +{ + static const char *hex = "0123456789abcdef"; + int q, + w; + + for (q = 0, w = 0; q < 16; q++) + { + s[w++] = hex[(b[q] >> 4) & 0x0F]; + s[w++] = hex[b[q] & 0x0F]; + } + s[w] = '\0'; +} + +/* + * PUBLIC FUNCTIONS + */ + +/* + * pg_md5_hash + * + * Calculates the MD5 sum of the bytes in a buffer. + * + * SYNOPSIS #include "md5.h" + * int pg_md5_hash(const void *buff, size_t len, char *hexsum) + * + * INPUT buff the buffer containing the bytes that you want + * the MD5 sum of. + * len number of bytes in the buffer. + * + * OUTPUT hexsum the MD5 sum as a '\0'-terminated string of + * hexadecimal digits. an MD5 sum is 16 bytes long. + * each byte is represented by two heaxadecimal + * characters. you thus need to provide an array + * of 33 characters, including the trailing '\0'. + * + * RETURNS false on failure (out of memory for internal buffers) or + * true on success. + * + * STANDARDS MD5 is described in RFC 1321. + * + * AUTHOR Sverre H. Huseby + * + */ +bool +pg_md5_hash(const void *buff, size_t len, char *hexsum) +{ + uint8 sum[16]; + + if (!calculateDigestFromBuffer(buff, len, sum)) + return false; + + bytesToHex(sum, hexsum); + return true; +} + +bool +pg_md5_binary(const void *buff, size_t len, void *outbuf) +{ + if (!calculateDigestFromBuffer(buff, len, outbuf)) + return false; + return true; +} + + +/* + * Computes MD5 checksum of "passwd" (a null-terminated string) followed + * by "salt" (which need not be null-terminated). + * + * Output format is "md5" followed by a 32-hex-digit MD5 checksum. + * Hence, the output buffer "buf" must be at least 36 bytes long. + * + * Returns TRUE if okay, FALSE on error (out of memory). + */ +bool +pg_md5_encrypt(const char *passwd, const char *salt, size_t salt_len, + char *buf) +{ + size_t passwd_len = strlen(passwd); + + /* +1 here is just to avoid risk of unportable malloc(0) */ + char *crypt_buf = malloc(passwd_len + salt_len + 1); + bool ret; + + if (!crypt_buf) + return false; + + /* + * Place salt at the end because it may be known by users trying to crack + * the MD5 output. + */ + memcpy(crypt_buf, passwd, passwd_len); + memcpy(crypt_buf + passwd_len, salt, salt_len); + + strcpy(buf, "md5"); + ret = pg_md5_hash(crypt_buf, passwd_len + salt_len, buf + 3); + + free(crypt_buf); + + return ret; +} diff --git a/libpq/noblock.c b/libpq/noblock.c new file mode 100644 index 0000000..959d3ad --- /dev/null +++ b/libpq/noblock.c @@ -0,0 +1,66 @@ +/*------------------------------------------------------------------------- + * + * noblock.c + * set a file descriptor as blocking or non-blocking + * + * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * IDENTIFICATION + * src/port/noblock.c + * + *------------------------------------------------------------------------- + */ + +#include "c.h" + +#include + + +/* + * Put socket into nonblock mode. + * Returns true on success, false on failure. + */ +bool +pg_set_noblock(pgsocket sock) +{ +#if !defined(WIN32) + int flags; + + flags = fcntl(sock, F_GETFL); + if (flags < 0) + return false; + if (fcntl(sock, F_SETFL, (flags | O_NONBLOCK)) == -1) + return false; + return true; +#else + unsigned long ioctlsocket_ret = 1; + + /* Returns non-0 on failure, while fcntl() returns -1 on failure */ + return (ioctlsocket(sock, FIONBIO, &ioctlsocket_ret) == 0); +#endif +} + +/* + * Put socket into blocking mode. + * Returns true on success, false on failure. + */ +bool +pg_set_block(pgsocket sock) +{ +#if !defined(WIN32) + int flags; + + flags = fcntl(sock, F_GETFL); + if (flags < 0) + return false; + if (fcntl(sock, F_SETFL, (flags & ~O_NONBLOCK)) == -1) + return false; + return true; +#else + unsigned long ioctlsocket_ret = 0; + + /* Returns non-0 on failure, while fcntl() returns -1 on failure */ + return (ioctlsocket(sock, FIONBIO, &ioctlsocket_ret) == 0); +#endif +} diff --git a/libpq/non-bsd/strlcpy.c b/libpq/non-bsd/strlcpy.c new file mode 100644 index 0000000..6aff65c --- /dev/null +++ b/libpq/non-bsd/strlcpy.c @@ -0,0 +1,71 @@ +/*------------------------------------------------------------------------- + * + * strlcpy.c + * strncpy done right + * + * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * + * + * IDENTIFICATION + * src/port/strlcpy.c + * + * This file was taken from OpenBSD and is used on platforms that don't + * provide strlcpy(). The OpenBSD copyright terms follow. + *------------------------------------------------------------------------- + */ + +/* $OpenBSD: strlcpy.c,v 1.11 2006/05/05 15:27:38 millert Exp $ */ + +/* + * Copyright (c) 1998 Todd C. Miller + * + * 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 THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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. + */ + +#include "c.h" + + +/* + * Copy src to string dst of size siz. At most siz-1 characters + * will be copied. Always NUL terminates (unless siz == 0). + * Returns strlen(src); if retval >= siz, truncation occurred. + * Function creation history: http://www.gratisoft.us/todd/papers/strlcpy.html + */ +size_t +strlcpy(char *dst, const char *src, size_t siz) +{ + char *d = dst; + const char *s = src; + size_t n = siz; + + /* Copy as many bytes as will fit */ + if (n != 0) + { + while (--n != 0) + { + if ((*d++ = *s++) == '\0') + break; + } + } + + /* Not enough room in dst, add NUL and traverse rest of src */ + if (n == 0) + { + if (siz != 0) + *d = '\0'; /* NUL-terminate dst */ + while (*s++) + ; + } + + return (s - src - 1); /* count does not include NUL */ +} diff --git a/libpq/pg_service.conf.sample b/libpq/pg_service.conf.sample new file mode 100644 index 0000000..5a1c083 --- /dev/null +++ b/libpq/pg_service.conf.sample @@ -0,0 +1,17 @@ +# +# Connection configuration file +# +# A service is a set of named connection parameters. You may specify +# multiple services in this file. Each starts with a service name in +# brackets. Subsequent lines have connection configuration parameters of +# the pattern "param=value" or LDAP URLs starting with "ldap://" +# to look up such parameters. A sample configuration for postgres is +# included in this file. Lines beginning with '#' are comments. +# +# Copy this to your sysconf directory (typically /usr/local/pgsql/etc) and +# rename it pg_service.conf. +# +# +#[postgres] +#dbname=postgres +#user=postgres diff --git a/libpq/pgstrcasecmp.c b/libpq/pgstrcasecmp.c new file mode 100644 index 0000000..ee87a65 --- /dev/null +++ b/libpq/pgstrcasecmp.c @@ -0,0 +1,151 @@ +/*------------------------------------------------------------------------- + * + * pgstrcasecmp.c + * Portable SQL-like case-independent comparisons and conversions. + * + * SQL99 specifies Unicode-aware case normalization, which we don't yet + * have the infrastructure for. Instead we use tolower() to provide a + * locale-aware translation. However, there are some locales where this + * is not right either (eg, Turkish may do strange things with 'i' and + * 'I'). Our current compromise is to use tolower() for characters with + * the high bit set, and use an ASCII-only downcasing for 7-bit + * characters. + * + * NB: this code should match downcase_truncate_identifier() in scansup.c. + * + * We also provide strict ASCII-only case conversion functions, which can + * be used to implement C/POSIX case folding semantics no matter what the + * C library thinks the locale is. + * + * + * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * + * src/port/pgstrcasecmp.c + * + *------------------------------------------------------------------------- + */ +#include "c.h" + +#include + + +/* + * Case-independent comparison of two null-terminated strings. + */ +int +pg_strcasecmp(const char *s1, const char *s2) +{ + for (;;) + { + unsigned char ch1 = (unsigned char) *s1++; + unsigned char ch2 = (unsigned char) *s2++; + + if (ch1 != ch2) + { + if (ch1 >= 'A' && ch1 <= 'Z') + ch1 += 'a' - 'A'; + else if (IS_HIGHBIT_SET(ch1) && isupper(ch1)) + ch1 = tolower(ch1); + + if (ch2 >= 'A' && ch2 <= 'Z') + ch2 += 'a' - 'A'; + else if (IS_HIGHBIT_SET(ch2) && isupper(ch2)) + ch2 = tolower(ch2); + + if (ch1 != ch2) + return (int) ch1 - (int) ch2; + } + if (ch1 == 0) + break; + } + return 0; +} + +/* + * Case-independent comparison of two not-necessarily-null-terminated strings. + * At most n bytes will be examined from each string. + */ +int +pg_strncasecmp(const char *s1, const char *s2, size_t n) +{ + while (n-- > 0) + { + unsigned char ch1 = (unsigned char) *s1++; + unsigned char ch2 = (unsigned char) *s2++; + + if (ch1 != ch2) + { + if (ch1 >= 'A' && ch1 <= 'Z') + ch1 += 'a' - 'A'; + else if (IS_HIGHBIT_SET(ch1) && isupper(ch1)) + ch1 = tolower(ch1); + + if (ch2 >= 'A' && ch2 <= 'Z') + ch2 += 'a' - 'A'; + else if (IS_HIGHBIT_SET(ch2) && isupper(ch2)) + ch2 = tolower(ch2); + + if (ch1 != ch2) + return (int) ch1 - (int) ch2; + } + if (ch1 == 0) + break; + } + return 0; +} + +/* + * Fold a character to upper case. + * + * Unlike some versions of toupper(), this is safe to apply to characters + * that aren't lower case letters. Note however that the whole thing is + * a bit bogus for multibyte character sets. + */ +unsigned char +pg_toupper(unsigned char ch) +{ + if (ch >= 'a' && ch <= 'z') + ch += 'A' - 'a'; + else if (IS_HIGHBIT_SET(ch) && islower(ch)) + ch = toupper(ch); + return ch; +} + +/* + * Fold a character to lower case. + * + * Unlike some versions of tolower(), this is safe to apply to characters + * that aren't upper case letters. Note however that the whole thing is + * a bit bogus for multibyte character sets. + */ +unsigned char +pg_tolower(unsigned char ch) +{ + if (ch >= 'A' && ch <= 'Z') + ch += 'a' - 'A'; + else if (IS_HIGHBIT_SET(ch) && isupper(ch)) + ch = tolower(ch); + return ch; +} + +/* + * Fold a character to upper case, following C/POSIX locale rules. + */ +unsigned char +pg_ascii_toupper(unsigned char ch) +{ + if (ch >= 'a' && ch <= 'z') + ch += 'A' - 'a'; + return ch; +} + +/* + * Fold a character to lower case, following C/POSIX locale rules. + */ +unsigned char +pg_ascii_tolower(unsigned char ch) +{ + if (ch >= 'A' && ch <= 'Z') + ch += 'a' - 'A'; + return ch; +} diff --git a/libpq/postgresql/c.h b/libpq/postgresql/c.h new file mode 100644 index 0000000..726f0f3 --- /dev/null +++ b/libpq/postgresql/c.h @@ -0,0 +1,1107 @@ +/*------------------------------------------------------------------------- + * + * c.h + * Fundamental C definitions. This is included by every .c file in + * PostgreSQL (via either postgres.h or postgres_fe.h, as appropriate). + * + * Note that the definitions here are not intended to be exposed to clients + * of the frontend interface libraries --- so we don't worry much about + * polluting the namespace with lots of stuff... + * + * + * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/c.h + * + *------------------------------------------------------------------------- + */ +/* + *---------------------------------------------------------------- + * TABLE OF CONTENTS + * + * When adding stuff to this file, please try to put stuff + * into the relevant section, or add new sections as appropriate. + * + * section description + * ------- ------------------------------------------------ + * 0) pg_config.h and standard system headers + * 1) hacks to cope with non-ANSI C compilers + * 2) bool, true, false, TRUE, FALSE, NULL + * 3) standard system types + * 4) IsValid macros for system types + * 5) offsetof, lengthof, endof, alignment + * 6) assertions + * 7) widely useful macros + * 8) random stuff + * 9) system-specific hacks + * + * NOTE: since this file is included by both frontend and backend modules, it's + * almost certainly wrong to put an "extern" declaration here. typedefs and + * macros are the kind of thing that might go here. + * + *---------------------------------------------------------------- + */ +#ifndef C_H +#define C_H + +#include "postgres_ext.h" + +/* Must undef pg_config_ext.h symbols before including pg_config.h */ +#undef PG_INT64_TYPE + +#include "pg_config.h" +#include "pg_config_manual.h" /* must be after pg_config.h */ + +/* + * We always rely on the WIN32 macro being set by our build system, + * but _WIN32 is the compiler pre-defined macro. So make sure we define + * WIN32 whenever _WIN32 is set, to facilitate standalone building. + */ +#if defined(_WIN32) && !defined(WIN32) +#define WIN32 +#endif + +#if !defined(WIN32) && !defined(__CYGWIN__) /* win32 includes further down */ +#include "pg_config_os.h" /* must be before any system header files */ +#endif + +#if _MSC_VER >= 1400 || defined(HAVE_CRTDEFS_H) +#define errcode __msvc_errcode +#include +#undef errcode +#endif + +/* + * We have to include stdlib.h here because it defines many of these macros + * on some platforms, and we only want our definitions used if stdlib.h doesn't + * have its own. The same goes for stddef and stdarg if present. + */ + +#include +#include +#include +#include +#include +#ifdef HAVE_STRINGS_H +#include +#endif +#ifdef HAVE_STDINT_H +#include +#endif +#include + +#include +#if defined(WIN32) || defined(__CYGWIN__) +#include /* ensure O_BINARY is available */ +#endif + +#if defined(WIN32) || defined(__CYGWIN__) +/* We have to redefine some system functions after they are included above. */ +#include "pg_config_os.h" +#endif + +/* + * Force disable inlining if PG_FORCE_DISABLE_INLINE is defined. This is used + * to work around compiler bugs and might also be useful for investigatory + * purposes by defining the symbol in the platform's header.. + * + * This is done early (in slightly the wrong section) as functionality later + * in this file might want to rely on inline functions. + */ +#ifdef PG_FORCE_DISABLE_INLINE +#undef inline +#define inline +#endif + +/* Must be before gettext() games below */ +#include + +#define _(x) gettext(x) + +#ifdef ENABLE_NLS +#include +#else +#define gettext(x) (x) +#define dgettext(d,x) (x) +#define ngettext(s,p,n) ((n) == 1 ? (s) : (p)) +#define dngettext(d,s,p,n) ((n) == 1 ? (s) : (p)) +#endif + +/* + * Use this to mark string constants as needing translation at some later + * time, rather than immediately. This is useful for cases where you need + * access to the original string and translated string, and for cases where + * immediate translation is not possible, like when initializing global + * variables. + * http://www.gnu.org/software/autoconf/manual/gettext/Special-cases.html + */ +#define gettext_noop(x) (x) + + +/* ---------------------------------------------------------------- + * Section 1: hacks to cope with non-ANSI C compilers + * + * type prefixes (const, signed, volatile, inline) are handled in pg_config.h. + * ---------------------------------------------------------------- + */ + +/* + * CppAsString + * Convert the argument to a string, using the C preprocessor. + * CppConcat + * Concatenate two arguments together, using the C preprocessor. + * + * Note: There used to be support here for pre-ANSI C compilers that didn't + * support # and ##. Nowadays, these macros are just for clarity and/or + * backward compatibility with existing PostgreSQL code. + */ +#define CppAsString(identifier) #identifier +#define CppConcat(x, y) x##y + +/* + * dummyret is used to set return values in macros that use ?: to make + * assignments. gcc wants these to be void, other compilers like char + */ +#ifdef __GNUC__ /* GNU cc */ +#define dummyret void +#else +#define dummyret char +#endif + +/* Which __func__ symbol do we have, if any? */ +#ifdef HAVE_FUNCNAME__FUNC +#define PG_FUNCNAME_MACRO __func__ +#else +#ifdef HAVE_FUNCNAME__FUNCTION +#define PG_FUNCNAME_MACRO __FUNCTION__ +#else +#define PG_FUNCNAME_MACRO NULL +#endif +#endif + +/* ---------------------------------------------------------------- + * Section 2: bool, true, false, TRUE, FALSE, NULL + * ---------------------------------------------------------------- + */ + +/* + * bool + * Boolean value, either true or false. + * + * XXX for C++ compilers, we assume the compiler has a compatible + * built-in definition of bool. + */ + +#ifndef __cplusplus + +#ifndef bool +typedef char bool; +#endif + +#ifndef true +#define true ((bool) 1) +#endif + +#ifndef false +#define false ((bool) 0) +#endif +#endif /* not C++ */ + +typedef bool *BoolPtr; + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +/* + * NULL + * Null pointer. + */ +#ifndef NULL +#define NULL ((void *) 0) +#endif + + +/* ---------------------------------------------------------------- + * Section 3: standard system types + * ---------------------------------------------------------------- + */ + +/* + * Pointer + * Variable holding address of any memory resident object. + * + * XXX Pointer arithmetic is done with this, so it can't be void * + * under "true" ANSI compilers. + */ +typedef char *Pointer; + +/* + * intN + * Signed integer, EXACTLY N BITS IN SIZE, + * used for numerical computations and the + * frontend/backend protocol. + */ +#ifndef HAVE_INT8 +typedef signed char int8; /* == 8 bits */ +typedef signed short int16; /* == 16 bits */ +typedef signed int int32; /* == 32 bits */ +#endif /* not HAVE_INT8 */ + +/* + * uintN + * Unsigned integer, EXACTLY N BITS IN SIZE, + * used for numerical computations and the + * frontend/backend protocol. + */ +#ifndef HAVE_UINT8 +typedef unsigned char uint8; /* == 8 bits */ +typedef unsigned short uint16; /* == 16 bits */ +typedef unsigned int uint32; /* == 32 bits */ +#endif /* not HAVE_UINT8 */ + +/* + * bitsN + * Unit of bitwise operation, AT LEAST N BITS IN SIZE. + */ +typedef uint8 bits8; /* >= 8 bits */ +typedef uint16 bits16; /* >= 16 bits */ +typedef uint32 bits32; /* >= 32 bits */ + +/* + * 64-bit integers + */ +#ifdef HAVE_LONG_INT_64 +/* Plain "long int" fits, use it */ + +#ifndef HAVE_INT64 +typedef long int int64; +#endif +#ifndef HAVE_UINT64 +typedef unsigned long int uint64; +#endif +#elif defined(HAVE_LONG_LONG_INT_64) +/* We have working support for "long long int", use that */ + +#ifndef HAVE_INT64 +typedef long long int int64; +#endif +#ifndef HAVE_UINT64 +typedef unsigned long long int uint64; +#endif +#else +/* neither HAVE_LONG_INT_64 nor HAVE_LONG_LONG_INT_64 */ +#error must have a working 64-bit integer datatype +#endif + +/* Decide if we need to decorate 64-bit constants */ +#ifdef HAVE_LL_CONSTANTS +#define INT64CONST(x) ((int64) x##LL) +#define UINT64CONST(x) ((uint64) x##ULL) +#else +#define INT64CONST(x) ((int64) x) +#define UINT64CONST(x) ((uint64) x) +#endif + +/* snprintf format strings to use for 64-bit integers */ +#define INT64_FORMAT "%" INT64_MODIFIER "d" +#define UINT64_FORMAT "%" INT64_MODIFIER "u" + +/* + * 128-bit signed and unsigned integers + * There currently is only a limited support for the type. E.g. 128bit + * literals and snprintf are not supported; but math is. + */ +#if defined(PG_INT128_TYPE) +#define HAVE_INT128 +typedef PG_INT128_TYPE int128; +typedef unsigned PG_INT128_TYPE uint128; +#endif + +/* + * stdint.h limits aren't guaranteed to be present and aren't guaranteed to + * have compatible types with our fixed width types. So just define our own. + */ +#define PG_INT8_MIN (-0x7F-1) +#define PG_INT8_MAX (0x7F) +#define PG_UINT8_MAX (0xFF) +#define PG_INT16_MIN (-0x7FFF-1) +#define PG_INT16_MAX (0x7FFF) +#define PG_UINT16_MAX (0xFFFF) +#define PG_INT32_MIN (-0x7FFFFFFF-1) +#define PG_INT32_MAX (0x7FFFFFFF) +#define PG_UINT32_MAX (0xFFFFFFFF) +#define PG_INT64_MIN (-INT64CONST(0x7FFFFFFFFFFFFFFF) - 1) +#define PG_INT64_MAX INT64CONST(0x7FFFFFFFFFFFFFFF) +#define PG_UINT64_MAX UINT64CONST(0xFFFFFFFFFFFFFFFF) + +/* Select timestamp representation (float8 or int64) */ +#ifdef USE_INTEGER_DATETIMES +#define HAVE_INT64_TIMESTAMP +#endif + +/* + * Size + * Size of any memory resident object, as returned by sizeof. + */ +typedef size_t Size; + +/* + * Index + * Index into any memory resident array. + * + * Note: + * Indices are non negative. + */ +typedef unsigned int Index; + +/* + * Offset + * Offset into any memory resident array. + * + * Note: + * This differs from an Index in that an Index is always + * non negative, whereas Offset may be negative. + */ +typedef signed int Offset; + +/* + * Common Postgres datatype names (as used in the catalogs) + */ +typedef float float4; +typedef double float8; + +/* + * Oid, RegProcedure, TransactionId, SubTransactionId, MultiXactId, + * CommandId + */ + +/* typedef Oid is in postgres_ext.h */ + +/* + * regproc is the type name used in the include/catalog headers, but + * RegProcedure is the preferred name in C code. + */ +typedef Oid regproc; +typedef regproc RegProcedure; + +typedef uint32 TransactionId; + +typedef uint32 LocalTransactionId; + +typedef uint32 SubTransactionId; + +#define InvalidSubTransactionId ((SubTransactionId) 0) +#define TopSubTransactionId ((SubTransactionId) 1) + +/* MultiXactId must be equivalent to TransactionId, to fit in t_xmax */ +typedef TransactionId MultiXactId; + +typedef uint32 MultiXactOffset; + +typedef uint32 CommandId; + +#define FirstCommandId ((CommandId) 0) +#define InvalidCommandId (~(CommandId)0) + +/* + * Array indexing support + */ +#define MAXDIM 6 +typedef struct +{ + int indx[MAXDIM]; +} IntArray; + +/* ---------------- + * Variable-length datatypes all share the 'struct varlena' header. + * + * NOTE: for TOASTable types, this is an oversimplification, since the value + * may be compressed or moved out-of-line. However datatype-specific routines + * are mostly content to deal with de-TOASTed values only, and of course + * client-side routines should never see a TOASTed value. But even in a + * de-TOASTed value, beware of touching vl_len_ directly, as its representation + * is no longer convenient. It's recommended that code always use the VARDATA, + * VARSIZE, and SET_VARSIZE macros instead of relying on direct mentions of + * the struct fields. See postgres.h for details of the TOASTed form. + * ---------------- + */ +struct varlena +{ + char vl_len_[4]; /* Do not touch this field directly! */ + char vl_dat[FLEXIBLE_ARRAY_MEMBER]; /* Data content is here */ +}; + +#define VARHDRSZ ((int32) sizeof(int32)) + +/* + * These widely-used datatypes are just a varlena header and the data bytes. + * There is no terminating null or anything like that --- the data length is + * always VARSIZE(ptr) - VARHDRSZ. + */ +typedef struct varlena bytea; +typedef struct varlena text; +typedef struct varlena BpChar; /* blank-padded char, ie SQL char(n) */ +typedef struct varlena VarChar; /* var-length char, ie SQL varchar(n) */ + +/* + * Specialized array types. These are physically laid out just the same + * as regular arrays (so that the regular array subscripting code works + * with them). They exist as distinct types mostly for historical reasons: + * they have nonstandard I/O behavior which we don't want to change for fear + * of breaking applications that look at the system catalogs. There is also + * an implementation issue for oidvector: it's part of the primary key for + * pg_proc, and we can't use the normal btree array support routines for that + * without circularity. + */ +typedef struct +{ + int32 vl_len_; /* these fields must match ArrayType! */ + int ndim; /* always 1 for int2vector */ + int32 dataoffset; /* always 0 for int2vector */ + Oid elemtype; + int dim1; + int lbound1; + int16 values[FLEXIBLE_ARRAY_MEMBER]; +} int2vector; + +typedef struct +{ + int32 vl_len_; /* these fields must match ArrayType! */ + int ndim; /* always 1 for oidvector */ + int32 dataoffset; /* always 0 for oidvector */ + Oid elemtype; + int dim1; + int lbound1; + Oid values[FLEXIBLE_ARRAY_MEMBER]; +} oidvector; + +/* + * Representation of a Name: effectively just a C string, but null-padded to + * exactly NAMEDATALEN bytes. The use of a struct is historical. + */ +typedef struct nameData +{ + char data[NAMEDATALEN]; +} NameData; +typedef NameData *Name; + +#define NameStr(name) ((name).data) + +/* + * Support macros for escaping strings. escape_backslash should be TRUE + * if generating a non-standard-conforming string. Prefixing a string + * with ESCAPE_STRING_SYNTAX guarantees it is non-standard-conforming. + * Beware of multiple evaluation of the "ch" argument! + */ +#define SQL_STR_DOUBLE(ch, escape_backslash) \ + ((ch) == '\'' || ((ch) == '\\' && (escape_backslash))) + +#define ESCAPE_STRING_SYNTAX 'E' + +/* ---------------------------------------------------------------- + * Section 4: IsValid macros for system types + * ---------------------------------------------------------------- + */ +/* + * BoolIsValid + * True iff bool is valid. + */ +#define BoolIsValid(boolean) ((boolean) == false || (boolean) == true) + +/* + * PointerIsValid + * True iff pointer is valid. + */ +#define PointerIsValid(pointer) ((const void*)(pointer) != NULL) + +/* + * PointerIsAligned + * True iff pointer is properly aligned to point to the given type. + */ +#define PointerIsAligned(pointer, type) \ + (((uintptr_t)(pointer) % (sizeof (type))) == 0) + +#define OidIsValid(objectId) ((bool) ((objectId) != InvalidOid)) + +#define RegProcedureIsValid(p) OidIsValid(p) + + +/* ---------------------------------------------------------------- + * Section 5: offsetof, lengthof, endof, alignment + * ---------------------------------------------------------------- + */ +/* + * offsetof + * Offset of a structure/union field within that structure/union. + * + * XXX This is supposed to be part of stddef.h, but isn't on + * some systems (like SunOS 4). + */ +#ifndef offsetof +#define offsetof(type, field) ((long) &((type *)0)->field) +#endif /* offsetof */ + +/* + * lengthof + * Number of elements in an array. + */ +#define lengthof(array) (sizeof (array) / sizeof ((array)[0])) + +/* + * endof + * Address of the element one past the last in an array. + */ +#define endof(array) (&(array)[lengthof(array)]) + +/* ---------------- + * Alignment macros: align a length or address appropriately for a given type. + * The fooALIGN() macros round up to a multiple of the required alignment, + * while the fooALIGN_DOWN() macros round down. The latter are more useful + * for problems like "how many X-sized structures will fit in a page?". + * + * NOTE: TYPEALIGN[_DOWN] will not work if ALIGNVAL is not a power of 2. + * That case seems extremely unlikely to be needed in practice, however. + * ---------------- + */ + +#define TYPEALIGN(ALIGNVAL,LEN) \ + (((uintptr_t) (LEN) + ((ALIGNVAL) - 1)) & ~((uintptr_t) ((ALIGNVAL) - 1))) + +#define SHORTALIGN(LEN) TYPEALIGN(ALIGNOF_SHORT, (LEN)) +#define INTALIGN(LEN) TYPEALIGN(ALIGNOF_INT, (LEN)) +#define LONGALIGN(LEN) TYPEALIGN(ALIGNOF_LONG, (LEN)) +#define DOUBLEALIGN(LEN) TYPEALIGN(ALIGNOF_DOUBLE, (LEN)) +#define MAXALIGN(LEN) TYPEALIGN(MAXIMUM_ALIGNOF, (LEN)) +/* MAXALIGN covers only built-in types, not buffers */ +#define BUFFERALIGN(LEN) TYPEALIGN(ALIGNOF_BUFFER, (LEN)) +#define CACHELINEALIGN(LEN) TYPEALIGN(PG_CACHE_LINE_SIZE, (LEN)) + +#define TYPEALIGN_DOWN(ALIGNVAL,LEN) \ + (((uintptr_t) (LEN)) & ~((uintptr_t) ((ALIGNVAL) - 1))) + +#define SHORTALIGN_DOWN(LEN) TYPEALIGN_DOWN(ALIGNOF_SHORT, (LEN)) +#define INTALIGN_DOWN(LEN) TYPEALIGN_DOWN(ALIGNOF_INT, (LEN)) +#define LONGALIGN_DOWN(LEN) TYPEALIGN_DOWN(ALIGNOF_LONG, (LEN)) +#define DOUBLEALIGN_DOWN(LEN) TYPEALIGN_DOWN(ALIGNOF_DOUBLE, (LEN)) +#define MAXALIGN_DOWN(LEN) TYPEALIGN_DOWN(MAXIMUM_ALIGNOF, (LEN)) + +/* + * The above macros will not work with types wider than uintptr_t, like with + * uint64 on 32-bit platforms. That's not problem for the usual use where a + * pointer or a length is aligned, but for the odd case that you need to + * align something (potentially) wider, use TYPEALIGN64. + */ +#define TYPEALIGN64(ALIGNVAL,LEN) \ + (((uint64) (LEN) + ((ALIGNVAL) - 1)) & ~((uint64) ((ALIGNVAL) - 1))) + +/* we don't currently need wider versions of the other ALIGN macros */ +#define MAXALIGN64(LEN) TYPEALIGN64(MAXIMUM_ALIGNOF, (LEN)) + +/* ---------------- + * Attribute macros + * + * GCC: https://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html + * GCC: https://gcc.gnu.org/onlinedocs/gcc/Type-Attributes.html + * Sunpro: https://docs.oracle.com/cd/E18659_01/html/821-1384/gjzke.html + * XLC: http://www-01.ibm.com/support/knowledgecenter/SSGH2K_11.1.0/com.ibm.xlc111.aix.doc/language_ref/function_attributes.html + * XLC: http://www-01.ibm.com/support/knowledgecenter/SSGH2K_11.1.0/com.ibm.xlc111.aix.doc/language_ref/type_attrib.html + * ---------------- + */ + +/* only GCC supports the unused attribute */ +#ifdef __GNUC__ +#define pg_attribute_unused() __attribute__((unused)) +#else +#define pg_attribute_unused() +#endif + +/* GCC and XLC support format attributes */ +#if defined(__GNUC__) || defined(__IBMC__) +#define pg_attribute_format_arg(a) __attribute__((format_arg(a))) +#define pg_attribute_printf(f,a) __attribute__((format(PG_PRINTF_ATTRIBUTE, f, a))) +#else +#define pg_attribute_format_arg(a) +#define pg_attribute_printf(f,a) +#endif + +/* GCC, Sunpro and XLC support aligned, packed and noreturn */ +#if defined(__GNUC__) || defined(__SUNPRO_C) || defined(__IBMC__) +#define pg_attribute_aligned(a) __attribute__((aligned(a))) +#define pg_attribute_noreturn() __attribute__((noreturn)) +#define pg_attribute_packed() __attribute__((packed)) +#define HAVE_PG_ATTRIBUTE_NORETURN 1 +#else +/* + * NB: aligned and packed are not given default definitions because they + * affect code functionality; they *must* be implemented by the compiler + * if they are to be used. + */ +#define pg_attribute_noreturn() +#endif + +/* ---------------------------------------------------------------- + * Section 6: assertions + * ---------------------------------------------------------------- + */ + +/* + * USE_ASSERT_CHECKING, if defined, turns on all the assertions. + * - plai 9/5/90 + * + * It should _NOT_ be defined in releases or in benchmark copies + */ + +/* + * Assert() can be used in both frontend and backend code. In frontend code it + * just calls the standard assert, if it's available. If use of assertions is + * not configured, it does nothing. + */ +#ifndef USE_ASSERT_CHECKING + +#define Assert(condition) ((void)true) +#define AssertMacro(condition) ((void)true) +#define AssertArg(condition) ((void)true) +#define AssertState(condition) ((void)true) +#define AssertPointerAlignment(ptr, bndr) ((void)true) +#define Trap(condition, errorType) ((void)true) +#define TrapMacro(condition, errorType) (true) + +#elif defined(FRONTEND) + +#include +#define Assert(p) assert(p) +#define AssertMacro(p) ((void) assert(p)) +#define AssertArg(condition) assert(condition) +#define AssertState(condition) assert(condition) +#define AssertPointerAlignment(ptr, bndr) ((void)true) +#else /* USE_ASSERT_CHECKING && !FRONTEND */ + +/* + * Trap + * Generates an exception if the given condition is true. + */ +#define Trap(condition, errorType) \ + do { \ + if (condition) \ + ExceptionalCondition(CppAsString(condition), (errorType), \ + __FILE__, __LINE__); \ + } while (0) + +/* + * TrapMacro is the same as Trap but it's intended for use in macros: + * + * #define foo(x) (AssertMacro(x != 0), bar(x)) + * + * Isn't CPP fun? + */ +#define TrapMacro(condition, errorType) \ + ((bool) (! (condition) || \ + (ExceptionalCondition(CppAsString(condition), (errorType), \ + __FILE__, __LINE__), 0))) + +#define Assert(condition) \ + Trap(!(condition), "FailedAssertion") + +#define AssertMacro(condition) \ + ((void) TrapMacro(!(condition), "FailedAssertion")) + +#define AssertArg(condition) \ + Trap(!(condition), "BadArgument") + +#define AssertState(condition) \ + Trap(!(condition), "BadState") + +/* + * Check that `ptr' is `bndr' aligned. + */ +#define AssertPointerAlignment(ptr, bndr) \ + Trap(TYPEALIGN(bndr, (uintptr_t)(ptr)) != (uintptr_t)(ptr), \ + "UnalignedPointer") + +#endif /* USE_ASSERT_CHECKING && !FRONTEND */ + +/* + * Macros to support compile-time assertion checks. + * + * If the "condition" (a compile-time-constant expression) evaluates to false, + * throw a compile error using the "errmessage" (a string literal). + * + * gcc 4.6 and up supports _Static_assert(), but there are bizarre syntactic + * placement restrictions. These macros make it safe to use as a statement + * or in an expression, respectively. + * + * Otherwise we fall back on a kluge that assumes the compiler will complain + * about a negative width for a struct bit-field. This will not include a + * helpful error message, but it beats not getting an error at all. + */ +#ifdef HAVE__STATIC_ASSERT +#define StaticAssertStmt(condition, errmessage) \ + do { _Static_assert(condition, errmessage); } while(0) +#define StaticAssertExpr(condition, errmessage) \ + ({ StaticAssertStmt(condition, errmessage); true; }) +#else /* !HAVE__STATIC_ASSERT */ +#define StaticAssertStmt(condition, errmessage) \ + ((void) sizeof(struct { int static_assert_failure : (condition) ? 1 : -1; })) +#define StaticAssertExpr(condition, errmessage) \ + StaticAssertStmt(condition, errmessage) +#endif /* HAVE__STATIC_ASSERT */ + + +/* + * Compile-time checks that a variable (or expression) has the specified type. + * + * AssertVariableIsOfType() can be used as a statement. + * AssertVariableIsOfTypeMacro() is intended for use in macros, eg + * #define foo(x) (AssertVariableIsOfTypeMacro(x, int), bar(x)) + * + * If we don't have __builtin_types_compatible_p, we can still assert that + * the types have the same size. This is far from ideal (especially on 32-bit + * platforms) but it provides at least some coverage. + */ +#ifdef HAVE__BUILTIN_TYPES_COMPATIBLE_P +#define AssertVariableIsOfType(varname, typename) \ + StaticAssertStmt(__builtin_types_compatible_p(__typeof__(varname), typename), \ + CppAsString(varname) " does not have type " CppAsString(typename)) +#define AssertVariableIsOfTypeMacro(varname, typename) \ + ((void) StaticAssertExpr(__builtin_types_compatible_p(__typeof__(varname), typename), \ + CppAsString(varname) " does not have type " CppAsString(typename))) +#else /* !HAVE__BUILTIN_TYPES_COMPATIBLE_P */ +#define AssertVariableIsOfType(varname, typename) \ + StaticAssertStmt(sizeof(varname) == sizeof(typename), \ + CppAsString(varname) " does not have type " CppAsString(typename)) +#define AssertVariableIsOfTypeMacro(varname, typename) \ + ((void) StaticAssertExpr(sizeof(varname) == sizeof(typename), \ + CppAsString(varname) " does not have type " CppAsString(typename))) +#endif /* HAVE__BUILTIN_TYPES_COMPATIBLE_P */ + + +/* ---------------------------------------------------------------- + * Section 7: widely useful macros + * ---------------------------------------------------------------- + */ +/* + * Max + * Return the maximum of two numbers. + */ +#define Max(x, y) ((x) > (y) ? (x) : (y)) + +/* + * Min + * Return the minimum of two numbers. + */ +#define Min(x, y) ((x) < (y) ? (x) : (y)) + +/* + * Abs + * Return the absolute value of the argument. + */ +#define Abs(x) ((x) >= 0 ? (x) : -(x)) + +/* + * StrNCpy + * Like standard library function strncpy(), except that result string + * is guaranteed to be null-terminated --- that is, at most N-1 bytes + * of the source string will be kept. + * Also, the macro returns no result (too hard to do that without + * evaluating the arguments multiple times, which seems worse). + * + * BTW: when you need to copy a non-null-terminated string (like a text + * datum) and add a null, do not do it with StrNCpy(..., len+1). That + * might seem to work, but it fetches one byte more than there is in the + * text object. One fine day you'll have a SIGSEGV because there isn't + * another byte before the end of memory. Don't laugh, we've had real + * live bug reports from real live users over exactly this mistake. + * Do it honestly with "memcpy(dst,src,len); dst[len] = '\0';", instead. + */ +#define StrNCpy(dst,src,len) \ + do \ + { \ + char * _dst = (dst); \ + Size _len = (len); \ +\ + if (_len > 0) \ + { \ + strncpy(_dst, (src), _len); \ + _dst[_len-1] = '\0'; \ + } \ + } while (0) + + +/* Get a bit mask of the bits set in non-long aligned addresses */ +#define LONG_ALIGN_MASK (sizeof(long) - 1) + +/* + * MemSet + * Exactly the same as standard library function memset(), but considerably + * faster for zeroing small word-aligned structures (such as parsetree nodes). + * This has to be a macro because the main point is to avoid function-call + * overhead. However, we have also found that the loop is faster than + * native libc memset() on some platforms, even those with assembler + * memset() functions. More research needs to be done, perhaps with + * MEMSET_LOOP_LIMIT tests in configure. + */ +#define MemSet(start, val, len) \ + do \ + { \ + /* must be void* because we don't know if it is integer aligned yet */ \ + void *_vstart = (void *) (start); \ + int _val = (val); \ + Size _len = (len); \ +\ + if ((((uintptr_t) _vstart) & LONG_ALIGN_MASK) == 0 && \ + (_len & LONG_ALIGN_MASK) == 0 && \ + _val == 0 && \ + _len <= MEMSET_LOOP_LIMIT && \ + /* \ + * If MEMSET_LOOP_LIMIT == 0, optimizer should find \ + * the whole "if" false at compile time. \ + */ \ + MEMSET_LOOP_LIMIT != 0) \ + { \ + long *_start = (long *) _vstart; \ + long *_stop = (long *) ((char *) _start + _len); \ + while (_start < _stop) \ + *_start++ = 0; \ + } \ + else \ + memset(_vstart, _val, _len); \ + } while (0) + +/* + * MemSetAligned is the same as MemSet except it omits the test to see if + * "start" is word-aligned. This is okay to use if the caller knows a-priori + * that the pointer is suitably aligned (typically, because he just got it + * from palloc(), which always delivers a max-aligned pointer). + */ +#define MemSetAligned(start, val, len) \ + do \ + { \ + long *_start = (long *) (start); \ + int _val = (val); \ + Size _len = (len); \ +\ + if ((_len & LONG_ALIGN_MASK) == 0 && \ + _val == 0 && \ + _len <= MEMSET_LOOP_LIMIT && \ + MEMSET_LOOP_LIMIT != 0) \ + { \ + long *_stop = (long *) ((char *) _start + _len); \ + while (_start < _stop) \ + *_start++ = 0; \ + } \ + else \ + memset(_start, _val, _len); \ + } while (0) + + +/* + * MemSetTest/MemSetLoop are a variant version that allow all the tests in + * MemSet to be done at compile time in cases where "val" and "len" are + * constants *and* we know the "start" pointer must be word-aligned. + * If MemSetTest succeeds, then it is okay to use MemSetLoop, otherwise use + * MemSetAligned. Beware of multiple evaluations of the arguments when using + * this approach. + */ +#define MemSetTest(val, len) \ + ( ((len) & LONG_ALIGN_MASK) == 0 && \ + (len) <= MEMSET_LOOP_LIMIT && \ + MEMSET_LOOP_LIMIT != 0 && \ + (val) == 0 ) + +#define MemSetLoop(start, val, len) \ + do \ + { \ + long * _start = (long *) (start); \ + long * _stop = (long *) ((char *) _start + (Size) (len)); \ + \ + while (_start < _stop) \ + *_start++ = 0; \ + } while (0) + + +/* + * Mark a point as unreachable in a portable fashion. This should preferably + * be something that the compiler understands, to aid code generation. + * In assert-enabled builds, we prefer abort() for debugging reasons. + */ +#if defined(HAVE__BUILTIN_UNREACHABLE) && !defined(USE_ASSERT_CHECKING) +#define pg_unreachable() __builtin_unreachable() +#elif defined(_MSC_VER) && !defined(USE_ASSERT_CHECKING) +#define pg_unreachable() __assume(0) +#else +#define pg_unreachable() abort() +#endif + + +/* ---------------------------------------------------------------- + * Section 8: random stuff + * ---------------------------------------------------------------- + */ + +/* msb for char */ +#define HIGHBIT (0x80) +#define IS_HIGHBIT_SET(ch) ((unsigned char)(ch) & HIGHBIT) + +#define STATUS_OK (0) +#define STATUS_ERROR (-1) +#define STATUS_EOF (-2) +#define STATUS_FOUND (1) +#define STATUS_WAITING (2) + + +/* + * Append PG_USED_FOR_ASSERTS_ONLY to definitions of variables that are only + * used in assert-enabled builds, to avoid compiler warnings about unused + * variables in assert-disabled builds. + */ +#ifdef USE_ASSERT_CHECKING +#define PG_USED_FOR_ASSERTS_ONLY +#else +#define PG_USED_FOR_ASSERTS_ONLY pg_attribute_unused() +#endif + + +/* gettext domain name mangling */ + +/* + * To better support parallel installations of major PostgreSQL + * versions as well as parallel installations of major library soname + * versions, we mangle the gettext domain name by appending those + * version numbers. The coding rule ought to be that wherever the + * domain name is mentioned as a literal, it must be wrapped into + * PG_TEXTDOMAIN(). The macros below do not work on non-literals; but + * that is somewhat intentional because it avoids having to worry + * about multiple states of premangling and postmangling as the values + * are being passed around. + * + * Make sure this matches the installation rules in nls-global.mk. + */ + +/* need a second indirection because we want to stringize the macro value, not the name */ +#define CppAsString2(x) CppAsString(x) + +#ifdef SO_MAJOR_VERSION +#define PG_TEXTDOMAIN(domain) (domain CppAsString2(SO_MAJOR_VERSION) "-" PG_MAJORVERSION) +#else +#define PG_TEXTDOMAIN(domain) (domain "-" PG_MAJORVERSION) +#endif + + +/* ---------------------------------------------------------------- + * Section 9: system-specific hacks + * + * This should be limited to things that absolutely have to be + * included in every source file. The port-specific header file + * is usually a better place for this sort of thing. + * ---------------------------------------------------------------- + */ + +/* + * NOTE: this is also used for opening text files. + * WIN32 treats Control-Z as EOF in files opened in text mode. + * Therefore, we open files in binary mode on Win32 so we can read + * literal control-Z. The other affect is that we see CRLF, but + * that is OK because we can already handle those cleanly. + */ +#if defined(WIN32) || defined(__CYGWIN__) +#define PG_BINARY O_BINARY +#define PG_BINARY_A "ab" +#define PG_BINARY_R "rb" +#define PG_BINARY_W "wb" +#else +#define PG_BINARY 0 +#define PG_BINARY_A "a" +#define PG_BINARY_R "r" +#define PG_BINARY_W "w" +#endif + +/* + * Provide prototypes for routines not present in a particular machine's + * standard C library. + */ + +#if !HAVE_DECL_SNPRINTF +extern int snprintf(char *str, size_t count, const char *fmt,...) pg_attribute_printf(3, 4); +#endif + +#if !HAVE_DECL_VSNPRINTF +extern int vsnprintf(char *str, size_t count, const char *fmt, va_list args); +#endif + +#if !defined(HAVE_MEMMOVE) && !defined(memmove) +#define memmove(d, s, c) bcopy(s, d, c) +#endif + +/* no special DLL markers on most ports */ +#ifndef PGDLLIMPORT +#define PGDLLIMPORT +#endif +#ifndef PGDLLEXPORT +#define PGDLLEXPORT +#endif + +/* + * The following is used as the arg list for signal handlers. Any ports + * that take something other than an int argument should override this in + * their pg_config_os.h file. Note that variable names are required + * because it is used in both the prototypes as well as the definitions. + * Note also the long name. We expect that this won't collide with + * other names causing compiler warnings. + */ + +#ifndef SIGNAL_ARGS +#define SIGNAL_ARGS int postgres_signal_arg +#endif + +/* + * When there is no sigsetjmp, its functionality is provided by plain + * setjmp. Incidentally, nothing provides setjmp's functionality in + * that case. We now support the case only on Windows. + */ +#ifdef WIN32 +#define sigjmp_buf jmp_buf +#define sigsetjmp(x,y) setjmp(x) +#define siglongjmp longjmp +#endif + +#if defined(HAVE_FDATASYNC) && !HAVE_DECL_FDATASYNC +extern int fdatasync(int fildes); +#endif + +/* If strtoq() exists, rename it to the more standard strtoll() */ +#if defined(HAVE_LONG_LONG_INT_64) && !defined(HAVE_STRTOLL) && defined(HAVE_STRTOQ) +#define strtoll strtoq +#define HAVE_STRTOLL 1 +#endif + +/* If strtouq() exists, rename it to the more standard strtoull() */ +#if defined(HAVE_LONG_LONG_INT_64) && !defined(HAVE_STRTOULL) && defined(HAVE_STRTOUQ) +#define strtoull strtouq +#define HAVE_STRTOULL 1 +#endif + +/* + * We assume if we have these two functions, we have their friends too, and + * can use the wide-character functions. + */ +#if defined(HAVE_WCSTOMBS) && defined(HAVE_TOWLOWER) +#define USE_WIDE_UPPER_LOWER +#endif + +/* EXEC_BACKEND defines */ +#ifdef EXEC_BACKEND +#define NON_EXEC_STATIC +#else +#define NON_EXEC_STATIC static +#endif + +/* /port compatibility functions */ +#include "port.h" + +#endif /* C_H */ diff --git a/libpq/postgresql/common/fe_memutils.h b/libpq/postgresql/common/fe_memutils.h new file mode 100644 index 0000000..b4ce3d4 --- /dev/null +++ b/libpq/postgresql/common/fe_memutils.h @@ -0,0 +1,44 @@ +/* + * fe_memutils.h + * memory management support for frontend code + * + * Copyright (c) 2003-2016, PostgreSQL Global Development Group + * + * src/include/common/fe_memutils.h + */ +#ifndef FE_MEMUTILS_H +#define FE_MEMUTILS_H + +/* + * Flags for pg_malloc_extended and palloc_extended, deliberately named + * the same as the backend flags. + */ +#define MCXT_ALLOC_HUGE 0x01 /* allow huge allocation (> 1 GB) not + * actually used for frontends */ +#define MCXT_ALLOC_NO_OOM 0x02 /* no failure if out-of-memory */ +#define MCXT_ALLOC_ZERO 0x04 /* zero allocated memory */ + +/* + * "Safe" memory allocation functions --- these exit(1) on failure + * (except pg_malloc_extended with MCXT_ALLOC_NO_OOM) + */ +extern char *pg_strdup(const char *in); +extern void *pg_malloc(size_t size); +extern void *pg_malloc0(size_t size); +extern void *pg_malloc_extended(size_t size, int flags); +extern void *pg_realloc(void *pointer, size_t size); +extern void pg_free(void *pointer); + +/* Equivalent functions, deliberately named the same as backend functions */ +extern char *pstrdup(const char *in); +extern void *palloc(Size size); +extern void *palloc0(Size size); +extern void *palloc_extended(Size size, int flags); +extern void *repalloc(void *pointer, Size size); +extern void pfree(void *pointer); + +/* sprintf into a palloc'd buffer --- these are in psprintf.c */ +extern char *psprintf(const char *fmt,...) pg_attribute_printf(1, 2); +extern size_t pvsnprintf(char *buf, size_t len, const char *fmt, va_list args) pg_attribute_printf(3, 0); + +#endif /* FE_MEMUTILS_H */ diff --git a/libpq/postgresql/getaddrinfo.h b/libpq/postgresql/getaddrinfo.h new file mode 100644 index 0000000..46aad59 --- /dev/null +++ b/libpq/postgresql/getaddrinfo.h @@ -0,0 +1,164 @@ +/*------------------------------------------------------------------------- + * + * getaddrinfo.h + * Support getaddrinfo() on platforms that don't have it. + * + * Note: we use our own routines on platforms that don't HAVE_STRUCT_ADDRINFO, + * whether or not the library routine getaddrinfo() can be found. This + * policy is needed because on some platforms a manually installed libbind.a + * may provide getaddrinfo(), yet the system headers may not provide the + * struct definitions needed to call it. To avoid conflict with the libbind + * definition in such cases, we rename our routines to pg_xxx() via macros. + * + * This code will also work on platforms where struct addrinfo is defined + * in the system headers but no getaddrinfo() can be located. + * + * Copyright (c) 2003-2016, PostgreSQL Global Development Group + * + * src/include/getaddrinfo.h + * + *------------------------------------------------------------------------- + */ +#ifndef GETADDRINFO_H +#define GETADDRINFO_H + +#include +#include + + +/* Various macros that ought to be in , but might not be */ + +#ifndef EAI_FAIL +#ifndef WIN32 +#define EAI_BADFLAGS (-1) +#define EAI_NONAME (-2) +#define EAI_AGAIN (-3) +#define EAI_FAIL (-4) +#define EAI_FAMILY (-6) +#define EAI_SOCKTYPE (-7) +#define EAI_SERVICE (-8) +#define EAI_MEMORY (-10) +#define EAI_SYSTEM (-11) +#else /* WIN32 */ +#ifdef WIN32_ONLY_COMPILER +#ifndef WSA_NOT_ENOUGH_MEMORY +#define WSA_NOT_ENOUGH_MEMORY (WSAENOBUFS) +#endif +#ifndef __BORLANDC__ +#define WSATYPE_NOT_FOUND (WSABASEERR+109) +#endif +#endif +#define EAI_AGAIN WSATRY_AGAIN +#define EAI_BADFLAGS WSAEINVAL +#define EAI_FAIL WSANO_RECOVERY +#define EAI_FAMILY WSAEAFNOSUPPORT +#define EAI_MEMORY WSA_NOT_ENOUGH_MEMORY +#define EAI_NODATA WSANO_DATA +#define EAI_NONAME WSAHOST_NOT_FOUND +#define EAI_SERVICE WSATYPE_NOT_FOUND +#define EAI_SOCKTYPE WSAESOCKTNOSUPPORT +#endif /* !WIN32 */ +#endif /* !EAI_FAIL */ + +#ifndef AI_PASSIVE +#define AI_PASSIVE 0x0001 +#endif + +#ifndef AI_NUMERICHOST +/* + * some platforms don't support AI_NUMERICHOST; define as zero if using + * the system version of getaddrinfo... + */ +#if defined(HAVE_STRUCT_ADDRINFO) && defined(HAVE_GETADDRINFO) +#define AI_NUMERICHOST 0 +#else +#define AI_NUMERICHOST 0x0004 +#endif +#endif + +#ifndef NI_NUMERICHOST +#define NI_NUMERICHOST 1 +#endif +#ifndef NI_NUMERICSERV +#define NI_NUMERICSERV 2 +#endif +#ifndef NI_NAMEREQD +#define NI_NAMEREQD 4 +#endif + +#ifndef NI_MAXHOST +#define NI_MAXHOST 1025 +#endif +#ifndef NI_MAXSERV +#define NI_MAXSERV 32 +#endif + + +#ifndef HAVE_STRUCT_ADDRINFO + +#ifndef WIN32 +struct addrinfo +{ + int ai_flags; + int ai_family; + int ai_socktype; + int ai_protocol; + size_t ai_addrlen; + struct sockaddr *ai_addr; + char *ai_canonname; + struct addrinfo *ai_next; +}; +#else +/* + * The order of the structure elements on Win32 doesn't match the + * order specified in the standard, but we have to match it for + * IPv6 to work. + */ +struct addrinfo +{ + int ai_flags; + int ai_family; + int ai_socktype; + int ai_protocol; + size_t ai_addrlen; + char *ai_canonname; + struct sockaddr *ai_addr; + struct addrinfo *ai_next; +}; +#endif +#endif /* HAVE_STRUCT_ADDRINFO */ + + +#ifndef HAVE_GETADDRINFO + +/* Rename private copies per comments above */ +#ifdef getaddrinfo +#undef getaddrinfo +#endif +#define getaddrinfo pg_getaddrinfo + +#ifdef freeaddrinfo +#undef freeaddrinfo +#endif +#define freeaddrinfo pg_freeaddrinfo + +#ifdef gai_strerror +#undef gai_strerror +#endif +#define gai_strerror pg_gai_strerror + +#ifdef getnameinfo +#undef getnameinfo +#endif +#define getnameinfo pg_getnameinfo + +extern int getaddrinfo(const char *node, const char *service, + const struct addrinfo * hints, struct addrinfo ** res); +extern void freeaddrinfo(struct addrinfo * res); +extern const char *gai_strerror(int errcode); +extern int getnameinfo(const struct sockaddr * sa, int salen, + char *node, int nodelen, + char *service, int servicelen, int flags); +#endif /* HAVE_GETADDRINFO */ + +#endif /* GETADDRINFO_H */ diff --git a/libpq/postgresql/libpq/ip.h b/libpq/postgresql/libpq/ip.h new file mode 100644 index 0000000..ce9bc6e --- /dev/null +++ b/libpq/postgresql/libpq/ip.h @@ -0,0 +1,51 @@ +/*------------------------------------------------------------------------- + * + * ip.h + * Definitions for IPv6-aware network access. + * + * These definitions are used by both frontend and backend code. Be careful + * what you include here! + * + * Copyright (c) 2003-2016, PostgreSQL Global Development Group + * + * src/include/libpq/ip.h + * + *------------------------------------------------------------------------- + */ +#ifndef IP_H +#define IP_H + +#include "getaddrinfo.h" /* pgrminclude ignore */ +#include "libpq/pqcomm.h" /* pgrminclude ignore */ + + +#ifdef HAVE_UNIX_SOCKETS +#define IS_AF_UNIX(fam) ((fam) == AF_UNIX) +#else +#define IS_AF_UNIX(fam) (0) +#endif + +typedef void (*PgIfAddrCallback) (struct sockaddr * addr, + struct sockaddr * netmask, + void *cb_data); + +extern int pg_getaddrinfo_all(const char *hostname, const char *servname, + const struct addrinfo * hintp, + struct addrinfo ** result); +extern void pg_freeaddrinfo_all(int hint_ai_family, struct addrinfo * ai); + +extern int pg_getnameinfo_all(const struct sockaddr_storage * addr, int salen, + char *node, int nodelen, + char *service, int servicelen, + int flags); + +extern int pg_range_sockaddr(const struct sockaddr_storage * addr, + const struct sockaddr_storage * netaddr, + const struct sockaddr_storage * netmask); + +extern int pg_sockaddr_cidr_mask(struct sockaddr_storage * mask, + char *numbits, int family); + +extern int pg_foreach_ifaddr(PgIfAddrCallback callback, void *cb_data); + +#endif /* IP_H */ diff --git a/libpq/postgresql/libpq/libpq-fs.h b/libpq/postgresql/libpq/libpq-fs.h new file mode 100644 index 0000000..5134ce6 --- /dev/null +++ b/libpq/postgresql/libpq/libpq-fs.h @@ -0,0 +1,24 @@ +/*------------------------------------------------------------------------- + * + * libpq-fs.h + * definitions for using Inversion file system routines (ie, large objects) + * + * + * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/libpq/libpq-fs.h + * + *------------------------------------------------------------------------- + */ +#ifndef LIBPQ_FS_H +#define LIBPQ_FS_H + +/* + * Read/write mode flags for inversion (large object) calls + */ + +#define INV_WRITE 0x00020000 +#define INV_READ 0x00040000 + +#endif /* LIBPQ_FS_H */ diff --git a/libpq/postgresql/libpq/md5.h b/libpq/postgresql/libpq/md5.h new file mode 100644 index 0000000..f3eec8b --- /dev/null +++ b/libpq/postgresql/libpq/md5.h @@ -0,0 +1,30 @@ +/*------------------------------------------------------------------------- + * + * md5.h + * Interface to libpq/md5.c + * + * These definitions are needed by both frontend and backend code to work + * with MD5-encrypted passwords. + * + * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/libpq/md5.h + * + *------------------------------------------------------------------------- + */ +#ifndef PG_MD5_H +#define PG_MD5_H + +#define MD5_PASSWD_LEN 35 + +#define isMD5(passwd) (strncmp(passwd, "md5", 3) == 0 && \ + strlen(passwd) == MD5_PASSWD_LEN) + + +extern bool pg_md5_hash(const void *buff, size_t len, char *hexsum); +extern bool pg_md5_binary(const void *buff, size_t len, void *outbuf); +extern bool pg_md5_encrypt(const char *passwd, const char *salt, + size_t salt_len, char *buf); + +#endif diff --git a/libpq/postgresql/libpq/pqcomm.h b/libpq/postgresql/libpq/pqcomm.h new file mode 100644 index 0000000..1d063d1 --- /dev/null +++ b/libpq/postgresql/libpq/pqcomm.h @@ -0,0 +1,206 @@ +/*------------------------------------------------------------------------- + * + * pqcomm.h + * Definitions common to frontends and backends. + * + * NOTE: for historical reasons, this does not correspond to pqcomm.c. + * pqcomm.c's routines are declared in libpq.h. + * + * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/libpq/pqcomm.h + * + *------------------------------------------------------------------------- + */ +#ifndef PQCOMM_H +#define PQCOMM_H + +#include +#include +#ifdef HAVE_SYS_UN_H +#include +#endif +#include + +#ifdef HAVE_STRUCT_SOCKADDR_STORAGE + +#ifndef HAVE_STRUCT_SOCKADDR_STORAGE_SS_FAMILY +#ifdef HAVE_STRUCT_SOCKADDR_STORAGE___SS_FAMILY +#define ss_family __ss_family +#else +#error struct sockaddr_storage does not provide an ss_family member +#endif +#endif + +#ifdef HAVE_STRUCT_SOCKADDR_STORAGE___SS_LEN +#define ss_len __ss_len +#define HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN 1 +#endif +#else /* !HAVE_STRUCT_SOCKADDR_STORAGE */ + +/* Define a struct sockaddr_storage if we don't have one. */ + +struct sockaddr_storage +{ + union + { + struct sockaddr sa; /* get the system-dependent fields */ + int64 ss_align; /* ensures struct is properly aligned */ + char ss_pad[128]; /* ensures struct has desired size */ + } ss_stuff; +}; + +#define ss_family ss_stuff.sa.sa_family +/* It should have an ss_len field if sockaddr has sa_len. */ +#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN +#define ss_len ss_stuff.sa.sa_len +#define HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN 1 +#endif +#endif /* HAVE_STRUCT_SOCKADDR_STORAGE */ + +typedef struct +{ + struct sockaddr_storage addr; + ACCEPT_TYPE_ARG3 salen; +} SockAddr; + +/* Configure the UNIX socket location for the well known port. */ + +#define UNIXSOCK_PATH(path, port, sockdir) \ + snprintf(path, sizeof(path), "%s/.s.PGSQL.%d", \ + ((sockdir) && *(sockdir) != '\0') ? (sockdir) : \ + DEFAULT_PGSOCKET_DIR, \ + (port)) + +/* + * The maximum workable length of a socket path is what will fit into + * struct sockaddr_un. This is usually only 100 or so bytes :-(. + * + * For consistency, always pass a MAXPGPATH-sized buffer to UNIXSOCK_PATH(), + * then complain if the resulting string is >= UNIXSOCK_PATH_BUFLEN bytes. + * (Because the standard API for getaddrinfo doesn't allow it to complain in + * a useful way when the socket pathname is too long, we have to test for + * this explicitly, instead of just letting the subroutine return an error.) + */ +#define UNIXSOCK_PATH_BUFLEN sizeof(((struct sockaddr_un *) NULL)->sun_path) + + +/* + * These manipulate the frontend/backend protocol version number. + * + * The major number should be incremented for incompatible changes. The minor + * number should be incremented for compatible changes (eg. additional + * functionality). + * + * If a backend supports version m.n of the protocol it must actually support + * versions m.[0..n]. Backend support for version m-1 can be dropped after a + * `reasonable' length of time. + * + * A frontend isn't required to support anything other than the current + * version. + */ + +#define PG_PROTOCOL_MAJOR(v) ((v) >> 16) +#define PG_PROTOCOL_MINOR(v) ((v) & 0x0000ffff) +#define PG_PROTOCOL(m,n) (((m) << 16) | (n)) + +/* The earliest and latest frontend/backend protocol version supported. */ + +#define PG_PROTOCOL_EARLIEST PG_PROTOCOL(1,0) +#define PG_PROTOCOL_LATEST PG_PROTOCOL(3,0) + +typedef uint32 ProtocolVersion; /* FE/BE protocol version number */ + +typedef ProtocolVersion MsgType; + + +/* + * Packet lengths are 4 bytes in network byte order. + * + * The initial length is omitted from the packet layouts appearing below. + */ + +typedef uint32 PacketLen; + + +/* + * Old-style startup packet layout with fixed-width fields. This is used in + * protocol 1.0 and 2.0, but not in later versions. Note that the fields + * in this layout are '\0' terminated only if there is room. + */ + +#define SM_DATABASE 64 +#define SM_USER 32 +/* We append database name if db_user_namespace true. */ +#define SM_DATABASE_USER (SM_DATABASE+SM_USER+1) /* +1 for @ */ +#define SM_OPTIONS 64 +#define SM_UNUSED 64 +#define SM_TTY 64 + +typedef struct StartupPacket +{ + ProtocolVersion protoVersion; /* Protocol version */ + char database[SM_DATABASE]; /* Database name */ + /* Db_user_namespace appends dbname */ + char user[SM_USER]; /* User name */ + char options[SM_OPTIONS]; /* Optional additional args */ + char unused[SM_UNUSED]; /* Unused */ + char tty[SM_TTY]; /* Tty for debug output */ +} StartupPacket; + +extern bool Db_user_namespace; + +/* + * In protocol 3.0 and later, the startup packet length is not fixed, but + * we set an arbitrary limit on it anyway. This is just to prevent simple + * denial-of-service attacks via sending enough data to run the server + * out of memory. + */ +#define MAX_STARTUP_PACKET_LENGTH 10000 + + +/* These are the authentication request codes sent by the backend. */ + +#define AUTH_REQ_OK 0 /* User is authenticated */ +#define AUTH_REQ_KRB4 1 /* Kerberos V4. Not supported any more. */ +#define AUTH_REQ_KRB5 2 /* Kerberos V5. Not supported any more. */ +#define AUTH_REQ_PASSWORD 3 /* Password */ +#define AUTH_REQ_CRYPT 4 /* crypt password. Not supported any more. */ +#define AUTH_REQ_MD5 5 /* md5 password */ +#define AUTH_REQ_SCM_CREDS 6 /* transfer SCM credentials */ +#define AUTH_REQ_GSS 7 /* GSSAPI without wrap() */ +#define AUTH_REQ_GSS_CONT 8 /* Continue GSS exchanges */ +#define AUTH_REQ_SSPI 9 /* SSPI negotiate without wrap() */ +#define AUTH_REQ_SASL 10 /* SASL authentication. Not supported before + * libpq version 10. */ + +typedef uint32 AuthRequest; + + +/* + * A client can also send a cancel-current-operation request to the postmaster. + * This is uglier than sending it directly to the client's backend, but it + * avoids depending on out-of-band communication facilities. + * + * The cancel request code must not match any protocol version number + * we're ever likely to use. This random choice should do. + */ +#define CANCEL_REQUEST_CODE PG_PROTOCOL(1234,5678) + +typedef struct CancelRequestPacket +{ + /* Note that each field is stored in network byte order! */ + MsgType cancelRequestCode; /* code to identify a cancel request */ + uint32 backendPID; /* PID of client's backend */ + uint32 cancelAuthCode; /* secret key to authorize cancel */ +} CancelRequestPacket; + + +/* + * A client can also start by sending a SSL negotiation request, to get a + * secure channel. + */ +#define NEGOTIATE_SSL_CODE PG_PROTOCOL(1234,5679) + +#endif /* PQCOMM_H */ diff --git a/libpq/postgresql/mb/pg_wchar.h b/libpq/postgresql/mb/pg_wchar.h new file mode 100644 index 0000000..24e8d0d --- /dev/null +++ b/libpq/postgresql/mb/pg_wchar.h @@ -0,0 +1,561 @@ +/*------------------------------------------------------------------------- + * + * pg_wchar.h + * multibyte-character support + * + * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/mb/pg_wchar.h + * + * NOTES + * This is used both by the backend and by libpq, but should not be + * included by libpq client programs. In particular, a libpq client + * should not assume that the encoding IDs used by the version of libpq + * it's linked to match up with the IDs declared here. + * + *------------------------------------------------------------------------- + */ +#ifndef PG_WCHAR_H +#define PG_WCHAR_H + +/* + * The pg_wchar type + */ +typedef unsigned int pg_wchar; + +/* + * Maximum byte length of multibyte characters in any backend encoding + */ +#define MAX_MULTIBYTE_CHAR_LEN 4 + +/* + * various definitions for EUC + */ +#define SS2 0x8e /* single shift 2 (JIS0201) */ +#define SS3 0x8f /* single shift 3 (JIS0212) */ + +/* + * SJIS validation macros + */ +#define ISSJISHEAD(c) (((c) >= 0x81 && (c) <= 0x9f) || ((c) >= 0xe0 && (c) <= 0xfc)) +#define ISSJISTAIL(c) (((c) >= 0x40 && (c) <= 0x7e) || ((c) >= 0x80 && (c) <= 0xfc)) + +/*---------------------------------------------------- + * MULE Internal Encoding (MIC) + * + * This encoding follows the design used within XEmacs; it is meant to + * subsume many externally-defined character sets. Each character includes + * identification of the character set it belongs to, so the encoding is + * general but somewhat bulky. + * + * Currently PostgreSQL supports 5 types of MULE character sets: + * + * 1) 1-byte ASCII characters. Each byte is below 0x80. + * + * 2) "Official" single byte charsets such as ISO-8859-1 (Latin1). + * Each MULE character consists of 2 bytes: LC1 + C1, where LC1 is + * an identifier for the charset (in the range 0x81 to 0x8d) and C1 + * is the character code (in the range 0xa0 to 0xff). + * + * 3) "Private" single byte charsets such as SISHENG. Each MULE + * character consists of 3 bytes: LCPRV1 + LC12 + C1, where LCPRV1 + * is a private-charset flag, LC12 is an identifier for the charset, + * and C1 is the character code (in the range 0xa0 to 0xff). + * LCPRV1 is either 0x9a (if LC12 is in the range 0xa0 to 0xdf) + * or 0x9b (if LC12 is in the range 0xe0 to 0xef). + * + * 4) "Official" multibyte charsets such as JIS X0208. Each MULE + * character consists of 3 bytes: LC2 + C1 + C2, where LC2 is + * an identifier for the charset (in the range 0x90 to 0x99) and C1 + * and C2 form the character code (each in the range 0xa0 to 0xff). + * + * 5) "Private" multibyte charsets such as CNS 11643-1992 Plane 3. + * Each MULE character consists of 4 bytes: LCPRV2 + LC22 + C1 + C2, + * where LCPRV2 is a private-charset flag, LC22 is an identifier for + * the charset, and C1 and C2 form the character code (each in the range + * 0xa0 to 0xff). LCPRV2 is either 0x9c (if LC22 is in the range 0xf0 + * to 0xf4) or 0x9d (if LC22 is in the range 0xf5 to 0xfe). + * + * "Official" encodings are those that have been assigned code numbers by + * the XEmacs project; "private" encodings have Postgres-specific charset + * identifiers. + * + * See the "XEmacs Internals Manual", available at http://www.xemacs.org, + * for more details. Note that for historical reasons, Postgres' + * private-charset flag values do not match what XEmacs says they should be, + * so this isn't really exactly MULE (not that private charsets would be + * interoperable anyway). + * + * Note that XEmacs's implementation is different from what emacs does. + * We follow emacs's implementation, rather than XEmacs's. + *---------------------------------------------------- + */ + +/* + * Charset identifiers (also called "leading bytes" in the MULE documentation) + */ + +/* + * Charset IDs for official single byte encodings (0x81-0x8e) + */ +#define LC_ISO8859_1 0x81 /* ISO8859 Latin 1 */ +#define LC_ISO8859_2 0x82 /* ISO8859 Latin 2 */ +#define LC_ISO8859_3 0x83 /* ISO8859 Latin 3 */ +#define LC_ISO8859_4 0x84 /* ISO8859 Latin 4 */ +#define LC_TIS620 0x85 /* Thai (not supported yet) */ +#define LC_ISO8859_7 0x86 /* Greek (not supported yet) */ +#define LC_ISO8859_6 0x87 /* Arabic (not supported yet) */ +#define LC_ISO8859_8 0x88 /* Hebrew (not supported yet) */ +#define LC_JISX0201K 0x89 /* Japanese 1 byte kana */ +#define LC_JISX0201R 0x8a /* Japanese 1 byte Roman */ +/* Note that 0x8b seems to be unused as of Emacs 20.7. + * However, there might be a chance that 0x8b could be used + * in later versions of Emacs. + */ +#define LC_KOI8_R 0x8b /* Cyrillic KOI8-R */ +#define LC_ISO8859_5 0x8c /* ISO8859 Cyrillic */ +#define LC_ISO8859_9 0x8d /* ISO8859 Latin 5 (not supported yet) */ +#define LC_ISO8859_15 0x8e /* ISO8859 Latin 15 (not supported yet) */ +/* #define CONTROL_1 0x8f control characters (unused) */ + +/* Is a leading byte for "official" single byte encodings? */ +#define IS_LC1(c) ((unsigned char)(c) >= 0x81 && (unsigned char)(c) <= 0x8d) + +/* + * Charset IDs for official multibyte encodings (0x90-0x99) + * 0x9a-0x9d are free. 0x9e and 0x9f are reserved. + */ +#define LC_JISX0208_1978 0x90 /* Japanese Kanji, old JIS (not supported) */ +#define LC_GB2312_80 0x91 /* Chinese */ +#define LC_JISX0208 0x92 /* Japanese Kanji (JIS X 0208) */ +#define LC_KS5601 0x93 /* Korean */ +#define LC_JISX0212 0x94 /* Japanese Kanji (JIS X 0212) */ +#define LC_CNS11643_1 0x95 /* CNS 11643-1992 Plane 1 */ +#define LC_CNS11643_2 0x96 /* CNS 11643-1992 Plane 2 */ +#define LC_JISX0213_1 0x97/* Japanese Kanji (JIS X 0213 Plane 1) (not + * supported) */ +#define LC_BIG5_1 0x98 /* Plane 1 Chinese traditional (not supported) */ +#define LC_BIG5_2 0x99 /* Plane 1 Chinese traditional (not supported) */ + +/* Is a leading byte for "official" multibyte encodings? */ +#define IS_LC2(c) ((unsigned char)(c) >= 0x90 && (unsigned char)(c) <= 0x99) + +/* + * Postgres-specific prefix bytes for "private" single byte encodings + * (According to the MULE docs, we should be using 0x9e for this) + */ +#define LCPRV1_A 0x9a +#define LCPRV1_B 0x9b +#define IS_LCPRV1(c) ((unsigned char)(c) == LCPRV1_A || (unsigned char)(c) == LCPRV1_B) +#define IS_LCPRV1_A_RANGE(c) \ + ((unsigned char)(c) >= 0xa0 && (unsigned char)(c) <= 0xdf) +#define IS_LCPRV1_B_RANGE(c) \ + ((unsigned char)(c) >= 0xe0 && (unsigned char)(c) <= 0xef) + +/* + * Postgres-specific prefix bytes for "private" multibyte encodings + * (According to the MULE docs, we should be using 0x9f for this) + */ +#define LCPRV2_A 0x9c +#define LCPRV2_B 0x9d +#define IS_LCPRV2(c) ((unsigned char)(c) == LCPRV2_A || (unsigned char)(c) == LCPRV2_B) +#define IS_LCPRV2_A_RANGE(c) \ + ((unsigned char)(c) >= 0xf0 && (unsigned char)(c) <= 0xf4) +#define IS_LCPRV2_B_RANGE(c) \ + ((unsigned char)(c) >= 0xf5 && (unsigned char)(c) <= 0xfe) + +/* + * Charset IDs for private single byte encodings (0xa0-0xef) + */ +#define LC_SISHENG 0xa0/* Chinese SiSheng characters for + * PinYin/ZhuYin (not supported) */ +#define LC_IPA 0xa1/* IPA (International Phonetic Association) + * (not supported) */ +#define LC_VISCII_LOWER 0xa2/* Vietnamese VISCII1.1 lower-case (not + * supported) */ +#define LC_VISCII_UPPER 0xa3/* Vietnamese VISCII1.1 upper-case (not + * supported) */ +#define LC_ARABIC_DIGIT 0xa4 /* Arabic digit (not supported) */ +#define LC_ARABIC_1_COLUMN 0xa5 /* Arabic 1-column (not supported) */ +#define LC_ASCII_RIGHT_TO_LEFT 0xa6 /* ASCII (left half of ISO8859-1) with + * right-to-left direction (not + * supported) */ +#define LC_LAO 0xa7/* Lao characters (ISO10646 0E80..0EDF) (not + * supported) */ +#define LC_ARABIC_2_COLUMN 0xa8 /* Arabic 1-column (not supported) */ + +/* + * Charset IDs for private multibyte encodings (0xf0-0xff) + */ +#define LC_INDIAN_1_COLUMN 0xf0/* Indian charset for 1-column width glyphs + * (not supported) */ +#define LC_TIBETAN_1_COLUMN 0xf1/* Tibetan 1-column width glyphs (not + * supported) */ +#define LC_UNICODE_SUBSET_2 0xf2/* Unicode characters of the range + * U+2500..U+33FF. (not supported) */ +#define LC_UNICODE_SUBSET_3 0xf3/* Unicode characters of the range + * U+E000..U+FFFF. (not supported) */ +#define LC_UNICODE_SUBSET 0xf4/* Unicode characters of the range + * U+0100..U+24FF. (not supported) */ +#define LC_ETHIOPIC 0xf5 /* Ethiopic characters (not supported) */ +#define LC_CNS11643_3 0xf6 /* CNS 11643-1992 Plane 3 */ +#define LC_CNS11643_4 0xf7 /* CNS 11643-1992 Plane 4 */ +#define LC_CNS11643_5 0xf8 /* CNS 11643-1992 Plane 5 */ +#define LC_CNS11643_6 0xf9 /* CNS 11643-1992 Plane 6 */ +#define LC_CNS11643_7 0xfa /* CNS 11643-1992 Plane 7 */ +#define LC_INDIAN_2_COLUMN 0xfb/* Indian charset for 2-column width glyphs + * (not supported) */ +#define LC_TIBETAN 0xfc /* Tibetan (not supported) */ +/* #define FREE 0xfd free (unused) */ +/* #define FREE 0xfe free (unused) */ +/* #define FREE 0xff free (unused) */ + +/*---------------------------------------------------- + * end of MULE stuff + *---------------------------------------------------- + */ + +/* + * PostgreSQL encoding identifiers + * + * WARNING: the order of this enum must be same as order of entries + * in the pg_enc2name_tbl[] array (in mb/encnames.c), and + * in the pg_wchar_table[] array (in mb/wchar.c)! + * + * If you add some encoding don't forget to check + * PG_ENCODING_BE_LAST macro. + * + * PG_SQL_ASCII is default encoding and must be = 0. + * + * XXX We must avoid renumbering any backend encoding until libpq's major + * version number is increased beyond 5; it turns out that the backend + * encoding IDs are effectively part of libpq's ABI as far as 8.2 initdb and + * psql are concerned. + */ +typedef enum pg_enc +{ + PG_SQL_ASCII = 0, /* SQL/ASCII */ + PG_EUC_JP, /* EUC for Japanese */ + PG_EUC_CN, /* EUC for Chinese */ + PG_EUC_KR, /* EUC for Korean */ + PG_EUC_TW, /* EUC for Taiwan */ + PG_EUC_JIS_2004, /* EUC-JIS-2004 */ + PG_UTF8, /* Unicode UTF8 */ + PG_MULE_INTERNAL, /* Mule internal code */ + PG_LATIN1, /* ISO-8859-1 Latin 1 */ + PG_LATIN2, /* ISO-8859-2 Latin 2 */ + PG_LATIN3, /* ISO-8859-3 Latin 3 */ + PG_LATIN4, /* ISO-8859-4 Latin 4 */ + PG_LATIN5, /* ISO-8859-9 Latin 5 */ + PG_LATIN6, /* ISO-8859-10 Latin6 */ + PG_LATIN7, /* ISO-8859-13 Latin7 */ + PG_LATIN8, /* ISO-8859-14 Latin8 */ + PG_LATIN9, /* ISO-8859-15 Latin9 */ + PG_LATIN10, /* ISO-8859-16 Latin10 */ + PG_WIN1256, /* windows-1256 */ + PG_WIN1258, /* Windows-1258 */ + PG_WIN866, /* (MS-DOS CP866) */ + PG_WIN874, /* windows-874 */ + PG_KOI8R, /* KOI8-R */ + PG_WIN1251, /* windows-1251 */ + PG_WIN1252, /* windows-1252 */ + 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_WIN1250, /* windows-1250 */ + PG_WIN1253, /* windows-1253 */ + PG_WIN1254, /* windows-1254 */ + PG_WIN1255, /* windows-1255 */ + PG_WIN1257, /* windows-1257 */ + PG_KOI8U, /* KOI8-U */ + /* PG_ENCODING_BE_LAST points to the above entry */ + + /* followings are for client encoding only */ + PG_SJIS, /* Shift JIS (Windows-932) */ + PG_BIG5, /* Big5 (Windows-950) */ + PG_GBK, /* GBK (Windows-936) */ + PG_UHC, /* UHC (Windows-949) */ + PG_GB18030, /* GB18030 */ + PG_JOHAB, /* EUC for Korean JOHAB */ + PG_SHIFT_JIS_2004, /* Shift-JIS-2004 */ + _PG_LAST_ENCODING_ /* mark only */ + +} pg_enc; + +#define PG_ENCODING_BE_LAST PG_KOI8U + +/* + * Please use these tests before access to pg_encconv_tbl[] + * or to other places... + */ +#define PG_VALID_BE_ENCODING(_enc) \ + ((_enc) >= 0 && (_enc) <= PG_ENCODING_BE_LAST) + +#define PG_ENCODING_IS_CLIENT_ONLY(_enc) \ + ((_enc) > PG_ENCODING_BE_LAST && (_enc) < _PG_LAST_ENCODING_) + +#define PG_VALID_ENCODING(_enc) \ + ((_enc) >= 0 && (_enc) < _PG_LAST_ENCODING_) + +/* On FE are possible all encodings */ +#define PG_VALID_FE_ENCODING(_enc) PG_VALID_ENCODING(_enc) + +/* + * Table for mapping an encoding number to official encoding name and + * possibly other subsidiary data. Be careful to check encoding number + * before accessing a table entry! + * + * if (PG_VALID_ENCODING(encoding)) + * pg_enc2name_tbl[ encoding ]; + */ +typedef struct pg_enc2name +{ + const char *name; + pg_enc encoding; +#ifdef WIN32 + unsigned codepage; /* codepage for WIN32 */ +#endif +} pg_enc2name; + +extern const pg_enc2name pg_enc2name_tbl[]; + +/* + * Encoding names for gettext + */ +typedef struct pg_enc2gettext +{ + pg_enc encoding; + const char *name; +} pg_enc2gettext; + +extern const pg_enc2gettext pg_enc2gettext_tbl[]; + +/* + * pg_wchar stuff + */ +typedef int (*mb2wchar_with_len_converter) (const unsigned char *from, + pg_wchar *to, + int len); + +typedef int (*wchar2mb_with_len_converter) (const pg_wchar *from, + unsigned char *to, + int len); + +typedef int (*mblen_converter) (const unsigned char *mbstr); + +typedef int (*mbdisplaylen_converter) (const unsigned char *mbstr); + +typedef bool (*mbcharacter_incrementer) (unsigned char *mbstr, int len); + +typedef int (*mbverifier) (const unsigned char *mbstr, int len); + +typedef struct +{ + mb2wchar_with_len_converter mb2wchar_with_len; /* convert a multibyte + * string to a wchar */ + wchar2mb_with_len_converter wchar2mb_with_len; /* convert a wchar + * string to a multibyte */ + mblen_converter mblen; /* get byte length of a char */ + mbdisplaylen_converter dsplen; /* get display width of a char */ + mbverifier mbverify; /* verify multibyte sequence */ + int maxmblen; /* max bytes for a char in this encoding */ +} pg_wchar_tbl; + +extern const pg_wchar_tbl pg_wchar_table[]; + +/* + * Data structures for conversions between UTF-8 and other encodings + * (UtfToLocal() and LocalToUtf()). In these data structures, characters of + * either encoding are represented by uint32 words; hence we can only support + * characters up to 4 bytes long. For example, the byte sequence 0xC2 0x89 + * would be represented by 0x0000C289, and 0xE8 0xA2 0xB4 by 0x00E8A2B4. + * + * Maps are arrays of these structs, which must be in order by the lookup key + * (so that bsearch() can be used). + * + * UTF-8 to local code conversion map + */ +typedef struct +{ + uint32 utf; /* UTF-8 */ + uint32 code; /* local code */ +} pg_utf_to_local; + +/* + * local code to UTF-8 conversion map + */ +typedef struct +{ + uint32 code; /* local code */ + uint32 utf; /* UTF-8 */ +} pg_local_to_utf; + +/* + * UTF-8 to local code conversion map (for combined characters) + */ +typedef struct +{ + uint32 utf1; /* UTF-8 code 1 */ + uint32 utf2; /* UTF-8 code 2 */ + uint32 code; /* local code */ +} pg_utf_to_local_combined; + +/* + * local code to UTF-8 conversion map (for combined characters) + */ +typedef struct +{ + uint32 code; /* local code */ + uint32 utf1; /* UTF-8 code 1 */ + uint32 utf2; /* UTF-8 code 2 */ +} pg_local_to_utf_combined; + +/* + * callback function for algorithmic encoding conversions (in either direction) + * + * if function returns zero, it does not know how to convert the code + */ +typedef uint32 (*utf_local_conversion_func) (uint32 code); + +/* + * Support macro for encoding conversion functions to validate their + * arguments. (This could be made more compact if we included fmgr.h + * here, but we don't want to do that because this header file is also + * used by frontends.) + */ +#define CHECK_ENCODING_CONVERSION_ARGS(srcencoding,destencoding) \ + check_encoding_conversion_args(PG_GETARG_INT32(0), \ + PG_GETARG_INT32(1), \ + PG_GETARG_INT32(4), \ + (srcencoding), \ + (destencoding)) + + +/* + * These functions are considered part of libpq's exported API and + * are also declared in libpq-fe.h. + */ +extern int pg_char_to_encoding(const char *name); +extern const char *pg_encoding_to_char(int encoding); +extern int pg_valid_server_encoding_id(int encoding); + +/* + * Remaining functions are not considered part of libpq's API, though many + * of them do exist inside libpq. + */ +extern int pg_mb2wchar(const char *from, pg_wchar *to); +extern int pg_mb2wchar_with_len(const char *from, pg_wchar *to, int len); +extern int pg_encoding_mb2wchar_with_len(int encoding, + const char *from, pg_wchar *to, int len); +extern int pg_wchar2mb(const pg_wchar *from, char *to); +extern int pg_wchar2mb_with_len(const pg_wchar *from, char *to, int len); +extern int pg_encoding_wchar2mb_with_len(int encoding, + const pg_wchar *from, char *to, int len); +extern int pg_char_and_wchar_strcmp(const char *s1, const pg_wchar *s2); +extern int pg_wchar_strncmp(const pg_wchar *s1, const pg_wchar *s2, size_t n); +extern int pg_char_and_wchar_strncmp(const char *s1, const pg_wchar *s2, size_t n); +extern size_t pg_wchar_strlen(const pg_wchar *wstr); +extern int pg_mblen(const char *mbstr); +extern int pg_dsplen(const char *mbstr); +extern int pg_encoding_mblen(int encoding, const char *mbstr); +extern int pg_encoding_dsplen(int encoding, const char *mbstr); +extern int pg_encoding_verifymb(int encoding, const char *mbstr, int len); +extern int pg_mule_mblen(const unsigned char *mbstr); +extern int pg_mic_mblen(const unsigned char *mbstr); +extern int pg_mbstrlen(const char *mbstr); +extern int pg_mbstrlen_with_len(const char *mbstr, int len); +extern int pg_mbcliplen(const char *mbstr, int len, int limit); +extern int pg_encoding_mbcliplen(int encoding, const char *mbstr, + int len, int limit); +extern int pg_mbcharcliplen(const char *mbstr, int len, int imit); +extern int pg_encoding_max_length(int encoding); +extern int pg_database_encoding_max_length(void); +extern mbcharacter_incrementer pg_database_encoding_character_incrementer(void); + +extern int PrepareClientEncoding(int encoding); +extern int SetClientEncoding(int encoding); +extern void InitializeClientEncoding(void); +extern int pg_get_client_encoding(void); +extern const char *pg_get_client_encoding_name(void); + +extern void SetDatabaseEncoding(int encoding); +extern int GetDatabaseEncoding(void); +extern const char *GetDatabaseEncodingName(void); +extern void SetMessageEncoding(int encoding); +extern int GetMessageEncoding(void); + +#ifdef ENABLE_NLS +extern int pg_bind_textdomain_codeset(const char *domainname); +#endif + +extern int pg_valid_client_encoding(const char *name); +extern int pg_valid_server_encoding(const char *name); + +extern unsigned char *unicode_to_utf8(pg_wchar c, unsigned char *utf8string); +extern pg_wchar utf8_to_unicode(const unsigned char *c); +extern int pg_utf_mblen(const unsigned char *); +extern unsigned char *pg_do_encoding_conversion(unsigned char *src, int len, + int src_encoding, + int dest_encoding); + +extern char *pg_client_to_server(const char *s, int len); +extern char *pg_server_to_client(const char *s, int len); +extern char *pg_any_to_server(const char *s, int len, int encoding); +extern char *pg_server_to_any(const char *s, int len, int encoding); + +extern unsigned short BIG5toCNS(unsigned short big5, unsigned char *lc); +extern unsigned short CNStoBIG5(unsigned short cns, unsigned char lc); + +extern void UtfToLocal(const unsigned char *utf, int len, + unsigned char *iso, + const pg_utf_to_local *map, int mapsize, + const pg_utf_to_local_combined *cmap, int cmapsize, + utf_local_conversion_func conv_func, + int encoding); +extern void LocalToUtf(const unsigned char *iso, int len, + unsigned char *utf, + const pg_local_to_utf *map, int mapsize, + const pg_local_to_utf_combined *cmap, int cmapsize, + utf_local_conversion_func conv_func, + int encoding); + +extern bool pg_verifymbstr(const char *mbstr, int len, bool noError); +extern bool pg_verify_mbstr(int encoding, const char *mbstr, int len, + bool noError); +extern int pg_verify_mbstr_len(int encoding, const char *mbstr, int len, + bool noError); + +extern void check_encoding_conversion_args(int src_encoding, + int dest_encoding, + int len, + int expected_src_encoding, + int expected_dest_encoding); + +extern void report_invalid_encoding(int encoding, const char *mbstr, int len) pg_attribute_noreturn(); +extern void report_untranslatable_char(int src_encoding, int dest_encoding, + const char *mbstr, int len) pg_attribute_noreturn(); + +extern void local2local(const unsigned char *l, unsigned char *p, int len, + int src_encoding, int dest_encoding, const unsigned char *tab); +extern void pg_ascii2mic(const unsigned char *l, unsigned char *p, int len); +extern void pg_mic2ascii(const unsigned char *mic, unsigned char *p, int len); +extern void latin2mic(const unsigned char *l, unsigned char *p, int len, + int lc, int encoding); +extern void mic2latin(const unsigned char *mic, unsigned char *p, int len, + int lc, int encoding); +extern void latin2mic_with_table(const unsigned char *l, unsigned char *p, + int len, int lc, int encoding, + const unsigned char *tab); +extern void mic2latin_with_table(const unsigned char *mic, unsigned char *p, + int len, int lc, int encoding, + const unsigned char *tab); + +extern bool pg_utf8_islegal(const unsigned char *source, int length); + +#ifdef WIN32 +extern WCHAR *pgwin32_message_to_UTF16(const char *str, int len, int *utf16len); +#endif + +#endif /* PG_WCHAR_H */ diff --git a/libpq/postgresql/pg_config.h b/libpq/postgresql/pg_config.h new file mode 100644 index 0000000..ee4408a --- /dev/null +++ b/libpq/postgresql/pg_config.h @@ -0,0 +1,280 @@ +/* file : libpq/postgresql/pg_config.h -*- C -*- + * copyright : Copyright (c) 2016-2017 Code Synthesis Ltd + * license : PostgreSQL License; see accompanying COPYRIGHT file + */ + +/* + * For the semanics of the following macros refer to + * libpq/postgresql/pg_config.h.in.orig and + * libpq/postgresql/pg_config.h.win32.orig files. + * + * Note that we will explicitly undefine macros that are present in the libpq + * source code but should not be defined. While this is not technically + * required, it simplifies the change tracking (see README-DEV). As a bonus we + * also make sure that they are not get eventually defined by some system + * headers. + */ + +#include /* offsetof() */ + +/* + * Version. + */ +#undef PG_MAJORVERSION +#undef PG_VERSION_NUM +#include + +/* + * Types, type sizes and alignments. + */ +#define ALIGNOF_(type) offsetof (struct {char c; type m;}, m) +#define ALIGNOF_DOUBLE ALIGNOF_ (double) +#define ALIGNOF_INT ALIGNOF_ (int) +#define ALIGNOF_LONG ALIGNOF_ (long) +#define ALIGNOF_SHORT ALIGNOF_ (short) + +/* + * GCC and Clang provide __SIZEOF_*__ and __*_TYPE__ predefined macros that we + * use to define the required libpq macros. Note that on Windows long and + * long long types are always of 32 and 64 bits width respectively. + */ +#ifndef _WIN32 +# if __SIZEOF_LONG__ == 8 +# define HAVE_LONG_INT_64 1 +# endif +# if __SIZEOF_LONG_LONG__ == 8 +# define HAVE_LONG_LONG_INT_64 1 +# endif +# if __SIZEOF_LONG_LONG__ > __SIZEOF_DOUBLE__ +# define MAXIMUM_ALIGNOF __SIZEOF_LONG_LONG__ +# else +# define MAXIMUM_ALIGNOF __SIZEOF_DOUBLE__ +# endif +# ifdef __SIZEOF_INT128__ +# define PG_INT128_TYPE __int128 +# endif +# define PG_INT64_TYPE __INT64_TYPE__ +# define ACCEPT_TYPE_ARG3 socklen_t +# define SIZEOF_SIZE_T __SIZEOF_SIZE_T__ +#else +# define HAVE_LONG_LONG_INT_64 1 +# define MAXIMUM_ALIGNOF 8 +# define PG_INT64_TYPE long long int +# define ACCEPT_TYPE_ARG3 int +# ifdef _WIN64 +# define SIZEOF_SIZE_T 8 +# else +# define SIZEOF_SIZE_T 4 +# endif +#endif + +#define INT64_MODIFIER "ll" + +/* + * Specific for FreeBSD. + */ +#ifdef __FreeBSD__ +# define HAVE_STRUCT_CMSGCRED 1 +#endif + +/* + * Specific for Mac OS. + */ +#ifdef __apple_build_version__ +# define HAVE_DECL_F_FULLFSYNC 1 +#else +# define HAVE_DECL_F_FULLFSYNC 0 +#endif + +/* + * Specific for FreeBSD and Mac OS. + */ +#if defined(__FreeBSD__) || defined(__apple_build_version__) +# define HAVE_DECL_STRLCAT 1 +# define HAVE_DECL_STRLCPY 1 +# define STRERROR_R_INT 1 +# define HAVE_FLS 1 +# define HAVE_GETPEEREID 1 +# define HAVE_STRTOQ 1 +# define HAVE_STRTOUQ 1 +# define HAVE_STRUCT_SOCKADDR_SA_LEN 1 +# define HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN 1 +# define HAVE_SYS_SOCKIO_H 1 +# define HAVE_SYS_UCRED_H 1 +# define HAVE_UNION_SEMUN 1 +#else +# define HAVE_DECL_STRLCAT 0 +# define HAVE_DECL_STRLCPY 0 +#endif + +/* + * Specific for POSIX. + */ +#ifndef _WIN32 +# define HAVE_CRYPT 1 +# define HAVE_DECL_FDATASYNC 1 +# define HAVE_FDATASYNC 1 +# define HAVE_GETADDRINFO 1 +# define HAVE_GETIFADDRS 1 +# define HAVE_IFADDRS_H 1 +# define HAVE_GETPWUID_R 1 +# define HAVE_INET_ATON 1 +# define HAVE_LANGINFO_H 1 +# define HAVE_MKDTEMP 1 +# define HAVE_NETINET_TCP_H 1 +# define HAVE_NET_IF_H 1 +# define HAVE_DECL_POSIX_FADVISE 1 +# define HAVE_POSIX_FADVISE 1 +# define HAVE_RANDOM 1 +# define HAVE_SRANDOM 1 +# define HAVE_STRERROR_R 1 +# define HAVE_STRINGS_H 1 +# define HAVE_SYS_IOCTL_H 1 +# define HAVE_POLL 1 +# define HAVE_POLL_H 1 +# define HAVE_SYS_POLL_H 1 +# define HAVE_SYS_SELECT_H 1 +# define HAVE_SYS_UN_H 1 +# define HAVE_TERMIOS_H 1 +# define HAVE_UNIX_SOCKETS 1 +# define HAVE_UNSETENV 1 +# define USE_INTEGER_DATETIMES 1 +/* + * Specific for Windows. + */ +#else +# define HAVE_DECL_FDATASYNC 0 +# define HAVE_DECL_POSIX_FADVISE 0 +# define HAVE_GETTIMEOFDAY 1 +# define HAVE_ISINF 1 +# define HAVE_FUNCNAME__FUNCTION 1 +# define USE_REPL_SNPRINTF 1 +#endif + +/* + * Specific for GNU C Library. + */ +#ifdef __GLIBC__ +# define HAVE_GETHOSTBYNAME_R 1 +#endif + +/* + * Specific for (non-) VC. + */ +#ifndef _MSC_VER +# define HAVE__BUILTIN_TYPES_COMPATIBLE_P 1 +# define HAVE__BUILTIN_UNREACHABLE 1 +#endif + +/* + * Common for all supported OSes/compilers. + */ +#define ENABLE_THREAD_SAFETY 1 +#define HAVE_MEMMOVE 1 +#define HAVE_RINT 1 +#define HAVE_DECL_SNPRINTF 1 +#define HAVE_DECL_VSNPRINTF 1 +#define HAVE_FSEEKO 1 +#define HAVE_FUNCNAME__FUNC 1 +#define HAVE_IPV6 1 +#define HAVE_STDINT_H 1 +#define HAVE_STRTOLL 1 +#define HAVE_STRTOULL 1 +#define HAVE_TOWLOWER 1 +#define HAVE_WCSTOMBS 1 +#define HAVE_SSL_GET_CURRENT_COMPRESSION 1 +#define HAVE_STRUCT_ADDRINFO 1 +#define HAVE_STRUCT_SOCKADDR_STORAGE 1 +#define HAVE_STRUCT_SOCKADDR_STORAGE_SS_FAMILY 1 +#define BLCKSZ 8192 +#define PG_KRB_SRVNAM "postgres" +#define PG_PRINTF_ATTRIBUTE printf +#define FLEXIBLE_ARRAY_MEMBER +#define MEMSET_LOOP_LIMIT 1024 +#define DEF_PGPORT 5432 +#define DEF_PGPORT_STR "5432" + +/* + * _Static_assert() was introduced in C11. However, all the latest major + * compilers support it for C99 as well. + */ +#define HAVE__STATIC_ASSERT 1 + +/* + * Undefined macros. + */ + +/* + * The following features are disabled by default, so we also disable them. + */ +#undef USE_ASSERT_CHECKING +#undef ENABLE_NLS +#undef ENABLE_GSS +#undef USE_OPENSSL +#undef USE_LDAP + +/* + * Is meaningless if NLS support is disabled (see above and libpq/buildfile for + * details). + */ +#undef LOCALEDIR + +/* + * Is meaningless if GSSAPI support is disabled (see above). It also seems that + * for modern systems including or will work both + * ( just includes ). + */ +#undef HAVE_GSSAPI_H + +/* + * Integer literal LL suffix is optional for C99. + */ +#undef HAVE_LL_CONSTANTS + +/* + * Windows-specific. is included for the latest (>= 1400) VC + * unconditionally. + */ +#undef HAVE_CRTDEFS_H + +/* + * Solaris-specific (getpeerucred() function). + */ +#undef HAVE_GETPEERUCRED + +/* + * Hard to even find any records of these types. + */ +#undef HAVE_INT64 +#undef HAVE_INT8 +#undef HAVE_UINT64 +#undef HAVE_UINT8 + +/* + * Something optimization-related for PowerPC machines (see + * libpq/postgresql/pg_config_manual.h for more details). + */ +#undef HAVE_PPC_LWARX_MUTEX_HINT + +/* + * None of the supported platforms has the '__' prefix for the mentioned + * sockaddr_storage struct members. + */ +#undef HAVE_STRUCT_SOCKADDR_STORAGE___SS_FAMILY +#undef HAVE_STRUCT_SOCKADDR_STORAGE___SS_LEN + +/* + * Let's follow Linux man page advise for sync_file_range() function: + * + * This system call is Linux-specific, and should be avoided in portable + * programs. + * + * The macro also seems to be backend-specific. + */ +#undef HAVE_SYNC_FILE_RANGE + +/* + * None of the supported OSes have . FreeBSD and Mac OS have + * (HAVE_SYS_UCRED_H macro). + */ +#undef HAVE_UCRED_H diff --git a/libpq/postgresql/pg_config.h.in.orig b/libpq/postgresql/pg_config.h.in.orig new file mode 100644 index 0000000..7dbfa90 --- /dev/null +++ b/libpq/postgresql/pg_config.h.in.orig @@ -0,0 +1,931 @@ +/* src/include/pg_config.h.in. Generated from configure.in by autoheader. */ + +/* Define to the type of arg 1 of 'accept' */ +#undef ACCEPT_TYPE_ARG1 + +/* Define to the type of arg 2 of 'accept' */ +#undef ACCEPT_TYPE_ARG2 + +/* Define to the type of arg 3 of 'accept' */ +#undef ACCEPT_TYPE_ARG3 + +/* Define to the return type of 'accept' */ +#undef ACCEPT_TYPE_RETURN + +/* Define if building universal (internal helper macro) */ +#undef AC_APPLE_UNIVERSAL_BUILD + +/* The normal alignment of `double', in bytes. */ +#undef ALIGNOF_DOUBLE + +/* The normal alignment of `int', in bytes. */ +#undef ALIGNOF_INT + +/* The normal alignment of `long', in bytes. */ +#undef ALIGNOF_LONG + +/* The normal alignment of `long long int', in bytes. */ +#undef ALIGNOF_LONG_LONG_INT + +/* The normal alignment of `short', in bytes. */ +#undef ALIGNOF_SHORT + +/* Size of a disk block --- this also limits the size of a tuple. You can set + it bigger if you need bigger tuples (although TOAST should reduce the need + to have large tuples, since fields can be spread across multiple tuples). + BLCKSZ must be a power of 2. The maximum possible value of BLCKSZ is + currently 2^15 (32768). This is determined by the 15-bit widths of the + lp_off and lp_len fields in ItemIdData (see include/storage/itemid.h). + Changing BLCKSZ requires an initdb. */ +#undef BLCKSZ + +/* Define to the default TCP port number on which the server listens and to + which clients will try to connect. This can be overridden at run-time, but + it's convenient if your clients have the right default compiled in. + (--with-pgport=PORTNUM) */ +#undef DEF_PGPORT + +/* Define to the default TCP port number as a string constant. */ +#undef DEF_PGPORT_STR + +/* Define to build with GSSAPI support. (--with-gssapi) */ +#undef ENABLE_GSS + +/* Define to 1 if you want National Language Support. (--enable-nls) */ +#undef ENABLE_NLS + +/* Define to 1 to build client libraries as thread-safe code. + (--enable-thread-safety) */ +#undef ENABLE_THREAD_SAFETY + +/* Define to nothing if C supports flexible array members, and to 1 if it does + not. That way, with a declaration like `struct s { int n; double + d[FLEXIBLE_ARRAY_MEMBER]; };', the struct hack can be used with pre-C99 + compilers. When computing the size of such an object, don't use 'sizeof + (struct s)' as it overestimates the size. Use 'offsetof (struct s, d)' + instead. Don't use 'offsetof (struct s, d[0])', as this doesn't work with + MSVC and with C++ compilers. */ +#undef FLEXIBLE_ARRAY_MEMBER + +/* float4 values are passed by value if 'true', by reference if 'false' */ +#undef FLOAT4PASSBYVAL + +/* float8, int8, and related values are passed by value if 'true', by + reference if 'false' */ +#undef FLOAT8PASSBYVAL + +/* Define to 1 if gettimeofday() takes only 1 argument. */ +#undef GETTIMEOFDAY_1ARG + +#ifdef GETTIMEOFDAY_1ARG +# define gettimeofday(a,b) gettimeofday(a) +#endif + +/* Define to 1 if you have the `append_history' function. */ +#undef HAVE_APPEND_HISTORY + +/* Define to 1 if you have the `ASN1_STRING_get0_data' function. */ +#undef HAVE_ASN1_STRING_GET0_DATA + +/* Define to 1 if you want to use atomics if available. */ +#undef HAVE_ATOMICS + +/* Define to 1 if you have the header file. */ +#undef HAVE_ATOMIC_H + +/* Define to 1 if you have the `BIO_get_data' function. */ +#undef HAVE_BIO_GET_DATA + +/* Define to 1 if you have the `BIO_meth_new' function. */ +#undef HAVE_BIO_METH_NEW + +/* Define to 1 if you have the `cbrt' function. */ +#undef HAVE_CBRT + +/* Define to 1 if you have the `class' function. */ +#undef HAVE_CLASS + +/* Define to 1 if you have the header file. */ +#undef HAVE_CRTDEFS_H + +/* Define to 1 if you have the `crypt' function. */ +#undef HAVE_CRYPT + +/* Define to 1 if you have the `CRYPTO_lock' function. */ +#undef HAVE_CRYPTO_LOCK + +/* Define to 1 if you have the header file. */ +#undef HAVE_CRYPT_H + +/* Define to 1 if you have the declaration of `fdatasync', and to 0 if you + don't. */ +#undef HAVE_DECL_FDATASYNC + +/* Define to 1 if you have the declaration of `F_FULLFSYNC', and to 0 if you + don't. */ +#undef HAVE_DECL_F_FULLFSYNC + +/* Define to 1 if you have the declaration of `posix_fadvise', and to 0 if you + don't. */ +#undef HAVE_DECL_POSIX_FADVISE + +/* Define to 1 if you have the declaration of `snprintf', and to 0 if you + don't. */ +#undef HAVE_DECL_SNPRINTF + +/* Define to 1 if you have the declaration of `strlcat', and to 0 if you + don't. */ +#undef HAVE_DECL_STRLCAT + +/* Define to 1 if you have the declaration of `strlcpy', and to 0 if you + don't. */ +#undef HAVE_DECL_STRLCPY + +/* Define to 1 if you have the declaration of `sys_siglist', and to 0 if you + don't. */ +#undef HAVE_DECL_SYS_SIGLIST + +/* Define to 1 if you have the declaration of `vsnprintf', and to 0 if you + don't. */ +#undef HAVE_DECL_VSNPRINTF + +/* Define to 1 if you have the header file. */ +#undef HAVE_DLD_H + +/* Define to 1 if you have the `dlopen' function. */ +#undef HAVE_DLOPEN + +/* Define to 1 if you have the header file. */ +#undef HAVE_EDITLINE_HISTORY_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_EDITLINE_READLINE_H + +/* Define to 1 if you have the `fdatasync' function. */ +#undef HAVE_FDATASYNC + +/* Define to 1 if you have the `fls' function. */ +#undef HAVE_FLS + +/* Define to 1 if you have the `fpclass' function. */ +#undef HAVE_FPCLASS + +/* Define to 1 if you have the `fp_class' function. */ +#undef HAVE_FP_CLASS + +/* Define to 1 if you have the `fp_class_d' function. */ +#undef HAVE_FP_CLASS_D + +/* Define to 1 if you have the header file. */ +#undef HAVE_FP_CLASS_H + +/* Define to 1 if fseeko (and presumably ftello) exists and is declared. */ +#undef HAVE_FSEEKO + +/* Define to 1 if your compiler understands __func__. */ +#undef HAVE_FUNCNAME__FUNC + +/* Define to 1 if your compiler understands __FUNCTION__. */ +#undef HAVE_FUNCNAME__FUNCTION + +/* Define to 1 if you have __atomic_compare_exchange_n(int *, int *, int). */ +#undef HAVE_GCC__ATOMIC_INT32_CAS + +/* Define to 1 if you have __atomic_compare_exchange_n(int64 *, int *, int64). + */ +#undef HAVE_GCC__ATOMIC_INT64_CAS + +/* Define to 1 if you have __sync_lock_test_and_set(char *) and friends. */ +#undef HAVE_GCC__SYNC_CHAR_TAS + +/* Define to 1 if you have __sync_compare_and_swap(int *, int, int). */ +#undef HAVE_GCC__SYNC_INT32_CAS + +/* Define to 1 if you have __sync_lock_test_and_set(int *) and friends. */ +#undef HAVE_GCC__SYNC_INT32_TAS + +/* Define to 1 if you have __sync_compare_and_swap(int64 *, int64, int64). */ +#undef HAVE_GCC__SYNC_INT64_CAS + +/* Define to 1 if you have the `getaddrinfo' function. */ +#undef HAVE_GETADDRINFO + +/* Define to 1 if you have the `gethostbyname_r' function. */ +#undef HAVE_GETHOSTBYNAME_R + +/* Define to 1 if you have the `getifaddrs' function. */ +#undef HAVE_GETIFADDRS + +/* Define to 1 if you have the `getopt' function. */ +#undef HAVE_GETOPT + +/* Define to 1 if you have the header file. */ +#undef HAVE_GETOPT_H + +/* Define to 1 if you have the `getopt_long' function. */ +#undef HAVE_GETOPT_LONG + +/* Define to 1 if you have the `getpeereid' function. */ +#undef HAVE_GETPEEREID + +/* Define to 1 if you have the `getpeerucred' function. */ +#undef HAVE_GETPEERUCRED + +/* Define to 1 if you have the `getpwuid_r' function. */ +#undef HAVE_GETPWUID_R + +/* Define to 1 if you have the `getrlimit' function. */ +#undef HAVE_GETRLIMIT + +/* Define to 1 if you have the `getrusage' function. */ +#undef HAVE_GETRUSAGE + +/* Define to 1 if you have the `gettimeofday' function. */ +#undef HAVE_GETTIMEOFDAY + +/* Define to 1 if you have the header file. */ +#undef HAVE_GSSAPI_GSSAPI_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_GSSAPI_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_HISTORY_H + +/* Define to 1 if you have the `history_truncate_file' function. */ +#undef HAVE_HISTORY_TRUNCATE_FILE + +/* Define to 1 if you have the header file. */ +#undef HAVE_IEEEFP_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_IFADDRS_H + +/* Define to 1 if you have the `inet_aton' function. */ +#undef HAVE_INET_ATON + +/* Define to 1 if the system has the type `int64'. */ +#undef HAVE_INT64 + +/* Define to 1 if the system has the type `int8'. */ +#undef HAVE_INT8 + +/* Define to 1 if the system has the type `intptr_t'. */ +#undef HAVE_INTPTR_T + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the global variable 'int opterr'. */ +#undef HAVE_INT_OPTERR + +/* Define to 1 if you have the global variable 'int optreset'. */ +#undef HAVE_INT_OPTRESET + +/* Define to 1 if you have the global variable 'int timezone'. */ +#undef HAVE_INT_TIMEZONE + +/* Define to 1 if you have support for IPv6. */ +#undef HAVE_IPV6 + +/* Define to 1 if you have isinf(). */ +#undef HAVE_ISINF + +/* Define to 1 if you have the header file. */ +#undef HAVE_LANGINFO_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_LDAP_H + +/* Define to 1 if you have the `crypto' library (-lcrypto). */ +#undef HAVE_LIBCRYPTO + +/* Define to 1 if you have the `ldap' library (-lldap). */ +#undef HAVE_LIBLDAP + +/* Define to 1 if you have the `ldap_r' library (-lldap_r). */ +#undef HAVE_LIBLDAP_R + +/* Define to 1 if you have the `m' library (-lm). */ +#undef HAVE_LIBM + +/* Define to 1 if you have the `pam' library (-lpam). */ +#undef HAVE_LIBPAM + +/* Define if you have a function readline library */ +#undef HAVE_LIBREADLINE + +/* Define to 1 if you have the `selinux' library (-lselinux). */ +#undef HAVE_LIBSELINUX + +/* Define to 1 if you have the `ssl' library (-lssl). */ +#undef HAVE_LIBSSL + +/* Define to 1 if you have the `wldap32' library (-lwldap32). */ +#undef HAVE_LIBWLDAP32 + +/* Define to 1 if you have the `xml2' library (-lxml2). */ +#undef HAVE_LIBXML2 + +/* Define to 1 if you have the `xslt' library (-lxslt). */ +#undef HAVE_LIBXSLT + +/* Define to 1 if you have the `z' library (-lz). */ +#undef HAVE_LIBZ + +/* Define to 1 if constants of type 'long long int' should have the suffix LL. + */ +#undef HAVE_LL_CONSTANTS + +/* Define to 1 if the system has the type `locale_t'. */ +#undef HAVE_LOCALE_T + +/* Define to 1 if `long int' works and is 64 bits. */ +#undef HAVE_LONG_INT_64 + +/* Define to 1 if the system has the type `long long int'. */ +#undef HAVE_LONG_LONG_INT + +/* Define to 1 if `long long int' works and is 64 bits. */ +#undef HAVE_LONG_LONG_INT_64 + +/* Define to 1 if you have the header file. */ +#undef HAVE_MBARRIER_H + +/* Define to 1 if you have the `mbstowcs_l' function. */ +#undef HAVE_MBSTOWCS_L + +/* Define to 1 if you have the `memmove' function. */ +#undef HAVE_MEMMOVE + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if the system has the type `MINIDUMP_TYPE'. */ +#undef HAVE_MINIDUMP_TYPE + +/* Define to 1 if you have the `mkdtemp' function. */ +#undef HAVE_MKDTEMP + +/* Define to 1 if you have the header file. */ +#undef HAVE_NETINET_IN_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_NETINET_TCP_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_NET_IF_H + +/* Define to 1 if you have the `OPENSSL_init_ssl' function. */ +#undef HAVE_OPENSSL_INIT_SSL + +/* Define to 1 if you have the header file. */ +#undef HAVE_OSSP_UUID_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_PAM_PAM_APPL_H + +/* Define to 1 if you have the `poll' function. */ +#undef HAVE_POLL + +/* Define to 1 if you have the header file. */ +#undef HAVE_POLL_H + +/* Define to 1 if you have the `posix_fadvise' function. */ +#undef HAVE_POSIX_FADVISE + +/* Define to 1 if the assembler supports PPC's LWARX mutex hint bit. */ +#undef HAVE_PPC_LWARX_MUTEX_HINT + +/* Define to 1 if you have the `pstat' function. */ +#undef HAVE_PSTAT + +/* Define to 1 if the PS_STRINGS thing exists. */ +#undef HAVE_PS_STRINGS + +/* Define if you have POSIX threads libraries and header files. */ +#undef HAVE_PTHREAD + +/* Define to 1 if you have the `pthread_is_threaded_np' function. */ +#undef HAVE_PTHREAD_IS_THREADED_NP + +/* Have PTHREAD_PRIO_INHERIT. */ +#undef HAVE_PTHREAD_PRIO_INHERIT + +/* Define to 1 if you have the header file. */ +#undef HAVE_PWD_H + +/* Define to 1 if you have the `random' function. */ +#undef HAVE_RANDOM + +/* Define to 1 if you have the `RAND_OpenSSL' function. */ +#undef HAVE_RAND_OPENSSL + +/* Define to 1 if you have the header file. */ +#undef HAVE_READLINE_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_READLINE_HISTORY_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_READLINE_READLINE_H + +/* Define to 1 if you have the `readlink' function. */ +#undef HAVE_READLINK + +/* Define to 1 if you have the `rint' function. */ +#undef HAVE_RINT + +/* Define to 1 if you have the global variable + 'rl_completion_append_character'. */ +#undef HAVE_RL_COMPLETION_APPEND_CHARACTER + +/* Define to 1 if you have the `rl_completion_matches' function. */ +#undef HAVE_RL_COMPLETION_MATCHES + +/* Define to 1 if you have the `rl_filename_completion_function' function. */ +#undef HAVE_RL_FILENAME_COMPLETION_FUNCTION + +/* Define to 1 if you have the `rl_reset_screen_size' function. */ +#undef HAVE_RL_RESET_SCREEN_SIZE + +/* Define to 1 if you have the header file. */ +#undef HAVE_SECURITY_PAM_APPL_H + +/* Define to 1 if you have the `setproctitle' function. */ +#undef HAVE_SETPROCTITLE + +/* Define to 1 if you have the `setsid' function. */ +#undef HAVE_SETSID + +/* Define to 1 if you have the `shm_open' function. */ +#undef HAVE_SHM_OPEN + +/* Define to 1 if you have the `snprintf' function. */ +#undef HAVE_SNPRINTF + +/* Define to 1 if you have spinlocks. */ +#undef HAVE_SPINLOCKS + +/* Define to 1 if you have the `srandom' function. */ +#undef HAVE_SRANDOM + +/* Define to 1 if you have the `SSL_get_current_compression' function. */ +#undef HAVE_SSL_GET_CURRENT_COMPRESSION + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the `strerror' function. */ +#undef HAVE_STRERROR + +/* Define to 1 if you have the `strerror_r' function. */ +#undef HAVE_STRERROR_R + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the `strlcat' function. */ +#undef HAVE_STRLCAT + +/* Define to 1 if you have the `strlcpy' function. */ +#undef HAVE_STRLCPY + +/* Define to 1 if you have the `strtoll' function. */ +#undef HAVE_STRTOLL + +/* Define to 1 if you have the `strtoq' function. */ +#undef HAVE_STRTOQ + +/* Define to 1 if you have the `strtoull' function. */ +#undef HAVE_STRTOULL + +/* Define to 1 if you have the `strtouq' function. */ +#undef HAVE_STRTOUQ + +/* Define to 1 if the system has the type `struct addrinfo'. */ +#undef HAVE_STRUCT_ADDRINFO + +/* Define to 1 if the system has the type `struct cmsgcred'. */ +#undef HAVE_STRUCT_CMSGCRED + +/* Define to 1 if the system has the type `struct option'. */ +#undef HAVE_STRUCT_OPTION + +/* Define to 1 if `sa_len' is a member of `struct sockaddr'. */ +#undef HAVE_STRUCT_SOCKADDR_SA_LEN + +/* Define to 1 if the system has the type `struct sockaddr_storage'. */ +#undef HAVE_STRUCT_SOCKADDR_STORAGE + +/* Define to 1 if `ss_family' is a member of `struct sockaddr_storage'. */ +#undef HAVE_STRUCT_SOCKADDR_STORAGE_SS_FAMILY + +/* Define to 1 if `ss_len' is a member of `struct sockaddr_storage'. */ +#undef HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN + +/* Define to 1 if `__ss_family' is a member of `struct sockaddr_storage'. */ +#undef HAVE_STRUCT_SOCKADDR_STORAGE___SS_FAMILY + +/* Define to 1 if `__ss_len' is a member of `struct sockaddr_storage'. */ +#undef HAVE_STRUCT_SOCKADDR_STORAGE___SS_LEN + +/* Define to 1 if `tm_zone' is a member of `struct tm'. */ +#undef HAVE_STRUCT_TM_TM_ZONE + +/* Define to 1 if you have the `symlink' function. */ +#undef HAVE_SYMLINK + +/* Define to 1 if you have the `sync_file_range' function. */ +#undef HAVE_SYNC_FILE_RANGE + +/* Define to 1 if you have the syslog interface. */ +#undef HAVE_SYSLOG + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_EPOLL_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_IOCTL_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_IPC_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_POLL_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_PSTAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_RESOURCE_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_SELECT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_SEM_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_SHM_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_SOCKET_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_SOCKIO_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TAS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TIME_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_UCRED_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_UN_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_TERMIOS_H + +/* Define to 1 if your `struct tm' has `tm_zone'. Deprecated, use + `HAVE_STRUCT_TM_TM_ZONE' instead. */ +#undef HAVE_TM_ZONE + +/* Define to 1 if you have the `towlower' function. */ +#undef HAVE_TOWLOWER + +/* Define to 1 if you have the external array `tzname'. */ +#undef HAVE_TZNAME + +/* Define to 1 if you have the header file. */ +#undef HAVE_UCRED_H + +/* Define to 1 if the system has the type `uint64'. */ +#undef HAVE_UINT64 + +/* Define to 1 if the system has the type `uint8'. */ +#undef HAVE_UINT8 + +/* Define to 1 if the system has the type `uintptr_t'. */ +#undef HAVE_UINTPTR_T + +/* Define to 1 if the system has the type `union semun'. */ +#undef HAVE_UNION_SEMUN + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define to 1 if you have unix sockets. */ +#undef HAVE_UNIX_SOCKETS + +/* Define to 1 if you have the `unsetenv' function. */ +#undef HAVE_UNSETENV + +/* Define to 1 if the system has the type `unsigned long long int'. */ +#undef HAVE_UNSIGNED_LONG_LONG_INT + +/* Define to 1 if you have the `utime' function. */ +#undef HAVE_UTIME + +/* Define to 1 if you have the `utimes' function. */ +#undef HAVE_UTIMES + +/* Define to 1 if you have the header file. */ +#undef HAVE_UTIME_H + +/* Define to 1 if you have BSD UUID support. */ +#undef HAVE_UUID_BSD + +/* Define to 1 if you have E2FS UUID support. */ +#undef HAVE_UUID_E2FS + +/* Define to 1 if you have the header file. */ +#undef HAVE_UUID_H + +/* Define to 1 if you have OSSP UUID support. */ +#undef HAVE_UUID_OSSP + +/* Define to 1 if you have the header file. */ +#undef HAVE_UUID_UUID_H + +/* Define to 1 if you have the `vsnprintf' function. */ +#undef HAVE_VSNPRINTF + +/* Define to 1 if you have the header file. */ +#undef HAVE_WCHAR_H + +/* Define to 1 if you have the `wcstombs' function. */ +#undef HAVE_WCSTOMBS + +/* Define to 1 if you have the `wcstombs_l' function. */ +#undef HAVE_WCSTOMBS_L + +/* Define to 1 if you have the header file. */ +#undef HAVE_WCTYPE_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_WINLDAP_H + +/* Define to 1 if your compiler understands __builtin_bswap32. */ +#undef HAVE__BUILTIN_BSWAP32 + +/* Define to 1 if your compiler understands __builtin_bswap64. */ +#undef HAVE__BUILTIN_BSWAP64 + +/* Define to 1 if your compiler understands __builtin_constant_p. */ +#undef HAVE__BUILTIN_CONSTANT_P + +/* Define to 1 if your compiler understands __builtin_types_compatible_p. */ +#undef HAVE__BUILTIN_TYPES_COMPATIBLE_P + +/* Define to 1 if your compiler understands __builtin_unreachable. */ +#undef HAVE__BUILTIN_UNREACHABLE + +/* Define to 1 if you have __cpuid. */ +#undef HAVE__CPUID + +/* Define to 1 if you have __get_cpuid. */ +#undef HAVE__GET_CPUID + +/* Define to 1 if your compiler understands _Static_assert. */ +#undef HAVE__STATIC_ASSERT + +/* Define to 1 if your compiler understands __VA_ARGS__ in macros. */ +#undef HAVE__VA_ARGS + +/* Define to the appropriate snprintf length modifier for 64-bit ints. */ +#undef INT64_MODIFIER + +/* Define to 1 if `locale_t' requires . */ +#undef LOCALE_T_IN_XLOCALE + +/* Define as the maximum alignment requirement of any C data type. */ +#undef MAXIMUM_ALIGNOF + +/* Define bytes to use libc memset(). */ +#undef MEMSET_LOOP_LIMIT + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the home page for this package. */ +#undef PACKAGE_URL + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* Define to the name of a signed 128-bit integer type. */ +#undef PG_INT128_TYPE + +/* Define to the name of a signed 64-bit integer type. */ +#undef PG_INT64_TYPE + +/* Define to the name of the default PostgreSQL service principal in Kerberos + (GSSAPI). (--with-krb-srvnam=NAME) */ +#undef PG_KRB_SRVNAM + +/* PostgreSQL major version as a string */ +#undef PG_MAJORVERSION + +/* Define to gnu_printf if compiler supports it, else printf. */ +#undef PG_PRINTF_ATTRIBUTE + +/* PostgreSQL version as a string */ +#undef PG_VERSION + +/* PostgreSQL version as a number */ +#undef PG_VERSION_NUM + +/* A string containing the version number, platform, and C compiler */ +#undef PG_VERSION_STR + +/* Define to 1 to allow profiling output to be saved separately for each + process. */ +#undef PROFILE_PID_DIR + +/* Define to necessary symbol if this constant uses a non-standard name on + your system. */ +#undef PTHREAD_CREATE_JOINABLE + +/* RELSEG_SIZE is the maximum number of blocks allowed in one disk file. Thus, + the maximum size of a single file is RELSEG_SIZE * BLCKSZ; relations bigger + than that are divided into multiple files. RELSEG_SIZE * BLCKSZ must be + less than your OS' limit on file size. This is often 2 GB or 4GB in a + 32-bit operating system, unless you have large file support enabled. By + default, we make the limit 1 GB to avoid any possible integer-overflow + problems within the OS. A limit smaller than necessary only means we divide + a large relation into more chunks than necessary, so it seems best to err + in the direction of a small limit. A power-of-2 value is recommended to + save a few cycles in md.c, but is not absolutely required. Changing + RELSEG_SIZE requires an initdb. */ +#undef RELSEG_SIZE + +/* The size of `long', as computed by sizeof. */ +#undef SIZEOF_LONG + +/* The size of `off_t', as computed by sizeof. */ +#undef SIZEOF_OFF_T + +/* The size of `size_t', as computed by sizeof. */ +#undef SIZEOF_SIZE_T + +/* The size of `void *', as computed by sizeof. */ +#undef SIZEOF_VOID_P + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Define to 1 if strerror_r() returns a int. */ +#undef STRERROR_R_INT + +/* Define to 1 if your declares `struct tm'. */ +#undef TM_IN_SYS_TIME + +/* Define to 1 to build with assertion checks. (--enable-cassert) */ +#undef USE_ASSERT_CHECKING + +/* Define to 1 to build with Bonjour support. (--with-bonjour) */ +#undef USE_BONJOUR + +/* Define to 1 to build with BSD Authentication support. (--with-bsd-auth) */ +#undef USE_BSD_AUTH + +/* Define to 1 if you want float4 values to be passed by value. + (--enable-float4-byval) */ +#undef USE_FLOAT4_BYVAL + +/* Define to 1 if you want float8, int8, etc values to be passed by value. + (--enable-float8-byval) */ +#undef USE_FLOAT8_BYVAL + +/* Define to 1 if you want 64-bit integer timestamp and interval support. + (--enable-integer-datetimes) */ +#undef USE_INTEGER_DATETIMES + +/* Define to 1 to build with LDAP support. (--with-ldap) */ +#undef USE_LDAP + +/* Define to 1 to build with XML support. (--with-libxml) */ +#undef USE_LIBXML + +/* Define to 1 to use XSLT support when building contrib/xml2. + (--with-libxslt) */ +#undef USE_LIBXSLT + +/* Define to select named POSIX semaphores. */ +#undef USE_NAMED_POSIX_SEMAPHORES + +/* Define to build with OpenSSL support. (--with-openssl) */ +#undef USE_OPENSSL + +/* Define to 1 to build with PAM support. (--with-pam) */ +#undef USE_PAM + +/* Use replacement snprintf() functions. */ +#undef USE_REPL_SNPRINTF + +/* Define to 1 to use Intel SSE 4.2 CRC instructions with a runtime check. */ +#undef USE_SLICING_BY_8_CRC32C + +/* Define to 1 use Intel SSE 4.2 CRC instructions. */ +#undef USE_SSE42_CRC32C + +/* Define to 1 to use Intel SSSE 4.2 CRC instructions with a runtime check. */ +#undef USE_SSE42_CRC32C_WITH_RUNTIME_CHECK + +/* Define to build with systemd support. (--with-systemd) */ +#undef USE_SYSTEMD + +/* Define to select SysV-style semaphores. */ +#undef USE_SYSV_SEMAPHORES + +/* Define to select SysV-style shared memory. */ +#undef USE_SYSV_SHARED_MEMORY + +/* Define to select unnamed POSIX semaphores. */ +#undef USE_UNNAMED_POSIX_SEMAPHORES + +/* Define to select Win32-style semaphores. */ +#undef USE_WIN32_SEMAPHORES + +/* Define to select Win32-style shared memory. */ +#undef USE_WIN32_SHARED_MEMORY + +/* Define to 1 if `wcstombs_l' requires . */ +#undef WCSTOMBS_L_IN_XLOCALE + +/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most + significant byte first (like Motorola and SPARC, unlike Intel). */ +#if defined AC_APPLE_UNIVERSAL_BUILD +# if defined __BIG_ENDIAN__ +# define WORDS_BIGENDIAN 1 +# endif +#else +# ifndef WORDS_BIGENDIAN +# undef WORDS_BIGENDIAN +# endif +#endif + +/* Size of a WAL file block. This need have no particular relation to BLCKSZ. + XLOG_BLCKSZ must be a power of 2, and if your system supports O_DIRECT I/O, + XLOG_BLCKSZ must be a multiple of the alignment requirement for direct-I/O + buffers, else direct I/O may fail. Changing XLOG_BLCKSZ requires an initdb. + */ +#undef XLOG_BLCKSZ + +/* XLOG_SEG_SIZE is the size of a single WAL file. This must be a power of 2 + and larger than XLOG_BLCKSZ (preferably, a great deal larger than + XLOG_BLCKSZ). Changing XLOG_SEG_SIZE requires an initdb. */ +#undef XLOG_SEG_SIZE + + + +/* Number of bits in a file offset, on hosts where this is settable. */ +#undef _FILE_OFFSET_BITS + +/* Define to 1 to make fseeko visible on some hosts (e.g. glibc 2.2). */ +#undef _LARGEFILE_SOURCE + +/* Define for large files, on AIX-style hosts. */ +#undef _LARGE_FILES + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef __cplusplus +#undef inline +#endif + +/* Define to the type of a signed integer type wide enough to hold a pointer, + if such a type exists, and if the system does not define it. */ +#undef intptr_t + +/* Define to empty if the C compiler does not understand signed types. */ +#undef signed + +/* Define to the type of an unsigned integer type wide enough to hold a + pointer, if such a type exists, and if the system does not define it. */ +#undef uintptr_t diff --git a/libpq/postgresql/pg_config.h.win32.orig b/libpq/postgresql/pg_config.h.win32.orig new file mode 100644 index 0000000..0e17ccb --- /dev/null +++ b/libpq/postgresql/pg_config.h.win32.orig @@ -0,0 +1,682 @@ +/* src/include/pg_config.h. Generated by configure. */ +/* src/include/pg_config.h.in. Generated from configure.in by autoheader. */ + +/* This file is generated from MingW ./configure, and with the following + * changes to be valid for Visual C++ (and compatible): + * + * HAVE_CBRT, HAVE_FUNCNAME_FUNC, HAVE_GETOPT, HAVE_GETOPT_H, HAVE_INTTYPES_H, + * HAVE_GETOPT_LONG, HAVE_LOCALE_T, HAVE_RINT, HAVE_STRINGS_H, HAVE_STRTOLL, + * HAVE_STRTOULL, HAVE_STRUCT_OPTION, ENABLE_THREAD_SAFETY, + * inline, USE_SSE42_CRC32C_WITH_RUNTIME_CHECK + */ + +/* Define to the type of arg 1 of 'accept' */ +#define ACCEPT_TYPE_ARG1 unsigned int + +/* Define to the type of arg 2 of 'accept' */ +#define ACCEPT_TYPE_ARG2 struct sockaddr * + +/* Define to the type of arg 3 of 'accept' */ +#define ACCEPT_TYPE_ARG3 int + +/* Define to the return type of 'accept' */ +#define ACCEPT_TYPE_RETURN unsigned int PASCAL + +/* The alignment requirement of a `double'. */ +#define ALIGNOF_DOUBLE 8 + +/* The alignment requirement of a `int'. */ +#define ALIGNOF_INT 4 + +/* The alignment requirement of a `long'. */ +#define ALIGNOF_LONG 4 + +/* The alignment requirement of a `long long int'. */ +#define ALIGNOF_LONG_LONG_INT 8 + +/* The alignment requirement of a `short'. */ +#define ALIGNOF_SHORT 2 + +/* Define to the default TCP port number on which the server listens and to + which clients will try to connect. This can be overridden at run-time, but + it's convenient if your clients have the right default compiled in. + (--with-pgport=PORTNUM) */ +#define DEF_PGPORT 5432 + +/* Define to the default TCP port number as a string constant. */ +#define DEF_PGPORT_STR "5432" + +/* Define to nothing if C supports flexible array members, and to 1 if it does + not. That way, with a declaration like `struct s { int n; double + d[FLEXIBLE_ARRAY_MEMBER]; };', the struct hack can be used with pre-C99 + compilers. When computing the size of such an object, don't use 'sizeof + (struct s)' as it overestimates the size. Use 'offsetof (struct s, d)' + instead. Don't use 'offsetof (struct s, d[0])', as this doesn't work with + MSVC and with C++ compilers. */ +#define FLEXIBLE_ARRAY_MEMBER + +/* Define to 1 if you want National Language Support. (--enable-nls) */ +/* #undef ENABLE_NLS */ + +/* Define to 1 to build client libraries as thread-safe code. + (--enable-thread-safety) */ +#define ENABLE_THREAD_SAFETY 1 + +/* Define to 1 if gettimeofday() takes only 1 argument. */ +/* #undef GETTIMEOFDAY_1ARG */ + +#ifdef GETTIMEOFDAY_1ARG +# define gettimeofday(a,b) gettimeofday(a) +#endif + +/* Define to 1 if you have the `cbrt' function. */ +//#define HAVE_CBRT 1 + +/* Define to 1 if you have the `class' function. */ +/* #undef HAVE_CLASS */ + +/* Define to 1 if you have the `crypt' function. */ +/* #undef HAVE_CRYPT */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_CRYPT_H */ + +/* Define to 1 if you have the declaration of `fdatasync', and to 0 if you + don't. */ +#define HAVE_DECL_FDATASYNC 0 + +/* Define to 1 if you have the declaration of `F_FULLFSYNC', and to 0 if you + don't. */ +#define HAVE_DECL_F_FULLFSYNC 0 + +/* Define to 1 if you have the declaration of `snprintf', and to 0 if you + don't. */ +#define HAVE_DECL_SNPRINTF 1 + +/* Define to 1 if you have the declaration of `vsnprintf', and to 0 if you + don't. */ +#define HAVE_DECL_VSNPRINTF 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_DLD_H */ + +/* Define to 1 if you have the `dlopen' function. */ +/* #undef HAVE_DLOPEN */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_EDITLINE_HISTORY_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_EDITLINE_READLINE_H */ + +/* Define to 1 if you have the `fcvt' function. */ +#define HAVE_FCVT 1 + +/* Define to 1 if you have the `fdatasync' function. */ +/* #undef HAVE_FDATASYNC */ + +/* Define to 1 if you have finite(). */ +#define HAVE_FINITE 1 + +/* Define to 1 if you have the `fpclass' function. */ +/* #undef HAVE_FPCLASS */ + +/* Define to 1 if you have the `fp_class' function. */ +/* #undef HAVE_FP_CLASS */ + +/* Define to 1 if you have the `fp_class_d' function. */ +/* #undef HAVE_FP_CLASS_D */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_FP_CLASS_H */ + +/* Define to 1 if fseeko (and presumably ftello) exists and is declared. */ +#define HAVE_FSEEKO 1 + +/* Define to 1 if your compiler understands __func__. */ +//#define HAVE_FUNCNAME__FUNC 1 + +/* Define to 1 if your compiler understands __FUNCTION__. */ +#define HAVE_FUNCNAME__FUNCTION 1 + +/* Define to 1 if you have getaddrinfo(). */ +/* #undef HAVE_GETADDRINFO */ + +/* Define to 1 if you have the `gethostbyname_r' function. */ +/* #undef HAVE_GETHOSTBYNAME_R */ + +/* Define to 1 if you have the `getopt' function. */ +//#define HAVE_GETOPT 1 + +/* Define to 1 if you have the header file. */ +//#define HAVE_GETOPT_H 1 + +/* Define to 1 if you have the `getopt_long' function. */ +//#define HAVE_GETOPT_LONG 1 + +/* Define to 1 if you have the `getpeereid' function. */ +/* #undef HAVE_GETPEEREID */ + +/* Define to 1 if you have the `getpwuid_r' function. */ +/* #undef HAVE_GETPWUID_R */ + +/* Define to 1 if you have the `getrusage' function. */ +/* #undef HAVE_GETRUSAGE */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_HISTORY_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_IEEEFP_H */ + +/* Define to 1 if you have the `inet_aton' function. */ +/* #undef HAVE_INET_ATON */ + +/* Define to 1 if the system has the type `int64'. */ +/* #undef HAVE_INT64 */ + +/* Define to 1 if the system has the type `int8'. */ +/* #undef HAVE_INT8 */ + +/* Define to 1 if you have the header file. */ +//#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the global variable 'int timezone'. */ +#define HAVE_INT_TIMEZONE 1 + +/* Define to 1 if you have support for IPv6. */ +#define HAVE_IPV6 1 + +/* Define to 1 if you have isinf(). */ +#define HAVE_ISINF 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_LANGINFO_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_LDAP_H */ + +/* Define to 1 if you have the `crypto' library (-lcrypto). */ +/* #undef HAVE_LIBCRYPTO */ + +/* Define to 1 if you have the `ldap' library (-lldap). */ +/* #undef HAVE_LIBLDAP */ + +/* Define to 1 if you have the `pam' library (-lpam). */ +/* #undef HAVE_LIBPAM */ + +/* Define if you have a function readline library */ +/* #undef HAVE_LIBREADLINE */ + +/* Define to 1 if you have the `ssl' library (-lssl). */ +/* #undef HAVE_LIBSSL */ + +/* Define to 1 if you have the `wldap32' library (-lwldap32). */ +/* #undef HAVE_LIBWLDAP32 */ + +/* Define to 1 if you have the `z' library (-lz). */ +/* #undef HAVE_LIBZ */ + +/* Define to 1 if constants of type 'long long int' should have the suffix LL. + */ +#if (_MSC_VER > 1200) +#define HAVE_LL_CONSTANTS 1 +#endif + +/* Define to 1 if the system has the type `locale_t'. */ +#define HAVE_LOCALE_T 1 + +/* Define to 1 if `long int' works and is 64 bits. */ +/* #undef HAVE_LONG_INT_64 */ + +/* Define to 1 if `long long int' works and is 64 bits. */ +#if (_MSC_VER > 1200) +#define HAVE_LONG_LONG_INT_64 +#endif + +/* Define to 1 if you have the `mbstowcs_l' function. */ +#define HAVE_MBSTOWCS_L 1 + +/* Define to 1 if you have the `memmove' function. */ +#define HAVE_MEMMOVE 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if the system has the type `MINIDUMP_TYPE'. */ +#define HAVE_MINIDUMP_TYPE 1 + +/* Define to 1 if you have the `mkdtemp' function. */ +/* #undef HAVE_MKDTEMP */ + +/* Define to 1 if you have the header file. */ +#define HAVE_NETINET_IN_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_NETINET_TCP_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_PAM_PAM_APPL_H */ + +/* Define to 1 if you have the `poll' function. */ +/* #undef HAVE_POLL */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_POLL_H */ + +/* Define to 1 if you have the `pstat' function. */ +/* #undef HAVE_PSTAT */ + +/* Define to 1 if the PS_STRINGS thing exists. */ +/* #undef HAVE_PS_STRINGS */ + +/* Define to 1 if you have the header file. */ +#define HAVE_PWD_H 1 + +/* Define to 1 if you have the `random' function. */ +/* #undef HAVE_RANDOM */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_READLINE_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_READLINE_HISTORY_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_READLINE_READLINE_H */ + +/* Define to 1 if you have the `readlink' function. */ +/* #undef HAVE_READLINK */ + +/* Define to 1 if you have the `rint' function. */ +#if (_MSC_VER >= 1800) +#define HAVE_RINT 1 +#endif + + +/* Define to 1 if you have the global variable + 'rl_completion_append_character'. */ +/* #undef HAVE_RL_COMPLETION_APPEND_CHARACTER */ + +/* Define to 1 if you have the `rl_completion_matches' function. */ +/* #undef HAVE_RL_COMPLETION_MATCHES */ + +/* Define to 1 if you have the `rl_filename_completion_function' function. */ +/* #undef HAVE_RL_FILENAME_COMPLETION_FUNCTION */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SECURITY_PAM_APPL_H */ + +/* Define to 1 if you have the `setproctitle' function. */ +/* #undef HAVE_SETPROCTITLE */ + +/* Define to 1 if you have the `setsid' function. */ +/* #undef HAVE_SETSID */ + +/* Define to 1 if you have the `snprintf' function. */ +/* #undef HAVE_SNPRINTF */ + +/* Define to 1 if you have spinlocks. */ +#define HAVE_SPINLOCKS 1 + +/* Define to 1 if you have atomics. */ +#define HAVE_ATOMICS 1 + +/* Define to 1 if you have the `srandom' function. */ +/* #undef HAVE_SRANDOM */ + +/* Define to 1 if you have the `SSL_get_current_compression' function. */ +#define HAVE_SSL_GET_CURRENT_COMPRESSION 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_STDINT_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the `strerror' function. */ +#ifndef HAVE_STRERROR +#define HAVE_STRERROR 1 +#endif + +/* Define to 1 if you have the `strerror_r' function. */ +/* #undef HAVE_STRERROR_R */ + +/* Define to 1 if you have the header file. */ +/*#define HAVE_STRINGS_H 1 */ + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the `strtoll' function. */ +//#define HAVE_STRTOLL 1 + +/* Define to 1 if you have the `strtoq' function. */ +/* #undef HAVE_STRTOQ */ + +/* Define to 1 if you have the `strtoull' function. */ +//#define HAVE_STRTOULL 1 + +/* Define to 1 if you have the `strtouq' function. */ +/* #undef HAVE_STRTOUQ */ + +/* Define to 1 if the system has the type `struct addrinfo'. */ +#if (_MSC_VER > 1200) +#define HAVE_STRUCT_ADDRINFO 1 +#endif + +/* Define to 1 if the system has the type `struct cmsgcred'. */ +/* #undef HAVE_STRUCT_CMSGCRED */ + +/* Define to 1 if the system has the type `struct option'. */ +//#define HAVE_STRUCT_OPTION 1 + +/* Define to 1 if `sa_len' is member of `struct sockaddr'. */ +/* #undef HAVE_STRUCT_SOCKADDR_SA_LEN */ + +/* Define to 1 if the system has the type `struct sockaddr_storage'. */ +#if (_MSC_VER > 1200) +#define HAVE_STRUCT_SOCKADDR_STORAGE 1 +#endif + +/* Define to 1 if `ss_family' is member of `struct sockaddr_storage'. */ +#if (_MSC_VER > 1200) +#define HAVE_STRUCT_SOCKADDR_STORAGE_SS_FAMILY 1 +#endif + +/* Define to 1 if `ss_len' is member of `struct sockaddr_storage'. */ +/* #undef HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN */ + +/* Define to 1 if `__ss_family' is member of `struct sockaddr_storage'. */ +/* #undef HAVE_STRUCT_SOCKADDR_STORAGE___SS_FAMILY */ + +/* Define to 1 if `__ss_len' is member of `struct sockaddr_storage'. */ +/* #undef HAVE_STRUCT_SOCKADDR_STORAGE___SS_LEN */ + +/* Define to 1 if the system has the type `struct sockaddr_un'. */ +/* #undef HAVE_STRUCT_SOCKADDR_UN */ + +/* Define to 1 if `tm_zone' is member of `struct tm'. */ +/* #undef HAVE_STRUCT_TM_TM_ZONE */ + +/* Define to 1 if you have the `symlink' function. */ +#define HAVE_SYMLINK 1 + +/* Define to 1 if you have the `sync_file_range' function. */ +/* #undef HAVE_SYNC_FILE_RANGE */ + +/* Define to 1 if you have the `sysconf' function. */ +/* #undef HAVE_SYSCONF */ + +/* Define to 1 if you have the syslog interface. */ +/* #undef HAVE_SYSLOG */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_IPC_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_POLL_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_PSTAT_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_SELECT_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_SEM_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_SHM_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_SOCKET_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TIME_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_UCRED_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_UN_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_TERMIOS_H */ + +/* Define to 1 if your `struct tm' has `tm_zone'. Deprecated, use + `HAVE_STRUCT_TM_TM_ZONE' instead. */ +/* #undef HAVE_TM_ZONE */ + +/* Define to 1 if you have the `towlower' function. */ +#define HAVE_TOWLOWER 1 + +/* Define to 1 if you have the external array `tzname'. */ +/* #undef HAVE_TZNAME */ + +/* Define to 1 if the system has the type `uint64'. */ +/* #undef HAVE_UINT64 */ + +/* Define to 1 if the system has the type `uint8'. */ +/* #undef HAVE_UINT8 */ + +/* Define to 1 if the system has the type `union semun'. */ +/* #undef HAVE_UNION_SEMUN */ + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to 1 if you have unix sockets. */ +/* #undef HAVE_UNIX_SOCKETS */ + +/* Define to 1 if you have the `unsetenv' function. */ +/* #undef HAVE_UNSETENV */ + +/* Define to 1 if you have the `utime' function. */ +#define HAVE_UTIME 1 + +/* Define to 1 if you have the `utimes' function. */ +/* #undef HAVE_UTIMES */ + +/* Define to 1 if you have the header file. */ +#define HAVE_UTIME_H 1 + +/* Define to 1 if you have the `vsnprintf' function. */ +#define HAVE_VSNPRINTF 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_WCHAR_H 1 + +/* Define to 1 if you have the `wcstombs' function. */ +#define HAVE_WCSTOMBS 1 + +/* Define to 1 if you have the `wcstombs_l' function. */ +#define HAVE_WCSTOMBS_L 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_WCTYPE_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_WINLDAP_H */ + +/* Define to 1 if your compiler understands __builtin_bswap32. */ +/* #undef HAVE__BUILTIN_BSWAP32 */ + +/* Define to 1 if your compiler understands __builtin_bswap64. */ +/* #undef HAVE__BUILTIN_BSWAP64 */ + +/* Define to 1 if your compiler understands __builtin_constant_p. */ +/* #undef HAVE__BUILTIN_CONSTANT_P */ + +/* Define to 1 if your compiler understands __builtin_types_compatible_p. */ +/* #undef HAVE__BUILTIN_TYPES_COMPATIBLE_P */ + +/* Define to 1 if your compiler understands __builtin_unreachable. */ +/* #undef HAVE__BUILTIN_UNREACHABLE */ + +/* Define to 1 if you have __cpuid. */ +#define HAVE__CPUID 1 + +/* Define to 1 if you have __get_cpuid. */ +#undef HAVE__GET_CPUID + +/* Define to 1 if your compiler understands _Static_assert. */ +/* #undef HAVE__STATIC_ASSERT */ + +/* Define to 1 if your compiler understands __VA_ARGS__ in macros. */ +#define HAVE__VA_ARGS 1 + +/* Define to the appropriate snprintf length modifier for 64-bit ints. */ +#define INT64_MODIFIER "ll" + +/* Define to 1 if `locale_t' requires . */ +/* #undef LOCALE_T_IN_XLOCALE */ + +/* Define to the location of locale files. */ +/* #undef LOCALEDIR */ + +/* Define as the maximum alignment requirement of any C data type. */ +#define MAXIMUM_ALIGNOF 8 + +/* Define bytes to use libc memset(). */ +#define MEMSET_LOOP_LIMIT 1024 + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "pgsql-bugs@postgresql.org" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "PostgreSQL" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "PostgreSQL 9.6.5" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "9.6.5" + +/* Define to the name of a signed 128-bit integer type. */ +#undef PG_INT128_TYPE + +/* Define to the name of a signed 64-bit integer type. */ +#define PG_INT64_TYPE long long int + +/* PostgreSQL version as a string */ +#define PG_VERSION "9.6.5" + +/* PostgreSQL version as a number */ +#define PG_VERSION_NUM 90605 + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "postgresql" + +/* Define to the name of the default PostgreSQL service principal in Kerberos. + (--with-krb-srvnam=NAME) */ +#define PG_KRB_SRVNAM "postgres" + +/* A string containing the version number, platform, and C compiler */ +#define PG_VERSION_STR "Uninitialized version string (win32)" + +/* The size of `long', as computed by sizeof. */ +#define SIZEOF_LONG 4 + +/* The size of `size_t', as computed by sizeof. */ +#ifndef _WIN64 +#define SIZEOF_SIZE_T 4 +#else +#define SIZEOF_SIZE_T 8 +#endif + +/* The size of `void *', as computed by sizeof. */ +#ifndef _WIN64 +#define SIZEOF_VOID_P 4 +#else +#define SIZEOF_VOID_P 8 +#endif + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Define to 1 if strerror_r() returns a int. */ +/* #undef STRERROR_R_INT */ + +/* Define to 1 if your declares `struct tm'. */ +/* #undef TM_IN_SYS_TIME */ + +/* Define to 1 to build with assertion checks. (--enable-cassert) */ +/* #undef USE_ASSERT_CHECKING */ + +/* Define to 1 to build with Bonjour support. (--with-bonjour) */ +/* #undef USE_BONJOUR */ + +/* Define to 1 to build with BSD Authentication support. (--with-bsd-auth) */ +/* #undef USE_BSD_AUTH */ + +/* Define to 1 if you want 64-bit integer timestamp and interval support. + (--enable-integer-datetimes) */ +/* #undef USE_INTEGER_DATETIMES */ + +/* Define to 1 to build with LDAP support. (--with-ldap) */ +/* #undef USE_LDAP */ + +/* Define to select named POSIX semaphores. */ +/* #undef USE_NAMED_POSIX_SEMAPHORES */ + +/* Define to build with OpenSSL support. (--with-openssl) */ +/* #undef USE_OPENSSL */ + +/* Define to 1 to build with PAM support. (--with-pam) */ +/* #undef USE_PAM */ + +/* Use replacement snprintf() functions. */ +#define USE_REPL_SNPRINTF 1 + +/* Define to 1 to use Intel SSE 4.2 CRC instructions with a runtime check. */ +#if (_MSC_VER < 1500) +#define USE_SLICING_BY_8_CRC32C 1 +#endif + +/* Define to 1 use Intel SSE 4.2 CRC instructions. */ +/* #undef USE_SSE42_CRC32C */ + +/* Define to 1 to use Intel SSSE 4.2 CRC instructions with a runtime check. */ +#if (_MSC_VER >= 1500) +#define USE_SSE42_CRC32C_WITH_RUNTIME_CHECK +#endif + +/* Define to select SysV-style semaphores. */ +/* #undef USE_SYSV_SEMAPHORES */ + +/* Define to select SysV-style shared memory. */ +#define USE_SYSV_SHARED_MEMORY 1 + +/* Define to select unnamed POSIX semaphores. */ +/* #undef USE_UNNAMED_POSIX_SEMAPHORES */ + +/* Define to select Win32-style semaphores. */ +#define USE_WIN32_SEMAPHORES 1 + +/* Define to 1 if `wcstombs_l' requires . */ +/* #undef WCSTOMBS_L_IN_XLOCALE */ + +/* Number of bits in a file offset, on hosts where this is settable. */ +/* #undef _FILE_OFFSET_BITS */ + +/* Define to 1 to make fseeko visible on some hosts (e.g. glibc 2.2). */ +/* #undef _LARGEFILE_SOURCE */ + +/* Define for large files, on AIX-style hosts. */ +/* #undef _LARGE_FILES */ + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef __cplusplus +#define inline __inline +#endif + +/* Define to empty if the C compiler does not understand signed types. */ +/* #undef signed */ diff --git a/libpq/postgresql/pg_config_ext.h b/libpq/postgresql/pg_config_ext.h new file mode 100644 index 0000000..78a5178 --- /dev/null +++ b/libpq/postgresql/pg_config_ext.h @@ -0,0 +1,20 @@ +/* file : libpq/postgresql/pg_config_ext.h -*- C -*- + * copyright : Copyright (c) 2016-2017 Code Synthesis Ltd + * license : PostgreSQL License; see accompanying COPYRIGHT file + */ + +/* + * For the semanics of the following macros refer to the + * libpq/postgresql/pg_config_ext.h.in.orig and + * libpq/postgresql/pg_config_ext.h.win32.orig file. + */ + +/* + * Note that is invented by C99 and we can't expect that the libpq + * client is compiled according to this standard. However, when compile with + * GCC, Clang or VC, even requesting C90 standard explicitly, then the header + * and int64_t type are both available. + */ +#include + +#define PG_INT64_TYPE int64_t diff --git a/libpq/postgresql/pg_config_ext.h.in.orig b/libpq/postgresql/pg_config_ext.h.in.orig new file mode 100644 index 0000000..8acadbd --- /dev/null +++ b/libpq/postgresql/pg_config_ext.h.in.orig @@ -0,0 +1,7 @@ +/* + * src/include/pg_config_ext.h.in. This is generated manually, not by + * autoheader, since we want to limit which symbols get defined here. + */ + +/* Define to the name of a signed 64-bit integer type. */ +#undef PG_INT64_TYPE diff --git a/libpq/postgresql/pg_config_ext.h.win32.orig b/libpq/postgresql/pg_config_ext.h.win32.orig new file mode 100644 index 0000000..65bbb5d --- /dev/null +++ b/libpq/postgresql/pg_config_ext.h.win32.orig @@ -0,0 +1,7 @@ +/* + * src/include/pg_config_ext.h.win32. This is generated manually, not by + * autoheader, since we want to limit which symbols get defined here. + */ + +/* Define to the name of a signed 64-bit integer type. */ +#define PG_INT64_TYPE long long int diff --git a/libpq/postgresql/pg_config_manual.h b/libpq/postgresql/pg_config_manual.h new file mode 100644 index 0000000..96885bb --- /dev/null +++ b/libpq/postgresql/pg_config_manual.h @@ -0,0 +1,327 @@ +/*------------------------------------------------------------------------ + * PostgreSQL manual configuration settings + * + * This file contains various configuration symbols and limits. In + * all cases, changing them is only useful in very rare situations or + * for developers. If you edit any of these, be sure to do a *full* + * rebuild (and an initdb if noted). + * + * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/pg_config_manual.h + *------------------------------------------------------------------------ + */ + +/* + * Maximum length for identifiers (e.g. table names, column names, + * function names). Names actually are limited to one less byte than this, + * because the length must include a trailing zero byte. + * + * Changing this requires an initdb. + */ +#define NAMEDATALEN 64 + +/* + * Maximum number of arguments to a function. + * + * The minimum value is 8 (GIN indexes use 8-argument support functions). + * The maximum possible value is around 600 (limited by index tuple size in + * pg_proc's index; BLCKSZ larger than 8K would allow more). Values larger + * than needed will waste memory and processing time, but do not directly + * cost disk space. + * + * Changing this does not require an initdb, but it does require a full + * backend recompile (including any user-defined C functions). + */ +#define FUNC_MAX_ARGS 100 + +/* + * Maximum number of columns in an index. There is little point in making + * this anything but a multiple of 32, because the main cost is associated + * with index tuple header size (see access/itup.h). + * + * Changing this requires an initdb. + */ +#define INDEX_MAX_KEYS 32 + +/* + * Set the upper and lower bounds of sequence values. + */ +#define SEQ_MAXVALUE PG_INT64_MAX +#define SEQ_MINVALUE (-SEQ_MAXVALUE) + +/* + * When we don't have native spinlocks, we use semaphores to simulate them. + * Decreasing this value reduces consumption of OS resources; increasing it + * may improve performance, but supplying a real spinlock implementation is + * probably far better. + */ +#define NUM_SPINLOCK_SEMAPHORES 128 + +/* + * When we have neither spinlocks nor atomic operations support we're + * implementing atomic operations on top of spinlock on top of semaphores. To + * be safe against atomic operations while holding a spinlock separate + * semaphores have to be used. + */ +#define NUM_ATOMICS_SEMAPHORES 64 + +/* + * Define this if you want to allow the lo_import and lo_export SQL + * functions to be executed by ordinary users. By default these + * functions are only available to the Postgres superuser. CAUTION: + * These functions are SECURITY HOLES since they can read and write + * any file that the PostgreSQL server has permission to access. If + * you turn this on, don't say we didn't warn you. + */ +/* #define ALLOW_DANGEROUS_LO_FUNCTIONS */ + +/* + * MAXPGPATH: standard size of a pathname buffer in PostgreSQL (hence, + * maximum usable pathname length is one less). + * + * We'd use a standard system header symbol for this, if there weren't + * so many to choose from: MAXPATHLEN, MAX_PATH, PATH_MAX are all + * defined by different "standards", and often have different values + * on the same platform! So we just punt and use a reasonably + * generous setting here. + */ +#define MAXPGPATH 1024 + +/* + * PG_SOMAXCONN: maximum accept-queue length limit passed to + * listen(2). You'd think we should use SOMAXCONN from + * , but on many systems that symbol is much smaller + * than the kernel's actual limit. In any case, this symbol need be + * twiddled only if you have a kernel that refuses large limit values, + * rather than silently reducing the value to what it can handle + * (which is what most if not all Unixen do). + */ +#define PG_SOMAXCONN 10000 + +/* + * You can try changing this if you have a machine with bytes of + * another size, but no guarantee... + */ +#define BITS_PER_BYTE 8 + +/* + * Preferred alignment for disk I/O buffers. On some CPUs, copies between + * user space and kernel space are significantly faster if the user buffer + * is aligned on a larger-than-MAXALIGN boundary. Ideally this should be + * a platform-dependent value, but for now we just hard-wire it. + */ +#define ALIGNOF_BUFFER 32 + +/* + * Disable UNIX sockets for certain operating systems. + */ +#if defined(WIN32) +#undef HAVE_UNIX_SOCKETS +#endif + +/* + * Define this if your operating system supports link() + */ +#if !defined(WIN32) && !defined(__CYGWIN__) +#define HAVE_WORKING_LINK 1 +#endif + +/* + * USE_POSIX_FADVISE controls whether Postgres will attempt to use the + * posix_fadvise() kernel call. Usually the automatic configure tests are + * sufficient, but some older Linux distributions had broken versions of + * posix_fadvise(). If necessary you can remove the #define here. + */ +#if HAVE_DECL_POSIX_FADVISE && defined(HAVE_POSIX_FADVISE) +#define USE_POSIX_FADVISE +#endif + +/* + * USE_PREFETCH code should be compiled only if we have a way to implement + * prefetching. (This is decoupled from USE_POSIX_FADVISE because there + * might in future be support for alternative low-level prefetch APIs.) + */ +#ifdef USE_POSIX_FADVISE +#define USE_PREFETCH +#endif + +/* + * Default and maximum values for backend_flush_after, bgwriter_flush_after + * and checkpoint_flush_after; measured in blocks. Currently, these are + * enabled by default if sync_file_range() exists, ie, only on Linux. Perhaps + * we could also enable by default if we have mmap and msync(MS_ASYNC)? + */ +#ifdef HAVE_SYNC_FILE_RANGE +#define DEFAULT_BACKEND_FLUSH_AFTER 0 /* never enabled by default */ +#define DEFAULT_BGWRITER_FLUSH_AFTER 64 +#define DEFAULT_CHECKPOINT_FLUSH_AFTER 32 +#else +#define DEFAULT_BACKEND_FLUSH_AFTER 0 +#define DEFAULT_BGWRITER_FLUSH_AFTER 0 +#define DEFAULT_CHECKPOINT_FLUSH_AFTER 0 +#endif +/* upper limit for all three variables */ +#define WRITEBACK_MAX_PENDING_FLUSHES 256 + +/* + * USE_SSL code should be compiled only when compiling with an SSL + * implementation. (Currently, only OpenSSL is supported, but we might add + * more implementations in the future.) + */ +#ifdef USE_OPENSSL +#define USE_SSL +#endif + +/* + * This is the default directory in which AF_UNIX socket files are + * placed. Caution: changing this risks breaking your existing client + * applications, which are likely to continue to look in the old + * directory. But if you just hate the idea of sockets in /tmp, + * here's where to twiddle it. You can also override this at runtime + * with the postmaster's -k switch. + */ +#define DEFAULT_PGSOCKET_DIR "/tmp" + +/* + * This is the default event source for Windows event log. + */ +#define DEFAULT_EVENT_SOURCE "PostgreSQL" + +/* + * The random() function is expected to yield values between 0 and + * MAX_RANDOM_VALUE. Currently, all known implementations yield + * 0..2^31-1, so we just hardwire this constant. We could do a + * configure test if it proves to be necessary. CAUTION: Think not to + * replace this with RAND_MAX. RAND_MAX defines the maximum value of + * the older rand() function, which is often different from --- and + * considerably inferior to --- random(). + */ +#define MAX_RANDOM_VALUE PG_INT32_MAX + +/* + * On PPC machines, decide whether to use the mutex hint bit in LWARX + * instructions. Setting the hint bit will slightly improve spinlock + * performance on POWER6 and later machines, but does nothing before that, + * and will result in illegal-instruction failures on some pre-POWER4 + * machines. By default we use the hint bit when building for 64-bit PPC, + * which should be safe in nearly all cases. You might want to override + * this if you are building 32-bit code for a known-recent PPC machine. + */ +#ifdef HAVE_PPC_LWARX_MUTEX_HINT /* must have assembler support in any case */ +#if defined(__ppc64__) || defined(__powerpc64__) +#define USE_PPC_LWARX_MUTEX_HINT +#endif +#endif + +/* + * On PPC machines, decide whether to use LWSYNC instructions in place of + * ISYNC and SYNC. This provides slightly better performance, but will + * result in illegal-instruction failures on some pre-POWER4 machines. + * By default we use LWSYNC when building for 64-bit PPC, which should be + * safe in nearly all cases. + */ +#if defined(__ppc64__) || defined(__powerpc64__) +#define USE_PPC_LWSYNC +#endif + +/* + * Assumed cache line size. This doesn't affect correctness, but can be used + * for low-level optimizations. Currently, this is used to pad some data + * structures in xlog.c, to ensure that highly-contended fields are on + * different cache lines. Too small a value can hurt performance due to false + * sharing, while the only downside of too large a value is a few bytes of + * wasted memory. The default is 128, which should be large enough for all + * supported platforms. + */ +#define PG_CACHE_LINE_SIZE 128 + +/* + *------------------------------------------------------------------------ + * The following symbols are for enabling debugging code, not for + * controlling user-visible features or resource limits. + *------------------------------------------------------------------------ + */ + +/* + * Include Valgrind "client requests", mostly in the memory allocator, so + * Valgrind understands PostgreSQL memory contexts. This permits detecting + * memory errors that Valgrind would not detect on a vanilla build. See also + * src/tools/valgrind.supp. "make installcheck" runs 20-30x longer under + * Valgrind. Note that USE_VALGRIND slowed older versions of Valgrind by an + * additional order of magnitude; Valgrind 3.8.1 does not have this problem. + * The client requests fall in hot code paths, so USE_VALGRIND also slows + * native execution by a few percentage points. + * + * You should normally use MEMORY_CONTEXT_CHECKING with USE_VALGRIND; + * instrumentation of repalloc() is inferior without it. + */ +/* #define USE_VALGRIND */ + +/* + * Define this to cause pfree()'d memory to be cleared immediately, to + * facilitate catching bugs that refer to already-freed values. + * Right now, this gets defined automatically if --enable-cassert. + */ +#ifdef USE_ASSERT_CHECKING +#define CLOBBER_FREED_MEMORY +#endif + +/* + * Define this to check memory allocation errors (scribbling on more + * bytes than were allocated). Right now, this gets defined + * automatically if --enable-cassert or USE_VALGRIND. + */ +#if defined(USE_ASSERT_CHECKING) || defined(USE_VALGRIND) +#define MEMORY_CONTEXT_CHECKING +#endif + +/* + * Define this to cause palloc()'d memory to be filled with random data, to + * facilitate catching code that depends on the contents of uninitialized + * memory. Caution: this is horrendously expensive. + */ +/* #define RANDOMIZE_ALLOCATED_MEMORY */ + +/* + * Define this to force all parse and plan trees to be passed through + * copyObject(), to facilitate catching errors and omissions in + * copyObject(). + */ +/* #define COPY_PARSE_PLAN_TREES */ + +/* + * Define this to force all raw parse trees for DML statements to be scanned + * by raw_expression_tree_walker(), to facilitate catching errors and + * omissions in that function. + */ +/* #define RAW_EXPRESSION_COVERAGE_TEST */ + +/* + * Enable debugging print statements for lock-related operations. + */ +/* #define LOCK_DEBUG */ + +/* + * Enable debugging print statements for WAL-related operations; see + * also the wal_debug GUC var. + */ +/* #define WAL_DEBUG */ + +/* + * Enable tracing of resource consumption during sort operations; + * see also the trace_sort GUC var. For 8.1 this is enabled by default. + */ +#define TRACE_SORT 1 + +/* + * Enable tracing of syncscan operations (see also the trace_syncscan GUC var). + */ +/* #define TRACE_SYNCSCAN */ + +/* + * Other debug #defines (documentation, anyone?) + */ +/* #define HEAPDEBUGALL */ +/* #define ACLDEBUG */ diff --git a/libpq/postgresql/pg_config_paths.h b/libpq/postgresql/pg_config_paths.h new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/libpq/postgresql/pg_config_paths.h @@ -0,0 +1 @@ + diff --git a/libpq/postgresql/port.h b/libpq/postgresql/port.h new file mode 100644 index 0000000..a61e59c --- /dev/null +++ b/libpq/postgresql/port.h @@ -0,0 +1,477 @@ +/*------------------------------------------------------------------------- + * + * port.h + * Header for src/port/ compatibility functions. + * + * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/port.h + * + *------------------------------------------------------------------------- + */ +#ifndef PG_PORT_H +#define PG_PORT_H + +#include +#include +#include + +/* socket has a different definition on WIN32 */ +#ifndef WIN32 +typedef int pgsocket; + +#define PGINVALID_SOCKET (-1) +#else +typedef SOCKET pgsocket; + +#define PGINVALID_SOCKET INVALID_SOCKET +#endif + +/* non-blocking */ +extern bool pg_set_noblock(pgsocket sock); +extern bool pg_set_block(pgsocket sock); + +/* Portable path handling for Unix/Win32 (in path.c) */ + +extern bool has_drive_prefix(const char *filename); +extern char *first_dir_separator(const char *filename); +extern char *last_dir_separator(const char *filename); +extern char *first_path_var_separator(const char *pathlist); +extern void join_path_components(char *ret_path, + const char *head, const char *tail); +extern void canonicalize_path(char *path); +extern void make_native_path(char *path); +extern void cleanup_path(char *path); +extern bool path_contains_parent_reference(const char *path); +extern bool path_is_relative_and_below_cwd(const char *path); +extern bool path_is_prefix_of_path(const char *path1, const char *path2); +extern char *make_absolute_path(const char *path); +extern const char *get_progname(const char *argv0); +extern void get_share_path(const char *my_exec_path, char *ret_path); +extern void get_etc_path(const char *my_exec_path, char *ret_path); +extern void get_include_path(const char *my_exec_path, char *ret_path); +extern void get_pkginclude_path(const char *my_exec_path, char *ret_path); +extern void get_includeserver_path(const char *my_exec_path, char *ret_path); +extern void get_lib_path(const char *my_exec_path, char *ret_path); +extern void get_pkglib_path(const char *my_exec_path, char *ret_path); +extern void get_locale_path(const char *my_exec_path, char *ret_path); +extern void get_doc_path(const char *my_exec_path, char *ret_path); +extern void get_html_path(const char *my_exec_path, char *ret_path); +extern void get_man_path(const char *my_exec_path, char *ret_path); +extern bool get_home_path(char *ret_path); +extern void get_parent_directory(char *path); + +/* common/pgfnames.c */ +extern char **pgfnames(const char *path); +extern void pgfnames_cleanup(char **filenames); + +/* + * is_absolute_path + * + * By making this a macro we avoid needing to include path.c in libpq. + */ +#ifndef WIN32 +#define IS_DIR_SEP(ch) ((ch) == '/') + +#define is_absolute_path(filename) \ +( \ + IS_DIR_SEP((filename)[0]) \ +) +#else +#define IS_DIR_SEP(ch) ((ch) == '/' || (ch) == '\\') + +/* See path_is_relative_and_below_cwd() for how we handle 'E:abc'. */ +#define is_absolute_path(filename) \ +( \ + IS_DIR_SEP((filename)[0]) || \ + (isalpha((unsigned char) ((filename)[0])) && (filename)[1] == ':' && \ + IS_DIR_SEP((filename)[2])) \ +) +#endif + +/* Portable locale initialization (in exec.c) */ +extern void set_pglocale_pgservice(const char *argv0, const char *app); + +/* Portable way to find binaries (in exec.c) */ +extern int find_my_exec(const char *argv0, char *retpath); +extern int find_other_exec(const char *argv0, const char *target, + const char *versionstr, char *retpath); + +/* Windows security token manipulation (in exec.c) */ +#ifdef WIN32 +extern BOOL AddUserToTokenDacl(HANDLE hToken); +#endif + + +#if defined(WIN32) || defined(__CYGWIN__) +#define EXE ".exe" +#else +#define EXE "" +#endif + +#if defined(WIN32) && !defined(__CYGWIN__) +#define DEVNULL "nul" +#else +#define DEVNULL "/dev/null" +#endif + +/* Portable delay handling */ +extern void pg_usleep(long microsec); + +/* Portable SQL-like case-independent comparisons and conversions */ +extern int pg_strcasecmp(const char *s1, const char *s2); +extern int pg_strncasecmp(const char *s1, const char *s2, size_t n); +extern unsigned char pg_toupper(unsigned char ch); +extern unsigned char pg_tolower(unsigned char ch); +extern unsigned char pg_ascii_toupper(unsigned char ch); +extern unsigned char pg_ascii_tolower(unsigned char ch); + +#ifdef USE_REPL_SNPRINTF + +/* + * Versions of libintl >= 0.13 try to replace printf() and friends with + * macros to their own versions that understand the %$ format. We do the + * same, so disable their macros, if they exist. + */ +#ifdef vsnprintf +#undef vsnprintf +#endif +#ifdef snprintf +#undef snprintf +#endif +#ifdef sprintf +#undef sprintf +#endif +#ifdef vfprintf +#undef vfprintf +#endif +#ifdef fprintf +#undef fprintf +#endif +#ifdef printf +#undef printf +#endif + +extern int pg_vsnprintf(char *str, size_t count, const char *fmt, va_list args); +extern int pg_snprintf(char *str, size_t count, const char *fmt,...) pg_attribute_printf(3, 4); +extern int pg_sprintf(char *str, const char *fmt,...) pg_attribute_printf(2, 3); +extern int pg_vfprintf(FILE *stream, const char *fmt, va_list args); +extern int pg_fprintf(FILE *stream, const char *fmt,...) pg_attribute_printf(2, 3); +extern int pg_printf(const char *fmt,...) pg_attribute_printf(1, 2); + +/* + * The GCC-specific code below prevents the pg_attribute_printf above from + * being replaced, and this is required because gcc doesn't know anything + * about pg_printf. + */ +#ifdef __GNUC__ +#define vsnprintf(...) pg_vsnprintf(__VA_ARGS__) +#define snprintf(...) pg_snprintf(__VA_ARGS__) +#define sprintf(...) pg_sprintf(__VA_ARGS__) +#define vfprintf(...) pg_vfprintf(__VA_ARGS__) +#define fprintf(...) pg_fprintf(__VA_ARGS__) +#define printf(...) pg_printf(__VA_ARGS__) +#else +#define vsnprintf pg_vsnprintf +#define snprintf pg_snprintf +#define sprintf pg_sprintf +#define vfprintf pg_vfprintf +#define fprintf pg_fprintf +#define printf pg_printf +#endif +#endif /* USE_REPL_SNPRINTF */ + +#if defined(WIN32) +/* + * Versions of libintl >= 0.18? try to replace setlocale() with a macro + * to their own versions. Remove the macro, if it exists, because it + * ends up calling the wrong version when the backend and libintl use + * different versions of msvcrt. + */ +#if defined(setlocale) +#undef setlocale +#endif + +/* + * Define our own wrapper macro around setlocale() to work around bugs in + * Windows' native setlocale() function. + */ +extern char *pgwin32_setlocale(int category, const char *locale); + +#define setlocale(a,b) pgwin32_setlocale(a,b) +#endif /* WIN32 */ + +/* Portable prompt handling */ +extern char *simple_prompt(const char *prompt, int maxlen, bool echo); + +#ifdef WIN32 +#define PG_SIGNAL_COUNT 32 +#define kill(pid,sig) pgkill(pid,sig) +extern int pgkill(int pid, int sig); +#endif + +extern int pclose_check(FILE *stream); + +/* Global variable holding time zone information. */ +#if defined(WIN32) || defined(__CYGWIN__) +#define TIMEZONE_GLOBAL _timezone +#define TZNAME_GLOBAL _tzname +#else +#define TIMEZONE_GLOBAL timezone +#define TZNAME_GLOBAL tzname +#endif + +#if defined(WIN32) || defined(__CYGWIN__) +/* + * Win32 doesn't have reliable rename/unlink during concurrent access. + */ +extern int pgrename(const char *from, const char *to); +extern int pgunlink(const char *path); + +/* Include this first so later includes don't see these defines */ +#ifdef WIN32_ONLY_COMPILER +#include +#endif + +#define rename(from, to) pgrename(from, to) +#define unlink(path) pgunlink(path) +#endif /* defined(WIN32) || defined(__CYGWIN__) */ + +/* + * Win32 also doesn't have symlinks, but we can emulate them with + * junction points on newer Win32 versions. + * + * Cygwin has its own symlinks which work on Win95/98/ME where + * junction points don't, so use those instead. We have no way of + * knowing what type of system Cygwin binaries will be run on. + * Note: Some CYGWIN includes might #define WIN32. + */ +#if defined(WIN32) && !defined(__CYGWIN__) +extern int pgsymlink(const char *oldpath, const char *newpath); +extern int pgreadlink(const char *path, char *buf, size_t size); +extern bool pgwin32_is_junction(char *path); + +#define symlink(oldpath, newpath) pgsymlink(oldpath, newpath) +#define readlink(path, buf, size) pgreadlink(path, buf, size) +#endif + +extern bool rmtree(const char *path, bool rmtopdir); + +/* + * stat() is not guaranteed to set the st_size field on win32, so we + * redefine it to our own implementation that is. + * + * We must pull in sys/stat.h here so the system header definition + * goes in first, and we redefine that, and not the other way around. + * + * Some frontends don't need the size from stat, so if UNSAFE_STAT_OK + * is defined we don't bother with this. + */ +#if defined(WIN32) && !defined(__CYGWIN__) && !defined(UNSAFE_STAT_OK) +#include +extern int pgwin32_safestat(const char *path, struct stat * buf); + +#define stat(a,b) pgwin32_safestat(a,b) +#endif + +#if defined(WIN32) && !defined(__CYGWIN__) + +/* + * open() and fopen() replacements to allow deletion of open files and + * passing of other special options. + */ +#define O_DIRECT 0x80000000 +extern int pgwin32_open(const char *, int,...); +extern FILE *pgwin32_fopen(const char *, const char *); + +#ifndef FRONTEND +#define open(a,b,c) pgwin32_open(a,b,c) +#define fopen(a,b) pgwin32_fopen(a,b) +#endif + +/* + * Mingw-w64 headers #define popen and pclose to _popen and _pclose. We want + * to use our popen wrapper, rather than plain _popen, so override that. For + * consistency, use our version of pclose, too. + */ +#ifdef popen +#undef popen +#endif +#ifdef pclose +#undef pclose +#endif + +/* + * system() and popen() replacements to enclose the command in an extra + * pair of quotes. + */ +extern int pgwin32_system(const char *command); +extern FILE *pgwin32_popen(const char *command, const char *type); + +#define system(a) pgwin32_system(a) +#define popen(a,b) pgwin32_popen(a,b) +#define pclose(a) _pclose(a) + +/* New versions of MingW have gettimeofday, old mingw and msvc don't */ +#ifndef HAVE_GETTIMEOFDAY +/* Last parameter not used */ +extern int gettimeofday(struct timeval * tp, struct timezone * tzp); +#endif +#else /* !WIN32 */ + +/* + * Win32 requires a special close for sockets and pipes, while on Unix + * close() does them all. + */ +#define closesocket close +#endif /* WIN32 */ + +/* + * On Windows, setvbuf() does not support _IOLBF mode, and interprets that + * as _IOFBF. To add insult to injury, setvbuf(file, NULL, _IOFBF, 0) + * crashes outright if "parameter validation" is enabled. Therefore, in + * places where we'd like to select line-buffered mode, we fall back to + * unbuffered mode instead on Windows. Always use PG_IOLBF not _IOLBF + * directly in order to implement this behavior. + */ +#ifndef WIN32 +#define PG_IOLBF _IOLBF +#else +#define PG_IOLBF _IONBF +#endif + +/* + * Default "extern" declarations or macro substitutes for library routines. + * When necessary, these routines are provided by files in src/port/. + */ +#ifndef HAVE_CRYPT +extern char *crypt(const char *key, const char *setting); +#endif + +/* WIN32 handled in port/win32.h */ +#ifndef WIN32 +#define pgoff_t off_t +#ifdef __NetBSD__ +extern int fseeko(FILE *stream, off_t offset, int whence); +extern off_t ftello(FILE *stream); +#endif +#endif + +extern double pg_erand48(unsigned short xseed[3]); +extern long pg_lrand48(void); +extern void pg_srand48(long seed); + +#ifndef HAVE_FLS +extern int fls(int mask); +#endif + +#ifndef HAVE_FSEEKO +#define fseeko(a, b, c) fseek(a, b, c) +#define ftello(a) ftell(a) +#endif + +#if !defined(HAVE_GETPEEREID) && !defined(WIN32) +extern int getpeereid(int sock, uid_t *uid, gid_t *gid); +#endif + +#ifndef HAVE_ISINF +extern int isinf(double x); +#endif + +#ifndef HAVE_MKDTEMP +extern char *mkdtemp(char *path); +#endif + +#ifndef HAVE_RINT +extern double rint(double x); +#endif + +#ifndef HAVE_INET_ATON +#include +#include +extern int inet_aton(const char *cp, struct in_addr * addr); +#endif + +#if !HAVE_DECL_STRLCAT +extern size_t strlcat(char *dst, const char *src, size_t siz); +#endif + +#if !HAVE_DECL_STRLCPY +extern size_t strlcpy(char *dst, const char *src, size_t siz); +#endif + +#if !defined(HAVE_RANDOM) && !defined(__BORLANDC__) +extern long random(void); +#endif + +#ifndef HAVE_UNSETENV +extern void unsetenv(const char *name); +#endif + +#ifndef HAVE_SRANDOM +extern void srandom(unsigned int seed); +#endif + +#ifndef HAVE_SSL_GET_CURRENT_COMPRESSION +#define SSL_get_current_compression(x) 0 +#endif + +/* thread.h */ +extern char *pqStrerror(int errnum, char *strerrbuf, size_t buflen); + +#ifndef WIN32 +extern int pqGetpwuid(uid_t uid, struct passwd * resultbuf, char *buffer, + size_t buflen, struct passwd ** result); +#endif + +extern int pqGethostbyname(const char *name, + struct hostent * resultbuf, + char *buffer, size_t buflen, + struct hostent ** result, + int *herrno); + +extern void pg_qsort(void *base, size_t nel, size_t elsize, + int (*cmp) (const void *, const void *)); +extern int pg_qsort_strcmp(const void *a, const void *b); + +#define qsort(a,b,c,d) pg_qsort(a,b,c,d) + +typedef int (*qsort_arg_comparator) (const void *a, const void *b, void *arg); + +extern void qsort_arg(void *base, size_t nel, size_t elsize, + qsort_arg_comparator cmp, void *arg); + +/* port/chklocale.c */ +extern int pg_get_encoding_from_locale(const char *ctype, bool write_message); + +#if defined(WIN32) && !defined(FRONTEND) +extern int pg_codepage_to_encoding(UINT cp); +#endif + +/* port/inet_net_ntop.c */ +extern char *inet_net_ntop(int af, const void *src, int bits, + char *dst, size_t size); + +/* port/pgcheckdir.c */ +extern int pg_check_dir(const char *dir); + +/* port/pgmkdirp.c */ +extern int pg_mkdir_p(char *path, int omode); + +/* port/pqsignal.c */ +typedef void (*pqsigfunc) (int signo); +extern pqsigfunc pqsignal(int signo, pqsigfunc func); +#ifndef WIN32 +extern pqsigfunc pqsignal_no_restart(int signo, pqsigfunc func); +#else +#define pqsignal_no_restart(signo, func) pqsignal(signo, func) +#endif + +/* port/quotes.c */ +extern char *escape_single_quotes_ascii(const char *src); + +/* port/wait_error.c */ +extern char *wait_result_to_str(int exit_status); + +#endif /* PG_PORT_H */ diff --git a/libpq/postgresql/port/bsd/pg_config_os.h b/libpq/postgresql/port/bsd/pg_config_os.h new file mode 100644 index 0000000..e69de29 diff --git a/libpq/postgresql/port/darwin/pg_config_os.h b/libpq/postgresql/port/darwin/pg_config_os.h new file mode 100644 index 0000000..29c4b91 --- /dev/null +++ b/libpq/postgresql/port/darwin/pg_config_os.h @@ -0,0 +1,8 @@ +/* src/include/port/darwin.h */ + +#define __darwin__ 1 + +#if HAVE_DECL_F_FULLFSYNC /* not present before OS X 10.3 */ +#define HAVE_FSYNC_WRITETHROUGH + +#endif diff --git a/libpq/postgresql/port/linux/pg_config_os.h b/libpq/postgresql/port/linux/pg_config_os.h new file mode 100644 index 0000000..7a6e46c --- /dev/null +++ b/libpq/postgresql/port/linux/pg_config_os.h @@ -0,0 +1,22 @@ +/* src/include/port/linux.h */ + +/* + * As of July 2007, all known versions of the Linux kernel will sometimes + * return EIDRM for a shmctl() operation when EINVAL is correct (it happens + * when the low-order 15 bits of the supplied shm ID match the slot number + * assigned to a newer shmem segment). We deal with this by assuming that + * EIDRM means EINVAL in PGSharedMemoryIsInUse(). This is reasonably safe + * since in fact Linux has no excuse for ever returning EIDRM; it doesn't + * track removed segments in a way that would allow distinguishing them from + * private ones. But someday that code might get upgraded, and we'd have + * to have a kernel version test here. + */ +#define HAVE_LINUX_EIDRM_BUG + +/* + * Set the default wal_sync_method to fdatasync. With recent Linux versions, + * xlogdefs.h's normal rules will prefer open_datasync, which (a) doesn't + * perform better and (b) causes outright failures on ext4 data=journal + * filesystems, because those don't support O_DIRECT. + */ +#define PLATFORM_DEFAULT_SYNC_METHOD SYNC_METHOD_FDATASYNC diff --git a/libpq/postgresql/port/win32/arpa/inet.h b/libpq/postgresql/port/win32/arpa/inet.h new file mode 100644 index 0000000..ad18031 --- /dev/null +++ b/libpq/postgresql/port/win32/arpa/inet.h @@ -0,0 +1,3 @@ +/* src/include/port/win32/arpa/inet.h */ + +#include diff --git a/libpq/postgresql/port/win32/netdb.h b/libpq/postgresql/port/win32/netdb.h new file mode 100644 index 0000000..ad0627e --- /dev/null +++ b/libpq/postgresql/port/win32/netdb.h @@ -0,0 +1 @@ +/* src/include/port/win32/netdb.h */ diff --git a/libpq/postgresql/port/win32/netinet/in.h b/libpq/postgresql/port/win32/netinet/in.h new file mode 100644 index 0000000..a4e22f8 --- /dev/null +++ b/libpq/postgresql/port/win32/netinet/in.h @@ -0,0 +1,3 @@ +/* src/include/port/win32/netinet/in.h */ + +#include diff --git a/libpq/postgresql/port/win32/pg_config_os.h b/libpq/postgresql/port/win32/pg_config_os.h new file mode 100644 index 0000000..4453c90 --- /dev/null +++ b/libpq/postgresql/port/win32/pg_config_os.h @@ -0,0 +1,486 @@ +/* src/include/port/win32.h */ + +#if defined(_MSC_VER) || defined(__BORLANDC__) +#define WIN32_ONLY_COMPILER +#endif + +/* + * Make sure _WIN32_WINNT has the minimum required value. + * Leave a higher value in place. When building with at least Visual + * Studio 2015 the minimum requirement is Windows Vista (0x0600) to + * get support for GetLocaleInfoEx() with locales. For everything else + * the minimum version is Windows XP (0x0501). + * Also for VS2015, add a define that stops compiler complaints about + * using the old Winsock API. + */ +#if defined(_MSC_VER) && _MSC_VER >= 1900 +#define _WINSOCK_DEPRECATED_NO_WARNINGS +#define MIN_WINNT 0x0600 +#else +#define MIN_WINNT 0x0501 +#endif + +#if defined(_WIN32_WINNT) && _WIN32_WINNT < MIN_WINNT +#undef _WIN32_WINNT +#endif + +#ifndef _WIN32_WINNT +#define _WIN32_WINNT MIN_WINNT +#endif + +/* + * Always build with SSPI support. Keep it as a #define in case + * we want a switch to disable it sometime in the future. + */ +#ifndef __BORLANDC__ +#define ENABLE_SSPI 1 +#endif + +/* undefine and redefine after #include */ +#undef mkdir + +#undef ERROR + +/* + * The Mingw64 headers choke if this is already defined - they + * define it themselves. + */ +#if !defined(__MINGW64_VERSION_MAJOR) || defined(WIN32_ONLY_COMPILER) +#define _WINSOCKAPI_ +#endif +#include +#include +#include +#undef small +#include +#include +#include +#include +#ifndef __BORLANDC__ +#include /* for non-unicode version */ +#endif +#undef near + +/* Must be here to avoid conflicting with prototype in windows.h */ +#define mkdir(a,b) mkdir(a) + +#define ftruncate(a,b) chsize(a,b) + +/* Windows doesn't have fsync() as such, use _commit() */ +#define fsync(fd) _commit(fd) + +/* + * For historical reasons, we allow setting wal_sync_method to + * fsync_writethrough on Windows, even though it's really identical to fsync + * (both code paths wind up at _commit()). + */ +#define HAVE_FSYNC_WRITETHROUGH +#define FSYNC_WRITETHROUGH_IS_FSYNC + +#define USES_WINSOCK + +/* defines for dynamic linking on Win32 platform + * + * http://support.microsoft.com/kb/132044 + * http://msdn.microsoft.com/en-us/library/8fskxacy(v=vs.80).aspx + * http://msdn.microsoft.com/en-us/library/a90k134d(v=vs.80).aspx + */ + +#if defined(WIN32) || defined(__CYGWIN__) + +#ifdef BUILDING_DLL +#define PGDLLIMPORT __declspec (dllexport) +#else /* not BUILDING_DLL */ +#define PGDLLIMPORT __declspec (dllimport) +#endif + +#ifdef _MSC_VER +#define PGDLLEXPORT __declspec (dllexport) +#else +#define PGDLLEXPORT +#endif +#else /* not CYGWIN, not MSVC, not MingW */ +#define PGDLLIMPORT +#define PGDLLEXPORT +#endif + + +/* + * IPC defines + */ +#undef HAVE_UNION_SEMUN +#define HAVE_UNION_SEMUN 1 + +#define IPC_RMID 256 +#define IPC_CREAT 512 +#define IPC_EXCL 1024 +#define IPC_PRIVATE 234564 +#define IPC_NOWAIT 2048 +#define IPC_STAT 4096 + +#define EACCESS 2048 +#ifndef EIDRM +#define EIDRM 4096 +#endif + +#define SETALL 8192 +#define GETNCNT 16384 +#define GETVAL 65536 +#define SETVAL 131072 +#define GETPID 262144 + + +/* + * Signal stuff + * + * For WIN32, there is no wait() call so there are no wait() macros + * to interpret the return value of system(). Instead, system() + * return values < 0x100 are used for exit() termination, and higher + * values are used to indicated non-exit() termination, which is + * similar to a unix-style signal exit (think SIGSEGV == + * STATUS_ACCESS_VIOLATION). Return values are broken up into groups: + * + * http://msdn2.microsoft.com/en-gb/library/aa489609.aspx + * + * NT_SUCCESS 0 - 0x3FFFFFFF + * NT_INFORMATION 0x40000000 - 0x7FFFFFFF + * NT_WARNING 0x80000000 - 0xBFFFFFFF + * NT_ERROR 0xC0000000 - 0xFFFFFFFF + * + * Effectively, we don't care on the severity of the return value from + * system(), we just need to know if it was because of exit() or generated + * by the system, and it seems values >= 0x100 are system-generated. + * See this URL for a list of WIN32 STATUS_* values: + * + * Wine (URL used in our error messages) - + * http://source.winehq.org/source/include/ntstatus.h + * Descriptions - http://www.comp.nus.edu.sg/~wuyongzh/my_doc/ntstatus.txt + * MS SDK - http://www.nologs.com/ntstatus.html + * + * It seems the exception lists are in both ntstatus.h and winnt.h, but + * ntstatus.h has a more comprehensive list, and it only contains + * exception values, rather than winnt, which contains lots of other + * things: + * + * http://www.microsoft.com/msj/0197/exception/exception.aspx + * + * The ExceptionCode parameter is the number that the operating system + * assigned to the exception. You can see a list of various exception codes + * in WINNT.H by searching for #defines that start with "STATUS_". For + * example, the code for the all-too-familiar STATUS_ACCESS_VIOLATION is + * 0xC0000005. A more complete set of exception codes can be found in + * NTSTATUS.H from the Windows NT DDK. + * + * Some day we might want to print descriptions for the most common + * exceptions, rather than printing an include file name. We could use + * RtlNtStatusToDosError() and pass to FormatMessage(), which can print + * the text of error values, but MinGW does not support + * RtlNtStatusToDosError(). + */ +#define WIFEXITED(w) (((w) & 0XFFFFFF00) == 0) +#define WIFSIGNALED(w) (!WIFEXITED(w)) +#define WEXITSTATUS(w) (w) +#define WTERMSIG(w) (w) + +#define sigmask(sig) ( 1 << ((sig)-1) ) + +/* Signal function return values */ +#undef SIG_DFL +#undef SIG_ERR +#undef SIG_IGN +#define SIG_DFL ((pqsigfunc)0) +#define SIG_ERR ((pqsigfunc)-1) +#define SIG_IGN ((pqsigfunc)1) + +/* Some extra signals */ +#define SIGHUP 1 +#define SIGQUIT 3 +#define SIGTRAP 5 +#define SIGABRT 22 /* Set to match W32 value -- not UNIX value */ +#define SIGKILL 9 +#define SIGPIPE 13 +#define SIGALRM 14 +#define SIGSTOP 17 +#define SIGTSTP 18 +#define SIGCONT 19 +#define SIGCHLD 20 +#define SIGTTIN 21 +#define SIGTTOU 22 /* Same as SIGABRT -- no problem, I hope */ +#define SIGWINCH 28 +#ifndef __BORLANDC__ +#define SIGUSR1 30 +#define SIGUSR2 31 +#endif + +/* + * New versions of mingw have gettimeofday() and also declare + * struct timezone to support it. + */ +#ifndef HAVE_GETTIMEOFDAY +struct timezone +{ + int tz_minuteswest; /* Minutes west of GMT. */ + int tz_dsttime; /* Nonzero if DST is ever in effect. */ +}; +#endif + +/* for setitimer in backend/port/win32/timer.c */ +#define ITIMER_REAL 0 +struct itimerval +{ + struct timeval it_interval; + struct timeval it_value; +}; + +int setitimer(int which, const struct itimerval * value, struct itimerval * ovalue); + +/* + * WIN32 does not provide 64-bit off_t, but does provide the functions operating + * with 64-bit offsets. + */ +#define pgoff_t __int64 +#ifdef WIN32_ONLY_COMPILER +#define fseeko(stream, offset, origin) _fseeki64(stream, offset, origin) +#define ftello(stream) _ftelli64(stream) +#else +#ifndef fseeko +#define fseeko(stream, offset, origin) fseeko64(stream, offset, origin) +#endif +#ifndef ftello +#define ftello(stream) ftello64(stream) +#endif +#endif + +/* + * Supplement to . + * + * Perl already has typedefs for uid_t and gid_t. + */ +#ifndef PLPERL_HAVE_UID_GID +typedef int uid_t; +typedef int gid_t; +#endif +typedef long key_t; + +#ifdef WIN32_ONLY_COMPILER +typedef int pid_t; +#endif + +/* + * Supplement to . + */ +#define lstat(path, sb) stat((path), (sb)) + +/* + * Supplement to . + * This is the same value as _O_NOINHERIT in the MS header file. This is + * to ensure that we don't collide with a future definition. It means + * we cannot use _O_NOINHERIT ourselves. + */ +#define O_DSYNC 0x0080 + +/* + * Supplement to . + * + * We redefine network-related Berkeley error symbols as the corresponding WSA + * constants. This allows elog.c to recognize them as being in the Winsock + * error code range and pass them off to pgwin32_socket_strerror(), since + * Windows' version of plain strerror() won't cope. Note that this will break + * if these names are used for anything else besides Windows Sockets errors. + * See TranslateSocketError() when changing this list. + */ +#undef EAGAIN +#define EAGAIN WSAEWOULDBLOCK +#undef EINTR +#define EINTR WSAEINTR +#undef EMSGSIZE +#define EMSGSIZE WSAEMSGSIZE +#undef EAFNOSUPPORT +#define EAFNOSUPPORT WSAEAFNOSUPPORT +#undef EWOULDBLOCK +#define EWOULDBLOCK WSAEWOULDBLOCK +#undef ECONNABORTED +#define ECONNABORTED WSAECONNABORTED +#undef ECONNRESET +#define ECONNRESET WSAECONNRESET +#undef EINPROGRESS +#define EINPROGRESS WSAEINPROGRESS +#undef EISCONN +#define EISCONN WSAEISCONN +#undef ENOBUFS +#define ENOBUFS WSAENOBUFS +#undef EPROTONOSUPPORT +#define EPROTONOSUPPORT WSAEPROTONOSUPPORT +#undef ECONNREFUSED +#define ECONNREFUSED WSAECONNREFUSED +#undef ENOTSOCK +#define ENOTSOCK WSAENOTSOCK +#undef EOPNOTSUPP +#define EOPNOTSUPP WSAEOPNOTSUPP +#undef EADDRINUSE +#define EADDRINUSE WSAEADDRINUSE +#undef EADDRNOTAVAIL +#define EADDRNOTAVAIL WSAEADDRNOTAVAIL +#undef EHOSTUNREACH +#define EHOSTUNREACH WSAEHOSTUNREACH +#undef ENOTCONN +#define ENOTCONN WSAENOTCONN + +/* + * Extended locale functions with gratuitous underscore prefixes. + * (These APIs are nevertheless fully documented by Microsoft.) + */ +#define locale_t _locale_t +#define tolower_l _tolower_l +#define toupper_l _toupper_l +#define towlower_l _towlower_l +#define towupper_l _towupper_l +#define isdigit_l _isdigit_l +#define iswdigit_l _iswdigit_l +#define isalpha_l _isalpha_l +#define iswalpha_l _iswalpha_l +#define isalnum_l _isalnum_l +#define iswalnum_l _iswalnum_l +#define isupper_l _isupper_l +#define iswupper_l _iswupper_l +#define islower_l _islower_l +#define iswlower_l _iswlower_l +#define isgraph_l _isgraph_l +#define iswgraph_l _iswgraph_l +#define isprint_l _isprint_l +#define iswprint_l _iswprint_l +#define ispunct_l _ispunct_l +#define iswpunct_l _iswpunct_l +#define isspace_l _isspace_l +#define iswspace_l _iswspace_l +#define strcoll_l _strcoll_l +#define strxfrm_l _strxfrm_l +#define wcscoll_l _wcscoll_l +#define wcstombs_l _wcstombs_l +#define mbstowcs_l _mbstowcs_l + + +/* In backend/port/win32/signal.c */ +extern PGDLLIMPORT volatile int pg_signal_queue; +extern PGDLLIMPORT int pg_signal_mask; +extern HANDLE pgwin32_signal_event; +extern HANDLE pgwin32_initial_signal_pipe; + +#define UNBLOCKED_SIGNAL_QUEUE() (pg_signal_queue & ~pg_signal_mask) + + +void pgwin32_signal_initialize(void); +HANDLE pgwin32_create_signal_listener(pid_t pid); +void pgwin32_dispatch_queued_signals(void); +void pg_queue_signal(int signum); + +/* In backend/port/win32/socket.c */ +#ifndef FRONTEND +#define socket(af, type, protocol) pgwin32_socket(af, type, protocol) +#define bind(s, addr, addrlen) pgwin32_bind(s, addr, addrlen) +#define listen(s, backlog) pgwin32_listen(s, backlog) +#define accept(s, addr, addrlen) pgwin32_accept(s, addr, addrlen) +#define connect(s, name, namelen) pgwin32_connect(s, name, namelen) +#define select(n, r, w, e, timeout) pgwin32_select(n, r, w, e, timeout) +#define recv(s, buf, len, flags) pgwin32_recv(s, buf, len, flags) +#define send(s, buf, len, flags) pgwin32_send(s, buf, len, flags) + +SOCKET pgwin32_socket(int af, int type, int protocol); +int pgwin32_bind(SOCKET s, struct sockaddr * addr, int addrlen); +int pgwin32_listen(SOCKET s, int backlog); +SOCKET pgwin32_accept(SOCKET s, struct sockaddr * addr, int *addrlen); +int pgwin32_connect(SOCKET s, const struct sockaddr * name, int namelen); +int pgwin32_select(int nfds, fd_set *readfs, fd_set *writefds, fd_set *exceptfds, const struct timeval * timeout); +int pgwin32_recv(SOCKET s, char *buf, int len, int flags); +int pgwin32_send(SOCKET s, const void *buf, int len, int flags); + +const char *pgwin32_socket_strerror(int err); +int pgwin32_waitforsinglesocket(SOCKET s, int what, int timeout); + +extern int pgwin32_noblock; + +#endif + +/* in backend/port/win32_shmem.c */ +extern int pgwin32_ReserveSharedMemoryRegion(HANDLE); + +/* in backend/port/win32/crashdump.c */ +extern void pgwin32_install_crashdump_handler(void); + +/* in port/win32error.c */ +extern void _dosmaperr(unsigned long); + +/* in port/win32env.c */ +extern int pgwin32_putenv(const char *); +extern void pgwin32_unsetenv(const char *); + +/* in port/win32security.c */ +extern int pgwin32_is_service(void); +extern int pgwin32_is_admin(void); + +#define putenv(x) pgwin32_putenv(x) +#define unsetenv(x) pgwin32_unsetenv(x) + +/* Things that exist in MingW headers, but need to be added to MSVC & BCC */ +#ifdef WIN32_ONLY_COMPILER + +#ifndef _WIN64 +typedef long ssize_t; +#else +typedef __int64 ssize_t; +#endif + +#ifndef __BORLANDC__ +typedef unsigned short mode_t; + +#define S_IRUSR _S_IREAD +#define S_IWUSR _S_IWRITE +#define S_IXUSR _S_IEXEC +#define S_IRWXU (S_IRUSR | S_IWUSR | S_IXUSR) +/* see also S_IRGRP etc below */ +#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) +#endif /* __BORLANDC__ */ + +#define F_OK 0 +#define W_OK 2 +#define R_OK 4 + +#if (_MSC_VER < 1800) +#define isinf(x) ((_fpclass(x) == _FPCLASS_PINF) || (_fpclass(x) == _FPCLASS_NINF)) +#define isnan(x) _isnan(x) +#endif + +/* Pulled from Makefile.port in mingw */ +#define DLSUFFIX ".dll" + +#ifdef __BORLANDC__ + +/* for port/dirent.c */ +#ifndef INVALID_FILE_ATTRIBUTES +#define INVALID_FILE_ATTRIBUTES ((DWORD) -1) +#endif + +/* for port/open.c */ +#ifndef O_RANDOM +#define O_RANDOM 0x0010 /* File access is primarily random */ +#define O_SEQUENTIAL 0x0020 /* File access is primarily sequential */ +#define O_TEMPORARY 0x0040 /* Temporary file bit */ +#define O_SHORT_LIVED 0x1000 /* Temporary storage file, try not to flush */ +#define _O_SHORT_LIVED O_SHORT_LIVED +#endif /* ifndef O_RANDOM */ +#endif /* __BORLANDC__ */ +#endif /* WIN32_ONLY_COMPILER */ + +/* These aren't provided by either MingW or MSVC */ +#ifndef __BORLANDC__ +#define S_IRGRP 0 +#define S_IWGRP 0 +#define S_IXGRP 0 +#define S_IRWXG 0 +#define S_IROTH 0 +#define S_IWOTH 0 +#define S_IXOTH 0 +#define S_IRWXO 0 + +#endif /* __BORLANDC__ */ diff --git a/libpq/postgresql/port/win32/pthread-win32.h b/libpq/postgresql/port/win32/pthread-win32.h new file mode 100644 index 0000000..97ccc17 --- /dev/null +++ b/libpq/postgresql/port/win32/pthread-win32.h @@ -0,0 +1,22 @@ +/* + * src/port/pthread-win32.h + */ +#ifndef __PTHREAD_H +#define __PTHREAD_H + +typedef ULONG pthread_key_t; +typedef CRITICAL_SECTION *pthread_mutex_t; +typedef int pthread_once_t; + +DWORD pthread_self(void); + +void pthread_setspecific(pthread_key_t, void *); +void *pthread_getspecific(pthread_key_t); + +int pthread_mutex_init(pthread_mutex_t *, void *attr); +int pthread_mutex_lock(pthread_mutex_t *); + +/* blocking */ +int pthread_mutex_unlock(pthread_mutex_t *); + +#endif diff --git a/libpq/postgresql/port/win32/pwd.h b/libpq/postgresql/port/win32/pwd.h new file mode 100644 index 0000000..b8c7178 --- /dev/null +++ b/libpq/postgresql/port/win32/pwd.h @@ -0,0 +1,3 @@ +/* + * src/include/port/win32/pwd.h + */ diff --git a/libpq/postgresql/port/win32/sys/socket.h b/libpq/postgresql/port/win32/sys/socket.h new file mode 100644 index 0000000..edaee6a --- /dev/null +++ b/libpq/postgresql/port/win32/sys/socket.h @@ -0,0 +1,33 @@ +/* + * src/include/port/win32/sys/socket.h + */ +#ifndef WIN32_SYS_SOCKET_H +#define WIN32_SYS_SOCKET_H + +/* + * Unfortunately, of VC++ also defines ERROR. + * To avoid the conflict, we include here and undefine ERROR + * immediately. + * + * Note: Don't include directly. It causes compile errors. + */ +#include +#include +#include + +#undef ERROR +#undef small + +/* Restore old ERROR value */ +#ifdef PGERROR +#define ERROR PGERROR +#endif + +/* + * we can't use the windows gai_strerror{AW} functions because + * they are defined inline in the MS header files. So we'll use our + * own + */ +#undef gai_strerror + +#endif /* WIN32_SYS_SOCKET_H */ diff --git a/libpq/postgresql/port/win32/sys/wait.h b/libpq/postgresql/port/win32/sys/wait.h new file mode 100644 index 0000000..eaeb566 --- /dev/null +++ b/libpq/postgresql/port/win32/sys/wait.h @@ -0,0 +1,3 @@ +/* + * src/include/port/win32/sys/wait.h + */ diff --git a/libpq/postgresql/port/win32_msvc/sys/file.h b/libpq/postgresql/port/win32_msvc/sys/file.h new file mode 100644 index 0000000..76be3e7 --- /dev/null +++ b/libpq/postgresql/port/win32_msvc/sys/file.h @@ -0,0 +1 @@ +/* src/include/port/win32_msvc/sys/file.h */ diff --git a/libpq/postgresql/port/win32_msvc/sys/param.h b/libpq/postgresql/port/win32_msvc/sys/param.h new file mode 100644 index 0000000..160df3b --- /dev/null +++ b/libpq/postgresql/port/win32_msvc/sys/param.h @@ -0,0 +1 @@ +/* src/include/port/win32_msvc/sys/param.h */ diff --git a/libpq/postgresql/port/win32_msvc/sys/time.h b/libpq/postgresql/port/win32_msvc/sys/time.h new file mode 100644 index 0000000..9d943ec --- /dev/null +++ b/libpq/postgresql/port/win32_msvc/sys/time.h @@ -0,0 +1 @@ +/* src/include/port/win32_msvc/sys/time.h */ diff --git a/libpq/postgresql/port/win32_msvc/unistd.h b/libpq/postgresql/port/win32_msvc/unistd.h new file mode 100644 index 0000000..b63f477 --- /dev/null +++ b/libpq/postgresql/port/win32_msvc/unistd.h @@ -0,0 +1 @@ +/* src/include/port/win32_msvc/unistd.h */ diff --git a/libpq/postgresql/postgres_ext.h b/libpq/postgresql/postgres_ext.h new file mode 100644 index 0000000..ae2f087 --- /dev/null +++ b/libpq/postgresql/postgres_ext.h @@ -0,0 +1,70 @@ +/*------------------------------------------------------------------------- + * + * postgres_ext.h + * + * This file contains declarations of things that are visible everywhere + * in PostgreSQL *and* are visible to clients of frontend interface libraries. + * For example, the Oid type is part of the API of libpq and other libraries. + * + * Declarations which are specific to a particular interface should + * go in the header file for that interface (such as libpq-fe.h). This + * file is only for fundamental Postgres declarations. + * + * User-written C functions don't count as "external to Postgres." + * Those function much as local modifications to the backend itself, and + * use header files that are otherwise internal to Postgres to interface + * with the backend. + * + * src/include/postgres_ext.h + * + *------------------------------------------------------------------------- + */ + +#ifndef POSTGRES_EXT_H +#define POSTGRES_EXT_H + +#include "pg_config_ext.h" + +/* + * Object ID is a fundamental type in Postgres. + */ +typedef unsigned int Oid; + +#ifdef __cplusplus +#define InvalidOid (Oid(0)) +#else +#define InvalidOid ((Oid) 0) +#endif + +#define OID_MAX UINT_MAX +/* you will need to include to use the above #define */ + +/* Define a signed 64-bit integer type for use in client API declarations. */ +typedef PG_INT64_TYPE pg_int64; + + +/* + * Identifiers of error message fields. Kept here to keep common + * between frontend and backend, and also to export them to libpq + * applications. + */ +#define PG_DIAG_SEVERITY 'S' +#define PG_DIAG_SEVERITY_NONLOCALIZED 'V' +#define PG_DIAG_SQLSTATE 'C' +#define PG_DIAG_MESSAGE_PRIMARY 'M' +#define PG_DIAG_MESSAGE_DETAIL 'D' +#define PG_DIAG_MESSAGE_HINT 'H' +#define PG_DIAG_STATEMENT_POSITION 'P' +#define PG_DIAG_INTERNAL_POSITION 'p' +#define PG_DIAG_INTERNAL_QUERY 'q' +#define PG_DIAG_CONTEXT 'W' +#define PG_DIAG_SCHEMA_NAME 's' +#define PG_DIAG_TABLE_NAME 't' +#define PG_DIAG_COLUMN_NAME 'c' +#define PG_DIAG_DATATYPE_NAME 'd' +#define PG_DIAG_CONSTRAINT_NAME 'n' +#define PG_DIAG_SOURCE_FILE 'F' +#define PG_DIAG_SOURCE_LINE 'L' +#define PG_DIAG_SOURCE_FUNCTION 'R' + +#endif /* POSTGRES_EXT_H */ diff --git a/libpq/postgresql/postgres_fe.h b/libpq/postgresql/postgres_fe.h new file mode 100644 index 0000000..69c0ad8 --- /dev/null +++ b/libpq/postgresql/postgres_fe.h @@ -0,0 +1,29 @@ +/*------------------------------------------------------------------------- + * + * postgres_fe.h + * Primary include file for PostgreSQL client-side .c files + * + * This should be the first file included by PostgreSQL client libraries and + * application programs --- but not by backend modules, which should include + * postgres.h. + * + * + * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1995, Regents of the University of California + * + * src/include/postgres_fe.h + * + *------------------------------------------------------------------------- + */ +#ifndef POSTGRES_FE_H +#define POSTGRES_FE_H + +#ifndef FRONTEND +#define FRONTEND 1 +#endif + +#include "c.h" + +#include "common/fe_memutils.h" + +#endif /* POSTGRES_FE_H */ diff --git a/libpq/pqexpbuffer.c b/libpq/pqexpbuffer.c new file mode 100644 index 0000000..8d82d5c --- /dev/null +++ b/libpq/pqexpbuffer.c @@ -0,0 +1,430 @@ +/*------------------------------------------------------------------------- + * + * pqexpbuffer.c + * + * PQExpBuffer provides an indefinitely-extensible string data type. + * It can be used to buffer either ordinary C strings (null-terminated text) + * or arbitrary binary data. All storage is allocated with malloc(). + * + * This module is essentially the same as the backend's StringInfo data type, + * but it is intended for use in frontend libpq and client applications. + * Thus, it does not rely on palloc() nor elog(), nor psprintf.c which + * will exit() on error. + * + * It does rely on vsnprintf(); if configure finds that libc doesn't provide + * a usable vsnprintf(), then a copy of our own implementation of it will + * be linked into libpq. + * + * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/interfaces/libpq/pqexpbuffer.c + * + *------------------------------------------------------------------------- + */ + +#include "postgres_fe.h" + +#include + +#include "pqexpbuffer.h" + +#ifdef WIN32 +#include "win32.h" +#endif + + +/* All "broken" PQExpBuffers point to this string. */ +static const char oom_buffer[1] = ""; + +static bool appendPQExpBufferVA(PQExpBuffer str, const char *fmt, va_list args) pg_attribute_printf(2, 0); + + +/* + * markPQExpBufferBroken + * + * Put a PQExpBuffer in "broken" state if it isn't already. + */ +static void +markPQExpBufferBroken(PQExpBuffer str) +{ + if (str->data != oom_buffer) + free(str->data); + + /* + * Casting away const here is a bit ugly, but it seems preferable to not + * marking oom_buffer const. We want to do that to encourage the compiler + * to put oom_buffer in read-only storage, so that anyone who tries to + * scribble on a broken PQExpBuffer will get a failure. + */ + str->data = (char *) oom_buffer; + str->len = 0; + str->maxlen = 0; +} + +/* + * createPQExpBuffer + * + * Create an empty 'PQExpBufferData' & return a pointer to it. + */ +PQExpBuffer +createPQExpBuffer(void) +{ + PQExpBuffer res; + + res = (PQExpBuffer) malloc(sizeof(PQExpBufferData)); + if (res != NULL) + initPQExpBuffer(res); + + return res; +} + +/* + * initPQExpBuffer + * + * Initialize a PQExpBufferData struct (with previously undefined contents) + * to describe an empty string. + */ +void +initPQExpBuffer(PQExpBuffer str) +{ + str->data = (char *) malloc(INITIAL_EXPBUFFER_SIZE); + if (str->data == NULL) + { + str->data = (char *) oom_buffer; /* see comment above */ + str->maxlen = 0; + str->len = 0; + } + else + { + str->maxlen = INITIAL_EXPBUFFER_SIZE; + str->len = 0; + str->data[0] = '\0'; + } +} + +/* + * destroyPQExpBuffer(str); + * + * free()s both the data buffer and the PQExpBufferData. + * This is the inverse of createPQExpBuffer(). + */ +void +destroyPQExpBuffer(PQExpBuffer str) +{ + if (str) + { + termPQExpBuffer(str); + free(str); + } +} + +/* + * termPQExpBuffer(str) + * free()s the data buffer but not the PQExpBufferData itself. + * This is the inverse of initPQExpBuffer(). + */ +void +termPQExpBuffer(PQExpBuffer str) +{ + if (str->data != oom_buffer) + free(str->data); + /* just for luck, make the buffer validly empty. */ + str->data = (char *) oom_buffer; /* see comment above */ + str->maxlen = 0; + str->len = 0; +} + +/* + * resetPQExpBuffer + * Reset a PQExpBuffer to empty + * + * Note: if possible, a "broken" PQExpBuffer is returned to normal. + */ +void +resetPQExpBuffer(PQExpBuffer str) +{ + if (str) + { + if (str->data != oom_buffer) + { + str->len = 0; + str->data[0] = '\0'; + } + else + { + /* try to reinitialize to valid state */ + initPQExpBuffer(str); + } + } +} + +/* + * enlargePQExpBuffer + * Make sure there is enough space for 'needed' more bytes in the buffer + * ('needed' does not include the terminating null). + * + * Returns 1 if OK, 0 if failed to enlarge buffer. (In the latter case + * the buffer is left in "broken" state.) + */ +int +enlargePQExpBuffer(PQExpBuffer str, size_t needed) +{ + size_t newlen; + char *newdata; + + if (PQExpBufferBroken(str)) + return 0; /* already failed */ + + /* + * Guard against ridiculous "needed" values, which can occur if we're fed + * bogus data. Without this, we can get an overflow or infinite loop in + * the following. + */ + if (needed >= ((size_t) INT_MAX - str->len)) + { + markPQExpBufferBroken(str); + return 0; + } + + needed += str->len + 1; /* total space required now */ + + /* Because of the above test, we now have needed <= INT_MAX */ + + if (needed <= str->maxlen) + return 1; /* got enough space already */ + + /* + * We don't want to allocate just a little more space with each append; + * for efficiency, double the buffer size each time it overflows. + * Actually, we might need to more than double it if 'needed' is big... + */ + newlen = (str->maxlen > 0) ? (2 * str->maxlen) : 64; + while (needed > newlen) + newlen = 2 * newlen; + + /* + * Clamp to INT_MAX in case we went past it. Note we are assuming here + * that INT_MAX <= UINT_MAX/2, else the above loop could overflow. We + * will still have newlen >= needed. + */ + if (newlen > (size_t) INT_MAX) + newlen = (size_t) INT_MAX; + + newdata = (char *) realloc(str->data, newlen); + if (newdata != NULL) + { + str->data = newdata; + str->maxlen = newlen; + return 1; + } + + markPQExpBufferBroken(str); + return 0; +} + +/* + * printfPQExpBuffer + * Format text data under the control of fmt (an sprintf-like format string) + * and insert it into str. More space is allocated to str if necessary. + * This is a convenience routine that does the same thing as + * resetPQExpBuffer() followed by appendPQExpBuffer(). + */ +void +printfPQExpBuffer(PQExpBuffer str, const char *fmt,...) +{ + va_list args; + bool done; + + resetPQExpBuffer(str); + + if (PQExpBufferBroken(str)) + return; /* already failed */ + + /* Loop in case we have to retry after enlarging the buffer. */ + do + { + va_start(args, fmt); + done = appendPQExpBufferVA(str, fmt, args); + va_end(args); + } while (!done); +} + +/* + * appendPQExpBuffer + * + * Format text data under the control of fmt (an sprintf-like format string) + * and append it to whatever is already in str. More space is allocated + * to str if necessary. This is sort of like a combination of sprintf and + * strcat. + */ +void +appendPQExpBuffer(PQExpBuffer str, const char *fmt,...) +{ + va_list args; + bool done; + + if (PQExpBufferBroken(str)) + return; /* already failed */ + + /* Loop in case we have to retry after enlarging the buffer. */ + do + { + va_start(args, fmt); + done = appendPQExpBufferVA(str, fmt, args); + va_end(args); + } while (!done); +} + +/* + * appendPQExpBufferVA + * Shared guts of printfPQExpBuffer/appendPQExpBuffer. + * Attempt to format data and append it to str. Returns true if done + * (either successful or hard failure), false if need to retry. + */ +static bool +appendPQExpBufferVA(PQExpBuffer str, const char *fmt, va_list args) +{ + size_t avail; + size_t needed; + int nprinted; + + /* + * Try to format the given string into the available space; but if there's + * hardly any space, don't bother trying, just enlarge the buffer first. + */ + if (str->maxlen > str->len + 16) + { + /* + * Note: we intentionally leave one byte unused, as a guard against + * old broken versions of vsnprintf. + */ + avail = str->maxlen - str->len - 1; + + errno = 0; + + nprinted = vsnprintf(str->data + str->len, avail, fmt, args); + + /* + * If vsnprintf reports an error other than ENOMEM, fail. + */ + if (nprinted < 0 && errno != 0 && errno != ENOMEM) + { + markPQExpBufferBroken(str); + return true; + } + + /* + * Note: some versions of vsnprintf return the number of chars + * actually stored, not the total space needed as C99 specifies. And + * at least one returns -1 on failure. Be conservative about + * believing whether the print worked. + */ + if (nprinted >= 0 && (size_t) nprinted < avail - 1) + { + /* Success. Note nprinted does not include trailing null. */ + str->len += nprinted; + return true; + } + + if (nprinted >= 0 && (size_t) nprinted > avail) + { + /* + * This appears to be a C99-compliant vsnprintf, so believe its + * estimate of the required space. (If it's wrong, the logic will + * still work, but we may loop multiple times.) Note that the + * space needed should be only nprinted+1 bytes, but we'd better + * allocate one more than that so that the test above will succeed + * next time. + * + * In the corner case where the required space just barely + * overflows, fail. + */ + if (nprinted > INT_MAX - 2) + { + markPQExpBufferBroken(str); + return true; + } + needed = nprinted + 2; + } + else + { + /* + * Buffer overrun, and we don't know how much space is needed. + * Estimate twice the previous buffer size, but not more than + * INT_MAX. + */ + if (avail >= INT_MAX / 2) + needed = INT_MAX; + else + needed = avail * 2; + } + } + else + { + /* + * We have to guess at how much to enlarge, since we're skipping the + * formatting work. + */ + needed = 32; + } + + /* Increase the buffer size and try again. */ + if (!enlargePQExpBuffer(str, needed)) + return true; /* oops, out of memory */ + + return false; +} + +/* + * appendPQExpBufferStr + * Append the given string to a PQExpBuffer, allocating more space + * if necessary. + */ +void +appendPQExpBufferStr(PQExpBuffer str, const char *data) +{ + appendBinaryPQExpBuffer(str, data, strlen(data)); +} + +/* + * appendPQExpBufferChar + * Append a single byte to str. + * Like appendPQExpBuffer(str, "%c", ch) but much faster. + */ +void +appendPQExpBufferChar(PQExpBuffer str, char ch) +{ + /* Make more room if needed */ + if (!enlargePQExpBuffer(str, 1)) + return; + + /* OK, append the character */ + str->data[str->len] = ch; + str->len++; + str->data[str->len] = '\0'; +} + +/* + * appendBinaryPQExpBuffer + * + * Append arbitrary binary data to a PQExpBuffer, allocating more space + * if necessary. + */ +void +appendBinaryPQExpBuffer(PQExpBuffer str, const char *data, size_t datalen) +{ + /* Make more room if needed */ + if (!enlargePQExpBuffer(str, datalen)) + return; + + /* OK, append the data */ + memcpy(str->data + str->len, data, datalen); + str->len += datalen; + + /* + * Keep a trailing null in place, even though it's probably useless for + * binary data... + */ + str->data[str->len] = '\0'; +} diff --git a/libpq/pqexpbuffer.h b/libpq/pqexpbuffer.h new file mode 100644 index 0000000..ce87cd5 --- /dev/null +++ b/libpq/pqexpbuffer.h @@ -0,0 +1,182 @@ +/*------------------------------------------------------------------------- + * + * pqexpbuffer.h + * Declarations/definitions for "PQExpBuffer" functions. + * + * PQExpBuffer provides an indefinitely-extensible string data type. + * It can be used to buffer either ordinary C strings (null-terminated text) + * or arbitrary binary data. All storage is allocated with malloc(). + * + * This module is essentially the same as the backend's StringInfo data type, + * but it is intended for use in frontend libpq and client applications. + * Thus, it does not rely on palloc() nor elog(). + * + * It does rely on vsnprintf(); if configure finds that libc doesn't provide + * a usable vsnprintf(), then a copy of our own implementation of it will + * be linked into libpq. + * + * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/interfaces/libpq/pqexpbuffer.h + * + *------------------------------------------------------------------------- + */ +#ifndef PQEXPBUFFER_H +#define PQEXPBUFFER_H + +/*------------------------- + * PQExpBufferData holds information about an extensible string. + * data is the current buffer for the string (allocated with malloc). + * len is the current string length. There is guaranteed to be + * a terminating '\0' at data[len], although this is not very + * useful when the string holds binary data rather than text. + * maxlen is the allocated size in bytes of 'data', i.e. the maximum + * string size (including the terminating '\0' char) that we can + * currently store in 'data' without having to reallocate + * more space. We must always have maxlen > len. + * + * An exception occurs if we failed to allocate enough memory for the string + * buffer. In that case data points to a statically allocated empty string, + * and len = maxlen = 0. + *------------------------- + */ +typedef struct PQExpBufferData +{ + char *data; + size_t len; + size_t maxlen; +} PQExpBufferData; + +typedef PQExpBufferData *PQExpBuffer; + +/*------------------------ + * Test for a broken (out of memory) PQExpBuffer. + * When a buffer is "broken", all operations except resetting or deleting it + * are no-ops. + *------------------------ + */ +#define PQExpBufferBroken(str) \ + ((str) == NULL || (str)->maxlen == 0) + +/*------------------------ + * Same, but for use when using a static or local PQExpBufferData struct. + * For that, a null-pointer test is useless and may draw compiler warnings. + *------------------------ + */ +#define PQExpBufferDataBroken(buf) \ + ((buf).maxlen == 0) + +/*------------------------ + * Initial size of the data buffer in a PQExpBuffer. + * NB: this must be large enough to hold error messages that might + * be returned by PQrequestCancel(). + *------------------------ + */ +#define INITIAL_EXPBUFFER_SIZE 256 + +/*------------------------ + * There are two ways to create a PQExpBuffer object initially: + * + * PQExpBuffer stringptr = createPQExpBuffer(); + * Both the PQExpBufferData and the data buffer are malloc'd. + * + * PQExpBufferData string; + * initPQExpBuffer(&string); + * The data buffer is malloc'd but the PQExpBufferData is presupplied. + * This is appropriate if the PQExpBufferData is a field of another + * struct. + *------------------------- + */ + +/*------------------------ + * createPQExpBuffer + * Create an empty 'PQExpBufferData' & return a pointer to it. + */ +extern PQExpBuffer createPQExpBuffer(void); + +/*------------------------ + * initPQExpBuffer + * Initialize a PQExpBufferData struct (with previously undefined contents) + * to describe an empty string. + */ +extern void initPQExpBuffer(PQExpBuffer str); + +/*------------------------ + * To destroy a PQExpBuffer, use either: + * + * destroyPQExpBuffer(str); + * free()s both the data buffer and the PQExpBufferData. + * This is the inverse of createPQExpBuffer(). + * + * termPQExpBuffer(str) + * free()s the data buffer but not the PQExpBufferData itself. + * This is the inverse of initPQExpBuffer(). + * + * NOTE: some routines build up a string using PQExpBuffer, and then + * release the PQExpBufferData but return the data string itself to their + * caller. At that point the data string looks like a plain malloc'd + * string. + */ +extern void destroyPQExpBuffer(PQExpBuffer str); +extern void termPQExpBuffer(PQExpBuffer str); + +/*------------------------ + * resetPQExpBuffer + * Reset a PQExpBuffer to empty + * + * Note: if possible, a "broken" PQExpBuffer is returned to normal. + */ +extern void resetPQExpBuffer(PQExpBuffer str); + +/*------------------------ + * enlargePQExpBuffer + * Make sure there is enough space for 'needed' more bytes in the buffer + * ('needed' does not include the terminating null). + * + * Returns 1 if OK, 0 if failed to enlarge buffer. (In the latter case + * the buffer is left in "broken" state.) + */ +extern int enlargePQExpBuffer(PQExpBuffer str, size_t needed); + +/*------------------------ + * printfPQExpBuffer + * Format text data under the control of fmt (an sprintf-like format string) + * and insert it into str. More space is allocated to str if necessary. + * This is a convenience routine that does the same thing as + * resetPQExpBuffer() followed by appendPQExpBuffer(). + */ +extern void printfPQExpBuffer(PQExpBuffer str, const char *fmt,...) pg_attribute_printf(2, 3); + +/*------------------------ + * appendPQExpBuffer + * Format text data under the control of fmt (an sprintf-like format string) + * and append it to whatever is already in str. More space is allocated + * to str if necessary. This is sort of like a combination of sprintf and + * strcat. + */ +extern void appendPQExpBuffer(PQExpBuffer str, const char *fmt,...) pg_attribute_printf(2, 3); + +/*------------------------ + * appendPQExpBufferStr + * Append the given string to a PQExpBuffer, allocating more space + * if necessary. + */ +extern void appendPQExpBufferStr(PQExpBuffer str, const char *data); + +/*------------------------ + * appendPQExpBufferChar + * Append a single byte to str. + * Like appendPQExpBuffer(str, "%c", ch) but much faster. + */ +extern void appendPQExpBufferChar(PQExpBuffer str, char ch); + +/*------------------------ + * appendBinaryPQExpBuffer + * Append arbitrary binary data to a PQExpBuffer, allocating more space + * if necessary. + */ +extern void appendBinaryPQExpBuffer(PQExpBuffer str, + const char *data, size_t datalen); + +#endif /* PQEXPBUFFER_H */ diff --git a/libpq/pqsignal.c b/libpq/pqsignal.c new file mode 100644 index 0000000..07797f9 --- /dev/null +++ b/libpq/pqsignal.c @@ -0,0 +1,90 @@ +/*------------------------------------------------------------------------- + * + * pqsignal.c + * reliable BSD-style signal(2) routine stolen from RWW who stole it + * from Stevens... + * + * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/port/pqsignal.c + * + * We now assume that all Unix-oid systems have POSIX sigaction(2) + * with support for restartable signals (SA_RESTART). We used to also + * support BSD-style signal(2), but there really shouldn't be anything + * out there anymore that doesn't have the POSIX API. + * + * Windows, of course, is resolutely in a class by itself. In the backend, + * we don't use this file at all; src/backend/port/win32/signal.c provides + * pqsignal() for the backend environment. Frontend programs can use + * this version of pqsignal() if they wish, but beware that this does + * not provide restartable signals on Windows. + * + * ------------------------------------------------------------------------ + */ + +#include "c.h" + +#include + +#if !defined(WIN32) || defined(FRONTEND) + +/* + * Set up a signal handler, with SA_RESTART, for signal "signo" + * + * Returns the previous handler. + */ +pqsigfunc +pqsignal(int signo, pqsigfunc func) +{ +#ifndef WIN32 + struct sigaction act, + oact; + + act.sa_handler = func; + sigemptyset(&act.sa_mask); + act.sa_flags = SA_RESTART; +#ifdef SA_NOCLDSTOP + if (signo == SIGCHLD) + act.sa_flags |= SA_NOCLDSTOP; +#endif + if (sigaction(signo, &act, &oact) < 0) + return SIG_ERR; + return oact.sa_handler; +#else /* WIN32 */ + return signal(signo, func); +#endif +} + +/* + * Set up a signal handler, without SA_RESTART, for signal "signo" + * + * Returns the previous handler. + * + * On Windows, this would be identical to pqsignal(), so don't bother. + */ +#ifndef WIN32 + +pqsigfunc +pqsignal_no_restart(int signo, pqsigfunc func) +{ + struct sigaction act, + oact; + + act.sa_handler = func; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; +#ifdef SA_NOCLDSTOP + if (signo == SIGCHLD) + act.sa_flags |= SA_NOCLDSTOP; +#endif + if (sigaction(signo, &act, &oact) < 0) + return SIG_ERR; + return oact.sa_handler; +} + +#endif /* !WIN32 */ + +#endif /* !defined(WIN32) || defined(FRONTEND) */ diff --git a/libpq/thread.c b/libpq/thread.c new file mode 100644 index 0000000..ed908ba --- /dev/null +++ b/libpq/thread.c @@ -0,0 +1,146 @@ +/*------------------------------------------------------------------------- + * + * thread.c + * + * Prototypes and macros around system calls, used to help make + * threaded libraries reentrant and safe to use from threaded applications. + * + * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * + * src/port/thread.c + * + *------------------------------------------------------------------------- + */ + +#include "c.h" + +#include + + +/* + * Threading sometimes requires specially-named versions of functions + * that return data in static buffers, like strerror_r() instead of + * strerror(). Other operating systems use pthread_setspecific() + * and pthread_getspecific() internally to allow standard library + * functions to return static data to threaded applications. And some + * operating systems have neither. + * + * Additional confusion exists because many operating systems that + * use pthread_setspecific/pthread_getspecific() also have *_r versions + * of standard library functions for compatibility with operating systems + * that require them. However, internally, these *_r functions merely + * call the thread-safe standard library functions. + * + * For example, BSD/OS 4.3 uses Bind 8.2.3 for getpwuid(). Internally, + * getpwuid() calls pthread_setspecific/pthread_getspecific() to return + * static data to the caller in a thread-safe manner. However, BSD/OS + * also has getpwuid_r(), which merely calls getpwuid() and shifts + * around the arguments to match the getpwuid_r() function declaration. + * Therefore, while BSD/OS has getpwuid_r(), it isn't required. It also + * doesn't have strerror_r(), so we can't fall back to only using *_r + * functions for threaded programs. + * + * The current setup is to try threading in this order: + * + * use *_r function names if they exit + * (*_THREADSAFE=yes) + * use non-*_r functions if they are thread-safe + * + * One thread-safe solution for gethostbyname() might be to use getaddrinfo(). + * + * Run src/test/thread to test if your operating system has thread-safe + * non-*_r functions. + */ + + +/* + * Wrapper around strerror and strerror_r to use the former if it is + * available and also return a more useful value (the error string). + */ +char * +pqStrerror(int errnum, char *strerrbuf, size_t buflen) +{ +#if defined(FRONTEND) && defined(ENABLE_THREAD_SAFETY) && defined(HAVE_STRERROR_R) + /* reentrant strerror_r is available */ +#ifdef STRERROR_R_INT + /* SUSv3 version */ + if (strerror_r(errnum, strerrbuf, buflen) == 0) + return strerrbuf; + else + return "Unknown error"; +#else + /* GNU libc */ + return strerror_r(errnum, strerrbuf, buflen); +#endif +#else + /* no strerror_r() available, just use strerror */ + strlcpy(strerrbuf, strerror(errnum), buflen); + + return strerrbuf; +#endif +} + +/* + * Wrapper around getpwuid() or getpwuid_r() to mimic POSIX getpwuid_r() + * behaviour, if that function is not available or required. + * + * Per POSIX, the possible cases are: + * success: returns zero, *result is non-NULL + * uid not found: returns zero, *result is NULL + * error during lookup: returns an errno code, *result is NULL + * (caller should *not* assume that the errno variable is set) + */ +#ifndef WIN32 +int +pqGetpwuid(uid_t uid, struct passwd * resultbuf, char *buffer, + size_t buflen, struct passwd ** result) +{ +#if defined(FRONTEND) && defined(ENABLE_THREAD_SAFETY) && defined(HAVE_GETPWUID_R) + return getpwuid_r(uid, resultbuf, buffer, buflen, result); +#else + /* no getpwuid_r() available, just use getpwuid() */ + errno = 0; + *result = getpwuid(uid); + /* paranoia: ensure we return zero on success */ + return (*result == NULL) ? errno : 0; +#endif +} +#endif + +/* + * Wrapper around gethostbyname() or gethostbyname_r() to mimic + * POSIX gethostbyname_r() behaviour, if it is not available or required. + * This function is called _only_ by our getaddinfo() portability function. + */ +#ifndef HAVE_GETADDRINFO +int +pqGethostbyname(const char *name, + struct hostent * resultbuf, + char *buffer, size_t buflen, + struct hostent ** result, + int *herrno) +{ +#if defined(FRONTEND) && defined(ENABLE_THREAD_SAFETY) && defined(HAVE_GETHOSTBYNAME_R) + + /* + * broken (well early POSIX draft) gethostbyname_r() which returns 'struct + * hostent *' + */ + *result = gethostbyname_r(name, resultbuf, buffer, buflen, herrno); + return (*result == NULL) ? -1 : 0; +#else + + /* no gethostbyname_r(), just use gethostbyname() */ + *result = gethostbyname(name); + + if (*result != NULL) + *herrno = h_errno; + + if (*result != NULL) + return 0; + else + return -1; +#endif +} + +#endif diff --git a/libpq/version.h.in b/libpq/version.h.in new file mode 100644 index 0000000..f5d6b31 --- /dev/null +++ b/libpq/version.h.in @@ -0,0 +1,26 @@ +/* file : libpq/version.h.in -*- C -*- + * copyright : Copyright (c) 2016-2017 Code Synthesis Ltd + * license : PostgreSQL License; see accompanying COPYRIGHT file + */ + +#ifndef PG_MAJORVERSION /* Note: using the version macro itself. */ + +/* + * Note that staring PostgreSQL 10 the release version schema changed (see + * build/bootstrap.build for details). + */ +#if $libpq.version.major$ < 10 +# define PG_MAJORVERSION "$libpq.version.major$.$libpq.version.minor$" +#else +# error "change PG_MAJORVERSION macro definition" +# define PG_MAJORVERSION "$libpq.version.major$" +#endif + +/* + * Is valid for both old and new version schemes (see above). + */ +#define PG_VERSION_NUM (10000 * $libpq.version.major$ + \ + 100 * $libpq.version.minor$ + \ + $libpq.version.patch$) + +#endif /* PG_MAJORVERSION */ diff --git a/libpq/wchar.c b/libpq/wchar.c new file mode 100644 index 0000000..fd51eed --- /dev/null +++ b/libpq/wchar.c @@ -0,0 +1,2054 @@ +/* + * conversion functions between pg_wchar and multibyte streams. + * Tatsuo Ishii + * src/backend/utils/mb/wchar.c + * + */ +/* can be used in either frontend or backend */ +#ifdef FRONTEND +#include "postgres_fe.h" +#else +#include "postgres.h" +#endif + +#include "mb/pg_wchar.h" + + +/* + * conversion to pg_wchar is done by "table driven." + * to add an encoding support, define mb2wchar_with_len(), mblen(), dsplen() + * for the particular encoding. Note that if the encoding is only + * supported in the client, you don't need to define + * mb2wchar_with_len() function (SJIS is the case). + * + * These functions generally assume that their input is validly formed. + * The "verifier" functions, further down in the file, have to be more + * paranoid. We expect that mblen() does not need to examine more than + * the first byte of the character to discover the correct length. + * + * Note: for the display output of psql to work properly, the return values + * of the dsplen functions must conform to the Unicode standard. In particular + * the NUL character is zero width and control characters are generally + * width -1. It is recommended that non-ASCII encodings refer their ASCII + * subset to the ASCII routines to ensure consistency. + */ + +/* + * SQL/ASCII + */ +static int +pg_ascii2wchar_with_len(const unsigned char *from, pg_wchar *to, int len) +{ + int cnt = 0; + + while (len > 0 && *from) + { + *to++ = *from++; + len--; + cnt++; + } + *to = 0; + return cnt; +} + +static int +pg_ascii_mblen(const unsigned char *s) +{ + return 1; +} + +static int +pg_ascii_dsplen(const unsigned char *s) +{ + if (*s == '\0') + return 0; + if (*s < 0x20 || *s == 0x7f) + return -1; + + return 1; +} + +/* + * EUC + */ +static int +pg_euc2wchar_with_len(const unsigned char *from, pg_wchar *to, int len) +{ + int cnt = 0; + + while (len > 0 && *from) + { + if (*from == SS2 && len >= 2) /* JIS X 0201 (so called "1 byte + * KANA") */ + { + from++; + *to = (SS2 << 8) | *from++; + len -= 2; + } + else if (*from == SS3 && len >= 3) /* JIS X 0212 KANJI */ + { + from++; + *to = (SS3 << 16) | (*from++ << 8); + *to |= *from++; + len -= 3; + } + else if (IS_HIGHBIT_SET(*from) && len >= 2) /* JIS X 0208 KANJI */ + { + *to = *from++ << 8; + *to |= *from++; + len -= 2; + } + else /* must be ASCII */ + { + *to = *from++; + len--; + } + to++; + cnt++; + } + *to = 0; + return cnt; +} + +static inline int +pg_euc_mblen(const unsigned char *s) +{ + int len; + + if (*s == SS2) + len = 2; + else if (*s == SS3) + len = 3; + else if (IS_HIGHBIT_SET(*s)) + len = 2; + else + len = 1; + return len; +} + +static inline int +pg_euc_dsplen(const unsigned char *s) +{ + int len; + + if (*s == SS2) + len = 2; + else if (*s == SS3) + len = 2; + else if (IS_HIGHBIT_SET(*s)) + len = 2; + else + len = pg_ascii_dsplen(s); + return len; +} + +/* + * EUC_JP + */ +static int +pg_eucjp2wchar_with_len(const unsigned char *from, pg_wchar *to, int len) +{ + return pg_euc2wchar_with_len(from, to, len); +} + +static int +pg_eucjp_mblen(const unsigned char *s) +{ + return pg_euc_mblen(s); +} + +static int +pg_eucjp_dsplen(const unsigned char *s) +{ + int len; + + if (*s == SS2) + len = 1; + else if (*s == SS3) + len = 2; + else if (IS_HIGHBIT_SET(*s)) + len = 2; + else + len = pg_ascii_dsplen(s); + return len; +} + +/* + * EUC_KR + */ +static int +pg_euckr2wchar_with_len(const unsigned char *from, pg_wchar *to, int len) +{ + return pg_euc2wchar_with_len(from, to, len); +} + +static int +pg_euckr_mblen(const unsigned char *s) +{ + return pg_euc_mblen(s); +} + +static int +pg_euckr_dsplen(const unsigned char *s) +{ + return pg_euc_dsplen(s); +} + +/* + * EUC_CN + * + */ +static int +pg_euccn2wchar_with_len(const unsigned char *from, pg_wchar *to, int len) +{ + int cnt = 0; + + while (len > 0 && *from) + { + if (*from == SS2 && len >= 3) /* code set 2 (unused?) */ + { + from++; + *to = (SS2 << 16) | (*from++ << 8); + *to |= *from++; + len -= 3; + } + else if (*from == SS3 && len >= 3) /* code set 3 (unused ?) */ + { + from++; + *to = (SS3 << 16) | (*from++ << 8); + *to |= *from++; + len -= 3; + } + else if (IS_HIGHBIT_SET(*from) && len >= 2) /* code set 1 */ + { + *to = *from++ << 8; + *to |= *from++; + len -= 2; + } + else + { + *to = *from++; + len--; + } + to++; + cnt++; + } + *to = 0; + return cnt; +} + +static int +pg_euccn_mblen(const unsigned char *s) +{ + int len; + + if (IS_HIGHBIT_SET(*s)) + len = 2; + else + len = 1; + return len; +} + +static int +pg_euccn_dsplen(const unsigned char *s) +{ + int len; + + if (IS_HIGHBIT_SET(*s)) + len = 2; + else + len = pg_ascii_dsplen(s); + return len; +} + +/* + * EUC_TW + * + */ +static int +pg_euctw2wchar_with_len(const unsigned char *from, pg_wchar *to, int len) +{ + int cnt = 0; + + while (len > 0 && *from) + { + if (*from == SS2 && len >= 4) /* code set 2 */ + { + from++; + *to = (((uint32) SS2) << 24) | (*from++ << 16); + *to |= *from++ << 8; + *to |= *from++; + len -= 4; + } + else if (*from == SS3 && len >= 3) /* code set 3 (unused?) */ + { + from++; + *to = (SS3 << 16) | (*from++ << 8); + *to |= *from++; + len -= 3; + } + else if (IS_HIGHBIT_SET(*from) && len >= 2) /* code set 2 */ + { + *to = *from++ << 8; + *to |= *from++; + len -= 2; + } + else + { + *to = *from++; + len--; + } + to++; + cnt++; + } + *to = 0; + return cnt; +} + +static int +pg_euctw_mblen(const unsigned char *s) +{ + int len; + + if (*s == SS2) + len = 4; + else if (*s == SS3) + len = 3; + else if (IS_HIGHBIT_SET(*s)) + len = 2; + else + len = 1; + return len; +} + +static int +pg_euctw_dsplen(const unsigned char *s) +{ + int len; + + if (*s == SS2) + len = 2; + else if (*s == SS3) + len = 2; + else if (IS_HIGHBIT_SET(*s)) + len = 2; + else + len = pg_ascii_dsplen(s); + return len; +} + +/* + * Convert pg_wchar to EUC_* encoding. + * caller must allocate enough space for "to", including a trailing zero! + * len: length of from. + * "from" not necessarily null terminated. + */ +static int +pg_wchar2euc_with_len(const pg_wchar *from, unsigned char *to, int len) +{ + int cnt = 0; + + while (len > 0 && *from) + { + unsigned char c; + + if ((c = (*from >> 24))) + { + *to++ = c; + *to++ = (*from >> 16) & 0xff; + *to++ = (*from >> 8) & 0xff; + *to++ = *from & 0xff; + cnt += 4; + } + else if ((c = (*from >> 16))) + { + *to++ = c; + *to++ = (*from >> 8) & 0xff; + *to++ = *from & 0xff; + cnt += 3; + } + else if ((c = (*from >> 8))) + { + *to++ = c; + *to++ = *from & 0xff; + cnt += 2; + } + else + { + *to++ = *from; + cnt++; + } + from++; + len--; + } + *to = 0; + return cnt; +} + + +/* + * JOHAB + */ +static int +pg_johab_mblen(const unsigned char *s) +{ + return pg_euc_mblen(s); +} + +static int +pg_johab_dsplen(const unsigned char *s) +{ + return pg_euc_dsplen(s); +} + +/* + * convert UTF8 string to pg_wchar (UCS-4) + * caller must allocate enough space for "to", including a trailing zero! + * len: length of from. + * "from" not necessarily null terminated. + */ +static int +pg_utf2wchar_with_len(const unsigned char *from, pg_wchar *to, int len) +{ + int cnt = 0; + uint32 c1, + c2, + c3, + c4; + + while (len > 0 && *from) + { + if ((*from & 0x80) == 0) + { + *to = *from++; + len--; + } + else if ((*from & 0xe0) == 0xc0) + { + if (len < 2) + break; /* drop trailing incomplete char */ + c1 = *from++ & 0x1f; + c2 = *from++ & 0x3f; + *to = (c1 << 6) | c2; + len -= 2; + } + else if ((*from & 0xf0) == 0xe0) + { + if (len < 3) + break; /* drop trailing incomplete char */ + c1 = *from++ & 0x0f; + c2 = *from++ & 0x3f; + c3 = *from++ & 0x3f; + *to = (c1 << 12) | (c2 << 6) | c3; + len -= 3; + } + else if ((*from & 0xf8) == 0xf0) + { + if (len < 4) + break; /* drop trailing incomplete char */ + c1 = *from++ & 0x07; + c2 = *from++ & 0x3f; + c3 = *from++ & 0x3f; + c4 = *from++ & 0x3f; + *to = (c1 << 18) | (c2 << 12) | (c3 << 6) | c4; + len -= 4; + } + else + { + /* treat a bogus char as length 1; not ours to raise error */ + *to = *from++; + len--; + } + to++; + cnt++; + } + *to = 0; + return cnt; +} + + +/* + * Map a Unicode code point to UTF-8. utf8string must have 4 bytes of + * space allocated. + */ +unsigned char * +unicode_to_utf8(pg_wchar c, unsigned char *utf8string) +{ + if (c <= 0x7F) + { + utf8string[0] = c; + } + else if (c <= 0x7FF) + { + utf8string[0] = 0xC0 | ((c >> 6) & 0x1F); + utf8string[1] = 0x80 | (c & 0x3F); + } + else if (c <= 0xFFFF) + { + utf8string[0] = 0xE0 | ((c >> 12) & 0x0F); + utf8string[1] = 0x80 | ((c >> 6) & 0x3F); + utf8string[2] = 0x80 | (c & 0x3F); + } + else + { + utf8string[0] = 0xF0 | ((c >> 18) & 0x07); + utf8string[1] = 0x80 | ((c >> 12) & 0x3F); + utf8string[2] = 0x80 | ((c >> 6) & 0x3F); + utf8string[3] = 0x80 | (c & 0x3F); + } + + return utf8string; +} + +/* + * Trivial conversion from pg_wchar to UTF-8. + * caller should allocate enough space for "to" + * len: length of from. + * "from" not necessarily null terminated. + */ +static int +pg_wchar2utf_with_len(const pg_wchar *from, unsigned char *to, int len) +{ + int cnt = 0; + + while (len > 0 && *from) + { + int char_len; + + unicode_to_utf8(*from, to); + char_len = pg_utf_mblen(to); + cnt += char_len; + to += char_len; + from++; + len--; + } + *to = 0; + return cnt; +} + +/* + * Return the byte length of a UTF8 character pointed to by s + * + * Note: in the current implementation we do not support UTF8 sequences + * of more than 4 bytes; hence do NOT return a value larger than 4. + * We return "1" for any leading byte that is either flat-out illegal or + * indicates a length larger than we support. + * + * pg_utf2wchar_with_len(), utf8_to_unicode(), pg_utf8_islegal(), and perhaps + * other places would need to be fixed to change this. + */ +int +pg_utf_mblen(const unsigned char *s) +{ + int len; + + if ((*s & 0x80) == 0) + len = 1; + else if ((*s & 0xe0) == 0xc0) + len = 2; + else if ((*s & 0xf0) == 0xe0) + len = 3; + else if ((*s & 0xf8) == 0xf0) + len = 4; +#ifdef NOT_USED + else if ((*s & 0xfc) == 0xf8) + len = 5; + else if ((*s & 0xfe) == 0xfc) + len = 6; +#endif + else + len = 1; + return len; +} + +/* + * This is an implementation of wcwidth() and wcswidth() as defined in + * "The Single UNIX Specification, Version 2, The Open Group, 1997" + * + * + * Markus Kuhn -- 2001-09-08 -- public domain + * + * customised for PostgreSQL + * + * original available at : http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c + */ + +struct mbinterval +{ + unsigned short first; + unsigned short last; +}; + +/* auxiliary function for binary search in interval table */ +static int +mbbisearch(pg_wchar ucs, const struct mbinterval * table, int max) +{ + int min = 0; + int mid; + + if (ucs < table[0].first || ucs > table[max].last) + return 0; + while (max >= min) + { + mid = (min + max) / 2; + if (ucs > table[mid].last) + min = mid + 1; + else if (ucs < table[mid].first) + max = mid - 1; + else + return 1; + } + + return 0; +} + + +/* The following functions define the column width of an ISO 10646 + * character as follows: + * + * - The null character (U+0000) has a column width of 0. + * + * - Other C0/C1 control characters and DEL will lead to a return + * value of -1. + * + * - Non-spacing and enclosing combining characters (general + * category code Mn or Me in the Unicode database) have a + * column width of 0. + * + * - Other format characters (general category code Cf in the Unicode + * database) and ZERO WIDTH SPACE (U+200B) have a column width of 0. + * + * - Hangul Jamo medial vowels and final consonants (U+1160-U+11FF) + * have a column width of 0. + * + * - Spacing characters in the East Asian Wide (W) or East Asian + * FullWidth (F) category as defined in Unicode Technical + * Report #11 have a column width of 2. + * + * - All remaining characters (including all printable + * ISO 8859-1 and WGL4 characters, Unicode control characters, + * etc.) have a column width of 1. + * + * This implementation assumes that wchar_t characters are encoded + * in ISO 10646. + */ + +static int +ucs_wcwidth(pg_wchar ucs) +{ + /* sorted list of non-overlapping intervals of non-spacing characters */ + static const struct mbinterval combining[] = { + {0x0300, 0x034E}, {0x0360, 0x0362}, {0x0483, 0x0486}, + {0x0488, 0x0489}, {0x0591, 0x05A1}, {0x05A3, 0x05B9}, + {0x05BB, 0x05BD}, {0x05BF, 0x05BF}, {0x05C1, 0x05C2}, + {0x05C4, 0x05C4}, {0x064B, 0x0655}, {0x0670, 0x0670}, + {0x06D6, 0x06E4}, {0x06E7, 0x06E8}, {0x06EA, 0x06ED}, + {0x070F, 0x070F}, {0x0711, 0x0711}, {0x0730, 0x074A}, + {0x07A6, 0x07B0}, {0x0901, 0x0902}, {0x093C, 0x093C}, + {0x0941, 0x0948}, {0x094D, 0x094D}, {0x0951, 0x0954}, + {0x0962, 0x0963}, {0x0981, 0x0981}, {0x09BC, 0x09BC}, + {0x09C1, 0x09C4}, {0x09CD, 0x09CD}, {0x09E2, 0x09E3}, + {0x0A02, 0x0A02}, {0x0A3C, 0x0A3C}, {0x0A41, 0x0A42}, + {0x0A47, 0x0A48}, {0x0A4B, 0x0A4D}, {0x0A70, 0x0A71}, + {0x0A81, 0x0A82}, {0x0ABC, 0x0ABC}, {0x0AC1, 0x0AC5}, + {0x0AC7, 0x0AC8}, {0x0ACD, 0x0ACD}, {0x0B01, 0x0B01}, + {0x0B3C, 0x0B3C}, {0x0B3F, 0x0B3F}, {0x0B41, 0x0B43}, + {0x0B4D, 0x0B4D}, {0x0B56, 0x0B56}, {0x0B82, 0x0B82}, + {0x0BC0, 0x0BC0}, {0x0BCD, 0x0BCD}, {0x0C3E, 0x0C40}, + {0x0C46, 0x0C48}, {0x0C4A, 0x0C4D}, {0x0C55, 0x0C56}, + {0x0CBF, 0x0CBF}, {0x0CC6, 0x0CC6}, {0x0CCC, 0x0CCD}, + {0x0D41, 0x0D43}, {0x0D4D, 0x0D4D}, {0x0DCA, 0x0DCA}, + {0x0DD2, 0x0DD4}, {0x0DD6, 0x0DD6}, {0x0E31, 0x0E31}, + {0x0E34, 0x0E3A}, {0x0E47, 0x0E4E}, {0x0EB1, 0x0EB1}, + {0x0EB4, 0x0EB9}, {0x0EBB, 0x0EBC}, {0x0EC8, 0x0ECD}, + {0x0F18, 0x0F19}, {0x0F35, 0x0F35}, {0x0F37, 0x0F37}, + {0x0F39, 0x0F39}, {0x0F71, 0x0F7E}, {0x0F80, 0x0F84}, + {0x0F86, 0x0F87}, {0x0F90, 0x0F97}, {0x0F99, 0x0FBC}, + {0x0FC6, 0x0FC6}, {0x102D, 0x1030}, {0x1032, 0x1032}, + {0x1036, 0x1037}, {0x1039, 0x1039}, {0x1058, 0x1059}, + {0x1160, 0x11FF}, {0x17B7, 0x17BD}, {0x17C6, 0x17C6}, + {0x17C9, 0x17D3}, {0x180B, 0x180E}, {0x18A9, 0x18A9}, + {0x200B, 0x200F}, {0x202A, 0x202E}, {0x206A, 0x206F}, + {0x20D0, 0x20E3}, {0x302A, 0x302F}, {0x3099, 0x309A}, + {0xFB1E, 0xFB1E}, {0xFE20, 0xFE23}, {0xFEFF, 0xFEFF}, + {0xFFF9, 0xFFFB} + }; + + /* test for 8-bit control characters */ + if (ucs == 0) + return 0; + + if (ucs < 0x20 || (ucs >= 0x7f && ucs < 0xa0) || ucs > 0x0010ffff) + return -1; + + /* binary search in table of non-spacing characters */ + if (mbbisearch(ucs, combining, + sizeof(combining) / sizeof(struct mbinterval) - 1)) + return 0; + + /* + * if we arrive here, ucs is not a combining or C0/C1 control character + */ + + return 1 + + (ucs >= 0x1100 && + (ucs <= 0x115f || /* Hangul Jamo init. consonants */ + (ucs >= 0x2e80 && ucs <= 0xa4cf && (ucs & ~0x0011) != 0x300a && + ucs != 0x303f) || /* CJK ... Yi */ + (ucs >= 0xac00 && ucs <= 0xd7a3) || /* Hangul Syllables */ + (ucs >= 0xf900 && ucs <= 0xfaff) || /* CJK Compatibility + * Ideographs */ + (ucs >= 0xfe30 && ucs <= 0xfe6f) || /* CJK Compatibility Forms */ + (ucs >= 0xff00 && ucs <= 0xff5f) || /* Fullwidth Forms */ + (ucs >= 0xffe0 && ucs <= 0xffe6) || + (ucs >= 0x20000 && ucs <= 0x2ffff))); +} + +/* + * Convert a UTF-8 character to a Unicode code point. + * This is a one-character version of pg_utf2wchar_with_len. + * + * No error checks here, c must point to a long-enough string. + */ +pg_wchar +utf8_to_unicode(const unsigned char *c) +{ + if ((*c & 0x80) == 0) + return (pg_wchar) c[0]; + else if ((*c & 0xe0) == 0xc0) + return (pg_wchar) (((c[0] & 0x1f) << 6) | + (c[1] & 0x3f)); + else if ((*c & 0xf0) == 0xe0) + return (pg_wchar) (((c[0] & 0x0f) << 12) | + ((c[1] & 0x3f) << 6) | + (c[2] & 0x3f)); + else if ((*c & 0xf8) == 0xf0) + return (pg_wchar) (((c[0] & 0x07) << 18) | + ((c[1] & 0x3f) << 12) | + ((c[2] & 0x3f) << 6) | + (c[3] & 0x3f)); + else + /* that is an invalid code on purpose */ + return 0xffffffff; +} + +static int +pg_utf_dsplen(const unsigned char *s) +{ + return ucs_wcwidth(utf8_to_unicode(s)); +} + +/* + * convert mule internal code to pg_wchar + * caller should allocate enough space for "to" + * len: length of from. + * "from" not necessarily null terminated. + */ +static int +pg_mule2wchar_with_len(const unsigned char *from, pg_wchar *to, int len) +{ + int cnt = 0; + + while (len > 0 && *from) + { + if (IS_LC1(*from) && len >= 2) + { + *to = *from++ << 16; + *to |= *from++; + len -= 2; + } + else if (IS_LCPRV1(*from) && len >= 3) + { + from++; + *to = *from++ << 16; + *to |= *from++; + len -= 3; + } + else if (IS_LC2(*from) && len >= 3) + { + *to = *from++ << 16; + *to |= *from++ << 8; + *to |= *from++; + len -= 3; + } + else if (IS_LCPRV2(*from) && len >= 4) + { + from++; + *to = *from++ << 16; + *to |= *from++ << 8; + *to |= *from++; + len -= 4; + } + else + { /* assume ASCII */ + *to = (unsigned char) *from++; + len--; + } + to++; + cnt++; + } + *to = 0; + return cnt; +} + +/* + * convert pg_wchar to mule internal code + * caller should allocate enough space for "to" + * len: length of from. + * "from" not necessarily null terminated. + */ +static int +pg_wchar2mule_with_len(const pg_wchar *from, unsigned char *to, int len) +{ + int cnt = 0; + + while (len > 0 && *from) + { + unsigned char lb; + + lb = (*from >> 16) & 0xff; + if (IS_LC1(lb)) + { + *to++ = lb; + *to++ = *from & 0xff; + cnt += 2; + } + else if (IS_LC2(lb)) + { + *to++ = lb; + *to++ = (*from >> 8) & 0xff; + *to++ = *from & 0xff; + cnt += 3; + } + else if (IS_LCPRV1_A_RANGE(lb)) + { + *to++ = LCPRV1_A; + *to++ = lb; + *to++ = *from & 0xff; + cnt += 3; + } + else if (IS_LCPRV1_B_RANGE(lb)) + { + *to++ = LCPRV1_B; + *to++ = lb; + *to++ = *from & 0xff; + cnt += 3; + } + else if (IS_LCPRV2_A_RANGE(lb)) + { + *to++ = LCPRV2_A; + *to++ = lb; + *to++ = (*from >> 8) & 0xff; + *to++ = *from & 0xff; + cnt += 4; + } + else if (IS_LCPRV2_B_RANGE(lb)) + { + *to++ = LCPRV2_B; + *to++ = lb; + *to++ = (*from >> 8) & 0xff; + *to++ = *from & 0xff; + cnt += 4; + } + else + { + *to++ = *from & 0xff; + cnt += 1; + } + from++; + len--; + } + *to = 0; + return cnt; +} + +int +pg_mule_mblen(const unsigned char *s) +{ + int len; + + if (IS_LC1(*s)) + len = 2; + else if (IS_LCPRV1(*s)) + len = 3; + else if (IS_LC2(*s)) + len = 3; + else if (IS_LCPRV2(*s)) + len = 4; + else + len = 1; /* assume ASCII */ + return len; +} + +static int +pg_mule_dsplen(const unsigned char *s) +{ + int len; + + /* + * Note: it's not really appropriate to assume that all multibyte charsets + * are double-wide on screen. But this seems an okay approximation for + * the MULE charsets we currently support. + */ + + if (IS_LC1(*s)) + len = 1; + else if (IS_LCPRV1(*s)) + len = 1; + else if (IS_LC2(*s)) + len = 2; + else if (IS_LCPRV2(*s)) + len = 2; + else + len = 1; /* assume ASCII */ + + return len; +} + +/* + * ISO8859-1 + */ +static int +pg_latin12wchar_with_len(const unsigned char *from, pg_wchar *to, int len) +{ + int cnt = 0; + + while (len > 0 && *from) + { + *to++ = *from++; + len--; + cnt++; + } + *to = 0; + return cnt; +} + +/* + * Trivial conversion from pg_wchar to single byte encoding. Just ignores + * high bits. + * caller should allocate enough space for "to" + * len: length of from. + * "from" not necessarily null terminated. + */ +static int +pg_wchar2single_with_len(const pg_wchar *from, unsigned char *to, int len) +{ + int cnt = 0; + + while (len > 0 && *from) + { + *to++ = *from++; + len--; + cnt++; + } + *to = 0; + return cnt; +} + +static int +pg_latin1_mblen(const unsigned char *s) +{ + return 1; +} + +static int +pg_latin1_dsplen(const unsigned char *s) +{ + return pg_ascii_dsplen(s); +} + +/* + * SJIS + */ +static int +pg_sjis_mblen(const unsigned char *s) +{ + int len; + + if (*s >= 0xa1 && *s <= 0xdf) + len = 1; /* 1 byte kana? */ + else if (IS_HIGHBIT_SET(*s)) + len = 2; /* kanji? */ + else + len = 1; /* should be ASCII */ + return len; +} + +static int +pg_sjis_dsplen(const unsigned char *s) +{ + int len; + + if (*s >= 0xa1 && *s <= 0xdf) + len = 1; /* 1 byte kana? */ + else if (IS_HIGHBIT_SET(*s)) + len = 2; /* kanji? */ + else + len = pg_ascii_dsplen(s); /* should be ASCII */ + return len; +} + +/* + * Big5 + */ +static int +pg_big5_mblen(const unsigned char *s) +{ + int len; + + if (IS_HIGHBIT_SET(*s)) + len = 2; /* kanji? */ + else + len = 1; /* should be ASCII */ + return len; +} + +static int +pg_big5_dsplen(const unsigned char *s) +{ + int len; + + if (IS_HIGHBIT_SET(*s)) + len = 2; /* kanji? */ + else + len = pg_ascii_dsplen(s); /* should be ASCII */ + return len; +} + +/* + * GBK + */ +static int +pg_gbk_mblen(const unsigned char *s) +{ + int len; + + if (IS_HIGHBIT_SET(*s)) + len = 2; /* kanji? */ + else + len = 1; /* should be ASCII */ + return len; +} + +static int +pg_gbk_dsplen(const unsigned char *s) +{ + int len; + + if (IS_HIGHBIT_SET(*s)) + len = 2; /* kanji? */ + else + len = pg_ascii_dsplen(s); /* should be ASCII */ + return len; +} + +/* + * UHC + */ +static int +pg_uhc_mblen(const unsigned char *s) +{ + int len; + + if (IS_HIGHBIT_SET(*s)) + len = 2; /* 2byte? */ + else + len = 1; /* should be ASCII */ + return len; +} + +static int +pg_uhc_dsplen(const unsigned char *s) +{ + int len; + + if (IS_HIGHBIT_SET(*s)) + len = 2; /* 2byte? */ + else + len = pg_ascii_dsplen(s); /* should be ASCII */ + return len; +} + +/* + * GB18030 + * Added by Bill Huang , + */ +static int +pg_gb18030_mblen(const unsigned char *s) +{ + int len; + + if (!IS_HIGHBIT_SET(*s)) + len = 1; /* ASCII */ + else if (*(s + 1) >= 0x30 && *(s + 1) <= 0x39) + len = 4; + else + len = 2; + return len; +} + +static int +pg_gb18030_dsplen(const unsigned char *s) +{ + int len; + + if (IS_HIGHBIT_SET(*s)) + len = 2; + else + len = pg_ascii_dsplen(s); /* ASCII */ + return len; +} + +/* + *------------------------------------------------------------------- + * multibyte sequence validators + * + * These functions accept "s", a pointer to the first byte of a string, + * and "len", the remaining length of the string. If there is a validly + * encoded character beginning at *s, return its length in bytes; else + * return -1. + * + * The functions can assume that len > 0 and that *s != '\0', but they must + * test for and reject zeroes in any additional bytes of a multibyte character. + * + * Note that this definition allows the function for a single-byte + * encoding to be just "return 1". + *------------------------------------------------------------------- + */ + +static int +pg_ascii_verifier(const unsigned char *s, int len) +{ + return 1; +} + +#define IS_EUC_RANGE_VALID(c) ((c) >= 0xa1 && (c) <= 0xfe) + +static int +pg_eucjp_verifier(const unsigned char *s, int len) +{ + int l; + unsigned char c1, + c2; + + c1 = *s++; + + switch (c1) + { + case SS2: /* JIS X 0201 */ + l = 2; + if (l > len) + return -1; + c2 = *s++; + if (c2 < 0xa1 || c2 > 0xdf) + return -1; + break; + + case SS3: /* JIS X 0212 */ + l = 3; + if (l > len) + return -1; + c2 = *s++; + if (!IS_EUC_RANGE_VALID(c2)) + return -1; + c2 = *s++; + if (!IS_EUC_RANGE_VALID(c2)) + return -1; + break; + + default: + if (IS_HIGHBIT_SET(c1)) /* JIS X 0208? */ + { + l = 2; + if (l > len) + return -1; + if (!IS_EUC_RANGE_VALID(c1)) + return -1; + c2 = *s++; + if (!IS_EUC_RANGE_VALID(c2)) + return -1; + } + else + /* must be ASCII */ + { + l = 1; + } + break; + } + + return l; +} + +static int +pg_euckr_verifier(const unsigned char *s, int len) +{ + int l; + unsigned char c1, + c2; + + c1 = *s++; + + if (IS_HIGHBIT_SET(c1)) + { + l = 2; + if (l > len) + return -1; + if (!IS_EUC_RANGE_VALID(c1)) + return -1; + c2 = *s++; + if (!IS_EUC_RANGE_VALID(c2)) + return -1; + } + else + /* must be ASCII */ + { + l = 1; + } + + return l; +} + +/* EUC-CN byte sequences are exactly same as EUC-KR */ +#define pg_euccn_verifier pg_euckr_verifier + +static int +pg_euctw_verifier(const unsigned char *s, int len) +{ + int l; + unsigned char c1, + c2; + + c1 = *s++; + + switch (c1) + { + case SS2: /* CNS 11643 Plane 1-7 */ + l = 4; + if (l > len) + return -1; + c2 = *s++; + if (c2 < 0xa1 || c2 > 0xa7) + return -1; + c2 = *s++; + if (!IS_EUC_RANGE_VALID(c2)) + return -1; + c2 = *s++; + if (!IS_EUC_RANGE_VALID(c2)) + return -1; + break; + + case SS3: /* unused */ + return -1; + + default: + if (IS_HIGHBIT_SET(c1)) /* CNS 11643 Plane 1 */ + { + l = 2; + if (l > len) + return -1; + /* no further range check on c1? */ + c2 = *s++; + if (!IS_EUC_RANGE_VALID(c2)) + return -1; + } + else + /* must be ASCII */ + { + l = 1; + } + break; + } + return l; +} + +static int +pg_johab_verifier(const unsigned char *s, int len) +{ + int l, + mbl; + unsigned char c; + + l = mbl = pg_johab_mblen(s); + + if (len < l) + return -1; + + if (!IS_HIGHBIT_SET(*s)) + return mbl; + + while (--l > 0) + { + c = *++s; + if (!IS_EUC_RANGE_VALID(c)) + return -1; + } + return mbl; +} + +static int +pg_mule_verifier(const unsigned char *s, int len) +{ + int l, + mbl; + unsigned char c; + + l = mbl = pg_mule_mblen(s); + + if (len < l) + return -1; + + while (--l > 0) + { + c = *++s; + if (!IS_HIGHBIT_SET(c)) + return -1; + } + return mbl; +} + +static int +pg_latin1_verifier(const unsigned char *s, int len) +{ + return 1; +} + +static int +pg_sjis_verifier(const unsigned char *s, int len) +{ + int l, + mbl; + unsigned char c1, + c2; + + l = mbl = pg_sjis_mblen(s); + + if (len < l) + return -1; + + if (l == 1) /* pg_sjis_mblen already verified it */ + return mbl; + + c1 = *s++; + c2 = *s; + if (!ISSJISHEAD(c1) || !ISSJISTAIL(c2)) + return -1; + return mbl; +} + +static int +pg_big5_verifier(const unsigned char *s, int len) +{ + int l, + mbl; + + l = mbl = pg_big5_mblen(s); + + if (len < l) + return -1; + + while (--l > 0) + { + if (*++s == '\0') + return -1; + } + + return mbl; +} + +static int +pg_gbk_verifier(const unsigned char *s, int len) +{ + int l, + mbl; + + l = mbl = pg_gbk_mblen(s); + + if (len < l) + return -1; + + while (--l > 0) + { + if (*++s == '\0') + return -1; + } + + return mbl; +} + +static int +pg_uhc_verifier(const unsigned char *s, int len) +{ + int l, + mbl; + + l = mbl = pg_uhc_mblen(s); + + if (len < l) + return -1; + + while (--l > 0) + { + if (*++s == '\0') + return -1; + } + + return mbl; +} + +static int +pg_gb18030_verifier(const unsigned char *s, int len) +{ + int l; + + if (!IS_HIGHBIT_SET(*s)) + l = 1; /* ASCII */ + else if (len >= 4 && *(s + 1) >= 0x30 && *(s + 1) <= 0x39) + { + /* Should be 4-byte, validate remaining bytes */ + if (*s >= 0x81 && *s <= 0xfe && + *(s + 2) >= 0x81 && *(s + 2) <= 0xfe && + *(s + 3) >= 0x30 && *(s + 3) <= 0x39) + l = 4; + else + l = -1; + } + else if (len >= 2 && *s >= 0x81 && *s <= 0xfe) + { + /* Should be 2-byte, validate */ + if ((*(s + 1) >= 0x40 && *(s + 1) <= 0x7e) || + (*(s + 1) >= 0x80 && *(s + 1) <= 0xfe)) + l = 2; + else + l = -1; + } + else + l = -1; + return l; +} + +static int +pg_utf8_verifier(const unsigned char *s, int len) +{ + int l = pg_utf_mblen(s); + + if (len < l) + return -1; + + if (!pg_utf8_islegal(s, l)) + return -1; + + return l; +} + +/* + * Check for validity of a single UTF-8 encoded character + * + * This directly implements the rules in RFC3629. The bizarre-looking + * restrictions on the second byte are meant to ensure that there isn't + * more than one encoding of a given Unicode character point; that is, + * you may not use a longer-than-necessary byte sequence with high order + * zero bits to represent a character that would fit in fewer bytes. + * To do otherwise is to create security hazards (eg, create an apparent + * non-ASCII character that decodes to plain ASCII). + * + * length is assumed to have been obtained by pg_utf_mblen(), and the + * caller must have checked that that many bytes are present in the buffer. + */ +bool +pg_utf8_islegal(const unsigned char *source, int length) +{ + unsigned char a; + + switch (length) + { + default: + /* reject lengths 5 and 6 for now */ + return false; + case 4: + a = source[3]; + if (a < 0x80 || a > 0xBF) + return false; + /* FALL THRU */ + case 3: + a = source[2]; + if (a < 0x80 || a > 0xBF) + return false; + /* FALL THRU */ + case 2: + a = source[1]; + switch (*source) + { + case 0xE0: + if (a < 0xA0 || a > 0xBF) + return false; + break; + case 0xED: + if (a < 0x80 || a > 0x9F) + return false; + break; + case 0xF0: + if (a < 0x90 || a > 0xBF) + return false; + break; + case 0xF4: + if (a < 0x80 || a > 0x8F) + return false; + break; + default: + if (a < 0x80 || a > 0xBF) + return false; + break; + } + /* FALL THRU */ + case 1: + a = *source; + if (a >= 0x80 && a < 0xC2) + return false; + if (a > 0xF4) + return false; + break; + } + return true; +} + +#ifndef FRONTEND + +/* + * Generic character incrementer function. + * + * Not knowing anything about the properties of the encoding in use, we just + * keep incrementing the last byte until we get a validly-encoded result, + * or we run out of values to try. We don't bother to try incrementing + * higher-order bytes, so there's no growth in runtime for wider characters. + * (If we did try to do that, we'd need to consider the likelihood that 255 + * is not a valid final byte in the encoding.) + */ +static bool +pg_generic_charinc(unsigned char *charptr, int len) +{ + unsigned char *lastbyte = charptr + len - 1; + mbverifier mbverify; + + /* We can just invoke the character verifier directly. */ + mbverify = pg_wchar_table[GetDatabaseEncoding()].mbverify; + + while (*lastbyte < (unsigned char) 255) + { + (*lastbyte)++; + if ((*mbverify) (charptr, len) == len) + return true; + } + + return false; +} + +/* + * UTF-8 character incrementer function. + * + * For a one-byte character less than 0x7F, we just increment the byte. + * + * For a multibyte character, every byte but the first must fall between 0x80 + * and 0xBF; and the first byte must be between 0xC0 and 0xF4. We increment + * the last byte that's not already at its maximum value. If we can't find a + * byte that's less than the maximum allowable value, we simply fail. We also + * need some special-case logic to skip regions used for surrogate pair + * handling, as those should not occur in valid UTF-8. + * + * Note that we don't reset lower-order bytes back to their minimums, since + * we can't afford to make an exhaustive search (see make_greater_string). + */ +static bool +pg_utf8_increment(unsigned char *charptr, int length) +{ + unsigned char a; + unsigned char limit; + + switch (length) + { + default: + /* reject lengths 5 and 6 for now */ + return false; + case 4: + a = charptr[3]; + if (a < 0xBF) + { + charptr[3]++; + break; + } + /* FALL THRU */ + case 3: + a = charptr[2]; + if (a < 0xBF) + { + charptr[2]++; + break; + } + /* FALL THRU */ + case 2: + a = charptr[1]; + switch (*charptr) + { + case 0xED: + limit = 0x9F; + break; + case 0xF4: + limit = 0x8F; + break; + default: + limit = 0xBF; + break; + } + if (a < limit) + { + charptr[1]++; + break; + } + /* FALL THRU */ + case 1: + a = *charptr; + if (a == 0x7F || a == 0xDF || a == 0xEF || a == 0xF4) + return false; + charptr[0]++; + break; + } + + return true; +} + +/* + * EUC-JP character incrementer function. + * + * If the sequence starts with SS2 (0x8e), it must be a two-byte sequence + * representing JIS X 0201 characters with the second byte ranging between + * 0xa1 and 0xdf. We just increment the last byte if it's less than 0xdf, + * and otherwise rewrite the whole sequence to 0xa1 0xa1. + * + * If the sequence starts with SS3 (0x8f), it must be a three-byte sequence + * in which the last two bytes range between 0xa1 and 0xfe. The last byte + * is incremented if possible, otherwise the second-to-last byte. + * + * If the sequence starts with a value other than the above and its MSB + * is set, it must be a two-byte sequence representing JIS X 0208 characters + * with both bytes ranging between 0xa1 and 0xfe. The last byte is + * incremented if possible, otherwise the second-to-last byte. + * + * Otherwise, the sequence is a single-byte ASCII character. It is + * incremented up to 0x7f. + */ +static bool +pg_eucjp_increment(unsigned char *charptr, int length) +{ + unsigned char c1, + c2; + int i; + + c1 = *charptr; + + switch (c1) + { + case SS2: /* JIS X 0201 */ + if (length != 2) + return false; + + c2 = charptr[1]; + + if (c2 >= 0xdf) + charptr[0] = charptr[1] = 0xa1; + else if (c2 < 0xa1) + charptr[1] = 0xa1; + else + charptr[1]++; + break; + + case SS3: /* JIS X 0212 */ + if (length != 3) + return false; + + for (i = 2; i > 0; i--) + { + c2 = charptr[i]; + if (c2 < 0xa1) + { + charptr[i] = 0xa1; + return true; + } + else if (c2 < 0xfe) + { + charptr[i]++; + return true; + } + } + + /* Out of 3-byte code region */ + return false; + + default: + if (IS_HIGHBIT_SET(c1)) /* JIS X 0208? */ + { + if (length != 2) + return false; + + for (i = 1; i >= 0; i--) + { + c2 = charptr[i]; + if (c2 < 0xa1) + { + charptr[i] = 0xa1; + return true; + } + else if (c2 < 0xfe) + { + charptr[i]++; + return true; + } + } + + /* Out of 2 byte code region */ + return false; + } + else + { /* ASCII, single byte */ + if (c1 > 0x7e) + return false; + (*charptr)++; + } + break; + } + + return true; +} +#endif /* !FRONTEND */ + + +/* + *------------------------------------------------------------------- + * encoding info table + * XXX must be sorted by the same order as enum pg_enc (in mb/pg_wchar.h) + *------------------------------------------------------------------- + */ +const pg_wchar_tbl pg_wchar_table[] = { + {pg_ascii2wchar_with_len, pg_wchar2single_with_len, pg_ascii_mblen, pg_ascii_dsplen, pg_ascii_verifier, 1}, /* PG_SQL_ASCII */ + {pg_eucjp2wchar_with_len, pg_wchar2euc_with_len, pg_eucjp_mblen, pg_eucjp_dsplen, pg_eucjp_verifier, 3}, /* PG_EUC_JP */ + {pg_euccn2wchar_with_len, pg_wchar2euc_with_len, pg_euccn_mblen, pg_euccn_dsplen, pg_euccn_verifier, 2}, /* PG_EUC_CN */ + {pg_euckr2wchar_with_len, pg_wchar2euc_with_len, pg_euckr_mblen, pg_euckr_dsplen, pg_euckr_verifier, 3}, /* PG_EUC_KR */ + {pg_euctw2wchar_with_len, pg_wchar2euc_with_len, pg_euctw_mblen, pg_euctw_dsplen, pg_euctw_verifier, 4}, /* PG_EUC_TW */ + {pg_eucjp2wchar_with_len, pg_wchar2euc_with_len, pg_eucjp_mblen, pg_eucjp_dsplen, pg_eucjp_verifier, 3}, /* PG_EUC_JIS_2004 */ + {pg_utf2wchar_with_len, pg_wchar2utf_with_len, pg_utf_mblen, pg_utf_dsplen, pg_utf8_verifier, 4}, /* PG_UTF8 */ + {pg_mule2wchar_with_len, pg_wchar2mule_with_len, pg_mule_mblen, pg_mule_dsplen, pg_mule_verifier, 4}, /* PG_MULE_INTERNAL */ + {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_LATIN1 */ + {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_LATIN2 */ + {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_LATIN3 */ + {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_LATIN4 */ + {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_LATIN5 */ + {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_LATIN6 */ + {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_LATIN7 */ + {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_LATIN8 */ + {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_LATIN9 */ + {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_LATIN10 */ + {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_WIN1256 */ + {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_WIN1258 */ + {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_WIN866 */ + {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_WIN874 */ + {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_KOI8R */ + {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_WIN1251 */ + {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_WIN1252 */ + {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* ISO-8859-5 */ + {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* ISO-8859-6 */ + {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* ISO-8859-7 */ + {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* ISO-8859-8 */ + {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_WIN1250 */ + {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_WIN1253 */ + {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_WIN1254 */ + {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_WIN1255 */ + {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_WIN1257 */ + {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_KOI8U */ + {0, 0, pg_sjis_mblen, pg_sjis_dsplen, pg_sjis_verifier, 2}, /* PG_SJIS */ + {0, 0, pg_big5_mblen, pg_big5_dsplen, pg_big5_verifier, 2}, /* PG_BIG5 */ + {0, 0, pg_gbk_mblen, pg_gbk_dsplen, pg_gbk_verifier, 2}, /* PG_GBK */ + {0, 0, pg_uhc_mblen, pg_uhc_dsplen, pg_uhc_verifier, 2}, /* PG_UHC */ + {0, 0, pg_gb18030_mblen, pg_gb18030_dsplen, pg_gb18030_verifier, 4}, /* PG_GB18030 */ + {0, 0, pg_johab_mblen, pg_johab_dsplen, pg_johab_verifier, 3}, /* PG_JOHAB */ + {0, 0, pg_sjis_mblen, pg_sjis_dsplen, pg_sjis_verifier, 2} /* PG_SHIFT_JIS_2004 */ +}; + +/* returns the byte length of a word for mule internal code */ +int +pg_mic_mblen(const unsigned char *mbstr) +{ + return pg_mule_mblen(mbstr); +} + +/* + * Returns the byte length of a multibyte character. + */ +int +pg_encoding_mblen(int encoding, const char *mbstr) +{ + return (PG_VALID_ENCODING(encoding) ? + ((*pg_wchar_table[encoding].mblen) ((const unsigned char *) mbstr)) : + ((*pg_wchar_table[PG_SQL_ASCII].mblen) ((const unsigned char *) mbstr))); +} + +/* + * Returns the display length of a multibyte character. + */ +int +pg_encoding_dsplen(int encoding, const char *mbstr) +{ + return (PG_VALID_ENCODING(encoding) ? + ((*pg_wchar_table[encoding].dsplen) ((const unsigned char *) mbstr)) : + ((*pg_wchar_table[PG_SQL_ASCII].dsplen) ((const unsigned char *) mbstr))); +} + +/* + * Verify the first multibyte character of the given string. + * Return its byte length if good, -1 if bad. (See comments above for + * full details of the mbverify API.) + */ +int +pg_encoding_verifymb(int encoding, const char *mbstr, int len) +{ + return (PG_VALID_ENCODING(encoding) ? + ((*pg_wchar_table[encoding].mbverify) ((const unsigned char *) mbstr, len)) : + ((*pg_wchar_table[PG_SQL_ASCII].mbverify) ((const unsigned char *) mbstr, len))); +} + +/* + * fetch maximum length of a given encoding + */ +int +pg_encoding_max_length(int encoding) +{ + Assert(PG_VALID_ENCODING(encoding)); + + return pg_wchar_table[encoding].maxmblen; +} + +#ifndef FRONTEND + +/* + * fetch maximum length of the encoding for the current database + */ +int +pg_database_encoding_max_length(void) +{ + return pg_wchar_table[GetDatabaseEncoding()].maxmblen; +} + +/* + * get the character incrementer for the encoding for the current database + */ +mbcharacter_incrementer +pg_database_encoding_character_incrementer(void) +{ + /* + * Eventually it might be best to add a field to pg_wchar_table[], but for + * now we just use a switch. + */ + switch (GetDatabaseEncoding()) + { + case PG_UTF8: + return pg_utf8_increment; + + case PG_EUC_JP: + return pg_eucjp_increment; + + default: + return pg_generic_charinc; + } +} + +/* + * Verify mbstr to make sure that it is validly encoded in the current + * database encoding. Otherwise same as pg_verify_mbstr(). + */ +bool +pg_verifymbstr(const char *mbstr, int len, bool noError) +{ + return + pg_verify_mbstr_len(GetDatabaseEncoding(), mbstr, len, noError) >= 0; +} + +/* + * Verify mbstr to make sure that it is validly encoded in the specified + * encoding. + */ +bool +pg_verify_mbstr(int encoding, const char *mbstr, int len, bool noError) +{ + return pg_verify_mbstr_len(encoding, mbstr, len, noError) >= 0; +} + +/* + * Verify mbstr to make sure that it is validly encoded in the specified + * encoding. + * + * mbstr is not necessarily zero terminated; length of mbstr is + * specified by len. + * + * If OK, return length of string in the encoding. + * If a problem is found, return -1 when noError is + * true; when noError is false, ereport() a descriptive message. + */ +int +pg_verify_mbstr_len(int encoding, const char *mbstr, int len, bool noError) +{ + mbverifier mbverify; + int mb_len; + + Assert(PG_VALID_ENCODING(encoding)); + + /* + * In single-byte encodings, we need only reject nulls (\0). + */ + if (pg_encoding_max_length(encoding) <= 1) + { + const char *nullpos = memchr(mbstr, 0, len); + + if (nullpos == NULL) + return len; + if (noError) + return -1; + report_invalid_encoding(encoding, nullpos, 1); + } + + /* fetch function pointer just once */ + mbverify = pg_wchar_table[encoding].mbverify; + + mb_len = 0; + + while (len > 0) + { + int l; + + /* fast path for ASCII-subset characters */ + if (!IS_HIGHBIT_SET(*mbstr)) + { + if (*mbstr != '\0') + { + mb_len++; + mbstr++; + len--; + continue; + } + if (noError) + return -1; + report_invalid_encoding(encoding, mbstr, len); + } + + l = (*mbverify) ((const unsigned char *) mbstr, len); + + if (l < 0) + { + if (noError) + return -1; + report_invalid_encoding(encoding, mbstr, len); + } + + mbstr += l; + len -= l; + mb_len++; + } + return mb_len; +} + +/* + * check_encoding_conversion_args: check arguments of a conversion function + * + * "expected" arguments can be either an encoding ID or -1 to indicate that + * the caller will check whether it accepts the ID. + * + * Note: the errors here are not really user-facing, so elog instead of + * ereport seems sufficient. Also, we trust that the "expected" encoding + * arguments are valid encoding IDs, but we don't trust the actuals. + */ +void +check_encoding_conversion_args(int src_encoding, + int dest_encoding, + int len, + int expected_src_encoding, + int expected_dest_encoding) +{ + if (!PG_VALID_ENCODING(src_encoding)) + elog(ERROR, "invalid source encoding ID: %d", src_encoding); + if (src_encoding != expected_src_encoding && expected_src_encoding >= 0) + elog(ERROR, "expected source encoding \"%s\", but got \"%s\"", + pg_enc2name_tbl[expected_src_encoding].name, + pg_enc2name_tbl[src_encoding].name); + if (!PG_VALID_ENCODING(dest_encoding)) + elog(ERROR, "invalid destination encoding ID: %d", dest_encoding); + if (dest_encoding != expected_dest_encoding && expected_dest_encoding >= 0) + elog(ERROR, "expected destination encoding \"%s\", but got \"%s\"", + pg_enc2name_tbl[expected_dest_encoding].name, + pg_enc2name_tbl[dest_encoding].name); + if (len < 0) + elog(ERROR, "encoding conversion length must not be negative"); +} + +/* + * report_invalid_encoding: complain about invalid multibyte character + * + * note: len is remaining length of string, not length of character; + * len must be greater than zero, as we always examine the first byte. + */ +void +report_invalid_encoding(int encoding, const char *mbstr, int len) +{ + int l = pg_encoding_mblen(encoding, mbstr); + char buf[8 * 5 + 1]; + char *p = buf; + int j, + jlimit; + + jlimit = Min(l, len); + jlimit = Min(jlimit, 8); /* prevent buffer overrun */ + + for (j = 0; j < jlimit; j++) + { + p += sprintf(p, "0x%02x", (unsigned char) mbstr[j]); + if (j < jlimit - 1) + p += sprintf(p, " "); + } + + ereport(ERROR, + (errcode(ERRCODE_CHARACTER_NOT_IN_REPERTOIRE), + errmsg("invalid byte sequence for encoding \"%s\": %s", + pg_enc2name_tbl[encoding].name, + buf))); +} + +/* + * report_untranslatable_char: complain about untranslatable character + * + * note: len is remaining length of string, not length of character; + * len must be greater than zero, as we always examine the first byte. + */ +void +report_untranslatable_char(int src_encoding, int dest_encoding, + const char *mbstr, int len) +{ + int l = pg_encoding_mblen(src_encoding, mbstr); + char buf[8 * 5 + 1]; + char *p = buf; + int j, + jlimit; + + jlimit = Min(l, len); + jlimit = Min(jlimit, 8); /* prevent buffer overrun */ + + for (j = 0; j < jlimit; j++) + { + p += sprintf(p, "0x%02x", (unsigned char) mbstr[j]); + if (j < jlimit - 1) + p += sprintf(p, " "); + } + + ereport(ERROR, + (errcode(ERRCODE_UNTRANSLATABLE_CHARACTER), + errmsg("character with byte sequence %s in encoding \"%s\" has no equivalent in encoding \"%s\"", + buf, + pg_enc2name_tbl[src_encoding].name, + pg_enc2name_tbl[dest_encoding].name))); +} + +#endif /* !FRONTEND */ diff --git a/libpq/win32/crypt.c b/libpq/win32/crypt.c new file mode 100644 index 0000000..6a902ef --- /dev/null +++ b/libpq/win32/crypt.c @@ -0,0 +1,1085 @@ +/* src/port/crypt.c */ +/* $NetBSD: crypt.c,v 1.18 2001/03/01 14:37:35 wiz Exp $ */ + +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Tom Truscott. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +#if 0 +static char sccsid[] = "@(#)crypt.c 8.1.1.1 (Berkeley) 8/18/93"; +#else +__RCSID("$NetBSD: crypt.c,v 1.18 2001/03/01 14:37:35 wiz Exp $"); +#endif +#endif /* not lint */ + +#include "c.h" + +#include + +#ifndef WIN32 +#include +#endif + +static int des_setkey(const char *key); +static int des_cipher(const char *in, char *out, long salt, int num_iter); + +/* + * UNIX password, and DES, encryption. + * By Tom Truscott, trt@rti.rti.org, + * from algorithms by Robert W. Baldwin and James Gillogly. + * + * References: + * "Mathematical Cryptology for Computer Scientists and Mathematicians," + * by Wayne Patterson, 1987, ISBN 0-8476-7438-X. + * + * "Password Security: A Case History," R. Morris and Ken Thompson, + * Communications of the ACM, vol. 22, pp. 594-597, Nov. 1979. + * + * "DES will be Totally Insecure within Ten Years," M.E. Hellman, + * IEEE Spectrum, vol. 16, pp. 32-39, July 1979. + */ + +/* ===== Configuration ==================== */ + +/* + * define "MUST_ALIGN" if your compiler cannot load/store + * long integers at arbitrary (e.g. odd) memory locations. + * (Either that or never pass unaligned addresses to des_cipher!) + */ +/* #define MUST_ALIGN */ + +#ifdef CHAR_BITS +#if CHAR_BITS != 8 +#error C_block structure assumes 8 bit characters +#endif +#endif + +/* + * define "B64" to be the declaration for a 64 bit integer. + * XXX this feature is currently unused, see "endian" comment below. + */ +/* #define B64 int64 */ + +/* + * define "LARGEDATA" to get faster permutations, by using about 72 kilobytes + * of lookup tables. This speeds up des_setkey() and des_cipher(), but has + * little effect on crypt(). + */ +/* #define LARGEDATA */ + +/* compile with "-DSTATIC=void" when profiling */ +#ifndef STATIC +#define STATIC static void +#endif + +/* + * Define the "int32_t" type for integral type with a width of at least + * 32 bits. + */ +typedef int int32_t; + +/* ==================================== */ + +#define _PASSWORD_EFMT1 '_' /* extended encryption format */ + +/* + * Cipher-block representation (Bob Baldwin): + * + * DES operates on groups of 64 bits, numbered 1..64 (sigh). One + * representation is to store one bit per byte in an array of bytes. Bit N of + * the NBS spec is stored as the LSB of the Nth byte (index N-1) in the array. + * Another representation stores the 64 bits in 8 bytes, with bits 1..8 in the + * first byte, 9..16 in the second, and so on. The DES spec apparently has + * bit 1 in the MSB of the first byte, but that is particularly noxious so we + * bit-reverse each byte so that bit 1 is the LSB of the first byte, bit 8 is + * the MSB of the first byte. Specifically, the 64-bit input data and key are + * converted to LSB format, and the output 64-bit block is converted back into + * MSB format. + * + * DES operates internally on groups of 32 bits which are expanded to 48 bits + * by permutation E and shrunk back to 32 bits by the S boxes. To speed up + * the computation, the expansion is applied only once, the expanded + * representation is maintained during the encryption, and a compression + * permutation is applied only at the end. To speed up the S-box lookups, + * the 48 bits are maintained as eight 6 bit groups, one per byte, which + * directly feed the eight S-boxes. Within each byte, the 6 bits are the + * most significant ones. The low two bits of each byte are zero. (Thus, + * bit 1 of the 48 bit E expansion is stored as the "4"-valued bit of the + * first byte in the eight byte representation, bit 2 of the 48 bit value is + * the "8"-valued bit, and so on.) In fact, a combined "SPE"-box lookup is + * used, in which the output is the 64 bit result of an S-box lookup which + * has been permuted by P and expanded by E, and is ready for use in the next + * iteration. Two 32-bit wide tables, SPE[0] and SPE[1], are used for this + * lookup. Since each byte in the 48 bit path is a multiple of four, indexed + * lookup of SPE[0] and SPE[1] is simple and fast. The key schedule and + * "salt" are also converted to this 8*(6+2) format. The SPE table size is + * 8*64*8 = 4K bytes. + * + * To speed up bit-parallel operations (such as XOR), the 8 byte + * representation is "union"ed with 32 bit values "i0" and "i1", and, on + * machines which support it, a 64 bit value "b64". This data structure, + * "C_block", has two problems. First, alignment restrictions must be + * honored. Second, the byte-order (e.g. little-endian or big-endian) of + * the architecture becomes visible. + * + * The byte-order problem is unfortunate, since on the one hand it is good + * to have a machine-independent C_block representation (bits 1..8 in the + * first byte, etc.), and on the other hand it is good for the LSB of the + * first byte to be the LSB of i0. We cannot have both these things, so we + * currently use the "little-endian" representation and avoid any multi-byte + * operations that depend on byte order. This largely precludes use of the + * 64-bit datatype since the relative order of i0 and i1 are unknown. It + * also inhibits grouping the SPE table to look up 12 bits at a time. (The + * 12 bits can be stored in a 16-bit field with 3 low-order zeroes and 1 + * high-order zero, providing fast indexing into a 64-bit wide SPE.) On the + * other hand, 64-bit datatypes are currently rare, and a 12-bit SPE lookup + * requires a 128 kilobyte table, so perhaps this is not a big loss. + * + * Permutation representation (Jim Gillogly): + * + * A transformation is defined by its effect on each of the 8 bytes of the + * 64-bit input. For each byte we give a 64-bit output that has the bits in + * the input distributed appropriately. The transformation is then the OR + * of the 8 sets of 64-bits. This uses 8*256*8 = 16K bytes of storage for + * each transformation. Unless LARGEDATA is defined, however, a more compact + * table is used which looks up 16 4-bit "chunks" rather than 8 8-bit chunks. + * The smaller table uses 16*16*8 = 2K bytes for each transformation. This + * is slower but tolerable, particularly for password encryption in which + * the SPE transformation is iterated many times. The small tables total 9K + * bytes, the large tables total 72K bytes. + * + * The transformations used are: + * IE3264: MSB->LSB conversion, initial permutation, and expansion. + * This is done by collecting the 32 even-numbered bits and applying + * a 32->64 bit transformation, and then collecting the 32 odd-numbered + * bits and applying the same transformation. Since there are only + * 32 input bits, the IE3264 transformation table is half the size of + * the usual table. + * CF6464: Compression, final permutation, and LSB->MSB conversion. + * This is done by two trivial 48->32 bit compressions to obtain + * a 64-bit block (the bit numbering is given in the "CIFP" table) + * followed by a 64->64 bit "cleanup" transformation. (It would + * be possible to group the bits in the 64-bit block so that 2 + * identical 32->32 bit transformations could be used instead, + * saving a factor of 4 in space and possibly 2 in time, but + * byte-ordering and other complications rear their ugly head. + * Similar opportunities/problems arise in the key schedule + * transforms.) + * PC1ROT: MSB->LSB, PC1 permutation, rotate, and PC2 permutation. + * This admittedly baroque 64->64 bit transformation is used to + * produce the first code (in 8*(6+2) format) of the key schedule. + * PC2ROT[0]: Inverse PC2 permutation, rotate, and PC2 permutation. + * It would be possible to define 15 more transformations, each + * with a different rotation, to generate the entire key schedule. + * To save space, however, we instead permute each code into the + * next by using a transformation that "undoes" the PC2 permutation, + * rotates the code, and then applies PC2. Unfortunately, PC2 + * transforms 56 bits into 48 bits, dropping 8 bits, so PC2 is not + * invertible. We get around that problem by using a modified PC2 + * which retains the 8 otherwise-lost bits in the unused low-order + * bits of each byte. The low-order bits are cleared when the + * codes are stored into the key schedule. + * PC2ROT[1]: Same as PC2ROT[0], but with two rotations. + * This is faster than applying PC2ROT[0] twice, + * + * The Bell Labs "salt" (Bob Baldwin): + * + * The salting is a simple permutation applied to the 48-bit result of E. + * Specifically, if bit i (1 <= i <= 24) of the salt is set then bits i and + * i+24 of the result are swapped. The salt is thus a 24 bit number, with + * 16777216 possible values. (The original salt was 12 bits and could not + * swap bits 13..24 with 36..48.) + * + * It is possible, but ugly, to warp the SPE table to account for the salt + * permutation. Fortunately, the conditional bit swapping requires only + * about four machine instructions and can be done on-the-fly with about an + * 8% performance penalty. + */ + +typedef union +{ + unsigned char b[8]; + struct + { + int32_t i0; + int32_t i1; + } b32; +#if defined(B64) + B64 b64; +#endif +} C_block; + +/* + * Convert twenty-four-bit long in host-order + * to six bits (and 2 low-order zeroes) per char little-endian format. + */ +#define TO_SIX_BIT(rslt, src) { \ + C_block cvt; \ + cvt.b[0] = src; src >>= 6; \ + cvt.b[1] = src; src >>= 6; \ + cvt.b[2] = src; src >>= 6; \ + cvt.b[3] = src; \ + rslt = (cvt.b32.i0 & 0x3f3f3f3fL) << 2; \ + } + +/* + * These macros may someday permit efficient use of 64-bit integers. + */ +#define ZERO(d,d0,d1) d0 = 0, d1 = 0 +#define LOAD(d,d0,d1,bl) d0 = (bl).b32.i0, d1 = (bl).b32.i1 +#define LOADREG(d,d0,d1,s,s0,s1) d0 = s0, d1 = s1 +#define OR(d,d0,d1,bl) d0 |= (bl).b32.i0, d1 |= (bl).b32.i1 +#define STORE(s,s0,s1,bl) (bl).b32.i0 = s0, (bl).b32.i1 = s1 +#define DCL_BLOCK(d,d0,d1) int32_t d0, d1 + +#if defined(LARGEDATA) + /* Waste memory like crazy. Also, do permutations in line */ +#define LGCHUNKBITS 3 +#define CHUNKBITS (1<> 4]; + OR(D, D0, D1, *tp); + p += (1 << CHUNKBITS); + } while (--chars_in > 0); + STORE(D, D0, D1, *out); +} +#endif /* LARGEDATA */ + + +/* ===== (mostly) Standard DES Tables ==================== */ + +static const unsigned char IP[] = { /* initial permutation */ + 58, 50, 42, 34, 26, 18, 10, 2, + 60, 52, 44, 36, 28, 20, 12, 4, + 62, 54, 46, 38, 30, 22, 14, 6, + 64, 56, 48, 40, 32, 24, 16, 8, + 57, 49, 41, 33, 25, 17, 9, 1, + 59, 51, 43, 35, 27, 19, 11, 3, + 61, 53, 45, 37, 29, 21, 13, 5, + 63, 55, 47, 39, 31, 23, 15, 7, +}; + +/* The final permutation is the inverse of IP - no table is necessary */ + +static const unsigned char ExpandTr[] = { /* expansion operation */ + 32, 1, 2, 3, 4, 5, + 4, 5, 6, 7, 8, 9, + 8, 9, 10, 11, 12, 13, + 12, 13, 14, 15, 16, 17, + 16, 17, 18, 19, 20, 21, + 20, 21, 22, 23, 24, 25, + 24, 25, 26, 27, 28, 29, + 28, 29, 30, 31, 32, 1, +}; + +static const unsigned char PC1[] = { /* permuted choice table 1 */ + 57, 49, 41, 33, 25, 17, 9, + 1, 58, 50, 42, 34, 26, 18, + 10, 2, 59, 51, 43, 35, 27, + 19, 11, 3, 60, 52, 44, 36, + + 63, 55, 47, 39, 31, 23, 15, + 7, 62, 54, 46, 38, 30, 22, + 14, 6, 61, 53, 45, 37, 29, + 21, 13, 5, 28, 20, 12, 4, +}; + +static const unsigned char Rotates[] = { /* PC1 rotation schedule */ + 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1, +}; + +/* note: each "row" of PC2 is left-padded with bits that make it invertible */ +static const unsigned char PC2[] = { /* permuted choice table 2 */ + 9, 18, 14, 17, 11, 24, 1, 5, + 22, 25, 3, 28, 15, 6, 21, 10, + 35, 38, 23, 19, 12, 4, 26, 8, + 43, 54, 16, 7, 27, 20, 13, 2, + + 0, 0, 41, 52, 31, 37, 47, 55, + 0, 0, 30, 40, 51, 45, 33, 48, + 0, 0, 44, 49, 39, 56, 34, 53, + 0, 0, 46, 42, 50, 36, 29, 32, +}; + +static const unsigned char S[8][64] = { /* 48->32 bit substitution tables */ + /* S[1] */ + {14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7, + 0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8, + 4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0, + 15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13}, + /* S[2] */ + {15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10, + 3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5, + 0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15, + 13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9}, + /* S[3] */ + {10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8, + 13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1, + 13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7, + 1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12}, + /* S[4] */ + {7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15, + 13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9, + 10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4, + 3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14}, + /* S[5] */ + {2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9, + 14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6, + 4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14, + 11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3}, + /* S[6] */ + {12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11, + 10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8, + 9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6, + 4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13}, + /* S[7] */ + {4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1, + 13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6, + 1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2, + 6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12}, + /* S[8] */ + {13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7, + 1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2, + 7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8, + 2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11} +}; + +static const unsigned char P32Tr[] = { /* 32-bit permutation function */ + 16, 7, 20, 21, + 29, 12, 28, 17, + 1, 15, 23, 26, + 5, 18, 31, 10, + 2, 8, 24, 14, + 32, 27, 3, 9, + 19, 13, 30, 6, + 22, 11, 4, 25, +}; + +static const unsigned char CIFP[] = { /* compressed/interleaved permutation */ + 1, 2, 3, 4, 17, 18, 19, 20, + 5, 6, 7, 8, 21, 22, 23, 24, + 9, 10, 11, 12, 25, 26, 27, 28, + 13, 14, 15, 16, 29, 30, 31, 32, + + 33, 34, 35, 36, 49, 50, 51, 52, + 37, 38, 39, 40, 53, 54, 55, 56, + 41, 42, 43, 44, 57, 58, 59, 60, + 45, 46, 47, 48, 61, 62, 63, 64, +}; + +static const unsigned char itoa64[] = /* 0..63 => ascii-64 */ +"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + + +/* ===== Tables that are initialized at run time ==================== */ + + +static unsigned char a64toi[128]; /* ascii-64 => 0..63 */ + +/* Initial key schedule permutation */ +static C_block PC1ROT[64 / CHUNKBITS][1 << CHUNKBITS]; + +/* Subsequent key schedule rotation permutations */ +static C_block PC2ROT[2][64 / CHUNKBITS][1 << CHUNKBITS]; + +/* Initial permutation/expansion table */ +static C_block IE3264[32 / CHUNKBITS][1 << CHUNKBITS]; + +/* Table that combines the S, P, and E operations. */ +static int32_t SPE[2][8][64]; + +/* compressed/interleaved => final permutation table */ +static C_block CF6464[64 / CHUNKBITS][1 << CHUNKBITS]; + + +/* ==================================== */ + + +static C_block constdatablock; /* encryption constant */ +static char cryptresult[1 + 4 + 4 + 11 + 1]; /* encrypted result */ + +extern char *__md5crypt(const char *, const char *); /* XXX */ +extern char *__bcrypt(const char *, const char *); /* XXX */ + + +/* + * Return a pointer to static data consisting of the "setting" + * followed by an encryption produced by the "key" and "setting". + */ +char * +crypt(key, setting) +const char *key; +const char *setting; +{ + char *encp; + int32_t i; + int t; + int32_t salt; + int num_iter, + salt_size; + C_block keyblock, + rsltblock; + +#if 0 + /* Non-DES encryption schemes hook in here. */ + if (setting[0] == _PASSWORD_NONDES) + { + switch (setting[1]) + { + case '2': + return (__bcrypt(key, setting)); + case '1': + default: + return (__md5crypt(key, setting)); + } + } +#endif + + for (i = 0; i < 8; i++) + { + if ((t = 2 * (unsigned char) (*key)) != 0) + key++; + keyblock.b[i] = t; + } + if (des_setkey((char *) keyblock.b)) /* also initializes "a64toi" */ + return (NULL); + + encp = &cryptresult[0]; + switch (*setting) + { + case _PASSWORD_EFMT1: + + /* + * Involve the rest of the password 8 characters at a time. + */ + while (*key) + { + if (des_cipher((char *) (void *) &keyblock, + (char *) (void *) &keyblock, 0L, 1)) + return (NULL); + for (i = 0; i < 8; i++) + { + if ((t = 2 * (unsigned char) (*key)) != 0) + key++; + keyblock.b[i] ^= t; + } + if (des_setkey((char *) keyblock.b)) + return (NULL); + } + + *encp++ = *setting++; + + /* get iteration count */ + num_iter = 0; + for (i = 4; --i >= 0;) + { + if ((t = (unsigned char) setting[i]) == '\0') + t = '.'; + encp[i] = t; + num_iter = (num_iter << 6) | a64toi[t]; + } + setting += 4; + encp += 4; + salt_size = 4; + break; + default: + num_iter = 25; + salt_size = 2; + } + + salt = 0; + for (i = salt_size; --i >= 0;) + { + if ((t = (unsigned char) setting[i]) == '\0') + t = '.'; + encp[i] = t; + salt = (salt << 6) | a64toi[t]; + } + encp += salt_size; + if (des_cipher((char *) (void *) &constdatablock, + (char *) (void *) &rsltblock, salt, num_iter)) + return (NULL); + + /* + * Encode the 64 cipher bits as 11 ascii characters. + */ + i = ((int32_t) ((rsltblock.b[0] << 8) | rsltblock.b[1]) << 8) | + rsltblock.b[2]; + encp[3] = itoa64[i & 0x3f]; + i >>= 6; + encp[2] = itoa64[i & 0x3f]; + i >>= 6; + encp[1] = itoa64[i & 0x3f]; + i >>= 6; + encp[0] = itoa64[i]; + encp += 4; + i = ((int32_t) ((rsltblock.b[3] << 8) | rsltblock.b[4]) << 8) | + rsltblock.b[5]; + encp[3] = itoa64[i & 0x3f]; + i >>= 6; + encp[2] = itoa64[i & 0x3f]; + i >>= 6; + encp[1] = itoa64[i & 0x3f]; + i >>= 6; + encp[0] = itoa64[i]; + encp += 4; + i = ((int32_t) ((rsltblock.b[6]) << 8) | rsltblock.b[7]) << 2; + encp[2] = itoa64[i & 0x3f]; + i >>= 6; + encp[1] = itoa64[i & 0x3f]; + i >>= 6; + encp[0] = itoa64[i]; + + encp[3] = 0; + + return (cryptresult); +} + + +/* + * The Key Schedule, filled in by des_setkey() or setkey(). + */ +#define KS_SIZE 16 +static C_block KS[KS_SIZE]; + +static volatile int des_ready = 0; + +/* + * Set up the key schedule from the key. + */ +static int +des_setkey(key) +const char *key; +{ + DCL_BLOCK(K, K0, K1); + C_block *ptabp; + int i; + + if (!des_ready) + init_des(); + + PERM6464(K, K0, K1, (unsigned char *) key, (C_block *) PC1ROT); + key = (char *) &KS[0]; + STORE(K & ~0x03030303L, K0 & ~0x03030303L, K1, *(C_block *) key); + for (i = 1; i < 16; i++) + { + key += sizeof(C_block); + STORE(K, K0, K1, *(C_block *) key); + ptabp = (C_block *) PC2ROT[Rotates[i] - 1]; + PERM6464(K, K0, K1, (unsigned char *) key, ptabp); + STORE(K & ~0x03030303L, K0 & ~0x03030303L, K1, *(C_block *) key); + } + return (0); +} + +/* + * Encrypt (or decrypt if num_iter < 0) the 8 chars at "in" with abs(num_iter) + * iterations of DES, using the given 24-bit salt and the pre-computed key + * schedule, and store the resulting 8 chars at "out" (in == out is permitted). + * + * NOTE: the performance of this routine is critically dependent on your + * compiler and machine architecture. + */ +static int +des_cipher(in, out, salt, num_iter) +const char *in; +char *out; +long salt; +int num_iter; +{ + /* variables that we want in registers, most important first */ +#if defined(pdp11) + int j; +#endif + int32_t L0, + L1, + R0, + R1, + k; + C_block *kp; + int ks_inc, + loop_count; + C_block B; + + L0 = salt; + TO_SIX_BIT(salt, L0); /* convert to 4*(6+2) format */ + +#if defined(__vax__) || defined(pdp11) + salt = ~salt; /* "x &~ y" is faster than "x & y". */ +#define SALT (~salt) +#else +#define SALT salt +#endif + +#if defined(MUST_ALIGN) + B.b[0] = in[0]; + B.b[1] = in[1]; + B.b[2] = in[2]; + B.b[3] = in[3]; + B.b[4] = in[4]; + B.b[5] = in[5]; + B.b[6] = in[6]; + B.b[7] = in[7]; + LOAD(L, L0, L1, B); +#else + LOAD(L, L0, L1, *(C_block *) in); +#endif + LOADREG(R, R0, R1, L, L0, L1); + L0 &= 0x55555555L; + L1 &= 0x55555555L; + L0 = (L0 << 1) | L1; /* L0 is the even-numbered input bits */ + R0 &= 0xaaaaaaaaL; + R1 = (R1 >> 1) & 0x55555555L; + L1 = R0 | R1; /* L1 is the odd-numbered input bits */ + STORE(L, L0, L1, B); + PERM3264(L, L0, L1, B.b, (C_block *) IE3264); /* even bits */ + PERM3264(R, R0, R1, B.b + 4, (C_block *) IE3264); /* odd bits */ + + if (num_iter >= 0) + { /* encryption */ + kp = &KS[0]; + ks_inc = sizeof(*kp); + } + else + { /* decryption */ + num_iter = -num_iter; + kp = &KS[KS_SIZE - 1]; + ks_inc = -(long) sizeof(*kp); + } + + while (--num_iter >= 0) + { + loop_count = 8; + do + { + +#define SPTAB(t, i) \ + (*(int32_t *)((unsigned char *)(t) + (i)*(sizeof(int32_t)/4))) +#if defined(gould) + /* use this if B.b[i] is evaluated just once ... */ +#define DOXOR(x,y,i) x^=SPTAB(SPE[0][i],B.b[i]); y^=SPTAB(SPE[1][i],B.b[i]); +#else +#if defined(pdp11) + /* use this if your "long" int indexing is slow */ +#define DOXOR(x,y,i) j=B.b[i]; x^=SPTAB(SPE[0][i],j); y^=SPTAB(SPE[1][i],j); +#else + /* use this if "k" is allocated to a register ... */ +#define DOXOR(x,y,i) k=B.b[i]; x^=SPTAB(SPE[0][i],k); y^=SPTAB(SPE[1][i],k); +#endif +#endif + +#define CRUNCH(p0, p1, q0, q1) \ + k = ((q0) ^ (q1)) & SALT; \ + B.b32.i0 = k ^ (q0) ^ kp->b32.i0; \ + B.b32.i1 = k ^ (q1) ^ kp->b32.i1; \ + kp = (C_block *)((char *)kp+ks_inc); \ + \ + DOXOR(p0, p1, 0); \ + DOXOR(p0, p1, 1); \ + DOXOR(p0, p1, 2); \ + DOXOR(p0, p1, 3); \ + DOXOR(p0, p1, 4); \ + DOXOR(p0, p1, 5); \ + DOXOR(p0, p1, 6); \ + DOXOR(p0, p1, 7); + + CRUNCH(L0, L1, R0, R1); + CRUNCH(R0, R1, L0, L1); + } while (--loop_count != 0); + kp = (C_block *) ((char *) kp - (ks_inc * KS_SIZE)); + + + /* swap L and R */ + L0 ^= R0; + L1 ^= R1; + R0 ^= L0; + R1 ^= L1; + L0 ^= R0; + L1 ^= R1; + } + + /* store the encrypted (or decrypted) result */ + L0 = ((L0 >> 3) & 0x0f0f0f0fL) | ((L1 << 1) & 0xf0f0f0f0L); + L1 = ((R0 >> 3) & 0x0f0f0f0fL) | ((R1 << 1) & 0xf0f0f0f0L); + STORE(L, L0, L1, B); + PERM6464(L, L0, L1, B.b, (C_block *) CF6464); +#if defined(MUST_ALIGN) + STORE(L, L0, L1, B); + out[0] = B.b[0]; + out[1] = B.b[1]; + out[2] = B.b[2]; + out[3] = B.b[3]; + out[4] = B.b[4]; + out[5] = B.b[5]; + out[6] = B.b[6]; + out[7] = B.b[7]; +#else + STORE(L, L0, L1, *(C_block *) out); +#endif + return (0); +} + + +/* + * Initialize various tables. This need only be done once. It could even be + * done at compile time, if the compiler were capable of that sort of thing. + */ +STATIC +init_des() +{ + int i, + j; + int32_t k; + int tableno; + static unsigned char perm[64], + tmp32[32]; /* "static" for speed */ + +/* static volatile long init_start = 0; not used */ + + /* + * table that converts chars "./0-9A-Za-z"to integers 0-63. + */ + for (i = 0; i < 64; i++) + a64toi[itoa64[i]] = i; + + /* + * PC1ROT - bit reverse, then PC1, then Rotate, then PC2. + */ + for (i = 0; i < 64; i++) + perm[i] = 0; + for (i = 0; i < 64; i++) + { + if ((k = PC2[i]) == 0) + continue; + k += Rotates[0] - 1; + if ((k % 28) < Rotates[0]) + k -= 28; + k = PC1[k]; + if (k > 0) + { + k--; + k = (k | 07) - (k & 07); + k++; + } + perm[i] = k; + } +#ifdef DEBUG + prtab("pc1tab", perm, 8); +#endif + init_perm(PC1ROT, perm, 8, 8); + + /* + * PC2ROT - PC2 inverse, then Rotate (once or twice), then PC2. + */ + for (j = 0; j < 2; j++) + { + unsigned char pc2inv[64]; + + for (i = 0; i < 64; i++) + perm[i] = pc2inv[i] = 0; + for (i = 0; i < 64; i++) + { + if ((k = PC2[i]) == 0) + continue; + pc2inv[k - 1] = i + 1; + } + for (i = 0; i < 64; i++) + { + if ((k = PC2[i]) == 0) + continue; + k += j; + if ((k % 28) <= j) + k -= 28; + perm[i] = pc2inv[k]; + } +#ifdef DEBUG + prtab("pc2tab", perm, 8); +#endif + init_perm(PC2ROT[j], perm, 8, 8); + } + + /* + * Bit reverse, then initial permutation, then expansion. + */ + for (i = 0; i < 8; i++) + { + for (j = 0; j < 8; j++) + { + k = (j < 2) ? 0 : IP[ExpandTr[i * 6 + j - 2] - 1]; + if (k > 32) + k -= 32; + else if (k > 0) + k--; + if (k > 0) + { + k--; + k = (k | 07) - (k & 07); + k++; + } + perm[i * 8 + j] = k; + } + } +#ifdef DEBUG + prtab("ietab", perm, 8); +#endif + init_perm(IE3264, perm, 4, 8); + + /* + * Compression, then final permutation, then bit reverse. + */ + for (i = 0; i < 64; i++) + { + k = IP[CIFP[i] - 1]; + if (k > 0) + { + k--; + k = (k | 07) - (k & 07); + k++; + } + perm[k - 1] = i + 1; + } +#ifdef DEBUG + prtab("cftab", perm, 8); +#endif + init_perm(CF6464, perm, 8, 8); + + /* + * SPE table + */ + for (i = 0; i < 48; i++) + perm[i] = P32Tr[ExpandTr[i] - 1]; + for (tableno = 0; tableno < 8; tableno++) + { + for (j = 0; j < 64; j++) + { + k = (((j >> 0) & 01) << 5) | + (((j >> 1) & 01) << 3) | + (((j >> 2) & 01) << 2) | + (((j >> 3) & 01) << 1) | + (((j >> 4) & 01) << 0) | + (((j >> 5) & 01) << 4); + k = S[tableno][k]; + k = (((k >> 3) & 01) << 0) | + (((k >> 2) & 01) << 1) | + (((k >> 1) & 01) << 2) | + (((k >> 0) & 01) << 3); + for (i = 0; i < 32; i++) + tmp32[i] = 0; + for (i = 0; i < 4; i++) + tmp32[4 * tableno + i] = (k >> i) & 01; + k = 0; + for (i = 24; --i >= 0;) + k = (k << 1) | tmp32[perm[i] - 1]; + TO_SIX_BIT(SPE[0][tableno][j], k); + k = 0; + for (i = 24; --i >= 0;) + k = (k << 1) | tmp32[perm[i + 24] - 1]; + TO_SIX_BIT(SPE[1][tableno][j], k); + } + } + + des_ready = 1; +} + +/* + * Initialize "perm" to represent transformation "p", which rearranges + * (perhaps with expansion and/or contraction) one packed array of bits + * (of size "chars_in" characters) into another array (of size "chars_out" + * characters). + * + * "perm" must be all-zeroes on entry to this routine. + */ +STATIC +init_perm(perm, p, chars_in, chars_out) +C_block perm[64 / CHUNKBITS][1 << CHUNKBITS]; +unsigned char p[64]; +int chars_in, + chars_out; +{ + int i, + j, + k, + l; + + for (k = 0; k < chars_out * 8; k++) + { /* each output bit position */ + l = p[k] - 1; /* where this bit comes from */ + if (l < 0) + continue; /* output bit is always 0 */ + i = l >> LGCHUNKBITS; /* which chunk this bit comes from */ + l = 1 << (l & (CHUNKBITS - 1)); /* mask for this bit */ + for (j = 0; j < (1 << CHUNKBITS); j++) + { /* each chunk value */ + if ((j & l) != 0) + perm[i][j].b[k >> 3] |= 1 << (k & 07); + } + } +} + +/* + * "setkey" routine (for backwards compatibility) + */ +#ifdef NOT_USED +int +setkey(key) +const char *key; +{ + int i, + j, + k; + C_block keyblock; + + for (i = 0; i < 8; i++) + { + k = 0; + for (j = 0; j < 8; j++) + { + k <<= 1; + k |= (unsigned char) *key++; + } + keyblock.b[i] = k; + } + return (des_setkey((char *) keyblock.b)); +} + +/* + * "encrypt" routine (for backwards compatibility) + */ +static int +encrypt(block, flag) +char *block; +int flag; +{ + int i, + j, + k; + C_block cblock; + + for (i = 0; i < 8; i++) + { + k = 0; + for (j = 0; j < 8; j++) + { + k <<= 1; + k |= (unsigned char) *block++; + } + cblock.b[i] = k; + } + if (des_cipher((char *) &cblock, (char *) &cblock, 0L, (flag ? -1 : 1))) + return (1); + for (i = 7; i >= 0; i--) + { + k = cblock.b[i]; + for (j = 7; j >= 0; j--) + { + *--block = k & 01; + k >>= 1; + } + } + return (0); +} +#endif + +#ifdef DEBUG +STATIC +prtab(s, t, num_rows) +char *s; +unsigned char *t; +int num_rows; +{ + int i, + j; + + (void) printf("%s:\n", s); + for (i = 0; i < num_rows; i++) + { + for (j = 0; j < 8; j++) + (void) printf("%3d", t[i * 8 + j]); + (void) printf("\n"); + } + (void) printf("\n"); +} + +#endif diff --git a/libpq/win32/getaddrinfo.c b/libpq/win32/getaddrinfo.c new file mode 100644 index 0000000..90c1b87 --- /dev/null +++ b/libpq/win32/getaddrinfo.c @@ -0,0 +1,412 @@ +/*------------------------------------------------------------------------- + * + * getaddrinfo.c + * Support getaddrinfo() on platforms that don't have it. + * + * We also supply getnameinfo() here, assuming that the platform will have + * it if and only if it has getaddrinfo(). If this proves false on some + * platform, we'll need to split this file and provide a separate configure + * test for getnameinfo(). + * + * Windows may or may not have these routines, so we handle Windows specially + * by dynamically checking for their existence. If they already exist, we + * use the Windows native routines, but if not, we use our own. + * + * + * Copyright (c) 2003-2016, PostgreSQL Global Development Group + * + * IDENTIFICATION + * src/port/getaddrinfo.c + * + *------------------------------------------------------------------------- + */ + +/* This is intended to be used in both frontend and backend, so use c.h */ +#include "c.h" + +#include +#include +#include +#include + +#include "getaddrinfo.h" +#include "libpq/pqcomm.h" /* needed for struct sockaddr_storage */ + + +#ifdef WIN32 +/* + * The native routines may or may not exist on the Windows platform we are on, + * so we dynamically look up the routines, and call them via function pointers. + * Here we need to declare what the function pointers look like + */ +typedef int (__stdcall * getaddrinfo_ptr_t) (const char *nodename, + const char *servname, + const struct addrinfo * hints, + struct addrinfo ** res); + +typedef void (__stdcall * freeaddrinfo_ptr_t) (struct addrinfo * ai); + +typedef int (__stdcall * getnameinfo_ptr_t) (const struct sockaddr * sa, + int salen, + char *host, int hostlen, + char *serv, int servlen, + int flags); + +/* static pointers to the native routines, so we only do the lookup once. */ +static getaddrinfo_ptr_t getaddrinfo_ptr = NULL; +static freeaddrinfo_ptr_t freeaddrinfo_ptr = NULL; +static getnameinfo_ptr_t getnameinfo_ptr = NULL; + + +static bool +haveNativeWindowsIPv6routines(void) +{ + void *hLibrary = NULL; + static bool alreadyLookedForIpv6routines = false; + + if (alreadyLookedForIpv6routines) + return (getaddrinfo_ptr != NULL); + + /* + * For Windows XP and Windows 2003 (and longhorn/vista), the IPv6 routines + * are present in the WinSock 2 library (ws2_32.dll). Try that first + */ + + hLibrary = LoadLibraryA("ws2_32"); + + if (hLibrary == NULL || GetProcAddress(hLibrary, "getaddrinfo") == NULL) + { + /* + * Well, ws2_32 doesn't exist, or more likely doesn't have + * getaddrinfo. + */ + if (hLibrary != NULL) + FreeLibrary(hLibrary); + + /* + * In Windows 2000, there was only the IPv6 Technology Preview look in + * the IPv6 WinSock library (wship6.dll). + */ + + hLibrary = LoadLibraryA("wship6"); + } + + /* If hLibrary is null, we couldn't find a dll with functions */ + if (hLibrary != NULL) + { + /* We found a dll, so now get the addresses of the routines */ + + getaddrinfo_ptr = (getaddrinfo_ptr_t) GetProcAddress(hLibrary, + "getaddrinfo"); + freeaddrinfo_ptr = (freeaddrinfo_ptr_t) GetProcAddress(hLibrary, + "freeaddrinfo"); + getnameinfo_ptr = (getnameinfo_ptr_t) GetProcAddress(hLibrary, + "getnameinfo"); + + /* + * If any one of the routines is missing, let's play it safe and + * ignore them all + */ + if (getaddrinfo_ptr == NULL || + freeaddrinfo_ptr == NULL || + getnameinfo_ptr == NULL) + { + FreeLibrary(hLibrary); + hLibrary = NULL; + getaddrinfo_ptr = NULL; + freeaddrinfo_ptr = NULL; + getnameinfo_ptr = NULL; + } + } + + alreadyLookedForIpv6routines = true; + return (getaddrinfo_ptr != NULL); +} +#endif + + +/* + * get address info for ipv4 sockets. + * + * Bugs: - only one addrinfo is set even though hintp is NULL or + * ai_socktype is 0 + * - AI_CANONNAME is not supported. + * - servname can only be a number, not text. + */ +int +getaddrinfo(const char *node, const char *service, + const struct addrinfo * hintp, + struct addrinfo ** res) +{ + struct addrinfo *ai; + struct sockaddr_in sin, + *psin; + struct addrinfo hints; + +#ifdef WIN32 + + /* + * If Windows has native IPv6 support, use the native Windows routine. + * Otherwise, fall through and use our own code. + */ + if (haveNativeWindowsIPv6routines()) + return (*getaddrinfo_ptr) (node, service, hintp, res); +#endif + + if (hintp == NULL) + { + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + } + else + memcpy(&hints, hintp, sizeof(hints)); + + if (hints.ai_family != AF_INET && hints.ai_family != AF_UNSPEC) + return EAI_FAMILY; + + if (hints.ai_socktype == 0) + hints.ai_socktype = SOCK_STREAM; + + if (!node && !service) + return EAI_NONAME; + + memset(&sin, 0, sizeof(sin)); + + sin.sin_family = AF_INET; + + if (node) + { + if (node[0] == '\0') + sin.sin_addr.s_addr = htonl(INADDR_ANY); + else if (hints.ai_flags & AI_NUMERICHOST) + { + if (!inet_aton(node, &sin.sin_addr)) + return EAI_NONAME; + } + else + { + struct hostent *hp; + +#ifdef FRONTEND + struct hostent hpstr; + char buf[BUFSIZ]; + int herrno = 0; + + pqGethostbyname(node, &hpstr, buf, sizeof(buf), + &hp, &herrno); +#else + hp = gethostbyname(node); +#endif + if (hp == NULL) + { + switch (h_errno) + { + case HOST_NOT_FOUND: + case NO_DATA: + return EAI_NONAME; + case TRY_AGAIN: + return EAI_AGAIN; + case NO_RECOVERY: + default: + return EAI_FAIL; + } + } + if (hp->h_addrtype != AF_INET) + return EAI_FAIL; + + memcpy(&(sin.sin_addr), hp->h_addr, hp->h_length); + } + } + else + { + if (hints.ai_flags & AI_PASSIVE) + sin.sin_addr.s_addr = htonl(INADDR_ANY); + else + sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + } + + if (service) + sin.sin_port = htons((unsigned short) atoi(service)); + +#ifdef HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN + sin.sin_len = sizeof(sin); +#endif + + ai = malloc(sizeof(*ai)); + if (!ai) + return EAI_MEMORY; + + psin = malloc(sizeof(*psin)); + if (!psin) + { + free(ai); + return EAI_MEMORY; + } + + memcpy(psin, &sin, sizeof(*psin)); + + ai->ai_flags = 0; + ai->ai_family = AF_INET; + ai->ai_socktype = hints.ai_socktype; + ai->ai_protocol = hints.ai_protocol; + ai->ai_addrlen = sizeof(*psin); + ai->ai_addr = (struct sockaddr *) psin; + ai->ai_canonname = NULL; + ai->ai_next = NULL; + + *res = ai; + + return 0; +} + + +void +freeaddrinfo(struct addrinfo * res) +{ + if (res) + { +#ifdef WIN32 + + /* + * If Windows has native IPv6 support, use the native Windows routine. + * Otherwise, fall through and use our own code. + */ + if (haveNativeWindowsIPv6routines()) + { + (*freeaddrinfo_ptr) (res); + return; + } +#endif + + if (res->ai_addr) + free(res->ai_addr); + free(res); + } +} + + +const char * +gai_strerror(int errcode) +{ +#ifdef HAVE_HSTRERROR + int hcode; + + switch (errcode) + { + case EAI_NONAME: + hcode = HOST_NOT_FOUND; + break; + case EAI_AGAIN: + hcode = TRY_AGAIN; + break; + case EAI_FAIL: + default: + hcode = NO_RECOVERY; + break; + } + + return hstrerror(hcode); +#else /* !HAVE_HSTRERROR */ + + switch (errcode) + { + case EAI_NONAME: + return "Unknown host"; + case EAI_AGAIN: + return "Host name lookup failure"; + /* Errors below are probably WIN32 only */ +#ifdef EAI_BADFLAGS + case EAI_BADFLAGS: + return "Invalid argument"; +#endif +#ifdef EAI_FAMILY + case EAI_FAMILY: + return "Address family not supported"; +#endif +#ifdef EAI_MEMORY + case EAI_MEMORY: + return "Not enough memory"; +#endif +#if defined(EAI_NODATA) && EAI_NODATA != EAI_NONAME /* MSVC/WIN64 duplicate */ + case EAI_NODATA: + return "No host data of that type was found"; +#endif +#ifdef EAI_SERVICE + case EAI_SERVICE: + return "Class type not found"; +#endif +#ifdef EAI_SOCKTYPE + case EAI_SOCKTYPE: + return "Socket type not supported"; +#endif + default: + return "Unknown server error"; + } +#endif /* HAVE_HSTRERROR */ +} + +/* + * Convert an ipv4 address to a hostname. + * + * Bugs: - Only supports NI_NUMERICHOST and NI_NUMERICSERV behavior. + * It will never resolve a hostname. + * - No IPv6 support. + */ +int +getnameinfo(const struct sockaddr * sa, int salen, + char *node, int nodelen, + char *service, int servicelen, int flags) +{ +#ifdef WIN32 + + /* + * If Windows has native IPv6 support, use the native Windows routine. + * Otherwise, fall through and use our own code. + */ + if (haveNativeWindowsIPv6routines()) + return (*getnameinfo_ptr) (sa, salen, node, nodelen, + service, servicelen, flags); +#endif + + /* Invalid arguments. */ + if (sa == NULL || (node == NULL && service == NULL)) + return EAI_FAIL; + +#ifdef HAVE_IPV6 + if (sa->sa_family == AF_INET6) + return EAI_FAMILY; +#endif + + /* Unsupported flags. */ + if (flags & NI_NAMEREQD) + return EAI_AGAIN; + + if (node) + { + if (sa->sa_family == AF_INET) + { + if (inet_net_ntop(AF_INET, &((struct sockaddr_in *) sa)->sin_addr, + sa->sa_family == AF_INET ? 32 : 128, + node, nodelen) == NULL) + return EAI_MEMORY; + } + else + return EAI_MEMORY; + } + + if (service) + { + int ret = -1; + + if (sa->sa_family == AF_INET) + { + ret = snprintf(service, servicelen, "%d", + ntohs(((struct sockaddr_in *) sa)->sin_port)); + } + if (ret == -1 || ret >= servicelen) + return EAI_MEMORY; + } + + return 0; +} diff --git a/libpq/win32/inet_aton.c b/libpq/win32/inet_aton.c new file mode 100644 index 0000000..27e8aaa --- /dev/null +++ b/libpq/win32/inet_aton.c @@ -0,0 +1,147 @@ +/* src/port/inet_aton.c + * + * This inet_aton() function was taken from the GNU C library and + * incorporated into Postgres for those systems which do not have this + * routine in their standard C libraries. + * + * The function was been extracted whole from the file inet_aton.c in + * Release 5.3.12 of the Linux C library, which is derived from the + * GNU C library, by Bryan Henderson in October 1996. The copyright + * notice from that file is below. + */ + +/* + * Copyright (c) 1983, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. */ + +#include "c.h" + +#include +#include + +/* + * Check whether "cp" is a valid ascii representation + * of an Internet address and convert to a binary address. + * Returns 1 if the address is valid, 0 if not. + * This replaces inet_addr, the return value from which + * cannot distinguish between failure and a local broadcast address. + */ +int +inet_aton(const char *cp, struct in_addr * addr) +{ + unsigned int val; + int base, + n; + char c; + u_int parts[4]; + u_int *pp = parts; + + for (;;) + { + /* + * Collect number up to ``.''. Values are specified as for C: 0x=hex, + * 0=octal, other=decimal. + */ + val = 0; + base = 10; + if (*cp == '0') + { + if (*++cp == 'x' || *cp == 'X') + base = 16, cp++; + else + base = 8; + } + while ((c = *cp) != '\0') + { + if (isdigit((unsigned char) c)) + { + val = (val * base) + (c - '0'); + cp++; + continue; + } + if (base == 16 && isxdigit((unsigned char) c)) + { + val = (val << 4) + + (c + 10 - (islower((unsigned char) c) ? 'a' : 'A')); + cp++; + continue; + } + break; + } + if (*cp == '.') + { + /* + * Internet format: a.b.c.d a.b.c (with c treated as 16-bits) + * a.b (with b treated as 24 bits) + */ + if (pp >= parts + 3 || val > 0xff) + return 0; + *pp++ = val, cp++; + } + else + break; + } + + /* + * Check for trailing junk. + */ + while (*cp) + if (!isspace((unsigned char) *cp++)) + return 0; + + /* + * Concoct the address according to the number of parts specified. + */ + n = pp - parts + 1; + switch (n) + { + + case 1: /* a -- 32 bits */ + break; + + case 2: /* a.b -- 8.24 bits */ + if (val > 0xffffff) + return 0; + val |= parts[0] << 24; + break; + + case 3: /* a.b.c -- 8.8.16 bits */ + if (val > 0xffff) + return 0; + val |= (parts[0] << 24) | (parts[1] << 16); + break; + + case 4: /* a.b.c.d -- 8.8.8.8 bits */ + if (val > 0xff) + return 0; + val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8); + break; + } + if (addr) + addr->s_addr = htonl(val); + return 1; +} diff --git a/libpq/win32/libpq.rc.in b/libpq/win32/libpq.rc.in new file mode 100644 index 0000000..da97574 --- /dev/null +++ b/libpq/win32/libpq.rc.in @@ -0,0 +1,31 @@ +#include + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 9,6,5,0 + PRODUCTVERSION 9,6,5,0 + FILEFLAGSMASK 0x3fL + FILEFLAGS 0 + FILEOS VOS__WINDOWS32 + FILETYPE VFT_DLL + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904B0" + BEGIN + VALUE "CompanyName", "\0" + VALUE "FileDescription", "PostgreSQL Access Library\0" + VALUE "FileVersion", "9.6.5\0" + VALUE "InternalName", "libpq\0" + VALUE "LegalCopyright", "Copyright (C) 2016\0" + VALUE "LegalTrademarks", "\0" + VALUE "OriginalFilename", "libpq.dll\0" + VALUE "ProductName", "PostgreSQL\0" + VALUE "ProductVersion", "9.6.5\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/libpq/win32/libpqdll.def b/libpq/win32/libpqdll.def new file mode 100644 index 0000000..810d4be --- /dev/null +++ b/libpq/win32/libpqdll.def @@ -0,0 +1,174 @@ +; DEF file for win32.mak release build and for Makefile.shlib (MinGW) +; LIBRARY LIBPQ.dll +EXPORTS + PQconnectdb @ 1 + PQsetdbLogin @ 2 + PQconndefaults @ 3 + PQfinish @ 4 + PQreset @ 5 + PQrequestCancel @ 6 + PQdb @ 7 + PQuser @ 8 + PQpass @ 9 + PQhost @ 10 + PQport @ 11 + PQtty @ 12 + PQoptions @ 13 + PQstatus @ 14 + PQerrorMessage @ 15 + PQsocket @ 16 + PQbackendPID @ 17 + PQtrace @ 18 + PQuntrace @ 19 + PQsetNoticeProcessor @ 20 + PQexec @ 21 + PQnotifies @ 22 + PQsendQuery @ 23 + PQgetResult @ 24 + PQisBusy @ 25 + PQconsumeInput @ 26 + PQgetline @ 27 + PQputline @ 28 + PQgetlineAsync @ 29 + PQputnbytes @ 30 + PQendcopy @ 31 + PQfn @ 32 + PQresultStatus @ 33 + PQntuples @ 34 + PQnfields @ 35 + PQbinaryTuples @ 36 + PQfname @ 37 + PQfnumber @ 38 + PQftype @ 39 + PQfsize @ 40 + PQfmod @ 41 + PQcmdStatus @ 42 + PQoidStatus @ 43 + PQcmdTuples @ 44 + PQgetvalue @ 45 + PQgetlength @ 46 + PQgetisnull @ 47 + PQclear @ 48 + PQmakeEmptyPGresult @ 49 + PQprint @ 50 + PQdisplayTuples @ 51 + PQprintTuples @ 52 + lo_open @ 53 + lo_close @ 54 + lo_read @ 55 + lo_write @ 56 + lo_lseek @ 57 + lo_creat @ 58 + lo_tell @ 59 + lo_unlink @ 60 + lo_import @ 61 + lo_export @ 62 + pgresStatus @ 63 + PQmblen @ 64 + PQresultErrorMessage @ 65 + PQresStatus @ 66 + termPQExpBuffer @ 67 + appendPQExpBufferChar @ 68 + initPQExpBuffer @ 69 + resetPQExpBuffer @ 70 + PQoidValue @ 71 + PQclientEncoding @ 72 + PQenv2encoding @ 73 + appendBinaryPQExpBuffer @ 74 + appendPQExpBufferStr @ 75 + destroyPQExpBuffer @ 76 + createPQExpBuffer @ 77 + PQconninfoFree @ 78 + PQconnectPoll @ 79 + PQconnectStart @ 80 + PQflush @ 81 + PQisnonblocking @ 82 + PQresetPoll @ 83 + PQresetStart @ 84 + PQsetClientEncoding @ 85 + PQsetnonblocking @ 86 + PQfreeNotify @ 87 + PQescapeString @ 88 + PQescapeBytea @ 89 + printfPQExpBuffer @ 90 + appendPQExpBuffer @ 91 + pg_encoding_to_char @ 92 + pg_utf_mblen @ 93 + PQunescapeBytea @ 94 + PQfreemem @ 95 + PQtransactionStatus @ 96 + PQparameterStatus @ 97 + PQprotocolVersion @ 98 + PQsetErrorVerbosity @ 99 + PQsetNoticeReceiver @ 100 + PQexecParams @ 101 + PQsendQueryParams @ 102 + PQputCopyData @ 103 + PQputCopyEnd @ 104 + PQgetCopyData @ 105 + PQresultErrorField @ 106 + PQftable @ 107 + PQftablecol @ 108 + PQfformat @ 109 + PQexecPrepared @ 110 + PQsendQueryPrepared @ 111 + PQdsplen @ 112 + PQserverVersion @ 113 + PQgetssl @ 114 + pg_char_to_encoding @ 115 + pg_valid_server_encoding @ 116 + pqsignal @ 117 + PQprepare @ 118 + PQsendPrepare @ 119 + PQgetCancel @ 120 + PQfreeCancel @ 121 + PQcancel @ 122 + lo_create @ 123 + PQinitSSL @ 124 + PQregisterThreadLock @ 125 + PQescapeStringConn @ 126 + PQescapeByteaConn @ 127 + PQencryptPassword @ 128 + PQisthreadsafe @ 129 + enlargePQExpBuffer @ 130 + PQnparams @ 131 + PQparamtype @ 132 + PQdescribePrepared @ 133 + PQdescribePortal @ 134 + PQsendDescribePrepared @ 135 + PQsendDescribePortal @ 136 + lo_truncate @ 137 + PQconnectionUsedPassword @ 138 + pg_valid_server_encoding_id @ 139 + PQconnectionNeedsPassword @ 140 + lo_import_with_oid @ 141 + PQcopyResult @ 142 + PQsetResultAttrs @ 143 + PQsetvalue @ 144 + PQresultAlloc @ 145 + PQregisterEventProc @ 146 + PQinstanceData @ 147 + PQsetInstanceData @ 148 + PQresultInstanceData @ 149 + PQresultSetInstanceData @ 150 + PQfireResultCreateEvents @ 151 + PQconninfoParse @ 152 + PQinitOpenSSL @ 153 + PQescapeLiteral @ 154 + PQescapeIdentifier @ 155 + PQconnectdbParams @ 156 + PQconnectStartParams @ 157 + PQping @ 158 + PQpingParams @ 159 + PQlibVersion @ 160 + PQsetSingleRowMode @ 161 + lo_lseek64 @ 162 + lo_tell64 @ 163 + lo_truncate64 @ 164 + PQconninfo @ 165 + PQsslInUse @ 166 + PQsslStruct @ 167 + PQsslAttributeNames @ 168 + PQsslAttribute @ 169 + PQsetErrorContextVisibility @ 170 + PQresultVerboseErrorMessage @ 171 diff --git a/libpq/win32/libpqdll.def.orig b/libpq/win32/libpqdll.def.orig new file mode 100644 index 0000000..0c9e8e4 --- /dev/null +++ b/libpq/win32/libpqdll.def.orig @@ -0,0 +1,174 @@ +; DEF file for win32.mak release build and for Makefile.shlib (MinGW) +LIBRARY LIBPQ.dll +EXPORTS + PQconnectdb @ 1 + PQsetdbLogin @ 2 + PQconndefaults @ 3 + PQfinish @ 4 + PQreset @ 5 + PQrequestCancel @ 6 + PQdb @ 7 + PQuser @ 8 + PQpass @ 9 + PQhost @ 10 + PQport @ 11 + PQtty @ 12 + PQoptions @ 13 + PQstatus @ 14 + PQerrorMessage @ 15 + PQsocket @ 16 + PQbackendPID @ 17 + PQtrace @ 18 + PQuntrace @ 19 + PQsetNoticeProcessor @ 20 + PQexec @ 21 + PQnotifies @ 22 + PQsendQuery @ 23 + PQgetResult @ 24 + PQisBusy @ 25 + PQconsumeInput @ 26 + PQgetline @ 27 + PQputline @ 28 + PQgetlineAsync @ 29 + PQputnbytes @ 30 + PQendcopy @ 31 + PQfn @ 32 + PQresultStatus @ 33 + PQntuples @ 34 + PQnfields @ 35 + PQbinaryTuples @ 36 + PQfname @ 37 + PQfnumber @ 38 + PQftype @ 39 + PQfsize @ 40 + PQfmod @ 41 + PQcmdStatus @ 42 + PQoidStatus @ 43 + PQcmdTuples @ 44 + PQgetvalue @ 45 + PQgetlength @ 46 + PQgetisnull @ 47 + PQclear @ 48 + PQmakeEmptyPGresult @ 49 + PQprint @ 50 + PQdisplayTuples @ 51 + PQprintTuples @ 52 + lo_open @ 53 + lo_close @ 54 + lo_read @ 55 + lo_write @ 56 + lo_lseek @ 57 + lo_creat @ 58 + lo_tell @ 59 + lo_unlink @ 60 + lo_import @ 61 + lo_export @ 62 + pgresStatus @ 63 + PQmblen @ 64 + PQresultErrorMessage @ 65 + PQresStatus @ 66 + termPQExpBuffer @ 67 + appendPQExpBufferChar @ 68 + initPQExpBuffer @ 69 + resetPQExpBuffer @ 70 + PQoidValue @ 71 + PQclientEncoding @ 72 + PQenv2encoding @ 73 + appendBinaryPQExpBuffer @ 74 + appendPQExpBufferStr @ 75 + destroyPQExpBuffer @ 76 + createPQExpBuffer @ 77 + PQconninfoFree @ 78 + PQconnectPoll @ 79 + PQconnectStart @ 80 + PQflush @ 81 + PQisnonblocking @ 82 + PQresetPoll @ 83 + PQresetStart @ 84 + PQsetClientEncoding @ 85 + PQsetnonblocking @ 86 + PQfreeNotify @ 87 + PQescapeString @ 88 + PQescapeBytea @ 89 + printfPQExpBuffer @ 90 + appendPQExpBuffer @ 91 + pg_encoding_to_char @ 92 + pg_utf_mblen @ 93 + PQunescapeBytea @ 94 + PQfreemem @ 95 + PQtransactionStatus @ 96 + PQparameterStatus @ 97 + PQprotocolVersion @ 98 + PQsetErrorVerbosity @ 99 + PQsetNoticeReceiver @ 100 + PQexecParams @ 101 + PQsendQueryParams @ 102 + PQputCopyData @ 103 + PQputCopyEnd @ 104 + PQgetCopyData @ 105 + PQresultErrorField @ 106 + PQftable @ 107 + PQftablecol @ 108 + PQfformat @ 109 + PQexecPrepared @ 110 + PQsendQueryPrepared @ 111 + PQdsplen @ 112 + PQserverVersion @ 113 + PQgetssl @ 114 + pg_char_to_encoding @ 115 + pg_valid_server_encoding @ 116 + pqsignal @ 117 + PQprepare @ 118 + PQsendPrepare @ 119 + PQgetCancel @ 120 + PQfreeCancel @ 121 + PQcancel @ 122 + lo_create @ 123 + PQinitSSL @ 124 + PQregisterThreadLock @ 125 + PQescapeStringConn @ 126 + PQescapeByteaConn @ 127 + PQencryptPassword @ 128 + PQisthreadsafe @ 129 + enlargePQExpBuffer @ 130 + PQnparams @ 131 + PQparamtype @ 132 + PQdescribePrepared @ 133 + PQdescribePortal @ 134 + PQsendDescribePrepared @ 135 + PQsendDescribePortal @ 136 + lo_truncate @ 137 + PQconnectionUsedPassword @ 138 + pg_valid_server_encoding_id @ 139 + PQconnectionNeedsPassword @ 140 + lo_import_with_oid @ 141 + PQcopyResult @ 142 + PQsetResultAttrs @ 143 + PQsetvalue @ 144 + PQresultAlloc @ 145 + PQregisterEventProc @ 146 + PQinstanceData @ 147 + PQsetInstanceData @ 148 + PQresultInstanceData @ 149 + PQresultSetInstanceData @ 150 + PQfireResultCreateEvents @ 151 + PQconninfoParse @ 152 + PQinitOpenSSL @ 153 + PQescapeLiteral @ 154 + PQescapeIdentifier @ 155 + PQconnectdbParams @ 156 + PQconnectStartParams @ 157 + PQping @ 158 + PQpingParams @ 159 + PQlibVersion @ 160 + PQsetSingleRowMode @ 161 + lo_lseek64 @ 162 + lo_tell64 @ 163 + lo_truncate64 @ 164 + PQconninfo @ 165 + PQsslInUse @ 166 + PQsslStruct @ 167 + PQsslAttributeNames @ 168 + PQsslAttribute @ 169 + PQsetErrorContextVisibility @ 170 + PQresultVerboseErrorMessage @ 171 diff --git a/libpq/win32/open.c b/libpq/win32/open.c new file mode 100644 index 0000000..717375d --- /dev/null +++ b/libpq/win32/open.c @@ -0,0 +1,167 @@ +/*------------------------------------------------------------------------- + * + * open.c + * Win32 open() replacement + * + * + * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * + * src/port/open.c + * + *------------------------------------------------------------------------- + */ + +#ifdef WIN32 + +#ifndef FRONTEND +#include "postgres.h" +#else +#include "postgres_fe.h" +#endif + +#include +#include +#include + + +static int +openFlagsToCreateFileFlags(int openFlags) +{ + switch (openFlags & (O_CREAT | O_TRUNC | O_EXCL)) + { + /* O_EXCL is meaningless without O_CREAT */ + case 0: + case O_EXCL: + return OPEN_EXISTING; + + case O_CREAT: + return OPEN_ALWAYS; + + /* O_EXCL is meaningless without O_CREAT */ + case O_TRUNC: + case O_TRUNC | O_EXCL: + return TRUNCATE_EXISTING; + + case O_CREAT | O_TRUNC: + return CREATE_ALWAYS; + + /* O_TRUNC is meaningless with O_CREAT */ + case O_CREAT | O_EXCL: + case O_CREAT | O_TRUNC | O_EXCL: + return CREATE_NEW; + } + + /* will never get here */ + return 0; +} + +/* + * - file attribute setting, based on fileMode? + */ +int +pgwin32_open(const char *fileName, int fileFlags,...) +{ + int fd; + HANDLE h = INVALID_HANDLE_VALUE; + SECURITY_ATTRIBUTES sa; + int loops = 0; + + /* Check that we can handle the request */ + assert((fileFlags & ((O_RDONLY | O_WRONLY | O_RDWR) | O_APPEND | + (O_RANDOM | O_SEQUENTIAL | O_TEMPORARY) | + _O_SHORT_LIVED | O_DSYNC | O_DIRECT | + (O_CREAT | O_TRUNC | O_EXCL) | (O_TEXT | O_BINARY))) == fileFlags); + + sa.nLength = sizeof(sa); + sa.bInheritHandle = TRUE; + sa.lpSecurityDescriptor = NULL; + + while ((h = CreateFile(fileName, + /* cannot use O_RDONLY, as it == 0 */ + (fileFlags & O_RDWR) ? (GENERIC_WRITE | GENERIC_READ) : + ((fileFlags & O_WRONLY) ? GENERIC_WRITE : GENERIC_READ), + /* These flags allow concurrent rename/unlink */ + (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE), + &sa, + openFlagsToCreateFileFlags(fileFlags), + FILE_ATTRIBUTE_NORMAL | + ((fileFlags & O_RANDOM) ? FILE_FLAG_RANDOM_ACCESS : 0) | + ((fileFlags & O_SEQUENTIAL) ? FILE_FLAG_SEQUENTIAL_SCAN : 0) | + ((fileFlags & _O_SHORT_LIVED) ? FILE_ATTRIBUTE_TEMPORARY : 0) | + ((fileFlags & O_TEMPORARY) ? FILE_FLAG_DELETE_ON_CLOSE : 0) | + ((fileFlags & O_DIRECT) ? FILE_FLAG_NO_BUFFERING : 0) | + ((fileFlags & O_DSYNC) ? FILE_FLAG_WRITE_THROUGH : 0), + NULL)) == INVALID_HANDLE_VALUE) + { + /* + * Sharing violation or locking error can indicate antivirus, backup + * or similar software that's locking the file. Try again for 30 + * seconds before giving up. + */ + DWORD err = GetLastError(); + + if (err == ERROR_SHARING_VIOLATION || + err == ERROR_LOCK_VIOLATION) + { + pg_usleep(100000); + loops++; + +#ifndef FRONTEND + if (loops == 50) + ereport(LOG, + (errmsg("could not open file \"%s\": %s", fileName, + (err == ERROR_SHARING_VIOLATION) ? _("sharing violation") : _("lock violation")), + errdetail("Continuing to retry for 30 seconds."), + errhint("You might have antivirus, backup, or similar software interfering with the database system."))); +#endif + + if (loops < 300) + continue; + } + + _dosmaperr(err); + return -1; + } + + /* _open_osfhandle will, on error, set errno accordingly */ + if ((fd = _open_osfhandle((intptr_t) h, fileFlags & O_APPEND)) < 0) + CloseHandle(h); /* will not affect errno */ + else if (fileFlags & (O_TEXT | O_BINARY) && + _setmode(fd, fileFlags & (O_TEXT | O_BINARY)) < 0) + { + _close(fd); + return -1; + } + + return fd; +} + +FILE * +pgwin32_fopen(const char *fileName, const char *mode) +{ + int openmode = 0; + int fd; + + if (strstr(mode, "r+")) + openmode |= O_RDWR; + else if (strchr(mode, 'r')) + openmode |= O_RDONLY; + if (strstr(mode, "w+")) + openmode |= O_RDWR | O_CREAT | O_TRUNC; + else if (strchr(mode, 'w')) + openmode |= O_WRONLY | O_CREAT | O_TRUNC; + if (strchr(mode, 'a')) + openmode |= O_WRONLY | O_CREAT | O_APPEND; + + if (strchr(mode, 'b')) + openmode |= O_BINARY; + if (strchr(mode, 't')) + openmode |= O_TEXT; + + fd = pgwin32_open(fileName, openmode); + if (fd == -1) + return NULL; + return _fdopen(fd, mode); +} + +#endif diff --git a/libpq/win32/pgsleep.c b/libpq/win32/pgsleep.c new file mode 100644 index 0000000..ecefe04 --- /dev/null +++ b/libpq/win32/pgsleep.c @@ -0,0 +1,63 @@ +/*------------------------------------------------------------------------- + * + * pgsleep.c + * Portable delay handling. + * + * + * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * + * src/port/pgsleep.c + * + *------------------------------------------------------------------------- + */ +#include "c.h" + +#include +#include +#ifdef HAVE_SYS_SELECT_H +#include +#endif + +/* + * In a Windows backend, we don't use this implementation, but rather + * the signal-aware version in src/backend/port/win32/signal.c. + */ +#if defined(FRONTEND) || !defined(WIN32) + +/* + * pg_usleep --- delay the specified number of microseconds. + * + * NOTE: although the delay is specified in microseconds, the effective + * resolution is only 1/HZ, or 10 milliseconds, on most Unixen. Expect + * the requested delay to be rounded up to the next resolution boundary. + * + * On machines where "long" is 32 bits, the maximum delay is ~2000 seconds. + * + * CAUTION: the behavior when a signal arrives during the sleep is platform + * dependent. On most Unix-ish platforms, a signal does not terminate the + * sleep; but on some, it will (the Windows implementation also allows signals + * to terminate pg_usleep). And there are platforms where not only does a + * signal not terminate the sleep, but it actually resets the timeout counter + * so that the sleep effectively starts over! It is therefore rather hazardous + * to use this for long sleeps; a continuing stream of signal events could + * prevent the sleep from ever terminating. Better practice for long sleeps + * is to use WaitLatch() with a timeout. + */ +void +pg_usleep(long microsec) +{ + if (microsec > 0) + { +#ifndef WIN32 + struct timeval delay; + + delay.tv_sec = microsec / 1000000L; + delay.tv_usec = microsec % 1000000L; + (void) select(0, NULL, NULL, NULL, &delay); +#else + SleepEx((microsec < 500 ? 1 : (microsec + 500) / 1000), FALSE); +#endif + } +} + +#endif /* defined(FRONTEND) || !defined(WIN32) */ diff --git a/libpq/win32/pthread-win32.c b/libpq/win32/pthread-win32.c new file mode 100644 index 0000000..68dfefc --- /dev/null +++ b/libpq/win32/pthread-win32.c @@ -0,0 +1,61 @@ +/*------------------------------------------------------------------------- +* +* pthread-win32.c +* partial pthread implementation for win32 +* +* Copyright (c) 2004-2016, PostgreSQL Global Development Group +* IDENTIFICATION +* src/interfaces/libpq/pthread-win32.c +* +*------------------------------------------------------------------------- +*/ + +#include "postgres_fe.h" + +#include +#include "pthread-win32.h" + +DWORD +pthread_self(void) +{ + return GetCurrentThreadId(); +} + +void +pthread_setspecific(pthread_key_t key, void *val) +{ +} + +void * +pthread_getspecific(pthread_key_t key) +{ + return NULL; +} + +int +pthread_mutex_init(pthread_mutex_t *mp, void *attr) +{ + *mp = (CRITICAL_SECTION *) malloc(sizeof(CRITICAL_SECTION)); + if (!*mp) + return 1; + InitializeCriticalSection(*mp); + return 0; +} + +int +pthread_mutex_lock(pthread_mutex_t *mp) +{ + if (!*mp) + return 1; + EnterCriticalSection(*mp); + return 0; +} + +int +pthread_mutex_unlock(pthread_mutex_t *mp) +{ + if (!*mp) + return 1; + LeaveCriticalSection(*mp); + return 0; +} diff --git a/libpq/win32/pthread-win32.h b/libpq/win32/pthread-win32.h new file mode 100644 index 0000000..97ccc17 --- /dev/null +++ b/libpq/win32/pthread-win32.h @@ -0,0 +1,22 @@ +/* + * src/port/pthread-win32.h + */ +#ifndef __PTHREAD_H +#define __PTHREAD_H + +typedef ULONG pthread_key_t; +typedef CRITICAL_SECTION *pthread_mutex_t; +typedef int pthread_once_t; + +DWORD pthread_self(void); + +void pthread_setspecific(pthread_key_t, void *); +void *pthread_getspecific(pthread_key_t); + +int pthread_mutex_init(pthread_mutex_t *, void *attr); +int pthread_mutex_lock(pthread_mutex_t *); + +/* blocking */ +int pthread_mutex_unlock(pthread_mutex_t *); + +#endif diff --git a/libpq/win32/snprintf.c b/libpq/win32/snprintf.c new file mode 100644 index 0000000..62b23b0 --- /dev/null +++ b/libpq/win32/snprintf.c @@ -0,0 +1,1141 @@ +/* + * Copyright (c) 1983, 1995, 1996 Eric P. Allman + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * src/port/snprintf.c + */ + +#include "c.h" + +#include +#ifdef _MSC_VER +#include /* for _isnan */ +#endif +#include +#include +#ifndef WIN32 +#include +#endif +#include + +#ifndef NL_ARGMAX +#define NL_ARGMAX 16 +#endif + + +/* + * SNPRINTF, VSNPRINTF and friends + * + * These versions have been grabbed off the net. They have been + * cleaned up to compile properly and support for most of the Single Unix + * Specification has been added. Remaining unimplemented features are: + * + * 1. No locale support: the radix character is always '.' and the ' + * (single quote) format flag is ignored. + * + * 2. No support for the "%n" format specification. + * + * 3. No support for wide characters ("lc" and "ls" formats). + * + * 4. No support for "long double" ("Lf" and related formats). + * + * 5. Space and '#' flags are not implemented. + * + * + * The result values of these functions are not the same across different + * platforms. This implementation is compatible with the Single Unix Spec: + * + * 1. -1 is returned only if processing is abandoned due to an invalid + * parameter, such as incorrect format string. (Although not required by + * the spec, this happens only when no characters have yet been transmitted + * to the destination.) + * + * 2. For snprintf and sprintf, 0 is returned if str == NULL or count == 0; + * no data has been stored. + * + * 3. Otherwise, the number of bytes actually transmitted to the destination + * is returned (excluding the trailing '\0' for snprintf and sprintf). + * + * For snprintf with nonzero count, the result cannot be more than count-1 + * (a trailing '\0' is always stored); it is not possible to distinguish + * buffer overrun from exact fit. This is unlike some implementations that + * return the number of bytes that would have been needed for the complete + * result string. + */ + +/************************************************************** + * Original: + * Patrick Powell Tue Apr 11 09:48:21 PDT 1995 + * A bombproof version of doprnt (dopr) included. + * Sigh. This sort of thing is always nasty do deal with. Note that + * the version here does not include floating point. (now it does ... tgl) + **************************************************************/ + +/* Prevent recursion */ +#undef vsnprintf +#undef snprintf +#undef sprintf +#undef vfprintf +#undef fprintf +#undef printf + +/* Info about where the formatted output is going */ +typedef struct +{ + char *bufptr; /* next buffer output position */ + char *bufstart; /* first buffer element */ + char *bufend; /* last buffer element, or NULL */ + /* bufend == NULL is for sprintf, where we assume buf is big enough */ + FILE *stream; /* eventual output destination, or NULL */ + int nchars; /* # chars already sent to stream */ + bool failed; /* call is a failure; errno is set */ +} PrintfTarget; + +/* + * Info about the type and value of a formatting parameter. Note that we + * don't currently support "long double", "wint_t", or "wchar_t *" data, + * nor the '%n' formatting code; else we'd need more types. Also, at this + * level we need not worry about signed vs unsigned values. + */ +typedef enum +{ + ATYPE_NONE = 0, + ATYPE_INT, + ATYPE_LONG, + ATYPE_LONGLONG, + ATYPE_DOUBLE, + ATYPE_CHARPTR +} PrintfArgType; + +typedef union +{ + int i; + long l; + int64 ll; + double d; + char *cptr; +} PrintfArgValue; + + +static void flushbuffer(PrintfTarget *target); +static void dopr(PrintfTarget *target, const char *format, va_list args); + + +int +pg_vsnprintf(char *str, size_t count, const char *fmt, va_list args) +{ + PrintfTarget target; + + if (str == NULL || count == 0) + return 0; + target.bufstart = target.bufptr = str; + target.bufend = str + count - 1; + target.stream = NULL; + /* target.nchars is unused in this case */ + target.failed = false; + dopr(&target, fmt, args); + *(target.bufptr) = '\0'; + return target.failed ? -1 : (target.bufptr - target.bufstart); +} + +int +pg_snprintf(char *str, size_t count, const char *fmt,...) +{ + int len; + va_list args; + + va_start(args, fmt); + len = pg_vsnprintf(str, count, fmt, args); + va_end(args); + return len; +} + +static int +pg_vsprintf(char *str, const char *fmt, va_list args) +{ + PrintfTarget target; + + if (str == NULL) + return 0; + target.bufstart = target.bufptr = str; + target.bufend = NULL; + target.stream = NULL; + /* target.nchars is unused in this case */ + target.failed = false; + dopr(&target, fmt, args); + *(target.bufptr) = '\0'; + return target.failed ? -1 : (target.bufptr - target.bufstart); +} + +int +pg_sprintf(char *str, const char *fmt,...) +{ + int len; + va_list args; + + va_start(args, fmt); + len = pg_vsprintf(str, fmt, args); + va_end(args); + return len; +} + +int +pg_vfprintf(FILE *stream, const char *fmt, va_list args) +{ + PrintfTarget target; + char buffer[1024]; /* size is arbitrary */ + + if (stream == NULL) + { + errno = EINVAL; + return -1; + } + target.bufstart = target.bufptr = buffer; + target.bufend = buffer + sizeof(buffer) - 1; + target.stream = stream; + target.nchars = 0; + target.failed = false; + dopr(&target, fmt, args); + /* dump any remaining buffer contents */ + flushbuffer(&target); + return target.failed ? -1 : target.nchars; +} + +int +pg_fprintf(FILE *stream, const char *fmt,...) +{ + int len; + va_list args; + + va_start(args, fmt); + len = pg_vfprintf(stream, fmt, args); + va_end(args); + return len; +} + +int +pg_printf(const char *fmt,...) +{ + int len; + va_list args; + + va_start(args, fmt); + len = pg_vfprintf(stdout, fmt, args); + va_end(args); + return len; +} + +/* + * Attempt to write the entire buffer to target->stream; discard the entire + * buffer in any case. Call this only when target->stream is defined. + */ +static void +flushbuffer(PrintfTarget *target) +{ + size_t nc = target->bufptr - target->bufstart; + + if (!target->failed && nc > 0) + { + size_t written; + + written = fwrite(target->bufstart, 1, nc, target->stream); + target->nchars += written; + if (written != nc) + target->failed = true; + } + target->bufptr = target->bufstart; +} + + +static void fmtstr(char *value, int leftjust, int minlen, int maxwidth, + int pointflag, PrintfTarget *target); +static void fmtptr(void *value, PrintfTarget *target); +static void fmtint(int64 value, char type, int forcesign, + int leftjust, int minlen, int zpad, int precision, int pointflag, + PrintfTarget *target); +static void fmtchar(int value, int leftjust, int minlen, PrintfTarget *target); +static void fmtfloat(double value, char type, int forcesign, + int leftjust, int minlen, int zpad, int precision, int pointflag, + PrintfTarget *target); +static void dostr(const char *str, int slen, PrintfTarget *target); +static void dopr_outch(int c, PrintfTarget *target); +static int adjust_sign(int is_negative, int forcesign, int *signvalue); +static void adjust_padlen(int minlen, int vallen, int leftjust, int *padlen); +static void leading_pad(int zpad, int *signvalue, int *padlen, + PrintfTarget *target); +static void trailing_pad(int *padlen, PrintfTarget *target); + + +/* + * dopr(): poor man's version of doprintf + */ +static void +dopr(PrintfTarget *target, const char *format, va_list args) +{ + const char *format_start = format; + int ch; + bool have_dollar; + bool have_non_dollar; + bool have_star; + bool afterstar; + int accum; + int longlongflag; + int longflag; + int pointflag; + int leftjust; + int fieldwidth; + int precision; + int zpad; + int forcesign; + int last_dollar; + int fmtpos; + int cvalue; + int64 numvalue; + double fvalue; + char *strvalue; + int i; + PrintfArgType argtypes[NL_ARGMAX + 1]; + PrintfArgValue argvalues[NL_ARGMAX + 1]; + + /* + * Parse the format string to determine whether there are %n$ format + * specs, and identify the types and order of the format parameters. + */ + have_dollar = have_non_dollar = false; + last_dollar = 0; + MemSet(argtypes, 0, sizeof(argtypes)); + + while ((ch = *format++) != '\0') + { + if (ch != '%') + continue; + longflag = longlongflag = pointflag = 0; + fmtpos = accum = 0; + afterstar = false; +nextch1: + ch = *format++; + if (ch == '\0') + break; /* illegal, but we don't complain */ + switch (ch) + { + case '-': + case '+': + goto nextch1; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + accum = accum * 10 + (ch - '0'); + goto nextch1; + case '.': + pointflag = 1; + accum = 0; + goto nextch1; + case '*': + if (afterstar) + have_non_dollar = true; /* multiple stars */ + afterstar = true; + accum = 0; + goto nextch1; + case '$': + have_dollar = true; + if (accum <= 0 || accum > NL_ARGMAX) + goto bad_format; + if (afterstar) + { + if (argtypes[accum] && + argtypes[accum] != ATYPE_INT) + goto bad_format; + argtypes[accum] = ATYPE_INT; + last_dollar = Max(last_dollar, accum); + afterstar = false; + } + else + fmtpos = accum; + accum = 0; + goto nextch1; + case 'l': + if (longflag) + longlongflag = 1; + else + longflag = 1; + goto nextch1; + case 'z': +#if SIZEOF_SIZE_T == 8 +#ifdef HAVE_LONG_INT_64 + longflag = 1; +#elif defined(HAVE_LONG_LONG_INT_64) + longlongflag = 1; +#else +#error "Don't know how to print 64bit integers" +#endif +#else + /* assume size_t is same size as int */ +#endif + goto nextch1; + case 'h': + case '\'': + /* ignore these */ + goto nextch1; + case 'd': + case 'i': + case 'o': + case 'u': + case 'x': + case 'X': + if (fmtpos) + { + PrintfArgType atype; + + if (longlongflag) + atype = ATYPE_LONGLONG; + else if (longflag) + atype = ATYPE_LONG; + else + atype = ATYPE_INT; + if (argtypes[fmtpos] && + argtypes[fmtpos] != atype) + goto bad_format; + argtypes[fmtpos] = atype; + last_dollar = Max(last_dollar, fmtpos); + } + else + have_non_dollar = true; + break; + case 'c': + if (fmtpos) + { + if (argtypes[fmtpos] && + argtypes[fmtpos] != ATYPE_INT) + goto bad_format; + argtypes[fmtpos] = ATYPE_INT; + last_dollar = Max(last_dollar, fmtpos); + } + else + have_non_dollar = true; + break; + case 's': + case 'p': + if (fmtpos) + { + if (argtypes[fmtpos] && + argtypes[fmtpos] != ATYPE_CHARPTR) + goto bad_format; + argtypes[fmtpos] = ATYPE_CHARPTR; + last_dollar = Max(last_dollar, fmtpos); + } + else + have_non_dollar = true; + break; + case 'e': + case 'E': + case 'f': + case 'g': + case 'G': + if (fmtpos) + { + if (argtypes[fmtpos] && + argtypes[fmtpos] != ATYPE_DOUBLE) + goto bad_format; + argtypes[fmtpos] = ATYPE_DOUBLE; + last_dollar = Max(last_dollar, fmtpos); + } + else + have_non_dollar = true; + break; + case '%': + break; + } + + /* + * If we finish the spec with afterstar still set, there's a + * non-dollar star in there. + */ + if (afterstar) + have_non_dollar = true; + } + + /* Per spec, you use either all dollar or all not. */ + if (have_dollar && have_non_dollar) + goto bad_format; + + /* + * In dollar mode, collect the arguments in physical order. + */ + for (i = 1; i <= last_dollar; i++) + { + switch (argtypes[i]) + { + case ATYPE_NONE: + goto bad_format; + case ATYPE_INT: + argvalues[i].i = va_arg(args, int); + break; + case ATYPE_LONG: + argvalues[i].l = va_arg(args, long); + break; + case ATYPE_LONGLONG: + argvalues[i].ll = va_arg(args, int64); + break; + case ATYPE_DOUBLE: + argvalues[i].d = va_arg(args, double); + break; + case ATYPE_CHARPTR: + argvalues[i].cptr = va_arg(args, char *); + break; + } + } + + /* + * At last we can parse the format for real. + */ + format = format_start; + while ((ch = *format++) != '\0') + { + if (target->failed) + break; + + if (ch != '%') + { + dopr_outch(ch, target); + continue; + } + fieldwidth = precision = zpad = leftjust = forcesign = 0; + longflag = longlongflag = pointflag = 0; + fmtpos = accum = 0; + have_star = afterstar = false; +nextch2: + ch = *format++; + if (ch == '\0') + break; /* illegal, but we don't complain */ + switch (ch) + { + case '-': + leftjust = 1; + goto nextch2; + case '+': + forcesign = 1; + goto nextch2; + case '0': + /* set zero padding if no nonzero digits yet */ + if (accum == 0 && !pointflag) + zpad = '0'; + /* FALL THRU */ + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + accum = accum * 10 + (ch - '0'); + goto nextch2; + case '.': + if (have_star) + have_star = false; + else + fieldwidth = accum; + pointflag = 1; + accum = 0; + goto nextch2; + case '*': + if (have_dollar) + { + /* process value after reading n$ */ + afterstar = true; + } + else + { + /* fetch and process value now */ + int starval = va_arg(args, int); + + if (pointflag) + { + precision = starval; + if (precision < 0) + { + precision = 0; + pointflag = 0; + } + } + else + { + fieldwidth = starval; + if (fieldwidth < 0) + { + leftjust = 1; + fieldwidth = -fieldwidth; + } + } + } + have_star = true; + accum = 0; + goto nextch2; + case '$': + if (afterstar) + { + /* fetch and process star value */ + int starval = argvalues[accum].i; + + if (pointflag) + { + precision = starval; + if (precision < 0) + { + precision = 0; + pointflag = 0; + } + } + else + { + fieldwidth = starval; + if (fieldwidth < 0) + { + leftjust = 1; + fieldwidth = -fieldwidth; + } + } + afterstar = false; + } + else + fmtpos = accum; + accum = 0; + goto nextch2; + case 'l': + if (longflag) + longlongflag = 1; + else + longflag = 1; + goto nextch2; + case 'z': +#if SIZEOF_SIZE_T == 8 +#ifdef HAVE_LONG_INT_64 + longflag = 1; +#elif defined(HAVE_LONG_LONG_INT_64) + longlongflag = 1; +#else +#error "Don't know how to print 64bit integers" +#endif +#else + /* assume size_t is same size as int */ +#endif + goto nextch2; + case 'h': + case '\'': + /* ignore these */ + goto nextch2; + case 'd': + case 'i': + if (!have_star) + { + if (pointflag) + precision = accum; + else + fieldwidth = accum; + } + if (have_dollar) + { + if (longlongflag) + numvalue = argvalues[fmtpos].ll; + else if (longflag) + numvalue = argvalues[fmtpos].l; + else + numvalue = argvalues[fmtpos].i; + } + else + { + if (longlongflag) + numvalue = va_arg(args, int64); + else if (longflag) + numvalue = va_arg(args, long); + else + numvalue = va_arg(args, int); + } + fmtint(numvalue, ch, forcesign, leftjust, fieldwidth, zpad, + precision, pointflag, target); + break; + case 'o': + case 'u': + case 'x': + case 'X': + if (!have_star) + { + if (pointflag) + precision = accum; + else + fieldwidth = accum; + } + if (have_dollar) + { + if (longlongflag) + numvalue = (uint64) argvalues[fmtpos].ll; + else if (longflag) + numvalue = (unsigned long) argvalues[fmtpos].l; + else + numvalue = (unsigned int) argvalues[fmtpos].i; + } + else + { + if (longlongflag) + numvalue = (uint64) va_arg(args, int64); + else if (longflag) + numvalue = (unsigned long) va_arg(args, long); + else + numvalue = (unsigned int) va_arg(args, int); + } + fmtint(numvalue, ch, forcesign, leftjust, fieldwidth, zpad, + precision, pointflag, target); + break; + case 'c': + if (!have_star) + { + if (pointflag) + precision = accum; + else + fieldwidth = accum; + } + if (have_dollar) + cvalue = (unsigned char) argvalues[fmtpos].i; + else + cvalue = (unsigned char) va_arg(args, int); + fmtchar(cvalue, leftjust, fieldwidth, target); + break; + case 's': + if (!have_star) + { + if (pointflag) + precision = accum; + else + fieldwidth = accum; + } + if (have_dollar) + strvalue = argvalues[fmtpos].cptr; + else + strvalue = va_arg(args, char *); + fmtstr(strvalue, leftjust, fieldwidth, precision, pointflag, + target); + break; + case 'p': + /* fieldwidth/leftjust are ignored ... */ + if (have_dollar) + strvalue = argvalues[fmtpos].cptr; + else + strvalue = va_arg(args, char *); + fmtptr((void *) strvalue, target); + break; + case 'e': + case 'E': + case 'f': + case 'g': + case 'G': + if (!have_star) + { + if (pointflag) + precision = accum; + else + fieldwidth = accum; + } + if (have_dollar) + fvalue = argvalues[fmtpos].d; + else + fvalue = va_arg(args, double); + fmtfloat(fvalue, ch, forcesign, leftjust, + fieldwidth, zpad, + precision, pointflag, + target); + break; + case '%': + dopr_outch('%', target); + break; + } + } + + return; + +bad_format: + errno = EINVAL; + target->failed = true; +} + +static size_t +pg_strnlen(const char *str, size_t maxlen) +{ + const char *p = str; + + while (maxlen-- > 0 && *p) + p++; + return p - str; +} + +static void +fmtstr(char *value, int leftjust, int minlen, int maxwidth, + int pointflag, PrintfTarget *target) +{ + int padlen, + vallen; /* amount to pad */ + + /* + * If a maxwidth (precision) is specified, we must not fetch more bytes + * than that. + */ + if (pointflag) + vallen = pg_strnlen(value, maxwidth); + else + vallen = strlen(value); + + adjust_padlen(minlen, vallen, leftjust, &padlen); + + while (padlen > 0) + { + dopr_outch(' ', target); + --padlen; + } + + dostr(value, vallen, target); + + trailing_pad(&padlen, target); +} + +static void +fmtptr(void *value, PrintfTarget *target) +{ + int vallen; + char convert[64]; + + /* we rely on regular C library's sprintf to do the basic conversion */ + vallen = sprintf(convert, "%p", value); + if (vallen < 0) + target->failed = true; + else + dostr(convert, vallen, target); +} + +static void +fmtint(int64 value, char type, int forcesign, int leftjust, + int minlen, int zpad, int precision, int pointflag, + PrintfTarget *target) +{ + uint64 base; + int dosign; + const char *cvt = "0123456789abcdef"; + int signvalue = 0; + char convert[64]; + int vallen = 0; + int padlen = 0; /* amount to pad */ + int zeropad; /* extra leading zeroes */ + + switch (type) + { + case 'd': + case 'i': + base = 10; + dosign = 1; + break; + case 'o': + base = 8; + dosign = 0; + break; + case 'u': + base = 10; + dosign = 0; + break; + case 'x': + base = 16; + dosign = 0; + break; + case 'X': + cvt = "0123456789ABCDEF"; + base = 16; + dosign = 0; + break; + default: + return; /* keep compiler quiet */ + } + + /* Handle +/- */ + if (dosign && adjust_sign((value < 0), forcesign, &signvalue)) + value = -value; + + /* + * SUS: the result of converting 0 with an explicit precision of 0 is no + * characters + */ + if (value == 0 && pointflag && precision == 0) + vallen = 0; + else + { + /* make integer string */ + uint64 uvalue = (uint64) value; + + do + { + convert[vallen++] = cvt[uvalue % base]; + uvalue = uvalue / base; + } while (uvalue); + } + + zeropad = Max(0, precision - vallen); + + adjust_padlen(minlen, vallen + zeropad, leftjust, &padlen); + + leading_pad(zpad, &signvalue, &padlen, target); + + while (zeropad-- > 0) + dopr_outch('0', target); + + while (vallen > 0) + dopr_outch(convert[--vallen], target); + + trailing_pad(&padlen, target); +} + +static void +fmtchar(int value, int leftjust, int minlen, PrintfTarget *target) +{ + int padlen = 0; /* amount to pad */ + + adjust_padlen(minlen, 1, leftjust, &padlen); + + while (padlen > 0) + { + dopr_outch(' ', target); + --padlen; + } + + dopr_outch(value, target); + + trailing_pad(&padlen, target); +} + +static void +fmtfloat(double value, char type, int forcesign, int leftjust, + int minlen, int zpad, int precision, int pointflag, + PrintfTarget *target) +{ + int signvalue = 0; + int prec; + int vallen; + char fmt[32]; + char convert[1024]; + int zeropadlen = 0; /* amount to pad with zeroes */ + int padlen = 0; /* amount to pad with spaces */ + + /* + * We rely on the regular C library's sprintf to do the basic conversion, + * then handle padding considerations here. + * + * The dynamic range of "double" is about 1E+-308 for IEEE math, and not + * too wildly more than that with other hardware. In "f" format, sprintf + * could therefore generate at most 308 characters to the left of the + * decimal point; while we need to allow the precision to get as high as + * 308+17 to ensure that we don't truncate significant digits from very + * small values. To handle both these extremes, we use a buffer of 1024 + * bytes and limit requested precision to 350 digits; this should prevent + * buffer overrun even with non-IEEE math. If the original precision + * request was more than 350, separately pad with zeroes. + */ + if (precision < 0) /* cover possible overflow of "accum" */ + precision = 0; + prec = Min(precision, 350); + + if (pointflag) + { + if (sprintf(fmt, "%%.%d%c", prec, type) < 0) + goto fail; + zeropadlen = precision - prec; + } + else if (sprintf(fmt, "%%%c", type) < 0) + goto fail; + + if (!isnan(value) && adjust_sign((value < 0), forcesign, &signvalue)) + value = -value; + + vallen = sprintf(convert, fmt, value); + if (vallen < 0) + goto fail; + + /* If it's infinity or NaN, forget about doing any zero-padding */ + if (zeropadlen > 0 && !isdigit((unsigned char) convert[vallen - 1])) + zeropadlen = 0; + + adjust_padlen(minlen, vallen + zeropadlen, leftjust, &padlen); + + leading_pad(zpad, &signvalue, &padlen, target); + + if (zeropadlen > 0) + { + /* If 'e' or 'E' format, inject zeroes before the exponent */ + char *epos = strrchr(convert, 'e'); + + if (!epos) + epos = strrchr(convert, 'E'); + if (epos) + { + /* pad after exponent */ + dostr(convert, epos - convert, target); + while (zeropadlen-- > 0) + dopr_outch('0', target); + dostr(epos, vallen - (epos - convert), target); + } + else + { + /* no exponent, pad after the digits */ + dostr(convert, vallen, target); + while (zeropadlen-- > 0) + dopr_outch('0', target); + } + } + else + { + /* no zero padding, just emit the number as-is */ + dostr(convert, vallen, target); + } + + trailing_pad(&padlen, target); + return; + +fail: + target->failed = true; +} + +static void +dostr(const char *str, int slen, PrintfTarget *target) +{ + while (slen > 0) + { + int avail; + + if (target->bufend != NULL) + avail = target->bufend - target->bufptr; + else + avail = slen; + if (avail <= 0) + { + /* buffer full, can we dump to stream? */ + if (target->stream == NULL) + return; /* no, lose the data */ + flushbuffer(target); + continue; + } + avail = Min(avail, slen); + memmove(target->bufptr, str, avail); + target->bufptr += avail; + str += avail; + slen -= avail; + } +} + +static void +dopr_outch(int c, PrintfTarget *target) +{ + if (target->bufend != NULL && target->bufptr >= target->bufend) + { + /* buffer full, can we dump to stream? */ + if (target->stream == NULL) + return; /* no, lose the data */ + flushbuffer(target); + } + *(target->bufptr++) = c; +} + + +static int +adjust_sign(int is_negative, int forcesign, int *signvalue) +{ + if (is_negative) + { + *signvalue = '-'; + return true; + } + else if (forcesign) + *signvalue = '+'; + return false; +} + + +static void +adjust_padlen(int minlen, int vallen, int leftjust, int *padlen) +{ + *padlen = minlen - vallen; + if (*padlen < 0) + *padlen = 0; + if (leftjust) + *padlen = -(*padlen); +} + + +static void +leading_pad(int zpad, int *signvalue, int *padlen, PrintfTarget *target) +{ + if (*padlen > 0 && zpad) + { + if (*signvalue) + { + dopr_outch(*signvalue, target); + --(*padlen); + *signvalue = 0; + } + while (*padlen > 0) + { + dopr_outch(zpad, target); + --(*padlen); + } + } + while (*padlen > (*signvalue != 0)) + { + dopr_outch(' ', target); + --(*padlen); + } + if (*signvalue) + { + dopr_outch(*signvalue, target); + if (*padlen > 0) + --(*padlen); + else if (*padlen < 0) + ++(*padlen); + } +} + + +static void +trailing_pad(int *padlen, PrintfTarget *target) +{ + while (*padlen < 0) + { + dopr_outch(' ', target); + ++(*padlen); + } +} diff --git a/libpq/win32/system.c b/libpq/win32/system.c new file mode 100644 index 0000000..15b6e37 --- /dev/null +++ b/libpq/win32/system.c @@ -0,0 +1,119 @@ +/*------------------------------------------------------------------------- + * + * system.c + * Win32 system() and popen() replacements + * + * + * Win32 needs double quotes at the beginning and end of system() + * strings. If not, it gets confused with multiple quoted strings. + * It also requires double-quotes around the executable name and + * any files used for redirection. Filter other args through + * appendShellString() to quote them. + * + * Generated using Win32 "CMD /?": + * + * 1. If all of the following conditions are met, then quote characters + * on the command line are preserved: + * + * - no /S switch + * - exactly two quote characters + * - no special characters between the two quote characters, where special + * is one of: &<>()@^| + * - there are one or more whitespace characters between the two quote + * characters + * - the string between the two quote characters is the name of an + * executable file. + * + * 2. Otherwise, old behavior is to see if the first character is a quote + * character and if so, strip the leading character and remove the last + * quote character on the command line, preserving any text after the last + * quote character. + * + * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * + * src/port/system.c + * + *------------------------------------------------------------------------- + */ +int a; + +#if defined(WIN32) && !defined(__CYGWIN__) + +#ifndef FRONTEND +#include "postgres.h" +#else +#include "postgres_fe.h" +#endif + +#include +#include + +#undef system +#undef popen + +int +pgwin32_system(const char *command) +{ + size_t cmdlen = strlen(command); + char *buf; + int save_errno; + int res; + + /* + * Create a malloc'd copy of the command string, enclosed with an extra + * pair of quotes + */ + buf = malloc(cmdlen + 2 + 1); + if (buf == NULL) + { + errno = ENOMEM; + return -1; + } + buf[0] = '"'; + memcpy(&buf[1], command, cmdlen); + buf[cmdlen + 1] = '"'; + buf[cmdlen + 2] = '\0'; + + res = system(buf); + + save_errno = errno; + free(buf); + errno = save_errno; + + return res; +} + + +FILE * +pgwin32_popen(const char *command, const char *type) +{ + size_t cmdlen = strlen(command); + char *buf; + int save_errno; + FILE *res; + + /* + * Create a malloc'd copy of the command string, enclosed with an extra + * pair of quotes + */ + buf = malloc(cmdlen + 2 + 1); + if (buf == NULL) + { + errno = ENOMEM; + return NULL; + } + buf[0] = '"'; + memcpy(&buf[1], command, cmdlen); + buf[cmdlen + 1] = '"'; + buf[cmdlen + 2] = '\0'; + + res = _popen(buf, type); + + save_errno = errno; + free(buf); + errno = save_errno; + + return res; +} + +#endif diff --git a/libpq/win32/win32.c b/libpq/win32/win32.c new file mode 100644 index 0000000..1b7ad37 --- /dev/null +++ b/libpq/win32/win32.c @@ -0,0 +1,327 @@ +/* + * src/interfaces/libpq/win32.c + * + * + * FILE + * win32.c + * + * DESCRIPTION + * Win32 support functions. + * + * Contains table and functions for looking up win32 socket error + * descriptions. But will/may contain other win32 helper functions + * for libpq. + * + * The error constants are taken from the Frambak Bakfram LGSOCKET + * library guys who in turn took them from the Winsock FAQ. + * + * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + */ + +/* Make stuff compile faster by excluding not used stuff */ + +#define VC_EXTRALEAN +#ifndef __MINGW32__ +#define NOGDI +#endif +#define NOCRYPT + +#include "postgres_fe.h" + +#include "win32.h" + +/* Declared here to avoid pulling in all includes, which causes name collisions */ +#ifdef ENABLE_NLS +extern char *libpq_gettext(const char *msgid) pg_attribute_format_arg(1); +#else +#define libpq_gettext(x) (x) +#endif + + +static struct WSErrorEntry +{ + DWORD error; + const char *description; +} WSErrors[] = + +{ + { + 0, "No error" + }, + { + WSAEINTR, "Interrupted system call" + }, + { + WSAEBADF, "Bad file number" + }, + { + WSAEACCES, "Permission denied" + }, + { + WSAEFAULT, "Bad address" + }, + { + WSAEINVAL, "Invalid argument" + }, + { + WSAEMFILE, "Too many open sockets" + }, + { + WSAEWOULDBLOCK, "Operation would block" + }, + { + WSAEINPROGRESS, "Operation now in progress" + }, + { + WSAEALREADY, "Operation already in progress" + }, + { + WSAENOTSOCK, "Socket operation on non-socket" + }, + { + WSAEDESTADDRREQ, "Destination address required" + }, + { + WSAEMSGSIZE, "Message too long" + }, + { + WSAEPROTOTYPE, "Protocol wrong type for socket" + }, + { + WSAENOPROTOOPT, "Bad protocol option" + }, + { + WSAEPROTONOSUPPORT, "Protocol not supported" + }, + { + WSAESOCKTNOSUPPORT, "Socket type not supported" + }, + { + WSAEOPNOTSUPP, "Operation not supported on socket" + }, + { + WSAEPFNOSUPPORT, "Protocol family not supported" + }, + { + WSAEAFNOSUPPORT, "Address family not supported" + }, + { + WSAEADDRINUSE, "Address already in use" + }, + { + WSAEADDRNOTAVAIL, "Cannot assign requested address" + }, + { + WSAENETDOWN, "Network is down" + }, + { + WSAENETUNREACH, "Network is unreachable" + }, + { + WSAENETRESET, "Net connection reset" + }, + { + WSAECONNABORTED, "Software caused connection abort" + }, + { + WSAECONNRESET, "Connection reset by peer" + }, + { + WSAENOBUFS, "No buffer space available" + }, + { + WSAEISCONN, "Socket is already connected" + }, + { + WSAENOTCONN, "Socket is not connected" + }, + { + WSAESHUTDOWN, "Cannot send after socket shutdown" + }, + { + WSAETOOMANYREFS, "Too many references, cannot splice" + }, + { + WSAETIMEDOUT, "Connection timed out" + }, + { + WSAECONNREFUSED, "Connection refused" + }, + { + WSAELOOP, "Too many levels of symbolic links" + }, + { + WSAENAMETOOLONG, "File name too long" + }, + { + WSAEHOSTDOWN, "Host is down" + }, + { + WSAEHOSTUNREACH, "No route to host" + }, + { + WSAENOTEMPTY, "Directory not empty" + }, + { + WSAEPROCLIM, "Too many processes" + }, + { + WSAEUSERS, "Too many users" + }, + { + WSAEDQUOT, "Disc quota exceeded" + }, + { + WSAESTALE, "Stale NFS file handle" + }, + { + WSAEREMOTE, "Too many levels of remote in path" + }, + { + WSASYSNOTREADY, "Network system is unavailable" + }, + { + WSAVERNOTSUPPORTED, "Winsock version out of range" + }, + { + WSANOTINITIALISED, "WSAStartup not yet called" + }, + { + WSAEDISCON, "Graceful shutdown in progress" + }, + { + WSAHOST_NOT_FOUND, "Host not found" + }, + { + WSATRY_AGAIN, "NA Host not found / SERVFAIL" + }, + { + WSANO_RECOVERY, "Non recoverable FORMERR||REFUSED||NOTIMP" + }, + { + WSANO_DATA, "No host data of that type was found" + }, + { + 0, 0 + } /* End of table */ +}; + + +/* + * Returns 0 if not found, linear but who cares, at this moment + * we're already in pain :) + */ + +static int +LookupWSErrorMessage(DWORD err, char *dest) +{ + struct WSErrorEntry *e; + + for (e = WSErrors; e->description; e++) + { + if (e->error == err) + { + strcpy(dest, e->description); + return 1; + } + } + return 0; +} + + +struct MessageDLL +{ + const char *dll_name; + void *handle; + int loaded; /* BOOL */ +} dlls[] = + +{ + { + "netmsg.dll", 0, 0 + }, + { + "winsock.dll", 0, 0 + }, + { + "ws2_32.dll", 0, 0 + }, + { + "wsock32n.dll", 0, 0 + }, + { + "mswsock.dll", 0, 0 + }, + { + "ws2help.dll", 0, 0 + }, + { + "ws2thk.dll", 0, 0 + }, + { + 0, 0, 1 + } /* Last one, no dll, always loaded */ +}; + +#define DLLS_SIZE (sizeof(dlls)/sizeof(struct MessageDLL)) + +/* + * Returns a description of the socket error by first trying + * to find it in the lookup table, and if that fails, tries + * to load any of the winsock dlls to find that message. + * The DLL thing works from Nt4 (spX ?) up, but some special + * versions of winsock might have this as well (seen on Win98 SE + * special install) / Magnus Naeslund (mag@fbab.net) + * + */ + +const char * +winsock_strerror(int err, char *strerrbuf, size_t buflen) +{ + unsigned long flags; + int offs, + i; + int success = LookupWSErrorMessage(err, strerrbuf); + + for (i = 0; !success && i < DLLS_SIZE; i++) + { + + if (!dlls[i].loaded) + { + dlls[i].loaded = 1; /* Only load once */ + dlls[i].handle = (void *) LoadLibraryEx( + dlls[i].dll_name, + 0, + LOAD_LIBRARY_AS_DATAFILE); + } + + if (dlls[i].dll_name && !dlls[i].handle) + continue; /* Didn't load */ + + flags = FORMAT_MESSAGE_FROM_SYSTEM + | FORMAT_MESSAGE_IGNORE_INSERTS + | (dlls[i].handle ? FORMAT_MESSAGE_FROM_HMODULE : 0); + + success = 0 != FormatMessage( + flags, + dlls[i].handle, err, + MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT), + strerrbuf, buflen - 64, + 0 + ); + } + + if (!success) + sprintf(strerrbuf, libpq_gettext("unrecognized socket error: 0x%08X/%d"), err, err); + else + { + strerrbuf[buflen - 1] = '\0'; + offs = strlen(strerrbuf); + if (offs > (int) buflen - 64) + offs = buflen - 64; + sprintf(strerrbuf + offs, " (0x%08X/%d)", err, err); + } + return strerrbuf; +} diff --git a/libpq/win32/win32.h b/libpq/win32/win32.h new file mode 100644 index 0000000..be00ea7 --- /dev/null +++ b/libpq/win32/win32.h @@ -0,0 +1,40 @@ +/* + * src/interfaces/libpq/win32.h + */ +#ifndef __win32_h_included +#define __win32_h_included + +/* + * Some compatibility functions + */ +#ifdef __BORLANDC__ +#define _timeb timeb +#define _ftime(a) ftime(a) +#define _errno errno +#define popen(a,b) _popen(a,b) +#else +/* open provided elsewhere */ +#define close(a) _close(a) +#define read(a,b,c) _read(a,b,c) +#define write(a,b,c) _write(a,b,c) +#endif + +#undef EAGAIN /* doesn't apply on sockets */ +#undef EINTR +#define EINTR WSAEINTR +#ifndef EWOULDBLOCK +#define EWOULDBLOCK WSAEWOULDBLOCK +#endif +#ifndef ECONNRESET +#define ECONNRESET WSAECONNRESET +#endif +#ifndef EINPROGRESS +#define EINPROGRESS WSAEINPROGRESS +#endif + +/* + * support for handling Windows Socket errors + */ +extern const char *winsock_strerror(int err, char *strerrbuf, size_t buflen); + +#endif diff --git a/libpq/win32/win32error.c b/libpq/win32/win32error.c new file mode 100644 index 0000000..cf65225 --- /dev/null +++ b/libpq/win32/win32error.c @@ -0,0 +1,206 @@ +/*------------------------------------------------------------------------- + * + * win32error.c + * Map win32 error codes to errno values + * + * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * + * IDENTIFICATION + * src/port/win32error.c + * + *------------------------------------------------------------------------- + */ + +#ifndef FRONTEND +#include "postgres.h" +#else +#include "postgres_fe.h" +#endif + +static const struct +{ + DWORD winerr; + int doserr; +} doserrors[] = + +{ + { + ERROR_INVALID_FUNCTION, EINVAL + }, + { + ERROR_FILE_NOT_FOUND, ENOENT + }, + { + ERROR_PATH_NOT_FOUND, ENOENT + }, + { + ERROR_TOO_MANY_OPEN_FILES, EMFILE + }, + { + ERROR_ACCESS_DENIED, EACCES + }, + { + ERROR_INVALID_HANDLE, EBADF + }, + { + ERROR_ARENA_TRASHED, ENOMEM + }, + { + ERROR_NOT_ENOUGH_MEMORY, ENOMEM + }, + { + ERROR_INVALID_BLOCK, ENOMEM + }, + { + ERROR_BAD_ENVIRONMENT, E2BIG + }, + { + ERROR_BAD_FORMAT, ENOEXEC + }, + { + ERROR_INVALID_ACCESS, EINVAL + }, + { + ERROR_INVALID_DATA, EINVAL + }, + { + ERROR_INVALID_DRIVE, ENOENT + }, + { + ERROR_CURRENT_DIRECTORY, EACCES + }, + { + ERROR_NOT_SAME_DEVICE, EXDEV + }, + { + ERROR_NO_MORE_FILES, ENOENT + }, + { + ERROR_LOCK_VIOLATION, EACCES + }, + { + ERROR_SHARING_VIOLATION, EACCES + }, + { + ERROR_BAD_NETPATH, ENOENT + }, + { + ERROR_NETWORK_ACCESS_DENIED, EACCES + }, + { + ERROR_BAD_NET_NAME, ENOENT + }, + { + ERROR_FILE_EXISTS, EEXIST + }, + { + ERROR_CANNOT_MAKE, EACCES + }, + { + ERROR_FAIL_I24, EACCES + }, + { + ERROR_INVALID_PARAMETER, EINVAL + }, + { + ERROR_NO_PROC_SLOTS, EAGAIN + }, + { + ERROR_DRIVE_LOCKED, EACCES + }, + { + ERROR_BROKEN_PIPE, EPIPE + }, + { + ERROR_DISK_FULL, ENOSPC + }, + { + ERROR_INVALID_TARGET_HANDLE, EBADF + }, + { + ERROR_INVALID_HANDLE, EINVAL + }, + { + ERROR_WAIT_NO_CHILDREN, ECHILD + }, + { + ERROR_CHILD_NOT_COMPLETE, ECHILD + }, + { + ERROR_DIRECT_ACCESS_HANDLE, EBADF + }, + { + ERROR_NEGATIVE_SEEK, EINVAL + }, + { + ERROR_SEEK_ON_DEVICE, EACCES + }, + { + ERROR_DIR_NOT_EMPTY, ENOTEMPTY + }, + { + ERROR_NOT_LOCKED, EACCES + }, + { + ERROR_BAD_PATHNAME, ENOENT + }, + { + ERROR_MAX_THRDS_REACHED, EAGAIN + }, + { + ERROR_LOCK_FAILED, EACCES + }, + { + ERROR_ALREADY_EXISTS, EEXIST + }, + { + ERROR_FILENAME_EXCED_RANGE, ENOENT + }, + { + ERROR_NESTING_NOT_ALLOWED, EAGAIN + }, + { + ERROR_NOT_ENOUGH_QUOTA, ENOMEM + } +}; + +void +_dosmaperr(unsigned long e) +{ + int i; + + if (e == 0) + { + errno = 0; + return; + } + + for (i = 0; i < lengthof(doserrors); i++) + { + if (doserrors[i].winerr == e) + { + int doserr = doserrors[i].doserr; + +#ifndef FRONTEND + ereport(DEBUG5, + (errmsg_internal("mapped win32 error code %lu to %d", + e, doserr))); +#elif FRONTEND_DEBUG + fprintf(stderr, "mapped win32 error code %lu to %d", e, doserr); +#endif + errno = doserr; + return; + } + } + +#ifndef FRONTEND + ereport(LOG, + (errmsg_internal("unrecognized win32 error code: %lu", + e))); +#else + fprintf(stderr, "unrecognized win32 error code: %lu", e); +#endif + + errno = EINVAL; + return; +} diff --git a/libpq/win32/win32setlocale.c b/libpq/win32/win32setlocale.c new file mode 100644 index 0000000..4abc7aa --- /dev/null +++ b/libpq/win32/win32setlocale.c @@ -0,0 +1,189 @@ +/*------------------------------------------------------------------------- + * + * win32setlocale.c + * Wrapper to work around bugs in Windows setlocale() implementation + * + * Copyright (c) 2011-2016, PostgreSQL Global Development Group + * + * IDENTIFICATION + * src/port/win32setlocale.c + * + * + * The setlocale() function in Windows is broken in two ways. First, it + * has a problem with locale names that have a dot in the country name. For + * example: + * + * "Chinese (Traditional)_Hong Kong S.A.R..950" + * + * For some reason, setlocale() doesn't accept that as argument, even though + * setlocale(LC_ALL, NULL) returns exactly that. Fortunately, it accepts + * various alternative names for such countries, so to work around the broken + * setlocale() function, we map the troublemaking locale names to accepted + * aliases, before calling setlocale(). + * + * The second problem is that the locale name for "Norwegian (Bokmål)" + * contains a non-ASCII character. That's problematic, because it's not clear + * what encoding the locale name itself is supposed to be in, when you + * haven't yet set a locale. Also, it causes problems when the cluster + * contains databases with different encodings, as the locale name is stored + * in the pg_database system catalog. To work around that, when setlocale() + * returns that locale name, map it to a pure-ASCII alias for the same + * locale. + *------------------------------------------------------------------------- + */ + +#include "c.h" + +#undef setlocale + +struct locale_map +{ + /* + * String in locale name to replace. Can be a single string (end is NULL), + * or separate start and end strings. If two strings are given, the locale + * name must contain both of them, and everything between them is + * replaced. This is used for a poor-man's regexp search, allowing + * replacement of "start.*end". + */ + const char *locale_name_start; + const char *locale_name_end; + + const char *replacement; /* string to replace the match with */ +}; + +/* + * Mappings applied before calling setlocale(), to the argument. + */ +static const struct locale_map locale_map_argument[] = { + /* + * "HKG" is listed here: + * http://msdn.microsoft.com/en-us/library/cdax410z%28v=vs.71%29.aspx + * (Country/Region Strings). + * + * "ARE" is the ISO-3166 three-letter code for U.A.E. It is not on the + * above list, but seems to work anyway. + */ + {"Hong Kong S.A.R.", NULL, "HKG"}, + {"U.A.E.", NULL, "ARE"}, + + /* + * The ISO-3166 country code for Macau S.A.R. is MAC, but Windows doesn't + * seem to recognize that. And Macau isn't listed in the table of accepted + * abbreviations linked above. Fortunately, "ZHM" seems to be accepted as + * an alias for "Chinese (Traditional)_Macau S.A.R..950". I'm not sure + * where "ZHM" comes from, must be some legacy naming scheme. But hey, it + * works. + * + * Note that unlike HKG and ARE, ZHM is an alias for the *whole* locale + * name, not just the country part. + * + * Some versions of Windows spell it "Macau", others "Macao". + */ + {"Chinese (Traditional)_Macau S.A.R..950", NULL, "ZHM"}, + {"Chinese_Macau S.A.R..950", NULL, "ZHM"}, + {"Chinese (Traditional)_Macao S.A.R..950", NULL, "ZHM"}, + {"Chinese_Macao S.A.R..950", NULL, "ZHM"}, + {NULL, NULL, NULL} +}; + +/* + * Mappings applied after calling setlocale(), to its return value. + */ +static const struct locale_map locale_map_result[] = { + /* + * "Norwegian (Bokmål)" locale name contains the a-ring character. + * Map it to a pure-ASCII alias. + * + * It's not clear what encoding setlocale() uses when it returns the + * locale name, so to play it safe, we search for "Norwegian (Bok*l)". + */ + {"Norwegian (Bokm", "l)_Norway", "Norwegian_Norway"}, + {NULL, NULL, NULL} +}; + +#define MAX_LOCALE_NAME_LEN 100 + +static const char * +map_locale(const struct locale_map * map, const char *locale) +{ + static char aliasbuf[MAX_LOCALE_NAME_LEN]; + int i; + + /* Check if the locale name matches any of the problematic ones. */ + for (i = 0; map[i].locale_name_start != NULL; i++) + { + const char *needle_start = map[i].locale_name_start; + const char *needle_end = map[i].locale_name_end; + const char *replacement = map[i].replacement; + char *match; + char *match_start = NULL; + char *match_end = NULL; + + match = strstr(locale, needle_start); + if (match) + { + /* + * Found a match for the first part. If this was a two-part + * replacement, find the second part. + */ + match_start = match; + if (needle_end) + { + match = strstr(match_start + strlen(needle_start), needle_end); + if (match) + match_end = match + strlen(needle_end); + else + match_start = NULL; + } + else + match_end = match_start + strlen(needle_start); + } + + if (match_start) + { + /* Found a match. Replace the matched string. */ + int matchpos = match_start - locale; + int replacementlen = strlen(replacement); + char *rest = match_end; + int restlen = strlen(rest); + + /* check that the result fits in the static buffer */ + if (matchpos + replacementlen + restlen + 1 > MAX_LOCALE_NAME_LEN) + return NULL; + + memcpy(&aliasbuf[0], &locale[0], matchpos); + memcpy(&aliasbuf[matchpos], replacement, replacementlen); + /* includes null terminator */ + memcpy(&aliasbuf[matchpos + replacementlen], rest, restlen + 1); + + return aliasbuf; + } + } + + /* no match, just return the original string */ + return locale; +} + +char * +pgwin32_setlocale(int category, const char *locale) +{ + const char *argument; + char *result; + + if (locale == NULL) + argument = NULL; + else + argument = map_locale(locale_map_argument, locale); + + /* Call the real setlocale() function */ + result = setlocale(category, argument); + + /* + * setlocale() is specified to return a "char *" that the caller is + * forbidden to modify, so casting away the "const" is innocuous. + */ + if (result) + result = (char *) map_locale(locale_map_result, result); + + return result; +} diff --git a/manifest b/manifest index 343bc84..ddeb36b 100644 --- a/manifest +++ b/manifest @@ -1,14 +1,20 @@ : 1 name: libpq -version: 0+3 + +# Note: remember to update doc-url below! +# +version: 9.6.5-a.0.z + summary: PostgreSQL C API client library license: PostgreSQL License; Permissive free software license. tags: postgresql, database, client, library, c, api, interface description-file: README url: https://www.postgresql.org/ +doc-url: https://www.postgresql.org/docs/9.6/static/libpq.html +src-url: https://git.build2.org/cgit/packaging/postgresql/libpq/tree/ package-url: https://git.build2.org/cgit/packaging/postgresql/ email: pgsql-general@postgresql.org; Mailing list. package-email: packaging@build2.org; Mailing list. build-email: builds@build2.org -depends: * build2 >= 0.5.0- -depends: * bpkg >= 0.5.0- +depends: * build2 >= 0.7.0- +depends: * bpkg >= 0.7.0- diff --git a/tests/.gitignore b/tests/.gitignore new file mode 100644 index 0000000..2e508a9 --- /dev/null +++ b/tests/.gitignore @@ -0,0 +1,3 @@ +driver +test/ +test-*/ diff --git a/tests/build/.gitignore b/tests/build/.gitignore new file mode 100644 index 0000000..225c27f --- /dev/null +++ b/tests/build/.gitignore @@ -0,0 +1 @@ +config.build diff --git a/tests/build/bootstrap.build b/tests/build/bootstrap.build new file mode 100644 index 0000000..3499bd0 --- /dev/null +++ b/tests/build/bootstrap.build @@ -0,0 +1,9 @@ +# file : tests/build/bootstrap.build +# copyright : Copyright (c) 2016-2017 Code Synthesis Ltd +# license : PostgreSQL License; see accompanying COPYRIGHT file + +project = # Unnamed subproject. + +using config +using dist +using test diff --git a/tests/build/root.build b/tests/build/root.build new file mode 100644 index 0000000..c48bb20 --- /dev/null +++ b/tests/build/root.build @@ -0,0 +1,21 @@ +# file : tests/build/root.build +# copyright : Copyright (c) 2016-2017 Code Synthesis Ltd +# license : PostgreSQL License; see accompanying COPYRIGHT file + +# Make sure libpq headers comply with an oldest C standard (see +# libpq/postgresql/pg_config_ext.h for details). +# +c.std = 90 + +using c + +h{*}: extension = h +c{*}: extension = c + +# Every exe{} in this subproject is by default a test. +# +exe{*}: test = true + +# Specify the test target for cross-testing. +# +test.target = $c.target diff --git a/tests/buildfile b/tests/buildfile new file mode 100644 index 0000000..8cbd5b1 --- /dev/null +++ b/tests/buildfile @@ -0,0 +1,5 @@ +# file : tests/buildfile +# copyright : Copyright (c) 2016-2017 Code Synthesis Ltd +# license : PostgreSQL License; see accompanying COPYRIGHT file + +./: {*/ -build/} diff --git a/tests/conninfo/buildfile b/tests/conninfo/buildfile new file mode 100644 index 0000000..3ba850b --- /dev/null +++ b/tests/conninfo/buildfile @@ -0,0 +1,13 @@ +# file : tests/conninfo/buildfile +# copyright : Copyright (c) 2016-2017 Code Synthesis Ltd +# license : PostgreSQL License; see accompanying COPYRIGHT file + +# Here we reproduce the original libpq test. See the src/interfaces/libpq/test +# directory of the original package for details. +# +import libs = libpq%lib{pq} + +exe{driver}: {h c}{* -uri-regress} $libs test{testscript} \ + file{uri-regress.c regress.in expected.out} + +c.poptions =+ "-I$src_base" diff --git a/tests/conninfo/driver.c b/tests/conninfo/driver.c new file mode 100644 index 0000000..2ff1007 --- /dev/null +++ b/tests/conninfo/driver.c @@ -0,0 +1,74 @@ +/* file : tests/conninfo/driver.c + * copyright : Copyright (c) 2016-2017 Code Synthesis Ltd + * license : PostgreSQL License; see accompanying COPYRIGHT file + */ + +/* + * Include the original package test and rename it's main() function to test() + * (see below for details). + */ +#define main test +#include +#undef main + +/* + * Enable assertions. + */ +#ifdef NDEBUG +# undef NDEBUG +#endif + +#include +#include +#include /* strlen() */ + +/* + * Usage: argv[0] + * + * Read connection info strings from STDIN and call original test main() + * function for each of them. The function prints the parsed connection info to + * stdout on success or error message to stderr on failure. + */ +int +main (int argc, char* argv[]) +{ + assert (argc == 1); + + char s[1024]; + + while (fgets (s, sizeof(s), stdin) != NULL) + { + /* + * Print the conninfo string that will be tested. + */ + printf ("trying %s", s); + + /* + * Strip the newline character and make sure it is printed to stdout. + */ + size_t n = strlen (s); + if (n != 0 && s[n - 1] == '\n') + s[n - 1] = '\0'; + else + printf ("\n"); + + /* + * Make sure the output make sense if stderr is redirected to stdout (and + * vice versa). + */ + fflush (stdout); + + /* + * Run the test. + * + * Note that we need to print the trailing newline character ourselves. + */ + char* args[] = {argv[0], s, NULL}; + int r = test (2, args); + + fprintf (r == 0 ? stdout : stderr, "\n"); + fflush (r == 0 ? stdout : stderr); + } + + return 0; +} diff --git a/tests/conninfo/expected.out b/tests/conninfo/expected.out new file mode 100644 index 0000000..d375e82 --- /dev/null +++ b/tests/conninfo/expected.out @@ -0,0 +1,171 @@ +trying postgresql://uri-user:secret@host:12345/db +user='uri-user' password='secret' dbname='db' host='host' port='12345' (inet) + +trying postgresql://uri-user@host:12345/db +user='uri-user' dbname='db' host='host' port='12345' (inet) + +trying postgresql://uri-user@host/db +user='uri-user' dbname='db' host='host' (inet) + +trying postgresql://host:12345/db +dbname='db' host='host' port='12345' (inet) + +trying postgresql://host/db +dbname='db' host='host' (inet) + +trying postgresql://uri-user@host:12345/ +user='uri-user' host='host' port='12345' (inet) + +trying postgresql://uri-user@host/ +user='uri-user' host='host' (inet) + +trying postgresql://uri-user@ +user='uri-user' (local) + +trying postgresql://host:12345/ +host='host' port='12345' (inet) + +trying postgresql://host:12345 +host='host' port='12345' (inet) + +trying postgresql://host/db +dbname='db' host='host' (inet) + +trying postgresql://host/ +host='host' (inet) + +trying postgresql://host +host='host' (inet) + +trying postgresql:// +(local) + +trying postgresql://?hostaddr=127.0.0.1 +hostaddr='127.0.0.1' (inet) + +trying postgresql://example.com?hostaddr=63.1.2.4 +host='example.com' hostaddr='63.1.2.4' (inet) + +trying postgresql://%68ost/ +host='host' (inet) + +trying postgresql://host/db?user=uri-user +user='uri-user' dbname='db' host='host' (inet) + +trying postgresql://host/db?user=uri-user&port=12345 +user='uri-user' dbname='db' host='host' port='12345' (inet) + +trying postgresql://host/db?u%73er=someotheruser&port=12345 +user='someotheruser' dbname='db' host='host' port='12345' (inet) + +trying postgresql://host/db?u%7aer=someotheruser&port=12345 +uri-regress: invalid URI query parameter: "uzer" + +trying postgresql://host:12345?user=uri-user +user='uri-user' host='host' port='12345' (inet) + +trying postgresql://host?user=uri-user +user='uri-user' host='host' (inet) + +trying postgresql://host? +host='host' (inet) + +trying postgresql://[::1]:12345/db +dbname='db' host='::1' port='12345' (inet) + +trying postgresql://[::1]/db +dbname='db' host='::1' (inet) + +trying postgresql://[2001:db8::1234]/ +host='2001:db8::1234' (inet) + +trying postgresql://[200z:db8::1234]/ +host='200z:db8::1234' (inet) + +trying postgresql://[::1] +host='::1' (inet) + +trying postgres:// +(local) + +trying postgres:/// +(local) + +trying postgres:///db +dbname='db' (local) + +trying postgres://uri-user@/db +user='uri-user' dbname='db' (local) + +trying postgres://?host=/path/to/socket/dir +host='/path/to/socket/dir' (local) + +trying postgresql://host?uzer= +uri-regress: invalid URI query parameter: "uzer" + +trying postgre:// +uri-regress: missing "=" after "postgre://" in connection info string + +trying postgres://[::1 +uri-regress: end of string reached when looking for matching "]" in IPv6 host address in URI: "postgres://[::1" + +trying postgres://[] +uri-regress: IPv6 host address may not be empty in URI: "postgres://[]" + +trying postgres://[::1]z +uri-regress: unexpected character "z" at position 17 in URI (expected ":" or "/"): "postgres://[::1]z" + +trying postgresql://host?zzz +uri-regress: missing key/value separator "=" in URI query parameter: "zzz" + +trying postgresql://host?value1&value2 +uri-regress: missing key/value separator "=" in URI query parameter: "value1" + +trying postgresql://host?key=key=value +uri-regress: extra key/value separator "=" in URI query parameter: "key" + +trying postgres://host?dbname=%XXfoo +uri-regress: invalid percent-encoded token: "%XXfoo" + +trying postgresql://a%00b +uri-regress: forbidden value %00 in percent-encoded value: "a%00b" + +trying postgresql://%zz +uri-regress: invalid percent-encoded token: "%zz" + +trying postgresql://%1 +uri-regress: invalid percent-encoded token: "%1" + +trying postgresql://% +uri-regress: invalid percent-encoded token: "%" + +trying postgres://@host +host='host' (inet) + +trying postgres://host:/ +host='host' (inet) + +trying postgres://:12345/ +port='12345' (local) + +trying postgres://otheruser@?host=/no/such/directory +user='otheruser' host='/no/such/directory' (local) + +trying postgres://otheruser@/?host=/no/such/directory +user='otheruser' host='/no/such/directory' (local) + +trying postgres://otheruser@:12345?host=/no/such/socket/path +user='otheruser' host='/no/such/socket/path' port='12345' (local) + +trying postgres://otheruser@:12345/db?host=/path/to/socket +user='otheruser' dbname='db' host='/path/to/socket' port='12345' (local) + +trying postgres://:12345/db?host=/path/to/socket +dbname='db' host='/path/to/socket' port='12345' (local) + +trying postgres://:12345?host=/path/to/socket +host='/path/to/socket' port='12345' (local) + +trying postgres://%2Fvar%2Flib%2Fpostgresql/dbname +dbname='dbname' host='/var/lib/postgresql' (local) + diff --git a/tests/conninfo/postgres_fe.h b/tests/conninfo/postgres_fe.h new file mode 100644 index 0000000..2a62d51 --- /dev/null +++ b/tests/conninfo/postgres_fe.h @@ -0,0 +1,19 @@ +/* file : tests/conninfo/postgres_fe.h + * copyright : Copyright (c) 2016-2017 Code Synthesis Ltd + * license : PostgreSQL License; see accompanying COPYRIGHT file + */ + +/* + * The original uri-regress.c includes src/include/postgres_fe.h that is + * located in libpq/postgresql/ directory in our source tree. This file is not + * installed, so to keep the test as a subproject and be able to test against + * the installed libpq library we replace it with the header stub, containing + * the bare minimum that is required the test to compile. + */ +#ifndef TESTS_CONNINFO_POSTGRES_FE_H +#define TESTS_CONNINFO_POSTGRES_FE_H + +#include +#include + +#endif /* TESTS_CONNINFO_POSTGRES_FE_H */ diff --git a/tests/conninfo/regress.in b/tests/conninfo/regress.in new file mode 100644 index 0000000..de034f3 --- /dev/null +++ b/tests/conninfo/regress.in @@ -0,0 +1,57 @@ +postgresql://uri-user:secret@host:12345/db +postgresql://uri-user@host:12345/db +postgresql://uri-user@host/db +postgresql://host:12345/db +postgresql://host/db +postgresql://uri-user@host:12345/ +postgresql://uri-user@host/ +postgresql://uri-user@ +postgresql://host:12345/ +postgresql://host:12345 +postgresql://host/db +postgresql://host/ +postgresql://host +postgresql:// +postgresql://?hostaddr=127.0.0.1 +postgresql://example.com?hostaddr=63.1.2.4 +postgresql://%68ost/ +postgresql://host/db?user=uri-user +postgresql://host/db?user=uri-user&port=12345 +postgresql://host/db?u%73er=someotheruser&port=12345 +postgresql://host/db?u%7aer=someotheruser&port=12345 +postgresql://host:12345?user=uri-user +postgresql://host?user=uri-user +postgresql://host? +postgresql://[::1]:12345/db +postgresql://[::1]/db +postgresql://[2001:db8::1234]/ +postgresql://[200z:db8::1234]/ +postgresql://[::1] +postgres:// +postgres:/// +postgres:///db +postgres://uri-user@/db +postgres://?host=/path/to/socket/dir +postgresql://host?uzer= +postgre:// +postgres://[::1 +postgres://[] +postgres://[::1]z +postgresql://host?zzz +postgresql://host?value1&value2 +postgresql://host?key=key=value +postgres://host?dbname=%XXfoo +postgresql://a%00b +postgresql://%zz +postgresql://%1 +postgresql://% +postgres://@host +postgres://host:/ +postgres://:12345/ +postgres://otheruser@?host=/no/such/directory +postgres://otheruser@/?host=/no/such/directory +postgres://otheruser@:12345?host=/no/such/socket/path +postgres://otheruser@:12345/db?host=/path/to/socket +postgres://:12345/db?host=/path/to/socket +postgres://:12345?host=/path/to/socket +postgres://%2Fvar%2Flib%2Fpostgresql/dbname diff --git a/tests/conninfo/testscript b/tests/conninfo/testscript new file mode 100644 index 0000000..9853029 --- /dev/null +++ b/tests/conninfo/testscript @@ -0,0 +1,5 @@ +# file : tests/conninfo/testscript +# copyright : Copyright (c) 2016-2017 Code Synthesis Ltd +# license : PostgreSQL License; see accompanying COPYRIGHT file + +$* <<<$src_base/regress.in >>>$src_base/expected.out 2>&1 diff --git a/tests/conninfo/uri-regress.c b/tests/conninfo/uri-regress.c new file mode 100644 index 0000000..bf1d970 --- /dev/null +++ b/tests/conninfo/uri-regress.c @@ -0,0 +1,84 @@ +/* + * uri-regress.c + * A test program for libpq URI format + * + * This is a helper for libpq conninfo regression testing. It takes a single + * conninfo string as a parameter, parses it using PQconninfoParse, and then + * prints out the values from the parsed PQconninfoOption struct that differ + * from the defaults (obtained from PQconndefaults). + * + * Portions Copyright (c) 2012-2016, PostgreSQL Global Development Group + * + * IDENTIFICATION + * src/interfaces/libpq/test/uri-regress.c + */ + +#include "postgres_fe.h" + +#include "libpq-fe.h" + +int +main(int argc, char *argv[]) +{ + PQconninfoOption *opts; + PQconninfoOption *defs; + PQconninfoOption *opt; + PQconninfoOption *def; + char *errmsg = NULL; + bool local = true; + + if (argc != 2) + return 1; + + opts = PQconninfoParse(argv[1], &errmsg); + if (opts == NULL) + { + fprintf(stderr, "uri-regress: %s", errmsg); + return 1; + } + + defs = PQconndefaults(); + if (defs == NULL) + { + fprintf(stderr, "uri-regress: cannot fetch default options\n"); + return 1; + } + + /* + * Loop on the options, and print the value of each if not the default. + * + * XXX this coding assumes that PQconninfoOption structs always have the + * keywords in the same order. + */ + for (opt = opts, def = defs; opt->keyword; ++opt, ++def) + { + if (opt->val != NULL) + { + if (def->val == NULL || strcmp(opt->val, def->val) != 0) + printf("%s='%s' ", opt->keyword, opt->val); + + /* + * Try to detect if this is a Unix-domain socket or inet. This is + * a bit grotty but it's the same thing that libpq itself does. + * + * Note that we directly test for '/' instead of using + * is_absolute_path, as that would be considerably more messy. + * This would fail on Windows, but that platform doesn't have + * Unix-domain sockets anyway. + */ + if (*opt->val && + (strcmp(opt->keyword, "hostaddr") == 0 || + (strcmp(opt->keyword, "host") == 0 && *opt->val != '/'))) + { + local = false; + } + } + } + + if (local) + printf("(local)\n"); + else + printf("(inet)\n"); + + return 0; +} -- cgit v1.1