diff options
author | Karen Arutyunov <karen@codesynthesis.com> | 2019-11-30 22:37:25 +0300 |
---|---|---|
committer | Karen Arutyunov <karen@codesynthesis.com> | 2019-12-06 15:11:04 +0300 |
commit | f1f39911e0d2d88c98eae96a3eb14a53c664206f (patch) | |
tree | 4cf4e3a84d895f59323d3b6ab4bfab38b3cab489 /libpq | |
parent | fc9499b8a7b7a3e350bfabf2cd6ae0bc13f04bea (diff) |
Upgrade to 12.1
Diffstat (limited to 'libpq')
161 files changed, 1400 insertions, 35314 deletions
diff --git a/libpq/.gitignore b/libpq/.gitignore index 620b4c8..cece09c 100644 --- a/libpq/.gitignore +++ b/libpq/.gitignore @@ -1,3 +1,19 @@ -# Generated version.h. +# Compiler/linker output. # -version.h +*.d +*.t +*.i +*.ii +*.o +*.obj +*.so +*.dll +*.a +*.lib +*.exp +*.pdb +*.ilk +*.exe +*.exe.dlls/ +*.exe.manifest +*.pc diff --git a/libpq/COPYRIGHT b/libpq/COPYRIGHT new file mode 120000 index 0000000..945f098 --- /dev/null +++ b/libpq/COPYRIGHT @@ -0,0 +1 @@ +../upstream/COPYRIGHT
\ No newline at end of file diff --git a/libpq/INSTALL b/libpq/INSTALL new file mode 100644 index 0000000..61ec557 --- /dev/null +++ b/libpq/INSTALL @@ -0,0 +1,7 @@ +The aim of this package is to make reading the INSTALL file unnecessary. So +next time try running: + +$ bpkg build libpq + +But if you don't want to use the package manager, then you can also build this +package manually using the standard build2 build system. diff --git a/libpq/README b/libpq/README new file mode 100644 index 0000000..f57e7a7 --- /dev/null +++ b/libpq/README @@ -0,0 +1,20 @@ +PostgreSQL is an object-relational SQL database management system with libpq +being its C client library. Applications can use this library to pass queries +to the PostgreSQL backend server and to receive the results of those queries +using the C programming language. For more information see: + +https://www.postgresql.org/ + +This package contains the original libpq library source code overlaid with the +build2-based build system and packaged for the build2 package manager (bpkg). + +See the INSTALL file for the prerequisites and installation instructions. + +Send questions, bug reports, or any other feedback about the library itself to +the PostgreSQL mailing lists. Send build system and packaging-related feedback +to the packaging@build2.org mailing list (see https://lists.build2.org for +posting guidelines, etc). + +The packaging of PostgreSQL for build2 is tracked in a Git repository at: + +https://git.build2.org/cgit/packaging/postgresql/ diff --git a/libpq/README-DEV b/libpq/README-DEV new file mode 100644 index 0000000..cadfe63 --- /dev/null +++ b/libpq/README-DEV @@ -0,0 +1,77 @@ +This document describes how libpq was packaged for build2. In particular, this +understanding will be useful when upgrading to a new upstream version. See +../README-DEV for general notes on PostgreSQL packaging. + +The upstream package builds the common and ports static libraries and links +them to the libpq library. These static libraries are generic enough to fit +all the binaries built by the package. We will build libpq selecting only the +required static libraries source files, relying on the linker diagnostics +(unresolved symbol errors, etc). + +Symlink the required upstream components and provide our own implementations +for auto-generated headers: + +$ ln -s ../upstream/COPYRIGHT + +Note that we symlink the libpq directory under the different name not to +confuse the headers auto-generating machinery while it maps the header +directory prefixes. + +$ ln -s ../../upstream/src/interfaces/libpq libpq/pq + +Note that while we could symlink the upstream's top source directories, this +would potentially bloat the distribution, uglify the buildfile, and complicate +pg_config.h change tracking on upgrade (see below). Thus, we selectively +symlink only the required files. + +$ mkdir libpq/mb +$ ln -s ../../../upstream/src/backend/utils/mb/{encnames,wchar}.c libpq/mb + +$ mkdir -p libpq/include/libpq libpq/include/mb libpq/include/port +$ ln -s ../../../upstream/src/include/{c,getaddrinfo,pg_config_manual,port,postgres_ext,postgres_fe}.h libpq/include +$ ln -s ../../../../upstream/src/include/libpq/{libpq-fs,pqcomm}.h libpq/include/libpq +$ ln -s ../../../../upstream/src/include/mb/pg_wchar.h libpq/include/mb +$ ln -s ../../../../upstream/src/include/port/{linux,freebsd,darwin,win32,win32_port,pg_bswap}.h libpq/include/port +$ ln -s ../../../../upstream/src/include/port/{win32,win32_msvc} libpq/include/port + +$ mkdir libpq/common +$ ln -s ../../../upstream/src/common/{md5,scram-common,ip,sha2_openssl,link-canary,base64,saslprep,unicode_norm}.c \ + libpq/common + +$ mkdir libpq/port +$ ln -s ../../../upstream/src/port/{pgstrcasecmp,snprintf,getpeereid,pg_strong_random,thread,strerror,chklocale,noblock,inet_net_ntop,strlcpy,win32setlocale,getaddrinfo,open,inet_aton,pgsleep,win32error,system}.c libpq/port +$ ln -s ../../../upstream/src/port/pthread-win32.h libpq/port + +$ ln -s ../../upstream/src/include/pg_config_ext.h.in libpq/pg_config_ext.h.in.orig +$ ln -s ../../upstream/src/include/pg_config_ext.h.win32 libpq/pg_config_ext.h.win32.orig +$ ln -s ../../upstream/src/include/pg_config.h.in libpq/pg_config.h.in.orig +$ ln -s ../../upstream/src/include/pg_config.h.win32 libpq/pg_config.h.win32.orig + +$ ln -s ../../../upstream/src/interfaces/libpq/test/uri-regress.c tests/conninfo + +Create libpq/{pg_config,pg_config_ext,pg_config_os,pg_config_paths}.h using as +a base the upstream's auto-generated headers and/or the corresponding +templates, makefiles, and perl/batch/shell scripts. + +Note that re-creating libpq/pg_config.h from scratch every time we upgrade to +a new upstream version would be a real pain. Instead we can only (un)define +the newly introduced macros, comparing the already defined and currently used +macro sets: + +$ for m in `cat libpq/pg_config.h.in.orig libpq/pg_config.h.win32.orig | \ + sed -n 's/.*#\s*\(define\|undef\)\s\{1,\}\([_A-Z0-9]\{1,\}\)\(\s.*\)\{0,1\}$/\2/p' | \ + sort -u`; do + if grep -q -e "\b$m\b" `find -L . -name '*.h' -a ! -name 'pg_config.h' -o -name '*.c'`; then + echo "$m" + fi + done >used-macros + +$ cat libpq/pg_config.h | \ + sed -n 's/#\s*\(define\|undef\)\s\{1,\}\([_A-Z0-9]\{1,\}\)\(\s.*\)\{0,1\}$/\2/p' | \ + sort -u >defined-macros + +$ diff defined-macros used-macros + +Copy upstream's auto-generated src/interfaces/libpq/{exports.list,libpqdll.def} +to libpq/{libpqdll.map,libpqdll.def} or create them manually based on +pq/exports.txt. Comment out the "LIBRARY LIBPQ" line in libpqdll.def. diff --git a/libpq/build/.gitignore b/libpq/build/.gitignore new file mode 100644 index 0000000..4a730a3 --- /dev/null +++ b/libpq/build/.gitignore @@ -0,0 +1,3 @@ +config.build +root/ +bootstrap/ diff --git a/libpq/build/bootstrap.build b/libpq/build/bootstrap.build new file mode 100644 index 0000000..22b180f --- /dev/null +++ b/libpq/build/bootstrap.build @@ -0,0 +1,40 @@ +# file : build/bootstrap.build +# copyright : Copyright (c) 2016-2019 Code Synthesis Ltd +# license : PostgreSQL License; see accompanying COPYRIGHT file + +project = libpq + +using version +using config +using dist +using test +using install + +# PostgreSQL releases (for quite a long time) had the 3-component versions, +# where the first 2 components denoted a major version and the third one -- +# the minor version. This has changed starting from version 10, with the major +# version represented by a single component. Minor releases are guaranteed to +# be backwards-compatible and contain only bug fixes. See also: +# +# https://www.postgresql.org/support/versioning/ +# +# Note that the release version is not a semantic version and we will map it to +# the standard version as <major>.<minor>.0. +# +# There is no document that describes libpq ABI versioning and compatibility +# rules, so everything that follows is implied from +# src/interfaces/libpq/Makefile. +# +# The library naming schema on Linux is libpq.so.<so_major>.<so_minor> +# (SO_MAJOR_VERSION and SO_MINOR_VERSION in the Makefile) So presumably +# so_major is incremented on backwards-incompatible ABI changes (it hasn't +# been for the several last major version releases). And so_minor is equal to +# the package major version. +# +if ($version.major == 12 && $version.minor == 1) +{ + abi_major = 5 + abi_minor = 12 +} +else + fail "increment the ABI version?" diff --git a/libpq/build/export.build b/libpq/build/export.build new file mode 100644 index 0000000..748c3ea --- /dev/null +++ b/libpq/build/export.build @@ -0,0 +1,10 @@ +# file : build/export.build +# copyright : Copyright (c) 2016-2019 Code Synthesis Ltd +# license : PostgreSQL License; see accompanying COPYRIGHT file + +$out_root/ +{ + include libpq/ +} + +export $out_root/libpq/lib{pq} diff --git a/libpq/build/root.build b/libpq/build/root.build new file mode 100644 index 0000000..76b1aa3 --- /dev/null +++ b/libpq/build/root.build @@ -0,0 +1,19 @@ +# file : build/root.build +# copyright : Copyright (c) 2016-2019 Code Synthesis Ltd +# license : PostgreSQL License; see accompanying COPYRIGHT file + +# We rely on this in macros/options deduction (see +# libpq/{buildfile,pg_config.h} for details). +# +c.std = 99 + +using c + +h{*}: extension = h +c{*}: extension = c + +if ($c.target.system == 'win32-msvc') + c.poptions += -D_CRT_SECURE_NO_WARNINGS -D_SCL_SECURE_NO_WARNINGS + +if ($c.class == 'msvc') + c.coptions += /wd4251 /wd4275 /wd4800 diff --git a/libpq/buildfile b/libpq/buildfile index 5a1732b..e2ceea2 100644 --- a/libpq/buildfile +++ b/libpq/buildfile @@ -1,188 +1,10 @@ -# file : libpq/buildfile +# file : buildfile # copyright : Copyright (c) 2016-2019 Code Synthesis Ltd # license : PostgreSQL Licenes; see accompanying COPYRIGHT file -# Headers other than these are not installed so treat them as files. -# -# @@ Make it postgresql/{postgres_ext.h pg_config_ext.h} when name pattern can -# be represented with a reversible name. -# -# @@ TODO: we should be able to redo it with install=true/false -# -h = version.h libpq-fe.h libpq-events.h \ - postgresql/postgres_ext.h postgresql/pg_config_ext.h - -lib{pq}: c{*} h{$h} file{**.h -{$h}} def{win32/libpqdll} \ - file{pg_service.conf.sample} - -tclass = $c.target.class - -bsd = ($tclass == 'bsd') -macos = ($tclass == 'macos') -windows = ($tclass == 'windows') - -msvc_runtime = ($c.target.system == 'win32-msvc') - -gcc = ($c.class == 'gcc') -msvc = ($c.class == 'msvc') - -lib{pq}: win32/c{*}: include = $windows -lib{pq}: non-bsd/c{*}: include = (($bsd || $macos) == false) +./: {*/ -build/} doc{COPYRIGHT INSTALL README} manifest -# The version file is an internal one (it is only included from -# postgresql/pg_config.h) so we don't distribute nor install it (see below). +# Don't install tests or the INSTALL file. # -h{version}: in{version} $src_root/manifest - -# Build options. -# -c.poptions += -DFRONTEND -DUNSAFE_STAT_OK -DSO_MAJOR_VERSION=$abi_major - -if! $windows - # Note that the original package uses -pthread compiler/linker option. It is - # currently unsupported by build2, so we use -D_REENTRANT and -lpthread - # preprocessor/linker options instead. We also omit -D_THREAD_SAFE (synonym - # for -D_REENTRANT) and Solaris-specific -D_POSIX_PTHREAD_SEMANTICS. - # - # @@ Maybe makes sense to support -pthread in build2, adding -lpthread - # to pkg-config's Libs.private and to cc.export.libs? - # - c.poptions += -D_REENTRANT -D_GNU_SOURCE -else - # Note that the original package defines the WIN32 macro for VC only, relying - # on the fact that MinGW GCC defines it by default. However, the macro - # disappears from the default ones if to compile with -std=c9x (as we do). So - # we define it for both VC and MinGW GCC. - # - # It's tempting to move this definition to libpq/postgresql/pg_config.h. - # However this header is not included into all files that use the macro, for - # example, libpq/win32/open.c. - # - c.poptions += -DWIN32 - -port_dir = ($windows ? "win32" : \ - $macos ? "darwin" : \ - $tclass) - -# Note that we add "-I$src_root" for the headers auto-generating machinery to -# work properly. -# -c.poptions =+ "-I$out_root" "-I$src_root" "-I$src_base" \ - "-I$src_base/postgresql/port/$port_dir" \ - "-I$src_base/postgresql" - -if $windows - c.poptions =+ "-I$src_base/win32" - -if $msvc_runtime -{ - c.poptions =+ "-I$src_base/postgresql/port/win32_msvc" - - c.poptions += -D_CRT_SECURE_NO_DEPRECATE -D_CRT_NONSTDC_NO_DEPRECATE - - # Disable warnings that pop up with /W3. - # - if $msvc - c.coptions += /wd4018 /wd4244 /wd4267 -} -elif $gcc -{ - # Omit -fexcess-precision=standard as -std=9x implies it. - # - c.coptions += -fno-strict-aliasing -fwrapv - - # Disable warnings that pop up with -Wall -Wextra. Upstream doesn't seem to - # care about these and it is not easy to disable specific warnings in a way - # that works across compilers/version (some -Wno-* options are only - # recognized in newer versions). - # - c.coptions += -Wno-all -Wno-extra -} - -# Define SYSCONFDIR macro. This path is used as a last resort for the -# pg_service.conf file search (see libpq/fe-connect.c for details). -# -# The whole idea feels utterly broken (hello cross-compilation) so we will -# just do bare minimum and wait and see. -# -# @@ We should probably allow to configure this macros via configuration -# variable config.libpq.sysconfdir. -# -if! $windows -{ - # For the original package if the resulted sysconfdir path doesn't contain - # the 'postgres' or 'pgsql' substring then the '/postgresql' suffix is - # automatically appended (see the original INSTALL file for details). Note - # that the same rule is applied for the datadir and docdir paths. Also if - # the root directory is /usr, then the resulting sysconfdir path is - # /etc/postgresql (rather than /usr/etc/postgresql). - # - # Let's do the same for the sysconfdir to increase the chance that libpq - # will find the configuration file. Note that we don't install anything at - # this path and don't amend the install.data and install.doc path variables. - # We also use the same default path as the original package. - # - if ($install.root != [null]) - { - root = $install.resolve($install.root) - sysconfdir = ($root != /usr ? $root/etc : /etc) - - if! $regex.match("$sysconfdir", '.*(pgsql|postgresql).*') - sysconfdir = $sysconfdir/postgresql - } - else - sysconfdir = /usr/local/pgsql/etc -} -else -{ - # win32.mak from the original package does this. - # - sysconfdir = "" -} - -# If we ever enable National Language Support (ENABLE_NLS macro) then we will -# need to define the LOCALEDIR macro as well. It refers to the locale data -# directory and should be $install.data/locale by default. We will also need -# to install this directory (see configure script --enable-nls options and the -# src/interfaces/libpq/po directory in the original package for details). -# -obj{fe-connect}: c.poptions += -DSYSCONFDIR="\"$sysconfdir\"" - -if! $windows - c.libs += -lpthread -else - # The original package also adds the resource file to the library. The file - # contains only the version information. First, libpq.rc is produced from - # libpq.rc.in with the following command: - # - # sed -e 's/\(VERSION.*\),0 *$/\1,'`date '+%y%j' | \ - # sed 's/^0*//'`'/' libpq.rc.in >libpq.rc - # - # Then libpq.rc is compiled with: - # - # windres -i libpq.rc -o libpqrc.o - # - # Afterwards libpqrc.o is linked to the library. - # - # @@ Currently we don't have support for the first two steps. - # - c.libs += $regex.apply(secur32 ws2_32 advapi32, \ - '(.+)', \ - $msvc_runtime ? '\1.lib' : '-l\1') - -# Export options. -# -lib{pq}: cc.export.poptions = "-I$src_base" "-I$src_base/postgresql" - -# See bootstrap.build for details. -# -if $version.pre_release - lib{pq}: bin.lib.version = @"-$version.project_id" -else - lib{pq}: bin.lib.version = @"-$abi_version" - -# Internal header (see above). -# -h{version}: install = false - -file{pg_service.conf.sample}@./: install = data/ +tests/: install = false +doc{INSTALL}@./: install = false diff --git a/libpq/chklocale.c b/libpq/chklocale.c deleted file mode 100644 index 3c0ef6a..0000000 --- a/libpq/chklocale.c +++ /dev/null @@ -1,445 +0,0 @@ -/*------------------------------------------------------------------------- - * - * chklocale.c - * Functions for handling locale-related info - * - * - * Copyright (c) 1996-2016, PostgreSQL Global Development Group - * - * - * IDENTIFICATION - * src/port/chklocale.c - * - *------------------------------------------------------------------------- - */ - -#ifndef FRONTEND -#include "postgres.h" -#else -#include "postgres_fe.h" -#endif - -#if defined(WIN32) && (_MSC_VER >= 1900) -#include <windows.h> -#endif - -#include <locale.h> -#ifdef HAVE_LANGINFO_H -#include <langinfo.h> -#endif - -#include "mb/pg_wchar.h" - - -/* - * This table needs to recognize all the CODESET spellings for supported - * backend encodings, as well as frontend-only encodings where possible - * (the latter case is currently only needed for initdb to recognize - * error situations). On Windows, we rely on entries for codepage - * numbers (CPnnn). - * - * Note that we search the table with pg_strcasecmp(), so variant - * capitalizations don't need their own entries. - */ -struct encoding_match -{ - enum pg_enc pg_enc_code; - const char *system_enc_name; -}; - -static const struct encoding_match encoding_match_list[] = { - {PG_EUC_JP, "EUC-JP"}, - {PG_EUC_JP, "eucJP"}, - {PG_EUC_JP, "IBM-eucJP"}, - {PG_EUC_JP, "sdeckanji"}, - {PG_EUC_JP, "CP20932"}, - - {PG_EUC_CN, "EUC-CN"}, - {PG_EUC_CN, "eucCN"}, - {PG_EUC_CN, "IBM-eucCN"}, - {PG_EUC_CN, "GB2312"}, - {PG_EUC_CN, "dechanzi"}, - {PG_EUC_CN, "CP20936"}, - - {PG_EUC_KR, "EUC-KR"}, - {PG_EUC_KR, "eucKR"}, - {PG_EUC_KR, "IBM-eucKR"}, - {PG_EUC_KR, "deckorean"}, - {PG_EUC_KR, "5601"}, - {PG_EUC_KR, "CP51949"}, - - {PG_EUC_TW, "EUC-TW"}, - {PG_EUC_TW, "eucTW"}, - {PG_EUC_TW, "IBM-eucTW"}, - {PG_EUC_TW, "cns11643"}, - /* No codepage for EUC-TW ? */ - - {PG_UTF8, "UTF-8"}, - {PG_UTF8, "utf8"}, - {PG_UTF8, "CP65001"}, - - {PG_LATIN1, "ISO-8859-1"}, - {PG_LATIN1, "ISO8859-1"}, - {PG_LATIN1, "iso88591"}, - {PG_LATIN1, "CP28591"}, - - {PG_LATIN2, "ISO-8859-2"}, - {PG_LATIN2, "ISO8859-2"}, - {PG_LATIN2, "iso88592"}, - {PG_LATIN2, "CP28592"}, - - {PG_LATIN3, "ISO-8859-3"}, - {PG_LATIN3, "ISO8859-3"}, - {PG_LATIN3, "iso88593"}, - {PG_LATIN3, "CP28593"}, - - {PG_LATIN4, "ISO-8859-4"}, - {PG_LATIN4, "ISO8859-4"}, - {PG_LATIN4, "iso88594"}, - {PG_LATIN4, "CP28594"}, - - {PG_LATIN5, "ISO-8859-9"}, - {PG_LATIN5, "ISO8859-9"}, - {PG_LATIN5, "iso88599"}, - {PG_LATIN5, "CP28599"}, - - {PG_LATIN6, "ISO-8859-10"}, - {PG_LATIN6, "ISO8859-10"}, - {PG_LATIN6, "iso885910"}, - - {PG_LATIN7, "ISO-8859-13"}, - {PG_LATIN7, "ISO8859-13"}, - {PG_LATIN7, "iso885913"}, - - {PG_LATIN8, "ISO-8859-14"}, - {PG_LATIN8, "ISO8859-14"}, - {PG_LATIN8, "iso885914"}, - - {PG_LATIN9, "ISO-8859-15"}, - {PG_LATIN9, "ISO8859-15"}, - {PG_LATIN9, "iso885915"}, - {PG_LATIN9, "CP28605"}, - - {PG_LATIN10, "ISO-8859-16"}, - {PG_LATIN10, "ISO8859-16"}, - {PG_LATIN10, "iso885916"}, - - {PG_KOI8R, "KOI8-R"}, - {PG_KOI8R, "CP20866"}, - - {PG_KOI8U, "KOI8-U"}, - {PG_KOI8U, "CP21866"}, - - {PG_WIN866, "CP866"}, - {PG_WIN874, "CP874"}, - {PG_WIN1250, "CP1250"}, - {PG_WIN1251, "CP1251"}, - {PG_WIN1251, "ansi-1251"}, - {PG_WIN1252, "CP1252"}, - {PG_WIN1253, "CP1253"}, - {PG_WIN1254, "CP1254"}, - {PG_WIN1255, "CP1255"}, - {PG_WIN1256, "CP1256"}, - {PG_WIN1257, "CP1257"}, - {PG_WIN1258, "CP1258"}, - - {PG_ISO_8859_5, "ISO-8859-5"}, - {PG_ISO_8859_5, "ISO8859-5"}, - {PG_ISO_8859_5, "iso88595"}, - {PG_ISO_8859_5, "CP28595"}, - - {PG_ISO_8859_6, "ISO-8859-6"}, - {PG_ISO_8859_6, "ISO8859-6"}, - {PG_ISO_8859_6, "iso88596"}, - {PG_ISO_8859_6, "CP28596"}, - - {PG_ISO_8859_7, "ISO-8859-7"}, - {PG_ISO_8859_7, "ISO8859-7"}, - {PG_ISO_8859_7, "iso88597"}, - {PG_ISO_8859_7, "CP28597"}, - - {PG_ISO_8859_8, "ISO-8859-8"}, - {PG_ISO_8859_8, "ISO8859-8"}, - {PG_ISO_8859_8, "iso88598"}, - {PG_ISO_8859_8, "CP28598"}, - - {PG_SJIS, "SJIS"}, - {PG_SJIS, "PCK"}, - {PG_SJIS, "CP932"}, - {PG_SJIS, "SHIFT_JIS"}, - - {PG_BIG5, "BIG5"}, - {PG_BIG5, "BIG5HKSCS"}, - {PG_BIG5, "Big5-HKSCS"}, - {PG_BIG5, "CP950"}, - - {PG_GBK, "GBK"}, - {PG_GBK, "CP936"}, - - {PG_UHC, "UHC"}, - {PG_UHC, "CP949"}, - - {PG_JOHAB, "JOHAB"}, - {PG_JOHAB, "CP1361"}, - - {PG_GB18030, "GB18030"}, - {PG_GB18030, "CP54936"}, - - {PG_SHIFT_JIS_2004, "SJIS_2004"}, - - {PG_SQL_ASCII, "US-ASCII"}, - - {PG_SQL_ASCII, NULL} /* end marker */ -}; - -#ifdef WIN32 -/* - * On Windows, use CP<code page number> instead of the nl_langinfo() result - * - * Visual Studio 2012 expanded the set of valid LC_CTYPE values, so have its - * locale machinery determine the code page. See comments at IsoLocaleName(). - * For other compilers, follow the locale's predictable format. - * - * Visual Studio 2015 should still be able to do the same, but the declaration - * of lc_codepage is missing in _locale_t, causing this code compilation to - * fail, hence this falls back instead on GetLocaleInfoEx. VS 2015 may be an - * exception and post-VS2015 versions should be able to handle properly the - * codepage number using _create_locale(). So, instead of the same logic as - * VS 2012 and VS 2013, this routine uses GetLocaleInfoEx to parse short - * locale names like "de-DE", "fr-FR", etc. If those cannot be parsed correctly - * process falls back to the pre-VS-2010 manual parsing done with - * using <Language>_<Country>.<CodePage> as a base. - * - * Returns a malloc()'d string for the caller to free. - */ -static char * -win32_langinfo(const char *ctype) -{ - char *r = NULL; - -#if (_MSC_VER >= 1700) && (_MSC_VER < 1900) - _locale_t loct = NULL; - - loct = _create_locale(LC_CTYPE, ctype); - if (loct != NULL) - { - r = malloc(16); /* excess */ - if (r != NULL) - sprintf(r, "CP%u", loct->locinfo->lc_codepage); - _free_locale(loct); - } -#else - char *codepage; - -#if (_MSC_VER >= 1900) - uint32 cp; - WCHAR wctype[LOCALE_NAME_MAX_LENGTH]; - - memset(wctype, 0, sizeof(wctype)); - MultiByteToWideChar(CP_ACP, 0, ctype, -1, wctype, LOCALE_NAME_MAX_LENGTH); - - if (GetLocaleInfoEx(wctype, - LOCALE_IDEFAULTANSICODEPAGE | LOCALE_RETURN_NUMBER, - (LPWSTR) &cp, sizeof(cp) / sizeof(WCHAR)) > 0) - { - r = malloc(16); /* excess */ - if (r != NULL) - sprintf(r, "CP%u", cp); - } - else -#endif - { - /* - * Locale format on Win32 is <Language>_<Country>.<CodePage> . For - * example, English_United States.1252. - */ - codepage = strrchr(ctype, '.'); - if (codepage != NULL) - { - int ln; - - codepage++; - ln = strlen(codepage); - r = malloc(ln + 3); - if (r != NULL) - sprintf(r, "CP%s", codepage); - } - - } -#endif - - return r; -} - -#ifndef FRONTEND -/* - * Given a Windows code page identifier, find the corresponding PostgreSQL - * encoding. Issue a warning and return -1 if none found. - */ -int -pg_codepage_to_encoding(UINT cp) -{ - char sys[16]; - int i; - - sprintf(sys, "CP%u", cp); - - /* Check the table */ - for (i = 0; encoding_match_list[i].system_enc_name; i++) - if (pg_strcasecmp(sys, encoding_match_list[i].system_enc_name) == 0) - return encoding_match_list[i].pg_enc_code; - - ereport(WARNING, - (errmsg("could not determine encoding for codeset \"%s\"", sys), - errdetail("Please report this to <pgsql-bugs@postgresql.org>."))); - - return -1; -} -#endif -#endif /* WIN32 */ - -#if (defined(HAVE_LANGINFO_H) && defined(CODESET)) || defined(WIN32) - -/* - * Given a setting for LC_CTYPE, return the Postgres ID of the associated - * encoding, if we can determine it. Return -1 if we can't determine it. - * - * Pass in NULL to get the encoding for the current locale setting. - * Pass "" to get the encoding selected by the server's environment. - * - * If the result is PG_SQL_ASCII, callers should treat it as being compatible - * with any desired encoding. - * - * If running in the backend and write_message is false, this function must - * cope with the possibility that elog() and palloc() are not yet usable. - */ -int -pg_get_encoding_from_locale(const char *ctype, bool write_message) -{ - char *sys; - int i; - - /* Get the CODESET property, and also LC_CTYPE if not passed in */ - if (ctype) - { - char *save; - char *name; - - /* If locale is C or POSIX, we can allow all encodings */ - if (pg_strcasecmp(ctype, "C") == 0 || - pg_strcasecmp(ctype, "POSIX") == 0) - return PG_SQL_ASCII; - - save = setlocale(LC_CTYPE, NULL); - if (!save) - return -1; /* setlocale() broken? */ - /* must copy result, or it might change after setlocale */ - save = strdup(save); - if (!save) - return -1; /* out of memory; unlikely */ - - name = setlocale(LC_CTYPE, ctype); - if (!name) - { - free(save); - return -1; /* bogus ctype passed in? */ - } - -#ifndef WIN32 - sys = nl_langinfo(CODESET); - if (sys) - sys = strdup(sys); -#else - sys = win32_langinfo(name); -#endif - - setlocale(LC_CTYPE, save); - free(save); - } - else - { - /* much easier... */ - ctype = setlocale(LC_CTYPE, NULL); - if (!ctype) - return -1; /* setlocale() broken? */ - - /* If locale is C or POSIX, we can allow all encodings */ - if (pg_strcasecmp(ctype, "C") == 0 || - pg_strcasecmp(ctype, "POSIX") == 0) - return PG_SQL_ASCII; - -#ifndef WIN32 - sys = nl_langinfo(CODESET); - if (sys) - sys = strdup(sys); -#else - sys = win32_langinfo(ctype); -#endif - } - - if (!sys) - return -1; /* out of memory; unlikely */ - - /* Check the table */ - for (i = 0; encoding_match_list[i].system_enc_name; i++) - { - if (pg_strcasecmp(sys, encoding_match_list[i].system_enc_name) == 0) - { - free(sys); - return encoding_match_list[i].pg_enc_code; - } - } - - /* Special-case kluges for particular platforms go here */ - -#ifdef __darwin__ - - /* - * Current OS X has many locales that report an empty string for CODESET, - * but they all seem to actually use UTF-8. - */ - if (strlen(sys) == 0) - { - free(sys); - return PG_UTF8; - } -#endif - - /* - * We print a warning if we got a CODESET string but couldn't recognize - * it. This means we need another entry in the table. - */ - if (write_message) - { -#ifdef FRONTEND - fprintf(stderr, _("could not determine encoding for locale \"%s\": codeset is \"%s\""), - ctype, sys); - /* keep newline separate so there's only one translatable string */ - fputc('\n', stderr); -#else - ereport(WARNING, - (errmsg("could not determine encoding for locale \"%s\": codeset is \"%s\"", - ctype, sys), - errdetail("Please report this to <pgsql-bugs@postgresql.org>."))); -#endif - } - - free(sys); - return -1; -} -#else /* (HAVE_LANGINFO_H && CODESET) || WIN32 */ - -/* - * stub if no multi-language platform support - * - * Note: we could return -1 here, but that would have the effect of - * forcing users to specify an encoding to initdb on such platforms. - * It seems better to silently default to SQL_ASCII. - */ -int -pg_get_encoding_from_locale(const char *ctype, bool write_message) -{ - return PG_SQL_ASCII; -} - -#endif /* (HAVE_LANGINFO_H && CODESET) || WIN32 */ diff --git a/libpq/encnames.c b/libpq/encnames.c deleted file mode 100644 index 11099b8..0000000 --- a/libpq/encnames.c +++ /dev/null @@ -1,553 +0,0 @@ -/* - * Encoding names and routines for work with it. All - * in this file is shared between FE and BE. - * - * src/backend/utils/mb/encnames.c - */ -#ifdef FRONTEND -#include "postgres_fe.h" -#else -#include "postgres.h" -#include "utils/builtins.h" -#endif - -#include <ctype.h> -#include <unistd.h> - -#include "mb/pg_wchar.h" - - -/* ---------- - * All encoding names, sorted: *** A L P H A B E T I C *** - * - * All names must be without irrelevant chars, search routines use - * isalnum() chars only. It means ISO-8859-1, iso_8859-1 and Iso8859_1 - * are always converted to 'iso88591'. All must be lower case. - * - * The table doesn't contain 'cs' aliases (like csISOLatin1). It's needed? - * - * Karel Zak, Aug 2001 - * ---------- - */ -typedef struct pg_encname -{ - const char *name; - pg_enc encoding; -} pg_encname; - -static const pg_encname pg_encname_tbl[] = -{ - { - "abc", PG_WIN1258 - }, /* alias for WIN1258 */ - { - "alt", PG_WIN866 - }, /* IBM866 */ - { - "big5", PG_BIG5 - }, /* Big5; Chinese for Taiwan multibyte set */ - { - "euccn", PG_EUC_CN - }, /* EUC-CN; Extended Unix Code for simplified - * Chinese */ - { - "eucjis2004", PG_EUC_JIS_2004 - }, /* EUC-JIS-2004; Extended UNIX Code fixed - * Width for Japanese, standard JIS X 0213 */ - { - "eucjp", PG_EUC_JP - }, /* EUC-JP; Extended UNIX Code fixed Width for - * Japanese, standard OSF */ - { - "euckr", PG_EUC_KR - }, /* EUC-KR; Extended Unix Code for Korean , KS - * X 1001 standard */ - { - "euctw", PG_EUC_TW - }, /* EUC-TW; Extended Unix Code for - * - * traditional Chinese */ - { - "gb18030", PG_GB18030 - }, /* GB18030;GB18030 */ - { - "gbk", PG_GBK - }, /* GBK; Chinese Windows CodePage 936 - * simplified Chinese */ - { - "iso88591", PG_LATIN1 - }, /* ISO-8859-1; RFC1345,KXS2 */ - { - "iso885910", PG_LATIN6 - }, /* ISO-8859-10; RFC1345,KXS2 */ - { - "iso885913", PG_LATIN7 - }, /* ISO-8859-13; RFC1345,KXS2 */ - { - "iso885914", PG_LATIN8 - }, /* ISO-8859-14; RFC1345,KXS2 */ - { - "iso885915", PG_LATIN9 - }, /* ISO-8859-15; RFC1345,KXS2 */ - { - "iso885916", PG_LATIN10 - }, /* ISO-8859-16; RFC1345,KXS2 */ - { - "iso88592", PG_LATIN2 - }, /* ISO-8859-2; RFC1345,KXS2 */ - { - "iso88593", PG_LATIN3 - }, /* ISO-8859-3; RFC1345,KXS2 */ - { - "iso88594", PG_LATIN4 - }, /* ISO-8859-4; RFC1345,KXS2 */ - { - "iso88595", PG_ISO_8859_5 - }, /* ISO-8859-5; RFC1345,KXS2 */ - { - "iso88596", PG_ISO_8859_6 - }, /* ISO-8859-6; RFC1345,KXS2 */ - { - "iso88597", PG_ISO_8859_7 - }, /* ISO-8859-7; RFC1345,KXS2 */ - { - "iso88598", PG_ISO_8859_8 - }, /* ISO-8859-8; RFC1345,KXS2 */ - { - "iso88599", PG_LATIN5 - }, /* ISO-8859-9; RFC1345,KXS2 */ - { - "johab", PG_JOHAB - }, /* JOHAB; Extended Unix Code for simplified - * Chinese */ - { - "koi8", PG_KOI8R - }, /* _dirty_ alias for KOI8-R (backward - * compatibility) */ - { - "koi8r", PG_KOI8R - }, /* KOI8-R; RFC1489 */ - { - "koi8u", PG_KOI8U - }, /* KOI8-U; RFC2319 */ - { - "latin1", PG_LATIN1 - }, /* alias for ISO-8859-1 */ - { - "latin10", PG_LATIN10 - }, /* alias for ISO-8859-16 */ - { - "latin2", PG_LATIN2 - }, /* alias for ISO-8859-2 */ - { - "latin3", PG_LATIN3 - }, /* alias for ISO-8859-3 */ - { - "latin4", PG_LATIN4 - }, /* alias for ISO-8859-4 */ - { - "latin5", PG_LATIN5 - }, /* alias for ISO-8859-9 */ - { - "latin6", PG_LATIN6 - }, /* alias for ISO-8859-10 */ - { - "latin7", PG_LATIN7 - }, /* alias for ISO-8859-13 */ - { - "latin8", PG_LATIN8 - }, /* alias for ISO-8859-14 */ - { - "latin9", PG_LATIN9 - }, /* alias for ISO-8859-15 */ - { - "mskanji", PG_SJIS - }, /* alias for Shift_JIS */ - { - "muleinternal", PG_MULE_INTERNAL - }, - { - "shiftjis", PG_SJIS - }, /* Shift_JIS; JIS X 0202-1991 */ - - { - "shiftjis2004", PG_SHIFT_JIS_2004 - }, /* SHIFT-JIS-2004; Shift JIS for Japanese, - * standard JIS X 0213 */ - { - "sjis", PG_SJIS - }, /* alias for Shift_JIS */ - { - "sqlascii", PG_SQL_ASCII - }, - { - "tcvn", PG_WIN1258 - }, /* alias for WIN1258 */ - { - "tcvn5712", PG_WIN1258 - }, /* alias for WIN1258 */ - { - "uhc", PG_UHC - }, /* UHC; Korean Windows CodePage 949 */ - { - "unicode", PG_UTF8 - }, /* alias for UTF8 */ - { - "utf8", PG_UTF8 - }, /* alias for UTF8 */ - { - "vscii", PG_WIN1258 - }, /* alias for WIN1258 */ - { - "win", PG_WIN1251 - }, /* _dirty_ alias for windows-1251 (backward - * compatibility) */ - { - "win1250", PG_WIN1250 - }, /* alias for Windows-1250 */ - { - "win1251", PG_WIN1251 - }, /* alias for Windows-1251 */ - { - "win1252", PG_WIN1252 - }, /* alias for Windows-1252 */ - { - "win1253", PG_WIN1253 - }, /* alias for Windows-1253 */ - { - "win1254", PG_WIN1254 - }, /* alias for Windows-1254 */ - { - "win1255", PG_WIN1255 - }, /* alias for Windows-1255 */ - { - "win1256", PG_WIN1256 - }, /* alias for Windows-1256 */ - { - "win1257", PG_WIN1257 - }, /* alias for Windows-1257 */ - { - "win1258", PG_WIN1258 - }, /* alias for Windows-1258 */ - { - "win866", PG_WIN866 - }, /* IBM866 */ - { - "win874", PG_WIN874 - }, /* alias for Windows-874 */ - { - "win932", PG_SJIS - }, /* alias for Shift_JIS */ - { - "win936", PG_GBK - }, /* alias for GBK */ - { - "win949", PG_UHC - }, /* alias for UHC */ - { - "win950", PG_BIG5 - }, /* alias for BIG5 */ - { - "windows1250", PG_WIN1250 - }, /* Windows-1251; Microsoft */ - { - "windows1251", PG_WIN1251 - }, /* Windows-1251; Microsoft */ - { - "windows1252", PG_WIN1252 - }, /* Windows-1252; Microsoft */ - { - "windows1253", PG_WIN1253 - }, /* Windows-1253; Microsoft */ - { - "windows1254", PG_WIN1254 - }, /* Windows-1254; Microsoft */ - { - "windows1255", PG_WIN1255 - }, /* Windows-1255; Microsoft */ - { - "windows1256", PG_WIN1256 - }, /* Windows-1256; Microsoft */ - { - "windows1257", PG_WIN1257 - }, /* Windows-1257; Microsoft */ - { - "windows1258", PG_WIN1258 - }, /* Windows-1258; Microsoft */ - { - "windows866", PG_WIN866 - }, /* IBM866 */ - { - "windows874", PG_WIN874 - }, /* Windows-874; Microsoft */ - { - "windows932", PG_SJIS - }, /* alias for Shift_JIS */ - { - "windows936", PG_GBK - }, /* alias for GBK */ - { - "windows949", PG_UHC - }, /* alias for UHC */ - { - "windows950", PG_BIG5 - } /* alias for BIG5 */ -}; - -/* ---------- - * These are "official" encoding names. - * XXX must be sorted by the same order as enum pg_enc (in mb/pg_wchar.h) - * ---------- - */ -#ifndef WIN32 -#define DEF_ENC2NAME(name, codepage) { #name, PG_##name } -#else -#define DEF_ENC2NAME(name, codepage) { #name, PG_##name, codepage } -#endif -const pg_enc2name pg_enc2name_tbl[] = -{ - DEF_ENC2NAME(SQL_ASCII, 0), - DEF_ENC2NAME(EUC_JP, 20932), - DEF_ENC2NAME(EUC_CN, 20936), - DEF_ENC2NAME(EUC_KR, 51949), - DEF_ENC2NAME(EUC_TW, 0), - DEF_ENC2NAME(EUC_JIS_2004, 20932), - DEF_ENC2NAME(UTF8, 65001), - DEF_ENC2NAME(MULE_INTERNAL, 0), - DEF_ENC2NAME(LATIN1, 28591), - DEF_ENC2NAME(LATIN2, 28592), - DEF_ENC2NAME(LATIN3, 28593), - DEF_ENC2NAME(LATIN4, 28594), - DEF_ENC2NAME(LATIN5, 28599), - DEF_ENC2NAME(LATIN6, 0), - DEF_ENC2NAME(LATIN7, 0), - DEF_ENC2NAME(LATIN8, 0), - DEF_ENC2NAME(LATIN9, 28605), - DEF_ENC2NAME(LATIN10, 0), - DEF_ENC2NAME(WIN1256, 1256), - DEF_ENC2NAME(WIN1258, 1258), - DEF_ENC2NAME(WIN866, 866), - DEF_ENC2NAME(WIN874, 874), - DEF_ENC2NAME(KOI8R, 20866), - DEF_ENC2NAME(WIN1251, 1251), - DEF_ENC2NAME(WIN1252, 1252), - DEF_ENC2NAME(ISO_8859_5, 28595), - DEF_ENC2NAME(ISO_8859_6, 28596), - DEF_ENC2NAME(ISO_8859_7, 28597), - DEF_ENC2NAME(ISO_8859_8, 28598), - DEF_ENC2NAME(WIN1250, 1250), - DEF_ENC2NAME(WIN1253, 1253), - DEF_ENC2NAME(WIN1254, 1254), - DEF_ENC2NAME(WIN1255, 1255), - DEF_ENC2NAME(WIN1257, 1257), - DEF_ENC2NAME(KOI8U, 21866), - DEF_ENC2NAME(SJIS, 932), - DEF_ENC2NAME(BIG5, 950), - DEF_ENC2NAME(GBK, 936), - DEF_ENC2NAME(UHC, 949), - DEF_ENC2NAME(GB18030, 54936), - DEF_ENC2NAME(JOHAB, 0), - DEF_ENC2NAME(SHIFT_JIS_2004, 932) -}; - -/* ---------- - * These are encoding names for gettext. - * - * This covers all encodings except MULE_INTERNAL, which is alien to gettext. - * ---------- - */ -const pg_enc2gettext pg_enc2gettext_tbl[] = -{ - {PG_SQL_ASCII, "US-ASCII"}, - {PG_UTF8, "UTF-8"}, - {PG_LATIN1, "LATIN1"}, - {PG_LATIN2, "LATIN2"}, - {PG_LATIN3, "LATIN3"}, - {PG_LATIN4, "LATIN4"}, - {PG_ISO_8859_5, "ISO-8859-5"}, - {PG_ISO_8859_6, "ISO_8859-6"}, - {PG_ISO_8859_7, "ISO-8859-7"}, - {PG_ISO_8859_8, "ISO-8859-8"}, - {PG_LATIN5, "LATIN5"}, - {PG_LATIN6, "LATIN6"}, - {PG_LATIN7, "LATIN7"}, - {PG_LATIN8, "LATIN8"}, - {PG_LATIN9, "LATIN-9"}, - {PG_LATIN10, "LATIN10"}, - {PG_KOI8R, "KOI8-R"}, - {PG_KOI8U, "KOI8-U"}, - {PG_WIN1250, "CP1250"}, - {PG_WIN1251, "CP1251"}, - {PG_WIN1252, "CP1252"}, - {PG_WIN1253, "CP1253"}, - {PG_WIN1254, "CP1254"}, - {PG_WIN1255, "CP1255"}, - {PG_WIN1256, "CP1256"}, - {PG_WIN1257, "CP1257"}, - {PG_WIN1258, "CP1258"}, - {PG_WIN866, "CP866"}, - {PG_WIN874, "CP874"}, - {PG_EUC_CN, "EUC-CN"}, - {PG_EUC_JP, "EUC-JP"}, - {PG_EUC_KR, "EUC-KR"}, - {PG_EUC_TW, "EUC-TW"}, - {PG_EUC_JIS_2004, "EUC-JP"}, - {PG_SJIS, "SHIFT-JIS"}, - {PG_BIG5, "BIG5"}, - {PG_GBK, "GBK"}, - {PG_UHC, "UHC"}, - {PG_GB18030, "GB18030"}, - {PG_JOHAB, "JOHAB"}, - {PG_SHIFT_JIS_2004, "SHIFT_JISX0213"}, - {0, NULL} -}; - - -/* ---------- - * Encoding checks, for error returns -1 else encoding id - * ---------- - */ -int -pg_valid_client_encoding(const char *name) -{ - int enc; - - if ((enc = pg_char_to_encoding(name)) < 0) - return -1; - - if (!PG_VALID_FE_ENCODING(enc)) - return -1; - - return enc; -} - -int -pg_valid_server_encoding(const char *name) -{ - int enc; - - if ((enc = pg_char_to_encoding(name)) < 0) - return -1; - - if (!PG_VALID_BE_ENCODING(enc)) - return -1; - - return enc; -} - -int -pg_valid_server_encoding_id(int encoding) -{ - return PG_VALID_BE_ENCODING(encoding); -} - -/* ---------- - * Remove irrelevant chars from encoding name - * ---------- - */ -static char * -clean_encoding_name(const char *key, char *newkey) -{ - const char *p; - char *np; - - for (p = key, np = newkey; *p != '\0'; p++) - { - if (isalnum((unsigned char) *p)) - { - if (*p >= 'A' && *p <= 'Z') - *np++ = *p + 'a' - 'A'; - else - *np++ = *p; - } - } - *np = '\0'; - return newkey; -} - -/* ---------- - * Search encoding by encoding name - * - * Returns encoding ID, or -1 for error - * ---------- - */ -int -pg_char_to_encoding(const char *name) -{ - unsigned int nel = lengthof(pg_encname_tbl); - const pg_encname *base = pg_encname_tbl, - *last = base + nel - 1, - *position; - int result; - char buff[NAMEDATALEN], - *key; - - if (name == NULL || *name == '\0') - return -1; - - if (strlen(name) >= NAMEDATALEN) - { -#ifdef FRONTEND - fprintf(stderr, "encoding name too long\n"); - return -1; -#else - ereport(ERROR, - (errcode(ERRCODE_NAME_TOO_LONG), - errmsg("encoding name too long"))); -#endif - } - key = clean_encoding_name(name, buff); - - while (last >= base) - { - position = base + ((last - base) >> 1); - result = key[0] - position->name[0]; - - if (result == 0) - { - result = strcmp(key, position->name); - if (result == 0) - return position->encoding; - } - if (result < 0) - last = position - 1; - else - base = position + 1; - } - return -1; -} - -#ifndef FRONTEND -Datum -PG_char_to_encoding(PG_FUNCTION_ARGS) -{ - Name s = PG_GETARG_NAME(0); - - PG_RETURN_INT32(pg_char_to_encoding(NameStr(*s))); -} -#endif - -const char * -pg_encoding_to_char(int encoding) -{ - if (PG_VALID_ENCODING(encoding)) - { - const pg_enc2name *p = &pg_enc2name_tbl[encoding]; - - Assert(encoding == p->encoding); - return p->name; - } - return ""; -} - -#ifndef FRONTEND -Datum -PG_encoding_to_char(PG_FUNCTION_ARGS) -{ - int32 encoding = PG_GETARG_INT32(0); - const char *encoding_name = pg_encoding_to_char(encoding); - - return DirectFunctionCall1(namein, CStringGetDatum(encoding_name)); -} - -#endif diff --git a/libpq/fe-auth.c b/libpq/fe-auth.c deleted file mode 100644 index ab057e9..0000000 --- a/libpq/fe-auth.c +++ /dev/null @@ -1,841 +0,0 @@ -/*------------------------------------------------------------------------- - * - * fe-auth.c - * The front-end (client) authorization routines - * - * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * IDENTIFICATION - * src/interfaces/libpq/fe-auth.c - * - *------------------------------------------------------------------------- - */ - -/* - * INTERFACE ROUTINES - * frontend (client) routines: - * pg_fe_sendauth send authentication information - * pg_fe_getauthname get user's name according to the client side - * of the authentication system - */ - -#include "postgres_fe.h" - -#ifdef WIN32 -#include "win32.h" -#else -#include <unistd.h> -#include <fcntl.h> -#include <sys/param.h> /* for MAXHOSTNAMELEN on most */ -#include <sys/socket.h> -#ifdef HAVE_SYS_UCRED_H -#include <sys/ucred.h> -#endif -#ifndef MAXHOSTNAMELEN -#include <netdb.h> /* for MAXHOSTNAMELEN on some */ -#endif -#include <pwd.h> -#endif - -#include "libpq-fe.h" -#include "fe-auth.h" -#include "libpq/md5.h" - - -#ifdef ENABLE_GSS -/* - * GSSAPI authentication system. - */ - -#if defined(WIN32) && !defined(WIN32_ONLY_COMPILER) -/* - * MIT Kerberos GSSAPI DLL doesn't properly export the symbols for MingW - * that contain the OIDs required. Redefine here, values copied - * from src/athena/auth/krb5/src/lib/gssapi/generic/gssapi_generic.c - */ -static const gss_OID_desc GSS_C_NT_HOSTBASED_SERVICE_desc = -{10, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x04"}; -static GSS_DLLIMP gss_OID GSS_C_NT_HOSTBASED_SERVICE = &GSS_C_NT_HOSTBASED_SERVICE_desc; -#endif - -/* - * Fetch all errors of a specific type and append to "str". - */ -static void -pg_GSS_error_int(PQExpBuffer str, const char *mprefix, - OM_uint32 stat, int type) -{ - OM_uint32 lmin_s; - gss_buffer_desc lmsg; - OM_uint32 msg_ctx = 0; - - do - { - gss_display_status(&lmin_s, stat, type, - GSS_C_NO_OID, &msg_ctx, &lmsg); - appendPQExpBuffer(str, "%s: %s\n", mprefix, (char *) lmsg.value); - gss_release_buffer(&lmin_s, &lmsg); - } while (msg_ctx); -} - -/* - * GSSAPI errors contain two parts; put both into conn->errorMessage. - */ -static void -pg_GSS_error(const char *mprefix, PGconn *conn, - OM_uint32 maj_stat, OM_uint32 min_stat) -{ - resetPQExpBuffer(&conn->errorMessage); - - /* Fetch major error codes */ - pg_GSS_error_int(&conn->errorMessage, mprefix, maj_stat, GSS_C_GSS_CODE); - - /* Add the minor codes as well */ - pg_GSS_error_int(&conn->errorMessage, mprefix, min_stat, GSS_C_MECH_CODE); -} - -/* - * Continue GSS authentication with next token as needed. - */ -static int -pg_GSS_continue(PGconn *conn) -{ - OM_uint32 maj_stat, - min_stat, - lmin_s; - - maj_stat = gss_init_sec_context(&min_stat, - GSS_C_NO_CREDENTIAL, - &conn->gctx, - conn->gtarg_nam, - GSS_C_NO_OID, - GSS_C_MUTUAL_FLAG, - 0, - GSS_C_NO_CHANNEL_BINDINGS, - (conn->gctx == GSS_C_NO_CONTEXT) ? GSS_C_NO_BUFFER : &conn->ginbuf, - NULL, - &conn->goutbuf, - NULL, - NULL); - - if (conn->gctx != GSS_C_NO_CONTEXT) - { - free(conn->ginbuf.value); - conn->ginbuf.value = NULL; - conn->ginbuf.length = 0; - } - - if (conn->goutbuf.length != 0) - { - /* - * GSS generated data to send to the server. We don't care if it's the - * first or subsequent packet, just send the same kind of password - * packet. - */ - if (pqPacketSend(conn, 'p', - conn->goutbuf.value, conn->goutbuf.length) - != STATUS_OK) - { - gss_release_buffer(&lmin_s, &conn->goutbuf); - return STATUS_ERROR; - } - } - gss_release_buffer(&lmin_s, &conn->goutbuf); - - if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED) - { - pg_GSS_error(libpq_gettext("GSSAPI continuation error"), - conn, - maj_stat, min_stat); - gss_release_name(&lmin_s, &conn->gtarg_nam); - if (conn->gctx) - gss_delete_sec_context(&lmin_s, &conn->gctx, GSS_C_NO_BUFFER); - return STATUS_ERROR; - } - - if (maj_stat == GSS_S_COMPLETE) - gss_release_name(&lmin_s, &conn->gtarg_nam); - - return STATUS_OK; -} - -/* - * Send initial GSS authentication token - */ -static int -pg_GSS_startup(PGconn *conn) -{ - OM_uint32 maj_stat, - min_stat; - int maxlen; - gss_buffer_desc temp_gbuf; - - if (!(conn->pghost && conn->pghost[0] != '\0')) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("host name must be specified\n")); - return STATUS_ERROR; - } - - if (conn->gctx) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("duplicate GSS authentication request\n")); - return STATUS_ERROR; - } - - /* - * Import service principal name so the proper ticket can be acquired by - * the GSSAPI system. - */ - maxlen = NI_MAXHOST + strlen(conn->krbsrvname) + 2; - temp_gbuf.value = (char *) malloc(maxlen); - if (!temp_gbuf.value) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("out of memory\n")); - return STATUS_ERROR; - } - snprintf(temp_gbuf.value, maxlen, "%s@%s", - conn->krbsrvname, conn->pghost); - temp_gbuf.length = strlen(temp_gbuf.value); - - maj_stat = gss_import_name(&min_stat, &temp_gbuf, - GSS_C_NT_HOSTBASED_SERVICE, &conn->gtarg_nam); - free(temp_gbuf.value); - - if (maj_stat != GSS_S_COMPLETE) - { - pg_GSS_error(libpq_gettext("GSSAPI name import error"), - conn, - maj_stat, min_stat); - return STATUS_ERROR; - } - - /* - * Initial packet is the same as a continuation packet with no initial - * context. - */ - conn->gctx = GSS_C_NO_CONTEXT; - - return pg_GSS_continue(conn); -} -#endif /* ENABLE_GSS */ - - -#ifdef ENABLE_SSPI -/* - * SSPI authentication system (Windows only) - */ - -static void -pg_SSPI_error(PGconn *conn, const char *mprefix, SECURITY_STATUS r) -{ - char sysmsg[256]; - - if (FormatMessage(FORMAT_MESSAGE_IGNORE_INSERTS | - FORMAT_MESSAGE_FROM_SYSTEM, - NULL, r, 0, - sysmsg, sizeof(sysmsg), NULL) == 0) - printfPQExpBuffer(&conn->errorMessage, "%s: SSPI error %x\n", - mprefix, (unsigned int) r); - else - printfPQExpBuffer(&conn->errorMessage, "%s: %s (%x)\n", - mprefix, sysmsg, (unsigned int) r); -} - -/* - * Continue SSPI authentication with next token as needed. - */ -static int -pg_SSPI_continue(PGconn *conn) -{ - SECURITY_STATUS r; - CtxtHandle newContext; - ULONG contextAttr; - SecBufferDesc inbuf; - SecBufferDesc outbuf; - SecBuffer OutBuffers[1]; - SecBuffer InBuffers[1]; - - if (conn->sspictx != NULL) - { - /* - * On runs other than the first we have some data to send. Put this - * data in a SecBuffer type structure. - */ - inbuf.ulVersion = SECBUFFER_VERSION; - inbuf.cBuffers = 1; - inbuf.pBuffers = InBuffers; - InBuffers[0].pvBuffer = conn->ginbuf.value; - InBuffers[0].cbBuffer = conn->ginbuf.length; - InBuffers[0].BufferType = SECBUFFER_TOKEN; - } - - OutBuffers[0].pvBuffer = NULL; - OutBuffers[0].BufferType = SECBUFFER_TOKEN; - OutBuffers[0].cbBuffer = 0; - outbuf.cBuffers = 1; - outbuf.pBuffers = OutBuffers; - outbuf.ulVersion = SECBUFFER_VERSION; - - r = InitializeSecurityContext(conn->sspicred, - conn->sspictx, - conn->sspitarget, - ISC_REQ_ALLOCATE_MEMORY, - 0, - SECURITY_NETWORK_DREP, - (conn->sspictx == NULL) ? NULL : &inbuf, - 0, - &newContext, - &outbuf, - &contextAttr, - NULL); - - if (r != SEC_E_OK && r != SEC_I_CONTINUE_NEEDED) - { - pg_SSPI_error(conn, libpq_gettext("SSPI continuation error"), r); - - return STATUS_ERROR; - } - - if (conn->sspictx == NULL) - { - /* On first run, transfer retrieved context handle */ - conn->sspictx = malloc(sizeof(CtxtHandle)); - if (conn->sspictx == NULL) - { - printfPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory\n")); - return STATUS_ERROR; - } - memcpy(conn->sspictx, &newContext, sizeof(CtxtHandle)); - } - else - { - /* - * On subsequent runs when we had data to send, free buffers that - * contained this data. - */ - free(conn->ginbuf.value); - conn->ginbuf.value = NULL; - conn->ginbuf.length = 0; - } - - /* - * If SSPI returned any data to be sent to the server (as it normally - * would), send this data as a password packet. - */ - if (outbuf.cBuffers > 0) - { - if (outbuf.cBuffers != 1) - { - /* - * This should never happen, at least not for Kerberos - * authentication. Keep check in case it shows up with other - * authentication methods later. - */ - printfPQExpBuffer(&conn->errorMessage, "SSPI returned invalid number of output buffers\n"); - return STATUS_ERROR; - } - - /* - * If the negotiation is complete, there may be zero bytes to send. - * The server is at this point not expecting any more data, so don't - * send it. - */ - if (outbuf.pBuffers[0].cbBuffer > 0) - { - if (pqPacketSend(conn, 'p', - outbuf.pBuffers[0].pvBuffer, outbuf.pBuffers[0].cbBuffer)) - { - FreeContextBuffer(outbuf.pBuffers[0].pvBuffer); - return STATUS_ERROR; - } - } - FreeContextBuffer(outbuf.pBuffers[0].pvBuffer); - } - - /* Cleanup is handled by the code in freePGconn() */ - return STATUS_OK; -} - -/* - * Send initial SSPI authentication token. - * If use_negotiate is 0, use kerberos authentication package which is - * compatible with Unix. If use_negotiate is 1, use the negotiate package - * which supports both kerberos and NTLM, but is not compatible with Unix. - */ -static int -pg_SSPI_startup(PGconn *conn, int use_negotiate) -{ - SECURITY_STATUS r; - TimeStamp expire; - - if (conn->sspictx) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("duplicate SSPI authentication request\n")); - return STATUS_ERROR; - } - - /* - * Retrieve credentials handle - */ - conn->sspicred = malloc(sizeof(CredHandle)); - if (conn->sspicred == NULL) - { - printfPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory\n")); - return STATUS_ERROR; - } - - r = AcquireCredentialsHandle(NULL, - use_negotiate ? "negotiate" : "kerberos", - SECPKG_CRED_OUTBOUND, - NULL, - NULL, - NULL, - NULL, - conn->sspicred, - &expire); - if (r != SEC_E_OK) - { - pg_SSPI_error(conn, libpq_gettext("could not acquire SSPI credentials"), r); - free(conn->sspicred); - conn->sspicred = NULL; - return STATUS_ERROR; - } - - /* - * Compute target principal name. SSPI has a different format from GSSAPI, - * but not more complex. We can skip the @REALM part, because Windows will - * fill that in for us automatically. - */ - if (!(conn->pghost && conn->pghost[0] != '\0')) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("host name must be specified\n")); - return STATUS_ERROR; - } - conn->sspitarget = malloc(strlen(conn->krbsrvname) + strlen(conn->pghost) + 2); - if (!conn->sspitarget) - { - printfPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory\n")); - return STATUS_ERROR; - } - sprintf(conn->sspitarget, "%s/%s", conn->krbsrvname, conn->pghost); - - /* - * Indicate that we're in SSPI authentication mode to make sure that - * pg_SSPI_continue is called next time in the negotiation. - */ - conn->usesspi = 1; - - return pg_SSPI_continue(conn); -} -#endif /* ENABLE_SSPI */ - -/* - * Respond to AUTH_REQ_SCM_CREDS challenge. - * - * Note: this is dead code as of Postgres 9.1, because current backends will - * never send this challenge. But we must keep it as long as libpq needs to - * interoperate with pre-9.1 servers. It is believed to be needed only on - * Debian/kFreeBSD (ie, FreeBSD kernel with Linux userland, so that the - * getpeereid() function isn't provided by libc). - */ -static int -pg_local_sendauth(PGconn *conn) -{ -#ifdef HAVE_STRUCT_CMSGCRED - char buf; - struct iovec iov; - struct msghdr msg; - struct cmsghdr *cmsg; - union - { - struct cmsghdr hdr; - unsigned char buf[CMSG_SPACE(sizeof(struct cmsgcred))]; - } cmsgbuf; - - /* - * The backend doesn't care what we send here, but it wants exactly one - * character to force recvmsg() to block and wait for us. - */ - buf = '\0'; - iov.iov_base = &buf; - iov.iov_len = 1; - - memset(&msg, 0, sizeof(msg)); - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - - /* We must set up a message that will be filled in by kernel */ - memset(&cmsgbuf, 0, sizeof(cmsgbuf)); - msg.msg_control = &cmsgbuf.buf; - msg.msg_controllen = sizeof(cmsgbuf.buf); - cmsg = CMSG_FIRSTHDR(&msg); - cmsg->cmsg_len = CMSG_LEN(sizeof(struct cmsgcred)); - cmsg->cmsg_level = SOL_SOCKET; - cmsg->cmsg_type = SCM_CREDS; - - if (sendmsg(conn->sock, &msg, 0) == -1) - { - char sebuf[256]; - - printfPQExpBuffer(&conn->errorMessage, - "pg_local_sendauth: sendmsg: %s\n", - pqStrerror(errno, sebuf, sizeof(sebuf))); - return STATUS_ERROR; - } - return STATUS_OK; -#else - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("SCM_CRED authentication method not supported\n")); - return STATUS_ERROR; -#endif -} - -static int -pg_password_sendauth(PGconn *conn, const char *password, AuthRequest areq) -{ - int ret; - char *crypt_pwd = NULL; - const char *pwd_to_send; - - /* Encrypt the password if needed. */ - - switch (areq) - { - case AUTH_REQ_MD5: - { - char *crypt_pwd2; - - /* Allocate enough space for two MD5 hashes */ - crypt_pwd = malloc(2 * (MD5_PASSWD_LEN + 1)); - if (!crypt_pwd) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("out of memory\n")); - return STATUS_ERROR; - } - - crypt_pwd2 = crypt_pwd + MD5_PASSWD_LEN + 1; - if (!pg_md5_encrypt(password, conn->pguser, - strlen(conn->pguser), crypt_pwd2)) - { - free(crypt_pwd); - return STATUS_ERROR; - } - if (!pg_md5_encrypt(crypt_pwd2 + strlen("md5"), conn->md5Salt, - sizeof(conn->md5Salt), crypt_pwd)) - { - free(crypt_pwd); - return STATUS_ERROR; - } - - pwd_to_send = crypt_pwd; - break; - } - case AUTH_REQ_PASSWORD: - pwd_to_send = password; - break; - default: - return STATUS_ERROR; - } - /* Packet has a message type as of protocol 3.0 */ - if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3) - ret = pqPacketSend(conn, 'p', pwd_to_send, strlen(pwd_to_send) + 1); - else - ret = pqPacketSend(conn, 0, pwd_to_send, strlen(pwd_to_send) + 1); - if (crypt_pwd) - free(crypt_pwd); - return ret; -} - -/* - * pg_fe_sendauth - * client demux routine for outgoing authentication information - */ -int -pg_fe_sendauth(AuthRequest areq, PGconn *conn) -{ - switch (areq) - { - case AUTH_REQ_OK: - break; - - case AUTH_REQ_KRB4: - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("Kerberos 4 authentication not supported\n")); - return STATUS_ERROR; - - case AUTH_REQ_KRB5: - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("Kerberos 5 authentication not supported\n")); - return STATUS_ERROR; - -#if defined(ENABLE_GSS) || defined(ENABLE_SSPI) - case AUTH_REQ_GSS: -#if !defined(ENABLE_SSPI) - /* no native SSPI, so use GSSAPI library for it */ - case AUTH_REQ_SSPI: -#endif - { - int r; - - pglock_thread(); - - /* - * If we have both GSS and SSPI support compiled in, use SSPI - * support by default. This is overridable by a connection - * string parameter. Note that when using SSPI we still leave - * the negotiate parameter off, since we want SSPI to use the - * GSSAPI kerberos protocol. For actual SSPI negotiate - * protocol, we use AUTH_REQ_SSPI. - */ -#if defined(ENABLE_GSS) && defined(ENABLE_SSPI) - if (conn->gsslib && (pg_strcasecmp(conn->gsslib, "gssapi") == 0)) - r = pg_GSS_startup(conn); - else - r = pg_SSPI_startup(conn, 0); -#elif defined(ENABLE_GSS) && !defined(ENABLE_SSPI) - r = pg_GSS_startup(conn); -#elif !defined(ENABLE_GSS) && defined(ENABLE_SSPI) - r = pg_SSPI_startup(conn, 0); -#endif - if (r != STATUS_OK) - { - /* Error message already filled in. */ - pgunlock_thread(); - return STATUS_ERROR; - } - pgunlock_thread(); - } - break; - - case AUTH_REQ_GSS_CONT: - { - int r; - - pglock_thread(); -#if defined(ENABLE_GSS) && defined(ENABLE_SSPI) - if (conn->usesspi) - r = pg_SSPI_continue(conn); - else - r = pg_GSS_continue(conn); -#elif defined(ENABLE_GSS) && !defined(ENABLE_SSPI) - r = pg_GSS_continue(conn); -#elif !defined(ENABLE_GSS) && defined(ENABLE_SSPI) - r = pg_SSPI_continue(conn); -#endif - if (r != STATUS_OK) - { - /* Error message already filled in. */ - pgunlock_thread(); - return STATUS_ERROR; - } - pgunlock_thread(); - } - break; -#else /* defined(ENABLE_GSS) || defined(ENABLE_SSPI) */ - /* No GSSAPI *or* SSPI support */ - case AUTH_REQ_GSS: - case AUTH_REQ_GSS_CONT: - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("GSSAPI authentication not supported\n")); - return STATUS_ERROR; -#endif /* defined(ENABLE_GSS) || defined(ENABLE_SSPI) */ - -#ifdef ENABLE_SSPI - case AUTH_REQ_SSPI: - - /* - * SSPI has it's own startup message so libpq can decide which - * method to use. Indicate to pg_SSPI_startup that we want SSPI - * negotiation instead of Kerberos. - */ - pglock_thread(); - if (pg_SSPI_startup(conn, 1) != STATUS_OK) - { - /* Error message already filled in. */ - pgunlock_thread(); - return STATUS_ERROR; - } - pgunlock_thread(); - break; -#else - - /* - * No SSPI support. However, if we have GSSAPI but not SSPI - * support, AUTH_REQ_SSPI will have been handled in the codepath - * for AUTH_REQ_GSSAPI above, so don't duplicate the case label in - * that case. - */ -#if !defined(ENABLE_GSS) - case AUTH_REQ_SSPI: - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("SSPI authentication not supported\n")); - return STATUS_ERROR; -#endif /* !define(ENABLE_GSSAPI) */ -#endif /* ENABLE_SSPI */ - - - case AUTH_REQ_CRYPT: - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("Crypt authentication not supported\n")); - return STATUS_ERROR; - - case AUTH_REQ_MD5: - case AUTH_REQ_PASSWORD: - conn->password_needed = true; - if (conn->pgpass == NULL || conn->pgpass[0] == '\0') - { - printfPQExpBuffer(&conn->errorMessage, - PQnoPasswordSupplied); - return STATUS_ERROR; - } - if (pg_password_sendauth(conn, conn->pgpass, areq) != STATUS_OK) - { - printfPQExpBuffer(&conn->errorMessage, - "fe_sendauth: error sending password authentication\n"); - return STATUS_ERROR; - } - break; - - case AUTH_REQ_SCM_CREDS: - if (pg_local_sendauth(conn) != STATUS_OK) - return STATUS_ERROR; - break; - - /* - * SASL authentication was introduced in version 10. Older - * versions recognize the request only to give a nicer error - * message. We call it "SCRAM authentication" in the error, rather - * than SASL, because SCRAM is more familiar to users, and it's - * the only SASL authentication mechanism that has been - * implemented as of this writing, anyway. - */ - case AUTH_REQ_SASL: - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("SCRAM authentication requires libpq version 10 or above\n")); - return STATUS_ERROR; - - default: - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("authentication method %u not supported\n"), areq); - return STATUS_ERROR; - } - - return STATUS_OK; -} - - -/* - * pg_fe_getauthname - * - * Returns a pointer to malloc'd space containing whatever name the user - * has authenticated to the system. If there is an error, return NULL, - * and put a suitable error message in *errorMessage if that's not NULL. - */ -char * -pg_fe_getauthname(PQExpBuffer errorMessage) -{ - char *result = NULL; - const char *name = NULL; - -#ifdef WIN32 - /* Microsoft recommends buffer size of UNLEN+1, where UNLEN = 256 */ - char username[256 + 1]; - DWORD namesize = sizeof(username); -#else - uid_t user_id = geteuid(); - char pwdbuf[BUFSIZ]; - struct passwd pwdstr; - struct passwd *pw = NULL; - int pwerr; -#endif - - /* - * Some users are using configure --enable-thread-safety-force, so we - * might as well do the locking within our library to protect - * pqGetpwuid(). In fact, application developers can use getpwuid() in - * their application if they use the locking call we provide, or install - * their own locking function using PQregisterThreadLock(). - */ - pglock_thread(); - -#ifdef WIN32 - if (GetUserName(username, &namesize)) - name = username; - else if (errorMessage) - printfPQExpBuffer(errorMessage, - libpq_gettext("user name lookup failure: error code %lu\n"), - GetLastError()); -#else - pwerr = pqGetpwuid(user_id, &pwdstr, pwdbuf, sizeof(pwdbuf), &pw); - if (pw != NULL) - name = pw->pw_name; - else if (errorMessage) - { - if (pwerr != 0) - printfPQExpBuffer(errorMessage, - libpq_gettext("could not look up local user ID %d: %s\n"), - (int) user_id, - pqStrerror(pwerr, pwdbuf, sizeof(pwdbuf))); - else - printfPQExpBuffer(errorMessage, - libpq_gettext("local user with ID %d does not exist\n"), - (int) user_id); - } -#endif - - if (name) - { - result = strdup(name); - if (result == NULL && errorMessage) - printfPQExpBuffer(errorMessage, - libpq_gettext("out of memory\n")); - } - - pgunlock_thread(); - - return result; -} - - -/* - * PQencryptPassword -- exported routine to encrypt a password - * - * This is intended to be used by client applications that wish to send - * commands like ALTER USER joe PASSWORD 'pwd'. The password need not - * be sent in cleartext if it is encrypted on the client side. This is - * good because it ensures the cleartext password won't end up in logs, - * pg_stat displays, etc. We export the function so that clients won't - * be dependent on low-level details like whether the encryption is MD5 - * or something else. - * - * Arguments are the cleartext password, and the SQL name of the user it - * is for. - * - * Return value is a malloc'd string, or NULL if out-of-memory. The client - * may assume the string doesn't contain any special characters that would - * require escaping. - */ -char * -PQencryptPassword(const char *passwd, const char *user) -{ - char *crypt_pwd; - - crypt_pwd = malloc(MD5_PASSWD_LEN + 1); - if (!crypt_pwd) - return NULL; - - if (!pg_md5_encrypt(passwd, user, strlen(user), crypt_pwd)) - { - free(crypt_pwd); - return NULL; - } - - return crypt_pwd; -} diff --git a/libpq/fe-auth.h b/libpq/fe-auth.h deleted file mode 100644 index 9d11654..0000000 --- a/libpq/fe-auth.h +++ /dev/null @@ -1,24 +0,0 @@ -/*------------------------------------------------------------------------- - * - * fe-auth.h - * - * Definitions for network authentication routines - * - * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * src/interfaces/libpq/fe-auth.h - * - *------------------------------------------------------------------------- - */ -#ifndef FE_AUTH_H -#define FE_AUTH_H - -#include "libpq-fe.h" -#include "libpq-int.h" - - -extern int pg_fe_sendauth(AuthRequest areq, PGconn *conn); -extern char *pg_fe_getauthname(PQExpBuffer errorMessage); - -#endif /* FE_AUTH_H */ diff --git a/libpq/fe-connect.c b/libpq/fe-connect.c deleted file mode 100644 index e61ed7d..0000000 --- a/libpq/fe-connect.c +++ /dev/null @@ -1,6002 +0,0 @@ -/*------------------------------------------------------------------------- - * - * fe-connect.c - * functions related to setting up a connection to the backend - * - * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * - * IDENTIFICATION - * src/interfaces/libpq/fe-connect.c - * - *------------------------------------------------------------------------- - */ - -#include "postgres_fe.h" - -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <ctype.h> -#include <time.h> -#include <unistd.h> - -#include "libpq-fe.h" -#include "libpq-int.h" -#include "fe-auth.h" -#include "pg_config_paths.h" - -#ifdef WIN32 -#include "win32.h" -#ifdef _WIN32_IE -#undef _WIN32_IE -#endif -#define _WIN32_IE 0x0500 -#ifdef near -#undef near -#endif -#define near -#include <shlobj.h> -#ifdef WIN32_ONLY_COMPILER /* mstcpip.h is missing on mingw */ -#include <mstcpip.h> -#endif -#else -#include <sys/socket.h> -#include <netdb.h> -#include <netinet/in.h> -#ifdef HAVE_NETINET_TCP_H -#include <netinet/tcp.h> -#endif -#include <arpa/inet.h> -#endif - -#ifdef ENABLE_THREAD_SAFETY -#ifdef WIN32 -#include "pthread-win32.h" -#else -#include <pthread.h> -#endif -#endif - -#ifdef USE_LDAP -#ifdef WIN32 -#include <winldap.h> -#else -/* OpenLDAP deprecates RFC 1823, but we want standard conformance */ -#define LDAP_DEPRECATED 1 -#include <ldap.h> -typedef struct timeval LDAP_TIMEVAL; -#endif -static int ldapServiceLookup(const char *purl, PQconninfoOption *options, - PQExpBuffer errorMessage); -#endif - -#include "libpq/ip.h" -#include "mb/pg_wchar.h" - -#ifndef FD_CLOEXEC -#define FD_CLOEXEC 1 -#endif - - -#ifndef WIN32 -#define PGPASSFILE ".pgpass" -#else -#define PGPASSFILE "pgpass.conf" -#endif - -/* - * Pre-9.0 servers will return this SQLSTATE if asked to set - * application_name in a startup packet. We hard-wire the value rather - * than looking into errcodes.h since it reflects historical behavior - * rather than that of the current code. - */ -#define ERRCODE_APPNAME_UNKNOWN "42704" - -/* This is part of the protocol so just define it */ -#define ERRCODE_INVALID_PASSWORD "28P01" -/* This too */ -#define ERRCODE_CANNOT_CONNECT_NOW "57P03" - -/* - * Cope with the various platform-specific ways to spell TCP keepalive socket - * options. This doesn't cover Windows, which as usual does its own thing. - */ -#if defined(TCP_KEEPIDLE) -/* TCP_KEEPIDLE is the name of this option on Linux and *BSD */ -#define PG_TCP_KEEPALIVE_IDLE TCP_KEEPIDLE -#define PG_TCP_KEEPALIVE_IDLE_STR "TCP_KEEPIDLE" -#elif defined(TCP_KEEPALIVE_THRESHOLD) -/* TCP_KEEPALIVE_THRESHOLD is the name of this option on Solaris >= 11 */ -#define PG_TCP_KEEPALIVE_IDLE TCP_KEEPALIVE_THRESHOLD -#define PG_TCP_KEEPALIVE_IDLE_STR "TCP_KEEPALIVE_THRESHOLD" -#elif defined(TCP_KEEPALIVE) && defined(__darwin__) -/* TCP_KEEPALIVE is the name of this option on macOS */ -/* Caution: Solaris has this symbol but it means something different */ -#define PG_TCP_KEEPALIVE_IDLE TCP_KEEPALIVE -#define PG_TCP_KEEPALIVE_IDLE_STR "TCP_KEEPALIVE" -#endif - -/* - * fall back options if they are not specified by arguments or defined - * by environment variables - */ -#define DefaultHost "localhost" -#define DefaultTty "" -#define DefaultOption "" -#define DefaultAuthtype "" -#define DefaultPassword "" -#ifdef USE_SSL -#define DefaultSSLMode "prefer" -#else -#define DefaultSSLMode "disable" -#endif - -/* ---------- - * Definition of the conninfo parameters and their fallback resources. - * - * If Environment-Var and Compiled-in are specified as NULL, no - * fallback is available. If after all no value can be determined - * for an option, an error is returned. - * - * The value for the username is treated specially in conninfo_add_defaults. - * If the value is not obtained any other way, the username is determined - * by pg_fe_getauthname(). - * - * The Label and Disp-Char entries are provided for applications that - * want to use PQconndefaults() to create a generic database connection - * dialog. Disp-Char is defined as follows: - * "" Normal input field - * "*" Password field - hide value - * "D" Debug option - don't show by default - * - * PQconninfoOptions[] is a constant static array that we use to initialize - * a dynamically allocated working copy. All the "val" fields in - * PQconninfoOptions[] *must* be NULL. In a working copy, non-null "val" - * fields point to malloc'd strings that should be freed when the working - * array is freed (see PQconninfoFree). - * - * The first part of each struct is identical to the one in libpq-fe.h, - * which is required since we memcpy() data between the two! - * ---------- - */ -typedef struct _internalPQconninfoOption -{ - char *keyword; /* The keyword of the option */ - char *envvar; /* Fallback environment variable name */ - char *compiled; /* Fallback compiled in default value */ - char *val; /* Option's current value, or NULL */ - char *label; /* Label for field in connect dialog */ - char *dispchar; /* Indicates how to display this field in a - * connect dialog. Values are: "" Display - * entered value as is "*" Password field - - * hide value "D" Debug option - don't show - * by default */ - int dispsize; /* Field size in characters for dialog */ - /* --- - * Anything above this comment must be synchronized with - * PQconninfoOption in libpq-fe.h, since we memcpy() data - * between them! - * --- - */ - off_t connofs; /* Offset into PGconn struct, -1 if not there */ -} internalPQconninfoOption; - -static const internalPQconninfoOption PQconninfoOptions[] = { - /* - * "authtype" is no longer used, so mark it "don't show". We keep it in - * the array so as not to reject conninfo strings from old apps that might - * still try to set it. - */ - {"authtype", "PGAUTHTYPE", DefaultAuthtype, NULL, - "Database-Authtype", "D", 20, -1}, - - {"service", "PGSERVICE", NULL, NULL, - "Database-Service", "", 20, -1}, - - {"user", "PGUSER", NULL, NULL, - "Database-User", "", 20, - offsetof(struct pg_conn, pguser)}, - - {"password", "PGPASSWORD", NULL, NULL, - "Database-Password", "*", 20, - offsetof(struct pg_conn, pgpass)}, - - {"connect_timeout", "PGCONNECT_TIMEOUT", NULL, NULL, - "Connect-timeout", "", 10, /* strlen(INT32_MAX) == 10 */ - offsetof(struct pg_conn, connect_timeout)}, - - {"dbname", "PGDATABASE", NULL, NULL, - "Database-Name", "", 20, - offsetof(struct pg_conn, dbName)}, - - {"host", "PGHOST", NULL, NULL, - "Database-Host", "", 40, - offsetof(struct pg_conn, pghost)}, - - {"hostaddr", "PGHOSTADDR", NULL, NULL, - "Database-Host-IP-Address", "", 45, - offsetof(struct pg_conn, pghostaddr)}, - - {"port", "PGPORT", DEF_PGPORT_STR, NULL, - "Database-Port", "", 6, - offsetof(struct pg_conn, pgport)}, - - {"client_encoding", "PGCLIENTENCODING", NULL, NULL, - "Client-Encoding", "", 10, - offsetof(struct pg_conn, client_encoding_initial)}, - - /* - * "tty" is no longer used either, but keep it present for backwards - * compatibility. - */ - {"tty", "PGTTY", DefaultTty, NULL, - "Backend-Debug-TTY", "D", 40, - offsetof(struct pg_conn, pgtty)}, - - {"options", "PGOPTIONS", DefaultOption, NULL, - "Backend-Debug-Options", "D", 40, - offsetof(struct pg_conn, pgoptions)}, - - {"application_name", "PGAPPNAME", NULL, NULL, - "Application-Name", "", 64, - offsetof(struct pg_conn, appname)}, - - {"fallback_application_name", NULL, NULL, NULL, - "Fallback-Application-Name", "", 64, - offsetof(struct pg_conn, fbappname)}, - - {"keepalives", NULL, NULL, NULL, - "TCP-Keepalives", "", 1, /* should be just '0' or '1' */ - offsetof(struct pg_conn, keepalives)}, - - {"keepalives_idle", NULL, NULL, NULL, - "TCP-Keepalives-Idle", "", 10, /* strlen(INT32_MAX) == 10 */ - offsetof(struct pg_conn, keepalives_idle)}, - - {"keepalives_interval", NULL, NULL, NULL, - "TCP-Keepalives-Interval", "", 10, /* strlen(INT32_MAX) == 10 */ - offsetof(struct pg_conn, keepalives_interval)}, - - {"keepalives_count", NULL, NULL, NULL, - "TCP-Keepalives-Count", "", 10, /* strlen(INT32_MAX) == 10 */ - offsetof(struct pg_conn, keepalives_count)}, - - /* - * ssl options are allowed even without client SSL support because the - * client can still handle SSL modes "disable" and "allow". Other - * parameters have no effect on non-SSL connections, so there is no reason - * to exclude them since none of them are mandatory. - */ - {"sslmode", "PGSSLMODE", DefaultSSLMode, NULL, - "SSL-Mode", "", 12, /* sizeof("verify-full") == 12 */ - offsetof(struct pg_conn, sslmode)}, - - {"sslcompression", "PGSSLCOMPRESSION", "1", NULL, - "SSL-Compression", "", 1, - offsetof(struct pg_conn, sslcompression)}, - - {"sslcert", "PGSSLCERT", NULL, NULL, - "SSL-Client-Cert", "", 64, - offsetof(struct pg_conn, sslcert)}, - - {"sslkey", "PGSSLKEY", NULL, NULL, - "SSL-Client-Key", "", 64, - offsetof(struct pg_conn, sslkey)}, - - {"sslrootcert", "PGSSLROOTCERT", NULL, NULL, - "SSL-Root-Certificate", "", 64, - offsetof(struct pg_conn, sslrootcert)}, - - {"sslcrl", "PGSSLCRL", NULL, NULL, - "SSL-Revocation-List", "", 64, - offsetof(struct pg_conn, sslcrl)}, - - {"requirepeer", "PGREQUIREPEER", NULL, NULL, - "Require-Peer", "", 10, - offsetof(struct pg_conn, requirepeer)}, - -#if defined(ENABLE_GSS) || defined(ENABLE_SSPI) - /* Kerberos and GSSAPI authentication support specifying the service name */ - {"krbsrvname", "PGKRBSRVNAME", PG_KRB_SRVNAM, NULL, - "Kerberos-service-name", "", 20, - offsetof(struct pg_conn, krbsrvname)}, -#endif - -#if defined(ENABLE_GSS) && defined(ENABLE_SSPI) - - /* - * GSSAPI and SSPI both enabled, give a way to override which is used by - * default - */ - {"gsslib", "PGGSSLIB", NULL, NULL, - "GSS-library", "", 7, /* sizeof("gssapi") = 7 */ - offsetof(struct pg_conn, gsslib)}, -#endif - - {"replication", NULL, NULL, NULL, - "Replication", "D", 5, - offsetof(struct pg_conn, replication)}, - - /* Terminating entry --- MUST BE LAST */ - {NULL, NULL, NULL, NULL, - NULL, NULL, 0} -}; - -static const PQEnvironmentOption EnvironmentOptions[] = -{ - /* common user-interface settings */ - { - "PGDATESTYLE", "datestyle" - }, - { - "PGTZ", "timezone" - }, - /* internal performance-related settings */ - { - "PGGEQO", "geqo" - }, - { - NULL, NULL - } -}; - -/* The connection URI must start with either of the following designators: */ -static const char uri_designator[] = "postgresql://"; -static const char short_uri_designator[] = "postgres://"; - -static bool connectOptions1(PGconn *conn, const char *conninfo); -static bool connectOptions2(PGconn *conn); -static int connectDBStart(PGconn *conn); -static int connectDBComplete(PGconn *conn); -static PGPing internal_ping(PGconn *conn); -static PGconn *makeEmptyPGconn(void); -static bool fillPGconn(PGconn *conn, PQconninfoOption *connOptions); -static void freePGconn(PGconn *conn); -static void closePGconn(PGconn *conn); -static PQconninfoOption *conninfo_init(PQExpBuffer errorMessage); -static PQconninfoOption *parse_connection_string(const char *conninfo, - PQExpBuffer errorMessage, bool use_defaults); -static int uri_prefix_length(const char *connstr); -static bool recognized_connection_string(const char *connstr); -static PQconninfoOption *conninfo_parse(const char *conninfo, - PQExpBuffer errorMessage, bool use_defaults); -static PQconninfoOption *conninfo_array_parse(const char *const * keywords, - const char *const * values, PQExpBuffer errorMessage, - bool use_defaults, int expand_dbname); -static bool conninfo_add_defaults(PQconninfoOption *options, - PQExpBuffer errorMessage); -static PQconninfoOption *conninfo_uri_parse(const char *uri, - PQExpBuffer errorMessage, bool use_defaults); -static bool conninfo_uri_parse_options(PQconninfoOption *options, - const char *uri, PQExpBuffer errorMessage); -static bool conninfo_uri_parse_params(char *params, - PQconninfoOption *connOptions, - PQExpBuffer errorMessage); -static char *conninfo_uri_decode(const char *str, PQExpBuffer errorMessage); -static bool get_hexdigit(char digit, int *value); -static const char *conninfo_getval(PQconninfoOption *connOptions, - const char *keyword); -static PQconninfoOption *conninfo_storeval(PQconninfoOption *connOptions, - const char *keyword, const char *value, - PQExpBuffer errorMessage, bool ignoreMissing, bool uri_decode); -static PQconninfoOption *conninfo_find(PQconninfoOption *connOptions, - const char *keyword); -static void defaultNoticeReceiver(void *arg, const PGresult *res); -static void defaultNoticeProcessor(void *arg, const char *message); -static int parseServiceInfo(PQconninfoOption *options, - PQExpBuffer errorMessage); -static int parseServiceFile(const char *serviceFile, - const char *service, - PQconninfoOption *options, - PQExpBuffer errorMessage, - bool *group_found); -static char *pwdfMatchesString(char *buf, char *token); -static char *PasswordFromFile(char *hostname, char *port, char *dbname, - char *username); -static bool getPgPassFilename(char *pgpassfile); -static void dot_pg_pass_warning(PGconn *conn); -static void default_threadlock(int acquire); - - -/* global variable because fe-auth.c needs to access it */ -pgthreadlock_t pg_g_threadlock = default_threadlock; - - -/* - * pqDropConnection - * - * Close any physical connection to the server, and reset associated - * state inside the connection object. We don't release state that - * would be needed to reconnect, though. - * - * We can always flush the output buffer, since there's no longer any hope - * of sending that data. However, unprocessed input data might still be - * valuable, so the caller must tell us whether to flush that or not. - */ -void -pqDropConnection(PGconn *conn, bool flushInput) -{ - /* Drop any SSL state */ - pqsecure_close(conn); - - /* Close the socket itself */ - if (conn->sock != PGINVALID_SOCKET) - closesocket(conn->sock); - conn->sock = PGINVALID_SOCKET; - - /* Optionally discard any unread data */ - if (flushInput) - conn->inStart = conn->inCursor = conn->inEnd = 0; - - /* Always discard any unsent data */ - conn->outCount = 0; - - /* Free authentication state */ -#ifdef ENABLE_GSS - { - OM_uint32 min_s; - - if (conn->gctx) - gss_delete_sec_context(&min_s, &conn->gctx, GSS_C_NO_BUFFER); - if (conn->gtarg_nam) - gss_release_name(&min_s, &conn->gtarg_nam); - if (conn->ginbuf.length) - gss_release_buffer(&min_s, &conn->ginbuf); - if (conn->goutbuf.length) - gss_release_buffer(&min_s, &conn->goutbuf); - } -#endif -#ifdef ENABLE_SSPI - if (conn->ginbuf.length) - free(conn->ginbuf.value); - conn->ginbuf.length = 0; - conn->ginbuf.value = NULL; - if (conn->sspitarget) - free(conn->sspitarget); - conn->sspitarget = NULL; - if (conn->sspicred) - { - FreeCredentialsHandle(conn->sspicred); - free(conn->sspicred); - conn->sspicred = NULL; - } - if (conn->sspictx) - { - DeleteSecurityContext(conn->sspictx); - free(conn->sspictx); - conn->sspictx = NULL; - } - conn->usesspi = 0; -#endif -} - - -/* - * Connecting to a Database - * - * There are now six different ways a user of this API can connect to the - * database. Two are not recommended for use in new code, because of their - * lack of extensibility with respect to the passing of options to the - * backend. These are PQsetdb and PQsetdbLogin (the former now being a macro - * to the latter). - * - * If it is desired to connect in a synchronous (blocking) manner, use the - * function PQconnectdb or PQconnectdbParams. The former accepts a string of - * option = value pairs (or a URI) which must be parsed; the latter takes two - * NULL terminated arrays instead. - * - * To connect in an asynchronous (non-blocking) manner, use the functions - * PQconnectStart or PQconnectStartParams (which differ in the same way as - * PQconnectdb and PQconnectdbParams) and PQconnectPoll. - * - * Internally, the static functions connectDBStart, connectDBComplete - * are part of the connection procedure. - */ - -/* - * PQconnectdbParams - * - * establishes a connection to a postgres backend through the postmaster - * using connection information in two arrays. - * - * The keywords array is defined as - * - * const char *params[] = {"option1", "option2", NULL} - * - * The values array is defined as - * - * const char *values[] = {"value1", "value2", NULL} - * - * Returns a PGconn* which is needed for all subsequent libpq calls, or NULL - * if a memory allocation failed. - * If the status field of the connection returned is CONNECTION_BAD, - * then some fields may be null'ed out instead of having valid values. - * - * You should call PQfinish (if conn is not NULL) regardless of whether this - * call succeeded. - */ -PGconn * -PQconnectdbParams(const char *const * keywords, - const char *const * values, - int expand_dbname) -{ - PGconn *conn = PQconnectStartParams(keywords, values, expand_dbname); - - if (conn && conn->status != CONNECTION_BAD) - (void) connectDBComplete(conn); - - return conn; - -} - -/* - * PQpingParams - * - * check server status, accepting parameters identical to PQconnectdbParams - */ -PGPing -PQpingParams(const char *const * keywords, - const char *const * values, - int expand_dbname) -{ - PGconn *conn = PQconnectStartParams(keywords, values, expand_dbname); - PGPing ret; - - ret = internal_ping(conn); - PQfinish(conn); - - return ret; -} - -/* - * PQconnectdb - * - * establishes a connection to a postgres backend through the postmaster - * using connection information in a string. - * - * The conninfo string is either a whitespace-separated list of - * - * option = value - * - * definitions or a URI (refer to the documentation for details.) Value - * might be a single value containing no whitespaces or a single quoted - * string. If a single quote should appear anywhere in the value, it must be - * escaped with a backslash like \' - * - * Returns a PGconn* which is needed for all subsequent libpq calls, or NULL - * if a memory allocation failed. - * If the status field of the connection returned is CONNECTION_BAD, - * then some fields may be null'ed out instead of having valid values. - * - * You should call PQfinish (if conn is not NULL) regardless of whether this - * call succeeded. - */ -PGconn * -PQconnectdb(const char *conninfo) -{ - PGconn *conn = PQconnectStart(conninfo); - - if (conn && conn->status != CONNECTION_BAD) - (void) connectDBComplete(conn); - - return conn; -} - -/* - * PQping - * - * check server status, accepting parameters identical to PQconnectdb - */ -PGPing -PQping(const char *conninfo) -{ - PGconn *conn = PQconnectStart(conninfo); - PGPing ret; - - ret = internal_ping(conn); - PQfinish(conn); - - return ret; -} - -/* - * PQconnectStartParams - * - * Begins the establishment of a connection to a postgres backend through the - * postmaster using connection information in a struct. - * - * See comment for PQconnectdbParams for the definition of the string format. - * - * Returns a PGconn*. If NULL is returned, a malloc error has occurred, and - * you should not attempt to proceed with this connection. If the status - * field of the connection returned is CONNECTION_BAD, an error has - * occurred. In this case you should call PQfinish on the result, (perhaps - * inspecting the error message first). Other fields of the structure may not - * be valid if that occurs. If the status field is not CONNECTION_BAD, then - * this stage has succeeded - call PQconnectPoll, using select(2) to see when - * this is necessary. - * - * See PQconnectPoll for more info. - */ -PGconn * -PQconnectStartParams(const char *const * keywords, - const char *const * values, - int expand_dbname) -{ - PGconn *conn; - PQconninfoOption *connOptions; - - /* - * Allocate memory for the conn structure - */ - conn = makeEmptyPGconn(); - if (conn == NULL) - return NULL; - - /* - * Parse the conninfo arrays - */ - connOptions = conninfo_array_parse(keywords, values, - &conn->errorMessage, - true, expand_dbname); - if (connOptions == NULL) - { - conn->status = CONNECTION_BAD; - /* errorMessage is already set */ - return conn; - } - - /* - * Move option values into conn structure - */ - if (!fillPGconn(conn, connOptions)) - { - PQconninfoFree(connOptions); - return conn; - } - - /* - * Free the option info - all is in conn now - */ - PQconninfoFree(connOptions); - - /* - * Compute derived options - */ - if (!connectOptions2(conn)) - return conn; - - /* - * Connect to the database - */ - if (!connectDBStart(conn)) - { - /* Just in case we failed to set it in connectDBStart */ - conn->status = CONNECTION_BAD; - } - - return conn; -} - -/* - * PQconnectStart - * - * Begins the establishment of a connection to a postgres backend through the - * postmaster using connection information in a string. - * - * See comment for PQconnectdb for the definition of the string format. - * - * Returns a PGconn*. If NULL is returned, a malloc error has occurred, and - * you should not attempt to proceed with this connection. If the status - * field of the connection returned is CONNECTION_BAD, an error has - * occurred. In this case you should call PQfinish on the result, (perhaps - * inspecting the error message first). Other fields of the structure may not - * be valid if that occurs. If the status field is not CONNECTION_BAD, then - * this stage has succeeded - call PQconnectPoll, using select(2) to see when - * this is necessary. - * - * See PQconnectPoll for more info. - */ -PGconn * -PQconnectStart(const char *conninfo) -{ - PGconn *conn; - - /* - * Allocate memory for the conn structure - */ - conn = makeEmptyPGconn(); - if (conn == NULL) - return NULL; - - /* - * Parse the conninfo string - */ - if (!connectOptions1(conn, conninfo)) - return conn; - - /* - * Compute derived options - */ - if (!connectOptions2(conn)) - return conn; - - /* - * Connect to the database - */ - if (!connectDBStart(conn)) - { - /* Just in case we failed to set it in connectDBStart */ - conn->status = CONNECTION_BAD; - } - - return conn; -} - -/* - * Move option values into conn structure - * - * Don't put anything cute here --- intelligence should be in - * connectOptions2 ... - * - * Returns true on success. On failure, returns false and sets error message. - */ -static bool -fillPGconn(PGconn *conn, PQconninfoOption *connOptions) -{ - const internalPQconninfoOption *option; - - for (option = PQconninfoOptions; option->keyword; option++) - { - if (option->connofs >= 0) - { - const char *tmp = conninfo_getval(connOptions, option->keyword); - - if (tmp) - { - char **connmember = (char **) ((char *) conn + option->connofs); - - if (*connmember) - free(*connmember); - *connmember = strdup(tmp); - if (*connmember == NULL) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("out of memory\n")); - return false; - } - } - } - } - - return true; -} - -/* - * connectOptions1 - * - * Internal subroutine to set up connection parameters given an already- - * created PGconn and a conninfo string. Derived settings should be - * processed by calling connectOptions2 next. (We split them because - * PQsetdbLogin overrides defaults in between.) - * - * Returns true if OK, false if trouble (in which case errorMessage is set - * and so is conn->status). - */ -static bool -connectOptions1(PGconn *conn, const char *conninfo) -{ - PQconninfoOption *connOptions; - - /* - * Parse the conninfo string - */ - connOptions = parse_connection_string(conninfo, &conn->errorMessage, true); - if (connOptions == NULL) - { - conn->status = CONNECTION_BAD; - /* errorMessage is already set */ - return false; - } - - /* - * Move option values into conn structure - */ - if (!fillPGconn(conn, connOptions)) - { - conn->status = CONNECTION_BAD; - PQconninfoFree(connOptions); - return false; - } - - /* - * Free the option info - all is in conn now - */ - PQconninfoFree(connOptions); - - return true; -} - -/* - * connectOptions2 - * - * Compute derived connection options after absorbing all user-supplied info. - * - * Returns true if OK, false if trouble (in which case errorMessage is set - * and so is conn->status). - */ -static bool -connectOptions2(PGconn *conn) -{ - /* - * If user name was not given, fetch it. (Most likely, the fetch will - * fail, since the only way we get here is if pg_fe_getauthname() failed - * during conninfo_add_defaults(). But now we want an error message.) - */ - if (conn->pguser == NULL || conn->pguser[0] == '\0') - { - if (conn->pguser) - free(conn->pguser); - conn->pguser = pg_fe_getauthname(&conn->errorMessage); - if (!conn->pguser) - { - conn->status = CONNECTION_BAD; - return false; - } - } - - /* - * If database name was not given, default it to equal user name - */ - if (conn->dbName == NULL || conn->dbName[0] == '\0') - { - if (conn->dbName) - free(conn->dbName); - conn->dbName = strdup(conn->pguser); - if (!conn->dbName) - goto oom_error; - } - - /* - * Supply default password if none given - */ - if (conn->pgpass == NULL || conn->pgpass[0] == '\0') - { - if (conn->pgpass) - free(conn->pgpass); - conn->pgpass = PasswordFromFile(conn->pghost, conn->pgport, - conn->dbName, conn->pguser); - if (conn->pgpass == NULL) - { - conn->pgpass = strdup(DefaultPassword); - if (!conn->pgpass) - goto oom_error; - } - else - conn->dot_pgpass_used = true; - } - - /* - * Allow unix socket specification in the host name - */ - if (conn->pghost && is_absolute_path(conn->pghost)) - { - if (conn->pgunixsocket) - free(conn->pgunixsocket); - conn->pgunixsocket = conn->pghost; - conn->pghost = NULL; - } - - /* - * validate sslmode option - */ - if (conn->sslmode) - { - if (strcmp(conn->sslmode, "disable") != 0 - && strcmp(conn->sslmode, "allow") != 0 - && strcmp(conn->sslmode, "prefer") != 0 - && strcmp(conn->sslmode, "require") != 0 - && strcmp(conn->sslmode, "verify-ca") != 0 - && strcmp(conn->sslmode, "verify-full") != 0) - { - conn->status = CONNECTION_BAD; - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("invalid sslmode value: \"%s\"\n"), - conn->sslmode); - return false; - } - -#ifndef USE_SSL - switch (conn->sslmode[0]) - { - case 'a': /* "allow" */ - case 'p': /* "prefer" */ - - /* - * warn user that an SSL connection will never be negotiated - * since SSL was not compiled in? - */ - break; - - case 'r': /* "require" */ - case 'v': /* "verify-ca" or "verify-full" */ - conn->status = CONNECTION_BAD; - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("sslmode value \"%s\" invalid when SSL support is not compiled in\n"), - conn->sslmode); - return false; - } -#endif - } - else - { - conn->sslmode = strdup(DefaultSSLMode); - if (!conn->sslmode) - goto oom_error; - } - - /* - * Resolve special "auto" client_encoding from the locale - */ - if (conn->client_encoding_initial && - strcmp(conn->client_encoding_initial, "auto") == 0) - { - free(conn->client_encoding_initial); - conn->client_encoding_initial = strdup(pg_encoding_to_char(pg_get_encoding_from_locale(NULL, true))); - if (!conn->client_encoding_initial) - goto oom_error; - } - - /* - * Only if we get this far is it appropriate to try to connect. (We need a - * state flag, rather than just the boolean result of this function, in - * case someone tries to PQreset() the PGconn.) - */ - conn->options_valid = true; - - return true; - -oom_error: - conn->status = CONNECTION_BAD; - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("out of memory\n")); - return false; -} - -/* - * PQconndefaults - * - * Construct a default connection options array, which identifies all the - * available options and shows any default values that are available from the - * environment etc. On error (eg out of memory), NULL is returned. - * - * Using this function, an application may determine all possible options - * and their current default values. - * - * NOTE: as of PostgreSQL 7.0, the returned array is dynamically allocated - * and should be freed when no longer needed via PQconninfoFree(). (In prior - * versions, the returned array was static, but that's not thread-safe.) - * Pre-7.0 applications that use this function will see a small memory leak - * until they are updated to call PQconninfoFree. - */ -PQconninfoOption * -PQconndefaults(void) -{ - PQExpBufferData errorBuf; - PQconninfoOption *connOptions; - - /* We don't actually report any errors here, but callees want a buffer */ - initPQExpBuffer(&errorBuf); - if (PQExpBufferDataBroken(errorBuf)) - return NULL; /* out of memory already :-( */ - - connOptions = conninfo_init(&errorBuf); - if (connOptions != NULL) - { - /* pass NULL errorBuf to ignore errors */ - if (!conninfo_add_defaults(connOptions, NULL)) - { - PQconninfoFree(connOptions); - connOptions = NULL; - } - } - - termPQExpBuffer(&errorBuf); - return connOptions; -} - -/* ---------------- - * PQsetdbLogin - * - * establishes a connection to a postgres backend through the postmaster - * at the specified host and port. - * - * returns a PGconn* which is needed for all subsequent libpq calls - * - * if the status field of the connection returned is CONNECTION_BAD, - * then only the errorMessage is likely to be useful. - * ---------------- - */ -PGconn * -PQsetdbLogin(const char *pghost, const char *pgport, const char *pgoptions, - const char *pgtty, const char *dbName, const char *login, - const char *pwd) -{ - PGconn *conn; - - /* - * Allocate memory for the conn structure - */ - conn = makeEmptyPGconn(); - if (conn == NULL) - return NULL; - - /* - * If the dbName parameter contains what looks like a connection string, - * parse it into conn struct using connectOptions1. - */ - if (dbName && recognized_connection_string(dbName)) - { - if (!connectOptions1(conn, dbName)) - return conn; - } - else - { - /* - * Old-style path: first, parse an empty conninfo string in order to - * set up the same defaults that PQconnectdb() would use. - */ - if (!connectOptions1(conn, "")) - return conn; - - /* Insert dbName parameter value into struct */ - if (dbName && dbName[0] != '\0') - { - if (conn->dbName) - free(conn->dbName); - conn->dbName = strdup(dbName); - if (!conn->dbName) - goto oom_error; - } - } - - /* - * Insert remaining parameters into struct, overriding defaults (as well - * as any conflicting data from dbName taken as a conninfo). - */ - if (pghost && pghost[0] != '\0') - { - if (conn->pghost) - free(conn->pghost); - conn->pghost = strdup(pghost); - if (!conn->pghost) - goto oom_error; - } - - if (pgport && pgport[0] != '\0') - { - if (conn->pgport) - free(conn->pgport); - conn->pgport = strdup(pgport); - if (!conn->pgport) - goto oom_error; - } - - if (pgoptions && pgoptions[0] != '\0') - { - if (conn->pgoptions) - free(conn->pgoptions); - conn->pgoptions = strdup(pgoptions); - if (!conn->pgoptions) - goto oom_error; - } - - if (pgtty && pgtty[0] != '\0') - { - if (conn->pgtty) - free(conn->pgtty); - conn->pgtty = strdup(pgtty); - if (!conn->pgtty) - goto oom_error; - } - - if (login && login[0] != '\0') - { - if (conn->pguser) - free(conn->pguser); - conn->pguser = strdup(login); - if (!conn->pguser) - goto oom_error; - } - - if (pwd && pwd[0] != '\0') - { - if (conn->pgpass) - free(conn->pgpass); - conn->pgpass = strdup(pwd); - if (!conn->pgpass) - goto oom_error; - } - - /* - * Compute derived options - */ - if (!connectOptions2(conn)) - return conn; - - /* - * Connect to the database - */ - if (connectDBStart(conn)) - (void) connectDBComplete(conn); - - return conn; - -oom_error: - conn->status = CONNECTION_BAD; - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("out of memory\n")); - return conn; -} - - -/* ---------- - * connectNoDelay - - * Sets the TCP_NODELAY socket option. - * Returns 1 if successful, 0 if not. - * ---------- - */ -static int -connectNoDelay(PGconn *conn) -{ -#ifdef TCP_NODELAY - int on = 1; - - if (setsockopt(conn->sock, IPPROTO_TCP, TCP_NODELAY, - (char *) &on, - sizeof(on)) < 0) - { - char sebuf[256]; - - appendPQExpBuffer(&conn->errorMessage, - libpq_gettext("could not set socket to TCP no delay mode: %s\n"), - SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf))); - return 0; - } -#endif - - return 1; -} - - -/* ---------- - * connectFailureMessage - - * create a friendly error message on connection failure. - * ---------- - */ -static void -connectFailureMessage(PGconn *conn, int errorno) -{ - char sebuf[256]; - -#ifdef HAVE_UNIX_SOCKETS - if (IS_AF_UNIX(conn->raddr.addr.ss_family)) - { - char service[NI_MAXHOST]; - - pg_getnameinfo_all(&conn->raddr.addr, conn->raddr.salen, - NULL, 0, - service, sizeof(service), - NI_NUMERICSERV); - appendPQExpBuffer(&conn->errorMessage, - libpq_gettext("could not connect to server: %s\n" - "\tIs the server running locally and accepting\n" - "\tconnections on Unix domain socket \"%s\"?\n"), - SOCK_STRERROR(errorno, sebuf, sizeof(sebuf)), - service); - } - else -#endif /* HAVE_UNIX_SOCKETS */ - { - char host_addr[NI_MAXHOST]; - const char *displayed_host; - struct sockaddr_storage *addr = &conn->raddr.addr; - - /* - * Optionally display the network address with the hostname. This is - * useful to distinguish between IPv4 and IPv6 connections. - */ - if (conn->pghostaddr != NULL) - strlcpy(host_addr, conn->pghostaddr, NI_MAXHOST); - else if (addr->ss_family == AF_INET) - { - if (inet_net_ntop(AF_INET, - &((struct sockaddr_in *) addr)->sin_addr.s_addr, - 32, - host_addr, sizeof(host_addr)) == NULL) - strcpy(host_addr, "???"); - } -#ifdef HAVE_IPV6 - else if (addr->ss_family == AF_INET6) - { - if (inet_net_ntop(AF_INET6, - &((struct sockaddr_in6 *) addr)->sin6_addr.s6_addr, - 128, - host_addr, sizeof(host_addr)) == NULL) - strcpy(host_addr, "???"); - } -#endif - else - strcpy(host_addr, "???"); - - if (conn->pghostaddr && conn->pghostaddr[0] != '\0') - displayed_host = conn->pghostaddr; - else if (conn->pghost && conn->pghost[0] != '\0') - displayed_host = conn->pghost; - else - displayed_host = DefaultHost; - - /* - * If the user did not supply an IP address using 'hostaddr', and - * 'host' was missing or does not match our lookup, display the - * looked-up IP address. - */ - if ((conn->pghostaddr == NULL) && - (conn->pghost == NULL || strcmp(conn->pghost, host_addr) != 0)) - appendPQExpBuffer(&conn->errorMessage, - libpq_gettext("could not connect to server: %s\n" - "\tIs the server running on host \"%s\" (%s) and accepting\n" - "\tTCP/IP connections on port %s?\n"), - SOCK_STRERROR(errorno, sebuf, sizeof(sebuf)), - displayed_host, - host_addr, - conn->pgport); - else - appendPQExpBuffer(&conn->errorMessage, - libpq_gettext("could not connect to server: %s\n" - "\tIs the server running on host \"%s\" and accepting\n" - "\tTCP/IP connections on port %s?\n"), - SOCK_STRERROR(errorno, sebuf, sizeof(sebuf)), - displayed_host, - conn->pgport); - } -} - -/* - * Should we use keepalives? Returns 1 if yes, 0 if no, and -1 if - * conn->keepalives is set to a value which is not parseable as an - * integer. - */ -static int -useKeepalives(PGconn *conn) -{ - char *ep; - int val; - - if (conn->keepalives == NULL) - return 1; - val = strtol(conn->keepalives, &ep, 10); - if (*ep) - return -1; - return val != 0 ? 1 : 0; -} - -#ifndef WIN32 -/* - * Set the keepalive idle timer. - */ -static int -setKeepalivesIdle(PGconn *conn) -{ - int idle; - - if (conn->keepalives_idle == NULL) - return 1; - - idle = atoi(conn->keepalives_idle); - if (idle < 0) - idle = 0; - -#ifdef PG_TCP_KEEPALIVE_IDLE - if (setsockopt(conn->sock, IPPROTO_TCP, PG_TCP_KEEPALIVE_IDLE, - (char *) &idle, sizeof(idle)) < 0) - { - char sebuf[256]; - - appendPQExpBuffer(&conn->errorMessage, - libpq_gettext("setsockopt(%s) failed: %s\n"), - PG_TCP_KEEPALIVE_IDLE_STR, - SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf))); - return 0; - } -#endif - - return 1; -} - -/* - * Set the keepalive interval. - */ -static int -setKeepalivesInterval(PGconn *conn) -{ - int interval; - - if (conn->keepalives_interval == NULL) - return 1; - - interval = atoi(conn->keepalives_interval); - if (interval < 0) - interval = 0; - -#ifdef TCP_KEEPINTVL - if (setsockopt(conn->sock, IPPROTO_TCP, TCP_KEEPINTVL, - (char *) &interval, sizeof(interval)) < 0) - { - char sebuf[256]; - - appendPQExpBuffer(&conn->errorMessage, - libpq_gettext("setsockopt(%s) failed: %s\n"), - "TCP_KEEPINTVL", - SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf))); - return 0; - } -#endif - - return 1; -} - -/* - * Set the count of lost keepalive packets that will trigger a connection - * break. - */ -static int -setKeepalivesCount(PGconn *conn) -{ - int count; - - if (conn->keepalives_count == NULL) - return 1; - - count = atoi(conn->keepalives_count); - if (count < 0) - count = 0; - -#ifdef TCP_KEEPCNT - if (setsockopt(conn->sock, IPPROTO_TCP, TCP_KEEPCNT, - (char *) &count, sizeof(count)) < 0) - { - char sebuf[256]; - - appendPQExpBuffer(&conn->errorMessage, - libpq_gettext("setsockopt(%s) failed: %s\n"), - "TCP_KEEPCNT", - SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf))); - return 0; - } -#endif - - return 1; -} -#else /* WIN32 */ -#ifdef SIO_KEEPALIVE_VALS -/* - * Enable keepalives and set the keepalive values on Win32, - * where they are always set in one batch. - */ -static int -setKeepalivesWin32(PGconn *conn) -{ - struct tcp_keepalive ka; - DWORD retsize; - int idle = 0; - int interval = 0; - - if (conn->keepalives_idle) - idle = atoi(conn->keepalives_idle); - if (idle <= 0) - idle = 2 * 60 * 60; /* 2 hours = default */ - - if (conn->keepalives_interval) - interval = atoi(conn->keepalives_interval); - if (interval <= 0) - interval = 1; /* 1 second = default */ - - ka.onoff = 1; - ka.keepalivetime = idle * 1000; - ka.keepaliveinterval = interval * 1000; - - if (WSAIoctl(conn->sock, - SIO_KEEPALIVE_VALS, - (LPVOID) &ka, - sizeof(ka), - NULL, - 0, - &retsize, - NULL, - NULL) - != 0) - { - appendPQExpBuffer(&conn->errorMessage, - libpq_gettext("WSAIoctl(SIO_KEEPALIVE_VALS) failed: %ui\n"), - WSAGetLastError()); - return 0; - } - return 1; -} -#endif /* SIO_KEEPALIVE_VALS */ -#endif /* WIN32 */ - -/* ---------- - * connectDBStart - - * Begin the process of making a connection to the backend. - * - * Returns 1 if successful, 0 if not. - * ---------- - */ -static int -connectDBStart(PGconn *conn) -{ - int portnum; - char portstr[MAXPGPATH]; - struct addrinfo *addrs = NULL; - struct addrinfo hint; - const char *node; - int ret; - - if (!conn) - return 0; - - if (!conn->options_valid) - goto connect_errReturn; - - /* Ensure our buffers are empty */ - conn->inStart = conn->inCursor = conn->inEnd = 0; - conn->outCount = 0; - - /* - * Determine the parameters to pass to pg_getaddrinfo_all. - */ - - /* Initialize hint structure */ - MemSet(&hint, 0, sizeof(hint)); - hint.ai_socktype = SOCK_STREAM; - hint.ai_family = AF_UNSPEC; - - /* Set up port number as a string */ - if (conn->pgport != NULL && conn->pgport[0] != '\0') - { - portnum = atoi(conn->pgport); - if (portnum < 1 || portnum > 65535) - { - appendPQExpBuffer(&conn->errorMessage, - libpq_gettext("invalid port number: \"%s\"\n"), - conn->pgport); - conn->options_valid = false; - goto connect_errReturn; - } - } - else - portnum = DEF_PGPORT; - snprintf(portstr, sizeof(portstr), "%d", portnum); - - if (conn->pghostaddr != NULL && conn->pghostaddr[0] != '\0') - { - /* Using pghostaddr avoids a hostname lookup */ - node = conn->pghostaddr; - hint.ai_family = AF_UNSPEC; - hint.ai_flags = AI_NUMERICHOST; - } - else if (conn->pghost != NULL && conn->pghost[0] != '\0') - { - /* Using pghost, so we have to look-up the hostname */ - node = conn->pghost; - hint.ai_family = AF_UNSPEC; - } - else - { -#ifdef HAVE_UNIX_SOCKETS - /* pghostaddr and pghost are NULL, so use Unix domain socket */ - node = NULL; - hint.ai_family = AF_UNIX; - UNIXSOCK_PATH(portstr, portnum, conn->pgunixsocket); - if (strlen(portstr) >= UNIXSOCK_PATH_BUFLEN) - { - appendPQExpBuffer(&conn->errorMessage, - libpq_gettext("Unix-domain socket path \"%s\" is too long (maximum %d bytes)\n"), - portstr, - (int) (UNIXSOCK_PATH_BUFLEN - 1)); - conn->options_valid = false; - goto connect_errReturn; - } -#else - /* Without Unix sockets, default to localhost instead */ - node = DefaultHost; - hint.ai_family = AF_UNSPEC; -#endif /* HAVE_UNIX_SOCKETS */ - } - - /* Use pg_getaddrinfo_all() to resolve the address */ - ret = pg_getaddrinfo_all(node, portstr, &hint, &addrs); - if (ret || !addrs) - { - if (node) - appendPQExpBuffer(&conn->errorMessage, - libpq_gettext("could not translate host name \"%s\" to address: %s\n"), - node, gai_strerror(ret)); - else - appendPQExpBuffer(&conn->errorMessage, - libpq_gettext("could not translate Unix-domain socket path \"%s\" to address: %s\n"), - portstr, gai_strerror(ret)); - if (addrs) - pg_freeaddrinfo_all(hint.ai_family, addrs); - conn->options_valid = false; - goto connect_errReturn; - } - -#ifdef USE_SSL - /* setup values based on SSL mode */ - if (conn->sslmode[0] == 'd') /* "disable" */ - conn->allow_ssl_try = false; - else if (conn->sslmode[0] == 'a') /* "allow" */ - conn->wait_ssl_try = true; -#endif - - /* - * Set up to try to connect, with protocol 3.0 as the first attempt. - */ - conn->addrlist = addrs; - conn->addr_cur = addrs; - conn->addrlist_family = hint.ai_family; - conn->pversion = PG_PROTOCOL(3, 0); - conn->send_appname = true; - conn->status = CONNECTION_NEEDED; - - /* - * The code for processing CONNECTION_NEEDED state is in PQconnectPoll(), - * so that it can easily be re-executed if needed again during the - * asynchronous startup process. However, we must run it once here, - * because callers expect a success return from this routine to mean that - * we are in PGRES_POLLING_WRITING connection state. - */ - if (PQconnectPoll(conn) == PGRES_POLLING_WRITING) - return 1; - -connect_errReturn: - pqDropConnection(conn, true); - conn->status = CONNECTION_BAD; - return 0; -} - - -/* - * connectDBComplete - * - * Block and complete a connection. - * - * Returns 1 on success, 0 on failure. - */ -static int -connectDBComplete(PGconn *conn) -{ - PostgresPollingStatusType flag = PGRES_POLLING_WRITING; - time_t finish_time = ((time_t) -1); - - if (conn == NULL || conn->status == CONNECTION_BAD) - return 0; - - /* - * Set up a time limit, if connect_timeout isn't zero. - */ - if (conn->connect_timeout != NULL) - { - int timeout = atoi(conn->connect_timeout); - - if (timeout > 0) - { - /* - * Rounding could cause connection to fail; need at least 2 secs - */ - if (timeout < 2) - timeout = 2; - /* calculate the finish time based on start + timeout */ - finish_time = time(NULL) + timeout; - } - } - - for (;;) - { - /* - * Wait, if necessary. Note that the initial state (just after - * PQconnectStart) is to wait for the socket to select for writing. - */ - switch (flag) - { - case PGRES_POLLING_OK: - - /* - * Reset stored error messages since we now have a working - * connection - */ - resetPQExpBuffer(&conn->errorMessage); - return 1; /* success! */ - - case PGRES_POLLING_READING: - if (pqWaitTimed(1, 0, conn, finish_time)) - { - conn->status = CONNECTION_BAD; - return 0; - } - break; - - case PGRES_POLLING_WRITING: - if (pqWaitTimed(0, 1, conn, finish_time)) - { - conn->status = CONNECTION_BAD; - return 0; - } - break; - - default: - /* Just in case we failed to set it in PQconnectPoll */ - conn->status = CONNECTION_BAD; - return 0; - } - - /* - * Now try to advance the state machine. - */ - flag = PQconnectPoll(conn); - } -} - -/* ---------------- - * PQconnectPoll - * - * Poll an asynchronous connection. - * - * Returns a PostgresPollingStatusType. - * Before calling this function, use select(2) to determine when data - * has arrived.. - * - * You must call PQfinish whether or not this fails. - * - * This function and PQconnectStart are intended to allow connections to be - * made without blocking the execution of your program on remote I/O. However, - * there are a number of caveats: - * - * o If you call PQtrace, ensure that the stream object into which you trace - * will not block. - * o If you do not supply an IP address for the remote host (i.e. you - * supply a host name instead) then PQconnectStart will block on - * gethostbyname. You will be fine if using Unix sockets (i.e. by - * supplying neither a host name nor a host address). - * o If your backend wants to use Kerberos authentication then you must - * supply both a host name and a host address, otherwise this function - * may block on gethostname. - * - * ---------------- - */ -PostgresPollingStatusType -PQconnectPoll(PGconn *conn) -{ - PGresult *res; - char sebuf[256]; - int optval; - - if (conn == NULL) - return PGRES_POLLING_FAILED; - - /* Get the new data */ - switch (conn->status) - { - /* - * We really shouldn't have been polled in these two cases, but we - * can handle it. - */ - case CONNECTION_BAD: - return PGRES_POLLING_FAILED; - case CONNECTION_OK: - return PGRES_POLLING_OK; - - /* These are reading states */ - case CONNECTION_AWAITING_RESPONSE: - case CONNECTION_AUTH_OK: - { - /* Load waiting data */ - int n = pqReadData(conn); - - if (n < 0) - goto error_return; - if (n == 0) - return PGRES_POLLING_READING; - - break; - } - - /* These are writing states, so we just proceed. */ - case CONNECTION_STARTED: - case CONNECTION_MADE: - break; - - /* We allow pqSetenvPoll to decide whether to proceed. */ - case CONNECTION_SETENV: - break; - - /* Special cases: proceed without waiting. */ - case CONNECTION_SSL_STARTUP: - case CONNECTION_NEEDED: - break; - - default: - appendPQExpBufferStr(&conn->errorMessage, - libpq_gettext( - "invalid connection state, " - "probably indicative of memory corruption\n" - )); - goto error_return; - } - - -keep_going: /* We will come back to here until there is - * nothing left to do. */ - switch (conn->status) - { - case CONNECTION_NEEDED: - { - /* - * Try to initiate a connection to one of the addresses - * returned by pg_getaddrinfo_all(). conn->addr_cur is the - * next one to try. We fail when we run out of addresses. - */ - while (conn->addr_cur != NULL) - { - struct addrinfo *addr_cur = conn->addr_cur; - - /* Remember current address for possible error msg */ - memcpy(&conn->raddr.addr, addr_cur->ai_addr, - addr_cur->ai_addrlen); - conn->raddr.salen = addr_cur->ai_addrlen; - - conn->sock = socket(addr_cur->ai_family, SOCK_STREAM, 0); - if (conn->sock == PGINVALID_SOCKET) - { - /* - * ignore socket() failure if we have more addresses - * to try - */ - if (addr_cur->ai_next != NULL) - { - conn->addr_cur = addr_cur->ai_next; - continue; - } - appendPQExpBuffer(&conn->errorMessage, - libpq_gettext("could not create socket: %s\n"), - SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf))); - break; - } - - /* - * Select socket options: no delay of outgoing data for - * TCP sockets, nonblock mode, close-on-exec. Fail if any - * of this fails. - */ - if (!IS_AF_UNIX(addr_cur->ai_family)) - { - if (!connectNoDelay(conn)) - { - pqDropConnection(conn, true); - conn->addr_cur = addr_cur->ai_next; - continue; - } - } - if (!pg_set_noblock(conn->sock)) - { - appendPQExpBuffer(&conn->errorMessage, - libpq_gettext("could not set socket to nonblocking mode: %s\n"), - SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf))); - pqDropConnection(conn, true); - conn->addr_cur = addr_cur->ai_next; - continue; - } - -#ifdef F_SETFD - if (fcntl(conn->sock, F_SETFD, FD_CLOEXEC) == -1) - { - appendPQExpBuffer(&conn->errorMessage, - libpq_gettext("could not set socket to close-on-exec mode: %s\n"), - SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf))); - pqDropConnection(conn, true); - conn->addr_cur = addr_cur->ai_next; - continue; - } -#endif /* F_SETFD */ - - if (!IS_AF_UNIX(addr_cur->ai_family)) - { -#ifndef WIN32 - int on = 1; -#endif - int usekeepalives = useKeepalives(conn); - int err = 0; - - if (usekeepalives < 0) - { - appendPQExpBufferStr(&conn->errorMessage, - libpq_gettext("keepalives parameter must be an integer\n")); - err = 1; - } - else if (usekeepalives == 0) - { - /* Do nothing */ - } -#ifndef WIN32 - else if (setsockopt(conn->sock, - SOL_SOCKET, SO_KEEPALIVE, - (char *) &on, sizeof(on)) < 0) - { - appendPQExpBuffer(&conn->errorMessage, - libpq_gettext("setsockopt(%s) failed: %s\n"), - "SO_KEEPALIVE", - SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf))); - err = 1; - } - else if (!setKeepalivesIdle(conn) - || !setKeepalivesInterval(conn) - || !setKeepalivesCount(conn)) - err = 1; -#else /* WIN32 */ -#ifdef SIO_KEEPALIVE_VALS - else if (!setKeepalivesWin32(conn)) - err = 1; -#endif /* SIO_KEEPALIVE_VALS */ -#endif /* WIN32 */ - - if (err) - { - pqDropConnection(conn, true); - conn->addr_cur = addr_cur->ai_next; - continue; - } - } - - /*---------- - * We have three methods of blocking SIGPIPE during - * send() calls to this socket: - * - * - setsockopt(sock, SO_NOSIGPIPE) - * - send(sock, ..., MSG_NOSIGNAL) - * - setting the signal mask to SIG_IGN during send() - * - * The third method requires three syscalls per send, - * so we prefer either of the first two, but they are - * less portable. The state is tracked in the following - * members of PGconn: - * - * conn->sigpipe_so - we have set up SO_NOSIGPIPE - * conn->sigpipe_flag - we're specifying MSG_NOSIGNAL - * - * If we can use SO_NOSIGPIPE, then set sigpipe_so here - * and we're done. Otherwise, set sigpipe_flag so that - * we will try MSG_NOSIGNAL on sends. If we get an error - * with MSG_NOSIGNAL, we'll clear that flag and revert to - * signal masking. - *---------- - */ - conn->sigpipe_so = false; -#ifdef MSG_NOSIGNAL - conn->sigpipe_flag = true; -#else - conn->sigpipe_flag = false; -#endif /* MSG_NOSIGNAL */ - -#ifdef SO_NOSIGPIPE - optval = 1; - if (setsockopt(conn->sock, SOL_SOCKET, SO_NOSIGPIPE, - (char *) &optval, sizeof(optval)) == 0) - { - conn->sigpipe_so = true; - conn->sigpipe_flag = false; - } -#endif /* SO_NOSIGPIPE */ - - /* - * Start/make connection. This should not block, since we - * are in nonblock mode. If it does, well, too bad. - */ - if (connect(conn->sock, addr_cur->ai_addr, - addr_cur->ai_addrlen) < 0) - { - if (SOCK_ERRNO == EINPROGRESS || -#ifdef WIN32 - SOCK_ERRNO == EWOULDBLOCK || -#endif - SOCK_ERRNO == EINTR) - { - /* - * This is fine - we're in non-blocking mode, and - * the connection is in progress. Tell caller to - * wait for write-ready on socket. - */ - conn->status = CONNECTION_STARTED; - return PGRES_POLLING_WRITING; - } - /* otherwise, trouble */ - } - else - { - /* - * Hm, we're connected already --- seems the "nonblock - * connection" wasn't. Advance the state machine and - * go do the next stuff. - */ - conn->status = CONNECTION_STARTED; - goto keep_going; - } - - /* - * This connection failed --- set up error report, then - * close socket (do it this way in case close() affects - * the value of errno...). We will ignore the connect() - * failure and keep going if there are more addresses. - */ - connectFailureMessage(conn, SOCK_ERRNO); - pqDropConnection(conn, true); - - /* - * Try the next address, if any. - */ - conn->addr_cur = addr_cur->ai_next; - } /* loop over addresses */ - - /* - * Ooops, no more addresses. An appropriate error message is - * already set up, so just set the right status. - */ - goto error_return; - } - - case CONNECTION_STARTED: - { - ACCEPT_TYPE_ARG3 optlen = sizeof(optval); - - /* - * Write ready, since we've made it here, so the connection - * has been made ... or has failed. - */ - - /* - * Now check (using getsockopt) that there is not an error - * state waiting for us on the socket. - */ - - if (getsockopt(conn->sock, SOL_SOCKET, SO_ERROR, - (char *) &optval, &optlen) == -1) - { - appendPQExpBuffer(&conn->errorMessage, - libpq_gettext("could not get socket error status: %s\n"), - SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf))); - goto error_return; - } - else if (optval != 0) - { - /* - * When using a nonblocking connect, we will typically see - * connect failures at this point, so provide a friendly - * error message. - */ - connectFailureMessage(conn, optval); - pqDropConnection(conn, true); - - /* - * If more addresses remain, keep trying, just as in the - * case where connect() returned failure immediately. - */ - if (conn->addr_cur->ai_next != NULL) - { - conn->addr_cur = conn->addr_cur->ai_next; - conn->status = CONNECTION_NEEDED; - goto keep_going; - } - goto error_return; - } - - /* Fill in the client address */ - conn->laddr.salen = sizeof(conn->laddr.addr); - if (getsockname(conn->sock, - (struct sockaddr *) & conn->laddr.addr, - &conn->laddr.salen) < 0) - { - appendPQExpBuffer(&conn->errorMessage, - libpq_gettext("could not get client address from socket: %s\n"), - SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf))); - goto error_return; - } - - /* - * Make sure we can write before advancing to next step. - */ - conn->status = CONNECTION_MADE; - return PGRES_POLLING_WRITING; - } - - case CONNECTION_MADE: - { - char *startpacket; - int packetlen; - -#ifdef HAVE_UNIX_SOCKETS - - /* - * Implement requirepeer check, if requested and it's a - * Unix-domain socket. - */ - if (conn->requirepeer && conn->requirepeer[0] && - IS_AF_UNIX(conn->raddr.addr.ss_family)) - { - char pwdbuf[BUFSIZ]; - struct passwd pass_buf; - struct passwd *pass; - int passerr; - uid_t uid; - gid_t gid; - - errno = 0; - if (getpeereid(conn->sock, &uid, &gid) != 0) - { - /* - * Provide special error message if getpeereid is a - * stub - */ - if (errno == ENOSYS) - appendPQExpBufferStr(&conn->errorMessage, - libpq_gettext("requirepeer parameter is not supported on this platform\n")); - else - appendPQExpBuffer(&conn->errorMessage, - libpq_gettext("could not get peer credentials: %s\n"), - pqStrerror(errno, sebuf, sizeof(sebuf))); - goto error_return; - } - - passerr = pqGetpwuid(uid, &pass_buf, pwdbuf, sizeof(pwdbuf), &pass); - if (pass == NULL) - { - if (passerr != 0) - appendPQExpBuffer(&conn->errorMessage, - libpq_gettext("could not look up local user ID %d: %s\n"), - (int) uid, - pqStrerror(passerr, sebuf, sizeof(sebuf))); - else - appendPQExpBuffer(&conn->errorMessage, - libpq_gettext("local user with ID %d does not exist\n"), - (int) uid); - goto error_return; - } - - if (strcmp(pass->pw_name, conn->requirepeer) != 0) - { - appendPQExpBuffer(&conn->errorMessage, - libpq_gettext("requirepeer specifies \"%s\", but actual peer user name is \"%s\"\n"), - conn->requirepeer, pass->pw_name); - goto error_return; - } - } -#endif /* HAVE_UNIX_SOCKETS */ - -#ifdef USE_SSL - - /* - * If SSL is enabled and we haven't already got it running, - * request it instead of sending the startup message. - */ - if (IS_AF_UNIX(conn->raddr.addr.ss_family)) - { - /* Don't bother requesting SSL over a Unix socket */ - conn->allow_ssl_try = false; - } - if (conn->allow_ssl_try && !conn->wait_ssl_try && - !conn->ssl_in_use) - { - ProtocolVersion pv; - - /* - * Send the SSL request packet. - * - * Theoretically, this could block, but it really - * shouldn't since we only got here if the socket is - * write-ready. - */ - pv = htonl(NEGOTIATE_SSL_CODE); - if (pqPacketSend(conn, 0, &pv, sizeof(pv)) != STATUS_OK) - { - appendPQExpBuffer(&conn->errorMessage, - libpq_gettext("could not send SSL negotiation packet: %s\n"), - SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf))); - goto error_return; - } - /* Ok, wait for response */ - conn->status = CONNECTION_SSL_STARTUP; - return PGRES_POLLING_READING; - } -#endif /* USE_SSL */ - - /* - * Build the startup packet. - */ - if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3) - startpacket = pqBuildStartupPacket3(conn, &packetlen, - EnvironmentOptions); - else - startpacket = pqBuildStartupPacket2(conn, &packetlen, - EnvironmentOptions); - if (!startpacket) - { - /* - * will not appendbuffer here, since it's likely to also - * run out of memory - */ - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("out of memory\n")); - goto error_return; - } - - /* - * Send the startup packet. - * - * Theoretically, this could block, but it really shouldn't - * since we only got here if the socket is write-ready. - */ - if (pqPacketSend(conn, 0, startpacket, packetlen) != STATUS_OK) - { - appendPQExpBuffer(&conn->errorMessage, - libpq_gettext("could not send startup packet: %s\n"), - SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf))); - free(startpacket); - goto error_return; - } - - free(startpacket); - - conn->status = CONNECTION_AWAITING_RESPONSE; - return PGRES_POLLING_READING; - } - - /* - * Handle SSL negotiation: wait for postmaster messages and - * respond as necessary. - */ - case CONNECTION_SSL_STARTUP: - { -#ifdef USE_SSL - PostgresPollingStatusType pollres; - - /* - * On first time through, get the postmaster's response to our - * SSL negotiation packet. - */ - if (!conn->ssl_in_use) - { - /* - * We use pqReadData here since it has the logic to - * distinguish no-data-yet from connection closure. Since - * conn->ssl isn't set, a plain recv() will occur. - */ - char SSLok; - int rdresult; - - rdresult = pqReadData(conn); - if (rdresult < 0) - { - /* errorMessage is already filled in */ - goto error_return; - } - if (rdresult == 0) - { - /* caller failed to wait for data */ - return PGRES_POLLING_READING; - } - if (pqGetc(&SSLok, conn) < 0) - { - /* should not happen really */ - return PGRES_POLLING_READING; - } - if (SSLok == 'S') - { - /* mark byte consumed */ - conn->inStart = conn->inCursor; - /* Set up global SSL state if required */ - if (pqsecure_initialize(conn) != 0) - goto error_return; - } - else if (SSLok == 'N') - { - /* mark byte consumed */ - conn->inStart = conn->inCursor; - /* OK to do without SSL? */ - if (conn->sslmode[0] == 'r' || /* "require" */ - conn->sslmode[0] == 'v') /* "verify-ca" or - * "verify-full" */ - { - /* Require SSL, but server does not want it */ - appendPQExpBufferStr(&conn->errorMessage, - libpq_gettext("server does not support SSL, but SSL was required\n")); - goto error_return; - } - /* Otherwise, proceed with normal startup */ - conn->allow_ssl_try = false; - conn->status = CONNECTION_MADE; - return PGRES_POLLING_WRITING; - } - else if (SSLok == 'E') - { - /* - * Server failure of some sort, such as failure to - * fork a backend process. We need to process and - * report the error message, which might be formatted - * according to either protocol 2 or protocol 3. - * Rather than duplicate the code for that, we flip - * into AWAITING_RESPONSE state and let the code there - * deal with it. Note we have *not* consumed the "E" - * byte here. - */ - conn->status = CONNECTION_AWAITING_RESPONSE; - goto keep_going; - } - else - { - appendPQExpBuffer(&conn->errorMessage, - libpq_gettext("received invalid response to SSL negotiation: %c\n"), - SSLok); - goto error_return; - } - } - - /* - * Begin or continue the SSL negotiation process. - */ - pollres = pqsecure_open_client(conn); - if (pollres == PGRES_POLLING_OK) - { - /* SSL handshake done, ready to send startup packet */ - conn->status = CONNECTION_MADE; - return PGRES_POLLING_WRITING; - } - if (pollres == PGRES_POLLING_FAILED) - { - /* - * Failed ... if sslmode is "prefer" then do a non-SSL - * retry - */ - if (conn->sslmode[0] == 'p' /* "prefer" */ - && conn->allow_ssl_try /* redundant? */ - && !conn->wait_ssl_try) /* redundant? */ - { - /* only retry once */ - conn->allow_ssl_try = false; - /* Must drop the old connection */ - pqDropConnection(conn, true); - conn->status = CONNECTION_NEEDED; - goto keep_going; - } - } - return pollres; -#else /* !USE_SSL */ - /* can't get here */ - goto error_return; -#endif /* USE_SSL */ - } - - /* - * Handle authentication exchange: wait for postmaster messages - * and respond as necessary. - */ - case CONNECTION_AWAITING_RESPONSE: - { - char beresp; - int msgLength; - int avail; - AuthRequest areq; - - /* - * Scan the message from current point (note that if we find - * the message is incomplete, we will return without advancing - * inStart, and resume here next time). - */ - conn->inCursor = conn->inStart; - - /* Read type byte */ - if (pqGetc(&beresp, conn)) - { - /* We'll come back when there is more data */ - return PGRES_POLLING_READING; - } - - /* - * Validate message type: we expect only an authentication - * request or an error here. Anything else probably means - * it's not Postgres on the other end at all. - */ - if (!(beresp == 'R' || beresp == 'E')) - { - appendPQExpBuffer(&conn->errorMessage, - libpq_gettext( - "expected authentication request from " - "server, but received %c\n"), - beresp); - goto error_return; - } - - if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3) - { - /* Read message length word */ - if (pqGetInt(&msgLength, 4, conn)) - { - /* We'll come back when there is more data */ - return PGRES_POLLING_READING; - } - } - else - { - /* Set phony message length to disable checks below */ - msgLength = 8; - } - - /* - * Try to validate message length before using it. - * Authentication requests can't be very large, although GSS - * auth requests may not be that small. Errors can be a - * little larger, but not huge. If we see a large apparent - * length in an error, it means we're really talking to a - * pre-3.0-protocol server; cope. - */ - if (beresp == 'R' && (msgLength < 8 || msgLength > 2000)) - { - appendPQExpBuffer(&conn->errorMessage, - libpq_gettext( - "expected authentication request from " - "server, but received %c\n"), - beresp); - goto error_return; - } - - if (beresp == 'E' && (msgLength < 8 || msgLength > 30000)) - { - /* Handle error from a pre-3.0 server */ - conn->inCursor = conn->inStart + 1; /* reread data */ - if (pqGets_append(&conn->errorMessage, conn)) - { - /* We'll come back when there is more data */ - return PGRES_POLLING_READING; - } - /* OK, we read the message; mark data consumed */ - conn->inStart = conn->inCursor; - - /* - * The postmaster typically won't end its message with a - * newline, so add one to conform to libpq conventions. - */ - appendPQExpBufferChar(&conn->errorMessage, '\n'); - - /* - * If we tried to open the connection in 3.0 protocol, - * fall back to 2.0 protocol. - */ - if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3) - { - conn->pversion = PG_PROTOCOL(2, 0); - /* Must drop the old connection */ - pqDropConnection(conn, true); - conn->status = CONNECTION_NEEDED; - goto keep_going; - } - - goto error_return; - } - - /* - * Can't process if message body isn't all here yet. - * - * (In protocol 2.0 case, we are assuming messages carry at - * least 4 bytes of data.) - */ - msgLength -= 4; - avail = conn->inEnd - conn->inCursor; - if (avail < msgLength) - { - /* - * Before returning, try to enlarge the input buffer if - * needed to hold the whole message; see notes in - * pqParseInput3. - */ - if (pqCheckInBufferSpace(conn->inCursor + (size_t) msgLength, - conn)) - goto error_return; - /* We'll come back when there is more data */ - return PGRES_POLLING_READING; - } - - /* Handle errors. */ - if (beresp == 'E') - { - if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3) - { - if (pqGetErrorNotice3(conn, true)) - { - /* We'll come back when there is more data */ - return PGRES_POLLING_READING; - } - } - else - { - if (pqGets_append(&conn->errorMessage, conn)) - { - /* We'll come back when there is more data */ - return PGRES_POLLING_READING; - } - } - /* OK, we read the message; mark data consumed */ - conn->inStart = conn->inCursor; - -#ifdef USE_SSL - - /* - * if sslmode is "allow" and we haven't tried an SSL - * connection already, then retry with an SSL connection - */ - if (conn->sslmode[0] == 'a' /* "allow" */ - && !conn->ssl_in_use - && conn->allow_ssl_try - && conn->wait_ssl_try) - { - /* only retry once */ - conn->wait_ssl_try = false; - /* Must drop the old connection */ - pqDropConnection(conn, true); - conn->status = CONNECTION_NEEDED; - goto keep_going; - } - - /* - * if sslmode is "prefer" and we're in an SSL connection, - * then do a non-SSL retry - */ - if (conn->sslmode[0] == 'p' /* "prefer" */ - && conn->allow_ssl_try - && !conn->wait_ssl_try) /* redundant? */ - { - /* only retry once */ - conn->allow_ssl_try = false; - /* Must drop the old connection */ - pqDropConnection(conn, true); - conn->status = CONNECTION_NEEDED; - goto keep_going; - } -#endif - - goto error_return; - } - - /* It is an authentication request. */ - conn->auth_req_received = true; - - /* Get the type of request. */ - if (pqGetInt((int *) &areq, 4, conn)) - { - /* We'll come back when there are more data */ - return PGRES_POLLING_READING; - } - - /* Get the password salt if there is one. */ - if (areq == AUTH_REQ_MD5) - { - if (pqGetnchar(conn->md5Salt, - sizeof(conn->md5Salt), conn)) - { - /* We'll come back when there are more data */ - return PGRES_POLLING_READING; - } - } -#if defined(ENABLE_GSS) || defined(ENABLE_SSPI) - - /* - * Continue GSSAPI/SSPI authentication - */ - if (areq == AUTH_REQ_GSS_CONT) - { - int llen = msgLength - 4; - - /* - * We can be called repeatedly for the same buffer. Avoid - * re-allocating the buffer in this case - just re-use the - * old buffer. - */ - if (llen != conn->ginbuf.length) - { - if (conn->ginbuf.value) - free(conn->ginbuf.value); - - conn->ginbuf.length = llen; - conn->ginbuf.value = malloc(llen); - if (!conn->ginbuf.value) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("out of memory allocating GSSAPI buffer (%d)"), - llen); - goto error_return; - } - } - - if (pqGetnchar(conn->ginbuf.value, llen, conn)) - { - /* We'll come back when there is more data. */ - return PGRES_POLLING_READING; - } - } -#endif - - /* - * OK, we successfully read the message; mark data consumed - */ - conn->inStart = conn->inCursor; - - /* Respond to the request if necessary. */ - - /* - * Note that conn->pghost must be non-NULL if we are going to - * avoid the Kerberos code doing a hostname look-up. - */ - - if (pg_fe_sendauth(areq, conn) != STATUS_OK) - { - conn->errorMessage.len = strlen(conn->errorMessage.data); - goto error_return; - } - conn->errorMessage.len = strlen(conn->errorMessage.data); - - /* - * Just make sure that any data sent by pg_fe_sendauth is - * flushed out. Although this theoretically could block, it - * really shouldn't since we don't send large auth responses. - */ - if (pqFlush(conn)) - goto error_return; - - if (areq == AUTH_REQ_OK) - { - /* We are done with authentication exchange */ - conn->status = CONNECTION_AUTH_OK; - - /* - * Set asyncStatus so that PQgetResult will think that - * what comes back next is the result of a query. See - * below. - */ - conn->asyncStatus = PGASYNC_BUSY; - } - - /* Look to see if we have more data yet. */ - goto keep_going; - } - - case CONNECTION_AUTH_OK: - { - /* - * Now we expect to hear from the backend. A ReadyForQuery - * message indicates that startup is successful, but we might - * also get an Error message indicating failure. (Notice - * messages indicating nonfatal warnings are also allowed by - * the protocol, as are ParameterStatus and BackendKeyData - * messages.) Easiest way to handle this is to let - * PQgetResult() read the messages. We just have to fake it - * out about the state of the connection, by setting - * asyncStatus = PGASYNC_BUSY (done above). - */ - - if (PQisBusy(conn)) - return PGRES_POLLING_READING; - - res = PQgetResult(conn); - - /* - * NULL return indicating we have gone to IDLE state is - * expected - */ - if (res) - { - if (res->resultStatus != PGRES_FATAL_ERROR) - appendPQExpBufferStr(&conn->errorMessage, - libpq_gettext("unexpected message from server during startup\n")); - else if (conn->send_appname && - (conn->appname || conn->fbappname)) - { - /* - * If we tried to send application_name, check to see - * if the error is about that --- pre-9.0 servers will - * reject it at this stage of the process. If so, - * close the connection and retry without sending - * application_name. We could possibly get a false - * SQLSTATE match here and retry uselessly, but there - * seems no great harm in that; we'll just get the - * same error again if it's unrelated. - */ - const char *sqlstate; - - sqlstate = PQresultErrorField(res, PG_DIAG_SQLSTATE); - if (sqlstate && - strcmp(sqlstate, ERRCODE_APPNAME_UNKNOWN) == 0) - { - PQclear(res); - conn->send_appname = false; - /* Must drop the old connection */ - pqDropConnection(conn, true); - conn->status = CONNECTION_NEEDED; - goto keep_going; - } - } - - /* - * if the resultStatus is FATAL, then conn->errorMessage - * already has a copy of the error; needn't copy it back. - * But add a newline if it's not there already, since - * postmaster error messages may not have one. - */ - if (conn->errorMessage.len <= 0 || - conn->errorMessage.data[conn->errorMessage.len - 1] != '\n') - appendPQExpBufferChar(&conn->errorMessage, '\n'); - PQclear(res); - goto error_return; - } - - /* We can release the address list now. */ - pg_freeaddrinfo_all(conn->addrlist_family, conn->addrlist); - conn->addrlist = NULL; - conn->addr_cur = NULL; - - /* Fire up post-connection housekeeping if needed */ - if (PG_PROTOCOL_MAJOR(conn->pversion) < 3) - { - conn->status = CONNECTION_SETENV; - conn->setenv_state = SETENV_STATE_CLIENT_ENCODING_SEND; - conn->next_eo = EnvironmentOptions; - return PGRES_POLLING_WRITING; - } - - /* Otherwise, we are open for business! */ - conn->status = CONNECTION_OK; - return PGRES_POLLING_OK; - } - - case CONNECTION_SETENV: - - /* - * Do post-connection housekeeping (only needed in protocol 2.0). - * - * We pretend that the connection is OK for the duration of these - * queries. - */ - conn->status = CONNECTION_OK; - - switch (pqSetenvPoll(conn)) - { - case PGRES_POLLING_OK: /* Success */ - break; - - case PGRES_POLLING_READING: /* Still going */ - conn->status = CONNECTION_SETENV; - return PGRES_POLLING_READING; - - case PGRES_POLLING_WRITING: /* Still going */ - conn->status = CONNECTION_SETENV; - return PGRES_POLLING_WRITING; - - default: - goto error_return; - } - - /* We are open for business! */ - conn->status = CONNECTION_OK; - return PGRES_POLLING_OK; - - default: - appendPQExpBuffer(&conn->errorMessage, - libpq_gettext("invalid connection state %d, " - "probably indicative of memory corruption\n"), - conn->status); - goto error_return; - } - - /* Unreachable */ - -error_return: - - dot_pg_pass_warning(conn); - - /* - * We used to close the socket at this point, but that makes it awkward - * for those above us if they wish to remove this socket from their own - * records (an fd_set for example). We'll just have this socket closed - * when PQfinish is called (which is compulsory even after an error, since - * the connection structure must be freed). - */ - conn->status = CONNECTION_BAD; - return PGRES_POLLING_FAILED; -} - - -/* - * internal_ping - * Determine if a server is running and if we can connect to it. - * - * The argument is a connection that's been started, but not completed. - */ -static PGPing -internal_ping(PGconn *conn) -{ - /* Say "no attempt" if we never got to PQconnectPoll */ - if (!conn || !conn->options_valid) - return PQPING_NO_ATTEMPT; - - /* Attempt to complete the connection */ - if (conn->status != CONNECTION_BAD) - (void) connectDBComplete(conn); - - /* Definitely OK if we succeeded */ - if (conn->status != CONNECTION_BAD) - return PQPING_OK; - - /* - * Here begins the interesting part of "ping": determine the cause of the - * failure in sufficient detail to decide what to return. We do not want - * to report that the server is not up just because we didn't have a valid - * password, for example. In fact, any sort of authentication request - * implies the server is up. (We need this check since the libpq side of - * things might have pulled the plug on the connection before getting an - * error as such from the postmaster.) - */ - if (conn->auth_req_received) - return PQPING_OK; - - /* - * If we failed to get any ERROR response from the postmaster, report - * PQPING_NO_RESPONSE. This result could be somewhat misleading for a - * pre-7.4 server, since it won't send back a SQLSTATE, but those are long - * out of support. Another corner case where the server could return a - * failure without a SQLSTATE is fork failure, but NO_RESPONSE isn't - * totally unreasonable for that anyway. We expect that every other - * failure case in a modern server will produce a report with a SQLSTATE. - * - * NOTE: whenever we get around to making libpq generate SQLSTATEs for - * client-side errors, we should either not store those into - * last_sqlstate, or add an extra flag so we can tell client-side errors - * apart from server-side ones. - */ - if (strlen(conn->last_sqlstate) != 5) - return PQPING_NO_RESPONSE; - - /* - * Report PQPING_REJECT if server says it's not accepting connections. (We - * distinguish this case mainly for the convenience of pg_ctl.) - */ - if (strcmp(conn->last_sqlstate, ERRCODE_CANNOT_CONNECT_NOW) == 0) - return PQPING_REJECT; - - /* - * Any other SQLSTATE can be taken to indicate that the server is up. - * Presumably it didn't like our username, password, or database name; or - * perhaps it had some transient failure, but that should not be taken as - * meaning "it's down". - */ - return PQPING_OK; -} - - -/* - * makeEmptyPGconn - * - create a PGconn data structure with (as yet) no interesting data - */ -static PGconn * -makeEmptyPGconn(void) -{ - PGconn *conn; - -#ifdef WIN32 - - /* - * Make sure socket support is up and running. - */ - WSADATA wsaData; - - if (WSAStartup(MAKEWORD(1, 1), &wsaData)) - return NULL; - WSASetLastError(0); -#endif - - conn = (PGconn *) malloc(sizeof(PGconn)); - if (conn == NULL) - { -#ifdef WIN32 - WSACleanup(); -#endif - return conn; - } - - /* Zero all pointers and booleans */ - MemSet(conn, 0, sizeof(PGconn)); - - /* install default notice hooks */ - conn->noticeHooks.noticeRec = defaultNoticeReceiver; - conn->noticeHooks.noticeProc = defaultNoticeProcessor; - - conn->status = CONNECTION_BAD; - conn->asyncStatus = PGASYNC_IDLE; - conn->xactStatus = PQTRANS_IDLE; - conn->options_valid = false; - conn->nonblocking = false; - conn->setenv_state = SETENV_STATE_IDLE; - conn->client_encoding = PG_SQL_ASCII; - conn->std_strings = false; /* unless server says differently */ - conn->verbosity = PQERRORS_DEFAULT; - conn->show_context = PQSHOW_CONTEXT_ERRORS; - conn->sock = PGINVALID_SOCKET; - conn->auth_req_received = false; - conn->password_needed = false; - conn->dot_pgpass_used = false; -#ifdef USE_SSL - conn->allow_ssl_try = true; - conn->wait_ssl_try = false; - conn->ssl_in_use = false; -#endif - - /* - * We try to send at least 8K at a time, which is the usual size of pipe - * buffers on Unix systems. That way, when we are sending a large amount - * of data, we avoid incurring extra kernel context swaps for partial - * bufferloads. The output buffer is initially made 16K in size, and we - * try to dump it after accumulating 8K. - * - * With the same goal of minimizing context swaps, the input buffer will - * be enlarged anytime it has less than 8K free, so we initially allocate - * twice that. - */ - conn->inBufSize = 16 * 1024; - conn->inBuffer = (char *) malloc(conn->inBufSize); - conn->outBufSize = 16 * 1024; - conn->outBuffer = (char *) malloc(conn->outBufSize); - conn->rowBufLen = 32; - conn->rowBuf = (PGdataValue *) malloc(conn->rowBufLen * sizeof(PGdataValue)); - initPQExpBuffer(&conn->errorMessage); - initPQExpBuffer(&conn->workBuffer); - - if (conn->inBuffer == NULL || - conn->outBuffer == NULL || - conn->rowBuf == NULL || - PQExpBufferBroken(&conn->errorMessage) || - PQExpBufferBroken(&conn->workBuffer)) - { - /* out of memory already :-( */ - freePGconn(conn); - conn = NULL; - } - - return conn; -} - -/* - * freePGconn - * - free an idle (closed) PGconn data structure - * - * NOTE: this should not overlap any functionality with closePGconn(). - * Clearing/resetting of transient state belongs there; what we do here is - * release data that is to be held for the life of the PGconn structure. - * If a value ought to be cleared/freed during PQreset(), do it there not here. - */ -static void -freePGconn(PGconn *conn) -{ - int i; - - /* let any event procs clean up their state data */ - for (i = 0; i < conn->nEvents; i++) - { - PGEventConnDestroy evt; - - evt.conn = conn; - (void) conn->events[i].proc(PGEVT_CONNDESTROY, &evt, - conn->events[i].passThrough); - free(conn->events[i].name); - } - - if (conn->client_encoding_initial) - free(conn->client_encoding_initial); - if (conn->events) - free(conn->events); - if (conn->pghost) - free(conn->pghost); - if (conn->pghostaddr) - free(conn->pghostaddr); - if (conn->pgport) - free(conn->pgport); - if (conn->pgunixsocket) - free(conn->pgunixsocket); - if (conn->pgtty) - free(conn->pgtty); - if (conn->connect_timeout) - free(conn->connect_timeout); - if (conn->pgoptions) - free(conn->pgoptions); - if (conn->appname) - free(conn->appname); - if (conn->fbappname) - free(conn->fbappname); - if (conn->dbName) - free(conn->dbName); - if (conn->replication) - free(conn->replication); - if (conn->pguser) - free(conn->pguser); - if (conn->pgpass) - free(conn->pgpass); - if (conn->keepalives) - free(conn->keepalives); - if (conn->keepalives_idle) - free(conn->keepalives_idle); - if (conn->keepalives_interval) - free(conn->keepalives_interval); - if (conn->keepalives_count) - free(conn->keepalives_count); - if (conn->sslmode) - free(conn->sslmode); - if (conn->sslcert) - free(conn->sslcert); - if (conn->sslkey) - free(conn->sslkey); - if (conn->sslrootcert) - free(conn->sslrootcert); - if (conn->sslcrl) - free(conn->sslcrl); - if (conn->sslcompression) - free(conn->sslcompression); - if (conn->requirepeer) - free(conn->requirepeer); -#if defined(ENABLE_GSS) || defined(ENABLE_SSPI) - if (conn->krbsrvname) - free(conn->krbsrvname); -#endif -#if defined(ENABLE_GSS) && defined(ENABLE_SSPI) - if (conn->gsslib) - free(conn->gsslib); -#endif - /* Note that conn->Pfdebug is not ours to close or free */ - if (conn->last_query) - free(conn->last_query); - if (conn->inBuffer) - free(conn->inBuffer); - if (conn->outBuffer) - free(conn->outBuffer); - if (conn->rowBuf) - free(conn->rowBuf); - termPQExpBuffer(&conn->errorMessage); - termPQExpBuffer(&conn->workBuffer); - - free(conn); - -#ifdef WIN32 - WSACleanup(); -#endif -} - -/* - * closePGconn - * - properly close a connection to the backend - * - * This should reset or release all transient state, but NOT the connection - * parameters. On exit, the PGconn should be in condition to start a fresh - * connection with the same parameters (see PQreset()). - */ -static void -closePGconn(PGconn *conn) -{ - PGnotify *notify; - pgParameterStatus *pstatus; - - /* - * Note that the protocol doesn't allow us to send Terminate messages - * during the startup phase. - */ - if (conn->sock != PGINVALID_SOCKET && conn->status == CONNECTION_OK) - { - /* - * Try to send "close connection" message to backend. Ignore any - * error. - */ - pqPutMsgStart('X', false, conn); - pqPutMsgEnd(conn); - (void) pqFlush(conn); - } - - /* - * Must reset the blocking status so a possible reconnect will work. - * - * Don't call PQsetnonblocking() because it will fail if it's unable to - * flush the connection. - */ - conn->nonblocking = FALSE; - - /* - * Close the connection, reset all transient state, flush I/O buffers. - */ - pqDropConnection(conn, true); - conn->status = CONNECTION_BAD; /* Well, not really _bad_ - just - * absent */ - conn->asyncStatus = PGASYNC_IDLE; - pqClearAsyncResult(conn); /* deallocate result */ - resetPQExpBuffer(&conn->errorMessage); - pg_freeaddrinfo_all(conn->addrlist_family, conn->addrlist); - conn->addrlist = NULL; - conn->addr_cur = NULL; - notify = conn->notifyHead; - while (notify != NULL) - { - PGnotify *prev = notify; - - notify = notify->next; - free(prev); - } - conn->notifyHead = conn->notifyTail = NULL; - pstatus = conn->pstatus; - while (pstatus != NULL) - { - pgParameterStatus *prev = pstatus; - - pstatus = pstatus->next; - free(prev); - } - conn->pstatus = NULL; - if (conn->lobjfuncs) - free(conn->lobjfuncs); - conn->lobjfuncs = NULL; -} - -/* - * PQfinish: properly close a connection to the backend. Also frees - * the PGconn data structure so it shouldn't be re-used after this. - */ -void -PQfinish(PGconn *conn) -{ - if (conn) - { - closePGconn(conn); - freePGconn(conn); - } -} - -/* - * PQreset: resets the connection to the backend by closing the - * existing connection and creating a new one. - */ -void -PQreset(PGconn *conn) -{ - if (conn) - { - closePGconn(conn); - - if (connectDBStart(conn) && connectDBComplete(conn)) - { - /* - * Notify event procs of successful reset. We treat an event proc - * failure as disabling the connection ... good idea? - */ - int i; - - for (i = 0; i < conn->nEvents; i++) - { - PGEventConnReset evt; - - evt.conn = conn; - if (!conn->events[i].proc(PGEVT_CONNRESET, &evt, - conn->events[i].passThrough)) - { - conn->status = CONNECTION_BAD; - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("PGEventProc \"%s\" failed during PGEVT_CONNRESET event\n"), - conn->events[i].name); - break; - } - } - } - } -} - - -/* - * PQresetStart: - * resets the connection to the backend - * closes the existing connection and makes a new one - * Returns 1 on success, 0 on failure. - */ -int -PQresetStart(PGconn *conn) -{ - if (conn) - { - closePGconn(conn); - - return connectDBStart(conn); - } - - return 0; -} - - -/* - * PQresetPoll: - * resets the connection to the backend - * closes the existing connection and makes a new one - */ -PostgresPollingStatusType -PQresetPoll(PGconn *conn) -{ - if (conn) - { - PostgresPollingStatusType status = PQconnectPoll(conn); - - if (status == PGRES_POLLING_OK) - { - /* - * Notify event procs of successful reset. We treat an event proc - * failure as disabling the connection ... good idea? - */ - int i; - - for (i = 0; i < conn->nEvents; i++) - { - PGEventConnReset evt; - - evt.conn = conn; - if (!conn->events[i].proc(PGEVT_CONNRESET, &evt, - conn->events[i].passThrough)) - { - conn->status = CONNECTION_BAD; - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("PGEventProc \"%s\" failed during PGEVT_CONNRESET event\n"), - conn->events[i].name); - return PGRES_POLLING_FAILED; - } - } - } - - return status; - } - - return PGRES_POLLING_FAILED; -} - -/* - * PQgetCancel: get a PGcancel structure corresponding to a connection. - * - * A copy is needed to be able to cancel a running query from a different - * thread. If the same structure is used all structure members would have - * to be individually locked (if the entire structure was locked, it would - * be impossible to cancel a synchronous query because the structure would - * have to stay locked for the duration of the query). - */ -PGcancel * -PQgetCancel(PGconn *conn) -{ - PGcancel *cancel; - - if (!conn) - return NULL; - - if (conn->sock == PGINVALID_SOCKET) - return NULL; - - cancel = malloc(sizeof(PGcancel)); - if (cancel == NULL) - return NULL; - - memcpy(&cancel->raddr, &conn->raddr, sizeof(SockAddr)); - cancel->be_pid = conn->be_pid; - cancel->be_key = conn->be_key; - - return cancel; -} - -/* PQfreeCancel: free a cancel structure */ -void -PQfreeCancel(PGcancel *cancel) -{ - if (cancel) - free(cancel); -} - - -/* - * PQcancel and PQrequestCancel: attempt to request cancellation of the - * current operation. - * - * The return value is TRUE if the cancel request was successfully - * dispatched, FALSE if not (in which case an error message is available). - * Note: successful dispatch is no guarantee that there will be any effect at - * the backend. The application must read the operation result as usual. - * - * CAUTION: we want this routine to be safely callable from a signal handler - * (for example, an application might want to call it in a SIGINT handler). - * This means we cannot use any C library routine that might be non-reentrant. - * malloc/free are often non-reentrant, and anything that might call them is - * just as dangerous. We avoid sprintf here for that reason. Building up - * error messages with strcpy/strcat is tedious but should be quite safe. - * We also save/restore errno in case the signal handler support doesn't. - * - * internal_cancel() is an internal helper function to make code-sharing - * between the two versions of the cancel function possible. - */ -static int -internal_cancel(SockAddr *raddr, int be_pid, int be_key, - char *errbuf, int errbufsize) -{ - int save_errno = SOCK_ERRNO; - pgsocket tmpsock = PGINVALID_SOCKET; - char sebuf[256]; - int maxlen; - struct - { - uint32 packetlen; - CancelRequestPacket cp; - } crp; - - /* - * We need to open a temporary connection to the postmaster. Do this with - * only kernel calls. - */ - if ((tmpsock = socket(raddr->addr.ss_family, SOCK_STREAM, 0)) == PGINVALID_SOCKET) - { - strlcpy(errbuf, "PQcancel() -- socket() failed: ", errbufsize); - goto cancel_errReturn; - } -retry3: - if (connect(tmpsock, (struct sockaddr *) & raddr->addr, - raddr->salen) < 0) - { - if (SOCK_ERRNO == EINTR) - /* Interrupted system call - we'll just try again */ - goto retry3; - strlcpy(errbuf, "PQcancel() -- connect() failed: ", errbufsize); - goto cancel_errReturn; - } - - /* - * We needn't set nonblocking I/O or NODELAY options here. - */ - - /* Create and send the cancel request packet. */ - - crp.packetlen = htonl((uint32) sizeof(crp)); - crp.cp.cancelRequestCode = (MsgType) htonl(CANCEL_REQUEST_CODE); - crp.cp.backendPID = htonl(be_pid); - crp.cp.cancelAuthCode = htonl(be_key); - -retry4: - if (send(tmpsock, (char *) &crp, sizeof(crp), 0) != (int) sizeof(crp)) - { - if (SOCK_ERRNO == EINTR) - /* Interrupted system call - we'll just try again */ - goto retry4; - strlcpy(errbuf, "PQcancel() -- send() failed: ", errbufsize); - goto cancel_errReturn; - } - - /* - * Wait for the postmaster to close the connection, which indicates that - * it's processed the request. Without this delay, we might issue another - * command only to find that our cancel zaps that command instead of the - * one we thought we were canceling. Note we don't actually expect this - * read to obtain any data, we are just waiting for EOF to be signaled. - */ -retry5: - if (recv(tmpsock, (char *) &crp, 1, 0) < 0) - { - if (SOCK_ERRNO == EINTR) - /* Interrupted system call - we'll just try again */ - goto retry5; - /* we ignore other error conditions */ - } - - /* All done */ - closesocket(tmpsock); - SOCK_ERRNO_SET(save_errno); - return TRUE; - -cancel_errReturn: - - /* - * Make sure we don't overflow the error buffer. Leave space for the \n at - * the end, and for the terminating zero. - */ - maxlen = errbufsize - strlen(errbuf) - 2; - if (maxlen >= 0) - { - strncat(errbuf, SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)), - maxlen); - strcat(errbuf, "\n"); - } - if (tmpsock != PGINVALID_SOCKET) - closesocket(tmpsock); - SOCK_ERRNO_SET(save_errno); - return FALSE; -} - -/* - * PQcancel: request query cancel - * - * Returns TRUE if able to send the cancel request, FALSE if not. - * - * On failure, an error message is stored in *errbuf, which must be of size - * errbufsize (recommended size is 256 bytes). *errbuf is not changed on - * success return. - */ -int -PQcancel(PGcancel *cancel, char *errbuf, int errbufsize) -{ - if (!cancel) - { - strlcpy(errbuf, "PQcancel() -- no cancel object supplied", errbufsize); - return FALSE; - } - - return internal_cancel(&cancel->raddr, cancel->be_pid, cancel->be_key, - errbuf, errbufsize); -} - -/* - * PQrequestCancel: old, not thread-safe function for requesting query cancel - * - * Returns TRUE if able to send the cancel request, FALSE if not. - * - * On failure, the error message is saved in conn->errorMessage; this means - * that this can't be used when there might be other active operations on - * the connection object. - * - * NOTE: error messages will be cut off at the current size of the - * error message buffer, since we dare not try to expand conn->errorMessage! - */ -int -PQrequestCancel(PGconn *conn) -{ - int r; - - /* Check we have an open connection */ - if (!conn) - return FALSE; - - if (conn->sock == PGINVALID_SOCKET) - { - strlcpy(conn->errorMessage.data, - "PQrequestCancel() -- connection is not open\n", - conn->errorMessage.maxlen); - conn->errorMessage.len = strlen(conn->errorMessage.data); - - return FALSE; - } - - r = internal_cancel(&conn->raddr, conn->be_pid, conn->be_key, - conn->errorMessage.data, conn->errorMessage.maxlen); - - if (!r) - conn->errorMessage.len = strlen(conn->errorMessage.data); - - return r; -} - - -/* - * pqPacketSend() -- convenience routine to send a message to server. - * - * pack_type: the single-byte message type code. (Pass zero for startup - * packets, which have no message type code.) - * - * buf, buf_len: contents of message. The given length includes only what - * is in buf; the message type and message length fields are added here. - * - * RETURNS: STATUS_ERROR if the write fails, STATUS_OK otherwise. - * SIDE_EFFECTS: may block. - * - * Note: all messages sent with this routine have a length word, whether - * it's protocol 2.0 or 3.0. - */ -int -pqPacketSend(PGconn *conn, char pack_type, - const void *buf, size_t buf_len) -{ - /* Start the message. */ - if (pqPutMsgStart(pack_type, true, conn)) - return STATUS_ERROR; - - /* Send the message body. */ - if (pqPutnchar(buf, buf_len, conn)) - return STATUS_ERROR; - - /* Finish the message. */ - if (pqPutMsgEnd(conn)) - return STATUS_ERROR; - - /* Flush to ensure backend gets it. */ - if (pqFlush(conn)) - return STATUS_ERROR; - - return STATUS_OK; -} - -#ifdef USE_LDAP - -#define LDAP_URL "ldap://" -#define LDAP_DEF_PORT 389 -#define PGLDAP_TIMEOUT 2 - -#define ld_is_sp_tab(x) ((x) == ' ' || (x) == '\t') -#define ld_is_nl_cr(x) ((x) == '\r' || (x) == '\n') - - -/* - * ldapServiceLookup - * - * Search the LDAP URL passed as first argument, treat the result as a - * string of connection options that are parsed and added to the array of - * options passed as second argument. - * - * LDAP URLs must conform to RFC 1959 without escape sequences. - * ldap://host:port/dn?attributes?scope?filter?extensions - * - * Returns - * 0 if the lookup was successful, - * 1 if the connection to the LDAP server could be established but - * the search was unsuccessful, - * 2 if a connection could not be established, and - * 3 if a fatal error occurred. - * - * An error message is returned in the third argument for return codes 1 and 3. - */ -static int -ldapServiceLookup(const char *purl, PQconninfoOption *options, - PQExpBuffer errorMessage) -{ - int port = LDAP_DEF_PORT, - scope, - rc, - size, - state, - oldstate, - i; -#ifndef WIN32 - int msgid; -#endif - bool found_keyword; - char *url, - *hostname, - *portstr, - *endptr, - *dn, - *scopestr, - *filter, - *result, - *p, - *p1 = NULL, - *optname = NULL, - *optval = NULL; - char *attrs[2] = {NULL, NULL}; - LDAP *ld = NULL; - LDAPMessage *res, - *entry; - struct berval **values; - LDAP_TIMEVAL time = {PGLDAP_TIMEOUT, 0}; - - if ((url = strdup(purl)) == NULL) - { - printfPQExpBuffer(errorMessage, libpq_gettext("out of memory\n")); - return 3; - } - - /* - * Parse URL components, check for correctness. Basically, url has '\0' - * placed at component boundaries and variables are pointed at each - * component. - */ - - if (pg_strncasecmp(url, LDAP_URL, strlen(LDAP_URL)) != 0) - { - printfPQExpBuffer(errorMessage, - libpq_gettext("invalid LDAP URL \"%s\": scheme must be ldap://\n"), purl); - free(url); - return 3; - } - - /* hostname */ - hostname = url + strlen(LDAP_URL); - if (*hostname == '/') /* no hostname? */ - hostname = DefaultHost; /* the default */ - - /* dn, "distinguished name" */ - p = strchr(url + strlen(LDAP_URL), '/'); - if (p == NULL || *(p + 1) == '\0' || *(p + 1) == '?') - { - printfPQExpBuffer(errorMessage, libpq_gettext( - "invalid LDAP URL \"%s\": missing distinguished name\n"), purl); - free(url); - return 3; - } - *p = '\0'; /* terminate hostname */ - dn = p + 1; - - /* attribute */ - if ((p = strchr(dn, '?')) == NULL || *(p + 1) == '\0' || *(p + 1) == '?') - { - printfPQExpBuffer(errorMessage, libpq_gettext( - "invalid LDAP URL \"%s\": must have exactly one attribute\n"), purl); - free(url); - return 3; - } - *p = '\0'; - attrs[0] = p + 1; - - /* scope */ - if ((p = strchr(attrs[0], '?')) == NULL || *(p + 1) == '\0' || *(p + 1) == '?') - { - printfPQExpBuffer(errorMessage, libpq_gettext("invalid LDAP URL \"%s\": must have search scope (base/one/sub)\n"), purl); - free(url); - return 3; - } - *p = '\0'; - scopestr = p + 1; - - /* filter */ - if ((p = strchr(scopestr, '?')) == NULL || *(p + 1) == '\0' || *(p + 1) == '?') - { - printfPQExpBuffer(errorMessage, - libpq_gettext("invalid LDAP URL \"%s\": no filter\n"), purl); - free(url); - return 3; - } - *p = '\0'; - filter = p + 1; - if ((p = strchr(filter, '?')) != NULL) - *p = '\0'; - - /* port number? */ - if ((p1 = strchr(hostname, ':')) != NULL) - { - long lport; - - *p1 = '\0'; - portstr = p1 + 1; - errno = 0; - lport = strtol(portstr, &endptr, 10); - if (*portstr == '\0' || *endptr != '\0' || errno || lport < 0 || lport > 65535) - { - printfPQExpBuffer(errorMessage, libpq_gettext( - "invalid LDAP URL \"%s\": invalid port number\n"), purl); - free(url); - return 3; - } - port = (int) lport; - } - - /* Allow only one attribute */ - if (strchr(attrs[0], ',') != NULL) - { - printfPQExpBuffer(errorMessage, libpq_gettext( - "invalid LDAP URL \"%s\": must have exactly one attribute\n"), purl); - free(url); - return 3; - } - - /* set scope */ - if (pg_strcasecmp(scopestr, "base") == 0) - scope = LDAP_SCOPE_BASE; - else if (pg_strcasecmp(scopestr, "one") == 0) - scope = LDAP_SCOPE_ONELEVEL; - else if (pg_strcasecmp(scopestr, "sub") == 0) - scope = LDAP_SCOPE_SUBTREE; - else - { - printfPQExpBuffer(errorMessage, libpq_gettext("invalid LDAP URL \"%s\": must have search scope (base/one/sub)\n"), purl); - free(url); - return 3; - } - - /* initialize LDAP structure */ - if ((ld = ldap_init(hostname, port)) == NULL) - { - printfPQExpBuffer(errorMessage, - libpq_gettext("could not create LDAP structure\n")); - free(url); - return 3; - } - - /* - * Perform an explicit anonymous bind. - * - * LDAP does not require that an anonymous bind is performed explicitly, - * but we want to distinguish between the case where LDAP bind does not - * succeed within PGLDAP_TIMEOUT seconds (return 2 to continue parsing the - * service control file) and the case where querying the LDAP server fails - * (return 1 to end parsing). - * - * Unfortunately there is no way of setting a timeout that works for both - * Windows and OpenLDAP. - */ -#ifdef WIN32 - /* the nonstandard ldap_connect function performs an anonymous bind */ - if (ldap_connect(ld, &time) != LDAP_SUCCESS) - { - /* error or timeout in ldap_connect */ - free(url); - ldap_unbind(ld); - return 2; - } -#else /* !WIN32 */ - /* in OpenLDAP, use the LDAP_OPT_NETWORK_TIMEOUT option */ - if (ldap_set_option(ld, LDAP_OPT_NETWORK_TIMEOUT, &time) != LDAP_SUCCESS) - { - free(url); - ldap_unbind(ld); - return 3; - } - - /* anonymous bind */ - if ((msgid = ldap_simple_bind(ld, NULL, NULL)) == -1) - { - /* error or network timeout */ - free(url); - ldap_unbind(ld); - return 2; - } - - /* wait some time for the connection to succeed */ - res = NULL; - if ((rc = ldap_result(ld, msgid, LDAP_MSG_ALL, &time, &res)) == -1 || - res == NULL) - { - /* error or timeout */ - if (res != NULL) - ldap_msgfree(res); - free(url); - ldap_unbind(ld); - return 2; - } - ldap_msgfree(res); - - /* reset timeout */ - time.tv_sec = -1; - if (ldap_set_option(ld, LDAP_OPT_NETWORK_TIMEOUT, &time) != LDAP_SUCCESS) - { - free(url); - ldap_unbind(ld); - return 3; - } -#endif /* WIN32 */ - - /* search */ - res = NULL; - if ((rc = ldap_search_st(ld, dn, scope, filter, attrs, 0, &time, &res)) - != LDAP_SUCCESS) - { - if (res != NULL) - ldap_msgfree(res); - printfPQExpBuffer(errorMessage, - libpq_gettext("lookup on LDAP server failed: %s\n"), - ldap_err2string(rc)); - ldap_unbind(ld); - free(url); - return 1; - } - - /* complain if there was not exactly one result */ - if ((rc = ldap_count_entries(ld, res)) != 1) - { - printfPQExpBuffer(errorMessage, - rc ? libpq_gettext("more than one entry found on LDAP lookup\n") - : libpq_gettext("no entry found on LDAP lookup\n")); - ldap_msgfree(res); - ldap_unbind(ld); - free(url); - return 1; - } - - /* get entry */ - if ((entry = ldap_first_entry(ld, res)) == NULL) - { - /* should never happen */ - printfPQExpBuffer(errorMessage, - libpq_gettext("no entry found on LDAP lookup\n")); - ldap_msgfree(res); - ldap_unbind(ld); - free(url); - return 1; - } - - /* get values */ - if ((values = ldap_get_values_len(ld, entry, attrs[0])) == NULL) - { - printfPQExpBuffer(errorMessage, - libpq_gettext("attribute has no values on LDAP lookup\n")); - ldap_msgfree(res); - ldap_unbind(ld); - free(url); - return 1; - } - - ldap_msgfree(res); - free(url); - - if (values[0] == NULL) - { - printfPQExpBuffer(errorMessage, - libpq_gettext("attribute has no values on LDAP lookup\n")); - ldap_value_free_len(values); - ldap_unbind(ld); - return 1; - } - - /* concatenate values into a single string with newline terminators */ - size = 1; /* for the trailing null */ - for (i = 0; values[i] != NULL; i++) - size += values[i]->bv_len + 1; - if ((result = malloc(size)) == NULL) - { - printfPQExpBuffer(errorMessage, - libpq_gettext("out of memory\n")); - ldap_value_free_len(values); - ldap_unbind(ld); - return 3; - } - p = result; - for (i = 0; values[i] != NULL; i++) - { - memcpy(p, values[i]->bv_val, values[i]->bv_len); - p += values[i]->bv_len; - *(p++) = '\n'; - } - *p = '\0'; - - ldap_value_free_len(values); - ldap_unbind(ld); - - /* parse result string */ - oldstate = state = 0; - for (p = result; *p != '\0'; ++p) - { - switch (state) - { - case 0: /* between entries */ - if (!ld_is_sp_tab(*p) && !ld_is_nl_cr(*p)) - { - optname = p; - state = 1; - } - break; - case 1: /* in option name */ - if (ld_is_sp_tab(*p)) - { - *p = '\0'; - state = 2; - } - else if (ld_is_nl_cr(*p)) - { - printfPQExpBuffer(errorMessage, libpq_gettext( - "missing \"=\" after \"%s\" in connection info string\n"), - optname); - free(result); - return 3; - } - else if (*p == '=') - { - *p = '\0'; - state = 3; - } - break; - case 2: /* after option name */ - if (*p == '=') - { - state = 3; - } - else if (!ld_is_sp_tab(*p)) - { - printfPQExpBuffer(errorMessage, libpq_gettext( - "missing \"=\" after \"%s\" in connection info string\n"), - optname); - free(result); - return 3; - } - break; - case 3: /* before option value */ - if (*p == '\'') - { - optval = p + 1; - p1 = p + 1; - state = 5; - } - else if (ld_is_nl_cr(*p)) - { - optval = optname + strlen(optname); /* empty */ - state = 0; - } - else if (!ld_is_sp_tab(*p)) - { - optval = p; - state = 4; - } - break; - case 4: /* in unquoted option value */ - if (ld_is_sp_tab(*p) || ld_is_nl_cr(*p)) - { - *p = '\0'; - state = 0; - } - break; - case 5: /* in quoted option value */ - if (*p == '\'') - { - *p1 = '\0'; - state = 0; - } - else if (*p == '\\') - state = 6; - else - *(p1++) = *p; - break; - case 6: /* in quoted option value after escape */ - *(p1++) = *p; - state = 5; - break; - } - - if (state == 0 && oldstate != 0) - { - found_keyword = false; - for (i = 0; options[i].keyword; i++) - { - if (strcmp(options[i].keyword, optname) == 0) - { - if (options[i].val == NULL) - { - options[i].val = strdup(optval); - if (!options[i].val) - { - printfPQExpBuffer(errorMessage, - libpq_gettext("out of memory\n")); - free(result); - return 3; - } - } - found_keyword = true; - break; - } - } - if (!found_keyword) - { - printfPQExpBuffer(errorMessage, - libpq_gettext("invalid connection option \"%s\"\n"), - optname); - free(result); - return 1; - } - optname = NULL; - optval = NULL; - } - oldstate = state; - } - - free(result); - - if (state == 5 || state == 6) - { - printfPQExpBuffer(errorMessage, libpq_gettext( - "unterminated quoted string in connection info string\n")); - return 3; - } - - return 0; -} - -#endif /* USE_LDAP */ - -#define MAXBUFSIZE 256 - -static int -parseServiceInfo(PQconninfoOption *options, PQExpBuffer errorMessage) -{ - const char *service = conninfo_getval(options, "service"); - char serviceFile[MAXPGPATH]; - char *env; - bool group_found = false; - int status; - struct stat stat_buf; - - /* - * We have to special-case the environment variable PGSERVICE here, since - * this is and should be called before inserting environment defaults for - * other connection options. - */ - if (service == NULL) - service = getenv("PGSERVICE"); - - if (service == NULL) - return 0; - - if ((env = getenv("PGSERVICEFILE")) != NULL) - strlcpy(serviceFile, env, sizeof(serviceFile)); - else - { - char homedir[MAXPGPATH]; - - if (!pqGetHomeDirectory(homedir, sizeof(homedir))) - { - printfPQExpBuffer(errorMessage, libpq_gettext("could not get home directory to locate service definition file")); - return 1; - } - snprintf(serviceFile, MAXPGPATH, "%s/%s", homedir, ".pg_service.conf"); - errno = 0; - if (stat(serviceFile, &stat_buf) != 0 && errno == ENOENT) - goto next_file; - } - - status = parseServiceFile(serviceFile, service, options, errorMessage, &group_found); - if (group_found || status != 0) - return status; - -next_file: - - /* - * This could be used by any application so we can't use the binary - * location to find our config files. - */ - snprintf(serviceFile, MAXPGPATH, "%s/pg_service.conf", - getenv("PGSYSCONFDIR") ? getenv("PGSYSCONFDIR") : SYSCONFDIR); - errno = 0; - if (stat(serviceFile, &stat_buf) != 0 && errno == ENOENT) - goto last_file; - - status = parseServiceFile(serviceFile, service, options, errorMessage, &group_found); - if (status != 0) - return status; - -last_file: - if (!group_found) - { - printfPQExpBuffer(errorMessage, - libpq_gettext("definition of service \"%s\" not found\n"), service); - return 3; - } - - return 0; -} - -static int -parseServiceFile(const char *serviceFile, - const char *service, - PQconninfoOption *options, - PQExpBuffer errorMessage, - bool *group_found) -{ - int linenr = 0, - i; - FILE *f; - char buf[MAXBUFSIZE], - *line; - - f = fopen(serviceFile, "r"); - if (f == NULL) - { - printfPQExpBuffer(errorMessage, libpq_gettext("service file \"%s\" not found\n"), - serviceFile); - return 1; - } - - while ((line = fgets(buf, sizeof(buf), f)) != NULL) - { - linenr++; - - if (strlen(line) >= sizeof(buf) - 1) - { - fclose(f); - printfPQExpBuffer(errorMessage, - libpq_gettext("line %d too long in service file \"%s\"\n"), - linenr, - serviceFile); - return 2; - } - - /* ignore EOL at end of line */ - if (strlen(line) && line[strlen(line) - 1] == '\n') - line[strlen(line) - 1] = 0; - - /* ignore leading blanks */ - while (*line && isspace((unsigned char) line[0])) - line++; - - /* ignore comments and empty lines */ - if (strlen(line) == 0 || line[0] == '#') - continue; - - /* Check for right groupname */ - if (line[0] == '[') - { - if (*group_found) - { - /* group info already read */ - fclose(f); - return 0; - } - - if (strncmp(line + 1, service, strlen(service)) == 0 && - line[strlen(service) + 1] == ']') - *group_found = true; - else - *group_found = false; - } - else - { - if (*group_found) - { - /* - * Finally, we are in the right group and can parse the line - */ - char *key, - *val; - bool found_keyword; - -#ifdef USE_LDAP - if (strncmp(line, "ldap", 4) == 0) - { - int rc = ldapServiceLookup(line, options, errorMessage); - - /* if rc = 2, go on reading for fallback */ - switch (rc) - { - case 0: - fclose(f); - return 0; - case 1: - case 3: - fclose(f); - return 3; - case 2: - continue; - } - } -#endif - - key = line; - val = strchr(line, '='); - if (val == NULL) - { - printfPQExpBuffer(errorMessage, - libpq_gettext("syntax error in service file \"%s\", line %d\n"), - serviceFile, - linenr); - fclose(f); - return 3; - } - *val++ = '\0'; - - if (strcmp(key, "service") == 0) - { - printfPQExpBuffer(errorMessage, - libpq_gettext("nested service specifications not supported in service file \"%s\", line %d\n"), - serviceFile, - linenr); - fclose(f); - return 3; - } - - /* - * Set the parameter --- but don't override any previous - * explicit setting. - */ - found_keyword = false; - for (i = 0; options[i].keyword; i++) - { - if (strcmp(options[i].keyword, key) == 0) - { - if (options[i].val == NULL) - options[i].val = strdup(val); - if (!options[i].val) - { - printfPQExpBuffer(errorMessage, - libpq_gettext("out of memory\n")); - fclose(f); - return 3; - } - found_keyword = true; - break; - } - } - - if (!found_keyword) - { - printfPQExpBuffer(errorMessage, - libpq_gettext("syntax error in service file \"%s\", line %d\n"), - serviceFile, - linenr); - fclose(f); - return 3; - } - } - } - } - - fclose(f); - - return 0; -} - - -/* - * PQconninfoParse - * - * Parse a string like PQconnectdb() would do and return the - * resulting connection options array. NULL is returned on failure. - * The result contains only options specified directly in the string, - * not any possible default values. - * - * If errmsg isn't NULL, *errmsg is set to NULL on success, or a malloc'd - * string on failure (use PQfreemem to free it). In out-of-memory conditions - * both *errmsg and the result could be NULL. - * - * NOTE: the returned array is dynamically allocated and should - * be freed when no longer needed via PQconninfoFree(). - */ -PQconninfoOption * -PQconninfoParse(const char *conninfo, char **errmsg) -{ - PQExpBufferData errorBuf; - PQconninfoOption *connOptions; - - if (errmsg) - *errmsg = NULL; /* default */ - initPQExpBuffer(&errorBuf); - if (PQExpBufferDataBroken(errorBuf)) - return NULL; /* out of memory already :-( */ - connOptions = parse_connection_string(conninfo, &errorBuf, false); - if (connOptions == NULL && errmsg) - *errmsg = errorBuf.data; - else - termPQExpBuffer(&errorBuf); - return connOptions; -} - -/* - * Build a working copy of the constant PQconninfoOptions array. - */ -static PQconninfoOption * -conninfo_init(PQExpBuffer errorMessage) -{ - PQconninfoOption *options; - PQconninfoOption *opt_dest; - const internalPQconninfoOption *cur_opt; - - /* - * Get enough memory for all options in PQconninfoOptions, even if some - * end up being filtered out. - */ - options = (PQconninfoOption *) malloc(sizeof(PQconninfoOption) * sizeof(PQconninfoOptions) / sizeof(PQconninfoOptions[0])); - if (options == NULL) - { - printfPQExpBuffer(errorMessage, - libpq_gettext("out of memory\n")); - return NULL; - } - opt_dest = options; - - for (cur_opt = PQconninfoOptions; cur_opt->keyword; cur_opt++) - { - /* Only copy the public part of the struct, not the full internal */ - memcpy(opt_dest, cur_opt, sizeof(PQconninfoOption)); - opt_dest++; - } - MemSet(opt_dest, 0, sizeof(PQconninfoOption)); - - return options; -} - -/* - * Connection string parser - * - * Returns a malloc'd PQconninfoOption array, if parsing is successful. - * Otherwise, NULL is returned and an error message is left in errorMessage. - * - * If use_defaults is TRUE, default values are filled in (from a service file, - * environment variables, etc). - */ -static PQconninfoOption * -parse_connection_string(const char *connstr, PQExpBuffer errorMessage, - bool use_defaults) -{ - /* Parse as URI if connection string matches URI prefix */ - if (uri_prefix_length(connstr) != 0) - return conninfo_uri_parse(connstr, errorMessage, use_defaults); - - /* Parse as default otherwise */ - return conninfo_parse(connstr, errorMessage, use_defaults); -} - -/* - * Checks if connection string starts with either of the valid URI prefix - * designators. - * - * Returns the URI prefix length, 0 if the string doesn't contain a URI prefix. - * - * XXX this is duplicated in psql/common.c. - */ -static int -uri_prefix_length(const char *connstr) -{ - if (strncmp(connstr, uri_designator, - sizeof(uri_designator) - 1) == 0) - return sizeof(uri_designator) - 1; - - if (strncmp(connstr, short_uri_designator, - sizeof(short_uri_designator) - 1) == 0) - return sizeof(short_uri_designator) - 1; - - return 0; -} - -/* - * Recognized connection string either starts with a valid URI prefix or - * contains a "=" in it. - * - * Must be consistent with parse_connection_string: anything for which this - * returns true should at least look like it's parseable by that routine. - * - * XXX this is duplicated in psql/common.c - */ -static bool -recognized_connection_string(const char *connstr) -{ - return uri_prefix_length(connstr) != 0 || strchr(connstr, '=') != NULL; -} - -/* - * Subroutine for parse_connection_string - * - * Deal with a string containing key=value pairs. - */ -static PQconninfoOption * -conninfo_parse(const char *conninfo, PQExpBuffer errorMessage, - bool use_defaults) -{ - char *pname; - char *pval; - char *buf; - char *cp; - char *cp2; - PQconninfoOption *options; - - /* Make a working copy of PQconninfoOptions */ - options = conninfo_init(errorMessage); - if (options == NULL) - return NULL; - - /* Need a modifiable copy of the input string */ - if ((buf = strdup(conninfo)) == NULL) - { - printfPQExpBuffer(errorMessage, - libpq_gettext("out of memory\n")); - PQconninfoFree(options); - return NULL; - } - cp = buf; - - while (*cp) - { - /* Skip blanks before the parameter name */ - if (isspace((unsigned char) *cp)) - { - cp++; - continue; - } - - /* Get the parameter name */ - pname = cp; - while (*cp) - { - if (*cp == '=') - break; - if (isspace((unsigned char) *cp)) - { - *cp++ = '\0'; - while (*cp) - { - if (!isspace((unsigned char) *cp)) - break; - cp++; - } - break; - } - cp++; - } - - /* Check that there is a following '=' */ - if (*cp != '=') - { - printfPQExpBuffer(errorMessage, - libpq_gettext("missing \"=\" after \"%s\" in connection info string\n"), - pname); - PQconninfoFree(options); - free(buf); - return NULL; - } - *cp++ = '\0'; - - /* Skip blanks after the '=' */ - while (*cp) - { - if (!isspace((unsigned char) *cp)) - break; - cp++; - } - - /* Get the parameter value */ - pval = cp; - - if (*cp != '\'') - { - cp2 = pval; - while (*cp) - { - if (isspace((unsigned char) *cp)) - { - *cp++ = '\0'; - break; - } - if (*cp == '\\') - { - cp++; - if (*cp != '\0') - *cp2++ = *cp++; - } - else - *cp2++ = *cp++; - } - *cp2 = '\0'; - } - else - { - cp2 = pval; - cp++; - for (;;) - { - if (*cp == '\0') - { - printfPQExpBuffer(errorMessage, - libpq_gettext("unterminated quoted string in connection info string\n")); - PQconninfoFree(options); - free(buf); - return NULL; - } - if (*cp == '\\') - { - cp++; - if (*cp != '\0') - *cp2++ = *cp++; - continue; - } - if (*cp == '\'') - { - *cp2 = '\0'; - cp++; - break; - } - *cp2++ = *cp++; - } - } - - /* - * Now that we have the name and the value, store the record. - */ - if (!conninfo_storeval(options, pname, pval, errorMessage, false, false)) - { - PQconninfoFree(options); - free(buf); - return NULL; - } - } - - /* Done with the modifiable input string */ - free(buf); - - /* - * Add in defaults if the caller wants that. - */ - if (use_defaults) - { - if (!conninfo_add_defaults(options, errorMessage)) - { - PQconninfoFree(options); - return NULL; - } - } - - return options; -} - -/* - * Conninfo array parser routine - * - * If successful, a malloc'd PQconninfoOption array is returned. - * If not successful, NULL is returned and an error message is - * left in errorMessage. - * Defaults are supplied (from a service file, environment variables, etc) - * for unspecified options, but only if use_defaults is TRUE. - * - * If expand_dbname is non-zero, and the value passed for the first occurrence - * of "dbname" keyword is a connection string (as indicated by - * recognized_connection_string) then parse and process it, overriding any - * previously processed conflicting keywords. Subsequent keywords will take - * precedence, however. In-tree programs generally specify expand_dbname=true, - * so command-line arguments naming a database can use a connection string. - * Some code acquires arbitrary database names from known-literal sources like - * PQdb(), PQconninfoParse() and pg_database.datname. When connecting to such - * a database, in-tree code first wraps the name in a connection string. - */ -static PQconninfoOption * -conninfo_array_parse(const char *const * keywords, const char *const * values, - PQExpBuffer errorMessage, bool use_defaults, - int expand_dbname) -{ - PQconninfoOption *options; - PQconninfoOption *dbname_options = NULL; - PQconninfoOption *option; - int i = 0; - - /* - * If expand_dbname is non-zero, check keyword "dbname" to see if val is - * actually a recognized connection string. - */ - while (expand_dbname && keywords[i]) - { - const char *pname = keywords[i]; - const char *pvalue = values[i]; - - /* first find "dbname" if any */ - if (strcmp(pname, "dbname") == 0 && pvalue) - { - /* - * If value is a connection string, parse it, but do not use - * defaults here -- those get picked up later. We only want to - * override for those parameters actually passed. - */ - if (recognized_connection_string(pvalue)) - { - dbname_options = parse_connection_string(pvalue, errorMessage, false); - if (dbname_options == NULL) - return NULL; - } - break; - } - ++i; - } - - /* Make a working copy of PQconninfoOptions */ - options = conninfo_init(errorMessage); - if (options == NULL) - { - PQconninfoFree(dbname_options); - return NULL; - } - - /* Parse the keywords/values arrays */ - i = 0; - while (keywords[i]) - { - const char *pname = keywords[i]; - const char *pvalue = values[i]; - - if (pvalue != NULL && pvalue[0] != '\0') - { - /* Search for the param record */ - for (option = options; option->keyword != NULL; option++) - { - if (strcmp(option->keyword, pname) == 0) - break; - } - - /* Check for invalid connection option */ - if (option->keyword == NULL) - { - printfPQExpBuffer(errorMessage, - libpq_gettext("invalid connection option \"%s\"\n"), - pname); - PQconninfoFree(options); - PQconninfoFree(dbname_options); - return NULL; - } - - /* - * If we are on the first dbname parameter, and we have a parsed - * connection string, copy those parameters across, overriding any - * existing previous settings. - */ - if (strcmp(pname, "dbname") == 0 && dbname_options) - { - PQconninfoOption *str_option; - - for (str_option = dbname_options; str_option->keyword != NULL; str_option++) - { - if (str_option->val != NULL) - { - int k; - - for (k = 0; options[k].keyword; k++) - { - if (strcmp(options[k].keyword, str_option->keyword) == 0) - { - if (options[k].val) - free(options[k].val); - options[k].val = strdup(str_option->val); - if (!options[k].val) - { - printfPQExpBuffer(errorMessage, - libpq_gettext("out of memory\n")); - PQconninfoFree(options); - PQconninfoFree(dbname_options); - return NULL; - } - break; - } - } - } - } - - /* - * Forget the parsed connection string, so that any subsequent - * dbname parameters will not be expanded. - */ - PQconninfoFree(dbname_options); - dbname_options = NULL; - } - else - { - /* - * Store the value, overriding previous settings - */ - if (option->val) - free(option->val); - option->val = strdup(pvalue); - if (!option->val) - { - printfPQExpBuffer(errorMessage, - libpq_gettext("out of memory\n")); - PQconninfoFree(options); - PQconninfoFree(dbname_options); - return NULL; - } - } - } - ++i; - } - PQconninfoFree(dbname_options); - - /* - * Add in defaults if the caller wants that. - */ - if (use_defaults) - { - if (!conninfo_add_defaults(options, errorMessage)) - { - PQconninfoFree(options); - return NULL; - } - } - - return options; -} - -/* - * Add the default values for any unspecified options to the connection - * options array. - * - * Defaults are obtained from a service file, environment variables, etc. - * - * Returns TRUE if successful, otherwise FALSE; errorMessage, if supplied, - * is filled in upon failure. Note that failure to locate a default value - * is not an error condition here --- we just leave the option's value as - * NULL. - */ -static bool -conninfo_add_defaults(PQconninfoOption *options, PQExpBuffer errorMessage) -{ - PQconninfoOption *option; - char *tmp; - - /* - * If there's a service spec, use it to obtain any not-explicitly-given - * parameters. Ignore error if no error message buffer is passed because - * there is no way to pass back the failure message. - */ - if (parseServiceInfo(options, errorMessage) != 0 && errorMessage) - return false; - - /* - * Get the fallback resources for parameters not specified in the conninfo - * string nor the service. - */ - for (option = options; option->keyword != NULL; option++) - { - if (option->val != NULL) - continue; /* Value was in conninfo or service */ - - /* - * Try to get the environment variable fallback - */ - if (option->envvar != NULL) - { - if ((tmp = getenv(option->envvar)) != NULL) - { - option->val = strdup(tmp); - if (!option->val) - { - if (errorMessage) - printfPQExpBuffer(errorMessage, - libpq_gettext("out of memory\n")); - return false; - } - continue; - } - } - - /* - * Interpret the deprecated PGREQUIRESSL environment variable. Per - * tradition, translate values starting with "1" to sslmode=require, - * and ignore other values. Given both PGREQUIRESSL=1 and PGSSLMODE, - * PGSSLMODE takes precedence; the opposite was true before v9.3. - */ - if (strcmp(option->keyword, "sslmode") == 0) - { - const char *requiresslenv = getenv("PGREQUIRESSL"); - - if (requiresslenv != NULL && requiresslenv[0] == '1') - { - option->val = strdup("require"); - if (!option->val) - { - if (errorMessage) - printfPQExpBuffer(errorMessage, - libpq_gettext("out of memory\n")); - return false; - } - continue; - } - } - - /* - * No environment variable specified or the variable isn't set - try - * compiled-in default - */ - if (option->compiled != NULL) - { - option->val = strdup(option->compiled); - if (!option->val) - { - if (errorMessage) - printfPQExpBuffer(errorMessage, - libpq_gettext("out of memory\n")); - return false; - } - continue; - } - - /* - * Special handling for "user" option. Note that if pg_fe_getauthname - * fails, we just leave the value as NULL; there's no need for this to - * be an error condition if the caller provides a user name. The only - * reason we do this now at all is so that callers of PQconndefaults - * will see a correct default (barring error, of course). - */ - if (strcmp(option->keyword, "user") == 0) - { - option->val = pg_fe_getauthname(NULL); - continue; - } - } - - return true; -} - -/* - * Subroutine for parse_connection_string - * - * Deal with a URI connection string. - */ -static PQconninfoOption * -conninfo_uri_parse(const char *uri, PQExpBuffer errorMessage, - bool use_defaults) -{ - PQconninfoOption *options; - - /* Make a working copy of PQconninfoOptions */ - options = conninfo_init(errorMessage); - if (options == NULL) - return NULL; - - if (!conninfo_uri_parse_options(options, uri, errorMessage)) - { - PQconninfoFree(options); - return NULL; - } - - /* - * Add in defaults if the caller wants that. - */ - if (use_defaults) - { - if (!conninfo_add_defaults(options, errorMessage)) - { - PQconninfoFree(options); - return NULL; - } - } - - return options; -} - -/* - * conninfo_uri_parse_options - * Actual URI parser. - * - * If successful, returns true while the options array is filled with parsed - * options from the URI. - * If not successful, returns false and fills errorMessage accordingly. - * - * Parses the connection URI string in 'uri' according to the URI syntax (RFC - * 3986): - * - * postgresql://[user[:password]@][netloc][:port][/dbname][?param1=value1&...] - * - * where "netloc" is a hostname, an IPv4 address, or an IPv6 address surrounded - * by literal square brackets. - * - * Any of the URI parts might use percent-encoding (%xy). - */ -static bool -conninfo_uri_parse_options(PQconninfoOption *options, const char *uri, - PQExpBuffer errorMessage) -{ - int prefix_len; - char *p; - char *buf; - char *start; - char prevchar = '\0'; - char *user = NULL; - char *host = NULL; - bool retval = false; - - /* need a modifiable copy of the input URI */ - buf = strdup(uri); - if (buf == NULL) - { - printfPQExpBuffer(errorMessage, - libpq_gettext("out of memory\n")); - return false; - } - start = buf; - - /* Skip the URI prefix */ - prefix_len = uri_prefix_length(uri); - if (prefix_len == 0) - { - /* Should never happen */ - printfPQExpBuffer(errorMessage, - libpq_gettext("invalid URI propagated to internal parser routine: \"%s\"\n"), - uri); - goto cleanup; - } - start += prefix_len; - p = start; - - /* Look ahead for possible user credentials designator */ - while (*p && *p != '@' && *p != '/') - ++p; - if (*p == '@') - { - /* - * Found username/password designator, so URI should be of the form - * "scheme://user[:password]@[netloc]". - */ - user = start; - - p = user; - while (*p != ':' && *p != '@') - ++p; - - /* Save last char and cut off at end of user name */ - prevchar = *p; - *p = '\0'; - - if (*user && - !conninfo_storeval(options, "user", user, - errorMessage, false, true)) - goto cleanup; - - if (prevchar == ':') - { - const char *password = p + 1; - - while (*p != '@') - ++p; - *p = '\0'; - - if (*password && - !conninfo_storeval(options, "password", password, - errorMessage, false, true)) - goto cleanup; - } - - /* Advance past end of parsed user name or password token */ - ++p; - } - else - { - /* - * No username/password designator found. Reset to start of URI. - */ - p = start; - } - - /* - * "p" has been incremented past optional URI credential information at - * this point and now points at the "netloc" part of the URI. - * - * Look for IPv6 address. - */ - if (*p == '[') - { - host = ++p; - while (*p && *p != ']') - ++p; - if (!*p) - { - printfPQExpBuffer(errorMessage, - libpq_gettext("end of string reached when looking for matching \"]\" in IPv6 host address in URI: \"%s\"\n"), - uri); - goto cleanup; - } - if (p == host) - { - printfPQExpBuffer(errorMessage, - libpq_gettext("IPv6 host address may not be empty in URI: \"%s\"\n"), - uri); - goto cleanup; - } - - /* Cut off the bracket and advance */ - *(p++) = '\0'; - - /* - * The address may be followed by a port specifier or a slash or a - * query. - */ - if (*p && *p != ':' && *p != '/' && *p != '?') - { - printfPQExpBuffer(errorMessage, - libpq_gettext("unexpected character \"%c\" at position %d in URI (expected \":\" or \"/\"): \"%s\"\n"), - *p, (int) (p - buf + 1), uri); - goto cleanup; - } - } - else - { - /* not an IPv6 address: DNS-named or IPv4 netloc */ - host = p; - - /* - * Look for port specifier (colon) or end of host specifier (slash), - * or query (question mark). - */ - while (*p && *p != ':' && *p != '/' && *p != '?') - ++p; - } - - /* Save the hostname terminator before we null it */ - prevchar = *p; - *p = '\0'; - - if (*host && - !conninfo_storeval(options, "host", host, - errorMessage, false, true)) - goto cleanup; - - - if (prevchar == ':') - { - const char *port = ++p; /* advance past host terminator */ - - while (*p && *p != '/' && *p != '?') - ++p; - - prevchar = *p; - *p = '\0'; - - if (*port && - !conninfo_storeval(options, "port", port, - errorMessage, false, true)) - goto cleanup; - } - - if (prevchar && prevchar != '?') - { - const char *dbname = ++p; /* advance past host terminator */ - - /* Look for query parameters */ - while (*p && *p != '?') - ++p; - - prevchar = *p; - *p = '\0'; - - /* - * Avoid setting dbname to an empty string, as it forces the default - * value (username) and ignores $PGDATABASE, as opposed to not setting - * it at all. - */ - if (*dbname && - !conninfo_storeval(options, "dbname", dbname, - errorMessage, false, true)) - goto cleanup; - } - - if (prevchar) - { - ++p; /* advance past terminator */ - - if (!conninfo_uri_parse_params(p, options, errorMessage)) - goto cleanup; - } - - /* everything parsed okay */ - retval = true; - -cleanup: - free(buf); - return retval; -} - -/* - * Connection URI parameters parser routine - * - * If successful, returns true while connOptions is filled with parsed - * parameters. Otherwise, returns false and fills errorMessage appropriately. - * - * Destructively modifies 'params' buffer. - */ -static bool -conninfo_uri_parse_params(char *params, - PQconninfoOption *connOptions, - PQExpBuffer errorMessage) -{ - while (*params) - { - char *keyword = params; - char *value = NULL; - char *p = params; - bool malloced = false; - - /* - * Scan the params string for '=' and '&', marking the end of keyword - * and value respectively. - */ - for (;;) - { - if (*p == '=') - { - /* Was there '=' already? */ - if (value != NULL) - { - printfPQExpBuffer(errorMessage, - libpq_gettext("extra key/value separator \"=\" in URI query parameter: \"%s\"\n"), - keyword); - return false; - } - /* Cut off keyword, advance to value */ - *p++ = '\0'; - value = p; - } - else if (*p == '&' || *p == '\0') - { - /* - * If not at the end, cut off value and advance; leave p - * pointing to start of the next parameter, if any. - */ - if (*p != '\0') - *p++ = '\0'; - /* Was there '=' at all? */ - if (value == NULL) - { - printfPQExpBuffer(errorMessage, - libpq_gettext("missing key/value separator \"=\" in URI query parameter: \"%s\"\n"), - keyword); - return false; - } - /* Got keyword and value, go process them. */ - break; - } - else - ++p; /* Advance over all other bytes. */ - } - - keyword = conninfo_uri_decode(keyword, errorMessage); - if (keyword == NULL) - { - /* conninfo_uri_decode already set an error message */ - return false; - } - value = conninfo_uri_decode(value, errorMessage); - if (value == NULL) - { - /* conninfo_uri_decode already set an error message */ - free(keyword); - return false; - } - malloced = true; - - /* - * Special keyword handling for improved JDBC compatibility. - */ - if (strcmp(keyword, "ssl") == 0 && - strcmp(value, "true") == 0) - { - free(keyword); - free(value); - malloced = false; - - keyword = "sslmode"; - value = "require"; - } - - /* - * Store the value if the corresponding option exists; ignore - * otherwise. At this point both keyword and value are not - * URI-encoded. - */ - if (!conninfo_storeval(connOptions, keyword, value, - errorMessage, true, false)) - { - /* Insert generic message if conninfo_storeval didn't give one. */ - if (errorMessage->len == 0) - printfPQExpBuffer(errorMessage, - libpq_gettext("invalid URI query parameter: \"%s\"\n"), - keyword); - /* And fail. */ - if (malloced) - { - free(keyword); - free(value); - } - return false; - } - - if (malloced) - { - free(keyword); - free(value); - } - - /* Proceed to next key=value pair, if any */ - params = p; - } - - return true; -} - -/* - * Connection URI decoder routine - * - * If successful, returns the malloc'd decoded string. - * If not successful, returns NULL and fills errorMessage accordingly. - * - * The string is decoded by replacing any percent-encoded tokens with - * corresponding characters, while preserving any non-encoded characters. A - * percent-encoded token is a character triplet: a percent sign, followed by a - * pair of hexadecimal digits (0-9A-F), where lower- and upper-case letters are - * treated identically. - */ -static char * -conninfo_uri_decode(const char *str, PQExpBuffer errorMessage) -{ - char *buf; - char *p; - const char *q = str; - - buf = malloc(strlen(str) + 1); - if (buf == NULL) - { - printfPQExpBuffer(errorMessage, libpq_gettext("out of memory\n")); - return NULL; - } - p = buf; - - for (;;) - { - if (*q != '%') - { - /* copy and check for NUL terminator */ - if (!(*(p++) = *(q++))) - break; - } - else - { - int hi; - int lo; - int c; - - ++q; /* skip the percent sign itself */ - - /* - * Possible EOL will be caught by the first call to - * get_hexdigit(), so we never dereference an invalid q pointer. - */ - if (!(get_hexdigit(*q++, &hi) && get_hexdigit(*q++, &lo))) - { - printfPQExpBuffer(errorMessage, - libpq_gettext("invalid percent-encoded token: \"%s\"\n"), - str); - free(buf); - return NULL; - } - - c = (hi << 4) | lo; - if (c == 0) - { - printfPQExpBuffer(errorMessage, - libpq_gettext("forbidden value %%00 in percent-encoded value: \"%s\"\n"), - str); - free(buf); - return NULL; - } - *(p++) = c; - } - } - - return buf; -} - -/* - * Convert hexadecimal digit character to its integer value. - * - * If successful, returns true and value is filled with digit's base 16 value. - * If not successful, returns false. - * - * Lower- and upper-case letters in the range A-F are treated identically. - */ -static bool -get_hexdigit(char digit, int *value) -{ - if ('0' <= digit && digit <= '9') - *value = digit - '0'; - else if ('A' <= digit && digit <= 'F') - *value = digit - 'A' + 10; - else if ('a' <= digit && digit <= 'f') - *value = digit - 'a' + 10; - else - return false; - - return true; -} - -/* - * Find an option value corresponding to the keyword in the connOptions array. - * - * If successful, returns a pointer to the corresponding option's value. - * If not successful, returns NULL. - */ -static const char * -conninfo_getval(PQconninfoOption *connOptions, - const char *keyword) -{ - PQconninfoOption *option; - - option = conninfo_find(connOptions, keyword); - - return option ? option->val : NULL; -} - -/* - * Store a (new) value for an option corresponding to the keyword in - * connOptions array. - * - * If uri_decode is true, the value is URI-decoded. The keyword is always - * assumed to be non URI-encoded. - * - * If successful, returns a pointer to the corresponding PQconninfoOption, - * which value is replaced with a strdup'd copy of the passed value string. - * The existing value for the option is free'd before replacing, if any. - * - * If not successful, returns NULL and fills errorMessage accordingly. - * However, if the reason of failure is an invalid keyword being passed and - * ignoreMissing is TRUE, errorMessage will be left untouched. - */ -static PQconninfoOption * -conninfo_storeval(PQconninfoOption *connOptions, - const char *keyword, const char *value, - PQExpBuffer errorMessage, bool ignoreMissing, - bool uri_decode) -{ - PQconninfoOption *option; - char *value_copy; - - /* - * For backwards compatibility, requiressl=1 gets translated to - * sslmode=require, and requiressl=0 gets translated to sslmode=prefer - * (which is the default for sslmode). - */ - if (strcmp(keyword, "requiressl") == 0) - { - keyword = "sslmode"; - if (value[0] == '1') - value = "require"; - else - value = "prefer"; - } - - option = conninfo_find(connOptions, keyword); - if (option == NULL) - { - if (!ignoreMissing) - printfPQExpBuffer(errorMessage, - libpq_gettext("invalid connection option \"%s\"\n"), - keyword); - return NULL; - } - - if (uri_decode) - { - value_copy = conninfo_uri_decode(value, errorMessage); - if (value_copy == NULL) - /* conninfo_uri_decode already set an error message */ - return NULL; - } - else - { - value_copy = strdup(value); - if (value_copy == NULL) - { - printfPQExpBuffer(errorMessage, libpq_gettext("out of memory\n")); - return NULL; - } - } - - if (option->val) - free(option->val); - option->val = value_copy; - - return option; -} - -/* - * Find a PQconninfoOption option corresponding to the keyword in the - * connOptions array. - * - * If successful, returns a pointer to the corresponding PQconninfoOption - * structure. - * If not successful, returns NULL. - */ -static PQconninfoOption * -conninfo_find(PQconninfoOption *connOptions, const char *keyword) -{ - PQconninfoOption *option; - - for (option = connOptions; option->keyword != NULL; option++) - { - if (strcmp(option->keyword, keyword) == 0) - return option; - } - - return NULL; -} - - -/* - * Return the connection options used for the connection - */ -PQconninfoOption * -PQconninfo(PGconn *conn) -{ - PQExpBufferData errorBuf; - PQconninfoOption *connOptions; - - if (conn == NULL) - return NULL; - - /* We don't actually report any errors here, but callees want a buffer */ - initPQExpBuffer(&errorBuf); - if (PQExpBufferDataBroken(errorBuf)) - return NULL; /* out of memory already :-( */ - - connOptions = conninfo_init(&errorBuf); - - if (connOptions != NULL) - { - const internalPQconninfoOption *option; - - for (option = PQconninfoOptions; option->keyword; option++) - { - char **connmember; - - if (option->connofs < 0) - continue; - - connmember = (char **) ((char *) conn + option->connofs); - - if (*connmember) - conninfo_storeval(connOptions, option->keyword, *connmember, - &errorBuf, true, false); - } - } - - termPQExpBuffer(&errorBuf); - - return connOptions; -} - - -void -PQconninfoFree(PQconninfoOption *connOptions) -{ - PQconninfoOption *option; - - if (connOptions == NULL) - return; - - for (option = connOptions; option->keyword != NULL; option++) - { - if (option->val != NULL) - free(option->val); - } - free(connOptions); -} - - -/* =========== accessor functions for PGconn ========= */ -char * -PQdb(const PGconn *conn) -{ - if (!conn) - return NULL; - return conn->dbName; -} - -char * -PQuser(const PGconn *conn) -{ - if (!conn) - return NULL; - return conn->pguser; -} - -char * -PQpass(const PGconn *conn) -{ - if (!conn) - return NULL; - return conn->pgpass; -} - -char * -PQhost(const PGconn *conn) -{ - if (!conn) - return NULL; - if (conn->pghost != NULL && conn->pghost[0] != '\0') - return conn->pghost; - else - { -#ifdef HAVE_UNIX_SOCKETS - if (conn->pgunixsocket != NULL && conn->pgunixsocket[0] != '\0') - return conn->pgunixsocket; - else - return DEFAULT_PGSOCKET_DIR; -#else - return DefaultHost; -#endif - } -} - -char * -PQport(const PGconn *conn) -{ - if (!conn) - return NULL; - return conn->pgport; -} - -char * -PQtty(const PGconn *conn) -{ - if (!conn) - return NULL; - return conn->pgtty; -} - -char * -PQoptions(const PGconn *conn) -{ - if (!conn) - return NULL; - return conn->pgoptions; -} - -ConnStatusType -PQstatus(const PGconn *conn) -{ - if (!conn) - return CONNECTION_BAD; - return conn->status; -} - -PGTransactionStatusType -PQtransactionStatus(const PGconn *conn) -{ - if (!conn || conn->status != CONNECTION_OK) - return PQTRANS_UNKNOWN; - if (conn->asyncStatus != PGASYNC_IDLE) - return PQTRANS_ACTIVE; - return conn->xactStatus; -} - -const char * -PQparameterStatus(const PGconn *conn, const char *paramName) -{ - const pgParameterStatus *pstatus; - - if (!conn || !paramName) - return NULL; - for (pstatus = conn->pstatus; pstatus != NULL; pstatus = pstatus->next) - { - if (strcmp(pstatus->name, paramName) == 0) - return pstatus->value; - } - return NULL; -} - -int -PQprotocolVersion(const PGconn *conn) -{ - if (!conn) - return 0; - if (conn->status == CONNECTION_BAD) - return 0; - return PG_PROTOCOL_MAJOR(conn->pversion); -} - -int -PQserverVersion(const PGconn *conn) -{ - if (!conn) - return 0; - if (conn->status == CONNECTION_BAD) - return 0; - return conn->sversion; -} - -char * -PQerrorMessage(const PGconn *conn) -{ - if (!conn) - return libpq_gettext("connection pointer is NULL\n"); - - return conn->errorMessage.data; -} - -/* - * In Windows, socket values are unsigned, and an invalid socket value - * (INVALID_SOCKET) is ~0, which equals -1 in comparisons (with no compiler - * warning). Ideally we would return an unsigned value for PQsocket() on - * Windows, but that would cause the function's return value to differ from - * Unix, so we just return -1 for invalid sockets. - * http://msdn.microsoft.com/en-us/library/windows/desktop/cc507522%28v=vs.85%29.aspx - * http://stackoverflow.com/questions/10817252/why-is-invalid-socket-defined-as-0-in-winsock2-h-c - */ -int -PQsocket(const PGconn *conn) -{ - if (!conn) - return -1; - return (conn->sock != PGINVALID_SOCKET) ? conn->sock : -1; -} - -int -PQbackendPID(const PGconn *conn) -{ - if (!conn || conn->status != CONNECTION_OK) - return 0; - return conn->be_pid; -} - -int -PQconnectionNeedsPassword(const PGconn *conn) -{ - if (!conn) - return false; - if (conn->password_needed && - (conn->pgpass == NULL || conn->pgpass[0] == '\0')) - return true; - else - return false; -} - -int -PQconnectionUsedPassword(const PGconn *conn) -{ - if (!conn) - return false; - if (conn->password_needed) - return true; - else - return false; -} - -int -PQclientEncoding(const PGconn *conn) -{ - if (!conn || conn->status != CONNECTION_OK) - return -1; - return conn->client_encoding; -} - -int -PQsetClientEncoding(PGconn *conn, const char *encoding) -{ - char qbuf[128]; - static const char query[] = "set client_encoding to '%s'"; - PGresult *res; - int status; - - if (!conn || conn->status != CONNECTION_OK) - return -1; - - if (!encoding) - return -1; - - /* Resolve special "auto" value from the locale */ - if (strcmp(encoding, "auto") == 0) - encoding = pg_encoding_to_char(pg_get_encoding_from_locale(NULL, true)); - - /* check query buffer overflow */ - if (sizeof(qbuf) < (sizeof(query) + strlen(encoding))) - return -1; - - /* ok, now send a query */ - sprintf(qbuf, query, encoding); - res = PQexec(conn, qbuf); - - if (res == NULL) - return -1; - if (res->resultStatus != PGRES_COMMAND_OK) - status = -1; - else - { - /* - * In protocol 2 we have to assume the setting will stick, and adjust - * our state immediately. In protocol 3 and up we can rely on the - * backend to report the parameter value, and we'll change state at - * that time. - */ - if (PG_PROTOCOL_MAJOR(conn->pversion) < 3) - pqSaveParameterStatus(conn, "client_encoding", encoding); - status = 0; /* everything is ok */ - } - PQclear(res); - return status; -} - -PGVerbosity -PQsetErrorVerbosity(PGconn *conn, PGVerbosity verbosity) -{ - PGVerbosity old; - - if (!conn) - return PQERRORS_DEFAULT; - old = conn->verbosity; - conn->verbosity = verbosity; - return old; -} - -PGContextVisibility -PQsetErrorContextVisibility(PGconn *conn, PGContextVisibility show_context) -{ - PGContextVisibility old; - - if (!conn) - return PQSHOW_CONTEXT_ERRORS; - old = conn->show_context; - conn->show_context = show_context; - return old; -} - -void -PQtrace(PGconn *conn, FILE *debug_port) -{ - if (conn == NULL) - return; - PQuntrace(conn); - conn->Pfdebug = debug_port; -} - -void -PQuntrace(PGconn *conn) -{ - if (conn == NULL) - return; - if (conn->Pfdebug) - { - fflush(conn->Pfdebug); - conn->Pfdebug = NULL; - } -} - -PQnoticeReceiver -PQsetNoticeReceiver(PGconn *conn, PQnoticeReceiver proc, void *arg) -{ - PQnoticeReceiver old; - - if (conn == NULL) - return NULL; - - old = conn->noticeHooks.noticeRec; - if (proc) - { - conn->noticeHooks.noticeRec = proc; - conn->noticeHooks.noticeRecArg = arg; - } - return old; -} - -PQnoticeProcessor -PQsetNoticeProcessor(PGconn *conn, PQnoticeProcessor proc, void *arg) -{ - PQnoticeProcessor old; - - if (conn == NULL) - return NULL; - - old = conn->noticeHooks.noticeProc; - if (proc) - { - conn->noticeHooks.noticeProc = proc; - conn->noticeHooks.noticeProcArg = arg; - } - return old; -} - -/* - * The default notice message receiver just gets the standard notice text - * and sends it to the notice processor. This two-level setup exists - * mostly for backwards compatibility; perhaps we should deprecate use of - * PQsetNoticeProcessor? - */ -static void -defaultNoticeReceiver(void *arg, const PGresult *res) -{ - (void) arg; /* not used */ - if (res->noticeHooks.noticeProc != NULL) - (*res->noticeHooks.noticeProc) (res->noticeHooks.noticeProcArg, - PQresultErrorMessage(res)); -} - -/* - * The default notice message processor just prints the - * message on stderr. Applications can override this if they - * want the messages to go elsewhere (a window, for example). - * Note that simply discarding notices is probably a bad idea. - */ -static void -defaultNoticeProcessor(void *arg, const char *message) -{ - (void) arg; /* not used */ - /* Note: we expect the supplied string to end with a newline already. */ - fprintf(stderr, "%s", message); -} - -/* - * returns a pointer to the next token or NULL if the current - * token doesn't match - */ -static char * -pwdfMatchesString(char *buf, char *token) -{ - char *tbuf, - *ttok; - bool bslash = false; - - if (buf == NULL || token == NULL) - return NULL; - tbuf = buf; - ttok = token; - if (tbuf[0] == '*' && tbuf[1] == ':') - return tbuf + 2; - while (*tbuf != 0) - { - if (*tbuf == '\\' && !bslash) - { - tbuf++; - bslash = true; - } - if (*tbuf == ':' && *ttok == 0 && !bslash) - return tbuf + 1; - bslash = false; - if (*ttok == 0) - return NULL; - if (*tbuf == *ttok) - { - tbuf++; - ttok++; - } - else - return NULL; - } - return NULL; -} - -/* Get a password from the password file. Return value is malloc'd. */ -static char * -PasswordFromFile(char *hostname, char *port, char *dbname, char *username) -{ - FILE *fp; - char pgpassfile[MAXPGPATH]; - struct stat stat_buf; - -#define LINELEN NAMEDATALEN*5 - char buf[LINELEN]; - - if (dbname == NULL || strlen(dbname) == 0) - return NULL; - - if (username == NULL || strlen(username) == 0) - return NULL; - - /* 'localhost' matches pghost of '' or the default socket directory */ - if (hostname == NULL) - hostname = DefaultHost; - else if (is_absolute_path(hostname)) - - /* - * We should probably use canonicalize_path(), but then we have to - * bring path.c into libpq, and it doesn't seem worth it. - */ - if (strcmp(hostname, DEFAULT_PGSOCKET_DIR) == 0) - hostname = DefaultHost; - - if (port == NULL) - port = DEF_PGPORT_STR; - - if (!getPgPassFilename(pgpassfile)) - return NULL; - - /* If password file cannot be opened, ignore it. */ - if (stat(pgpassfile, &stat_buf) != 0) - return NULL; - -#ifndef WIN32 - if (!S_ISREG(stat_buf.st_mode)) - { - fprintf(stderr, - libpq_gettext("WARNING: password file \"%s\" is not a plain file\n"), - pgpassfile); - return NULL; - } - - /* If password file is insecure, alert the user and ignore it. */ - if (stat_buf.st_mode & (S_IRWXG | S_IRWXO)) - { - fprintf(stderr, - libpq_gettext("WARNING: password file \"%s\" has group or world access; permissions should be u=rw (0600) or less\n"), - pgpassfile); - return NULL; - } -#else - - /* - * On Win32, the directory is protected, so we don't have to check the - * file. - */ -#endif - - fp = fopen(pgpassfile, "r"); - if (fp == NULL) - return NULL; - - while (!feof(fp) && !ferror(fp)) - { - char *t = buf, - *ret, - *p1, - *p2; - int len; - - if (fgets(buf, sizeof(buf), fp) == NULL) - break; - - len = strlen(buf); - - /* Remove trailing newline */ - if (len > 0 && buf[len - 1] == '\n') - { - buf[--len] = '\0'; - /* Handle DOS-style line endings, too, even when not on Windows */ - if (len > 0 && buf[len - 1] == '\r') - buf[--len] = '\0'; - } - - if (len == 0) - continue; - - if ((t = pwdfMatchesString(t, hostname)) == NULL || - (t = pwdfMatchesString(t, port)) == NULL || - (t = pwdfMatchesString(t, dbname)) == NULL || - (t = pwdfMatchesString(t, username)) == NULL) - continue; - - /* Found a match. */ - ret = strdup(t); - fclose(fp); - - if (!ret) - { - /* Out of memory. XXX: an error message would be nice. */ - return NULL; - } - - /* De-escape password. */ - for (p1 = p2 = ret; *p1 != ':' && *p1 != '\0'; ++p1, ++p2) - { - if (*p1 == '\\' && p1[1] != '\0') - ++p1; - *p2 = *p1; - } - *p2 = '\0'; - - return ret; - } - - fclose(fp); - return NULL; - -#undef LINELEN -} - - -static bool -getPgPassFilename(char *pgpassfile) -{ - char *passfile_env; - - if ((passfile_env = getenv("PGPASSFILE")) != NULL) - /* use the literal path from the environment, if set */ - strlcpy(pgpassfile, passfile_env, MAXPGPATH); - else - { - char homedir[MAXPGPATH]; - - if (!pqGetHomeDirectory(homedir, sizeof(homedir))) - return false; - snprintf(pgpassfile, MAXPGPATH, "%s/%s", homedir, PGPASSFILE); - } - return true; -} - -/* - * If the connection failed, we should mention if - * we got the password from .pgpass in case that - * password is wrong. - */ -static void -dot_pg_pass_warning(PGconn *conn) -{ - /* If it was 'invalid authorization', add .pgpass mention */ - /* only works with >= 9.0 servers */ - if (conn->dot_pgpass_used && conn->password_needed && conn->result && - strcmp(PQresultErrorField(conn->result, PG_DIAG_SQLSTATE), - ERRCODE_INVALID_PASSWORD) == 0) - { - char pgpassfile[MAXPGPATH]; - - if (!getPgPassFilename(pgpassfile)) - return; - appendPQExpBuffer(&conn->errorMessage, - libpq_gettext("password retrieved from file \"%s\"\n"), - pgpassfile); - } -} - - -/* - * Obtain user's home directory, return in given buffer - * - * On Unix, this actually returns the user's home directory. On Windows - * it returns the PostgreSQL-specific application data folder. - * - * This is essentially the same as get_home_path(), but we don't use that - * because we don't want to pull path.c into libpq (it pollutes application - * namespace) - */ -bool -pqGetHomeDirectory(char *buf, int bufsize) -{ -#ifndef WIN32 - char pwdbuf[BUFSIZ]; - struct passwd pwdstr; - struct passwd *pwd = NULL; - - (void) pqGetpwuid(geteuid(), &pwdstr, pwdbuf, sizeof(pwdbuf), &pwd); - if (pwd == NULL) - return false; - strlcpy(buf, pwd->pw_dir, bufsize); - return true; -#else - char tmppath[MAX_PATH]; - - ZeroMemory(tmppath, sizeof(tmppath)); - if (SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, 0, tmppath) != S_OK) - return false; - snprintf(buf, bufsize, "%s/postgresql", tmppath); - return true; -#endif -} - -/* - * To keep the API consistent, the locking stubs are always provided, even - * if they are not required. - */ - -static void -default_threadlock(int acquire) -{ -#ifdef ENABLE_THREAD_SAFETY -#ifndef WIN32 - static pthread_mutex_t singlethread_lock = PTHREAD_MUTEX_INITIALIZER; -#else - static pthread_mutex_t singlethread_lock = NULL; - static long mutex_initlock = 0; - - if (singlethread_lock == NULL) - { - while (InterlockedExchange(&mutex_initlock, 1) == 1) - /* loop, another thread own the lock */ ; - if (singlethread_lock == NULL) - { - if (pthread_mutex_init(&singlethread_lock, NULL)) - PGTHREAD_ERROR("failed to initialize mutex"); - } - InterlockedExchange(&mutex_initlock, 0); - } -#endif - if (acquire) - { - if (pthread_mutex_lock(&singlethread_lock)) - PGTHREAD_ERROR("failed to lock mutex"); - } - else - { - if (pthread_mutex_unlock(&singlethread_lock)) - PGTHREAD_ERROR("failed to unlock mutex"); - } -#endif -} - -pgthreadlock_t -PQregisterThreadLock(pgthreadlock_t newhandler) -{ - pgthreadlock_t prev = pg_g_threadlock; - - if (newhandler) - pg_g_threadlock = newhandler; - else - pg_g_threadlock = default_threadlock; - - return prev; -} diff --git a/libpq/fe-exec.c b/libpq/fe-exec.c deleted file mode 100644 index 87ff565..0000000 --- a/libpq/fe-exec.c +++ /dev/null @@ -1,3764 +0,0 @@ -/*------------------------------------------------------------------------- - * - * fe-exec.c - * functions related to sending a query down to the backend - * - * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * - * IDENTIFICATION - * src/interfaces/libpq/fe-exec.c - * - *------------------------------------------------------------------------- - */ -#include "postgres_fe.h" - -#include <ctype.h> -#include <fcntl.h> - -#include "libpq-fe.h" -#include "libpq-int.h" - -#include "mb/pg_wchar.h" - -#ifdef WIN32 -#include "win32.h" -#else -#include <unistd.h> -#endif - -/* keep this in same order as ExecStatusType in libpq-fe.h */ -char *const pgresStatus[] = { - "PGRES_EMPTY_QUERY", - "PGRES_COMMAND_OK", - "PGRES_TUPLES_OK", - "PGRES_COPY_OUT", - "PGRES_COPY_IN", - "PGRES_BAD_RESPONSE", - "PGRES_NONFATAL_ERROR", - "PGRES_FATAL_ERROR", - "PGRES_COPY_BOTH", - "PGRES_SINGLE_TUPLE" -}; - -/* - * static state needed by PQescapeString and PQescapeBytea; initialize to - * values that result in backward-compatible behavior - */ -static int static_client_encoding = PG_SQL_ASCII; -static bool static_std_strings = false; - - -static PGEvent *dupEvents(PGEvent *events, int count); -static bool pqAddTuple(PGresult *res, PGresAttValue *tup); -static bool PQsendQueryStart(PGconn *conn); -static int PQsendQueryGuts(PGconn *conn, - const char *command, - const char *stmtName, - int nParams, - const Oid *paramTypes, - const char *const * paramValues, - const int *paramLengths, - const int *paramFormats, - int resultFormat); -static void parseInput(PGconn *conn); -static PGresult *getCopyResult(PGconn *conn, ExecStatusType copytype); -static bool PQexecStart(PGconn *conn); -static PGresult *PQexecFinish(PGconn *conn); -static int PQsendDescribe(PGconn *conn, char desc_type, - const char *desc_target); -static int check_field_number(const PGresult *res, int field_num); - - -/* ---------------- - * Space management for PGresult. - * - * Formerly, libpq did a separate malloc() for each field of each tuple - * returned by a query. This was remarkably expensive --- malloc/free - * consumed a sizable part of the application's runtime. And there is - * no real need to keep track of the fields separately, since they will - * all be freed together when the PGresult is released. So now, we grab - * large blocks of storage from malloc and allocate space for query data - * within these blocks, using a trivially simple allocator. This reduces - * the number of malloc/free calls dramatically, and it also avoids - * fragmentation of the malloc storage arena. - * The PGresult structure itself is still malloc'd separately. We could - * combine it with the first allocation block, but that would waste space - * for the common case that no extra storage is actually needed (that is, - * the SQL command did not return tuples). - * - * We also malloc the top-level array of tuple pointers separately, because - * we need to be able to enlarge it via realloc, and our trivial space - * allocator doesn't handle that effectively. (Too bad the FE/BE protocol - * doesn't tell us up front how many tuples will be returned.) - * All other subsidiary storage for a PGresult is kept in PGresult_data blocks - * of size PGRESULT_DATA_BLOCKSIZE. The overhead at the start of each block - * is just a link to the next one, if any. Free-space management info is - * kept in the owning PGresult. - * A query returning a small amount of data will thus require three malloc - * calls: one for the PGresult, one for the tuples pointer array, and one - * PGresult_data block. - * - * Only the most recently allocated PGresult_data block is a candidate to - * have more stuff added to it --- any extra space left over in older blocks - * is wasted. We could be smarter and search the whole chain, but the point - * here is to be simple and fast. Typical applications do not keep a PGresult - * around very long anyway, so some wasted space within one is not a problem. - * - * Tuning constants for the space allocator are: - * PGRESULT_DATA_BLOCKSIZE: size of a standard allocation block, in bytes - * PGRESULT_ALIGN_BOUNDARY: assumed alignment requirement for binary data - * PGRESULT_SEP_ALLOC_THRESHOLD: objects bigger than this are given separate - * blocks, instead of being crammed into a regular allocation block. - * Requirements for correct function are: - * PGRESULT_ALIGN_BOUNDARY must be a multiple of the alignment requirements - * of all machine data types. (Currently this is set from configure - * tests, so it should be OK automatically.) - * PGRESULT_SEP_ALLOC_THRESHOLD + PGRESULT_BLOCK_OVERHEAD <= - * PGRESULT_DATA_BLOCKSIZE - * pqResultAlloc assumes an object smaller than the threshold will fit - * in a new block. - * The amount of space wasted at the end of a block could be as much as - * PGRESULT_SEP_ALLOC_THRESHOLD, so it doesn't pay to make that too large. - * ---------------- - */ - -#define PGRESULT_DATA_BLOCKSIZE 2048 -#define PGRESULT_ALIGN_BOUNDARY MAXIMUM_ALIGNOF /* from configure */ -#define PGRESULT_BLOCK_OVERHEAD Max(sizeof(PGresult_data), PGRESULT_ALIGN_BOUNDARY) -#define PGRESULT_SEP_ALLOC_THRESHOLD (PGRESULT_DATA_BLOCKSIZE / 2) - - -/* - * PQmakeEmptyPGresult - * returns a newly allocated, initialized PGresult with given status. - * If conn is not NULL and status indicates an error, the conn's - * errorMessage is copied. Also, any PGEvents are copied from the conn. - */ -PGresult * -PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status) -{ - PGresult *result; - - result = (PGresult *) malloc(sizeof(PGresult)); - if (!result) - return NULL; - - result->ntups = 0; - result->numAttributes = 0; - result->attDescs = NULL; - result->tuples = NULL; - result->tupArrSize = 0; - result->numParameters = 0; - result->paramDescs = NULL; - result->resultStatus = status; - result->cmdStatus[0] = '\0'; - result->binary = 0; - result->events = NULL; - result->nEvents = 0; - result->errMsg = NULL; - result->errFields = NULL; - result->errQuery = NULL; - result->null_field[0] = '\0'; - result->curBlock = NULL; - result->curOffset = 0; - result->spaceLeft = 0; - - if (conn) - { - /* copy connection data we might need for operations on PGresult */ - result->noticeHooks = conn->noticeHooks; - result->client_encoding = conn->client_encoding; - - /* consider copying conn's errorMessage */ - switch (status) - { - case PGRES_EMPTY_QUERY: - case PGRES_COMMAND_OK: - case PGRES_TUPLES_OK: - case PGRES_COPY_OUT: - case PGRES_COPY_IN: - case PGRES_COPY_BOTH: - case PGRES_SINGLE_TUPLE: - /* non-error cases */ - break; - default: - pqSetResultError(result, conn->errorMessage.data); - break; - } - - /* copy events last; result must be valid if we need to PQclear */ - if (conn->nEvents > 0) - { - result->events = dupEvents(conn->events, conn->nEvents); - if (!result->events) - { - PQclear(result); - return NULL; - } - result->nEvents = conn->nEvents; - } - } - else - { - /* defaults... */ - result->noticeHooks.noticeRec = NULL; - result->noticeHooks.noticeRecArg = NULL; - result->noticeHooks.noticeProc = NULL; - result->noticeHooks.noticeProcArg = NULL; - result->client_encoding = PG_SQL_ASCII; - } - - return result; -} - -/* - * PQsetResultAttrs - * - * Set the attributes for a given result. This function fails if there are - * already attributes contained in the provided result. The call is - * ignored if numAttributes is zero or attDescs is NULL. If the - * function fails, it returns zero. If the function succeeds, it - * returns a non-zero value. - */ -int -PQsetResultAttrs(PGresult *res, int numAttributes, PGresAttDesc *attDescs) -{ - int i; - - /* If attrs already exist, they cannot be overwritten. */ - if (!res || res->numAttributes > 0) - return FALSE; - - /* ignore no-op request */ - if (numAttributes <= 0 || !attDescs) - return TRUE; - - res->attDescs = (PGresAttDesc *) - PQresultAlloc(res, numAttributes * sizeof(PGresAttDesc)); - - if (!res->attDescs) - return FALSE; - - res->numAttributes = numAttributes; - memcpy(res->attDescs, attDescs, numAttributes * sizeof(PGresAttDesc)); - - /* deep-copy the attribute names, and determine format */ - res->binary = 1; - for (i = 0; i < res->numAttributes; i++) - { - if (res->attDescs[i].name) - res->attDescs[i].name = pqResultStrdup(res, res->attDescs[i].name); - else - res->attDescs[i].name = res->null_field; - - if (!res->attDescs[i].name) - return FALSE; - - if (res->attDescs[i].format == 0) - res->binary = 0; - } - - return TRUE; -} - -/* - * PQcopyResult - * - * Returns a deep copy of the provided 'src' PGresult, which cannot be NULL. - * The 'flags' argument controls which portions of the result will or will - * NOT be copied. The created result is always put into the - * PGRES_TUPLES_OK status. The source result error message is not copied, - * although cmdStatus is. - * - * To set custom attributes, use PQsetResultAttrs. That function requires - * that there are no attrs contained in the result, so to use that - * function you cannot use the PG_COPYRES_ATTRS or PG_COPYRES_TUPLES - * options with this function. - * - * Options: - * PG_COPYRES_ATTRS - Copy the source result's attributes - * - * PG_COPYRES_TUPLES - Copy the source result's tuples. This implies - * copying the attrs, seeing how the attrs are needed by the tuples. - * - * PG_COPYRES_EVENTS - Copy the source result's events. - * - * PG_COPYRES_NOTICEHOOKS - Copy the source result's notice hooks. - */ -PGresult * -PQcopyResult(const PGresult *src, int flags) -{ - PGresult *dest; - int i; - - if (!src) - return NULL; - - dest = PQmakeEmptyPGresult(NULL, PGRES_TUPLES_OK); - if (!dest) - return NULL; - - /* Always copy these over. Is cmdStatus really useful here? */ - dest->client_encoding = src->client_encoding; - strcpy(dest->cmdStatus, src->cmdStatus); - - /* Wants attrs? */ - if (flags & (PG_COPYRES_ATTRS | PG_COPYRES_TUPLES)) - { - if (!PQsetResultAttrs(dest, src->numAttributes, src->attDescs)) - { - PQclear(dest); - return NULL; - } - } - - /* Wants to copy tuples? */ - if (flags & PG_COPYRES_TUPLES) - { - int tup, - field; - - for (tup = 0; tup < src->ntups; tup++) - { - for (field = 0; field < src->numAttributes; field++) - { - if (!PQsetvalue(dest, tup, field, - src->tuples[tup][field].value, - src->tuples[tup][field].len)) - { - PQclear(dest); - return NULL; - } - } - } - } - - /* Wants to copy notice hooks? */ - if (flags & PG_COPYRES_NOTICEHOOKS) - dest->noticeHooks = src->noticeHooks; - - /* Wants to copy PGEvents? */ - if ((flags & PG_COPYRES_EVENTS) && src->nEvents > 0) - { - dest->events = dupEvents(src->events, src->nEvents); - if (!dest->events) - { - PQclear(dest); - return NULL; - } - dest->nEvents = src->nEvents; - } - - /* Okay, trigger PGEVT_RESULTCOPY event */ - for (i = 0; i < dest->nEvents; i++) - { - if (src->events[i].resultInitialized) - { - PGEventResultCopy evt; - - evt.src = src; - evt.dest = dest; - if (!dest->events[i].proc(PGEVT_RESULTCOPY, &evt, - dest->events[i].passThrough)) - { - PQclear(dest); - return NULL; - } - dest->events[i].resultInitialized = TRUE; - } - } - - return dest; -} - -/* - * Copy an array of PGEvents (with no extra space for more). - * Does not duplicate the event instance data, sets this to NULL. - * Also, the resultInitialized flags are all cleared. - */ -static PGEvent * -dupEvents(PGEvent *events, int count) -{ - PGEvent *newEvents; - int i; - - if (!events || count <= 0) - return NULL; - - newEvents = (PGEvent *) malloc(count * sizeof(PGEvent)); - if (!newEvents) - return NULL; - - for (i = 0; i < count; i++) - { - newEvents[i].proc = events[i].proc; - newEvents[i].passThrough = events[i].passThrough; - newEvents[i].data = NULL; - newEvents[i].resultInitialized = FALSE; - newEvents[i].name = strdup(events[i].name); - if (!newEvents[i].name) - { - while (--i >= 0) - free(newEvents[i].name); - free(newEvents); - return NULL; - } - } - - return newEvents; -} - - -/* - * Sets the value for a tuple field. The tup_num must be less than or - * equal to PQntuples(res). If it is equal, a new tuple is created and - * added to the result. - * Returns a non-zero value for success and zero for failure. - */ -int -PQsetvalue(PGresult *res, int tup_num, int field_num, char *value, int len) -{ - PGresAttValue *attval; - - if (!check_field_number(res, field_num)) - return FALSE; - - /* Invalid tup_num, must be <= ntups */ - if (tup_num < 0 || tup_num > res->ntups) - return FALSE; - - /* need to allocate a new tuple? */ - if (tup_num == res->ntups) - { - PGresAttValue *tup; - int i; - - tup = (PGresAttValue *) - pqResultAlloc(res, res->numAttributes * sizeof(PGresAttValue), - TRUE); - - if (!tup) - return FALSE; - - /* initialize each column to NULL */ - for (i = 0; i < res->numAttributes; i++) - { - tup[i].len = NULL_LEN; - tup[i].value = res->null_field; - } - - /* add it to the array */ - if (!pqAddTuple(res, tup)) - return FALSE; - } - - attval = &res->tuples[tup_num][field_num]; - - /* treat either NULL_LEN or NULL value pointer as a NULL field */ - if (len == NULL_LEN || value == NULL) - { - attval->len = NULL_LEN; - attval->value = res->null_field; - } - else if (len <= 0) - { - attval->len = 0; - attval->value = res->null_field; - } - else - { - attval->value = (char *) pqResultAlloc(res, len + 1, TRUE); - if (!attval->value) - return FALSE; - attval->len = len; - memcpy(attval->value, value, len); - attval->value[len] = '\0'; - } - - return TRUE; -} - -/* - * pqResultAlloc - exported routine to allocate local storage in a PGresult. - * - * We force all such allocations to be maxaligned, since we don't know - * whether the value might be binary. - */ -void * -PQresultAlloc(PGresult *res, size_t nBytes) -{ - return pqResultAlloc(res, nBytes, TRUE); -} - -/* - * pqResultAlloc - - * Allocate subsidiary storage for a PGresult. - * - * nBytes is the amount of space needed for the object. - * If isBinary is true, we assume that we need to align the object on - * a machine allocation boundary. - * If isBinary is false, we assume the object is a char string and can - * be allocated on any byte boundary. - */ -void * -pqResultAlloc(PGresult *res, size_t nBytes, bool isBinary) -{ - char *space; - PGresult_data *block; - - if (!res) - return NULL; - - if (nBytes <= 0) - return res->null_field; - - /* - * If alignment is needed, round up the current position to an alignment - * boundary. - */ - if (isBinary) - { - int offset = res->curOffset % PGRESULT_ALIGN_BOUNDARY; - - if (offset) - { - res->curOffset += PGRESULT_ALIGN_BOUNDARY - offset; - res->spaceLeft -= PGRESULT_ALIGN_BOUNDARY - offset; - } - } - - /* If there's enough space in the current block, no problem. */ - if (nBytes <= (size_t) res->spaceLeft) - { - space = res->curBlock->space + res->curOffset; - res->curOffset += nBytes; - res->spaceLeft -= nBytes; - return space; - } - - /* - * If the requested object is very large, give it its own block; this - * avoids wasting what might be most of the current block to start a new - * block. (We'd have to special-case requests bigger than the block size - * anyway.) The object is always given binary alignment in this case. - */ - if (nBytes >= PGRESULT_SEP_ALLOC_THRESHOLD) - { - block = (PGresult_data *) malloc(nBytes + PGRESULT_BLOCK_OVERHEAD); - if (!block) - return NULL; - space = block->space + PGRESULT_BLOCK_OVERHEAD; - if (res->curBlock) - { - /* - * Tuck special block below the active block, so that we don't - * have to waste the free space in the active block. - */ - block->next = res->curBlock->next; - res->curBlock->next = block; - } - else - { - /* Must set up the new block as the first active block. */ - block->next = NULL; - res->curBlock = block; - res->spaceLeft = 0; /* be sure it's marked full */ - } - return space; - } - - /* Otherwise, start a new block. */ - block = (PGresult_data *) malloc(PGRESULT_DATA_BLOCKSIZE); - if (!block) - return NULL; - block->next = res->curBlock; - res->curBlock = block; - if (isBinary) - { - /* object needs full alignment */ - res->curOffset = PGRESULT_BLOCK_OVERHEAD; - res->spaceLeft = PGRESULT_DATA_BLOCKSIZE - PGRESULT_BLOCK_OVERHEAD; - } - else - { - /* we can cram it right after the overhead pointer */ - res->curOffset = sizeof(PGresult_data); - res->spaceLeft = PGRESULT_DATA_BLOCKSIZE - sizeof(PGresult_data); - } - - space = block->space + res->curOffset; - res->curOffset += nBytes; - res->spaceLeft -= nBytes; - return space; -} - -/* - * pqResultStrdup - - * Like strdup, but the space is subsidiary PGresult space. - */ -char * -pqResultStrdup(PGresult *res, const char *str) -{ - char *space = (char *) pqResultAlloc(res, strlen(str) + 1, FALSE); - - if (space) - strcpy(space, str); - return space; -} - -/* - * pqSetResultError - - * assign a new error message to a PGresult - */ -void -pqSetResultError(PGresult *res, const char *msg) -{ - if (!res) - return; - if (msg && *msg) - res->errMsg = pqResultStrdup(res, msg); - else - res->errMsg = NULL; -} - -/* - * pqCatenateResultError - - * concatenate a new error message to the one already in a PGresult - */ -void -pqCatenateResultError(PGresult *res, const char *msg) -{ - PQExpBufferData errorBuf; - - if (!res || !msg) - return; - initPQExpBuffer(&errorBuf); - if (res->errMsg) - appendPQExpBufferStr(&errorBuf, res->errMsg); - appendPQExpBufferStr(&errorBuf, msg); - pqSetResultError(res, errorBuf.data); - termPQExpBuffer(&errorBuf); -} - -/* - * PQclear - - * free's the memory associated with a PGresult - */ -void -PQclear(PGresult *res) -{ - PGresult_data *block; - int i; - - if (!res) - return; - - for (i = 0; i < res->nEvents; i++) - { - /* only send DESTROY to successfully-initialized event procs */ - if (res->events[i].resultInitialized) - { - PGEventResultDestroy evt; - - evt.result = res; - (void) res->events[i].proc(PGEVT_RESULTDESTROY, &evt, - res->events[i].passThrough); - } - free(res->events[i].name); - } - - if (res->events) - free(res->events); - - /* Free all the subsidiary blocks */ - while ((block = res->curBlock) != NULL) - { - res->curBlock = block->next; - free(block); - } - - /* Free the top-level tuple pointer array */ - if (res->tuples) - free(res->tuples); - - /* zero out the pointer fields to catch programming errors */ - res->attDescs = NULL; - res->tuples = NULL; - res->paramDescs = NULL; - res->errFields = NULL; - res->events = NULL; - res->nEvents = 0; - /* res->curBlock was zeroed out earlier */ - - /* Free the PGresult structure itself */ - free(res); -} - -/* - * Handy subroutine to deallocate any partially constructed async result. - * - * Any "next" result gets cleared too. - */ -void -pqClearAsyncResult(PGconn *conn) -{ - if (conn->result) - PQclear(conn->result); - conn->result = NULL; - if (conn->next_result) - PQclear(conn->next_result); - conn->next_result = NULL; -} - -/* - * This subroutine deletes any existing async result, sets conn->result - * to a PGresult with status PGRES_FATAL_ERROR, and stores the current - * contents of conn->errorMessage into that result. It differs from a - * plain call on PQmakeEmptyPGresult() in that if there is already an - * async result with status PGRES_FATAL_ERROR, the current error message - * is APPENDED to the old error message instead of replacing it. This - * behavior lets us report multiple error conditions properly, if necessary. - * (An example where this is needed is when the backend sends an 'E' message - * and immediately closes the connection --- we want to report both the - * backend error and the connection closure error.) - */ -void -pqSaveErrorResult(PGconn *conn) -{ - /* - * If no old async result, just let PQmakeEmptyPGresult make one. Likewise - * if old result is not an error message. - */ - if (conn->result == NULL || - conn->result->resultStatus != PGRES_FATAL_ERROR || - conn->result->errMsg == NULL) - { - pqClearAsyncResult(conn); - conn->result = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR); - } - else - { - /* Else, concatenate error message to existing async result. */ - pqCatenateResultError(conn->result, conn->errorMessage.data); - } -} - -/* - * This subroutine prepares an async result object for return to the caller. - * If there is not already an async result object, build an error object - * using whatever is in conn->errorMessage. In any case, clear the async - * result storage and make sure PQerrorMessage will agree with the result's - * error string. - */ -PGresult * -pqPrepareAsyncResult(PGconn *conn) -{ - PGresult *res; - - /* - * conn->result is the PGresult to return. If it is NULL (which probably - * shouldn't happen) we assume there is an appropriate error message in - * conn->errorMessage. - */ - res = conn->result; - if (!res) - res = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR); - else - { - /* - * Make sure PQerrorMessage agrees with result; it could be different - * if we have concatenated messages. - */ - resetPQExpBuffer(&conn->errorMessage); - appendPQExpBufferStr(&conn->errorMessage, - PQresultErrorMessage(res)); - } - - /* - * Replace conn->result with next_result, if any. In the normal case - * there isn't a next result and we're just dropping ownership of the - * current result. In single-row mode this restores the situation to what - * it was before we created the current single-row result. - */ - conn->result = conn->next_result; - conn->next_result = NULL; - - return res; -} - -/* - * pqInternalNotice - produce an internally-generated notice message - * - * A format string and optional arguments can be passed. Note that we do - * libpq_gettext() here, so callers need not. - * - * The supplied text is taken as primary message (ie., it should not include - * a trailing newline, and should not be more than one line). - */ -void -pqInternalNotice(const PGNoticeHooks *hooks, const char *fmt,...) -{ - char msgBuf[1024]; - va_list args; - PGresult *res; - - if (hooks->noticeRec == NULL) - return; /* nobody home to receive notice? */ - - /* Format the message */ - va_start(args, fmt); - vsnprintf(msgBuf, sizeof(msgBuf), libpq_gettext(fmt), args); - va_end(args); - msgBuf[sizeof(msgBuf) - 1] = '\0'; /* make real sure it's terminated */ - - /* Make a PGresult to pass to the notice receiver */ - res = PQmakeEmptyPGresult(NULL, PGRES_NONFATAL_ERROR); - if (!res) - return; - res->noticeHooks = *hooks; - - /* - * Set up fields of notice. - */ - pqSaveMessageField(res, PG_DIAG_MESSAGE_PRIMARY, msgBuf); - pqSaveMessageField(res, PG_DIAG_SEVERITY, libpq_gettext("NOTICE")); - pqSaveMessageField(res, PG_DIAG_SEVERITY_NONLOCALIZED, "NOTICE"); - /* XXX should provide a SQLSTATE too? */ - - /* - * Result text is always just the primary message + newline. If we can't - * allocate it, don't bother invoking the receiver. - */ - res->errMsg = (char *) pqResultAlloc(res, strlen(msgBuf) + 2, FALSE); - if (res->errMsg) - { - sprintf(res->errMsg, "%s\n", msgBuf); - - /* - * Pass to receiver, then free it. - */ - (*res->noticeHooks.noticeRec) (res->noticeHooks.noticeRecArg, res); - } - PQclear(res); -} - -/* - * pqAddTuple - * add a row pointer to the PGresult structure, growing it if necessary - * Returns TRUE if OK, FALSE if not enough memory to add the row - */ -static bool -pqAddTuple(PGresult *res, PGresAttValue *tup) -{ - if (res->ntups >= res->tupArrSize) - { - /* - * Try to grow the array. - * - * We can use realloc because shallow copying of the structure is - * okay. Note that the first time through, res->tuples is NULL. While - * ANSI says that realloc() should act like malloc() in that case, - * some old C libraries (like SunOS 4.1.x) coredump instead. On - * failure realloc is supposed to return NULL without damaging the - * existing allocation. Note that the positions beyond res->ntups are - * garbage, not necessarily NULL. - */ - int newSize = (res->tupArrSize > 0) ? res->tupArrSize * 2 : 128; - PGresAttValue **newTuples; - - if (res->tuples == NULL) - newTuples = (PGresAttValue **) - malloc(newSize * sizeof(PGresAttValue *)); - else - newTuples = (PGresAttValue **) - realloc(res->tuples, newSize * sizeof(PGresAttValue *)); - if (!newTuples) - return FALSE; /* malloc or realloc failed */ - res->tupArrSize = newSize; - res->tuples = newTuples; - } - res->tuples[res->ntups] = tup; - res->ntups++; - return TRUE; -} - -/* - * pqSaveMessageField - save one field of an error or notice message - */ -void -pqSaveMessageField(PGresult *res, char code, const char *value) -{ - PGMessageField *pfield; - - pfield = (PGMessageField *) - pqResultAlloc(res, - offsetof(PGMessageField, contents) + - strlen(value) + 1, - TRUE); - if (!pfield) - return; /* out of memory? */ - pfield->code = code; - strcpy(pfield->contents, value); - pfield->next = res->errFields; - res->errFields = pfield; -} - -/* - * pqSaveParameterStatus - remember parameter status sent by backend - */ -void -pqSaveParameterStatus(PGconn *conn, const char *name, const char *value) -{ - pgParameterStatus *pstatus; - pgParameterStatus *prev; - - if (conn->Pfdebug) - fprintf(conn->Pfdebug, "pqSaveParameterStatus: '%s' = '%s'\n", - name, value); - - /* - * Forget any old information about the parameter - */ - for (pstatus = conn->pstatus, prev = NULL; - pstatus != NULL; - prev = pstatus, pstatus = pstatus->next) - { - if (strcmp(pstatus->name, name) == 0) - { - if (prev) - prev->next = pstatus->next; - else - conn->pstatus = pstatus->next; - free(pstatus); /* frees name and value strings too */ - break; - } - } - - /* - * Store new info as a single malloc block - */ - pstatus = (pgParameterStatus *) malloc(sizeof(pgParameterStatus) + - strlen(name) +strlen(value) + 2); - if (pstatus) - { - char *ptr; - - ptr = ((char *) pstatus) + sizeof(pgParameterStatus); - pstatus->name = ptr; - strcpy(ptr, name); - ptr += strlen(name) + 1; - pstatus->value = ptr; - strcpy(ptr, value); - pstatus->next = conn->pstatus; - conn->pstatus = pstatus; - } - - /* - * Special hacks: remember client_encoding and - * standard_conforming_strings, and convert server version to a numeric - * form. We keep the first two of these in static variables as well, so - * that PQescapeString and PQescapeBytea can behave somewhat sanely (at - * least in single-connection-using programs). - */ - if (strcmp(name, "client_encoding") == 0) - { - conn->client_encoding = pg_char_to_encoding(value); - /* if we don't recognize the encoding name, fall back to SQL_ASCII */ - if (conn->client_encoding < 0) - conn->client_encoding = PG_SQL_ASCII; - static_client_encoding = conn->client_encoding; - } - else if (strcmp(name, "standard_conforming_strings") == 0) - { - conn->std_strings = (strcmp(value, "on") == 0); - static_std_strings = conn->std_strings; - } - else if (strcmp(name, "server_version") == 0) - { - int cnt; - int vmaj, - vmin, - vrev; - - cnt = sscanf(value, "%d.%d.%d", &vmaj, &vmin, &vrev); - - if (cnt == 3) - { - /* old style, e.g. 9.6.1 */ - conn->sversion = (100 * vmaj + vmin) * 100 + vrev; - } - else if (cnt == 2) - { - if (vmaj >= 10) - { - /* new style, e.g. 10.1 */ - conn->sversion = 100 * 100 * vmaj + vmin; - } - else - { - /* old style without minor version, e.g. 9.6devel */ - conn->sversion = (100 * vmaj + vmin) * 100; - } - } - else if (cnt == 1) - { - /* new style without minor version, e.g. 10devel */ - conn->sversion = 100 * 100 * vmaj; - } - else - conn->sversion = 0; /* unknown */ - } -} - - -/* - * pqRowProcessor - * Add the received row to the current async result (conn->result). - * Returns 1 if OK, 0 if error occurred. - * - * On error, *errmsgp can be set to an error string to be returned. - * If it is left NULL, the error is presumed to be "out of memory". - * - * In single-row mode, we create a new result holding just the current row, - * stashing the previous result in conn->next_result so that it becomes - * active again after pqPrepareAsyncResult(). This allows the result metadata - * (column descriptions) to be carried forward to each result row. - */ -int -pqRowProcessor(PGconn *conn, const char **errmsgp) -{ - PGresult *res = conn->result; - int nfields = res->numAttributes; - const PGdataValue *columns = conn->rowBuf; - PGresAttValue *tup; - int i; - - /* - * In single-row mode, make a new PGresult that will hold just this one - * row; the original conn->result is left unchanged so that it can be used - * again as the template for future rows. - */ - if (conn->singleRowMode) - { - /* Copy everything that should be in the result at this point */ - res = PQcopyResult(res, - PG_COPYRES_ATTRS | PG_COPYRES_EVENTS | - PG_COPYRES_NOTICEHOOKS); - if (!res) - return 0; - } - - /* - * Basically we just allocate space in the PGresult for each field and - * copy the data over. - * - * Note: on malloc failure, we return 0 leaving *errmsgp still NULL, which - * caller will take to mean "out of memory". This is preferable to trying - * to set up such a message here, because evidently there's not enough - * memory for gettext() to do anything. - */ - tup = (PGresAttValue *) - pqResultAlloc(res, nfields * sizeof(PGresAttValue), TRUE); - if (tup == NULL) - goto fail; - - for (i = 0; i < nfields; i++) - { - int clen = columns[i].len; - - if (clen < 0) - { - /* null field */ - tup[i].len = NULL_LEN; - tup[i].value = res->null_field; - } - else - { - bool isbinary = (res->attDescs[i].format != 0); - char *val; - - val = (char *) pqResultAlloc(res, clen + 1, isbinary); - if (val == NULL) - goto fail; - - /* copy and zero-terminate the data (even if it's binary) */ - memcpy(val, columns[i].value, clen); - val[clen] = '\0'; - - tup[i].len = clen; - tup[i].value = val; - } - } - - /* And add the tuple to the PGresult's tuple array */ - if (!pqAddTuple(res, tup)) - goto fail; - - /* - * Success. In single-row mode, make the result available to the client - * immediately. - */ - if (conn->singleRowMode) - { - /* Change result status to special single-row value */ - res->resultStatus = PGRES_SINGLE_TUPLE; - /* Stash old result for re-use later */ - conn->next_result = conn->result; - conn->result = res; - /* And mark the result ready to return */ - conn->asyncStatus = PGASYNC_READY; - } - - return 1; - -fail: - /* release locally allocated PGresult, if we made one */ - if (res != conn->result) - PQclear(res); - return 0; -} - - -/* - * PQsendQuery - * Submit a query, but don't wait for it to finish - * - * Returns: 1 if successfully submitted - * 0 if error (conn->errorMessage is set) - */ -int -PQsendQuery(PGconn *conn, const char *query) -{ - if (!PQsendQueryStart(conn)) - return 0; - - /* check the argument */ - if (!query) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("command string is a null pointer\n")); - return 0; - } - - /* construct the outgoing Query message */ - if (pqPutMsgStart('Q', false, conn) < 0 || - pqPuts(query, conn) < 0 || - pqPutMsgEnd(conn) < 0) - { - pqHandleSendFailure(conn); - return 0; - } - - /* remember we are using simple query protocol */ - conn->queryclass = PGQUERY_SIMPLE; - - /* and remember the query text too, if possible */ - /* if insufficient memory, last_query just winds up NULL */ - if (conn->last_query) - free(conn->last_query); - conn->last_query = strdup(query); - - /* - * Give the data a push. In nonblock mode, don't complain if we're unable - * to send it all; PQgetResult() will do any additional flushing needed. - */ - if (pqFlush(conn) < 0) - { - pqHandleSendFailure(conn); - return 0; - } - - /* OK, it's launched! */ - conn->asyncStatus = PGASYNC_BUSY; - return 1; -} - -/* - * PQsendQueryParams - * Like PQsendQuery, but use protocol 3.0 so we can pass parameters - */ -int -PQsendQueryParams(PGconn *conn, - const char *command, - int nParams, - const Oid *paramTypes, - const char *const * paramValues, - const int *paramLengths, - const int *paramFormats, - int resultFormat) -{ - if (!PQsendQueryStart(conn)) - return 0; - - /* check the arguments */ - if (!command) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("command string is a null pointer\n")); - return 0; - } - if (nParams < 0 || nParams > 65535) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("number of parameters must be between 0 and 65535\n")); - return 0; - } - - return PQsendQueryGuts(conn, - command, - "", /* use unnamed statement */ - nParams, - paramTypes, - paramValues, - paramLengths, - paramFormats, - resultFormat); -} - -/* - * PQsendPrepare - * Submit a Parse message, but don't wait for it to finish - * - * Returns: 1 if successfully submitted - * 0 if error (conn->errorMessage is set) - */ -int -PQsendPrepare(PGconn *conn, - const char *stmtName, const char *query, - int nParams, const Oid *paramTypes) -{ - if (!PQsendQueryStart(conn)) - return 0; - - /* check the arguments */ - if (!stmtName) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("statement name is a null pointer\n")); - return 0; - } - if (!query) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("command string is a null pointer\n")); - return 0; - } - if (nParams < 0 || nParams > 65535) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("number of parameters must be between 0 and 65535\n")); - return 0; - } - - /* This isn't gonna work on a 2.0 server */ - if (PG_PROTOCOL_MAJOR(conn->pversion) < 3) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("function requires at least protocol version 3.0\n")); - return 0; - } - - /* construct the Parse message */ - if (pqPutMsgStart('P', false, conn) < 0 || - pqPuts(stmtName, conn) < 0 || - pqPuts(query, conn) < 0) - goto sendFailed; - - if (nParams > 0 && paramTypes) - { - int i; - - if (pqPutInt(nParams, 2, conn) < 0) - goto sendFailed; - for (i = 0; i < nParams; i++) - { - if (pqPutInt(paramTypes[i], 4, conn) < 0) - goto sendFailed; - } - } - else - { - if (pqPutInt(0, 2, conn) < 0) - goto sendFailed; - } - if (pqPutMsgEnd(conn) < 0) - goto sendFailed; - - /* construct the Sync message */ - if (pqPutMsgStart('S', false, conn) < 0 || - pqPutMsgEnd(conn) < 0) - goto sendFailed; - - /* remember we are doing just a Parse */ - conn->queryclass = PGQUERY_PREPARE; - - /* and remember the query text too, if possible */ - /* if insufficient memory, last_query just winds up NULL */ - if (conn->last_query) - free(conn->last_query); - conn->last_query = strdup(query); - - /* - * Give the data a push. In nonblock mode, don't complain if we're unable - * to send it all; PQgetResult() will do any additional flushing needed. - */ - if (pqFlush(conn) < 0) - goto sendFailed; - - /* OK, it's launched! */ - conn->asyncStatus = PGASYNC_BUSY; - return 1; - -sendFailed: - pqHandleSendFailure(conn); - return 0; -} - -/* - * PQsendQueryPrepared - * Like PQsendQuery, but execute a previously prepared statement, - * using protocol 3.0 so we can pass parameters - */ -int -PQsendQueryPrepared(PGconn *conn, - const char *stmtName, - int nParams, - const char *const * paramValues, - const int *paramLengths, - const int *paramFormats, - int resultFormat) -{ - if (!PQsendQueryStart(conn)) - return 0; - - /* check the arguments */ - if (!stmtName) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("statement name is a null pointer\n")); - return 0; - } - if (nParams < 0 || nParams > 65535) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("number of parameters must be between 0 and 65535\n")); - return 0; - } - - return PQsendQueryGuts(conn, - NULL, /* no command to parse */ - stmtName, - nParams, - NULL, /* no param types */ - paramValues, - paramLengths, - paramFormats, - resultFormat); -} - -/* - * Common startup code for PQsendQuery and sibling routines - */ -static bool -PQsendQueryStart(PGconn *conn) -{ - if (!conn) - return false; - - /* clear the error string */ - resetPQExpBuffer(&conn->errorMessage); - - /* Don't try to send if we know there's no live connection. */ - if (conn->status != CONNECTION_OK) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("no connection to the server\n")); - return false; - } - /* Can't send while already busy, either. */ - if (conn->asyncStatus != PGASYNC_IDLE) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("another command is already in progress\n")); - return false; - } - - /* initialize async result-accumulation state */ - pqClearAsyncResult(conn); - - /* reset single-row processing mode */ - conn->singleRowMode = false; - - /* ready to send command message */ - return true; -} - -/* - * PQsendQueryGuts - * Common code for protocol-3.0 query sending - * PQsendQueryStart should be done already - * - * command may be NULL to indicate we use an already-prepared statement - */ -static int -PQsendQueryGuts(PGconn *conn, - const char *command, - const char *stmtName, - int nParams, - const Oid *paramTypes, - const char *const * paramValues, - const int *paramLengths, - const int *paramFormats, - int resultFormat) -{ - int i; - - /* This isn't gonna work on a 2.0 server */ - if (PG_PROTOCOL_MAJOR(conn->pversion) < 3) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("function requires at least protocol version 3.0\n")); - return 0; - } - - /* - * We will send Parse (if needed), Bind, Describe Portal, Execute, Sync, - * using specified statement name and the unnamed portal. - */ - - if (command) - { - /* construct the Parse message */ - if (pqPutMsgStart('P', false, conn) < 0 || - pqPuts(stmtName, conn) < 0 || - pqPuts(command, conn) < 0) - goto sendFailed; - if (nParams > 0 && paramTypes) - { - if (pqPutInt(nParams, 2, conn) < 0) - goto sendFailed; - for (i = 0; i < nParams; i++) - { - if (pqPutInt(paramTypes[i], 4, conn) < 0) - goto sendFailed; - } - } - else - { - if (pqPutInt(0, 2, conn) < 0) - goto sendFailed; - } - if (pqPutMsgEnd(conn) < 0) - goto sendFailed; - } - - /* Construct the Bind message */ - if (pqPutMsgStart('B', false, conn) < 0 || - pqPuts("", conn) < 0 || - pqPuts(stmtName, conn) < 0) - goto sendFailed; - - /* Send parameter formats */ - if (nParams > 0 && paramFormats) - { - if (pqPutInt(nParams, 2, conn) < 0) - goto sendFailed; - for (i = 0; i < nParams; i++) - { - if (pqPutInt(paramFormats[i], 2, conn) < 0) - goto sendFailed; - } - } - else - { - if (pqPutInt(0, 2, conn) < 0) - goto sendFailed; - } - - if (pqPutInt(nParams, 2, conn) < 0) - goto sendFailed; - - /* Send parameters */ - for (i = 0; i < nParams; i++) - { - if (paramValues && paramValues[i]) - { - int nbytes; - - if (paramFormats && paramFormats[i] != 0) - { - /* binary parameter */ - if (paramLengths) - nbytes = paramLengths[i]; - else - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("length must be given for binary parameter\n")); - goto sendFailed; - } - } - else - { - /* text parameter, do not use paramLengths */ - nbytes = strlen(paramValues[i]); - } - if (pqPutInt(nbytes, 4, conn) < 0 || - pqPutnchar(paramValues[i], nbytes, conn) < 0) - goto sendFailed; - } - else - { - /* take the param as NULL */ - if (pqPutInt(-1, 4, conn) < 0) - goto sendFailed; - } - } - if (pqPutInt(1, 2, conn) < 0 || - pqPutInt(resultFormat, 2, conn)) - goto sendFailed; - if (pqPutMsgEnd(conn) < 0) - goto sendFailed; - - /* construct the Describe Portal message */ - if (pqPutMsgStart('D', false, conn) < 0 || - pqPutc('P', conn) < 0 || - pqPuts("", conn) < 0 || - pqPutMsgEnd(conn) < 0) - goto sendFailed; - - /* construct the Execute message */ - if (pqPutMsgStart('E', false, conn) < 0 || - pqPuts("", conn) < 0 || - pqPutInt(0, 4, conn) < 0 || - pqPutMsgEnd(conn) < 0) - goto sendFailed; - - /* construct the Sync message */ - if (pqPutMsgStart('S', false, conn) < 0 || - pqPutMsgEnd(conn) < 0) - goto sendFailed; - - /* remember we are using extended query protocol */ - conn->queryclass = PGQUERY_EXTENDED; - - /* and remember the query text too, if possible */ - /* if insufficient memory, last_query just winds up NULL */ - if (conn->last_query) - free(conn->last_query); - if (command) - conn->last_query = strdup(command); - else - conn->last_query = NULL; - - /* - * Give the data a push. In nonblock mode, don't complain if we're unable - * to send it all; PQgetResult() will do any additional flushing needed. - */ - if (pqFlush(conn) < 0) - goto sendFailed; - - /* OK, it's launched! */ - conn->asyncStatus = PGASYNC_BUSY; - return 1; - -sendFailed: - pqHandleSendFailure(conn); - return 0; -} - -/* - * pqHandleSendFailure: try to clean up after failure to send command. - * - * Primarily, what we want to accomplish here is to process any ERROR or - * NOTICE messages that the backend might have sent just before it died. - * Since we're in IDLE state, all such messages will get sent to the notice - * processor. - * - * NOTE: this routine should only be called in PGASYNC_IDLE state. - */ -void -pqHandleSendFailure(PGconn *conn) -{ - /* - * Accept and parse any available input data, ignoring I/O errors. Note - * that if pqReadData decides the backend has closed the channel, it will - * close our side of the socket --- that's just what we want here. - */ - while (pqReadData(conn) > 0) - parseInput(conn); - - /* - * Be sure to parse available input messages even if we read no data. - * (Note: calling parseInput within the above loop isn't really necessary, - * but it prevents buffer bloat if there's a lot of data available.) - */ - parseInput(conn); -} - -/* - * Select row-by-row processing mode - */ -int -PQsetSingleRowMode(PGconn *conn) -{ - /* - * Only allow setting the flag when we have launched a query and not yet - * received any results. - */ - if (!conn) - return 0; - if (conn->asyncStatus != PGASYNC_BUSY) - return 0; - if (conn->queryclass != PGQUERY_SIMPLE && - conn->queryclass != PGQUERY_EXTENDED) - return 0; - if (conn->result) - return 0; - - /* OK, set flag */ - conn->singleRowMode = true; - return 1; -} - -/* - * Consume any available input from the backend - * 0 return: some kind of trouble - * 1 return: no problem - */ -int -PQconsumeInput(PGconn *conn) -{ - if (!conn) - return 0; - - /* - * for non-blocking connections try to flush the send-queue, otherwise we - * may never get a response for something that may not have already been - * sent because it's in our write buffer! - */ - if (pqIsnonblocking(conn)) - { - if (pqFlush(conn) < 0) - return 0; - } - - /* - * Load more data, if available. We do this no matter what state we are - * in, since we are probably getting called because the application wants - * to get rid of a read-select condition. Note that we will NOT block - * waiting for more input. - */ - if (pqReadData(conn) < 0) - return 0; - - /* Parsing of the data waits till later. */ - return 1; -} - - -/* - * parseInput: if appropriate, parse input data from backend - * until input is exhausted or a stopping state is reached. - * Note that this function will NOT attempt to read more data from the backend. - */ -static void -parseInput(PGconn *conn) -{ - if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3) - pqParseInput3(conn); - else - pqParseInput2(conn); -} - -/* - * PQisBusy - * Return TRUE if PQgetResult would block waiting for input. - */ - -int -PQisBusy(PGconn *conn) -{ - if (!conn) - return FALSE; - - /* Parse any available data, if our state permits. */ - parseInput(conn); - - /* PQgetResult will return immediately in all states except BUSY. */ - return conn->asyncStatus == PGASYNC_BUSY; -} - - -/* - * PQgetResult - * Get the next PGresult produced by a query. Returns NULL if no - * query work remains or an error has occurred (e.g. out of - * memory). - */ - -PGresult * -PQgetResult(PGconn *conn) -{ - PGresult *res; - - if (!conn) - return NULL; - - /* Parse any available data, if our state permits. */ - parseInput(conn); - - /* If not ready to return something, block until we are. */ - while (conn->asyncStatus == PGASYNC_BUSY) - { - int flushResult; - - /* - * If data remains unsent, send it. Else we might be waiting for the - * result of a command the backend hasn't even got yet. - */ - while ((flushResult = pqFlush(conn)) > 0) - { - if (pqWait(FALSE, TRUE, conn)) - { - flushResult = -1; - break; - } - } - - /* Wait for some more data, and load it. */ - if (flushResult || - pqWait(TRUE, FALSE, conn) || - pqReadData(conn) < 0) - { - /* - * conn->errorMessage has been set by pqWait or pqReadData. We - * want to append it to any already-received error message. - */ - pqSaveErrorResult(conn); - conn->asyncStatus = PGASYNC_IDLE; - return pqPrepareAsyncResult(conn); - } - - /* Parse it. */ - parseInput(conn); - } - - /* Return the appropriate thing. */ - switch (conn->asyncStatus) - { - case PGASYNC_IDLE: - res = NULL; /* query is complete */ - break; - case PGASYNC_READY: - res = pqPrepareAsyncResult(conn); - /* Set the state back to BUSY, allowing parsing to proceed. */ - conn->asyncStatus = PGASYNC_BUSY; - break; - case PGASYNC_COPY_IN: - res = getCopyResult(conn, PGRES_COPY_IN); - break; - case PGASYNC_COPY_OUT: - res = getCopyResult(conn, PGRES_COPY_OUT); - break; - case PGASYNC_COPY_BOTH: - res = getCopyResult(conn, PGRES_COPY_BOTH); - break; - default: - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("unexpected asyncStatus: %d\n"), - (int) conn->asyncStatus); - res = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR); - break; - } - - if (res) - { - int i; - - for (i = 0; i < res->nEvents; i++) - { - PGEventResultCreate evt; - - evt.conn = conn; - evt.result = res; - if (!res->events[i].proc(PGEVT_RESULTCREATE, &evt, - res->events[i].passThrough)) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("PGEventProc \"%s\" failed during PGEVT_RESULTCREATE event\n"), - res->events[i].name); - pqSetResultError(res, conn->errorMessage.data); - res->resultStatus = PGRES_FATAL_ERROR; - break; - } - res->events[i].resultInitialized = TRUE; - } - } - - return res; -} - -/* - * getCopyResult - * Helper for PQgetResult: generate result for COPY-in-progress cases - */ -static PGresult * -getCopyResult(PGconn *conn, ExecStatusType copytype) -{ - /* - * If the server connection has been lost, don't pretend everything is - * hunky-dory; instead return a PGRES_FATAL_ERROR result, and reset the - * asyncStatus to idle (corresponding to what we'd do if we'd detected I/O - * error in the earlier steps in PQgetResult). The text returned in the - * result is whatever is in conn->errorMessage; we hope that was filled - * with something relevant when the lost connection was detected. - */ - if (conn->status != CONNECTION_OK) - { - pqSaveErrorResult(conn); - conn->asyncStatus = PGASYNC_IDLE; - return pqPrepareAsyncResult(conn); - } - - /* If we have an async result for the COPY, return that */ - if (conn->result && conn->result->resultStatus == copytype) - return pqPrepareAsyncResult(conn); - - /* Otherwise, invent a suitable PGresult */ - return PQmakeEmptyPGresult(conn, copytype); -} - - -/* - * PQexec - * send a query to the backend and package up the result in a PGresult - * - * If the query was not even sent, return NULL; conn->errorMessage is set to - * a relevant message. - * If the query was sent, a new PGresult is returned (which could indicate - * either success or failure). - * The user is responsible for freeing the PGresult via PQclear() - * when done with it. - */ -PGresult * -PQexec(PGconn *conn, const char *query) -{ - if (!PQexecStart(conn)) - return NULL; - if (!PQsendQuery(conn, query)) - return NULL; - return PQexecFinish(conn); -} - -/* - * PQexecParams - * Like PQexec, but use protocol 3.0 so we can pass parameters - */ -PGresult * -PQexecParams(PGconn *conn, - const char *command, - int nParams, - const Oid *paramTypes, - const char *const * paramValues, - const int *paramLengths, - const int *paramFormats, - int resultFormat) -{ - if (!PQexecStart(conn)) - return NULL; - if (!PQsendQueryParams(conn, command, - nParams, paramTypes, paramValues, paramLengths, - paramFormats, resultFormat)) - return NULL; - return PQexecFinish(conn); -} - -/* - * PQprepare - * Creates a prepared statement by issuing a v3.0 parse message. - * - * If the query was not even sent, return NULL; conn->errorMessage is set to - * a relevant message. - * If the query was sent, a new PGresult is returned (which could indicate - * either success or failure). - * The user is responsible for freeing the PGresult via PQclear() - * when done with it. - */ -PGresult * -PQprepare(PGconn *conn, - const char *stmtName, const char *query, - int nParams, const Oid *paramTypes) -{ - if (!PQexecStart(conn)) - return NULL; - if (!PQsendPrepare(conn, stmtName, query, nParams, paramTypes)) - return NULL; - return PQexecFinish(conn); -} - -/* - * PQexecPrepared - * Like PQexec, but execute a previously prepared statement, - * using protocol 3.0 so we can pass parameters - */ -PGresult * -PQexecPrepared(PGconn *conn, - const char *stmtName, - int nParams, - const char *const * paramValues, - const int *paramLengths, - const int *paramFormats, - int resultFormat) -{ - if (!PQexecStart(conn)) - return NULL; - if (!PQsendQueryPrepared(conn, stmtName, - nParams, paramValues, paramLengths, - paramFormats, resultFormat)) - return NULL; - return PQexecFinish(conn); -} - -/* - * Common code for PQexec and sibling routines: prepare to send command - */ -static bool -PQexecStart(PGconn *conn) -{ - PGresult *result; - - if (!conn) - return false; - - /* - * Silently discard any prior query result that application didn't eat. - * This is probably poor design, but it's here for backward compatibility. - */ - while ((result = PQgetResult(conn)) != NULL) - { - ExecStatusType resultStatus = result->resultStatus; - - PQclear(result); /* only need its status */ - if (resultStatus == PGRES_COPY_IN) - { - if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3) - { - /* In protocol 3, we can get out of a COPY IN state */ - if (PQputCopyEnd(conn, - libpq_gettext("COPY terminated by new PQexec")) < 0) - return false; - /* keep waiting to swallow the copy's failure message */ - } - else - { - /* In older protocols we have to punt */ - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("COPY IN state must be terminated first\n")); - return false; - } - } - else if (resultStatus == PGRES_COPY_OUT) - { - if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3) - { - /* - * In protocol 3, we can get out of a COPY OUT state: we just - * switch back to BUSY and allow the remaining COPY data to be - * dropped on the floor. - */ - conn->asyncStatus = PGASYNC_BUSY; - /* keep waiting to swallow the copy's completion message */ - } - else - { - /* In older protocols we have to punt */ - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("COPY OUT state must be terminated first\n")); - return false; - } - } - else if (resultStatus == PGRES_COPY_BOTH) - { - /* We don't allow PQexec during COPY BOTH */ - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("PQexec not allowed during COPY BOTH\n")); - return false; - } - /* check for loss of connection, too */ - if (conn->status == CONNECTION_BAD) - return false; - } - - /* OK to send a command */ - return true; -} - -/* - * Common code for PQexec and sibling routines: wait for command result - */ -static PGresult * -PQexecFinish(PGconn *conn) -{ - PGresult *result; - PGresult *lastResult; - - /* - * For backwards compatibility, return the last result if there are more - * than one --- but merge error messages if we get more than one error - * result. - * - * We have to stop if we see copy in/out/both, however. We will resume - * parsing after application performs the data transfer. - * - * Also stop if the connection is lost (else we'll loop infinitely). - */ - lastResult = NULL; - while ((result = PQgetResult(conn)) != NULL) - { - if (lastResult) - { - if (lastResult->resultStatus == PGRES_FATAL_ERROR && - result->resultStatus == PGRES_FATAL_ERROR) - { - pqCatenateResultError(lastResult, result->errMsg); - PQclear(result); - result = lastResult; - - /* - * Make sure PQerrorMessage agrees with concatenated result - */ - resetPQExpBuffer(&conn->errorMessage); - appendPQExpBufferStr(&conn->errorMessage, result->errMsg); - } - else - PQclear(lastResult); - } - lastResult = result; - if (result->resultStatus == PGRES_COPY_IN || - result->resultStatus == PGRES_COPY_OUT || - result->resultStatus == PGRES_COPY_BOTH || - conn->status == CONNECTION_BAD) - break; - } - - return lastResult; -} - -/* - * PQdescribePrepared - * Obtain information about a previously prepared statement - * - * If the query was not even sent, return NULL; conn->errorMessage is set to - * a relevant message. - * If the query was sent, a new PGresult is returned (which could indicate - * either success or failure). On success, the PGresult contains status - * PGRES_COMMAND_OK, and its parameter and column-heading fields describe - * the statement's inputs and outputs respectively. - * The user is responsible for freeing the PGresult via PQclear() - * when done with it. - */ -PGresult * -PQdescribePrepared(PGconn *conn, const char *stmt) -{ - if (!PQexecStart(conn)) - return NULL; - if (!PQsendDescribe(conn, 'S', stmt)) - return NULL; - return PQexecFinish(conn); -} - -/* - * PQdescribePortal - * Obtain information about a previously created portal - * - * This is much like PQdescribePrepared, except that no parameter info is - * returned. Note that at the moment, libpq doesn't really expose portals - * to the client; but this can be used with a portal created by a SQL - * DECLARE CURSOR command. - */ -PGresult * -PQdescribePortal(PGconn *conn, const char *portal) -{ - if (!PQexecStart(conn)) - return NULL; - if (!PQsendDescribe(conn, 'P', portal)) - return NULL; - return PQexecFinish(conn); -} - -/* - * PQsendDescribePrepared - * Submit a Describe Statement command, but don't wait for it to finish - * - * Returns: 1 if successfully submitted - * 0 if error (conn->errorMessage is set) - */ -int -PQsendDescribePrepared(PGconn *conn, const char *stmt) -{ - return PQsendDescribe(conn, 'S', stmt); -} - -/* - * PQsendDescribePortal - * Submit a Describe Portal command, but don't wait for it to finish - * - * Returns: 1 if successfully submitted - * 0 if error (conn->errorMessage is set) - */ -int -PQsendDescribePortal(PGconn *conn, const char *portal) -{ - return PQsendDescribe(conn, 'P', portal); -} - -/* - * PQsendDescribe - * Common code to send a Describe command - * - * Available options for desc_type are - * 'S' to describe a prepared statement; or - * 'P' to describe a portal. - * Returns 1 on success and 0 on failure. - */ -static int -PQsendDescribe(PGconn *conn, char desc_type, const char *desc_target) -{ - /* Treat null desc_target as empty string */ - if (!desc_target) - desc_target = ""; - - if (!PQsendQueryStart(conn)) - return 0; - - /* This isn't gonna work on a 2.0 server */ - if (PG_PROTOCOL_MAJOR(conn->pversion) < 3) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("function requires at least protocol version 3.0\n")); - return 0; - } - - /* construct the Describe message */ - if (pqPutMsgStart('D', false, conn) < 0 || - pqPutc(desc_type, conn) < 0 || - pqPuts(desc_target, conn) < 0 || - pqPutMsgEnd(conn) < 0) - goto sendFailed; - - /* construct the Sync message */ - if (pqPutMsgStart('S', false, conn) < 0 || - pqPutMsgEnd(conn) < 0) - goto sendFailed; - - /* remember we are doing a Describe */ - conn->queryclass = PGQUERY_DESCRIBE; - - /* reset last-query string (not relevant now) */ - if (conn->last_query) - { - free(conn->last_query); - conn->last_query = NULL; - } - - /* - * Give the data a push. In nonblock mode, don't complain if we're unable - * to send it all; PQgetResult() will do any additional flushing needed. - */ - if (pqFlush(conn) < 0) - goto sendFailed; - - /* OK, it's launched! */ - conn->asyncStatus = PGASYNC_BUSY; - return 1; - -sendFailed: - pqHandleSendFailure(conn); - return 0; -} - -/* - * PQnotifies - * returns a PGnotify* structure of the latest async notification - * that has not yet been handled - * - * returns NULL, if there is currently - * no unhandled async notification from the backend - * - * the CALLER is responsible for FREE'ing the structure returned - */ -PGnotify * -PQnotifies(PGconn *conn) -{ - PGnotify *event; - - if (!conn) - return NULL; - - /* Parse any available data to see if we can extract NOTIFY messages. */ - parseInput(conn); - - event = conn->notifyHead; - if (event) - { - conn->notifyHead = event->next; - if (!conn->notifyHead) - conn->notifyTail = NULL; - event->next = NULL; /* don't let app see the internal state */ - } - return event; -} - -/* - * PQputCopyData - send some data to the backend during COPY IN or COPY BOTH - * - * Returns 1 if successful, 0 if data could not be sent (only possible - * in nonblock mode), or -1 if an error occurs. - */ -int -PQputCopyData(PGconn *conn, const char *buffer, int nbytes) -{ - if (!conn) - return -1; - if (conn->asyncStatus != PGASYNC_COPY_IN && - conn->asyncStatus != PGASYNC_COPY_BOTH) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("no COPY in progress\n")); - return -1; - } - - /* - * Process any NOTICE or NOTIFY messages that might be pending in the - * input buffer. Since the server might generate many notices during the - * COPY, we want to clean those out reasonably promptly to prevent - * indefinite expansion of the input buffer. (Note: the actual read of - * input data into the input buffer happens down inside pqSendSome, but - * it's not authorized to get rid of the data again.) - */ - parseInput(conn); - - if (nbytes > 0) - { - /* - * Try to flush any previously sent data in preference to growing the - * output buffer. If we can't enlarge the buffer enough to hold the - * data, return 0 in the nonblock case, else hard error. (For - * simplicity, always assume 5 bytes of overhead even in protocol 2.0 - * case.) - */ - if ((conn->outBufSize - conn->outCount - 5) < nbytes) - { - if (pqFlush(conn) < 0) - return -1; - if (pqCheckOutBufferSpace(conn->outCount + 5 + (size_t) nbytes, - conn)) - return pqIsnonblocking(conn) ? 0 : -1; - } - /* Send the data (too simple to delegate to fe-protocol files) */ - if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3) - { - if (pqPutMsgStart('d', false, conn) < 0 || - pqPutnchar(buffer, nbytes, conn) < 0 || - pqPutMsgEnd(conn) < 0) - return -1; - } - else - { - if (pqPutMsgStart(0, false, conn) < 0 || - pqPutnchar(buffer, nbytes, conn) < 0 || - pqPutMsgEnd(conn) < 0) - return -1; - } - } - return 1; -} - -/* - * PQputCopyEnd - send EOF indication to the backend during COPY IN - * - * After calling this, use PQgetResult() to check command completion status. - * - * Returns 1 if successful, 0 if data could not be sent (only possible - * in nonblock mode), or -1 if an error occurs. - */ -int -PQputCopyEnd(PGconn *conn, const char *errormsg) -{ - if (!conn) - return -1; - if (conn->asyncStatus != PGASYNC_COPY_IN && - conn->asyncStatus != PGASYNC_COPY_BOTH) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("no COPY in progress\n")); - return -1; - } - - /* - * Send the COPY END indicator. This is simple enough that we don't - * bother delegating it to the fe-protocol files. - */ - if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3) - { - if (errormsg) - { - /* Send COPY FAIL */ - if (pqPutMsgStart('f', false, conn) < 0 || - pqPuts(errormsg, conn) < 0 || - pqPutMsgEnd(conn) < 0) - return -1; - } - else - { - /* Send COPY DONE */ - if (pqPutMsgStart('c', false, conn) < 0 || - pqPutMsgEnd(conn) < 0) - return -1; - } - - /* - * If we sent the COPY command in extended-query mode, we must issue a - * Sync as well. - */ - if (conn->queryclass != PGQUERY_SIMPLE) - { - if (pqPutMsgStart('S', false, conn) < 0 || - pqPutMsgEnd(conn) < 0) - return -1; - } - } - else - { - if (errormsg) - { - /* Ooops, no way to do this in 2.0 */ - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("function requires at least protocol version 3.0\n")); - return -1; - } - else - { - /* Send old-style end-of-data marker */ - if (pqPutMsgStart(0, false, conn) < 0 || - pqPutnchar("\\.\n", 3, conn) < 0 || - pqPutMsgEnd(conn) < 0) - return -1; - } - } - - /* Return to active duty */ - if (conn->asyncStatus == PGASYNC_COPY_BOTH) - conn->asyncStatus = PGASYNC_COPY_OUT; - else - conn->asyncStatus = PGASYNC_BUSY; - resetPQExpBuffer(&conn->errorMessage); - - /* Try to flush data */ - if (pqFlush(conn) < 0) - return -1; - - return 1; -} - -/* - * PQgetCopyData - read a row of data from the backend during COPY OUT - * or COPY BOTH - * - * If successful, sets *buffer to point to a malloc'd row of data, and - * returns row length (always > 0) as result. - * Returns 0 if no row available yet (only possible if async is true), - * -1 if end of copy (consult PQgetResult), or -2 if error (consult - * PQerrorMessage). - */ -int -PQgetCopyData(PGconn *conn, char **buffer, int async) -{ - *buffer = NULL; /* for all failure cases */ - if (!conn) - return -2; - if (conn->asyncStatus != PGASYNC_COPY_OUT && - conn->asyncStatus != PGASYNC_COPY_BOTH) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("no COPY in progress\n")); - return -2; - } - if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3) - return pqGetCopyData3(conn, buffer, async); - else - return pqGetCopyData2(conn, buffer, async); -} - -/* - * PQgetline - gets a newline-terminated string from the backend. - * - * Chiefly here so that applications can use "COPY <rel> to stdout" - * and read the output string. Returns a null-terminated string in s. - * - * XXX this routine is now deprecated, because it can't handle binary data. - * If called during a COPY BINARY we return EOF. - * - * PQgetline reads up to maxlen-1 characters (like fgets(3)) but strips - * the terminating \n (like gets(3)). - * - * CAUTION: the caller is responsible for detecting the end-of-copy signal - * (a line containing just "\.") when using this routine. - * - * RETURNS: - * EOF if error (eg, invalid arguments are given) - * 0 if EOL is reached (i.e., \n has been read) - * (this is required for backward-compatibility -- this - * routine used to always return EOF or 0, assuming that - * the line ended within maxlen bytes.) - * 1 in other cases (i.e., the buffer was filled before \n is reached) - */ -int -PQgetline(PGconn *conn, char *s, int maxlen) -{ - if (!s || maxlen <= 0) - return EOF; - *s = '\0'; - /* maxlen must be at least 3 to hold the \. terminator! */ - if (maxlen < 3) - return EOF; - - if (!conn) - return EOF; - - if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3) - return pqGetline3(conn, s, maxlen); - else - return pqGetline2(conn, s, maxlen); -} - -/* - * PQgetlineAsync - gets a COPY data row without blocking. - * - * This routine is for applications that want to do "COPY <rel> to stdout" - * asynchronously, that is without blocking. Having issued the COPY command - * and gotten a PGRES_COPY_OUT response, the app should call PQconsumeInput - * and this routine until the end-of-data signal is detected. Unlike - * PQgetline, this routine takes responsibility for detecting end-of-data. - * - * On each call, PQgetlineAsync will return data if a complete data row - * is available in libpq's input buffer. Otherwise, no data is returned - * until the rest of the row arrives. - * - * If -1 is returned, the end-of-data signal has been recognized (and removed - * from libpq's input buffer). The caller *must* next call PQendcopy and - * then return to normal processing. - * - * RETURNS: - * -1 if the end-of-copy-data marker has been recognized - * 0 if no data is available - * >0 the number of bytes returned. - * - * The data returned will not extend beyond a data-row boundary. If possible - * a whole row will be returned at one time. But if the buffer offered by - * the caller is too small to hold a row sent by the backend, then a partial - * data row will be returned. In text mode this can be detected by testing - * whether the last returned byte is '\n' or not. - * - * The returned data is *not* null-terminated. - */ - -int -PQgetlineAsync(PGconn *conn, char *buffer, int bufsize) -{ - if (!conn) - return -1; - - if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3) - return pqGetlineAsync3(conn, buffer, bufsize); - else - return pqGetlineAsync2(conn, buffer, bufsize); -} - -/* - * PQputline -- sends a string to the backend during COPY IN. - * Returns 0 if OK, EOF if not. - * - * This is deprecated primarily because the return convention doesn't allow - * caller to tell the difference between a hard error and a nonblock-mode - * send failure. - */ -int -PQputline(PGconn *conn, const char *s) -{ - return PQputnbytes(conn, s, strlen(s)); -} - -/* - * PQputnbytes -- like PQputline, but buffer need not be null-terminated. - * Returns 0 if OK, EOF if not. - */ -int -PQputnbytes(PGconn *conn, const char *buffer, int nbytes) -{ - if (PQputCopyData(conn, buffer, nbytes) > 0) - return 0; - else - return EOF; -} - -/* - * PQendcopy - * After completing the data transfer portion of a copy in/out, - * the application must call this routine to finish the command protocol. - * - * When using protocol 3.0 this is deprecated; it's cleaner to use PQgetResult - * to get the transfer status. Note however that when using 2.0 protocol, - * recovering from a copy failure often requires a PQreset. PQendcopy will - * take care of that, PQgetResult won't. - * - * RETURNS: - * 0 on success - * 1 on failure - */ -int -PQendcopy(PGconn *conn) -{ - if (!conn) - return 0; - - if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3) - return pqEndcopy3(conn); - else - return pqEndcopy2(conn); -} - - -/* ---------------- - * PQfn - Send a function call to the POSTGRES backend. - * - * conn : backend connection - * fnid : OID of function to be called - * result_buf : pointer to result buffer - * result_len : actual length of result is returned here - * result_is_int : If the result is an integer, this must be 1, - * otherwise this should be 0 - * args : pointer to an array of function arguments - * (each has length, if integer, and value/pointer) - * nargs : # of arguments in args array. - * - * RETURNS - * PGresult with status = PGRES_COMMAND_OK if successful. - * *result_len is > 0 if there is a return value, 0 if not. - * PGresult with status = PGRES_FATAL_ERROR if backend returns an error. - * NULL on communications failure. conn->errorMessage will be set. - * ---------------- - */ - -PGresult * -PQfn(PGconn *conn, - int fnid, - int *result_buf, - int *result_len, - int result_is_int, - const PQArgBlock *args, - int nargs) -{ - *result_len = 0; - - if (!conn) - return NULL; - - /* clear the error string */ - resetPQExpBuffer(&conn->errorMessage); - - if (conn->sock == PGINVALID_SOCKET || conn->asyncStatus != PGASYNC_IDLE || - conn->result != NULL) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("connection in wrong state\n")); - return NULL; - } - - if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3) - return pqFunctionCall3(conn, fnid, - result_buf, result_len, - result_is_int, - args, nargs); - else - return pqFunctionCall2(conn, fnid, - result_buf, result_len, - result_is_int, - args, nargs); -} - - -/* ====== accessor funcs for PGresult ======== */ - -ExecStatusType -PQresultStatus(const PGresult *res) -{ - if (!res) - return PGRES_FATAL_ERROR; - return res->resultStatus; -} - -char * -PQresStatus(ExecStatusType status) -{ - if ((unsigned int) status >= sizeof pgresStatus / sizeof pgresStatus[0]) - return libpq_gettext("invalid ExecStatusType code"); - return pgresStatus[status]; -} - -char * -PQresultErrorMessage(const PGresult *res) -{ - if (!res || !res->errMsg) - return ""; - return res->errMsg; -} - -char * -PQresultVerboseErrorMessage(const PGresult *res, - PGVerbosity verbosity, - PGContextVisibility show_context) -{ - PQExpBufferData workBuf; - - /* - * Because the caller is expected to free the result string, we must - * strdup any constant result. We use plain strdup and document that - * callers should expect NULL if out-of-memory. - */ - if (!res || - (res->resultStatus != PGRES_FATAL_ERROR && - res->resultStatus != PGRES_NONFATAL_ERROR)) - return strdup(libpq_gettext("PGresult is not an error result\n")); - - initPQExpBuffer(&workBuf); - - /* - * Currently, we pass this off to fe-protocol3.c in all cases; it will - * behave reasonably sanely with an error reported by fe-protocol2.c as - * well. If necessary, we could record the protocol version in PGresults - * so as to be able to invoke a version-specific message formatter, but - * for now there's no need. - */ - pqBuildErrorMessage3(&workBuf, res, verbosity, show_context); - - /* If insufficient memory to format the message, fail cleanly */ - if (PQExpBufferDataBroken(workBuf)) - { - termPQExpBuffer(&workBuf); - return strdup(libpq_gettext("out of memory\n")); - } - - return workBuf.data; -} - -char * -PQresultErrorField(const PGresult *res, int fieldcode) -{ - PGMessageField *pfield; - - if (!res) - return NULL; - for (pfield = res->errFields; pfield != NULL; pfield = pfield->next) - { - if (pfield->code == fieldcode) - return pfield->contents; - } - return NULL; -} - -int -PQntuples(const PGresult *res) -{ - if (!res) - return 0; - return res->ntups; -} - -int -PQnfields(const PGresult *res) -{ - if (!res) - return 0; - return res->numAttributes; -} - -int -PQbinaryTuples(const PGresult *res) -{ - if (!res) - return 0; - return res->binary; -} - -/* - * Helper routines to range-check field numbers and tuple numbers. - * Return TRUE if OK, FALSE if not - */ - -static int -check_field_number(const PGresult *res, int field_num) -{ - if (!res) - return FALSE; /* no way to display error message... */ - if (field_num < 0 || field_num >= res->numAttributes) - { - pqInternalNotice(&res->noticeHooks, - "column number %d is out of range 0..%d", - field_num, res->numAttributes - 1); - return FALSE; - } - return TRUE; -} - -static int -check_tuple_field_number(const PGresult *res, - int tup_num, int field_num) -{ - if (!res) - return FALSE; /* no way to display error message... */ - if (tup_num < 0 || tup_num >= res->ntups) - { - pqInternalNotice(&res->noticeHooks, - "row number %d is out of range 0..%d", - tup_num, res->ntups - 1); - return FALSE; - } - if (field_num < 0 || field_num >= res->numAttributes) - { - pqInternalNotice(&res->noticeHooks, - "column number %d is out of range 0..%d", - field_num, res->numAttributes - 1); - return FALSE; - } - return TRUE; -} - -static int -check_param_number(const PGresult *res, int param_num) -{ - if (!res) - return FALSE; /* no way to display error message... */ - if (param_num < 0 || param_num >= res->numParameters) - { - pqInternalNotice(&res->noticeHooks, - "parameter number %d is out of range 0..%d", - param_num, res->numParameters - 1); - return FALSE; - } - - return TRUE; -} - -/* - * returns NULL if the field_num is invalid - */ -char * -PQfname(const PGresult *res, int field_num) -{ - if (!check_field_number(res, field_num)) - return NULL; - if (res->attDescs) - return res->attDescs[field_num].name; - else - return NULL; -} - -/* - * PQfnumber: find column number given column name - * - * The column name is parsed as if it were in a SQL statement, including - * case-folding and double-quote processing. But note a possible gotcha: - * downcasing in the frontend might follow different locale rules than - * downcasing in the backend... - * - * Returns -1 if no match. In the present backend it is also possible - * to have multiple matches, in which case the first one is found. - */ -int -PQfnumber(const PGresult *res, const char *field_name) -{ - char *field_case; - bool in_quotes; - bool all_lower = true; - const char *iptr; - char *optr; - int i; - - if (!res) - return -1; - - /* - * Note: it is correct to reject a zero-length input string; the proper - * input to match a zero-length field name would be "". - */ - if (field_name == NULL || - field_name[0] == '\0' || - res->attDescs == NULL) - return -1; - - /* - * Check if we can avoid the strdup() and related work because the - * passed-in string wouldn't be changed before we do the check anyway. - */ - for (iptr = field_name; *iptr; iptr++) - { - char c = *iptr; - - if (c == '"' || c != pg_tolower((unsigned char) c)) - { - all_lower = false; - break; - } - } - - if (all_lower) - for (i = 0; i < res->numAttributes; i++) - if (strcmp(field_name, res->attDescs[i].name) == 0) - return i; - - /* Fall through to the normal check if that didn't work out. */ - - /* - * Note: this code will not reject partially quoted strings, eg - * foo"BAR"foo will become fooBARfoo when it probably ought to be an error - * condition. - */ - field_case = strdup(field_name); - if (field_case == NULL) - return -1; /* grotty */ - - in_quotes = false; - optr = field_case; - for (iptr = field_case; *iptr; iptr++) - { - char c = *iptr; - - if (in_quotes) - { - if (c == '"') - { - if (iptr[1] == '"') - { - /* doubled quotes become a single quote */ - *optr++ = '"'; - iptr++; - } - else - in_quotes = false; - } - else - *optr++ = c; - } - else if (c == '"') - in_quotes = true; - else - { - c = pg_tolower((unsigned char) c); - *optr++ = c; - } - } - *optr = '\0'; - - for (i = 0; i < res->numAttributes; i++) - { - if (strcmp(field_case, res->attDescs[i].name) == 0) - { - free(field_case); - return i; - } - } - free(field_case); - return -1; -} - -Oid -PQftable(const PGresult *res, int field_num) -{ - if (!check_field_number(res, field_num)) - return InvalidOid; - if (res->attDescs) - return res->attDescs[field_num].tableid; - else - return InvalidOid; -} - -int -PQftablecol(const PGresult *res, int field_num) -{ - if (!check_field_number(res, field_num)) - return 0; - if (res->attDescs) - return res->attDescs[field_num].columnid; - else - return 0; -} - -int -PQfformat(const PGresult *res, int field_num) -{ - if (!check_field_number(res, field_num)) - return 0; - if (res->attDescs) - return res->attDescs[field_num].format; - else - return 0; -} - -Oid -PQftype(const PGresult *res, int field_num) -{ - if (!check_field_number(res, field_num)) - return InvalidOid; - if (res->attDescs) - return res->attDescs[field_num].typid; - else - return InvalidOid; -} - -int -PQfsize(const PGresult *res, int field_num) -{ - if (!check_field_number(res, field_num)) - return 0; - if (res->attDescs) - return res->attDescs[field_num].typlen; - else - return 0; -} - -int -PQfmod(const PGresult *res, int field_num) -{ - if (!check_field_number(res, field_num)) - return 0; - if (res->attDescs) - return res->attDescs[field_num].atttypmod; - else - return 0; -} - -char * -PQcmdStatus(PGresult *res) -{ - if (!res) - return NULL; - return res->cmdStatus; -} - -/* - * PQoidStatus - - * if the last command was an INSERT, return the oid string - * if not, return "" - */ -char * -PQoidStatus(const PGresult *res) -{ - /* - * This must be enough to hold the result. Don't laugh, this is better - * than what this function used to do. - */ - static char buf[24]; - - size_t len; - - if (!res || strncmp(res->cmdStatus, "INSERT ", 7) != 0) - return ""; - - len = strspn(res->cmdStatus + 7, "0123456789"); - if (len > sizeof(buf) - 1) - len = sizeof(buf) - 1; - memcpy(buf, res->cmdStatus + 7, len); - buf[len] = '\0'; - - return buf; -} - -/* - * PQoidValue - - * a perhaps preferable form of the above which just returns - * an Oid type - */ -Oid -PQoidValue(const PGresult *res) -{ - char *endptr = NULL; - unsigned long result; - - if (!res || - strncmp(res->cmdStatus, "INSERT ", 7) != 0 || - res->cmdStatus[7] < '0' || - res->cmdStatus[7] > '9') - return InvalidOid; - - result = strtoul(res->cmdStatus + 7, &endptr, 10); - - if (!endptr || (*endptr != ' ' && *endptr != '\0')) - return InvalidOid; - else - return (Oid) result; -} - - -/* - * PQcmdTuples - - * If the last command was INSERT/UPDATE/DELETE/MOVE/FETCH/COPY, return - * a string containing the number of inserted/affected tuples. If not, - * return "". - * - * XXX: this should probably return an int - */ -char * -PQcmdTuples(PGresult *res) -{ - char *p, - *c; - - if (!res) - return ""; - - if (strncmp(res->cmdStatus, "INSERT ", 7) == 0) - { - p = res->cmdStatus + 7; - /* INSERT: skip oid and space */ - while (*p && *p != ' ') - p++; - if (*p == 0) - goto interpret_error; /* no space? */ - p++; - } - else if (strncmp(res->cmdStatus, "SELECT ", 7) == 0 || - strncmp(res->cmdStatus, "DELETE ", 7) == 0 || - strncmp(res->cmdStatus, "UPDATE ", 7) == 0) - p = res->cmdStatus + 7; - else if (strncmp(res->cmdStatus, "FETCH ", 6) == 0) - p = res->cmdStatus + 6; - else if (strncmp(res->cmdStatus, "MOVE ", 5) == 0 || - strncmp(res->cmdStatus, "COPY ", 5) == 0) - p = res->cmdStatus + 5; - else - return ""; - - /* check that we have an integer (at least one digit, nothing else) */ - for (c = p; *c; c++) - { - if (!isdigit((unsigned char) *c)) - goto interpret_error; - } - if (c == p) - goto interpret_error; - - return p; - -interpret_error: - pqInternalNotice(&res->noticeHooks, - "could not interpret result from server: %s", - res->cmdStatus); - return ""; -} - -/* - * PQgetvalue: - * return the value of field 'field_num' of row 'tup_num' - */ -char * -PQgetvalue(const PGresult *res, int tup_num, int field_num) -{ - if (!check_tuple_field_number(res, tup_num, field_num)) - return NULL; - return res->tuples[tup_num][field_num].value; -} - -/* PQgetlength: - * returns the actual length of a field value in bytes. - */ -int -PQgetlength(const PGresult *res, int tup_num, int field_num) -{ - if (!check_tuple_field_number(res, tup_num, field_num)) - return 0; - if (res->tuples[tup_num][field_num].len != NULL_LEN) - return res->tuples[tup_num][field_num].len; - else - return 0; -} - -/* PQgetisnull: - * returns the null status of a field value. - */ -int -PQgetisnull(const PGresult *res, int tup_num, int field_num) -{ - if (!check_tuple_field_number(res, tup_num, field_num)) - return 1; /* pretend it is null */ - if (res->tuples[tup_num][field_num].len == NULL_LEN) - return 1; - else - return 0; -} - -/* PQnparams: - * returns the number of input parameters of a prepared statement. - */ -int -PQnparams(const PGresult *res) -{ - if (!res) - return 0; - return res->numParameters; -} - -/* PQparamtype: - * returns type Oid of the specified statement parameter. - */ -Oid -PQparamtype(const PGresult *res, int param_num) -{ - if (!check_param_number(res, param_num)) - return InvalidOid; - if (res->paramDescs) - return res->paramDescs[param_num].typid; - else - return InvalidOid; -} - - -/* PQsetnonblocking: - * sets the PGconn's database connection non-blocking if the arg is TRUE - * or makes it blocking if the arg is FALSE, this will not protect - * you from PQexec(), you'll only be safe when using the non-blocking API. - * Needs to be called only on a connected database connection. - */ -int -PQsetnonblocking(PGconn *conn, int arg) -{ - bool barg; - - if (!conn || conn->status == CONNECTION_BAD) - return -1; - - barg = (arg ? TRUE : FALSE); - - /* early out if the socket is already in the state requested */ - if (barg == conn->nonblocking) - return 0; - - /* - * to guarantee constancy for flushing/query/result-polling behavior we - * need to flush the send queue at this point in order to guarantee proper - * behavior. this is ok because either they are making a transition _from_ - * or _to_ blocking mode, either way we can block them. - */ - /* if we are going from blocking to non-blocking flush here */ - if (pqFlush(conn)) - return -1; - - conn->nonblocking = barg; - - return 0; -} - -/* - * return the blocking status of the database connection - * TRUE == nonblocking, FALSE == blocking - */ -int -PQisnonblocking(const PGconn *conn) -{ - return pqIsnonblocking(conn); -} - -/* libpq is thread-safe? */ -int -PQisthreadsafe(void) -{ -#ifdef ENABLE_THREAD_SAFETY - return true; -#else - return false; -#endif -} - - -/* try to force data out, really only useful for non-blocking users */ -int -PQflush(PGconn *conn) -{ - return pqFlush(conn); -} - - -/* - * PQfreemem - safely frees memory allocated - * - * Needed mostly by Win32, unless multithreaded DLL (/MD in VC6) - * Used for freeing memory from PQescapeByte()a/PQunescapeBytea() - */ -void -PQfreemem(void *ptr) -{ - free(ptr); -} - -/* - * PQfreeNotify - free's the memory associated with a PGnotify - * - * This function is here only for binary backward compatibility. - * New code should use PQfreemem(). A macro will automatically map - * calls to PQfreemem. It should be removed in the future. bjm 2003-03-24 - */ - -#undef PQfreeNotify -void PQfreeNotify(PGnotify *notify); - -void -PQfreeNotify(PGnotify *notify) -{ - PQfreemem(notify); -} - - -/* - * Escaping arbitrary strings to get valid SQL literal strings. - * - * Replaces "'" with "''", and if not std_strings, replaces "\" with "\\". - * - * length is the length of the source string. (Note: if a terminating NUL - * is encountered sooner, PQescapeString stops short of "length"; the behavior - * is thus rather like strncpy.) - * - * For safety the buffer at "to" must be at least 2*length + 1 bytes long. - * A terminating NUL character is added to the output string, whether the - * input is NUL-terminated or not. - * - * Returns the actual length of the output (not counting the terminating NUL). - */ -static size_t -PQescapeStringInternal(PGconn *conn, - char *to, const char *from, size_t length, - int *error, - int encoding, bool std_strings) -{ - const char *source = from; - char *target = to; - size_t remaining = length; - - if (error) - *error = 0; - - while (remaining > 0 && *source != '\0') - { - char c = *source; - int len; - int i; - - /* Fast path for plain ASCII */ - if (!IS_HIGHBIT_SET(c)) - { - /* Apply quoting if needed */ - if (SQL_STR_DOUBLE(c, !std_strings)) - *target++ = c; - /* Copy the character */ - *target++ = c; - source++; - remaining--; - continue; - } - - /* Slow path for possible multibyte characters */ - len = pg_encoding_mblen(encoding, source); - - /* Copy the character */ - for (i = 0; i < len; i++) - { - if (remaining == 0 || *source == '\0') - break; - *target++ = *source++; - remaining--; - } - - /* - * If we hit premature end of string (ie, incomplete multibyte - * character), try to pad out to the correct length with spaces. We - * may not be able to pad completely, but we will always be able to - * insert at least one pad space (since we'd not have quoted a - * multibyte character). This should be enough to make a string that - * the server will error out on. - */ - if (i < len) - { - if (error) - *error = 1; - if (conn) - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("incomplete multibyte character\n")); - for (; i < len; i++) - { - if (((size_t) (target - to)) / 2 >= length) - break; - *target++ = ' '; - } - break; - } - } - - /* Write the terminating NUL character. */ - *target = '\0'; - - return target - to; -} - -size_t -PQescapeStringConn(PGconn *conn, - char *to, const char *from, size_t length, - int *error) -{ - if (!conn) - { - /* force empty-string result */ - *to = '\0'; - if (error) - *error = 1; - return 0; - } - return PQescapeStringInternal(conn, to, from, length, error, - conn->client_encoding, - conn->std_strings); -} - -size_t -PQescapeString(char *to, const char *from, size_t length) -{ - return PQescapeStringInternal(NULL, to, from, length, NULL, - static_client_encoding, - static_std_strings); -} - - -/* - * Escape arbitrary strings. If as_ident is true, we escape the result - * as an identifier; if false, as a literal. The result is returned in - * a newly allocated buffer. If we fail due to an encoding violation or out - * of memory condition, we return NULL, storing an error message into conn. - */ -static char * -PQescapeInternal(PGconn *conn, const char *str, size_t len, bool as_ident) -{ - const char *s; - char *result; - char *rp; - int num_quotes = 0; /* single or double, depending on as_ident */ - int num_backslashes = 0; - int input_len; - int result_size; - char quote_char = as_ident ? '"' : '\''; - - /* We must have a connection, else fail immediately. */ - if (!conn) - return NULL; - - /* Scan the string for characters that must be escaped. */ - for (s = str; (s - str) < len && *s != '\0'; ++s) - { - if (*s == quote_char) - ++num_quotes; - else if (*s == '\\') - ++num_backslashes; - else if (IS_HIGHBIT_SET(*s)) - { - int charlen; - - /* Slow path for possible multibyte characters */ - charlen = pg_encoding_mblen(conn->client_encoding, s); - - /* Multibyte character overruns allowable length. */ - if ((s - str) + charlen > len || memchr(s, 0, charlen) != NULL) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("incomplete multibyte character\n")); - return NULL; - } - - /* Adjust s, bearing in mind that for loop will increment it. */ - s += charlen - 1; - } - } - - /* Allocate output buffer. */ - input_len = s - str; - result_size = input_len + num_quotes + 3; /* two quotes, plus a NUL */ - if (!as_ident && num_backslashes > 0) - result_size += num_backslashes + 2; - result = rp = (char *) malloc(result_size); - if (rp == NULL) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("out of memory\n")); - return NULL; - } - - /* - * If we are escaping a literal that contains backslashes, we use the - * escape string syntax so that the result is correct under either value - * of standard_conforming_strings. We also emit a leading space in this - * case, to guard against the possibility that the result might be - * interpolated immediately following an identifier. - */ - if (!as_ident && num_backslashes > 0) - { - *rp++ = ' '; - *rp++ = 'E'; - } - - /* Opening quote. */ - *rp++ = quote_char; - - /* - * Use fast path if possible. - * - * We've already verified that the input string is well-formed in the - * current encoding. If it contains no quotes and, in the case of - * literal-escaping, no backslashes, then we can just copy it directly to - * the output buffer, adding the necessary quotes. - * - * If not, we must rescan the input and process each character - * individually. - */ - if (num_quotes == 0 && (num_backslashes == 0 || as_ident)) - { - memcpy(rp, str, input_len); - rp += input_len; - } - else - { - for (s = str; s - str < input_len; ++s) - { - if (*s == quote_char || (!as_ident && *s == '\\')) - { - *rp++ = *s; - *rp++ = *s; - } - else if (!IS_HIGHBIT_SET(*s)) - *rp++ = *s; - else - { - int i = pg_encoding_mblen(conn->client_encoding, s); - - while (1) - { - *rp++ = *s; - if (--i == 0) - break; - ++s; /* for loop will provide the final increment */ - } - } - } - } - - /* Closing quote and terminating NUL. */ - *rp++ = quote_char; - *rp = '\0'; - - return result; -} - -char * -PQescapeLiteral(PGconn *conn, const char *str, size_t len) -{ - return PQescapeInternal(conn, str, len, false); -} - -char * -PQescapeIdentifier(PGconn *conn, const char *str, size_t len) -{ - return PQescapeInternal(conn, str, len, true); -} - -/* HEX encoding support for bytea */ -static const char hextbl[] = "0123456789abcdef"; - -static const int8 hexlookup[128] = { - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, - -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -}; - -static inline char -get_hex(char c) -{ - int res = -1; - - if (c > 0 && c < 127) - res = hexlookup[(unsigned char) c]; - - return (char) res; -} - - -/* - * PQescapeBytea - converts from binary string to the - * minimal encoding necessary to include the string in an SQL - * INSERT statement with a bytea type column as the target. - * - * We can use either hex or escape (traditional) encoding. - * In escape mode, the following transformations are applied: - * '\0' == ASCII 0 == \000 - * '\'' == ASCII 39 == '' - * '\\' == ASCII 92 == \\ - * anything < 0x20, or > 0x7e ---> \ooo - * (where ooo is an octal expression) - * - * If not std_strings, all backslashes sent to the output are doubled. - */ -static unsigned char * -PQescapeByteaInternal(PGconn *conn, - const unsigned char *from, size_t from_length, - size_t *to_length, bool std_strings, bool use_hex) -{ - const unsigned char *vp; - unsigned char *rp; - unsigned char *result; - size_t i; - size_t len; - size_t bslash_len = (std_strings ? 1 : 2); - - /* - * empty string has 1 char ('\0') - */ - len = 1; - - if (use_hex) - { - len += bslash_len + 1 + 2 * from_length; - } - else - { - vp = from; - for (i = from_length; i > 0; i--, vp++) - { - if (*vp < 0x20 || *vp > 0x7e) - len += bslash_len + 3; - else if (*vp == '\'') - len += 2; - else if (*vp == '\\') - len += bslash_len + bslash_len; - else - len++; - } - } - - *to_length = len; - rp = result = (unsigned char *) malloc(len); - if (rp == NULL) - { - if (conn) - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("out of memory\n")); - return NULL; - } - - if (use_hex) - { - if (!std_strings) - *rp++ = '\\'; - *rp++ = '\\'; - *rp++ = 'x'; - } - - vp = from; - for (i = from_length; i > 0; i--, vp++) - { - unsigned char c = *vp; - - if (use_hex) - { - *rp++ = hextbl[(c >> 4) & 0xF]; - *rp++ = hextbl[c & 0xF]; - } - else if (c < 0x20 || c > 0x7e) - { - if (!std_strings) - *rp++ = '\\'; - *rp++ = '\\'; - *rp++ = (c >> 6) + '0'; - *rp++ = ((c >> 3) & 07) + '0'; - *rp++ = (c & 07) + '0'; - } - else if (c == '\'') - { - *rp++ = '\''; - *rp++ = '\''; - } - else if (c == '\\') - { - if (!std_strings) - { - *rp++ = '\\'; - *rp++ = '\\'; - } - *rp++ = '\\'; - *rp++ = '\\'; - } - else - *rp++ = c; - } - *rp = '\0'; - - return result; -} - -unsigned char * -PQescapeByteaConn(PGconn *conn, - const unsigned char *from, size_t from_length, - size_t *to_length) -{ - if (!conn) - return NULL; - return PQescapeByteaInternal(conn, from, from_length, to_length, - conn->std_strings, - (conn->sversion >= 90000)); -} - -unsigned char * -PQescapeBytea(const unsigned char *from, size_t from_length, size_t *to_length) -{ - return PQescapeByteaInternal(NULL, from, from_length, to_length, - static_std_strings, - false /* can't use hex */ ); -} - - -#define ISFIRSTOCTDIGIT(CH) ((CH) >= '0' && (CH) <= '3') -#define ISOCTDIGIT(CH) ((CH) >= '0' && (CH) <= '7') -#define OCTVAL(CH) ((CH) - '0') - -/* - * PQunescapeBytea - converts the null terminated string representation - * of a bytea, strtext, into binary, filling a buffer. It returns a - * pointer to the buffer (or NULL on error), and the size of the - * buffer in retbuflen. The pointer may subsequently be used as an - * argument to the function PQfreemem. - * - * The following transformations are made: - * \\ == ASCII 92 == \ - * \ooo == a byte whose value = ooo (ooo is an octal number) - * \x == x (x is any character not matched by the above transformations) - */ -unsigned char * -PQunescapeBytea(const unsigned char *strtext, size_t *retbuflen) -{ - size_t strtextlen, - buflen; - unsigned char *buffer, - *tmpbuf; - size_t i, - j; - - if (strtext == NULL) - return NULL; - - strtextlen = strlen((const char *) strtext); - - if (strtext[0] == '\\' && strtext[1] == 'x') - { - const unsigned char *s; - unsigned char *p; - - buflen = (strtextlen - 2) / 2; - /* Avoid unportable malloc(0) */ - buffer = (unsigned char *) malloc(buflen > 0 ? buflen : 1); - if (buffer == NULL) - return NULL; - - s = strtext + 2; - p = buffer; - while (*s) - { - char v1, - v2; - - /* - * Bad input is silently ignored. Note that this includes - * whitespace between hex pairs, which is allowed by byteain. - */ - v1 = get_hex(*s++); - if (!*s || v1 == (char) -1) - continue; - v2 = get_hex(*s++); - if (v2 != (char) -1) - *p++ = (v1 << 4) | v2; - } - - buflen = p - buffer; - } - else - { - /* - * Length of input is max length of output, but add one to avoid - * unportable malloc(0) if input is zero-length. - */ - buffer = (unsigned char *) malloc(strtextlen + 1); - if (buffer == NULL) - return NULL; - - for (i = j = 0; i < strtextlen;) - { - switch (strtext[i]) - { - case '\\': - i++; - if (strtext[i] == '\\') - buffer[j++] = strtext[i++]; - else - { - if ((ISFIRSTOCTDIGIT(strtext[i])) && - (ISOCTDIGIT(strtext[i + 1])) && - (ISOCTDIGIT(strtext[i + 2]))) - { - int byte; - - byte = OCTVAL(strtext[i++]); - byte = (byte << 3) + OCTVAL(strtext[i++]); - byte = (byte << 3) + OCTVAL(strtext[i++]); - buffer[j++] = byte; - } - } - - /* - * Note: if we see '\' followed by something that isn't a - * recognized escape sequence, we loop around having done - * nothing except advance i. Therefore the something will - * be emitted as ordinary data on the next cycle. Corner - * case: '\' at end of string will just be discarded. - */ - break; - - default: - buffer[j++] = strtext[i++]; - break; - } - } - buflen = j; /* buflen is the length of the dequoted data */ - } - - /* Shrink the buffer to be no larger than necessary */ - /* +1 avoids unportable behavior when buflen==0 */ - tmpbuf = realloc(buffer, buflen + 1); - - /* It would only be a very brain-dead realloc that could fail, but... */ - if (!tmpbuf) - { - free(buffer); - return NULL; - } - - *retbuflen = buflen; - return tmpbuf; -} diff --git a/libpq/fe-lobj.c b/libpq/fe-lobj.c deleted file mode 100644 index 3b08768..0000000 --- a/libpq/fe-lobj.c +++ /dev/null @@ -1,1103 +0,0 @@ -/*------------------------------------------------------------------------- - * - * fe-lobj.c - * Front-end large object interface - * - * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * - * IDENTIFICATION - * src/interfaces/libpq/fe-lobj.c - * - *------------------------------------------------------------------------- - */ - -#ifdef WIN32 -/* - * As unlink/rename are #define'd in port.h (via postgres_fe.h), io.h - * must be included first on MS C. Might as well do it for all WIN32's - * here. - */ -#include <io.h> -#endif - -#include "postgres_fe.h" - -#ifdef WIN32 -#include "win32.h" -#else -#include <unistd.h> -#endif - -#include <fcntl.h> -#include <limits.h> -#include <sys/stat.h> -#include <netinet/in.h> /* for ntohl/htonl */ -#include <arpa/inet.h> - -#include "libpq-fe.h" -#include "libpq-int.h" -#include "libpq/libpq-fs.h" /* must come after sys/stat.h */ - -#define LO_BUFSIZE 8192 - -static int lo_initialize(PGconn *conn); -static Oid lo_import_internal(PGconn *conn, const char *filename, Oid oid); -static pg_int64 lo_hton64(pg_int64 host64); -static pg_int64 lo_ntoh64(pg_int64 net64); - -/* - * lo_open - * opens an existing large object - * - * returns the file descriptor for use in later lo_* calls - * return -1 upon failure. - */ -int -lo_open(PGconn *conn, Oid lobjId, int mode) -{ - int fd; - int result_len; - PQArgBlock argv[2]; - PGresult *res; - - if (conn == NULL || conn->lobjfuncs == NULL) - { - if (lo_initialize(conn) < 0) - return -1; - } - - argv[0].isint = 1; - argv[0].len = 4; - argv[0].u.integer = lobjId; - - argv[1].isint = 1; - argv[1].len = 4; - argv[1].u.integer = mode; - - res = PQfn(conn, conn->lobjfuncs->fn_lo_open, &fd, &result_len, 1, argv, 2); - if (PQresultStatus(res) == PGRES_COMMAND_OK) - { - PQclear(res); - return fd; - } - else - { - PQclear(res); - return -1; - } -} - -/* - * lo_close - * closes an existing large object - * - * returns 0 upon success - * returns -1 upon failure. - */ -int -lo_close(PGconn *conn, int fd) -{ - PQArgBlock argv[1]; - PGresult *res; - int retval; - int result_len; - - if (conn == NULL || conn->lobjfuncs == NULL) - { - if (lo_initialize(conn) < 0) - return -1; - } - - argv[0].isint = 1; - argv[0].len = 4; - argv[0].u.integer = fd; - res = PQfn(conn, conn->lobjfuncs->fn_lo_close, - &retval, &result_len, 1, argv, 1); - if (PQresultStatus(res) == PGRES_COMMAND_OK) - { - PQclear(res); - return retval; - } - else - { - PQclear(res); - return -1; - } -} - -/* - * lo_truncate - * truncates an existing large object to the given size - * - * returns 0 upon success - * returns -1 upon failure - */ -int -lo_truncate(PGconn *conn, int fd, size_t len) -{ - PQArgBlock argv[2]; - PGresult *res; - int retval; - int result_len; - - if (conn == NULL || conn->lobjfuncs == NULL) - { - if (lo_initialize(conn) < 0) - return -1; - } - - /* Must check this on-the-fly because it's not there pre-8.3 */ - if (conn->lobjfuncs->fn_lo_truncate == 0) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("cannot determine OID of function lo_truncate\n")); - return -1; - } - - /* - * Long ago, somebody thought it'd be a good idea to declare this function - * as taking size_t ... but the underlying backend function only accepts a - * signed int32 length. So throw error if the given value overflows - * int32. (A possible alternative is to automatically redirect the call - * to lo_truncate64; but if the caller wanted to rely on that backend - * function being available, he could have called lo_truncate64 for - * himself.) - */ - if (len > (size_t) INT_MAX) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("argument of lo_truncate exceeds integer range\n")); - return -1; - } - - argv[0].isint = 1; - argv[0].len = 4; - argv[0].u.integer = fd; - - argv[1].isint = 1; - argv[1].len = 4; - argv[1].u.integer = (int) len; - - res = PQfn(conn, conn->lobjfuncs->fn_lo_truncate, - &retval, &result_len, 1, argv, 2); - - if (PQresultStatus(res) == PGRES_COMMAND_OK) - { - PQclear(res); - return retval; - } - else - { - PQclear(res); - return -1; - } -} - -/* - * lo_truncate64 - * truncates an existing large object to the given size - * - * returns 0 upon success - * returns -1 upon failure - */ -int -lo_truncate64(PGconn *conn, int fd, pg_int64 len) -{ - PQArgBlock argv[2]; - PGresult *res; - int retval; - int result_len; - - if (conn == NULL || conn->lobjfuncs == NULL) - { - if (lo_initialize(conn) < 0) - return -1; - } - - if (conn->lobjfuncs->fn_lo_truncate64 == 0) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("cannot determine OID of function lo_truncate64\n")); - return -1; - } - - argv[0].isint = 1; - argv[0].len = 4; - argv[0].u.integer = fd; - - len = lo_hton64(len); - argv[1].isint = 0; - argv[1].len = 8; - argv[1].u.ptr = (int *) &len; - - res = PQfn(conn, conn->lobjfuncs->fn_lo_truncate64, - &retval, &result_len, 1, argv, 2); - - if (PQresultStatus(res) == PGRES_COMMAND_OK) - { - PQclear(res); - return retval; - } - else - { - PQclear(res); - return -1; - } -} - -/* - * lo_read - * read len bytes of the large object into buf - * - * returns the number of bytes read, or -1 on failure. - * the CALLER must have allocated enough space to hold the result returned - */ - -int -lo_read(PGconn *conn, int fd, char *buf, size_t len) -{ - PQArgBlock argv[2]; - PGresult *res; - int result_len; - - if (conn == NULL || conn->lobjfuncs == NULL) - { - if (lo_initialize(conn) < 0) - return -1; - } - - /* - * Long ago, somebody thought it'd be a good idea to declare this function - * as taking size_t ... but the underlying backend function only accepts a - * signed int32 length. So throw error if the given value overflows - * int32. - */ - if (len > (size_t) INT_MAX) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("argument of lo_read exceeds integer range\n")); - return -1; - } - - argv[0].isint = 1; - argv[0].len = 4; - argv[0].u.integer = fd; - - argv[1].isint = 1; - argv[1].len = 4; - argv[1].u.integer = (int) len; - - res = PQfn(conn, conn->lobjfuncs->fn_lo_read, - (void *) buf, &result_len, 0, argv, 2); - if (PQresultStatus(res) == PGRES_COMMAND_OK) - { - PQclear(res); - return result_len; - } - else - { - PQclear(res); - return -1; - } -} - -/* - * lo_write - * write len bytes of buf into the large object fd - * - * returns the number of bytes written, or -1 on failure. - */ -int -lo_write(PGconn *conn, int fd, const char *buf, size_t len) -{ - PQArgBlock argv[2]; - PGresult *res; - int result_len; - int retval; - - if (conn == NULL || conn->lobjfuncs == NULL) - { - if (lo_initialize(conn) < 0) - return -1; - } - - /* - * Long ago, somebody thought it'd be a good idea to declare this function - * as taking size_t ... but the underlying backend function only accepts a - * signed int32 length. So throw error if the given value overflows - * int32. - */ - if (len > (size_t) INT_MAX) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("argument of lo_write exceeds integer range\n")); - return -1; - } - - argv[0].isint = 1; - argv[0].len = 4; - argv[0].u.integer = fd; - - argv[1].isint = 0; - argv[1].len = (int) len; - argv[1].u.ptr = (int *) buf; - - res = PQfn(conn, conn->lobjfuncs->fn_lo_write, - &retval, &result_len, 1, argv, 2); - if (PQresultStatus(res) == PGRES_COMMAND_OK) - { - PQclear(res); - return retval; - } - else - { - PQclear(res); - return -1; - } -} - -/* - * lo_lseek - * change the current read or write location on a large object - */ -int -lo_lseek(PGconn *conn, int fd, int offset, int whence) -{ - PQArgBlock argv[3]; - PGresult *res; - int retval; - int result_len; - - if (conn == NULL || conn->lobjfuncs == NULL) - { - if (lo_initialize(conn) < 0) - return -1; - } - - argv[0].isint = 1; - argv[0].len = 4; - argv[0].u.integer = fd; - - argv[1].isint = 1; - argv[1].len = 4; - argv[1].u.integer = offset; - - argv[2].isint = 1; - argv[2].len = 4; - argv[2].u.integer = whence; - - res = PQfn(conn, conn->lobjfuncs->fn_lo_lseek, - &retval, &result_len, 1, argv, 3); - if (PQresultStatus(res) == PGRES_COMMAND_OK) - { - PQclear(res); - return retval; - } - else - { - PQclear(res); - return -1; - } -} - -/* - * lo_lseek64 - * change the current read or write location on a large object - */ -pg_int64 -lo_lseek64(PGconn *conn, int fd, pg_int64 offset, int whence) -{ - PQArgBlock argv[3]; - PGresult *res; - pg_int64 retval; - int result_len; - - if (conn == NULL || conn->lobjfuncs == NULL) - { - if (lo_initialize(conn) < 0) - return -1; - } - - if (conn->lobjfuncs->fn_lo_lseek64 == 0) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("cannot determine OID of function lo_lseek64\n")); - return -1; - } - - argv[0].isint = 1; - argv[0].len = 4; - argv[0].u.integer = fd; - - offset = lo_hton64(offset); - argv[1].isint = 0; - argv[1].len = 8; - argv[1].u.ptr = (int *) &offset; - - argv[2].isint = 1; - argv[2].len = 4; - argv[2].u.integer = whence; - - res = PQfn(conn, conn->lobjfuncs->fn_lo_lseek64, - (void *) &retval, &result_len, 0, argv, 3); - if (PQresultStatus(res) == PGRES_COMMAND_OK && result_len == 8) - { - PQclear(res); - return lo_ntoh64(retval); - } - else - { - PQclear(res); - return -1; - } -} - -/* - * lo_creat - * create a new large object - * the mode is ignored (once upon a time it had a use) - * - * returns the oid of the large object created or - * InvalidOid upon failure - */ -Oid -lo_creat(PGconn *conn, int mode) -{ - PQArgBlock argv[1]; - PGresult *res; - int retval; - int result_len; - - if (conn == NULL || conn->lobjfuncs == NULL) - { - if (lo_initialize(conn) < 0) - return InvalidOid; - } - - argv[0].isint = 1; - argv[0].len = 4; - argv[0].u.integer = mode; - res = PQfn(conn, conn->lobjfuncs->fn_lo_creat, - &retval, &result_len, 1, argv, 1); - if (PQresultStatus(res) == PGRES_COMMAND_OK) - { - PQclear(res); - return (Oid) retval; - } - else - { - PQclear(res); - return InvalidOid; - } -} - -/* - * lo_create - * create a new large object - * if lobjId isn't InvalidOid, it specifies the OID to (attempt to) create - * - * returns the oid of the large object created or - * InvalidOid upon failure - */ -Oid -lo_create(PGconn *conn, Oid lobjId) -{ - PQArgBlock argv[1]; - PGresult *res; - int retval; - int result_len; - - if (conn == NULL || conn->lobjfuncs == NULL) - { - if (lo_initialize(conn) < 0) - return InvalidOid; - } - - /* Must check this on-the-fly because it's not there pre-8.1 */ - if (conn->lobjfuncs->fn_lo_create == 0) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("cannot determine OID of function lo_create\n")); - return InvalidOid; - } - - argv[0].isint = 1; - argv[0].len = 4; - argv[0].u.integer = lobjId; - res = PQfn(conn, conn->lobjfuncs->fn_lo_create, - &retval, &result_len, 1, argv, 1); - if (PQresultStatus(res) == PGRES_COMMAND_OK) - { - PQclear(res); - return (Oid) retval; - } - else - { - PQclear(res); - return InvalidOid; - } -} - - -/* - * lo_tell - * returns the current seek location of the large object - */ -int -lo_tell(PGconn *conn, int fd) -{ - int retval; - PQArgBlock argv[1]; - PGresult *res; - int result_len; - - if (conn == NULL || conn->lobjfuncs == NULL) - { - if (lo_initialize(conn) < 0) - return -1; - } - - argv[0].isint = 1; - argv[0].len = 4; - argv[0].u.integer = fd; - - res = PQfn(conn, conn->lobjfuncs->fn_lo_tell, - &retval, &result_len, 1, argv, 1); - if (PQresultStatus(res) == PGRES_COMMAND_OK) - { - PQclear(res); - return retval; - } - else - { - PQclear(res); - return -1; - } -} - -/* - * lo_tell64 - * returns the current seek location of the large object - */ -pg_int64 -lo_tell64(PGconn *conn, int fd) -{ - pg_int64 retval; - PQArgBlock argv[1]; - PGresult *res; - int result_len; - - if (conn == NULL || conn->lobjfuncs == NULL) - { - if (lo_initialize(conn) < 0) - return -1; - } - - if (conn->lobjfuncs->fn_lo_tell64 == 0) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("cannot determine OID of function lo_tell64\n")); - return -1; - } - - argv[0].isint = 1; - argv[0].len = 4; - argv[0].u.integer = fd; - - res = PQfn(conn, conn->lobjfuncs->fn_lo_tell64, - (void *) &retval, &result_len, 0, argv, 1); - if (PQresultStatus(res) == PGRES_COMMAND_OK && result_len == 8) - { - PQclear(res); - return lo_ntoh64(retval); - } - else - { - PQclear(res); - return -1; - } -} - -/* - * lo_unlink - * delete a file - */ - -int -lo_unlink(PGconn *conn, Oid lobjId) -{ - PQArgBlock argv[1]; - PGresult *res; - int result_len; - int retval; - - if (conn == NULL || conn->lobjfuncs == NULL) - { - if (lo_initialize(conn) < 0) - return -1; - } - - argv[0].isint = 1; - argv[0].len = 4; - argv[0].u.integer = lobjId; - - res = PQfn(conn, conn->lobjfuncs->fn_lo_unlink, - &retval, &result_len, 1, argv, 1); - if (PQresultStatus(res) == PGRES_COMMAND_OK) - { - PQclear(res); - return retval; - } - else - { - PQclear(res); - return -1; - } -} - -/* - * lo_import - - * imports a file as an (inversion) large object. - * - * returns the oid of that object upon success, - * returns InvalidOid upon failure - */ - -Oid -lo_import(PGconn *conn, const char *filename) -{ - return lo_import_internal(conn, filename, InvalidOid); -} - -/* - * lo_import_with_oid - - * imports a file as an (inversion) large object. - * large object id can be specified. - * - * returns the oid of that object upon success, - * returns InvalidOid upon failure - */ - -Oid -lo_import_with_oid(PGconn *conn, const char *filename, Oid lobjId) -{ - return lo_import_internal(conn, filename, lobjId); -} - -static Oid -lo_import_internal(PGconn *conn, const char *filename, Oid oid) -{ - int fd; - int nbytes, - tmp; - char buf[LO_BUFSIZE]; - Oid lobjOid; - int lobj; - char sebuf[256]; - - /* - * open the file to be read in - */ - fd = open(filename, O_RDONLY | PG_BINARY, 0666); - if (fd < 0) - { /* error */ - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("could not open file \"%s\": %s\n"), - filename, pqStrerror(errno, sebuf, sizeof(sebuf))); - return InvalidOid; - } - - /* - * create an inversion object - */ - if (oid == InvalidOid) - lobjOid = lo_creat(conn, INV_READ | INV_WRITE); - else - lobjOid = lo_create(conn, oid); - - if (lobjOid == InvalidOid) - { - /* we assume lo_create() already set a suitable error message */ - (void) close(fd); - return InvalidOid; - } - - lobj = lo_open(conn, lobjOid, INV_WRITE); - if (lobj == -1) - { - /* we assume lo_open() already set a suitable error message */ - (void) close(fd); - return InvalidOid; - } - - /* - * read in from the file and write to the large object - */ - while ((nbytes = read(fd, buf, LO_BUFSIZE)) > 0) - { - tmp = lo_write(conn, lobj, buf, nbytes); - if (tmp != nbytes) - { - /* - * If lo_write() failed, we are now in an aborted transaction so - * there's no need for lo_close(); furthermore, if we tried it - * we'd overwrite the useful error result with a useless one. So - * just nail the doors shut and get out of town. - */ - (void) close(fd); - return InvalidOid; - } - } - - if (nbytes < 0) - { - /* We must do lo_close before setting the errorMessage */ - int save_errno = errno; - - (void) lo_close(conn, lobj); - (void) close(fd); - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("could not read from file \"%s\": %s\n"), - filename, - pqStrerror(save_errno, sebuf, sizeof(sebuf))); - return InvalidOid; - } - - (void) close(fd); - - if (lo_close(conn, lobj) != 0) - { - /* we assume lo_close() already set a suitable error message */ - return InvalidOid; - } - - return lobjOid; -} - -/* - * lo_export - - * exports an (inversion) large object. - * returns -1 upon failure, 1 if OK - */ -int -lo_export(PGconn *conn, Oid lobjId, const char *filename) -{ - int result = 1; - int fd; - int nbytes, - tmp; - char buf[LO_BUFSIZE]; - int lobj; - char sebuf[256]; - - /* - * open the large object. - */ - lobj = lo_open(conn, lobjId, INV_READ); - if (lobj == -1) - { - /* we assume lo_open() already set a suitable error message */ - return -1; - } - - /* - * create the file to be written to - */ - fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC | PG_BINARY, 0666); - if (fd < 0) - { - /* We must do lo_close before setting the errorMessage */ - int save_errno = errno; - - (void) lo_close(conn, lobj); - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("could not open file \"%s\": %s\n"), - filename, - pqStrerror(save_errno, sebuf, sizeof(sebuf))); - return -1; - } - - /* - * read in from the large object and write to the file - */ - while ((nbytes = lo_read(conn, lobj, buf, LO_BUFSIZE)) > 0) - { - tmp = write(fd, buf, nbytes); - if (tmp != nbytes) - { - /* We must do lo_close before setting the errorMessage */ - int save_errno = errno; - - (void) lo_close(conn, lobj); - (void) close(fd); - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("could not write to file \"%s\": %s\n"), - filename, - pqStrerror(save_errno, sebuf, sizeof(sebuf))); - return -1; - } - } - - /* - * If lo_read() failed, we are now in an aborted transaction so there's no - * need for lo_close(); furthermore, if we tried it we'd overwrite the - * useful error result with a useless one. So skip lo_close() if we got a - * failure result. - */ - if (nbytes < 0 || - lo_close(conn, lobj) != 0) - { - /* assume lo_read() or lo_close() left a suitable error message */ - result = -1; - } - - /* if we already failed, don't overwrite that msg with a close error */ - if (close(fd) && result >= 0) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("could not write to file \"%s\": %s\n"), - filename, pqStrerror(errno, sebuf, sizeof(sebuf))); - result = -1; - } - - return result; -} - - -/* - * lo_initialize - * - * Initialize the large object interface for an existing connection. - * We ask the backend about the functions OID's in pg_proc for all - * functions that are required for large object operations. - */ -static int -lo_initialize(PGconn *conn) -{ - PGresult *res; - PGlobjfuncs *lobjfuncs; - int n; - const char *query; - const char *fname; - Oid foid; - - if (!conn) - return -1; - - /* - * Allocate the structure to hold the functions OID's - */ - lobjfuncs = (PGlobjfuncs *) malloc(sizeof(PGlobjfuncs)); - if (lobjfuncs == NULL) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("out of memory\n")); - return -1; - } - MemSet((char *) lobjfuncs, 0, sizeof(PGlobjfuncs)); - - /* - * Execute the query to get all the functions at once. In 7.3 and later - * we need to be schema-safe. lo_create only exists in 8.1 and up. - * lo_truncate only exists in 8.3 and up. - */ - if (conn->sversion >= 70300) - query = "select proname, oid from pg_catalog.pg_proc " - "where proname in (" - "'lo_open', " - "'lo_close', " - "'lo_creat', " - "'lo_create', " - "'lo_unlink', " - "'lo_lseek', " - "'lo_lseek64', " - "'lo_tell', " - "'lo_tell64', " - "'lo_truncate', " - "'lo_truncate64', " - "'loread', " - "'lowrite') " - "and pronamespace = (select oid from pg_catalog.pg_namespace " - "where nspname = 'pg_catalog')"; - else - query = "select proname, oid from pg_proc " - "where proname = 'lo_open' " - "or proname = 'lo_close' " - "or proname = 'lo_creat' " - "or proname = 'lo_unlink' " - "or proname = 'lo_lseek' " - "or proname = 'lo_tell' " - "or proname = 'loread' " - "or proname = 'lowrite'"; - - res = PQexec(conn, query); - if (res == NULL) - { - free(lobjfuncs); - return -1; - } - - if (res->resultStatus != PGRES_TUPLES_OK) - { - free(lobjfuncs); - PQclear(res); - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("query to initialize large object functions did not return data\n")); - return -1; - } - - /* - * Examine the result and put the OID's into the struct - */ - for (n = 0; n < PQntuples(res); n++) - { - fname = PQgetvalue(res, n, 0); - foid = (Oid) atoi(PQgetvalue(res, n, 1)); - if (strcmp(fname, "lo_open") == 0) - lobjfuncs->fn_lo_open = foid; - else if (strcmp(fname, "lo_close") == 0) - lobjfuncs->fn_lo_close = foid; - else if (strcmp(fname, "lo_creat") == 0) - lobjfuncs->fn_lo_creat = foid; - else if (strcmp(fname, "lo_create") == 0) - lobjfuncs->fn_lo_create = foid; - else if (strcmp(fname, "lo_unlink") == 0) - lobjfuncs->fn_lo_unlink = foid; - else if (strcmp(fname, "lo_lseek") == 0) - lobjfuncs->fn_lo_lseek = foid; - else if (strcmp(fname, "lo_lseek64") == 0) - lobjfuncs->fn_lo_lseek64 = foid; - else if (strcmp(fname, "lo_tell") == 0) - lobjfuncs->fn_lo_tell = foid; - else if (strcmp(fname, "lo_tell64") == 0) - lobjfuncs->fn_lo_tell64 = foid; - else if (strcmp(fname, "lo_truncate") == 0) - lobjfuncs->fn_lo_truncate = foid; - else if (strcmp(fname, "lo_truncate64") == 0) - lobjfuncs->fn_lo_truncate64 = foid; - else if (strcmp(fname, "loread") == 0) - lobjfuncs->fn_lo_read = foid; - else if (strcmp(fname, "lowrite") == 0) - lobjfuncs->fn_lo_write = foid; - } - - PQclear(res); - - /* - * Finally check that we got all required large object interface functions - * (ones that have been added later than the stone age are instead checked - * only if used) - */ - if (lobjfuncs->fn_lo_open == 0) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("cannot determine OID of function lo_open\n")); - free(lobjfuncs); - return -1; - } - if (lobjfuncs->fn_lo_close == 0) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("cannot determine OID of function lo_close\n")); - free(lobjfuncs); - return -1; - } - if (lobjfuncs->fn_lo_creat == 0) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("cannot determine OID of function lo_creat\n")); - free(lobjfuncs); - return -1; - } - if (lobjfuncs->fn_lo_unlink == 0) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("cannot determine OID of function lo_unlink\n")); - free(lobjfuncs); - return -1; - } - if (lobjfuncs->fn_lo_lseek == 0) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("cannot determine OID of function lo_lseek\n")); - free(lobjfuncs); - return -1; - } - if (lobjfuncs->fn_lo_tell == 0) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("cannot determine OID of function lo_tell\n")); - free(lobjfuncs); - return -1; - } - if (lobjfuncs->fn_lo_read == 0) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("cannot determine OID of function loread\n")); - free(lobjfuncs); - return -1; - } - if (lobjfuncs->fn_lo_write == 0) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("cannot determine OID of function lowrite\n")); - free(lobjfuncs); - return -1; - } - - /* - * Put the structure into the connection control - */ - conn->lobjfuncs = lobjfuncs; - return 0; -} - -/* - * lo_hton64 - * converts a 64-bit integer from host byte order to network byte order - */ -static pg_int64 -lo_hton64(pg_int64 host64) -{ - union - { - pg_int64 i64; - uint32 i32[2]; - } swap; - uint32 t; - - /* High order half first, since we're doing MSB-first */ - t = (uint32) (host64 >> 32); - swap.i32[0] = htonl(t); - - /* Now the low order half */ - t = (uint32) host64; - swap.i32[1] = htonl(t); - - return swap.i64; -} - -/* - * lo_ntoh64 - * converts a 64-bit integer from network byte order to host byte order - */ -static pg_int64 -lo_ntoh64(pg_int64 net64) -{ - union - { - pg_int64 i64; - uint32 i32[2]; - } swap; - pg_int64 result; - - swap.i64 = net64; - - result = (uint32) ntohl(swap.i32[0]); - result <<= 32; - result |= (uint32) ntohl(swap.i32[1]); - - return result; -} diff --git a/libpq/fe-misc.c b/libpq/fe-misc.c deleted file mode 100644 index 32da8ca..0000000 --- a/libpq/fe-misc.c +++ /dev/null @@ -1,1267 +0,0 @@ -/*------------------------------------------------------------------------- - * - * FILE - * fe-misc.c - * - * DESCRIPTION - * miscellaneous useful functions - * - * The communication routines here are analogous to the ones in - * backend/libpq/pqcomm.c and backend/libpq/pqcomprim.c, but operate - * in the considerably different environment of the frontend libpq. - * In particular, we work with a bare nonblock-mode socket, rather than - * a stdio stream, so that we can avoid unwanted blocking of the application. - * - * XXX: MOVE DEBUG PRINTOUT TO HIGHER LEVEL. As is, block and restart - * will cause repeat printouts. - * - * We must speak the same transmitted data representations as the backend - * routines. - * - * - * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * IDENTIFICATION - * src/interfaces/libpq/fe-misc.c - * - *------------------------------------------------------------------------- - */ - -#include "postgres_fe.h" - -#include <signal.h> -#include <time.h> - -#include <netinet/in.h> -#include <arpa/inet.h> - -#ifdef WIN32 -#include "win32.h" -#else -#include <unistd.h> -#include <sys/time.h> -#endif - -#ifdef HAVE_POLL_H -#include <poll.h> -#endif -#ifdef HAVE_SYS_POLL_H -#include <sys/poll.h> -#endif -#ifdef HAVE_SYS_SELECT_H -#include <sys/select.h> -#endif - -#include "libpq-fe.h" -#include "libpq-int.h" -#include "mb/pg_wchar.h" -#include "pg_config_paths.h" - - -static int pqPutMsgBytes(const void *buf, size_t len, PGconn *conn); -static int pqSendSome(PGconn *conn, int len); -static int pqSocketCheck(PGconn *conn, int forRead, int forWrite, - time_t end_time); -static int pqSocketPoll(int sock, int forRead, int forWrite, time_t end_time); - -/* - * PQlibVersion: return the libpq version number - */ -int -PQlibVersion(void) -{ - return PG_VERSION_NUM; -} - -/* - * fputnbytes: print exactly N bytes to a file - * - * We avoid using %.*s here because it can misbehave if the data - * is not valid in what libc thinks is the prevailing encoding. - */ -static void -fputnbytes(FILE *f, const char *str, size_t n) -{ - while (n-- > 0) - fputc(*str++, f); -} - - -/* - * pqGetc: get 1 character from the connection - * - * All these routines return 0 on success, EOF on error. - * Note that for the Get routines, EOF only means there is not enough - * data in the buffer, not that there is necessarily a hard error. - */ -int -pqGetc(char *result, PGconn *conn) -{ - if (conn->inCursor >= conn->inEnd) - return EOF; - - *result = conn->inBuffer[conn->inCursor++]; - - if (conn->Pfdebug) - fprintf(conn->Pfdebug, "From backend> %c\n", *result); - - return 0; -} - - -/* - * pqPutc: write 1 char to the current message - */ -int -pqPutc(char c, PGconn *conn) -{ - if (pqPutMsgBytes(&c, 1, conn)) - return EOF; - - if (conn->Pfdebug) - fprintf(conn->Pfdebug, "To backend> %c\n", c); - - return 0; -} - - -/* - * pqGets[_append]: - * get a null-terminated string from the connection, - * and store it in an expansible PQExpBuffer. - * If we run out of memory, all of the string is still read, - * but the excess characters are silently discarded. - */ -static int -pqGets_internal(PQExpBuffer buf, PGconn *conn, bool resetbuffer) -{ - /* Copy conn data to locals for faster search loop */ - char *inBuffer = conn->inBuffer; - int inCursor = conn->inCursor; - int inEnd = conn->inEnd; - int slen; - - while (inCursor < inEnd && inBuffer[inCursor]) - inCursor++; - - if (inCursor >= inEnd) - return EOF; - - slen = inCursor - conn->inCursor; - - if (resetbuffer) - resetPQExpBuffer(buf); - - appendBinaryPQExpBuffer(buf, inBuffer + conn->inCursor, slen); - - conn->inCursor = ++inCursor; - - if (conn->Pfdebug) - fprintf(conn->Pfdebug, "From backend> \"%s\"\n", - buf->data); - - return 0; -} - -int -pqGets(PQExpBuffer buf, PGconn *conn) -{ - return pqGets_internal(buf, conn, true); -} - -int -pqGets_append(PQExpBuffer buf, PGconn *conn) -{ - return pqGets_internal(buf, conn, false); -} - - -/* - * pqPuts: write a null-terminated string to the current message - */ -int -pqPuts(const char *s, PGconn *conn) -{ - if (pqPutMsgBytes(s, strlen(s) + 1, conn)) - return EOF; - - if (conn->Pfdebug) - fprintf(conn->Pfdebug, "To backend> \"%s\"\n", s); - - return 0; -} - -/* - * pqGetnchar: - * get a string of exactly len bytes in buffer s, no null termination - */ -int -pqGetnchar(char *s, size_t len, PGconn *conn) -{ - if (len > (size_t) (conn->inEnd - conn->inCursor)) - return EOF; - - memcpy(s, conn->inBuffer + conn->inCursor, len); - /* no terminating null */ - - conn->inCursor += len; - - if (conn->Pfdebug) - { - fprintf(conn->Pfdebug, "From backend (%lu)> ", (unsigned long) len); - fputnbytes(conn->Pfdebug, s, len); - fprintf(conn->Pfdebug, "\n"); - } - - return 0; -} - -/* - * pqSkipnchar: - * skip over len bytes in input buffer. - * - * Note: this is primarily useful for its debug output, which should - * be exactly the same as for pqGetnchar. We assume the data in question - * will actually be used, but just isn't getting copied anywhere as yet. - */ -int -pqSkipnchar(size_t len, PGconn *conn) -{ - if (len > (size_t) (conn->inEnd - conn->inCursor)) - return EOF; - - if (conn->Pfdebug) - { - fprintf(conn->Pfdebug, "From backend (%lu)> ", (unsigned long) len); - fputnbytes(conn->Pfdebug, conn->inBuffer + conn->inCursor, len); - fprintf(conn->Pfdebug, "\n"); - } - - conn->inCursor += len; - - return 0; -} - -/* - * pqPutnchar: - * write exactly len bytes to the current message - */ -int -pqPutnchar(const char *s, size_t len, PGconn *conn) -{ - if (pqPutMsgBytes(s, len, conn)) - return EOF; - - if (conn->Pfdebug) - { - fprintf(conn->Pfdebug, "To backend> "); - fputnbytes(conn->Pfdebug, s, len); - fprintf(conn->Pfdebug, "\n"); - } - - return 0; -} - -/* - * pqGetInt - * read a 2 or 4 byte integer and convert from network byte order - * to local byte order - */ -int -pqGetInt(int *result, size_t bytes, PGconn *conn) -{ - uint16 tmp2; - uint32 tmp4; - - switch (bytes) - { - case 2: - if (conn->inCursor + 2 > conn->inEnd) - return EOF; - memcpy(&tmp2, conn->inBuffer + conn->inCursor, 2); - conn->inCursor += 2; - *result = (int) ntohs(tmp2); - break; - case 4: - if (conn->inCursor + 4 > conn->inEnd) - return EOF; - memcpy(&tmp4, conn->inBuffer + conn->inCursor, 4); - conn->inCursor += 4; - *result = (int) ntohl(tmp4); - break; - default: - pqInternalNotice(&conn->noticeHooks, - "integer of size %lu not supported by pqGetInt", - (unsigned long) bytes); - return EOF; - } - - if (conn->Pfdebug) - fprintf(conn->Pfdebug, "From backend (#%lu)> %d\n", (unsigned long) bytes, *result); - - return 0; -} - -/* - * pqPutInt - * write an integer of 2 or 4 bytes, converting from host byte order - * to network byte order. - */ -int -pqPutInt(int value, size_t bytes, PGconn *conn) -{ - uint16 tmp2; - uint32 tmp4; - - switch (bytes) - { - case 2: - tmp2 = htons((uint16) value); - if (pqPutMsgBytes((const char *) &tmp2, 2, conn)) - return EOF; - break; - case 4: - tmp4 = htonl((uint32) value); - if (pqPutMsgBytes((const char *) &tmp4, 4, conn)) - return EOF; - break; - default: - pqInternalNotice(&conn->noticeHooks, - "integer of size %lu not supported by pqPutInt", - (unsigned long) bytes); - return EOF; - } - - if (conn->Pfdebug) - fprintf(conn->Pfdebug, "To backend (%lu#)> %d\n", (unsigned long) bytes, value); - - return 0; -} - -/* - * Make sure conn's output buffer can hold bytes_needed bytes (caller must - * include already-stored data into the value!) - * - * Returns 0 on success, EOF if failed to enlarge buffer - */ -int -pqCheckOutBufferSpace(size_t bytes_needed, PGconn *conn) -{ - int newsize = conn->outBufSize; - char *newbuf; - - /* Quick exit if we have enough space */ - if (bytes_needed <= (size_t) newsize) - return 0; - - /* - * If we need to enlarge the buffer, we first try to double it in size; if - * that doesn't work, enlarge in multiples of 8K. This avoids thrashing - * the malloc pool by repeated small enlargements. - * - * Note: tests for newsize > 0 are to catch integer overflow. - */ - do - { - newsize *= 2; - } while (newsize > 0 && bytes_needed > (size_t) newsize); - - if (newsize > 0 && bytes_needed <= (size_t) newsize) - { - newbuf = realloc(conn->outBuffer, newsize); - if (newbuf) - { - /* realloc succeeded */ - conn->outBuffer = newbuf; - conn->outBufSize = newsize; - return 0; - } - } - - newsize = conn->outBufSize; - do - { - newsize += 8192; - } while (newsize > 0 && bytes_needed > (size_t) newsize); - - if (newsize > 0 && bytes_needed <= (size_t) newsize) - { - newbuf = realloc(conn->outBuffer, newsize); - if (newbuf) - { - /* realloc succeeded */ - conn->outBuffer = newbuf; - conn->outBufSize = newsize; - return 0; - } - } - - /* realloc failed. Probably out of memory */ - printfPQExpBuffer(&conn->errorMessage, - "cannot allocate memory for output buffer\n"); - return EOF; -} - -/* - * Make sure conn's input buffer can hold bytes_needed bytes (caller must - * include already-stored data into the value!) - * - * Returns 0 on success, EOF if failed to enlarge buffer - */ -int -pqCheckInBufferSpace(size_t bytes_needed, PGconn *conn) -{ - int newsize = conn->inBufSize; - char *newbuf; - - /* Quick exit if we have enough space */ - if (bytes_needed <= (size_t) newsize) - return 0; - - /* - * Before concluding that we need to enlarge the buffer, left-justify - * whatever is in it and recheck. The caller's value of bytes_needed - * includes any data to the left of inStart, but we can delete that in - * preference to enlarging the buffer. It's slightly ugly to have this - * function do this, but it's better than making callers worry about it. - */ - bytes_needed -= conn->inStart; - - if (conn->inStart < conn->inEnd) - { - if (conn->inStart > 0) - { - memmove(conn->inBuffer, conn->inBuffer + conn->inStart, - conn->inEnd - conn->inStart); - conn->inEnd -= conn->inStart; - conn->inCursor -= conn->inStart; - conn->inStart = 0; - } - } - else - { - /* buffer is logically empty, reset it */ - conn->inStart = conn->inCursor = conn->inEnd = 0; - } - - /* Recheck whether we have enough space */ - if (bytes_needed <= (size_t) newsize) - return 0; - - /* - * If we need to enlarge the buffer, we first try to double it in size; if - * that doesn't work, enlarge in multiples of 8K. This avoids thrashing - * the malloc pool by repeated small enlargements. - * - * Note: tests for newsize > 0 are to catch integer overflow. - */ - do - { - newsize *= 2; - } while (newsize > 0 && bytes_needed > (size_t) newsize); - - if (newsize > 0 && bytes_needed <= (size_t) newsize) - { - newbuf = realloc(conn->inBuffer, newsize); - if (newbuf) - { - /* realloc succeeded */ - conn->inBuffer = newbuf; - conn->inBufSize = newsize; - return 0; - } - } - - newsize = conn->inBufSize; - do - { - newsize += 8192; - } while (newsize > 0 && bytes_needed > (size_t) newsize); - - if (newsize > 0 && bytes_needed <= (size_t) newsize) - { - newbuf = realloc(conn->inBuffer, newsize); - if (newbuf) - { - /* realloc succeeded */ - conn->inBuffer = newbuf; - conn->inBufSize = newsize; - return 0; - } - } - - /* realloc failed. Probably out of memory */ - printfPQExpBuffer(&conn->errorMessage, - "cannot allocate memory for input buffer\n"); - return EOF; -} - -/* - * pqPutMsgStart: begin construction of a message to the server - * - * msg_type is the message type byte, or 0 for a message without type byte - * (only startup messages have no type byte) - * - * force_len forces the message to have a length word; otherwise, we add - * a length word if protocol 3. - * - * Returns 0 on success, EOF on error - * - * The idea here is that we construct the message in conn->outBuffer, - * beginning just past any data already in outBuffer (ie, at - * outBuffer+outCount). We enlarge the buffer as needed to hold the message. - * When the message is complete, we fill in the length word (if needed) and - * then advance outCount past the message, making it eligible to send. - * - * The state variable conn->outMsgStart points to the incomplete message's - * length word: it is either outCount or outCount+1 depending on whether - * there is a type byte. If we are sending a message without length word - * (pre protocol 3.0 only), then outMsgStart is -1. The state variable - * conn->outMsgEnd is the end of the data collected so far. - */ -int -pqPutMsgStart(char msg_type, bool force_len, PGconn *conn) -{ - int lenPos; - int endPos; - - /* allow room for message type byte */ - if (msg_type) - endPos = conn->outCount + 1; - else - endPos = conn->outCount; - - /* do we want a length word? */ - if (force_len || PG_PROTOCOL_MAJOR(conn->pversion) >= 3) - { - lenPos = endPos; - /* allow room for message length */ - endPos += 4; - } - else - lenPos = -1; - - /* make sure there is room for message header */ - if (pqCheckOutBufferSpace(endPos, conn)) - return EOF; - /* okay, save the message type byte if any */ - if (msg_type) - conn->outBuffer[conn->outCount] = msg_type; - /* set up the message pointers */ - conn->outMsgStart = lenPos; - conn->outMsgEnd = endPos; - /* length word, if needed, will be filled in by pqPutMsgEnd */ - - if (conn->Pfdebug) - fprintf(conn->Pfdebug, "To backend> Msg %c\n", - msg_type ? msg_type : ' '); - - return 0; -} - -/* - * pqPutMsgBytes: add bytes to a partially-constructed message - * - * Returns 0 on success, EOF on error - */ -static int -pqPutMsgBytes(const void *buf, size_t len, PGconn *conn) -{ - /* make sure there is room for it */ - if (pqCheckOutBufferSpace(conn->outMsgEnd + len, conn)) - return EOF; - /* okay, save the data */ - memcpy(conn->outBuffer + conn->outMsgEnd, buf, len); - conn->outMsgEnd += len; - /* no Pfdebug call here, caller should do it */ - return 0; -} - -/* - * pqPutMsgEnd: finish constructing a message and possibly send it - * - * Returns 0 on success, EOF on error - * - * We don't actually send anything here unless we've accumulated at least - * 8K worth of data (the typical size of a pipe buffer on Unix systems). - * This avoids sending small partial packets. The caller must use pqFlush - * when it's important to flush all the data out to the server. - */ -int -pqPutMsgEnd(PGconn *conn) -{ - if (conn->Pfdebug) - fprintf(conn->Pfdebug, "To backend> Msg complete, length %u\n", - conn->outMsgEnd - conn->outCount); - - /* Fill in length word if needed */ - if (conn->outMsgStart >= 0) - { - uint32 msgLen = conn->outMsgEnd - conn->outMsgStart; - - msgLen = htonl(msgLen); - memcpy(conn->outBuffer + conn->outMsgStart, &msgLen, 4); - } - - /* Make message eligible to send */ - conn->outCount = conn->outMsgEnd; - - if (conn->outCount >= 8192) - { - int toSend = conn->outCount - (conn->outCount % 8192); - - if (pqSendSome(conn, toSend) < 0) - return EOF; - /* in nonblock mode, don't complain if unable to send it all */ - } - - return 0; -} - -/* ---------- - * pqReadData: read more data, if any is available - * Possible return values: - * 1: successfully loaded at least one more byte - * 0: no data is presently available, but no error detected - * -1: error detected (including EOF = connection closure); - * conn->errorMessage set - * NOTE: callers must not assume that pointers or indexes into conn->inBuffer - * remain valid across this call! - * ---------- - */ -int -pqReadData(PGconn *conn) -{ - int someread = 0; - int nread; - - if (conn->sock == PGINVALID_SOCKET) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("connection not open\n")); - return -1; - } - - /* Left-justify any data in the buffer to make room */ - if (conn->inStart < conn->inEnd) - { - if (conn->inStart > 0) - { - memmove(conn->inBuffer, conn->inBuffer + conn->inStart, - conn->inEnd - conn->inStart); - conn->inEnd -= conn->inStart; - conn->inCursor -= conn->inStart; - conn->inStart = 0; - } - } - else - { - /* buffer is logically empty, reset it */ - conn->inStart = conn->inCursor = conn->inEnd = 0; - } - - /* - * If the buffer is fairly full, enlarge it. We need to be able to enlarge - * the buffer in case a single message exceeds the initial buffer size. We - * enlarge before filling the buffer entirely so as to avoid asking the - * kernel for a partial packet. The magic constant here should be large - * enough for a TCP packet or Unix pipe bufferload. 8K is the usual pipe - * buffer size, so... - */ - if (conn->inBufSize - conn->inEnd < 8192) - { - if (pqCheckInBufferSpace(conn->inEnd + (size_t) 8192, conn)) - { - /* - * We don't insist that the enlarge worked, but we need some room - */ - if (conn->inBufSize - conn->inEnd < 100) - return -1; /* errorMessage already set */ - } - } - - /* OK, try to read some data */ -retry3: - nread = pqsecure_read(conn, conn->inBuffer + conn->inEnd, - conn->inBufSize - conn->inEnd); - if (nread < 0) - { - if (SOCK_ERRNO == EINTR) - goto retry3; - /* Some systems return EAGAIN/EWOULDBLOCK for no data */ -#ifdef EAGAIN - if (SOCK_ERRNO == EAGAIN) - return someread; -#endif -#if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN)) - if (SOCK_ERRNO == EWOULDBLOCK) - return someread; -#endif - /* We might get ECONNRESET here if using TCP and backend died */ -#ifdef ECONNRESET - if (SOCK_ERRNO == ECONNRESET) - goto definitelyFailed; -#endif - /* pqsecure_read set the error message for us */ - return -1; - } - if (nread > 0) - { - conn->inEnd += nread; - - /* - * Hack to deal with the fact that some kernels will only give us back - * 1 packet per recv() call, even if we asked for more and there is - * more available. If it looks like we are reading a long message, - * loop back to recv() again immediately, until we run out of data or - * buffer space. Without this, the block-and-restart behavior of - * libpq's higher levels leads to O(N^2) performance on long messages. - * - * Since we left-justified the data above, conn->inEnd gives the - * amount of data already read in the current message. We consider - * the message "long" once we have acquired 32k ... - */ - if (conn->inEnd > 32768 && - (conn->inBufSize - conn->inEnd) >= 8192) - { - someread = 1; - goto retry3; - } - return 1; - } - - if (someread) - return 1; /* got a zero read after successful tries */ - - /* - * A return value of 0 could mean just that no data is now available, or - * it could mean EOF --- that is, the server has closed the connection. - * Since we have the socket in nonblock mode, the only way to tell the - * difference is to see if select() is saying that the file is ready. - * Grumble. Fortunately, we don't expect this path to be taken much, - * since in normal practice we should not be trying to read data unless - * the file selected for reading already. - * - * In SSL mode it's even worse: SSL_read() could say WANT_READ and then - * data could arrive before we make the pqReadReady() test, but the second - * SSL_read() could still say WANT_READ because the data received was not - * a complete SSL record. So we must play dumb and assume there is more - * data, relying on the SSL layer to detect true EOF. - */ - -#ifdef USE_SSL - if (conn->ssl_in_use) - return 0; -#endif - - switch (pqReadReady(conn)) - { - case 0: - /* definitely no data available */ - return 0; - case 1: - /* ready for read */ - break; - default: - /* we override pqReadReady's message with something more useful */ - goto definitelyEOF; - } - - /* - * Still not sure that it's EOF, because some data could have just - * arrived. - */ -retry4: - nread = pqsecure_read(conn, conn->inBuffer + conn->inEnd, - conn->inBufSize - conn->inEnd); - if (nread < 0) - { - if (SOCK_ERRNO == EINTR) - goto retry4; - /* Some systems return EAGAIN/EWOULDBLOCK for no data */ -#ifdef EAGAIN - if (SOCK_ERRNO == EAGAIN) - return 0; -#endif -#if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN)) - if (SOCK_ERRNO == EWOULDBLOCK) - return 0; -#endif - /* We might get ECONNRESET here if using TCP and backend died */ -#ifdef ECONNRESET - if (SOCK_ERRNO == ECONNRESET) - goto definitelyFailed; -#endif - /* pqsecure_read set the error message for us */ - return -1; - } - if (nread > 0) - { - conn->inEnd += nread; - return 1; - } - - /* - * OK, we are getting a zero read even though select() says ready. This - * means the connection has been closed. Cope. - */ -definitelyEOF: - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext( - "server closed the connection unexpectedly\n" - "\tThis probably means the server terminated abnormally\n" - "\tbefore or while processing the request.\n")); - - /* Come here if lower-level code already set a suitable errorMessage */ -definitelyFailed: - /* Do *not* drop any already-read data; caller still wants it */ - pqDropConnection(conn, false); - conn->status = CONNECTION_BAD; /* No more connection to backend */ - return -1; -} - -/* - * pqSendSome: send data waiting in the output buffer. - * - * len is how much to try to send (typically equal to outCount, but may - * be less). - * - * Return 0 on success, -1 on failure and 1 when not all data could be sent - * because the socket would block and the connection is non-blocking. - */ -static int -pqSendSome(PGconn *conn, int len) -{ - char *ptr = conn->outBuffer; - int remaining = conn->outCount; - int result = 0; - - if (conn->sock == PGINVALID_SOCKET) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("connection not open\n")); - /* Discard queued data; no chance it'll ever be sent */ - conn->outCount = 0; - return -1; - } - - /* while there's still data to send */ - while (len > 0) - { - int sent; - -#ifndef WIN32 - sent = pqsecure_write(conn, ptr, len); -#else - - /* - * Windows can fail on large sends, per KB article Q201213. The - * failure-point appears to be different in different versions of - * Windows, but 64k should always be safe. - */ - sent = pqsecure_write(conn, ptr, Min(len, 65536)); -#endif - - if (sent < 0) - { - /* Anything except EAGAIN/EWOULDBLOCK/EINTR is trouble */ - switch (SOCK_ERRNO) - { -#ifdef EAGAIN - case EAGAIN: - break; -#endif -#if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN)) - case EWOULDBLOCK: - break; -#endif - case EINTR: - continue; - - default: - /* pqsecure_write set the error message for us */ - - /* - * We used to close the socket here, but that's a bad idea - * since there might be unread data waiting (typically, a - * NOTICE message from the backend telling us it's - * committing hara-kiri...). Leave the socket open until - * pqReadData finds no more data can be read. But abandon - * attempt to send data. - */ - conn->outCount = 0; - return -1; - } - } - else - { - ptr += sent; - len -= sent; - remaining -= sent; - } - - if (len > 0) - { - /* - * We didn't send it all, wait till we can send more. - * - * There are scenarios in which we can't send data because the - * communications channel is full, but we cannot expect the server - * to clear the channel eventually because it's blocked trying to - * send data to us. (This can happen when we are sending a large - * amount of COPY data, and the server has generated lots of - * NOTICE responses.) To avoid a deadlock situation, we must be - * prepared to accept and buffer incoming data before we try - * again. Furthermore, it is possible that such incoming data - * might not arrive until after we've gone to sleep. Therefore, - * we wait for either read ready or write ready. - * - * In non-blocking mode, we don't wait here directly, but return 1 - * to indicate that data is still pending. The caller should wait - * for both read and write ready conditions, and call - * PQconsumeInput() on read ready, but just in case it doesn't, we - * call pqReadData() ourselves before returning. That's not - * enough if the data has not arrived yet, but it's the best we - * can do, and works pretty well in practice. (The documentation - * used to say that you only need to wait for write-ready, so - * there are still plenty of applications like that out there.) - */ - if (pqReadData(conn) < 0) - { - result = -1; /* error message already set up */ - break; - } - - if (pqIsnonblocking(conn)) - { - result = 1; - break; - } - - if (pqWait(TRUE, TRUE, conn)) - { - result = -1; - break; - } - } - } - - /* shift the remaining contents of the buffer */ - if (remaining > 0) - memmove(conn->outBuffer, ptr, remaining); - conn->outCount = remaining; - - return result; -} - - -/* - * pqFlush: send any data waiting in the output buffer - * - * Return 0 on success, -1 on failure and 1 when not all data could be sent - * because the socket would block and the connection is non-blocking. - */ -int -pqFlush(PGconn *conn) -{ - if (conn->Pfdebug) - fflush(conn->Pfdebug); - - if (conn->outCount > 0) - return pqSendSome(conn, conn->outCount); - - return 0; -} - - -/* - * pqWait: wait until we can read or write the connection socket - * - * JAB: If SSL enabled and used and forRead, buffered bytes short-circuit the - * call to select(). - * - * We also stop waiting and return if the kernel flags an exception condition - * on the socket. The actual error condition will be detected and reported - * when the caller tries to read or write the socket. - */ -int -pqWait(int forRead, int forWrite, PGconn *conn) -{ - return pqWaitTimed(forRead, forWrite, conn, (time_t) -1); -} - -/* - * pqWaitTimed: wait, but not past finish_time. - * - * If finish_time is exceeded then we return failure (EOF). This is like - * the response for a kernel exception because we don't want the caller - * to try to read/write in that case. - * - * finish_time = ((time_t) -1) disables the wait limit. - */ -int -pqWaitTimed(int forRead, int forWrite, PGconn *conn, time_t finish_time) -{ - int result; - - result = pqSocketCheck(conn, forRead, forWrite, finish_time); - - if (result < 0) - return EOF; /* errorMessage is already set */ - - if (result == 0) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("timeout expired\n")); - return EOF; - } - - return 0; -} - -/* - * pqReadReady: is select() saying the file is ready to read? - * Returns -1 on failure, 0 if not ready, 1 if ready. - */ -int -pqReadReady(PGconn *conn) -{ - return pqSocketCheck(conn, 1, 0, (time_t) 0); -} - -/* - * pqWriteReady: is select() saying the file is ready to write? - * Returns -1 on failure, 0 if not ready, 1 if ready. - */ -int -pqWriteReady(PGconn *conn) -{ - return pqSocketCheck(conn, 0, 1, (time_t) 0); -} - -/* - * Checks a socket, using poll or select, for data to be read, written, - * or both. Returns >0 if one or more conditions are met, 0 if it timed - * out, -1 if an error occurred. - * - * If SSL is in use, the SSL buffer is checked prior to checking the socket - * for read data directly. - */ -static int -pqSocketCheck(PGconn *conn, int forRead, int forWrite, time_t end_time) -{ - int result; - - if (!conn) - return -1; - if (conn->sock == PGINVALID_SOCKET) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("invalid socket\n")); - return -1; - } - -#ifdef USE_SSL - /* Check for SSL library buffering read bytes */ - if (forRead && conn->ssl_in_use && pgtls_read_pending(conn) > 0) - { - /* short-circuit the select */ - return 1; - } -#endif - - /* We will retry as long as we get EINTR */ - do - result = pqSocketPoll(conn->sock, forRead, forWrite, end_time); - while (result < 0 && SOCK_ERRNO == EINTR); - - if (result < 0) - { - char sebuf[256]; - - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("select() failed: %s\n"), - SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf))); - } - - return result; -} - - -/* - * Check a file descriptor for read and/or write data, possibly waiting. - * If neither forRead nor forWrite are set, immediately return a timeout - * condition (without waiting). Return >0 if condition is met, 0 - * if a timeout occurred, -1 if an error or interrupt occurred. - * - * Timeout is infinite if end_time is -1. Timeout is immediate (no blocking) - * if end_time is 0 (or indeed, any time before now). - */ -static int -pqSocketPoll(int sock, int forRead, int forWrite, time_t end_time) -{ - /* We use poll(2) if available, otherwise select(2) */ -#ifdef HAVE_POLL - struct pollfd input_fd; - int timeout_ms; - - if (!forRead && !forWrite) - return 0; - - input_fd.fd = sock; - input_fd.events = POLLERR; - input_fd.revents = 0; - - if (forRead) - input_fd.events |= POLLIN; - if (forWrite) - input_fd.events |= POLLOUT; - - /* Compute appropriate timeout interval */ - if (end_time == ((time_t) -1)) - timeout_ms = -1; - else - { - time_t now = time(NULL); - - if (end_time > now) - timeout_ms = (end_time - now) * 1000; - else - timeout_ms = 0; - } - - return poll(&input_fd, 1, timeout_ms); -#else /* !HAVE_POLL */ - - fd_set input_mask; - fd_set output_mask; - fd_set except_mask; - struct timeval timeout; - struct timeval *ptr_timeout; - - if (!forRead && !forWrite) - return 0; - - FD_ZERO(&input_mask); - FD_ZERO(&output_mask); - FD_ZERO(&except_mask); - if (forRead) - FD_SET(sock, &input_mask); - - if (forWrite) - FD_SET(sock, &output_mask); - FD_SET(sock, &except_mask); - - /* Compute appropriate timeout interval */ - if (end_time == ((time_t) -1)) - ptr_timeout = NULL; - else - { - time_t now = time(NULL); - - if (end_time > now) - timeout.tv_sec = end_time - now; - else - timeout.tv_sec = 0; - timeout.tv_usec = 0; - ptr_timeout = &timeout; - } - - return select(sock + 1, &input_mask, &output_mask, - &except_mask, ptr_timeout); -#endif /* HAVE_POLL */ -} - - -/* - * A couple of "miscellaneous" multibyte related functions. They used - * to be in fe-print.c but that file is doomed. - */ - -/* - * returns the byte length of the character beginning at s, using the - * specified encoding. - */ -int -PQmblen(const char *s, int encoding) -{ - return pg_encoding_mblen(encoding, s); -} - -/* - * returns the display length of the character beginning at s, using the - * specified encoding. - */ -int -PQdsplen(const char *s, int encoding) -{ - return pg_encoding_dsplen(encoding, s); -} - -/* - * Get encoding id from environment variable PGCLIENTENCODING. - */ -int -PQenv2encoding(void) -{ - char *str; - int encoding = PG_SQL_ASCII; - - str = getenv("PGCLIENTENCODING"); - if (str && *str != '\0') - { - encoding = pg_char_to_encoding(str); - if (encoding < 0) - encoding = PG_SQL_ASCII; - } - return encoding; -} - - -#ifdef ENABLE_NLS - -static void -libpq_binddomain() -{ - static bool already_bound = false; - - if (!already_bound) - { - /* bindtextdomain() does not preserve errno */ -#ifdef WIN32 - int save_errno = GetLastError(); -#else - int save_errno = errno; -#endif - const char *ldir; - - already_bound = true; - /* No relocatable lookup here because the binary could be anywhere */ - ldir = getenv("PGLOCALEDIR"); - if (!ldir) - ldir = LOCALEDIR; - bindtextdomain(PG_TEXTDOMAIN("libpq"), ldir); -#ifdef WIN32 - SetLastError(save_errno); -#else - errno = save_errno; -#endif - } -} - -char * -libpq_gettext(const char *msgid) -{ - libpq_binddomain(); - return dgettext(PG_TEXTDOMAIN("libpq"), msgid); -} - -char * -libpq_ngettext(const char *msgid, const char *msgid_plural, unsigned long n) -{ - libpq_binddomain(); - return dngettext(PG_TEXTDOMAIN("libpq"), msgid, msgid_plural, n); -} - -#endif /* ENABLE_NLS */ diff --git a/libpq/fe-print.c b/libpq/fe-print.c deleted file mode 100644 index e596a51..0000000 --- a/libpq/fe-print.c +++ /dev/null @@ -1,761 +0,0 @@ -/*------------------------------------------------------------------------- - * - * fe-print.c - * functions for pretty-printing query results - * - * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * These functions were formerly part of fe-exec.c, but they - * didn't really belong there. - * - * IDENTIFICATION - * src/interfaces/libpq/fe-print.c - * - *------------------------------------------------------------------------- - */ -#include "postgres_fe.h" - -#include <signal.h> - -#ifdef WIN32 -#include "win32.h" -#else -#include <unistd.h> -#include <sys/ioctl.h> -#endif - -#ifdef HAVE_TERMIOS_H -#include <termios.h> -#else -#ifndef WIN32 -#include <sys/termios.h> -#endif -#endif - -#include "libpq-fe.h" -#include "libpq-int.h" - - -static void do_field(const PQprintOpt *po, const PGresult *res, - const int i, const int j, const int fs_len, - char **fields, - const int nFields, const char **fieldNames, - unsigned char *fieldNotNum, int *fieldMax, - const int fieldMaxLen, FILE *fout); -static char *do_header(FILE *fout, const PQprintOpt *po, const int nFields, - int *fieldMax, const char **fieldNames, unsigned char *fieldNotNum, - const int fs_len, const PGresult *res); -static void output_row(FILE *fout, const PQprintOpt *po, const int nFields, char **fields, - unsigned char *fieldNotNum, int *fieldMax, char *border, - const int row_index); -static void fill(int length, int max, char filler, FILE *fp); - -/* - * PQprint() - * - * Format results of a query for printing. - * - * PQprintOpt is a typedef (structure) that contains - * various flags and options. consult libpq-fe.h for - * details - * - * This function should probably be removed sometime since psql - * doesn't use it anymore. It is unclear to what extent this is used - * by external clients, however. - */ -void -PQprint(FILE *fout, const PGresult *res, const PQprintOpt *po) -{ - int nFields; - - nFields = PQnfields(res); - - if (nFields > 0) - { /* only print rows with at least 1 field. */ - int i, - j; - int nTups; - int *fieldMax = NULL; /* in case we don't use them */ - unsigned char *fieldNotNum = NULL; - char *border = NULL; - char **fields = NULL; - const char **fieldNames; - int fieldMaxLen = 0; - int numFieldName; - int fs_len = strlen(po->fieldSep); - int total_line_length = 0; - int usePipe = 0; - char *pagerenv; - -#if defined(ENABLE_THREAD_SAFETY) && !defined(WIN32) - sigset_t osigset; - bool sigpipe_masked = false; - bool sigpipe_pending; -#endif -#if !defined(ENABLE_THREAD_SAFETY) && !defined(WIN32) - pqsigfunc oldsigpipehandler = NULL; -#endif - -#ifdef TIOCGWINSZ - struct winsize screen_size; -#else - struct winsize - { - int ws_row; - int ws_col; - } screen_size; -#endif - - nTups = PQntuples(res); - if (!(fieldNames = (const char **) calloc(nFields, sizeof(char *)))) - { - fprintf(stderr, libpq_gettext("out of memory\n")); - abort(); - } - if (!(fieldNotNum = (unsigned char *) calloc(nFields, 1))) - { - fprintf(stderr, libpq_gettext("out of memory\n")); - abort(); - } - if (!(fieldMax = (int *) calloc(nFields, sizeof(int)))) - { - fprintf(stderr, libpq_gettext("out of memory\n")); - abort(); - } - for (numFieldName = 0; - po->fieldName && po->fieldName[numFieldName]; - numFieldName++) - ; - for (j = 0; j < nFields; j++) - { - int len; - const char *s = (j < numFieldName && po->fieldName[j][0]) ? - po->fieldName[j] : PQfname(res, j); - - fieldNames[j] = s; - len = s ? strlen(s) : 0; - fieldMax[j] = len; - len += fs_len; - if (len > fieldMaxLen) - fieldMaxLen = len; - total_line_length += len; - } - - total_line_length += nFields * strlen(po->fieldSep) + 1; - - if (fout == NULL) - fout = stdout; - if (po->pager && fout == stdout && isatty(fileno(stdin)) && - isatty(fileno(stdout))) - { - /* - * If we think there'll be more than one screen of output, try to - * pipe to the pager program. - */ -#ifdef TIOCGWINSZ - if (ioctl(fileno(stdout), TIOCGWINSZ, &screen_size) == -1 || - screen_size.ws_col == 0 || - screen_size.ws_row == 0) - { - screen_size.ws_row = 24; - screen_size.ws_col = 80; - } -#else - screen_size.ws_row = 24; - screen_size.ws_col = 80; -#endif - pagerenv = getenv("PAGER"); - /* if PAGER is unset, empty or all-white-space, don't use pager */ - if (pagerenv != NULL && - strspn(pagerenv, " \t\r\n") != strlen(pagerenv) && - !po->html3 && - ((po->expanded && - nTups * (nFields + 1) >= screen_size.ws_row) || - (!po->expanded && - nTups * (total_line_length / screen_size.ws_col + 1) * - (1 + (po->standard != 0)) >= screen_size.ws_row - - (po->header != 0) * - (total_line_length / screen_size.ws_col + 1) * 2 - - (po->header != 0) * 2 /* row count and newline */ - ))) - { - fout = popen(pagerenv, "w"); - if (fout) - { - usePipe = 1; -#ifndef WIN32 -#ifdef ENABLE_THREAD_SAFETY - if (pq_block_sigpipe(&osigset, &sigpipe_pending) == 0) - sigpipe_masked = true; -#else - oldsigpipehandler = pqsignal(SIGPIPE, SIG_IGN); -#endif /* ENABLE_THREAD_SAFETY */ -#endif /* WIN32 */ - } - else - fout = stdout; - } - } - - if (!po->expanded && (po->align || po->html3)) - { - if (!(fields = (char **) calloc(nFields * (nTups + 1), sizeof(char *)))) - { - fprintf(stderr, libpq_gettext("out of memory\n")); - abort(); - } - } - else if (po->header && !po->html3) - { - if (po->expanded) - { - if (po->align) - fprintf(fout, libpq_gettext("%-*s%s Value\n"), - fieldMaxLen - fs_len, libpq_gettext("Field"), po->fieldSep); - else - fprintf(fout, libpq_gettext("%s%sValue\n"), libpq_gettext("Field"), po->fieldSep); - } - else - { - int len = 0; - - for (j = 0; j < nFields; j++) - { - const char *s = fieldNames[j]; - - fputs(s, fout); - len += strlen(s) + fs_len; - if ((j + 1) < nFields) - fputs(po->fieldSep, fout); - } - fputc('\n', fout); - for (len -= fs_len; len--; fputc('-', fout)); - fputc('\n', fout); - } - } - if (po->expanded && po->html3) - { - if (po->caption) - fprintf(fout, "<center><h2>%s</h2></center>\n", po->caption); - else - fprintf(fout, - "<center><h2>" - "Query retrieved %d rows * %d fields" - "</h2></center>\n", - nTups, nFields); - } - for (i = 0; i < nTups; i++) - { - if (po->expanded) - { - if (po->html3) - fprintf(fout, - "<table %s><caption align=\"top\">%d</caption>\n", - po->tableOpt ? po->tableOpt : "", i); - else - fprintf(fout, libpq_gettext("-- RECORD %d --\n"), i); - } - for (j = 0; j < nFields; j++) - do_field(po, res, i, j, fs_len, fields, nFields, - fieldNames, fieldNotNum, - fieldMax, fieldMaxLen, fout); - if (po->html3 && po->expanded) - fputs("</table>\n", fout); - } - if (!po->expanded && (po->align || po->html3)) - { - if (po->html3) - { - if (po->header) - { - if (po->caption) - fprintf(fout, - "<table %s><caption align=\"top\">%s</caption>\n", - po->tableOpt ? po->tableOpt : "", - po->caption); - else - fprintf(fout, - "<table %s><caption align=\"top\">" - "Retrieved %d rows * %d fields" - "</caption>\n", - po->tableOpt ? po->tableOpt : "", nTups, nFields); - } - else - fprintf(fout, "<table %s>", po->tableOpt ? po->tableOpt : ""); - } - if (po->header) - border = do_header(fout, po, nFields, fieldMax, fieldNames, - fieldNotNum, fs_len, res); - for (i = 0; i < nTups; i++) - output_row(fout, po, nFields, fields, - fieldNotNum, fieldMax, border, i); - free(fields); - if (border) - free(border); - } - if (po->header && !po->html3) - fprintf(fout, "(%d row%s)\n\n", PQntuples(res), - (PQntuples(res) == 1) ? "" : "s"); - free(fieldMax); - free(fieldNotNum); - free((void *) fieldNames); - if (usePipe) - { -#ifdef WIN32 - _pclose(fout); -#else - pclose(fout); - -#ifdef ENABLE_THREAD_SAFETY - /* we can't easily verify if EPIPE occurred, so say it did */ - if (sigpipe_masked) - pq_reset_sigpipe(&osigset, sigpipe_pending, true); -#else - pqsignal(SIGPIPE, oldsigpipehandler); -#endif /* ENABLE_THREAD_SAFETY */ -#endif /* WIN32 */ - } - if (po->html3 && !po->expanded) - fputs("</table>\n", fout); - } -} - - -static void -do_field(const PQprintOpt *po, const PGresult *res, - const int i, const int j, const int fs_len, - char **fields, - const int nFields, char const ** fieldNames, - unsigned char *fieldNotNum, int *fieldMax, - const int fieldMaxLen, FILE *fout) -{ - const char *pval, - *p; - int plen; - bool skipit; - - plen = PQgetlength(res, i, j); - pval = PQgetvalue(res, i, j); - - if (plen < 1 || !pval || !*pval) - { - if (po->align || po->expanded) - skipit = true; - else - { - skipit = false; - goto efield; - } - } - else - skipit = false; - - if (!skipit) - { - if (po->align && !fieldNotNum[j]) - { - /* Detect whether field contains non-numeric data */ - char ch = '0'; - - for (p = pval; *p; p += PQmblen(p, res->client_encoding)) - { - ch = *p; - if (!((ch >= '0' && ch <= '9') || - ch == '.' || - ch == 'E' || - ch == 'e' || - ch == ' ' || - ch == '-')) - { - fieldNotNum[j] = 1; - break; - } - } - - /* - * Above loop will believe E in first column is numeric; also, we - * insist on a digit in the last column for a numeric. This test - * is still not bulletproof but it handles most cases. - */ - if (*pval == 'E' || *pval == 'e' || - !(ch >= '0' && ch <= '9')) - fieldNotNum[j] = 1; - } - - if (!po->expanded && (po->align || po->html3)) - { - if (plen > fieldMax[j]) - fieldMax[j] = plen; - if (!(fields[i * nFields + j] = (char *) malloc(plen + 1))) - { - fprintf(stderr, libpq_gettext("out of memory\n")); - abort(); - } - strcpy(fields[i * nFields + j], pval); - } - else - { - if (po->expanded) - { - if (po->html3) - fprintf(fout, - "<tr><td align=\"left\"><b>%s</b></td>" - "<td align=\"%s\">%s</td></tr>\n", - fieldNames[j], - fieldNotNum[j] ? "left" : "right", - pval); - else - { - if (po->align) - fprintf(fout, - "%-*s%s %s\n", - fieldMaxLen - fs_len, fieldNames[j], - po->fieldSep, - pval); - else - fprintf(fout, - "%s%s%s\n", - fieldNames[j], po->fieldSep, pval); - } - } - else - { - if (!po->html3) - { - fputs(pval, fout); - efield: - if ((j + 1) < nFields) - fputs(po->fieldSep, fout); - else - fputc('\n', fout); - } - } - } - } -} - - -static char * -do_header(FILE *fout, const PQprintOpt *po, const int nFields, int *fieldMax, - const char **fieldNames, unsigned char *fieldNotNum, - const int fs_len, const PGresult *res) -{ - int j; /* for loop index */ - char *border = NULL; - - if (po->html3) - fputs("<tr>", fout); - else - { - int tot = 0; - int n = 0; - char *p = NULL; - - for (; n < nFields; n++) - tot += fieldMax[n] + fs_len + (po->standard ? 2 : 0); - if (po->standard) - tot += fs_len * 2 + 2; - border = malloc(tot + 1); - if (!border) - { - fprintf(stderr, libpq_gettext("out of memory\n")); - abort(); - } - p = border; - if (po->standard) - { - char *fs = po->fieldSep; - - while (*fs++) - *p++ = '+'; - } - for (j = 0; j < nFields; j++) - { - int len; - - for (len = fieldMax[j] + (po->standard ? 2 : 0); len--; *p++ = '-'); - if (po->standard || (j + 1) < nFields) - { - char *fs = po->fieldSep; - - while (*fs++) - *p++ = '+'; - } - } - *p = '\0'; - if (po->standard) - fprintf(fout, "%s\n", border); - } - if (po->standard) - fputs(po->fieldSep, fout); - for (j = 0; j < nFields; j++) - { - const char *s = PQfname(res, j); - - if (po->html3) - { - fprintf(fout, "<th align=\"%s\">%s</th>", - fieldNotNum[j] ? "left" : "right", fieldNames[j]); - } - else - { - int n = strlen(s); - - if (n > fieldMax[j]) - fieldMax[j] = n; - if (po->standard) - fprintf(fout, - fieldNotNum[j] ? " %-*s " : " %*s ", - fieldMax[j], s); - else - fprintf(fout, fieldNotNum[j] ? "%-*s" : "%*s", fieldMax[j], s); - if (po->standard || (j + 1) < nFields) - fputs(po->fieldSep, fout); - } - } - if (po->html3) - fputs("</tr>\n", fout); - else - fprintf(fout, "\n%s\n", border); - return border; -} - - -static void -output_row(FILE *fout, const PQprintOpt *po, const int nFields, char **fields, - unsigned char *fieldNotNum, int *fieldMax, char *border, - const int row_index) -{ - int field_index; /* for loop index */ - - if (po->html3) - fputs("<tr>", fout); - else if (po->standard) - fputs(po->fieldSep, fout); - for (field_index = 0; field_index < nFields; field_index++) - { - char *p = fields[row_index * nFields + field_index]; - - if (po->html3) - fprintf(fout, "<td align=\"%s\">%s</td>", - fieldNotNum[field_index] ? "left" : "right", p ? p : ""); - else - { - fprintf(fout, - fieldNotNum[field_index] ? - (po->standard ? " %-*s " : "%-*s") : - (po->standard ? " %*s " : "%*s"), - fieldMax[field_index], - p ? p : ""); - if (po->standard || field_index + 1 < nFields) - fputs(po->fieldSep, fout); - } - if (p) - free(p); - } - if (po->html3) - fputs("</tr>", fout); - else if (po->standard) - fprintf(fout, "\n%s", border); - fputc('\n', fout); -} - - - -/* - * really old printing routines - */ - -void -PQdisplayTuples(const PGresult *res, - FILE *fp, /* where to send the output */ - int fillAlign, /* pad the fields with spaces */ - const char *fieldSep, /* field separator */ - int printHeader, /* display headers? */ - int quiet -) -{ -#define DEFAULT_FIELD_SEP " " - - int i, - j; - int nFields; - int nTuples; - int *fLength = NULL; - - if (fieldSep == NULL) - fieldSep = DEFAULT_FIELD_SEP; - - /* Get some useful info about the results */ - nFields = PQnfields(res); - nTuples = PQntuples(res); - - if (fp == NULL) - fp = stdout; - - /* Figure the field lengths to align to */ - /* will be somewhat time consuming for very large results */ - if (fillAlign) - { - fLength = (int *) malloc(nFields * sizeof(int)); - if (!fLength) - { - fprintf(stderr, libpq_gettext("out of memory\n")); - abort(); - } - - for (j = 0; j < nFields; j++) - { - fLength[j] = strlen(PQfname(res, j)); - for (i = 0; i < nTuples; i++) - { - int flen = PQgetlength(res, i, j); - - if (flen > fLength[j]) - fLength[j] = flen; - } - } - } - - if (printHeader) - { - /* first, print out the attribute names */ - for (i = 0; i < nFields; i++) - { - fputs(PQfname(res, i), fp); - if (fillAlign) - fill(strlen(PQfname(res, i)), fLength[i], ' ', fp); - fputs(fieldSep, fp); - } - fprintf(fp, "\n"); - - /* Underline the attribute names */ - for (i = 0; i < nFields; i++) - { - if (fillAlign) - fill(0, fLength[i], '-', fp); - fputs(fieldSep, fp); - } - fprintf(fp, "\n"); - } - - /* next, print out the instances */ - for (i = 0; i < nTuples; i++) - { - for (j = 0; j < nFields; j++) - { - fprintf(fp, "%s", PQgetvalue(res, i, j)); - if (fillAlign) - fill(strlen(PQgetvalue(res, i, j)), fLength[j], ' ', fp); - fputs(fieldSep, fp); - } - fprintf(fp, "\n"); - } - - if (!quiet) - fprintf(fp, "\nQuery returned %d row%s.\n", PQntuples(res), - (PQntuples(res) == 1) ? "" : "s"); - - fflush(fp); - - if (fLength) - free(fLength); -} - - - -void -PQprintTuples(const PGresult *res, - FILE *fout, /* output stream */ - int PrintAttNames, /* print attribute names or not */ - int TerseOutput, /* delimiter bars or not? */ - int colWidth /* width of column, if 0, use variable width */ -) -{ - int nFields; - int nTups; - int i, - j; - char formatString[80]; - char *tborder = NULL; - - nFields = PQnfields(res); - nTups = PQntuples(res); - - if (colWidth > 0) - sprintf(formatString, "%%s %%-%ds", colWidth); - else - sprintf(formatString, "%%s %%s"); - - if (nFields > 0) - { /* only print rows with at least 1 field. */ - - if (!TerseOutput) - { - int width; - - width = nFields * 14; - tborder = (char *) malloc(width + 1); - if (!tborder) - { - fprintf(stderr, libpq_gettext("out of memory\n")); - abort(); - } - for (i = 0; i < width; i++) - tborder[i] = '-'; - tborder[width] = '\0'; - fprintf(fout, "%s\n", tborder); - } - - for (i = 0; i < nFields; i++) - { - if (PrintAttNames) - { - fprintf(fout, formatString, - TerseOutput ? "" : "|", - PQfname(res, i)); - } - } - - if (PrintAttNames) - { - if (TerseOutput) - fprintf(fout, "\n"); - else - fprintf(fout, "|\n%s\n", tborder); - } - - for (i = 0; i < nTups; i++) - { - for (j = 0; j < nFields; j++) - { - const char *pval = PQgetvalue(res, i, j); - - fprintf(fout, formatString, - TerseOutput ? "" : "|", - pval ? pval : ""); - } - if (TerseOutput) - fprintf(fout, "\n"); - else - fprintf(fout, "|\n%s\n", tborder); - } - } - - if (tborder) - free(tborder); -} - - -/* simply send out max-length number of filler characters to fp */ - -static void -fill(int length, int max, char filler, FILE *fp) -{ - int count; - - count = max - length; - while (count-- >= 0) - putc(filler, fp); -} diff --git a/libpq/fe-protocol2.c b/libpq/fe-protocol2.c deleted file mode 100644 index f1b90f3..0000000 --- a/libpq/fe-protocol2.c +++ /dev/null @@ -1,1623 +0,0 @@ -/*------------------------------------------------------------------------- - * - * fe-protocol2.c - * functions that are specific to frontend/backend protocol version 2 - * - * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * - * IDENTIFICATION - * src/interfaces/libpq/fe-protocol2.c - * - *------------------------------------------------------------------------- - */ -#include "postgres_fe.h" - -#include <ctype.h> -#include <fcntl.h> - -#include "libpq-fe.h" -#include "libpq-int.h" - - -#ifdef WIN32 -#include "win32.h" -#else -#include <unistd.h> -#include <netinet/in.h> -#ifdef HAVE_NETINET_TCP_H -#include <netinet/tcp.h> -#endif -#include <arpa/inet.h> -#endif - - -static int getRowDescriptions(PGconn *conn); -static int getAnotherTuple(PGconn *conn, bool binary); -static int pqGetErrorNotice2(PGconn *conn, bool isError); -static void checkXactStatus(PGconn *conn, const char *cmdTag); -static int getNotify(PGconn *conn); - - -/* - * pqSetenvPoll - * - * Polls the process of passing the values of a standard set of environment - * variables to the backend. - */ -PostgresPollingStatusType -pqSetenvPoll(PGconn *conn) -{ - PGresult *res; - - if (conn == NULL || conn->status == CONNECTION_BAD) - return PGRES_POLLING_FAILED; - - /* Check whether there are any data for us */ - switch (conn->setenv_state) - { - /* These are reading states */ - case SETENV_STATE_CLIENT_ENCODING_WAIT: - case SETENV_STATE_OPTION_WAIT: - case SETENV_STATE_QUERY1_WAIT: - case SETENV_STATE_QUERY2_WAIT: - { - /* Load waiting data */ - int n = pqReadData(conn); - - if (n < 0) - goto error_return; - if (n == 0) - return PGRES_POLLING_READING; - - break; - } - - /* These are writing states, so we just proceed. */ - case SETENV_STATE_CLIENT_ENCODING_SEND: - case SETENV_STATE_OPTION_SEND: - case SETENV_STATE_QUERY1_SEND: - case SETENV_STATE_QUERY2_SEND: - break; - - /* Should we raise an error if called when not active? */ - case SETENV_STATE_IDLE: - return PGRES_POLLING_OK; - - default: - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext( - "invalid setenv state %c, " - "probably indicative of memory corruption\n" - ), - conn->setenv_state); - goto error_return; - } - - /* We will loop here until there is nothing left to do in this call. */ - for (;;) - { - switch (conn->setenv_state) - { - /* - * The _CLIENT_ENCODING_SEND code is slightly different from - * _OPTION_SEND below (e.g., no getenv() call), which is why a - * different state is used. - */ - case SETENV_STATE_CLIENT_ENCODING_SEND: - { - char setQuery[100]; /* note length limit in - * sprintf below */ - const char *val = conn->client_encoding_initial; - - if (val) - { - if (pg_strcasecmp(val, "default") == 0) - sprintf(setQuery, "SET client_encoding = DEFAULT"); - else - sprintf(setQuery, "SET client_encoding = '%.60s'", - val); -#ifdef CONNECTDEBUG - fprintf(stderr, - "Sending client_encoding with %s\n", - setQuery); -#endif - if (!PQsendQuery(conn, setQuery)) - goto error_return; - - conn->setenv_state = SETENV_STATE_CLIENT_ENCODING_WAIT; - } - else - conn->setenv_state = SETENV_STATE_OPTION_SEND; - break; - } - - case SETENV_STATE_OPTION_SEND: - { - /* - * Send SET commands for stuff directed by Environment - * Options. Note: we assume that SET commands won't start - * transaction blocks, even in a 7.3 server with - * autocommit off. - */ - char setQuery[100]; /* note length limit in - * sprintf below */ - - if (conn->next_eo->envName) - { - const char *val; - - if ((val = getenv(conn->next_eo->envName))) - { - if (pg_strcasecmp(val, "default") == 0) - sprintf(setQuery, "SET %s = DEFAULT", - conn->next_eo->pgName); - else - sprintf(setQuery, "SET %s = '%.60s'", - conn->next_eo->pgName, val); -#ifdef CONNECTDEBUG - fprintf(stderr, - "Use environment variable %s to send %s\n", - conn->next_eo->envName, setQuery); -#endif - if (!PQsendQuery(conn, setQuery)) - goto error_return; - - conn->setenv_state = SETENV_STATE_OPTION_WAIT; - } - else - conn->next_eo++; - } - else - { - /* No more options to send, so move on to querying */ - conn->setenv_state = SETENV_STATE_QUERY1_SEND; - } - break; - } - - case SETENV_STATE_CLIENT_ENCODING_WAIT: - { - if (PQisBusy(conn)) - return PGRES_POLLING_READING; - - res = PQgetResult(conn); - - if (res) - { - if (PQresultStatus(res) != PGRES_COMMAND_OK) - { - PQclear(res); - goto error_return; - } - PQclear(res); - /* Keep reading until PQgetResult returns NULL */ - } - else - { - /* Query finished, so send the next option */ - conn->setenv_state = SETENV_STATE_OPTION_SEND; - } - break; - } - - case SETENV_STATE_OPTION_WAIT: - { - if (PQisBusy(conn)) - return PGRES_POLLING_READING; - - res = PQgetResult(conn); - - if (res) - { - if (PQresultStatus(res) != PGRES_COMMAND_OK) - { - PQclear(res); - goto error_return; - } - PQclear(res); - /* Keep reading until PQgetResult returns NULL */ - } - else - { - /* Query finished, so send the next option */ - conn->next_eo++; - conn->setenv_state = SETENV_STATE_OPTION_SEND; - } - break; - } - - case SETENV_STATE_QUERY1_SEND: - { - /* - * Issue query to get information we need. Here we must - * use begin/commit in case autocommit is off by default - * in a 7.3 server. - * - * Note: version() exists in all protocol-2.0-supporting - * backends. In 7.3 it would be safer to write - * pg_catalog.version(), but we can't do that without - * causing problems on older versions. - */ - if (!PQsendQuery(conn, "begin; select version(); end")) - goto error_return; - - conn->setenv_state = SETENV_STATE_QUERY1_WAIT; - return PGRES_POLLING_READING; - } - - case SETENV_STATE_QUERY1_WAIT: - { - if (PQisBusy(conn)) - return PGRES_POLLING_READING; - - res = PQgetResult(conn); - - if (res) - { - char *val; - - if (PQresultStatus(res) == PGRES_COMMAND_OK) - { - /* ignore begin/commit command results */ - PQclear(res); - continue; - } - - if (PQresultStatus(res) != PGRES_TUPLES_OK || - PQntuples(res) != 1) - { - PQclear(res); - goto error_return; - } - - /* - * Extract server version and save as if - * ParameterStatus - */ - val = PQgetvalue(res, 0, 0); - if (val && strncmp(val, "PostgreSQL ", 11) == 0) - { - char *ptr; - - /* strip off PostgreSQL part */ - val += 11; - - /* - * strip off platform part (scribbles on result, - * naughty naughty) - */ - ptr = strchr(val, ' '); - if (ptr) - *ptr = '\0'; - - pqSaveParameterStatus(conn, "server_version", - val); - } - - PQclear(res); - /* Keep reading until PQgetResult returns NULL */ - } - else - { - /* Query finished, move to next */ - conn->setenv_state = SETENV_STATE_QUERY2_SEND; - } - break; - } - - case SETENV_STATE_QUERY2_SEND: - { - const char *query; - - /* - * pg_client_encoding does not exist in pre-7.2 servers. - * So we need to be prepared for an error here. Do *not* - * start a transaction block, except in 7.3 servers where - * we need to prevent autocommit-off from starting a - * transaction anyway. - */ - if (conn->sversion >= 70300 && - conn->sversion < 70400) - query = "begin; select pg_catalog.pg_client_encoding(); end"; - else - query = "select pg_client_encoding()"; - if (!PQsendQuery(conn, query)) - goto error_return; - - conn->setenv_state = SETENV_STATE_QUERY2_WAIT; - return PGRES_POLLING_READING; - } - - case SETENV_STATE_QUERY2_WAIT: - { - if (PQisBusy(conn)) - return PGRES_POLLING_READING; - - res = PQgetResult(conn); - - if (res) - { - const char *val; - - if (PQresultStatus(res) == PGRES_COMMAND_OK) - { - /* ignore begin/commit command results */ - PQclear(res); - continue; - } - - if (PQresultStatus(res) == PGRES_TUPLES_OK && - PQntuples(res) == 1) - { - /* Extract client encoding and save it */ - val = PQgetvalue(res, 0, 0); - if (val && *val) /* null should not happen, but */ - pqSaveParameterStatus(conn, "client_encoding", - val); - } - else - { - /* - * Error: presumably function not available, so - * use PGCLIENTENCODING or SQL_ASCII as the - * fallback. - */ - val = getenv("PGCLIENTENCODING"); - if (val && *val) - pqSaveParameterStatus(conn, "client_encoding", - val); - else - pqSaveParameterStatus(conn, "client_encoding", - "SQL_ASCII"); - } - - PQclear(res); - /* Keep reading until PQgetResult returns NULL */ - } - else - { - /* Query finished, so we're done */ - conn->setenv_state = SETENV_STATE_IDLE; - return PGRES_POLLING_OK; - } - break; - } - - default: - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("invalid state %c, " - "probably indicative of memory corruption\n"), - conn->setenv_state); - goto error_return; - } - } - - /* Unreachable */ - -error_return: - conn->setenv_state = SETENV_STATE_IDLE; - return PGRES_POLLING_FAILED; -} - - -/* - * parseInput: if appropriate, parse input data from backend - * until input is exhausted or a stopping state is reached. - * Note that this function will NOT attempt to read more data from the backend. - */ -void -pqParseInput2(PGconn *conn) -{ - char id; - - /* - * Loop to parse successive complete messages available in the buffer. - */ - for (;;) - { - /* - * Quit if in COPY_OUT state: we expect raw data from the server until - * PQendcopy is called. Don't try to parse it according to the normal - * protocol. (This is bogus. The data lines ought to be part of the - * protocol and have identifying leading characters.) - */ - if (conn->asyncStatus == PGASYNC_COPY_OUT) - return; - - /* - * OK to try to read a message type code. - */ - conn->inCursor = conn->inStart; - if (pqGetc(&id, conn)) - return; - - /* - * NOTIFY and NOTICE messages can happen in any state besides COPY - * OUT; always process them right away. - * - * Most other messages should only be processed while in BUSY state. - * (In particular, in READY state we hold off further parsing until - * the application collects the current PGresult.) - * - * However, if the state is IDLE then we got trouble; we need to deal - * with the unexpected message somehow. - */ - if (id == 'A') - { - if (getNotify(conn)) - return; - } - else if (id == 'N') - { - if (pqGetErrorNotice2(conn, false)) - return; - } - else if (conn->asyncStatus != PGASYNC_BUSY) - { - /* If not IDLE state, just wait ... */ - if (conn->asyncStatus != PGASYNC_IDLE) - return; - - /* - * Unexpected message in IDLE state; need to recover somehow. - * ERROR messages are displayed using the notice processor; - * anything else is just dropped on the floor after displaying a - * suitable warning notice. (An ERROR is very possibly the - * backend telling us why it is about to close the connection, so - * we don't want to just discard it...) - */ - if (id == 'E') - { - if (pqGetErrorNotice2(conn, false /* treat as notice */ )) - return; - } - else - { - pqInternalNotice(&conn->noticeHooks, - "message type 0x%02x arrived from server while idle", - id); - /* Discard the unexpected message; good idea?? */ - conn->inStart = conn->inEnd; - break; - } - } - else - { - /* - * In BUSY state, we can process everything. - */ - switch (id) - { - case 'C': /* command complete */ - if (pqGets(&conn->workBuffer, conn)) - return; - if (conn->result == NULL) - { - conn->result = PQmakeEmptyPGresult(conn, - PGRES_COMMAND_OK); - if (!conn->result) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("out of memory")); - pqSaveErrorResult(conn); - } - } - if (conn->result) - { - strlcpy(conn->result->cmdStatus, conn->workBuffer.data, - CMDSTATUS_LEN); - } - checkXactStatus(conn, conn->workBuffer.data); - conn->asyncStatus = PGASYNC_READY; - break; - case 'E': /* error return */ - if (pqGetErrorNotice2(conn, true)) - return; - conn->asyncStatus = PGASYNC_READY; - break; - case 'Z': /* backend is ready for new query */ - conn->asyncStatus = PGASYNC_IDLE; - break; - case 'I': /* empty query */ - /* read and throw away the closing '\0' */ - if (pqGetc(&id, conn)) - return; - if (id != '\0') - pqInternalNotice(&conn->noticeHooks, - "unexpected character %c following empty query response (\"I\" message)", - id); - if (conn->result == NULL) - { - conn->result = PQmakeEmptyPGresult(conn, - PGRES_EMPTY_QUERY); - if (!conn->result) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("out of memory")); - pqSaveErrorResult(conn); - } - } - conn->asyncStatus = PGASYNC_READY; - break; - case 'K': /* secret key data from the backend */ - - /* - * This is expected only during backend startup, but it's - * just as easy to handle it as part of the main loop. - * Save the data and continue processing. - */ - if (pqGetInt(&(conn->be_pid), 4, conn)) - return; - if (pqGetInt(&(conn->be_key), 4, conn)) - return; - break; - case 'P': /* synchronous (normal) portal */ - if (pqGets(&conn->workBuffer, conn)) - return; - /* We pretty much ignore this message type... */ - break; - case 'T': /* row descriptions (start of query results) */ - if (conn->result == NULL) - { - /* First 'T' in a query sequence */ - if (getRowDescriptions(conn)) - return; - /* getRowDescriptions() moves inStart itself */ - continue; - } - else - { - /* - * A new 'T' message is treated as the start of - * another PGresult. (It is not clear that this is - * really possible with the current backend.) We stop - * parsing until the application accepts the current - * result. - */ - conn->asyncStatus = PGASYNC_READY; - return; - } - break; - case 'D': /* ASCII data tuple */ - if (conn->result != NULL) - { - /* Read another tuple of a normal query response */ - if (getAnotherTuple(conn, FALSE)) - return; - /* getAnotherTuple() moves inStart itself */ - continue; - } - else - { - pqInternalNotice(&conn->noticeHooks, - "server sent data (\"D\" message) without prior row description (\"T\" message)"); - /* Discard the unexpected message; good idea?? */ - conn->inStart = conn->inEnd; - return; - } - break; - case 'B': /* Binary data tuple */ - if (conn->result != NULL) - { - /* Read another tuple of a normal query response */ - if (getAnotherTuple(conn, TRUE)) - return; - /* getAnotherTuple() moves inStart itself */ - continue; - } - else - { - pqInternalNotice(&conn->noticeHooks, - "server sent binary data (\"B\" message) without prior row description (\"T\" message)"); - /* Discard the unexpected message; good idea?? */ - conn->inStart = conn->inEnd; - return; - } - break; - case 'G': /* Start Copy In */ - conn->asyncStatus = PGASYNC_COPY_IN; - break; - case 'H': /* Start Copy Out */ - conn->asyncStatus = PGASYNC_COPY_OUT; - break; - - /* - * Don't need to process CopyBothResponse here because it - * never arrives from the server during protocol 2.0. - */ - default: - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext( - "unexpected response from server; first received character was \"%c\"\n"), - id); - /* build an error result holding the error message */ - pqSaveErrorResult(conn); - /* Discard the unexpected message; good idea?? */ - conn->inStart = conn->inEnd; - conn->asyncStatus = PGASYNC_READY; - return; - } /* switch on protocol character */ - } - /* Successfully consumed this message */ - conn->inStart = conn->inCursor; - } -} - -/* - * parseInput subroutine to read a 'T' (row descriptions) message. - * We build a PGresult structure containing the attribute data. - * Returns: 0 if completed message, EOF if error or not enough data - * received yet. - * - * Note that if we run out of data, we have to suspend and reprocess - * the message after more data is received. Otherwise, conn->inStart - * must get advanced past the processed data. - */ -static int -getRowDescriptions(PGconn *conn) -{ - PGresult *result; - int nfields; - const char *errmsg; - int i; - - result = PQmakeEmptyPGresult(conn, PGRES_TUPLES_OK); - if (!result) - { - errmsg = NULL; /* means "out of memory", see below */ - goto advance_and_error; - } - - /* parseInput already read the 'T' label. */ - /* the next two bytes are the number of fields */ - if (pqGetInt(&(result->numAttributes), 2, conn)) - goto EOFexit; - nfields = result->numAttributes; - - /* allocate space for the attribute descriptors */ - if (nfields > 0) - { - result->attDescs = (PGresAttDesc *) - pqResultAlloc(result, nfields * sizeof(PGresAttDesc), TRUE); - if (!result->attDescs) - { - errmsg = NULL; /* means "out of memory", see below */ - goto advance_and_error; - } - MemSet(result->attDescs, 0, nfields * sizeof(PGresAttDesc)); - } - - /* get type info */ - for (i = 0; i < nfields; i++) - { - int typid; - int typlen; - int atttypmod; - - if (pqGets(&conn->workBuffer, conn) || - pqGetInt(&typid, 4, conn) || - pqGetInt(&typlen, 2, conn) || - pqGetInt(&atttypmod, 4, conn)) - goto EOFexit; - - /* - * Since pqGetInt treats 2-byte integers as unsigned, we need to - * coerce the result to signed form. - */ - typlen = (int) ((int16) typlen); - - result->attDescs[i].name = pqResultStrdup(result, - conn->workBuffer.data); - if (!result->attDescs[i].name) - { - errmsg = NULL; /* means "out of memory", see below */ - goto advance_and_error; - } - result->attDescs[i].tableid = 0; - result->attDescs[i].columnid = 0; - result->attDescs[i].format = 0; - result->attDescs[i].typid = typid; - result->attDescs[i].typlen = typlen; - result->attDescs[i].atttypmod = atttypmod; - } - - /* Success! */ - conn->result = result; - - /* Advance inStart to show that the "T" message has been processed. */ - conn->inStart = conn->inCursor; - - /* - * We could perform additional setup for the new result set here, but for - * now there's nothing else to do. - */ - - /* And we're done. */ - return 0; - -advance_and_error: - - /* - * Discard the failed message. Unfortunately we don't know for sure where - * the end is, so just throw away everything in the input buffer. This is - * not very desirable but it's the best we can do in protocol v2. - */ - conn->inStart = conn->inEnd; - - /* - * Replace partially constructed result with an error result. First - * discard the old result to try to win back some memory. - */ - pqClearAsyncResult(conn); - - /* - * If preceding code didn't provide an error message, assume "out of - * memory" was meant. The advantage of having this special case is that - * freeing the old result first greatly improves the odds that gettext() - * will succeed in providing a translation. - */ - if (!errmsg) - errmsg = libpq_gettext("out of memory for query result"); - - printfPQExpBuffer(&conn->errorMessage, "%s\n", errmsg); - - /* - * XXX: if PQmakeEmptyPGresult() fails, there's probably not much we can - * do to recover... - */ - conn->result = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR); - conn->asyncStatus = PGASYNC_READY; - -EOFexit: - if (result && result != conn->result) - PQclear(result); - return EOF; -} - -/* - * parseInput subroutine to read a 'B' or 'D' (row data) message. - * We fill rowbuf with column pointers and then call the row processor. - * Returns: 0 if completed message, EOF if error or not enough data - * received yet. - * - * Note that if we run out of data, we have to suspend and reprocess - * the message after more data is received. Otherwise, conn->inStart - * must get advanced past the processed data. - */ -static int -getAnotherTuple(PGconn *conn, bool binary) -{ - PGresult *result = conn->result; - int nfields = result->numAttributes; - const char *errmsg; - PGdataValue *rowbuf; - - /* the backend sends us a bitmap of which attributes are null */ - char std_bitmap[64]; /* used unless it doesn't fit */ - char *bitmap = std_bitmap; - int i; - size_t nbytes; /* the number of bytes in bitmap */ - char bmap; /* One byte of the bitmap */ - int bitmap_index; /* Its index */ - int bitcnt; /* number of bits examined in current byte */ - int vlen; /* length of the current field value */ - - /* Resize row buffer if needed */ - rowbuf = conn->rowBuf; - if (nfields > conn->rowBufLen) - { - rowbuf = (PGdataValue *) realloc(rowbuf, - nfields * sizeof(PGdataValue)); - if (!rowbuf) - { - errmsg = NULL; /* means "out of memory", see below */ - goto advance_and_error; - } - conn->rowBuf = rowbuf; - conn->rowBufLen = nfields; - } - - /* Save format specifier */ - result->binary = binary; - - /* - * If it's binary, fix the column format indicators. We assume the - * backend will consistently send either B or D, not a mix. - */ - if (binary) - { - for (i = 0; i < nfields; i++) - result->attDescs[i].format = 1; - } - - /* Get the null-value bitmap */ - nbytes = (nfields + BITS_PER_BYTE - 1) / BITS_PER_BYTE; - /* malloc() only for unusually large field counts... */ - if (nbytes > sizeof(std_bitmap)) - { - bitmap = (char *) malloc(nbytes); - if (!bitmap) - { - errmsg = NULL; /* means "out of memory", see below */ - goto advance_and_error; - } - } - - if (pqGetnchar(bitmap, nbytes, conn)) - goto EOFexit; - - /* Scan the fields */ - bitmap_index = 0; - bmap = bitmap[bitmap_index]; - bitcnt = 0; - - for (i = 0; i < nfields; i++) - { - /* get the value length */ - if (!(bmap & 0200)) - vlen = NULL_LEN; - else if (pqGetInt(&vlen, 4, conn)) - goto EOFexit; - else - { - if (!binary) - vlen = vlen - 4; - if (vlen < 0) - vlen = 0; - } - rowbuf[i].len = vlen; - - /* - * rowbuf[i].value always points to the next address in the data - * buffer even if the value is NULL. This allows row processors to - * estimate data sizes more easily. - */ - rowbuf[i].value = conn->inBuffer + conn->inCursor; - - /* Skip over the data value */ - if (vlen > 0) - { - if (pqSkipnchar(vlen, conn)) - goto EOFexit; - } - - /* advance the bitmap stuff */ - bitcnt++; - if (bitcnt == BITS_PER_BYTE) - { - bitmap_index++; - bmap = bitmap[bitmap_index]; - bitcnt = 0; - } - else - bmap <<= 1; - } - - /* Release bitmap now if we allocated it */ - if (bitmap != std_bitmap) - free(bitmap); - bitmap = NULL; - - /* Advance inStart to show that the "D" message has been processed. */ - conn->inStart = conn->inCursor; - - /* Process the collected row */ - errmsg = NULL; - if (pqRowProcessor(conn, &errmsg)) - return 0; /* normal, successful exit */ - - goto set_error_result; /* pqRowProcessor failed, report it */ - -advance_and_error: - - /* - * Discard the failed message. Unfortunately we don't know for sure where - * the end is, so just throw away everything in the input buffer. This is - * not very desirable but it's the best we can do in protocol v2. - */ - conn->inStart = conn->inEnd; - -set_error_result: - - /* - * Replace partially constructed result with an error result. First - * discard the old result to try to win back some memory. - */ - pqClearAsyncResult(conn); - - /* - * If preceding code didn't provide an error message, assume "out of - * memory" was meant. The advantage of having this special case is that - * freeing the old result first greatly improves the odds that gettext() - * will succeed in providing a translation. - */ - if (!errmsg) - errmsg = libpq_gettext("out of memory for query result"); - - printfPQExpBuffer(&conn->errorMessage, "%s\n", errmsg); - - /* - * XXX: if PQmakeEmptyPGresult() fails, there's probably not much we can - * do to recover... - */ - conn->result = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR); - conn->asyncStatus = PGASYNC_READY; - -EOFexit: - if (bitmap != NULL && bitmap != std_bitmap) - free(bitmap); - return EOF; -} - - -/* - * Attempt to read an Error or Notice response message. - * This is possible in several places, so we break it out as a subroutine. - * Entry: 'E' or 'N' message type has already been consumed. - * Exit: returns 0 if successfully consumed message. - * returns EOF if not enough data. - */ -static int -pqGetErrorNotice2(PGconn *conn, bool isError) -{ - PGresult *res = NULL; - PQExpBufferData workBuf; - char *startp; - char *splitp; - - /* - * Since the message might be pretty long, we create a temporary - * PQExpBuffer rather than using conn->workBuffer. workBuffer is intended - * for stuff that is expected to be short. - */ - initPQExpBuffer(&workBuf); - if (pqGets(&workBuf, conn)) - goto failure; - - /* - * Make a PGresult to hold the message. We temporarily lie about the - * result status, so that PQmakeEmptyPGresult doesn't uselessly copy - * conn->errorMessage. - * - * NB: This allocation can fail, if you run out of memory. The rest of the - * function handles that gracefully, and we still try to set the error - * message as the connection's error message. - */ - res = PQmakeEmptyPGresult(conn, PGRES_EMPTY_QUERY); - if (res) - { - res->resultStatus = isError ? PGRES_FATAL_ERROR : PGRES_NONFATAL_ERROR; - res->errMsg = pqResultStrdup(res, workBuf.data); - } - - /* - * Break the message into fields. We can't do very much here, but we can - * split the severity code off, and remove trailing newlines. Also, we use - * the heuristic that the primary message extends only to the first - * newline --- anything after that is detail message. (In some cases it'd - * be better classed as hint, but we can hardly be expected to guess that - * here.) - */ - while (workBuf.len > 0 && workBuf.data[workBuf.len - 1] == '\n') - workBuf.data[--workBuf.len] = '\0'; - splitp = strstr(workBuf.data, ": "); - if (splitp) - { - /* what comes before the colon is severity */ - *splitp = '\0'; - pqSaveMessageField(res, PG_DIAG_SEVERITY, workBuf.data); - startp = splitp + 3; - } - else - { - /* can't find a colon? oh well... */ - startp = workBuf.data; - } - splitp = strchr(startp, '\n'); - if (splitp) - { - /* what comes before the newline is primary message */ - *splitp++ = '\0'; - pqSaveMessageField(res, PG_DIAG_MESSAGE_PRIMARY, startp); - /* the rest is detail; strip any leading whitespace */ - while (*splitp && isspace((unsigned char) *splitp)) - splitp++; - pqSaveMessageField(res, PG_DIAG_MESSAGE_DETAIL, splitp); - } - else - { - /* single-line message, so all primary */ - pqSaveMessageField(res, PG_DIAG_MESSAGE_PRIMARY, startp); - } - - /* - * Either save error as current async result, or just emit the notice. - * Also, if it's an error and we were in a transaction block, assume the - * server has now gone to error-in-transaction state. - */ - if (isError) - { - pqClearAsyncResult(conn); - conn->result = res; - resetPQExpBuffer(&conn->errorMessage); - if (res && !PQExpBufferDataBroken(workBuf) && res->errMsg) - appendPQExpBufferStr(&conn->errorMessage, res->errMsg); - else - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("out of memory")); - if (conn->xactStatus == PQTRANS_INTRANS) - conn->xactStatus = PQTRANS_INERROR; - } - else - { - if (res) - { - if (res->noticeHooks.noticeRec != NULL) - (*res->noticeHooks.noticeRec) (res->noticeHooks.noticeRecArg, res); - PQclear(res); - } - } - - termPQExpBuffer(&workBuf); - return 0; - -failure: - if (res) - PQclear(res); - termPQExpBuffer(&workBuf); - return EOF; -} - -/* - * checkXactStatus - attempt to track transaction-block status of server - * - * This is called each time we receive a command-complete message. By - * watching for messages from BEGIN/COMMIT/ROLLBACK commands, we can do - * a passable job of tracking the server's xact status. BUT: this does - * not work at all on 7.3 servers with AUTOCOMMIT OFF. (Man, was that - * feature ever a mistake.) Caveat user. - * - * The tags known here are all those used as far back as 7.0; is it worth - * adding those from even-older servers? - */ -static void -checkXactStatus(PGconn *conn, const char *cmdTag) -{ - if (strcmp(cmdTag, "BEGIN") == 0) - conn->xactStatus = PQTRANS_INTRANS; - else if (strcmp(cmdTag, "COMMIT") == 0) - conn->xactStatus = PQTRANS_IDLE; - else if (strcmp(cmdTag, "ROLLBACK") == 0) - conn->xactStatus = PQTRANS_IDLE; - else if (strcmp(cmdTag, "START TRANSACTION") == 0) /* 7.3 only */ - conn->xactStatus = PQTRANS_INTRANS; - - /* - * Normally we get into INERROR state by detecting an Error message. - * However, if we see one of these tags then we know for sure the server - * is in abort state ... - */ - else if (strcmp(cmdTag, "*ABORT STATE*") == 0) /* pre-7.3 only */ - conn->xactStatus = PQTRANS_INERROR; -} - -/* - * Attempt to read a Notify response message. - * This is possible in several places, so we break it out as a subroutine. - * Entry: 'A' message type and length have already been consumed. - * Exit: returns 0 if successfully consumed Notify message. - * returns EOF if not enough data. - */ -static int -getNotify(PGconn *conn) -{ - int be_pid; - int nmlen; - PGnotify *newNotify; - - if (pqGetInt(&be_pid, 4, conn)) - return EOF; - if (pqGets(&conn->workBuffer, conn)) - return EOF; - - /* - * Store the relation name right after the PQnotify structure so it can - * all be freed at once. We don't use NAMEDATALEN because we don't want - * to tie this interface to a specific server name length. - */ - nmlen = strlen(conn->workBuffer.data); - newNotify = (PGnotify *) malloc(sizeof(PGnotify) + nmlen + 1); - if (newNotify) - { - newNotify->relname = (char *) newNotify + sizeof(PGnotify); - strcpy(newNotify->relname, conn->workBuffer.data); - /* fake up an empty-string extra field */ - newNotify->extra = newNotify->relname + nmlen; - newNotify->be_pid = be_pid; - newNotify->next = NULL; - if (conn->notifyTail) - conn->notifyTail->next = newNotify; - else - conn->notifyHead = newNotify; - conn->notifyTail = newNotify; - } - - return 0; -} - - -/* - * PQgetCopyData - read a row of data from the backend during COPY OUT - * - * If successful, sets *buffer to point to a malloc'd row of data, and - * returns row length (always > 0) as result. - * Returns 0 if no row available yet (only possible if async is true), - * -1 if end of copy (consult PQgetResult), or -2 if error (consult - * PQerrorMessage). - */ -int -pqGetCopyData2(PGconn *conn, char **buffer, int async) -{ - bool found; - int msgLength; - - for (;;) - { - /* - * Do we have a complete line of data? - */ - conn->inCursor = conn->inStart; - found = false; - while (conn->inCursor < conn->inEnd) - { - char c = conn->inBuffer[conn->inCursor++]; - - if (c == '\n') - { - found = true; - break; - } - } - if (!found) - goto nodata; - msgLength = conn->inCursor - conn->inStart; - - /* - * If it's the end-of-data marker, consume it, exit COPY_OUT mode, and - * let caller read status with PQgetResult(). - */ - if (msgLength == 3 && - strncmp(&conn->inBuffer[conn->inStart], "\\.\n", 3) == 0) - { - conn->inStart = conn->inCursor; - conn->asyncStatus = PGASYNC_BUSY; - return -1; - } - - /* - * Pass the line back to the caller. - */ - *buffer = (char *) malloc(msgLength + 1); - if (*buffer == NULL) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("out of memory\n")); - return -2; - } - memcpy(*buffer, &conn->inBuffer[conn->inStart], msgLength); - (*buffer)[msgLength] = '\0'; /* Add terminating null */ - - /* Mark message consumed */ - conn->inStart = conn->inCursor; - - return msgLength; - -nodata: - /* Don't block if async read requested */ - if (async) - return 0; - /* Need to load more data */ - if (pqWait(TRUE, FALSE, conn) || - pqReadData(conn) < 0) - return -2; - } -} - - -/* - * PQgetline - gets a newline-terminated string from the backend. - * - * See fe-exec.c for documentation. - */ -int -pqGetline2(PGconn *conn, char *s, int maxlen) -{ - int result = 1; /* return value if buffer overflows */ - - if (conn->sock == PGINVALID_SOCKET || - conn->asyncStatus != PGASYNC_COPY_OUT) - { - *s = '\0'; - return EOF; - } - - /* - * Since this is a purely synchronous routine, we don't bother to maintain - * conn->inCursor; there is no need to back up. - */ - while (maxlen > 1) - { - if (conn->inStart < conn->inEnd) - { - char c = conn->inBuffer[conn->inStart++]; - - if (c == '\n') - { - result = 0; /* success exit */ - break; - } - *s++ = c; - maxlen--; - } - else - { - /* need to load more data */ - if (pqWait(TRUE, FALSE, conn) || - pqReadData(conn) < 0) - { - result = EOF; - break; - } - } - } - *s = '\0'; - - return result; -} - -/* - * PQgetlineAsync - gets a COPY data row without blocking. - * - * See fe-exec.c for documentation. - */ -int -pqGetlineAsync2(PGconn *conn, char *buffer, int bufsize) -{ - int avail; - - if (conn->asyncStatus != PGASYNC_COPY_OUT) - return -1; /* we are not doing a copy... */ - - /* - * Move data from libpq's buffer to the caller's. We want to accept data - * only in units of whole lines, not partial lines. This ensures that we - * can recognize the terminator line "\\.\n". (Otherwise, if it happened - * to cross a packet/buffer boundary, we might hand the first one or two - * characters off to the caller, which we shouldn't.) - */ - - conn->inCursor = conn->inStart; - - avail = bufsize; - while (avail > 0 && conn->inCursor < conn->inEnd) - { - char c = conn->inBuffer[conn->inCursor++]; - - *buffer++ = c; - --avail; - if (c == '\n') - { - /* Got a complete line; mark the data removed from libpq */ - conn->inStart = conn->inCursor; - /* Is it the endmarker line? */ - if (bufsize - avail == 3 && buffer[-3] == '\\' && buffer[-2] == '.') - return -1; - /* No, return the data line to the caller */ - return bufsize - avail; - } - } - - /* - * We don't have a complete line. We'd prefer to leave it in libpq's - * buffer until the rest arrives, but there is a special case: what if the - * line is longer than the buffer the caller is offering us? In that case - * we'd better hand over a partial line, else we'd get into an infinite - * loop. Do this in a way that ensures we can't misrecognize a terminator - * line later: leave last 3 characters in libpq buffer. - */ - if (avail == 0 && bufsize > 3) - { - conn->inStart = conn->inCursor - 3; - return bufsize - 3; - } - return 0; -} - -/* - * PQendcopy - * - * See fe-exec.c for documentation. - */ -int -pqEndcopy2(PGconn *conn) -{ - PGresult *result; - - if (conn->asyncStatus != PGASYNC_COPY_IN && - conn->asyncStatus != PGASYNC_COPY_OUT) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("no COPY in progress\n")); - return 1; - } - - /* - * make sure no data is waiting to be sent, abort if we are non-blocking - * and the flush fails - */ - if (pqFlush(conn) && pqIsnonblocking(conn)) - return 1; - - /* non blocking connections may have to abort at this point. */ - if (pqIsnonblocking(conn) && PQisBusy(conn)) - return 1; - - /* Return to active duty */ - conn->asyncStatus = PGASYNC_BUSY; - resetPQExpBuffer(&conn->errorMessage); - - /* Wait for the completion response */ - result = PQgetResult(conn); - - /* Expecting a successful result */ - if (result && result->resultStatus == PGRES_COMMAND_OK) - { - PQclear(result); - return 0; - } - - /* - * Trouble. For backwards-compatibility reasons, we issue the error - * message as if it were a notice (would be nice to get rid of this - * silliness, but too many apps probably don't handle errors from - * PQendcopy reasonably). Note that the app can still obtain the error - * status from the PGconn object. - */ - if (conn->errorMessage.len > 0) - { - /* We have to strip the trailing newline ... pain in neck... */ - char svLast = conn->errorMessage.data[conn->errorMessage.len - 1]; - - if (svLast == '\n') - conn->errorMessage.data[conn->errorMessage.len - 1] = '\0'; - pqInternalNotice(&conn->noticeHooks, "%s", conn->errorMessage.data); - conn->errorMessage.data[conn->errorMessage.len - 1] = svLast; - } - - PQclear(result); - - /* - * The worst case is that we've lost sync with the backend entirely due to - * application screwup of the copy in/out protocol. To recover, reset the - * connection (talk about using a sledgehammer...) - */ - pqInternalNotice(&conn->noticeHooks, - "lost synchronization with server, resetting connection"); - - /* - * Users doing non-blocking connections need to handle the reset - * themselves, they'll need to check the connection status if we return an - * error. - */ - if (pqIsnonblocking(conn)) - PQresetStart(conn); - else - PQreset(conn); - - return 1; -} - - -/* - * PQfn - Send a function call to the POSTGRES backend. - * - * See fe-exec.c for documentation. - */ -PGresult * -pqFunctionCall2(PGconn *conn, Oid fnid, - int *result_buf, int *actual_result_len, - int result_is_int, - const PQArgBlock *args, int nargs) -{ - bool needInput = false; - ExecStatusType status = PGRES_FATAL_ERROR; - char id; - int i; - - /* PQfn already validated connection state */ - - if (pqPutMsgStart('F', false, conn) < 0 || /* function call msg */ - pqPuts(" ", conn) < 0 || /* dummy string */ - pqPutInt(fnid, 4, conn) != 0 || /* function id */ - pqPutInt(nargs, 4, conn) != 0) /* # of args */ - { - pqHandleSendFailure(conn); - return NULL; - } - - for (i = 0; i < nargs; ++i) - { /* len.int4 + contents */ - if (pqPutInt(args[i].len, 4, conn)) - { - pqHandleSendFailure(conn); - return NULL; - } - - if (args[i].isint) - { - if (pqPutInt(args[i].u.integer, 4, conn)) - { - pqHandleSendFailure(conn); - return NULL; - } - } - else - { - if (pqPutnchar((char *) args[i].u.ptr, args[i].len, conn)) - { - pqHandleSendFailure(conn); - return NULL; - } - } - } - - if (pqPutMsgEnd(conn) < 0 || - pqFlush(conn)) - { - pqHandleSendFailure(conn); - return NULL; - } - - for (;;) - { - if (needInput) - { - /* Wait for some data to arrive (or for the channel to close) */ - if (pqWait(TRUE, FALSE, conn) || - pqReadData(conn) < 0) - break; - } - - /* - * Scan the message. If we run out of data, loop around to try again. - */ - conn->inCursor = conn->inStart; - needInput = true; - - if (pqGetc(&id, conn)) - continue; - - /* - * We should see V or E response to the command, but might get N - * and/or A notices first. We also need to swallow the final Z before - * returning. - */ - switch (id) - { - case 'V': /* function result */ - if (pqGetc(&id, conn)) - continue; - if (id == 'G') - { - /* function returned nonempty value */ - if (pqGetInt(actual_result_len, 4, conn)) - continue; - if (result_is_int) - { - if (pqGetInt(result_buf, 4, conn)) - continue; - } - else - { - if (pqGetnchar((char *) result_buf, - *actual_result_len, - conn)) - continue; - } - if (pqGetc(&id, conn)) /* get the last '0' */ - continue; - } - if (id == '0') - { - /* correctly finished function result message */ - status = PGRES_COMMAND_OK; - } - else - { - /* The backend violates the protocol. */ - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("protocol error: id=0x%x\n"), - id); - pqSaveErrorResult(conn); - conn->inStart = conn->inCursor; - return pqPrepareAsyncResult(conn); - } - break; - case 'E': /* error return */ - if (pqGetErrorNotice2(conn, true)) - continue; - status = PGRES_FATAL_ERROR; - break; - case 'A': /* notify message */ - /* handle notify and go back to processing return values */ - if (getNotify(conn)) - continue; - break; - case 'N': /* notice */ - /* handle notice and go back to processing return values */ - if (pqGetErrorNotice2(conn, false)) - continue; - break; - case 'Z': /* backend is ready for new query */ - /* consume the message and exit */ - conn->inStart = conn->inCursor; - /* if we saved a result object (probably an error), use it */ - if (conn->result) - return pqPrepareAsyncResult(conn); - return PQmakeEmptyPGresult(conn, status); - default: - /* The backend violates the protocol. */ - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("protocol error: id=0x%x\n"), - id); - pqSaveErrorResult(conn); - conn->inStart = conn->inCursor; - return pqPrepareAsyncResult(conn); - } - /* Completed this message, keep going */ - conn->inStart = conn->inCursor; - needInput = false; - } - - /* - * We fall out of the loop only upon failing to read data. - * conn->errorMessage has been set by pqWait or pqReadData. We want to - * append it to any already-received error message. - */ - pqSaveErrorResult(conn); - return pqPrepareAsyncResult(conn); -} - - -/* - * Construct startup packet - * - * Returns a malloc'd packet buffer, or NULL if out of memory - */ -char * -pqBuildStartupPacket2(PGconn *conn, int *packetlen, - const PQEnvironmentOption *options) -{ - StartupPacket *startpacket; - - *packetlen = sizeof(StartupPacket); - startpacket = (StartupPacket *) malloc(sizeof(StartupPacket)); - if (!startpacket) - return NULL; - - MemSet(startpacket, 0, sizeof(StartupPacket)); - - startpacket->protoVersion = htonl(conn->pversion); - - /* strncpy is safe here: postmaster will handle full fields correctly */ - strncpy(startpacket->user, conn->pguser, SM_USER); - strncpy(startpacket->database, conn->dbName, SM_DATABASE); - strncpy(startpacket->tty, conn->pgtty, SM_TTY); - - if (conn->pgoptions) - strncpy(startpacket->options, conn->pgoptions, SM_OPTIONS); - - return (char *) startpacket; -} diff --git a/libpq/fe-protocol3.c b/libpq/fe-protocol3.c deleted file mode 100644 index a8a987a..0000000 --- a/libpq/fe-protocol3.c +++ /dev/null @@ -1,2204 +0,0 @@ -/*------------------------------------------------------------------------- - * - * fe-protocol3.c - * functions that are specific to frontend/backend protocol version 3 - * - * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * - * IDENTIFICATION - * src/interfaces/libpq/fe-protocol3.c - * - *------------------------------------------------------------------------- - */ -#include "postgres_fe.h" - -#include <ctype.h> -#include <fcntl.h> - -#include "libpq-fe.h" -#include "libpq-int.h" - -#include "mb/pg_wchar.h" - -#ifdef WIN32 -#include "win32.h" -#else -#include <unistd.h> -#include <netinet/in.h> -#ifdef HAVE_NETINET_TCP_H -#include <netinet/tcp.h> -#endif -#include <arpa/inet.h> -#endif - - -/* - * This macro lists the backend message types that could be "long" (more - * than a couple of kilobytes). - */ -#define VALID_LONG_MESSAGE_TYPE(id) \ - ((id) == 'T' || (id) == 'D' || (id) == 'd' || (id) == 'V' || \ - (id) == 'E' || (id) == 'N' || (id) == 'A') - - -static void handleSyncLoss(PGconn *conn, char id, int msgLength); -static int getRowDescriptions(PGconn *conn, int msgLength); -static int getParamDescriptions(PGconn *conn, int msgLength); -static int getAnotherTuple(PGconn *conn, int msgLength); -static int getParameterStatus(PGconn *conn); -static int getNotify(PGconn *conn); -static int getCopyStart(PGconn *conn, ExecStatusType copytype); -static int getReadyForQuery(PGconn *conn); -static void reportErrorPosition(PQExpBuffer msg, const char *query, - int loc, int encoding); -static int build_startup_packet(const PGconn *conn, char *packet, - const PQEnvironmentOption *options); - - -/* - * parseInput: if appropriate, parse input data from backend - * until input is exhausted or a stopping state is reached. - * Note that this function will NOT attempt to read more data from the backend. - */ -void -pqParseInput3(PGconn *conn) -{ - char id; - int msgLength; - int avail; - - /* - * Loop to parse successive complete messages available in the buffer. - */ - for (;;) - { - /* - * Try to read a message. First get the type code and length. Return - * if not enough data. - */ - conn->inCursor = conn->inStart; - if (pqGetc(&id, conn)) - return; - if (pqGetInt(&msgLength, 4, conn)) - return; - - /* - * Try to validate message type/length here. A length less than 4 is - * definitely broken. Large lengths should only be believed for a few - * message types. - */ - if (msgLength < 4) - { - handleSyncLoss(conn, id, msgLength); - return; - } - if (msgLength > 30000 && !VALID_LONG_MESSAGE_TYPE(id)) - { - handleSyncLoss(conn, id, msgLength); - return; - } - - /* - * Can't process if message body isn't all here yet. - */ - msgLength -= 4; - avail = conn->inEnd - conn->inCursor; - if (avail < msgLength) - { - /* - * Before returning, enlarge the input buffer if needed to hold - * the whole message. This is better than leaving it to - * pqReadData because we can avoid multiple cycles of realloc() - * when the message is large; also, we can implement a reasonable - * recovery strategy if we are unable to make the buffer big - * enough. - */ - if (pqCheckInBufferSpace(conn->inCursor + (size_t) msgLength, - conn)) - { - /* - * XXX add some better recovery code... plan is to skip over - * the message using its length, then report an error. For the - * moment, just treat this like loss of sync (which indeed it - * might be!) - */ - handleSyncLoss(conn, id, msgLength); - } - return; - } - - /* - * NOTIFY and NOTICE messages can happen in any state; always process - * them right away. - * - * Most other messages should only be processed while in BUSY state. - * (In particular, in READY state we hold off further parsing until - * the application collects the current PGresult.) - * - * However, if the state is IDLE then we got trouble; we need to deal - * with the unexpected message somehow. - * - * ParameterStatus ('S') messages are a special case: in IDLE state we - * must process 'em (this case could happen if a new value was adopted - * from config file due to SIGHUP), but otherwise we hold off until - * BUSY state. - */ - if (id == 'A') - { - if (getNotify(conn)) - return; - } - else if (id == 'N') - { - if (pqGetErrorNotice3(conn, false)) - return; - } - else if (conn->asyncStatus != PGASYNC_BUSY) - { - /* If not IDLE state, just wait ... */ - if (conn->asyncStatus != PGASYNC_IDLE) - return; - - /* - * Unexpected message in IDLE state; need to recover somehow. - * ERROR messages are handled using the notice processor; - * ParameterStatus is handled normally; anything else is just - * dropped on the floor after displaying a suitable warning - * notice. (An ERROR is very possibly the backend telling us why - * it is about to close the connection, so we don't want to just - * discard it...) - */ - if (id == 'E') - { - if (pqGetErrorNotice3(conn, false /* treat as notice */ )) - return; - } - else if (id == 'S') - { - if (getParameterStatus(conn)) - return; - } - else - { - pqInternalNotice(&conn->noticeHooks, - "message type 0x%02x arrived from server while idle", - id); - /* Discard the unexpected message */ - conn->inCursor += msgLength; - } - } - else - { - /* - * In BUSY state, we can process everything. - */ - switch (id) - { - case 'C': /* command complete */ - if (pqGets(&conn->workBuffer, conn)) - return; - if (conn->result == NULL) - { - conn->result = PQmakeEmptyPGresult(conn, - PGRES_COMMAND_OK); - if (!conn->result) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("out of memory")); - pqSaveErrorResult(conn); - } - } - if (conn->result) - strlcpy(conn->result->cmdStatus, conn->workBuffer.data, - CMDSTATUS_LEN); - conn->asyncStatus = PGASYNC_READY; - break; - case 'E': /* error return */ - if (pqGetErrorNotice3(conn, true)) - return; - conn->asyncStatus = PGASYNC_READY; - break; - case 'Z': /* backend is ready for new query */ - if (getReadyForQuery(conn)) - return; - conn->asyncStatus = PGASYNC_IDLE; - break; - case 'I': /* empty query */ - if (conn->result == NULL) - { - conn->result = PQmakeEmptyPGresult(conn, - PGRES_EMPTY_QUERY); - if (!conn->result) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("out of memory")); - pqSaveErrorResult(conn); - } - } - conn->asyncStatus = PGASYNC_READY; - break; - case '1': /* Parse Complete */ - /* If we're doing PQprepare, we're done; else ignore */ - if (conn->queryclass == PGQUERY_PREPARE) - { - if (conn->result == NULL) - { - conn->result = PQmakeEmptyPGresult(conn, - PGRES_COMMAND_OK); - if (!conn->result) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("out of memory")); - pqSaveErrorResult(conn); - } - } - conn->asyncStatus = PGASYNC_READY; - } - break; - case '2': /* Bind Complete */ - case '3': /* Close Complete */ - /* Nothing to do for these message types */ - break; - case 'S': /* parameter status */ - if (getParameterStatus(conn)) - return; - break; - case 'K': /* secret key data from the backend */ - - /* - * This is expected only during backend startup, but it's - * just as easy to handle it as part of the main loop. - * Save the data and continue processing. - */ - if (pqGetInt(&(conn->be_pid), 4, conn)) - return; - if (pqGetInt(&(conn->be_key), 4, conn)) - return; - break; - case 'T': /* Row Description */ - if (conn->result != NULL && - conn->result->resultStatus == PGRES_FATAL_ERROR) - { - /* - * We've already choked for some reason. Just discard - * the data till we get to the end of the query. - */ - conn->inCursor += msgLength; - } - else if (conn->result == NULL || - conn->queryclass == PGQUERY_DESCRIBE) - { - /* First 'T' in a query sequence */ - if (getRowDescriptions(conn, msgLength)) - return; - /* getRowDescriptions() moves inStart itself */ - continue; - } - else - { - /* - * A new 'T' message is treated as the start of - * another PGresult. (It is not clear that this is - * really possible with the current backend.) We stop - * parsing until the application accepts the current - * result. - */ - conn->asyncStatus = PGASYNC_READY; - return; - } - break; - case 'n': /* No Data */ - - /* - * NoData indicates that we will not be seeing a - * RowDescription message because the statement or portal - * inquired about doesn't return rows. - * - * If we're doing a Describe, we have to pass something - * back to the client, so set up a COMMAND_OK result, - * instead of TUPLES_OK. Otherwise we can just ignore - * this message. - */ - if (conn->queryclass == PGQUERY_DESCRIBE) - { - if (conn->result == NULL) - { - conn->result = PQmakeEmptyPGresult(conn, - PGRES_COMMAND_OK); - if (!conn->result) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("out of memory")); - pqSaveErrorResult(conn); - } - } - conn->asyncStatus = PGASYNC_READY; - } - break; - case 't': /* Parameter Description */ - if (getParamDescriptions(conn, msgLength)) - return; - /* getParamDescriptions() moves inStart itself */ - continue; - case 'D': /* Data Row */ - if (conn->result != NULL && - conn->result->resultStatus == PGRES_TUPLES_OK) - { - /* Read another tuple of a normal query response */ - if (getAnotherTuple(conn, msgLength)) - return; - /* getAnotherTuple() moves inStart itself */ - continue; - } - else if (conn->result != NULL && - conn->result->resultStatus == PGRES_FATAL_ERROR) - { - /* - * We've already choked for some reason. Just discard - * tuples till we get to the end of the query. - */ - conn->inCursor += msgLength; - } - else - { - /* Set up to report error at end of query */ - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("server sent data (\"D\" message) without prior row description (\"T\" message)\n")); - pqSaveErrorResult(conn); - /* Discard the unexpected message */ - conn->inCursor += msgLength; - } - break; - case 'G': /* Start Copy In */ - if (getCopyStart(conn, PGRES_COPY_IN)) - return; - conn->asyncStatus = PGASYNC_COPY_IN; - break; - case 'H': /* Start Copy Out */ - if (getCopyStart(conn, PGRES_COPY_OUT)) - return; - conn->asyncStatus = PGASYNC_COPY_OUT; - conn->copy_already_done = 0; - break; - case 'W': /* Start Copy Both */ - if (getCopyStart(conn, PGRES_COPY_BOTH)) - return; - conn->asyncStatus = PGASYNC_COPY_BOTH; - conn->copy_already_done = 0; - break; - case 'd': /* Copy Data */ - - /* - * If we see Copy Data, just silently drop it. This would - * only occur if application exits COPY OUT mode too - * early. - */ - conn->inCursor += msgLength; - break; - case 'c': /* Copy Done */ - - /* - * If we see Copy Done, just silently drop it. This is - * the normal case during PQendcopy. We will keep - * swallowing data, expecting to see command-complete for - * the COPY command. - */ - break; - default: - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext( - "unexpected response from server; first received character was \"%c\"\n"), - id); - /* build an error result holding the error message */ - pqSaveErrorResult(conn); - /* not sure if we will see more, so go to ready state */ - conn->asyncStatus = PGASYNC_READY; - /* Discard the unexpected message */ - conn->inCursor += msgLength; - break; - } /* switch on protocol character */ - } - /* Successfully consumed this message */ - if (conn->inCursor == conn->inStart + 5 + msgLength) - { - /* Normal case: parsing agrees with specified length */ - conn->inStart = conn->inCursor; - } - else - { - /* Trouble --- report it */ - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("message contents do not agree with length in message type \"%c\"\n"), - id); - /* build an error result holding the error message */ - pqSaveErrorResult(conn); - conn->asyncStatus = PGASYNC_READY; - /* trust the specified message length as what to skip */ - conn->inStart += 5 + msgLength; - } - } -} - -/* - * handleSyncLoss: clean up after loss of message-boundary sync - * - * There isn't really a lot we can do here except abandon the connection. - */ -static void -handleSyncLoss(PGconn *conn, char id, int msgLength) -{ - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext( - "lost synchronization with server: got message type \"%c\", length %d\n"), - id, msgLength); - /* build an error result holding the error message */ - pqSaveErrorResult(conn); - conn->asyncStatus = PGASYNC_READY; /* drop out of GetResult wait loop */ - /* flush input data since we're giving up on processing it */ - pqDropConnection(conn, true); - conn->status = CONNECTION_BAD; /* No more connection to backend */ -} - -/* - * parseInput subroutine to read a 'T' (row descriptions) message. - * We'll build a new PGresult structure (unless called for a Describe - * command for a prepared statement) containing the attribute data. - * Returns: 0 if processed message successfully, EOF to suspend parsing - * (the latter case is not actually used currently). - * In the former case, conn->inStart has been advanced past the message. - */ -static int -getRowDescriptions(PGconn *conn, int msgLength) -{ - PGresult *result; - int nfields; - const char *errmsg; - int i; - - /* - * When doing Describe for a prepared statement, there'll already be a - * PGresult created by getParamDescriptions, and we should fill data into - * that. Otherwise, create a new, empty PGresult. - */ - if (conn->queryclass == PGQUERY_DESCRIBE) - { - if (conn->result) - result = conn->result; - else - result = PQmakeEmptyPGresult(conn, PGRES_COMMAND_OK); - } - else - result = PQmakeEmptyPGresult(conn, PGRES_TUPLES_OK); - if (!result) - { - errmsg = NULL; /* means "out of memory", see below */ - goto advance_and_error; - } - - /* parseInput already read the 'T' label and message length. */ - /* the next two bytes are the number of fields */ - if (pqGetInt(&(result->numAttributes), 2, conn)) - { - /* We should not run out of data here, so complain */ - errmsg = libpq_gettext("insufficient data in \"T\" message"); - goto advance_and_error; - } - nfields = result->numAttributes; - - /* allocate space for the attribute descriptors */ - if (nfields > 0) - { - result->attDescs = (PGresAttDesc *) - pqResultAlloc(result, nfields * sizeof(PGresAttDesc), TRUE); - if (!result->attDescs) - { - errmsg = NULL; /* means "out of memory", see below */ - goto advance_and_error; - } - MemSet(result->attDescs, 0, nfields * sizeof(PGresAttDesc)); - } - - /* result->binary is true only if ALL columns are binary */ - result->binary = (nfields > 0) ? 1 : 0; - - /* get type info */ - for (i = 0; i < nfields; i++) - { - int tableid; - int columnid; - int typid; - int typlen; - int atttypmod; - int format; - - if (pqGets(&conn->workBuffer, conn) || - pqGetInt(&tableid, 4, conn) || - pqGetInt(&columnid, 2, conn) || - pqGetInt(&typid, 4, conn) || - pqGetInt(&typlen, 2, conn) || - pqGetInt(&atttypmod, 4, conn) || - pqGetInt(&format, 2, conn)) - { - /* We should not run out of data here, so complain */ - errmsg = libpq_gettext("insufficient data in \"T\" message"); - goto advance_and_error; - } - - /* - * Since pqGetInt treats 2-byte integers as unsigned, we need to - * coerce these results to signed form. - */ - columnid = (int) ((int16) columnid); - typlen = (int) ((int16) typlen); - format = (int) ((int16) format); - - result->attDescs[i].name = pqResultStrdup(result, - conn->workBuffer.data); - if (!result->attDescs[i].name) - { - errmsg = NULL; /* means "out of memory", see below */ - goto advance_and_error; - } - result->attDescs[i].tableid = tableid; - result->attDescs[i].columnid = columnid; - result->attDescs[i].format = format; - result->attDescs[i].typid = typid; - result->attDescs[i].typlen = typlen; - result->attDescs[i].atttypmod = atttypmod; - - if (format != 1) - result->binary = 0; - } - - /* Sanity check that we absorbed all the data */ - if (conn->inCursor != conn->inStart + 5 + msgLength) - { - errmsg = libpq_gettext("extraneous data in \"T\" message"); - goto advance_and_error; - } - - /* Success! */ - conn->result = result; - - /* Advance inStart to show that the "T" message has been processed. */ - conn->inStart = conn->inCursor; - - /* - * If we're doing a Describe, we're done, and ready to pass the result - * back to the client. - */ - if (conn->queryclass == PGQUERY_DESCRIBE) - { - conn->asyncStatus = PGASYNC_READY; - return 0; - } - - /* - * We could perform additional setup for the new result set here, but for - * now there's nothing else to do. - */ - - /* And we're done. */ - return 0; - -advance_and_error: - /* Discard unsaved result, if any */ - if (result && result != conn->result) - PQclear(result); - - /* Discard the failed message by pretending we read it */ - conn->inStart += 5 + msgLength; - - /* - * Replace partially constructed result with an error result. First - * discard the old result to try to win back some memory. - */ - pqClearAsyncResult(conn); - - /* - * If preceding code didn't provide an error message, assume "out of - * memory" was meant. The advantage of having this special case is that - * freeing the old result first greatly improves the odds that gettext() - * will succeed in providing a translation. - */ - if (!errmsg) - errmsg = libpq_gettext("out of memory for query result"); - - printfPQExpBuffer(&conn->errorMessage, "%s\n", errmsg); - pqSaveErrorResult(conn); - - /* - * Return zero to allow input parsing to continue. Subsequent "D" - * messages will be ignored until we get to end of data, since an error - * result is already set up. - */ - return 0; -} - -/* - * parseInput subroutine to read a 't' (ParameterDescription) message. - * We'll build a new PGresult structure containing the parameter data. - * Returns: 0 if completed message, EOF if not enough data yet. - * In the former case, conn->inStart has been advanced past the message. - * - * Note that if we run out of data, we have to release the partially - * constructed PGresult, and rebuild it again next time. Fortunately, - * that shouldn't happen often, since 't' messages usually fit in a packet. - */ -static int -getParamDescriptions(PGconn *conn, int msgLength) -{ - PGresult *result; - const char *errmsg = NULL; /* means "out of memory", see below */ - int nparams; - int i; - - result = PQmakeEmptyPGresult(conn, PGRES_COMMAND_OK); - if (!result) - goto advance_and_error; - - /* parseInput already read the 't' label and message length. */ - /* the next two bytes are the number of parameters */ - if (pqGetInt(&(result->numParameters), 2, conn)) - goto not_enough_data; - nparams = result->numParameters; - - /* allocate space for the parameter descriptors */ - if (nparams > 0) - { - result->paramDescs = (PGresParamDesc *) - pqResultAlloc(result, nparams * sizeof(PGresParamDesc), TRUE); - if (!result->paramDescs) - goto advance_and_error; - MemSet(result->paramDescs, 0, nparams * sizeof(PGresParamDesc)); - } - - /* get parameter info */ - for (i = 0; i < nparams; i++) - { - int typid; - - if (pqGetInt(&typid, 4, conn)) - goto not_enough_data; - result->paramDescs[i].typid = typid; - } - - /* Sanity check that we absorbed all the data */ - if (conn->inCursor != conn->inStart + 5 + msgLength) - { - errmsg = libpq_gettext("extraneous data in \"t\" message"); - goto advance_and_error; - } - - /* Success! */ - conn->result = result; - - /* Advance inStart to show that the "t" message has been processed. */ - conn->inStart = conn->inCursor; - - return 0; - -not_enough_data: - PQclear(result); - return EOF; - -advance_and_error: - /* Discard unsaved result, if any */ - if (result && result != conn->result) - PQclear(result); - - /* Discard the failed message by pretending we read it */ - conn->inStart += 5 + msgLength; - - /* - * Replace partially constructed result with an error result. First - * discard the old result to try to win back some memory. - */ - pqClearAsyncResult(conn); - - /* - * If preceding code didn't provide an error message, assume "out of - * memory" was meant. The advantage of having this special case is that - * freeing the old result first greatly improves the odds that gettext() - * will succeed in providing a translation. - */ - if (!errmsg) - errmsg = libpq_gettext("out of memory"); - printfPQExpBuffer(&conn->errorMessage, "%s\n", errmsg); - pqSaveErrorResult(conn); - - /* - * Return zero to allow input parsing to continue. Essentially, we've - * replaced the COMMAND_OK result with an error result, but since this - * doesn't affect the protocol state, it's fine. - */ - return 0; -} - -/* - * parseInput subroutine to read a 'D' (row data) message. - * We fill rowbuf with column pointers and then call the row processor. - * Returns: 0 if processed message successfully, EOF to suspend parsing - * (the latter case is not actually used currently). - * In the former case, conn->inStart has been advanced past the message. - */ -static int -getAnotherTuple(PGconn *conn, int msgLength) -{ - PGresult *result = conn->result; - int nfields = result->numAttributes; - const char *errmsg; - PGdataValue *rowbuf; - int tupnfields; /* # fields from tuple */ - int vlen; /* length of the current field value */ - int i; - - /* Get the field count and make sure it's what we expect */ - if (pqGetInt(&tupnfields, 2, conn)) - { - /* We should not run out of data here, so complain */ - errmsg = libpq_gettext("insufficient data in \"D\" message"); - goto advance_and_error; - } - - if (tupnfields != nfields) - { - errmsg = libpq_gettext("unexpected field count in \"D\" message"); - goto advance_and_error; - } - - /* Resize row buffer if needed */ - rowbuf = conn->rowBuf; - if (nfields > conn->rowBufLen) - { - rowbuf = (PGdataValue *) realloc(rowbuf, - nfields * sizeof(PGdataValue)); - if (!rowbuf) - { - errmsg = NULL; /* means "out of memory", see below */ - goto advance_and_error; - } - conn->rowBuf = rowbuf; - conn->rowBufLen = nfields; - } - - /* Scan the fields */ - for (i = 0; i < nfields; i++) - { - /* get the value length */ - if (pqGetInt(&vlen, 4, conn)) - { - /* We should not run out of data here, so complain */ - errmsg = libpq_gettext("insufficient data in \"D\" message"); - goto advance_and_error; - } - rowbuf[i].len = vlen; - - /* - * rowbuf[i].value always points to the next address in the data - * buffer even if the value is NULL. This allows row processors to - * estimate data sizes more easily. - */ - rowbuf[i].value = conn->inBuffer + conn->inCursor; - - /* Skip over the data value */ - if (vlen > 0) - { - if (pqSkipnchar(vlen, conn)) - { - /* We should not run out of data here, so complain */ - errmsg = libpq_gettext("insufficient data in \"D\" message"); - goto advance_and_error; - } - } - } - - /* Sanity check that we absorbed all the data */ - if (conn->inCursor != conn->inStart + 5 + msgLength) - { - errmsg = libpq_gettext("extraneous data in \"D\" message"); - goto advance_and_error; - } - - /* Advance inStart to show that the "D" message has been processed. */ - conn->inStart = conn->inCursor; - - /* Process the collected row */ - errmsg = NULL; - if (pqRowProcessor(conn, &errmsg)) - return 0; /* normal, successful exit */ - - goto set_error_result; /* pqRowProcessor failed, report it */ - -advance_and_error: - /* Discard the failed message by pretending we read it */ - conn->inStart += 5 + msgLength; - -set_error_result: - - /* - * Replace partially constructed result with an error result. First - * discard the old result to try to win back some memory. - */ - pqClearAsyncResult(conn); - - /* - * If preceding code didn't provide an error message, assume "out of - * memory" was meant. The advantage of having this special case is that - * freeing the old result first greatly improves the odds that gettext() - * will succeed in providing a translation. - */ - if (!errmsg) - errmsg = libpq_gettext("out of memory for query result"); - - printfPQExpBuffer(&conn->errorMessage, "%s\n", errmsg); - pqSaveErrorResult(conn); - - /* - * Return zero to allow input parsing to continue. Subsequent "D" - * messages will be ignored until we get to end of data, since an error - * result is already set up. - */ - return 0; -} - - -/* - * Attempt to read an Error or Notice response message. - * This is possible in several places, so we break it out as a subroutine. - * Entry: 'E' or 'N' message type and length have already been consumed. - * Exit: returns 0 if successfully consumed message. - * returns EOF if not enough data. - */ -int -pqGetErrorNotice3(PGconn *conn, bool isError) -{ - PGresult *res = NULL; - bool have_position = false; - PQExpBufferData workBuf; - char id; - - /* - * Since the fields might be pretty long, we create a temporary - * PQExpBuffer rather than using conn->workBuffer. workBuffer is intended - * for stuff that is expected to be short. We shouldn't use - * conn->errorMessage either, since this might be only a notice. - */ - initPQExpBuffer(&workBuf); - - /* - * Make a PGresult to hold the accumulated fields. We temporarily lie - * about the result status, so that PQmakeEmptyPGresult doesn't uselessly - * copy conn->errorMessage. - * - * NB: This allocation can fail, if you run out of memory. The rest of the - * function handles that gracefully, and we still try to set the error - * message as the connection's error message. - */ - res = PQmakeEmptyPGresult(conn, PGRES_EMPTY_QUERY); - if (res) - res->resultStatus = isError ? PGRES_FATAL_ERROR : PGRES_NONFATAL_ERROR; - - /* - * Read the fields and save into res. - * - * While at it, save the SQLSTATE in conn->last_sqlstate, and note whether - * we saw a PG_DIAG_STATEMENT_POSITION field. - */ - for (;;) - { - if (pqGetc(&id, conn)) - goto fail; - if (id == '\0') - break; /* terminator found */ - if (pqGets(&workBuf, conn)) - goto fail; - pqSaveMessageField(res, id, workBuf.data); - if (id == PG_DIAG_SQLSTATE) - strlcpy(conn->last_sqlstate, workBuf.data, - sizeof(conn->last_sqlstate)); - else if (id == PG_DIAG_STATEMENT_POSITION) - have_position = true; - } - - /* - * Save the active query text, if any, into res as well; but only if we - * might need it for an error cursor display, which is only true if there - * is a PG_DIAG_STATEMENT_POSITION field. - */ - if (have_position && conn->last_query && res) - res->errQuery = pqResultStrdup(res, conn->last_query); - - /* - * Now build the "overall" error message for PQresultErrorMessage. - */ - resetPQExpBuffer(&workBuf); - pqBuildErrorMessage3(&workBuf, res, conn->verbosity, conn->show_context); - - /* - * Either save error as current async result, or just emit the notice. - */ - if (isError) - { - if (res) - res->errMsg = pqResultStrdup(res, workBuf.data); - pqClearAsyncResult(conn); - conn->result = res; - if (PQExpBufferDataBroken(workBuf)) - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("out of memory")); - else - appendPQExpBufferStr(&conn->errorMessage, workBuf.data); - } - else - { - /* if we couldn't allocate the result set, just discard the NOTICE */ - if (res) - { - /* We can cheat a little here and not copy the message. */ - res->errMsg = workBuf.data; - if (res->noticeHooks.noticeRec != NULL) - (*res->noticeHooks.noticeRec) (res->noticeHooks.noticeRecArg, res); - PQclear(res); - } - } - - termPQExpBuffer(&workBuf); - return 0; - -fail: - PQclear(res); - termPQExpBuffer(&workBuf); - return EOF; -} - -/* - * Construct an error message from the fields in the given PGresult, - * appending it to the contents of "msg". - */ -void -pqBuildErrorMessage3(PQExpBuffer msg, const PGresult *res, - PGVerbosity verbosity, PGContextVisibility show_context) -{ - const char *val; - const char *querytext = NULL; - int querypos = 0; - - /* If we couldn't allocate a PGresult, just say "out of memory" */ - if (res == NULL) - { - appendPQExpBuffer(msg, libpq_gettext("out of memory\n")); - return; - } - - /* - * If we don't have any broken-down fields, just return the base message. - * This mainly applies if we're given a libpq-generated error result. - */ - if (res->errFields == NULL) - { - if (res->errMsg && res->errMsg[0]) - appendPQExpBufferStr(msg, res->errMsg); - else - appendPQExpBuffer(msg, libpq_gettext("no error message available\n")); - return; - } - - /* Else build error message from relevant fields */ - val = PQresultErrorField(res, PG_DIAG_SEVERITY); - if (val) - appendPQExpBuffer(msg, "%s: ", val); - if (verbosity == PQERRORS_VERBOSE) - { - val = PQresultErrorField(res, PG_DIAG_SQLSTATE); - if (val) - appendPQExpBuffer(msg, "%s: ", val); - } - val = PQresultErrorField(res, PG_DIAG_MESSAGE_PRIMARY); - if (val) - appendPQExpBufferStr(msg, val); - val = PQresultErrorField(res, PG_DIAG_STATEMENT_POSITION); - if (val) - { - if (verbosity != PQERRORS_TERSE && res->errQuery != NULL) - { - /* emit position as a syntax cursor display */ - querytext = res->errQuery; - querypos = atoi(val); - } - else - { - /* emit position as text addition to primary message */ - /* translator: %s represents a digit string */ - appendPQExpBuffer(msg, libpq_gettext(" at character %s"), - val); - } - } - else - { - val = PQresultErrorField(res, PG_DIAG_INTERNAL_POSITION); - if (val) - { - querytext = PQresultErrorField(res, PG_DIAG_INTERNAL_QUERY); - if (verbosity != PQERRORS_TERSE && querytext != NULL) - { - /* emit position as a syntax cursor display */ - querypos = atoi(val); - } - else - { - /* emit position as text addition to primary message */ - /* translator: %s represents a digit string */ - appendPQExpBuffer(msg, libpq_gettext(" at character %s"), - val); - } - } - } - appendPQExpBufferChar(msg, '\n'); - if (verbosity != PQERRORS_TERSE) - { - if (querytext && querypos > 0) - reportErrorPosition(msg, querytext, querypos, - res->client_encoding); - val = PQresultErrorField(res, PG_DIAG_MESSAGE_DETAIL); - if (val) - appendPQExpBuffer(msg, libpq_gettext("DETAIL: %s\n"), val); - val = PQresultErrorField(res, PG_DIAG_MESSAGE_HINT); - if (val) - appendPQExpBuffer(msg, libpq_gettext("HINT: %s\n"), val); - val = PQresultErrorField(res, PG_DIAG_INTERNAL_QUERY); - if (val) - appendPQExpBuffer(msg, libpq_gettext("QUERY: %s\n"), val); - if (show_context == PQSHOW_CONTEXT_ALWAYS || - (show_context == PQSHOW_CONTEXT_ERRORS && - res->resultStatus == PGRES_FATAL_ERROR)) - { - val = PQresultErrorField(res, PG_DIAG_CONTEXT); - if (val) - appendPQExpBuffer(msg, libpq_gettext("CONTEXT: %s\n"), - val); - } - } - if (verbosity == PQERRORS_VERBOSE) - { - val = PQresultErrorField(res, PG_DIAG_SCHEMA_NAME); - if (val) - appendPQExpBuffer(msg, - libpq_gettext("SCHEMA NAME: %s\n"), val); - val = PQresultErrorField(res, PG_DIAG_TABLE_NAME); - if (val) - appendPQExpBuffer(msg, - libpq_gettext("TABLE NAME: %s\n"), val); - val = PQresultErrorField(res, PG_DIAG_COLUMN_NAME); - if (val) - appendPQExpBuffer(msg, - libpq_gettext("COLUMN NAME: %s\n"), val); - val = PQresultErrorField(res, PG_DIAG_DATATYPE_NAME); - if (val) - appendPQExpBuffer(msg, - libpq_gettext("DATATYPE NAME: %s\n"), val); - val = PQresultErrorField(res, PG_DIAG_CONSTRAINT_NAME); - if (val) - appendPQExpBuffer(msg, - libpq_gettext("CONSTRAINT NAME: %s\n"), val); - } - if (verbosity == PQERRORS_VERBOSE) - { - const char *valf; - const char *vall; - - valf = PQresultErrorField(res, PG_DIAG_SOURCE_FILE); - vall = PQresultErrorField(res, PG_DIAG_SOURCE_LINE); - val = PQresultErrorField(res, PG_DIAG_SOURCE_FUNCTION); - if (val || valf || vall) - { - appendPQExpBufferStr(msg, libpq_gettext("LOCATION: ")); - if (val) - appendPQExpBuffer(msg, libpq_gettext("%s, "), val); - if (valf && vall) /* unlikely we'd have just one */ - appendPQExpBuffer(msg, libpq_gettext("%s:%s"), - valf, vall); - appendPQExpBufferChar(msg, '\n'); - } - } -} - -/* - * Add an error-location display to the error message under construction. - * - * The cursor location is measured in logical characters; the query string - * is presumed to be in the specified encoding. - */ -static void -reportErrorPosition(PQExpBuffer msg, const char *query, int loc, int encoding) -{ -#define DISPLAY_SIZE 60 /* screen width limit, in screen cols */ -#define MIN_RIGHT_CUT 10 /* try to keep this far away from EOL */ - - char *wquery; - int slen, - cno, - i, - *qidx, - *scridx, - qoffset, - scroffset, - ibeg, - iend, - loc_line; - bool mb_encoding, - beg_trunc, - end_trunc; - - /* Convert loc from 1-based to 0-based; no-op if out of range */ - loc--; - if (loc < 0) - return; - - /* Need a writable copy of the query */ - wquery = strdup(query); - if (wquery == NULL) - return; /* fail silently if out of memory */ - - /* - * Each character might occupy multiple physical bytes in the string, and - * in some Far Eastern character sets it might take more than one screen - * column as well. We compute the starting byte offset and starting - * screen column of each logical character, and store these in qidx[] and - * scridx[] respectively. - */ - - /* we need a safe allocation size... */ - slen = strlen(wquery) + 1; - - qidx = (int *) malloc(slen * sizeof(int)); - if (qidx == NULL) - { - free(wquery); - return; - } - scridx = (int *) malloc(slen * sizeof(int)); - if (scridx == NULL) - { - free(qidx); - free(wquery); - return; - } - - /* We can optimize a bit if it's a single-byte encoding */ - mb_encoding = (pg_encoding_max_length(encoding) != 1); - - /* - * Within the scanning loop, cno is the current character's logical - * number, qoffset is its offset in wquery, and scroffset is its starting - * logical screen column (all indexed from 0). "loc" is the logical - * character number of the error location. We scan to determine loc_line - * (the 1-based line number containing loc) and ibeg/iend (first character - * number and last+1 character number of the line containing loc). Note - * that qidx[] and scridx[] are filled only as far as iend. - */ - qoffset = 0; - scroffset = 0; - loc_line = 1; - ibeg = 0; - iend = -1; /* -1 means not set yet */ - - for (cno = 0; wquery[qoffset] != '\0'; cno++) - { - char ch = wquery[qoffset]; - - qidx[cno] = qoffset; - scridx[cno] = scroffset; - - /* - * Replace tabs with spaces in the writable copy. (Later we might - * want to think about coping with their variable screen width, but - * not today.) - */ - if (ch == '\t') - wquery[qoffset] = ' '; - - /* - * If end-of-line, count lines and mark positions. Each \r or \n - * counts as a line except when \r \n appear together. - */ - else if (ch == '\r' || ch == '\n') - { - if (cno < loc) - { - if (ch == '\r' || - cno == 0 || - wquery[qidx[cno - 1]] != '\r') - loc_line++; - /* extract beginning = last line start before loc. */ - ibeg = cno + 1; - } - else - { - /* set extract end. */ - iend = cno; - /* done scanning. */ - break; - } - } - - /* Advance */ - if (mb_encoding) - { - int w; - - w = pg_encoding_dsplen(encoding, &wquery[qoffset]); - /* treat any non-tab control chars as width 1 */ - if (w <= 0) - w = 1; - scroffset += w; - qoffset += pg_encoding_mblen(encoding, &wquery[qoffset]); - } - else - { - /* We assume wide chars only exist in multibyte encodings */ - scroffset++; - qoffset++; - } - } - /* Fix up if we didn't find an end-of-line after loc */ - if (iend < 0) - { - iend = cno; /* query length in chars, +1 */ - qidx[iend] = qoffset; - scridx[iend] = scroffset; - } - - /* Print only if loc is within computed query length */ - if (loc <= cno) - { - /* If the line extracted is too long, we truncate it. */ - beg_trunc = false; - end_trunc = false; - if (scridx[iend] - scridx[ibeg] > DISPLAY_SIZE) - { - /* - * We first truncate right if it is enough. This code might be - * off a space or so on enforcing MIN_RIGHT_CUT if there's a wide - * character right there, but that should be okay. - */ - if (scridx[ibeg] + DISPLAY_SIZE >= scridx[loc] + MIN_RIGHT_CUT) - { - while (scridx[iend] - scridx[ibeg] > DISPLAY_SIZE) - iend--; - end_trunc = true; - } - else - { - /* Truncate right if not too close to loc. */ - while (scridx[loc] + MIN_RIGHT_CUT < scridx[iend]) - { - iend--; - end_trunc = true; - } - - /* Truncate left if still too long. */ - while (scridx[iend] - scridx[ibeg] > DISPLAY_SIZE) - { - ibeg++; - beg_trunc = true; - } - } - } - - /* truncate working copy at desired endpoint */ - wquery[qidx[iend]] = '\0'; - - /* Begin building the finished message. */ - i = msg->len; - appendPQExpBuffer(msg, libpq_gettext("LINE %d: "), loc_line); - if (beg_trunc) - appendPQExpBufferStr(msg, "..."); - - /* - * While we have the prefix in the msg buffer, compute its screen - * width. - */ - scroffset = 0; - for (; i < msg->len; i += pg_encoding_mblen(encoding, &msg->data[i])) - { - int w = pg_encoding_dsplen(encoding, &msg->data[i]); - - if (w <= 0) - w = 1; - scroffset += w; - } - - /* Finish up the LINE message line. */ - appendPQExpBufferStr(msg, &wquery[qidx[ibeg]]); - if (end_trunc) - appendPQExpBufferStr(msg, "..."); - appendPQExpBufferChar(msg, '\n'); - - /* Now emit the cursor marker line. */ - scroffset += scridx[loc] - scridx[ibeg]; - for (i = 0; i < scroffset; i++) - appendPQExpBufferChar(msg, ' '); - appendPQExpBufferChar(msg, '^'); - appendPQExpBufferChar(msg, '\n'); - } - - /* Clean up. */ - free(scridx); - free(qidx); - free(wquery); -} - - -/* - * Attempt to read a ParameterStatus message. - * This is possible in several places, so we break it out as a subroutine. - * Entry: 'S' message type and length have already been consumed. - * Exit: returns 0 if successfully consumed message. - * returns EOF if not enough data. - */ -static int -getParameterStatus(PGconn *conn) -{ - PQExpBufferData valueBuf; - - /* Get the parameter name */ - if (pqGets(&conn->workBuffer, conn)) - return EOF; - /* Get the parameter value (could be large) */ - initPQExpBuffer(&valueBuf); - if (pqGets(&valueBuf, conn)) - { - termPQExpBuffer(&valueBuf); - return EOF; - } - /* And save it */ - pqSaveParameterStatus(conn, conn->workBuffer.data, valueBuf.data); - termPQExpBuffer(&valueBuf); - return 0; -} - - -/* - * Attempt to read a Notify response message. - * This is possible in several places, so we break it out as a subroutine. - * Entry: 'A' message type and length have already been consumed. - * Exit: returns 0 if successfully consumed Notify message. - * returns EOF if not enough data. - */ -static int -getNotify(PGconn *conn) -{ - int be_pid; - char *svname; - int nmlen; - int extralen; - PGnotify *newNotify; - - if (pqGetInt(&be_pid, 4, conn)) - return EOF; - if (pqGets(&conn->workBuffer, conn)) - return EOF; - /* must save name while getting extra string */ - svname = strdup(conn->workBuffer.data); - if (!svname) - return EOF; - if (pqGets(&conn->workBuffer, conn)) - { - free(svname); - return EOF; - } - - /* - * Store the strings right after the PQnotify structure so it can all be - * freed at once. We don't use NAMEDATALEN because we don't want to tie - * this interface to a specific server name length. - */ - nmlen = strlen(svname); - extralen = strlen(conn->workBuffer.data); - newNotify = (PGnotify *) malloc(sizeof(PGnotify) + nmlen + extralen + 2); - if (newNotify) - { - newNotify->relname = (char *) newNotify + sizeof(PGnotify); - strcpy(newNotify->relname, svname); - newNotify->extra = newNotify->relname + nmlen + 1; - strcpy(newNotify->extra, conn->workBuffer.data); - newNotify->be_pid = be_pid; - newNotify->next = NULL; - if (conn->notifyTail) - conn->notifyTail->next = newNotify; - else - conn->notifyHead = newNotify; - conn->notifyTail = newNotify; - } - - free(svname); - return 0; -} - -/* - * getCopyStart - process CopyInResponse, CopyOutResponse or - * CopyBothResponse message - * - * parseInput already read the message type and length. - */ -static int -getCopyStart(PGconn *conn, ExecStatusType copytype) -{ - PGresult *result; - int nfields; - int i; - - result = PQmakeEmptyPGresult(conn, copytype); - if (!result) - goto failure; - - if (pqGetc(&conn->copy_is_binary, conn)) - goto failure; - result->binary = conn->copy_is_binary; - /* the next two bytes are the number of fields */ - if (pqGetInt(&(result->numAttributes), 2, conn)) - goto failure; - nfields = result->numAttributes; - - /* allocate space for the attribute descriptors */ - if (nfields > 0) - { - result->attDescs = (PGresAttDesc *) - pqResultAlloc(result, nfields * sizeof(PGresAttDesc), TRUE); - if (!result->attDescs) - goto failure; - MemSet(result->attDescs, 0, nfields * sizeof(PGresAttDesc)); - } - - for (i = 0; i < nfields; i++) - { - int format; - - if (pqGetInt(&format, 2, conn)) - goto failure; - - /* - * Since pqGetInt treats 2-byte integers as unsigned, we need to - * coerce these results to signed form. - */ - format = (int) ((int16) format); - result->attDescs[i].format = format; - } - - /* Success! */ - conn->result = result; - return 0; - -failure: - PQclear(result); - return EOF; -} - -/* - * getReadyForQuery - process ReadyForQuery message - */ -static int -getReadyForQuery(PGconn *conn) -{ - char xact_status; - - if (pqGetc(&xact_status, conn)) - return EOF; - switch (xact_status) - { - case 'I': - conn->xactStatus = PQTRANS_IDLE; - break; - case 'T': - conn->xactStatus = PQTRANS_INTRANS; - break; - case 'E': - conn->xactStatus = PQTRANS_INERROR; - break; - default: - conn->xactStatus = PQTRANS_UNKNOWN; - break; - } - - return 0; -} - -/* - * getCopyDataMessage - fetch next CopyData message, process async messages - * - * Returns length word of CopyData message (> 0), or 0 if no complete - * message available, -1 if end of copy, -2 if error. - */ -static int -getCopyDataMessage(PGconn *conn) -{ - char id; - int msgLength; - int avail; - - for (;;) - { - /* - * Do we have the next input message? To make life simpler for async - * callers, we keep returning 0 until the next message is fully - * available, even if it is not Copy Data. - */ - conn->inCursor = conn->inStart; - if (pqGetc(&id, conn)) - return 0; - if (pqGetInt(&msgLength, 4, conn)) - return 0; - if (msgLength < 4) - { - handleSyncLoss(conn, id, msgLength); - return -2; - } - avail = conn->inEnd - conn->inCursor; - if (avail < msgLength - 4) - { - /* - * Before returning, enlarge the input buffer if needed to hold - * the whole message. See notes in parseInput. - */ - if (pqCheckInBufferSpace(conn->inCursor + (size_t) msgLength - 4, - conn)) - { - /* - * XXX add some better recovery code... plan is to skip over - * the message using its length, then report an error. For the - * moment, just treat this like loss of sync (which indeed it - * might be!) - */ - handleSyncLoss(conn, id, msgLength); - return -2; - } - return 0; - } - - /* - * If it's a legitimate async message type, process it. (NOTIFY - * messages are not currently possible here, but we handle them for - * completeness.) Otherwise, if it's anything except Copy Data, - * report end-of-copy. - */ - switch (id) - { - case 'A': /* NOTIFY */ - if (getNotify(conn)) - return 0; - break; - case 'N': /* NOTICE */ - if (pqGetErrorNotice3(conn, false)) - return 0; - break; - case 'S': /* ParameterStatus */ - if (getParameterStatus(conn)) - return 0; - break; - case 'd': /* Copy Data, pass it back to caller */ - return msgLength; - case 'c': - - /* - * If this is a CopyDone message, exit COPY_OUT mode and let - * caller read status with PQgetResult(). If we're in - * COPY_BOTH mode, return to COPY_IN mode. - */ - if (conn->asyncStatus == PGASYNC_COPY_BOTH) - conn->asyncStatus = PGASYNC_COPY_IN; - else - conn->asyncStatus = PGASYNC_BUSY; - return -1; - default: /* treat as end of copy */ - - /* - * Any other message terminates either COPY_IN or COPY_BOTH - * mode. - */ - conn->asyncStatus = PGASYNC_BUSY; - return -1; - } - - /* Drop the processed message and loop around for another */ - conn->inStart = conn->inCursor; - } -} - -/* - * PQgetCopyData - read a row of data from the backend during COPY OUT - * or COPY BOTH - * - * If successful, sets *buffer to point to a malloc'd row of data, and - * returns row length (always > 0) as result. - * Returns 0 if no row available yet (only possible if async is true), - * -1 if end of copy (consult PQgetResult), or -2 if error (consult - * PQerrorMessage). - */ -int -pqGetCopyData3(PGconn *conn, char **buffer, int async) -{ - int msgLength; - - for (;;) - { - /* - * Collect the next input message. To make life simpler for async - * callers, we keep returning 0 until the next message is fully - * available, even if it is not Copy Data. - */ - msgLength = getCopyDataMessage(conn); - if (msgLength < 0) - return msgLength; /* end-of-copy or error */ - if (msgLength == 0) - { - /* Don't block if async read requested */ - if (async) - return 0; - /* Need to load more data */ - if (pqWait(TRUE, FALSE, conn) || - pqReadData(conn) < 0) - return -2; - continue; - } - - /* - * Drop zero-length messages (shouldn't happen anyway). Otherwise - * pass the data back to the caller. - */ - msgLength -= 4; - if (msgLength > 0) - { - *buffer = (char *) malloc(msgLength + 1); - if (*buffer == NULL) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("out of memory\n")); - return -2; - } - memcpy(*buffer, &conn->inBuffer[conn->inCursor], msgLength); - (*buffer)[msgLength] = '\0'; /* Add terminating null */ - - /* Mark message consumed */ - conn->inStart = conn->inCursor + msgLength; - - return msgLength; - } - - /* Empty, so drop it and loop around for another */ - conn->inStart = conn->inCursor; - } -} - -/* - * PQgetline - gets a newline-terminated string from the backend. - * - * See fe-exec.c for documentation. - */ -int -pqGetline3(PGconn *conn, char *s, int maxlen) -{ - int status; - - if (conn->sock == PGINVALID_SOCKET || - (conn->asyncStatus != PGASYNC_COPY_OUT && - conn->asyncStatus != PGASYNC_COPY_BOTH) || - conn->copy_is_binary) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("PQgetline: not doing text COPY OUT\n")); - *s = '\0'; - return EOF; - } - - while ((status = PQgetlineAsync(conn, s, maxlen - 1)) == 0) - { - /* need to load more data */ - if (pqWait(TRUE, FALSE, conn) || - pqReadData(conn) < 0) - { - *s = '\0'; - return EOF; - } - } - - if (status < 0) - { - /* End of copy detected; gin up old-style terminator */ - strcpy(s, "\\."); - return 0; - } - - /* Add null terminator, and strip trailing \n if present */ - if (s[status - 1] == '\n') - { - s[status - 1] = '\0'; - return 0; - } - else - { - s[status] = '\0'; - return 1; - } -} - -/* - * PQgetlineAsync - gets a COPY data row without blocking. - * - * See fe-exec.c for documentation. - */ -int -pqGetlineAsync3(PGconn *conn, char *buffer, int bufsize) -{ - int msgLength; - int avail; - - if (conn->asyncStatus != PGASYNC_COPY_OUT - && conn->asyncStatus != PGASYNC_COPY_BOTH) - return -1; /* we are not doing a copy... */ - - /* - * Recognize the next input message. To make life simpler for async - * callers, we keep returning 0 until the next message is fully available - * even if it is not Copy Data. This should keep PQendcopy from blocking. - * (Note: unlike pqGetCopyData3, we do not change asyncStatus here.) - */ - msgLength = getCopyDataMessage(conn); - if (msgLength < 0) - return -1; /* end-of-copy or error */ - if (msgLength == 0) - return 0; /* no data yet */ - - /* - * Move data from libpq's buffer to the caller's. In the case where a - * prior call found the caller's buffer too small, we use - * conn->copy_already_done to remember how much of the row was already - * returned to the caller. - */ - conn->inCursor += conn->copy_already_done; - avail = msgLength - 4 - conn->copy_already_done; - if (avail <= bufsize) - { - /* Able to consume the whole message */ - memcpy(buffer, &conn->inBuffer[conn->inCursor], avail); - /* Mark message consumed */ - conn->inStart = conn->inCursor + avail; - /* Reset state for next time */ - conn->copy_already_done = 0; - return avail; - } - else - { - /* We must return a partial message */ - memcpy(buffer, &conn->inBuffer[conn->inCursor], bufsize); - /* The message is NOT consumed from libpq's buffer */ - conn->copy_already_done += bufsize; - return bufsize; - } -} - -/* - * PQendcopy - * - * See fe-exec.c for documentation. - */ -int -pqEndcopy3(PGconn *conn) -{ - PGresult *result; - - if (conn->asyncStatus != PGASYNC_COPY_IN && - conn->asyncStatus != PGASYNC_COPY_OUT && - conn->asyncStatus != PGASYNC_COPY_BOTH) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("no COPY in progress\n")); - return 1; - } - - /* Send the CopyDone message if needed */ - if (conn->asyncStatus == PGASYNC_COPY_IN || - conn->asyncStatus == PGASYNC_COPY_BOTH) - { - if (pqPutMsgStart('c', false, conn) < 0 || - pqPutMsgEnd(conn) < 0) - return 1; - - /* - * If we sent the COPY command in extended-query mode, we must issue a - * Sync as well. - */ - if (conn->queryclass != PGQUERY_SIMPLE) - { - if (pqPutMsgStart('S', false, conn) < 0 || - pqPutMsgEnd(conn) < 0) - return 1; - } - } - - /* - * make sure no data is waiting to be sent, abort if we are non-blocking - * and the flush fails - */ - if (pqFlush(conn) && pqIsnonblocking(conn)) - return 1; - - /* Return to active duty */ - conn->asyncStatus = PGASYNC_BUSY; - resetPQExpBuffer(&conn->errorMessage); - - /* - * Non blocking connections may have to abort at this point. If everyone - * played the game there should be no problem, but in error scenarios the - * expected messages may not have arrived yet. (We are assuming that the - * backend's packetizing will ensure that CommandComplete arrives along - * with the CopyDone; are there corner cases where that doesn't happen?) - */ - if (pqIsnonblocking(conn) && PQisBusy(conn)) - return 1; - - /* Wait for the completion response */ - result = PQgetResult(conn); - - /* Expecting a successful result */ - if (result && result->resultStatus == PGRES_COMMAND_OK) - { - PQclear(result); - return 0; - } - - /* - * Trouble. For backwards-compatibility reasons, we issue the error - * message as if it were a notice (would be nice to get rid of this - * silliness, but too many apps probably don't handle errors from - * PQendcopy reasonably). Note that the app can still obtain the error - * status from the PGconn object. - */ - if (conn->errorMessage.len > 0) - { - /* We have to strip the trailing newline ... pain in neck... */ - char svLast = conn->errorMessage.data[conn->errorMessage.len - 1]; - - if (svLast == '\n') - conn->errorMessage.data[conn->errorMessage.len - 1] = '\0'; - pqInternalNotice(&conn->noticeHooks, "%s", conn->errorMessage.data); - conn->errorMessage.data[conn->errorMessage.len - 1] = svLast; - } - - PQclear(result); - - return 1; -} - - -/* - * PQfn - Send a function call to the POSTGRES backend. - * - * See fe-exec.c for documentation. - */ -PGresult * -pqFunctionCall3(PGconn *conn, Oid fnid, - int *result_buf, int *actual_result_len, - int result_is_int, - const PQArgBlock *args, int nargs) -{ - bool needInput = false; - ExecStatusType status = PGRES_FATAL_ERROR; - char id; - int msgLength; - int avail; - int i; - - /* PQfn already validated connection state */ - - if (pqPutMsgStart('F', false, conn) < 0 || /* function call msg */ - pqPutInt(fnid, 4, conn) < 0 || /* function id */ - pqPutInt(1, 2, conn) < 0 || /* # of format codes */ - pqPutInt(1, 2, conn) < 0 || /* format code: BINARY */ - pqPutInt(nargs, 2, conn) < 0) /* # of args */ - { - pqHandleSendFailure(conn); - return NULL; - } - - for (i = 0; i < nargs; ++i) - { /* len.int4 + contents */ - if (pqPutInt(args[i].len, 4, conn)) - { - pqHandleSendFailure(conn); - return NULL; - } - if (args[i].len == -1) - continue; /* it's NULL */ - - if (args[i].isint) - { - if (pqPutInt(args[i].u.integer, args[i].len, conn)) - { - pqHandleSendFailure(conn); - return NULL; - } - } - else - { - if (pqPutnchar((char *) args[i].u.ptr, args[i].len, conn)) - { - pqHandleSendFailure(conn); - return NULL; - } - } - } - - if (pqPutInt(1, 2, conn) < 0) /* result format code: BINARY */ - { - pqHandleSendFailure(conn); - return NULL; - } - - if (pqPutMsgEnd(conn) < 0 || - pqFlush(conn)) - { - pqHandleSendFailure(conn); - return NULL; - } - - for (;;) - { - if (needInput) - { - /* Wait for some data to arrive (or for the channel to close) */ - if (pqWait(TRUE, FALSE, conn) || - pqReadData(conn) < 0) - break; - } - - /* - * Scan the message. If we run out of data, loop around to try again. - */ - needInput = true; - - conn->inCursor = conn->inStart; - if (pqGetc(&id, conn)) - continue; - if (pqGetInt(&msgLength, 4, conn)) - continue; - - /* - * Try to validate message type/length here. A length less than 4 is - * definitely broken. Large lengths should only be believed for a few - * message types. - */ - if (msgLength < 4) - { - handleSyncLoss(conn, id, msgLength); - break; - } - if (msgLength > 30000 && !VALID_LONG_MESSAGE_TYPE(id)) - { - handleSyncLoss(conn, id, msgLength); - break; - } - - /* - * Can't process if message body isn't all here yet. - */ - msgLength -= 4; - avail = conn->inEnd - conn->inCursor; - if (avail < msgLength) - { - /* - * Before looping, enlarge the input buffer if needed to hold the - * whole message. See notes in parseInput. - */ - if (pqCheckInBufferSpace(conn->inCursor + (size_t) msgLength, - conn)) - { - /* - * XXX add some better recovery code... plan is to skip over - * the message using its length, then report an error. For the - * moment, just treat this like loss of sync (which indeed it - * might be!) - */ - handleSyncLoss(conn, id, msgLength); - break; - } - continue; - } - - /* - * We should see V or E response to the command, but might get N - * and/or A notices first. We also need to swallow the final Z before - * returning. - */ - switch (id) - { - case 'V': /* function result */ - if (pqGetInt(actual_result_len, 4, conn)) - continue; - if (*actual_result_len != -1) - { - if (result_is_int) - { - if (pqGetInt(result_buf, *actual_result_len, conn)) - continue; - } - else - { - if (pqGetnchar((char *) result_buf, - *actual_result_len, - conn)) - continue; - } - } - /* correctly finished function result message */ - status = PGRES_COMMAND_OK; - break; - case 'E': /* error return */ - if (pqGetErrorNotice3(conn, true)) - continue; - status = PGRES_FATAL_ERROR; - break; - case 'A': /* notify message */ - /* handle notify and go back to processing return values */ - if (getNotify(conn)) - continue; - break; - case 'N': /* notice */ - /* handle notice and go back to processing return values */ - if (pqGetErrorNotice3(conn, false)) - continue; - break; - case 'Z': /* backend is ready for new query */ - if (getReadyForQuery(conn)) - continue; - /* consume the message and exit */ - conn->inStart += 5 + msgLength; - /* if we saved a result object (probably an error), use it */ - if (conn->result) - return pqPrepareAsyncResult(conn); - return PQmakeEmptyPGresult(conn, status); - case 'S': /* parameter status */ - if (getParameterStatus(conn)) - continue; - break; - default: - /* The backend violates the protocol. */ - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("protocol error: id=0x%x\n"), - id); - pqSaveErrorResult(conn); - /* trust the specified message length as what to skip */ - conn->inStart += 5 + msgLength; - return pqPrepareAsyncResult(conn); - } - /* Completed this message, keep going */ - /* trust the specified message length as what to skip */ - conn->inStart += 5 + msgLength; - needInput = false; - } - - /* - * We fall out of the loop only upon failing to read data. - * conn->errorMessage has been set by pqWait or pqReadData. We want to - * append it to any already-received error message. - */ - pqSaveErrorResult(conn); - return pqPrepareAsyncResult(conn); -} - - -/* - * Construct startup packet - * - * Returns a malloc'd packet buffer, or NULL if out of memory - */ -char * -pqBuildStartupPacket3(PGconn *conn, int *packetlen, - const PQEnvironmentOption *options) -{ - char *startpacket; - - *packetlen = build_startup_packet(conn, NULL, options); - startpacket = (char *) malloc(*packetlen); - if (!startpacket) - return NULL; - *packetlen = build_startup_packet(conn, startpacket, options); - return startpacket; -} - -/* - * Build a startup packet given a filled-in PGconn structure. - * - * We need to figure out how much space is needed, then fill it in. - * To avoid duplicate logic, this routine is called twice: the first time - * (with packet == NULL) just counts the space needed, the second time - * (with packet == allocated space) fills it in. Return value is the number - * of bytes used. - */ -static int -build_startup_packet(const PGconn *conn, char *packet, - const PQEnvironmentOption *options) -{ - int packet_len = 0; - const PQEnvironmentOption *next_eo; - const char *val; - - /* Protocol version comes first. */ - if (packet) - { - ProtocolVersion pv = htonl(conn->pversion); - - memcpy(packet + packet_len, &pv, sizeof(ProtocolVersion)); - } - packet_len += sizeof(ProtocolVersion); - - /* Add user name, database name, options */ - -#define ADD_STARTUP_OPTION(optname, optval) \ - do { \ - if (packet) \ - strcpy(packet + packet_len, optname); \ - packet_len += strlen(optname) + 1; \ - if (packet) \ - strcpy(packet + packet_len, optval); \ - packet_len += strlen(optval) + 1; \ - } while(0) - - if (conn->pguser && conn->pguser[0]) - ADD_STARTUP_OPTION("user", conn->pguser); - if (conn->dbName && conn->dbName[0]) - ADD_STARTUP_OPTION("database", conn->dbName); - if (conn->replication && conn->replication[0]) - ADD_STARTUP_OPTION("replication", conn->replication); - if (conn->pgoptions && conn->pgoptions[0]) - ADD_STARTUP_OPTION("options", conn->pgoptions); - if (conn->send_appname) - { - /* Use appname if present, otherwise use fallback */ - val = conn->appname ? conn->appname : conn->fbappname; - if (val && val[0]) - ADD_STARTUP_OPTION("application_name", val); - } - - if (conn->client_encoding_initial && conn->client_encoding_initial[0]) - ADD_STARTUP_OPTION("client_encoding", conn->client_encoding_initial); - - /* Add any environment-driven GUC settings needed */ - for (next_eo = options; next_eo->envName; next_eo++) - { - if ((val = getenv(next_eo->envName)) != NULL) - { - if (pg_strcasecmp(val, "default") != 0) - ADD_STARTUP_OPTION(next_eo->pgName, val); - } - } - - /* Add trailing terminator */ - if (packet) - packet[packet_len] = '\0'; - packet_len++; - - return packet_len; -} diff --git a/libpq/fe-secure.c b/libpq/fe-secure.c deleted file mode 100644 index 94e47a5..0000000 --- a/libpq/fe-secure.c +++ /dev/null @@ -1,505 +0,0 @@ -/*------------------------------------------------------------------------- - * - * fe-secure.c - * functions related to setting up a secure connection to the backend. - * Secure connections are expected to provide confidentiality, - * message integrity and endpoint authentication. - * - * - * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * - * IDENTIFICATION - * src/interfaces/libpq/fe-secure.c - * - * NOTES - * - * We don't provide informational callbacks here (like - * info_cb() in be-secure.c), since there's no good mechanism to - * display such information to the user. - * - *------------------------------------------------------------------------- - */ - -#include "postgres_fe.h" - -#include <signal.h> -#include <fcntl.h> -#include <ctype.h> - -#include "libpq-fe.h" -#include "fe-auth.h" -#include "libpq-int.h" - -#ifdef WIN32 -#include "win32.h" -#else -#include <sys/socket.h> -#include <unistd.h> -#include <netdb.h> -#include <netinet/in.h> -#ifdef HAVE_NETINET_TCP_H -#include <netinet/tcp.h> -#endif -#include <arpa/inet.h> -#endif - -#include <sys/stat.h> - -#ifdef ENABLE_THREAD_SAFETY -#ifdef WIN32 -#include "pthread-win32.h" -#else -#include <pthread.h> -#endif -#endif - -/* - * Macros to handle disabling and then restoring the state of SIGPIPE handling. - * On Windows, these are all no-ops since there's no SIGPIPEs. - */ - -#ifndef WIN32 - -#define SIGPIPE_MASKED(conn) ((conn)->sigpipe_so || (conn)->sigpipe_flag) - -#ifdef ENABLE_THREAD_SAFETY - -struct sigpipe_info -{ - sigset_t oldsigmask; - bool sigpipe_pending; - bool got_epipe; -}; - -#define DECLARE_SIGPIPE_INFO(spinfo) struct sigpipe_info spinfo - -#define DISABLE_SIGPIPE(conn, spinfo, failaction) \ - do { \ - (spinfo).got_epipe = false; \ - if (!SIGPIPE_MASKED(conn)) \ - { \ - if (pq_block_sigpipe(&(spinfo).oldsigmask, \ - &(spinfo).sigpipe_pending) < 0) \ - failaction; \ - } \ - } while (0) - -#define REMEMBER_EPIPE(spinfo, cond) \ - do { \ - if (cond) \ - (spinfo).got_epipe = true; \ - } while (0) - -#define RESTORE_SIGPIPE(conn, spinfo) \ - do { \ - if (!SIGPIPE_MASKED(conn)) \ - pq_reset_sigpipe(&(spinfo).oldsigmask, (spinfo).sigpipe_pending, \ - (spinfo).got_epipe); \ - } while (0) -#else /* !ENABLE_THREAD_SAFETY */ - -#define DECLARE_SIGPIPE_INFO(spinfo) pqsigfunc spinfo = NULL - -#define DISABLE_SIGPIPE(conn, spinfo, failaction) \ - do { \ - if (!SIGPIPE_MASKED(conn)) \ - spinfo = pqsignal(SIGPIPE, SIG_IGN); \ - } while (0) - -#define REMEMBER_EPIPE(spinfo, cond) - -#define RESTORE_SIGPIPE(conn, spinfo) \ - do { \ - if (!SIGPIPE_MASKED(conn)) \ - pqsignal(SIGPIPE, spinfo); \ - } while (0) -#endif /* ENABLE_THREAD_SAFETY */ -#else /* WIN32 */ - -#define DECLARE_SIGPIPE_INFO(spinfo) -#define DISABLE_SIGPIPE(conn, spinfo, failaction) -#define REMEMBER_EPIPE(spinfo, cond) -#define RESTORE_SIGPIPE(conn, spinfo) -#endif /* WIN32 */ - -/* ------------------------------------------------------------ */ -/* Procedures common to all secure sessions */ -/* ------------------------------------------------------------ */ - - -/* - * Exported function to allow application to tell us it's already - * initialized OpenSSL. - */ -void -PQinitSSL(int do_init) -{ -#ifdef USE_SSL - pgtls_init_library(do_init, do_init); -#endif -} - -/* - * Exported function to allow application to tell us it's already - * initialized OpenSSL and/or libcrypto. - */ -void -PQinitOpenSSL(int do_ssl, int do_crypto) -{ -#ifdef USE_SSL - pgtls_init_library(do_ssl, do_crypto); -#endif -} - -/* - * Initialize global SSL context - */ -int -pqsecure_initialize(PGconn *conn) -{ - int r = 0; - -#ifdef USE_SSL - r = pgtls_init(conn); -#endif - - return r; -} - -/* - * Begin or continue negotiating a secure session. - */ -PostgresPollingStatusType -pqsecure_open_client(PGconn *conn) -{ -#ifdef USE_SSL - return pgtls_open_client(conn); -#else - /* shouldn't get here */ - return PGRES_POLLING_FAILED; -#endif -} - -/* - * Close secure session. - */ -void -pqsecure_close(PGconn *conn) -{ -#ifdef USE_SSL - if (conn->ssl_in_use) - pgtls_close(conn); -#endif -} - -/* - * Read data from a secure connection. - * - * On failure, this function is responsible for putting a suitable message - * into conn->errorMessage. The caller must still inspect errno, but only - * to determine whether to continue/retry after error. - */ -ssize_t -pqsecure_read(PGconn *conn, void *ptr, size_t len) -{ - ssize_t n; - -#ifdef USE_SSL - if (conn->ssl_in_use) - { - n = pgtls_read(conn, ptr, len); - } - else -#endif - { - n = pqsecure_raw_read(conn, ptr, len); - } - - return n; -} - -ssize_t -pqsecure_raw_read(PGconn *conn, void *ptr, size_t len) -{ - ssize_t n; - int result_errno = 0; - char sebuf[256]; - - n = recv(conn->sock, ptr, len, 0); - - if (n < 0) - { - result_errno = SOCK_ERRNO; - - /* Set error message if appropriate */ - switch (result_errno) - { -#ifdef EAGAIN - case EAGAIN: -#endif -#if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN)) - case EWOULDBLOCK: -#endif - case EINTR: - /* no error message, caller is expected to retry */ - break; - -#ifdef ECONNRESET - case ECONNRESET: - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext( - "server closed the connection unexpectedly\n" - "\tThis probably means the server terminated abnormally\n" - "\tbefore or while processing the request.\n")); - break; -#endif - - default: - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("could not receive data from server: %s\n"), - SOCK_STRERROR(result_errno, - sebuf, sizeof(sebuf))); - break; - } - } - - /* ensure we return the intended errno to caller */ - SOCK_ERRNO_SET(result_errno); - - return n; -} - -/* - * Write data to a secure connection. - * - * On failure, this function is responsible for putting a suitable message - * into conn->errorMessage. The caller must still inspect errno, but only - * to determine whether to continue/retry after error. - */ -ssize_t -pqsecure_write(PGconn *conn, const void *ptr, size_t len) -{ - ssize_t n; - -#ifdef USE_SSL - if (conn->ssl_in_use) - { - n = pgtls_write(conn, ptr, len); - } - else -#endif - { - n = pqsecure_raw_write(conn, ptr, len); - } - - return n; -} - -ssize_t -pqsecure_raw_write(PGconn *conn, const void *ptr, size_t len) -{ - ssize_t n; - int flags = 0; - int result_errno = 0; - char sebuf[256]; - - DECLARE_SIGPIPE_INFO(spinfo); - -#ifdef MSG_NOSIGNAL - if (conn->sigpipe_flag) - flags |= MSG_NOSIGNAL; - -retry_masked: -#endif /* MSG_NOSIGNAL */ - - DISABLE_SIGPIPE(conn, spinfo, return -1); - - n = send(conn->sock, ptr, len, flags); - - if (n < 0) - { - result_errno = SOCK_ERRNO; - - /* - * If we see an EINVAL, it may be because MSG_NOSIGNAL isn't available - * on this machine. So, clear sigpipe_flag so we don't try the flag - * again, and retry the send(). - */ -#ifdef MSG_NOSIGNAL - if (flags != 0 && result_errno == EINVAL) - { - conn->sigpipe_flag = false; - flags = 0; - goto retry_masked; - } -#endif /* MSG_NOSIGNAL */ - - /* Set error message if appropriate */ - switch (result_errno) - { -#ifdef EAGAIN - case EAGAIN: -#endif -#if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN)) - case EWOULDBLOCK: -#endif - case EINTR: - /* no error message, caller is expected to retry */ - break; - - case EPIPE: - /* Set flag for EPIPE */ - REMEMBER_EPIPE(spinfo, true); - /* FALL THRU */ - -#ifdef ECONNRESET - case ECONNRESET: -#endif - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext( - "server closed the connection unexpectedly\n" - "\tThis probably means the server terminated abnormally\n" - "\tbefore or while processing the request.\n")); - break; - - default: - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("could not send data to server: %s\n"), - SOCK_STRERROR(result_errno, - sebuf, sizeof(sebuf))); - break; - } - } - - RESTORE_SIGPIPE(conn, spinfo); - - /* ensure we return the intended errno to caller */ - SOCK_ERRNO_SET(result_errno); - - return n; -} - -/* Dummy versions of SSL info functions, when built without SSL support */ -#ifndef USE_SSL - -int -PQsslInUse(PGconn *conn) -{ - return 0; -} - -void * -PQgetssl(PGconn *conn) -{ - return NULL; -} - -void * -PQsslStruct(PGconn *conn, const char *struct_name) -{ - return NULL; -} - -const char * -PQsslAttribute(PGconn *conn, const char *attribute_name) -{ - return NULL; -} - -const char *const * -PQsslAttributeNames(PGconn *conn) -{ - static const char *const result[] = {NULL}; - - return result; -} -#endif /* USE_SSL */ - - -#if defined(ENABLE_THREAD_SAFETY) && !defined(WIN32) - -/* - * Block SIGPIPE for this thread. This prevents send()/write() from exiting - * the application. - */ -int -pq_block_sigpipe(sigset_t *osigset, bool *sigpipe_pending) -{ - sigset_t sigpipe_sigset; - sigset_t sigset; - - sigemptyset(&sigpipe_sigset); - sigaddset(&sigpipe_sigset, SIGPIPE); - - /* Block SIGPIPE and save previous mask for later reset */ - SOCK_ERRNO_SET(pthread_sigmask(SIG_BLOCK, &sigpipe_sigset, osigset)); - if (SOCK_ERRNO) - return -1; - - /* We can have a pending SIGPIPE only if it was blocked before */ - if (sigismember(osigset, SIGPIPE)) - { - /* Is there a pending SIGPIPE? */ - if (sigpending(&sigset) != 0) - return -1; - - if (sigismember(&sigset, SIGPIPE)) - *sigpipe_pending = true; - else - *sigpipe_pending = false; - } - else - *sigpipe_pending = false; - - return 0; -} - -/* - * Discard any pending SIGPIPE and reset the signal mask. - * - * Note: we are effectively assuming here that the C library doesn't queue - * up multiple SIGPIPE events. If it did, then we'd accidentally leave - * ours in the queue when an event was already pending and we got another. - * As long as it doesn't queue multiple events, we're OK because the caller - * can't tell the difference. - * - * The caller should say got_epipe = FALSE if it is certain that it - * didn't get an EPIPE error; in that case we'll skip the clear operation - * and things are definitely OK, queuing or no. If it got one or might have - * gotten one, pass got_epipe = TRUE. - * - * We do not want this to change errno, since if it did that could lose - * the error code from a preceding send(). We essentially assume that if - * we were able to do pq_block_sigpipe(), this can't fail. - */ -void -pq_reset_sigpipe(sigset_t *osigset, bool sigpipe_pending, bool got_epipe) -{ - int save_errno = SOCK_ERRNO; - int signo; - sigset_t sigset; - - /* Clear SIGPIPE only if none was pending */ - if (got_epipe && !sigpipe_pending) - { - if (sigpending(&sigset) == 0 && - sigismember(&sigset, SIGPIPE)) - { - sigset_t sigpipe_sigset; - - sigemptyset(&sigpipe_sigset); - sigaddset(&sigpipe_sigset, SIGPIPE); - - sigwait(&sigpipe_sigset, &signo); - } - } - - /* Restore saved block mask */ - pthread_sigmask(SIG_SETMASK, osigset, NULL); - - SOCK_ERRNO_SET(save_errno); -} - -#endif /* ENABLE_THREAD_SAFETY && !WIN32 */ diff --git a/libpq/getpeereid.c b/libpq/getpeereid.c deleted file mode 100644 index 5f8c0be..0000000 --- a/libpq/getpeereid.c +++ /dev/null @@ -1,80 +0,0 @@ -/*------------------------------------------------------------------------- - * - * getpeereid.c - * get peer userid for UNIX-domain socket connection - * - * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group - * - * - * IDENTIFICATION - * src/port/getpeereid.c - * - *------------------------------------------------------------------------- - */ - -#include "c.h" - -#include <sys/param.h> -#include <sys/socket.h> -#include <unistd.h> -#ifdef HAVE_SYS_UN_H -#include <sys/un.h> -#endif -#ifdef HAVE_UCRED_H -#include <ucred.h> -#endif -#ifdef HAVE_SYS_UCRED_H -#include <sys/ucred.h> -#endif - - -/* - * BSD-style getpeereid() for platforms that lack it. - */ -int -getpeereid(int sock, uid_t *uid, gid_t *gid) -{ -#if defined(SO_PEERCRED) - /* Linux: use getsockopt(SO_PEERCRED) */ - struct ucred peercred; - ACCEPT_TYPE_ARG3 so_len = sizeof(peercred); - - if (getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &peercred, &so_len) != 0 || - so_len != sizeof(peercred)) - return -1; - *uid = peercred.uid; - *gid = peercred.gid; - return 0; -#elif defined(LOCAL_PEERCRED) - /* Debian with FreeBSD kernel: use getsockopt(LOCAL_PEERCRED) */ - struct xucred peercred; - ACCEPT_TYPE_ARG3 so_len = sizeof(peercred); - - if (getsockopt(sock, 0, LOCAL_PEERCRED, &peercred, &so_len) != 0 || - so_len != sizeof(peercred) || - peercred.cr_version != XUCRED_VERSION) - return -1; - *uid = peercred.cr_uid; - *gid = peercred.cr_gid; - return 0; -#elif defined(HAVE_GETPEERUCRED) - /* Solaris: use getpeerucred() */ - ucred_t *ucred; - - ucred = NULL; /* must be initialized to NULL */ - if (getpeerucred(sock, &ucred) == -1) - return -1; - - *uid = ucred_geteuid(ucred); - *gid = ucred_getegid(ucred); - ucred_free(ucred); - - if (*uid == (uid_t) (-1) || *gid == (gid_t) (-1)) - return -1; - return 0; -#else - /* No implementation available on this platform */ - errno = ENOSYS; - return -1; -#endif -} diff --git a/libpq/inet_net_ntop.c b/libpq/inet_net_ntop.c deleted file mode 100644 index 047895e..0000000 --- a/libpq/inet_net_ntop.c +++ /dev/null @@ -1,298 +0,0 @@ -/* - * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") - * Copyright (c) 1996,1999 by Internet Software Consortium. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT - * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - * src/backend/utils/adt/inet_net_ntop.c - */ - -#if defined(LIBC_SCCS) && !defined(lint) -static const char rcsid[] = "Id: inet_net_ntop.c,v 1.1.2.2 2004/03/09 09:17:27 marka Exp $"; -#endif - -#ifndef FRONTEND -#include "postgres.h" -#else -#include "postgres_fe.h" -#endif - -#include <sys/types.h> -#include <sys/socket.h> -#include <netinet/in.h> -#include <arpa/inet.h> - -#ifndef FRONTEND -#include "utils/inet.h" -#else -/* - * In a frontend build, we can't include inet.h, but we still need to have - * sensible definitions of these two constants. Note that inet_net_ntop() - * assumes that PGSQL_AF_INET is equal to AF_INET. - */ -#define PGSQL_AF_INET (AF_INET + 0) -#define PGSQL_AF_INET6 (AF_INET + 1) -#endif - - -#define NS_IN6ADDRSZ 16 -#define NS_INT16SZ 2 - -#ifdef SPRINTF_CHAR -#define SPRINTF(x) strlen(sprintf/**/x) -#else -#define SPRINTF(x) ((size_t)sprintf x) -#endif - -static char *inet_net_ntop_ipv4(const u_char *src, int bits, - char *dst, size_t size); -static char *inet_net_ntop_ipv6(const u_char *src, int bits, - char *dst, size_t size); - - -/* - * char * - * inet_net_ntop(af, src, bits, dst, size) - * convert host/network address from network to presentation format. - * "src"'s size is determined from its "af". - * return: - * pointer to dst, or NULL if an error occurred (check errno). - * note: - * 192.5.5.1/28 has a nonzero host part, which means it isn't a network - * as called for by inet_net_pton() but it can be a host address with - * an included netmask. - * author: - * Paul Vixie (ISC), October 1998 - */ -char * -inet_net_ntop(int af, const void *src, int bits, char *dst, size_t size) -{ - /* - * We need to cover both the address family constants used by the PG inet - * type (PGSQL_AF_INET and PGSQL_AF_INET6) and those used by the system - * libraries (AF_INET and AF_INET6). We can safely assume PGSQL_AF_INET - * == AF_INET, but the INET6 constants are very likely to be different. If - * AF_INET6 isn't defined, silently ignore it. - */ - switch (af) - { - case PGSQL_AF_INET: - return (inet_net_ntop_ipv4(src, bits, dst, size)); - case PGSQL_AF_INET6: -#if defined(AF_INET6) && AF_INET6 != PGSQL_AF_INET6 - case AF_INET6: -#endif - return (inet_net_ntop_ipv6(src, bits, dst, size)); - default: - errno = EAFNOSUPPORT; - return (NULL); - } -} - -/* - * static char * - * inet_net_ntop_ipv4(src, bits, dst, size) - * convert IPv4 network address from network to presentation format. - * "src"'s size is determined from its "af". - * return: - * pointer to dst, or NULL if an error occurred (check errno). - * note: - * network byte order assumed. this means 192.5.5.240/28 has - * 0b11110000 in its fourth octet. - * author: - * Paul Vixie (ISC), October 1998 - */ -static char * -inet_net_ntop_ipv4(const u_char *src, int bits, char *dst, size_t size) -{ - char *odst = dst; - char *t; - int len = 4; - int b; - - if (bits < 0 || bits > 32) - { - errno = EINVAL; - return (NULL); - } - - /* Always format all four octets, regardless of mask length. */ - for (b = len; b > 0; b--) - { - if (size <= sizeof ".255") - goto emsgsize; - t = dst; - if (dst != odst) - *dst++ = '.'; - dst += SPRINTF((dst, "%u", *src++)); - size -= (size_t) (dst - t); - } - - /* don't print masklen if 32 bits */ - if (bits != 32) - { - if (size <= sizeof "/32") - goto emsgsize; - dst += SPRINTF((dst, "/%u", bits)); - } - - return (odst); - -emsgsize: - errno = EMSGSIZE; - return (NULL); -} - -static int -decoct(const u_char *src, int bytes, char *dst, size_t size) -{ - char *odst = dst; - char *t; - int b; - - for (b = 1; b <= bytes; b++) - { - if (size <= sizeof "255.") - return (0); - t = dst; - dst += SPRINTF((dst, "%u", *src++)); - if (b != bytes) - { - *dst++ = '.'; - *dst = '\0'; - } - size -= (size_t) (dst - t); - } - return (dst - odst); -} - -static char * -inet_net_ntop_ipv6(const u_char *src, int bits, char *dst, size_t size) -{ - /* - * Note that int32_t and int16_t need only be "at least" large enough to - * contain a value of the specified size. On some systems, like Crays, - * there is no such thing as an integer variable with 16 bits. Keep this - * in mind if you think this function should have been coded to use - * pointer overlays. All the world's not a VAX. - */ - char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255/128"]; - char *tp; - struct - { - int base, - len; - } best, cur; - u_int words[NS_IN6ADDRSZ / NS_INT16SZ]; - int i; - - if ((bits < -1) || (bits > 128)) - { - errno = EINVAL; - return (NULL); - } - - /* - * Preprocess: Copy the input (bytewise) array into a wordwise array. Find - * the longest run of 0x00's in src[] for :: shorthanding. - */ - memset(words, '\0', sizeof words); - for (i = 0; i < NS_IN6ADDRSZ; i++) - words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3)); - best.base = -1; - cur.base = -1; - best.len = 0; - cur.len = 0; - for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) - { - if (words[i] == 0) - { - if (cur.base == -1) - cur.base = i, cur.len = 1; - else - cur.len++; - } - else - { - if (cur.base != -1) - { - if (best.base == -1 || cur.len > best.len) - best = cur; - cur.base = -1; - } - } - } - if (cur.base != -1) - { - if (best.base == -1 || cur.len > best.len) - best = cur; - } - if (best.base != -1 && best.len < 2) - best.base = -1; - - /* - * Format the result. - */ - tp = tmp; - for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) - { - /* Are we inside the best run of 0x00's? */ - if (best.base != -1 && i >= best.base && - i < (best.base + best.len)) - { - if (i == best.base) - *tp++ = ':'; - continue; - } - /* Are we following an initial run of 0x00s or any real hex? */ - if (i != 0) - *tp++ = ':'; - /* Is this address an encapsulated IPv4? */ - if (i == 6 && best.base == 0 && (best.len == 6 || - (best.len == 7 && words[7] != 0x0001) || - (best.len == 5 && words[5] == 0xffff))) - { - int n; - - n = decoct(src + 12, 4, tp, sizeof tmp - (tp - tmp)); - if (n == 0) - { - errno = EMSGSIZE; - return (NULL); - } - tp += strlen(tp); - break; - } - tp += SPRINTF((tp, "%x", words[i])); - } - - /* Was it a trailing run of 0x00's? */ - if (best.base != -1 && (best.base + best.len) == - (NS_IN6ADDRSZ / NS_INT16SZ)) - *tp++ = ':'; - *tp = '\0'; - - if (bits != -1 && bits != 128) - tp += SPRINTF((tp, "/%u", bits)); - - /* - * Check for overflow, copy, and we're done. - */ - if ((size_t) (tp - tmp) > size) - { - errno = EMSGSIZE; - return (NULL); - } - strcpy(dst, tmp); - return (dst); -} diff --git a/libpq/ip.c b/libpq/ip.c deleted file mode 100644 index 9591ed2..0000000 --- a/libpq/ip.c +++ /dev/null @@ -1,819 +0,0 @@ -/*------------------------------------------------------------------------- - * - * ip.c - * IPv6-aware network access. - * - * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * - * IDENTIFICATION - * src/backend/libpq/ip.c - * - * This file and the IPV6 implementation were initially provided by - * Nigel Kukard <nkukard@lbsd.net>, Linux Based Systems Design - * http://www.lbsd.net. - * - *--------------------------------------------------------- |