From 2a969b7f4bdb223d3626dc14b684701942ccafb2 Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Thu, 21 Sep 2017 01:02:04 +0300 Subject: Make package to be source rather than stub --- libpq/.gitignore | 3 + libpq/buildfile | 190 + libpq/chklocale.c | 445 ++ libpq/encnames.c | 553 +++ libpq/fe-auth.c | 841 ++++ libpq/fe-auth.h | 24 + libpq/fe-connect.c | 6002 ++++++++++++++++++++++++++ libpq/fe-exec.c | 3764 ++++++++++++++++ libpq/fe-lobj.c | 1103 +++++ libpq/fe-misc.c | 1267 ++++++ libpq/fe-print.c | 761 ++++ libpq/fe-protocol2.c | 1623 +++++++ libpq/fe-protocol3.c | 2204 ++++++++++ libpq/fe-secure.c | 505 +++ libpq/getpeereid.c | 80 + libpq/inet_net_ntop.c | 298 ++ libpq/ip.c | 819 ++++ libpq/libpq-events.c | 209 + libpq/libpq-events.h | 94 + libpq/libpq-fe.h | 607 +++ libpq/libpq-int.h | 675 +++ libpq/md5.c | 345 ++ libpq/noblock.c | 66 + libpq/non-bsd/strlcpy.c | 71 + libpq/pg_service.conf.sample | 17 + libpq/pgstrcasecmp.c | 151 + libpq/postgresql/c.h | 1107 +++++ libpq/postgresql/common/fe_memutils.h | 44 + libpq/postgresql/getaddrinfo.h | 164 + libpq/postgresql/libpq/ip.h | 51 + libpq/postgresql/libpq/libpq-fs.h | 24 + libpq/postgresql/libpq/md5.h | 30 + libpq/postgresql/libpq/pqcomm.h | 206 + libpq/postgresql/mb/pg_wchar.h | 561 +++ libpq/postgresql/pg_config.h | 280 ++ libpq/postgresql/pg_config.h.in.orig | 931 ++++ libpq/postgresql/pg_config.h.win32.orig | 682 +++ libpq/postgresql/pg_config_ext.h | 20 + libpq/postgresql/pg_config_ext.h.in.orig | 7 + libpq/postgresql/pg_config_ext.h.win32.orig | 7 + libpq/postgresql/pg_config_manual.h | 327 ++ libpq/postgresql/pg_config_paths.h | 1 + libpq/postgresql/port.h | 477 ++ libpq/postgresql/port/bsd/pg_config_os.h | 0 libpq/postgresql/port/darwin/pg_config_os.h | 8 + libpq/postgresql/port/linux/pg_config_os.h | 22 + libpq/postgresql/port/win32/arpa/inet.h | 3 + libpq/postgresql/port/win32/netdb.h | 1 + libpq/postgresql/port/win32/netinet/in.h | 3 + libpq/postgresql/port/win32/pg_config_os.h | 486 +++ libpq/postgresql/port/win32/pthread-win32.h | 22 + libpq/postgresql/port/win32/pwd.h | 3 + libpq/postgresql/port/win32/sys/socket.h | 33 + libpq/postgresql/port/win32/sys/wait.h | 3 + libpq/postgresql/port/win32_msvc/sys/file.h | 1 + libpq/postgresql/port/win32_msvc/sys/param.h | 1 + libpq/postgresql/port/win32_msvc/sys/time.h | 1 + libpq/postgresql/port/win32_msvc/unistd.h | 1 + libpq/postgresql/postgres_ext.h | 70 + libpq/postgresql/postgres_fe.h | 29 + libpq/pqexpbuffer.c | 430 ++ libpq/pqexpbuffer.h | 182 + libpq/pqsignal.c | 90 + libpq/thread.c | 146 + libpq/version.h.in | 26 + libpq/wchar.c | 2054 +++++++++ libpq/win32/crypt.c | 1085 +++++ libpq/win32/getaddrinfo.c | 412 ++ libpq/win32/inet_aton.c | 147 + libpq/win32/libpq.rc.in | 31 + libpq/win32/libpqdll.def | 174 + libpq/win32/libpqdll.def.orig | 174 + libpq/win32/open.c | 167 + libpq/win32/pgsleep.c | 63 + libpq/win32/pthread-win32.c | 61 + libpq/win32/pthread-win32.h | 22 + libpq/win32/snprintf.c | 1141 +++++ libpq/win32/system.c | 119 + libpq/win32/win32.c | 327 ++ libpq/win32/win32.h | 40 + libpq/win32/win32error.c | 206 + libpq/win32/win32setlocale.c | 189 + 82 files changed, 35609 insertions(+) create mode 100644 libpq/.gitignore create mode 100644 libpq/buildfile create mode 100644 libpq/chklocale.c create mode 100644 libpq/encnames.c create mode 100644 libpq/fe-auth.c create mode 100644 libpq/fe-auth.h create mode 100644 libpq/fe-connect.c create mode 100644 libpq/fe-exec.c create mode 100644 libpq/fe-lobj.c create mode 100644 libpq/fe-misc.c create mode 100644 libpq/fe-print.c create mode 100644 libpq/fe-protocol2.c create mode 100644 libpq/fe-protocol3.c create mode 100644 libpq/fe-secure.c create mode 100644 libpq/getpeereid.c create mode 100644 libpq/inet_net_ntop.c create mode 100644 libpq/ip.c create mode 100644 libpq/libpq-events.c create mode 100644 libpq/libpq-events.h create mode 100644 libpq/libpq-fe.h create mode 100644 libpq/libpq-int.h create mode 100644 libpq/md5.c create mode 100644 libpq/noblock.c create mode 100644 libpq/non-bsd/strlcpy.c create mode 100644 libpq/pg_service.conf.sample create mode 100644 libpq/pgstrcasecmp.c create mode 100644 libpq/postgresql/c.h create mode 100644 libpq/postgresql/common/fe_memutils.h create mode 100644 libpq/postgresql/getaddrinfo.h create mode 100644 libpq/postgresql/libpq/ip.h create mode 100644 libpq/postgresql/libpq/libpq-fs.h create mode 100644 libpq/postgresql/libpq/md5.h create mode 100644 libpq/postgresql/libpq/pqcomm.h create mode 100644 libpq/postgresql/mb/pg_wchar.h create mode 100644 libpq/postgresql/pg_config.h create mode 100644 libpq/postgresql/pg_config.h.in.orig create mode 100644 libpq/postgresql/pg_config.h.win32.orig create mode 100644 libpq/postgresql/pg_config_ext.h create mode 100644 libpq/postgresql/pg_config_ext.h.in.orig create mode 100644 libpq/postgresql/pg_config_ext.h.win32.orig create mode 100644 libpq/postgresql/pg_config_manual.h create mode 100644 libpq/postgresql/pg_config_paths.h create mode 100644 libpq/postgresql/port.h create mode 100644 libpq/postgresql/port/bsd/pg_config_os.h create mode 100644 libpq/postgresql/port/darwin/pg_config_os.h create mode 100644 libpq/postgresql/port/linux/pg_config_os.h create mode 100644 libpq/postgresql/port/win32/arpa/inet.h create mode 100644 libpq/postgresql/port/win32/netdb.h create mode 100644 libpq/postgresql/port/win32/netinet/in.h create mode 100644 libpq/postgresql/port/win32/pg_config_os.h create mode 100644 libpq/postgresql/port/win32/pthread-win32.h create mode 100644 libpq/postgresql/port/win32/pwd.h create mode 100644 libpq/postgresql/port/win32/sys/socket.h create mode 100644 libpq/postgresql/port/win32/sys/wait.h create mode 100644 libpq/postgresql/port/win32_msvc/sys/file.h create mode 100644 libpq/postgresql/port/win32_msvc/sys/param.h create mode 100644 libpq/postgresql/port/win32_msvc/sys/time.h create mode 100644 libpq/postgresql/port/win32_msvc/unistd.h create mode 100644 libpq/postgresql/postgres_ext.h create mode 100644 libpq/postgresql/postgres_fe.h create mode 100644 libpq/pqexpbuffer.c create mode 100644 libpq/pqexpbuffer.h create mode 100644 libpq/pqsignal.c create mode 100644 libpq/thread.c create mode 100644 libpq/version.h.in create mode 100644 libpq/wchar.c create mode 100644 libpq/win32/crypt.c create mode 100644 libpq/win32/getaddrinfo.c create mode 100644 libpq/win32/inet_aton.c create mode 100644 libpq/win32/libpq.rc.in create mode 100644 libpq/win32/libpqdll.def create mode 100644 libpq/win32/libpqdll.def.orig create mode 100644 libpq/win32/open.c create mode 100644 libpq/win32/pgsleep.c create mode 100644 libpq/win32/pthread-win32.c create mode 100644 libpq/win32/pthread-win32.h create mode 100644 libpq/win32/snprintf.c create mode 100644 libpq/win32/system.c create mode 100644 libpq/win32/win32.c create mode 100644 libpq/win32/win32.h create mode 100644 libpq/win32/win32error.c create mode 100644 libpq/win32/win32setlocale.c (limited to 'libpq') diff --git a/libpq/.gitignore b/libpq/.gitignore new file mode 100644 index 0000000..620b4c8 --- /dev/null +++ b/libpq/.gitignore @@ -0,0 +1,3 @@ +# Generated version.h. +# +version.h diff --git a/libpq/buildfile b/libpq/buildfile new file mode 100644 index 0000000..35638c8 --- /dev/null +++ b/libpq/buildfile @@ -0,0 +1,190 @@ +# file : libpq/buildfile +# copyright : Copyright (c) 2016-2017 Code Synthesis Ltd +# license : PostgreSQL Licenes; see accompanying COPYRIGHT file + +# Headers other than these are not installed so treat them as files. +# +# @@ Make it postgresql/{postgres_ext.h pg_config_ext.h} when name pattern can +# be represented with a reversible name. +# +# @@ TODO: we should be able to redo it with install=true/false +# +h = version.h libpq-fe.h libpq-events.h \ + postgresql/postgres_ext.h postgresql/pg_config_ext.h + +lib{pq}: c{*} h{$h} file{**.h -{$h}} file{win32/libpqdll.def} \ + file{pg_service.conf.sample} + +tclass = $c.target.class +tsys = $c.target.system + +if ($tclass == "windows") + lib{pq}: win32/c{*} +else + lib{pq}: win32/file{*.c} + +if ($tclass != "bsd" && $tclass != "macos") + lib{pq}: non-bsd/c{*} +else + lib{pq}: non-bsd/file{*.c} + +# See bootstrap.build for details. +# +if $version.pre_release + lib{pq}: bin.lib.version = @"-$version.project_id" +else + lib{pq}: bin.lib.version = @"-$abi_version" + +# The version file is an internal one (it is only included from +# postgresql/pg_config.h) so we don't distribute nor install it (see below). +# +h{version}: in{version} $src_root/file{manifest} + +c.poptions += -DFRONTEND -DUNSAFE_STAT_OK -DSO_MAJOR_VERSION=$abi_major + +if ($tclass != "windows") + # Note that the original package uses -pthread compiler/linker option. It is + # currently unsupported by build2, so we use -D_REENTRANT and -lpthread + # preprocessor/linker options instead. We also omit -D_THREAD_SAFE (synonym + # for -D_REENTRANT) and Solaris-specific -D_POSIX_PTHREAD_SEMANTICS. + # + # @@ Maybe makes sense to support -pthread in build2, adding -lpthread + # to pkg-config's Libs.private and to cc.export.libs? + # + c.poptions += -D_REENTRANT -D_GNU_SOURCE +else + # Note that the original package defines the WIN32 macro for VC only, relying + # on the fact that MinGW GCC defines it by default. However, the macro + # disappears from the default ones if to compile with -std=c9x (as we do). So + # we define it for both VC and MinGW GCC. + # + # It's tempting to move this definition to libpq/postgresql/pg_config.h. + # However this header is not included into all files that use the macro, for + # example, libpq/win32/open.c. + # + c.poptions += -DWIN32 + +port_dir = ($tclass == "windows" ? "win32" : \ + $tclass == "macos" ? "darwin" : \ + $tclass) + +c.poptions =+ "-I$src_base" \ + "-I$src_base/postgresql/port/$port_dir" \ + "-I$src_base/postgresql" \ + "-I$src_root" + +if ($tclass == "windows") + obj{*}: c.poptions =+ "-I$src_base/win32" + +if ($tsys == "win32-msvc") +{ + c.poptions =+ "-I$src_base/postgresql/port/win32_msvc" + + # Disable warnings that pop up with /W3. + # + c.poptions += -D_CRT_SECURE_NO_DEPRECATE -D_CRT_NONSTDC_NO_DEPRECATE + c.coptions += /wd4018 /wd4244 /wd4267 +} +else +{ + # Omit -fexcess-precision=standard as -std=9x implies it. + # + c.coptions += -fno-strict-aliasing -fwrapv + + # Disable warnings that pop up with -W -Wall. + # + c.coptions += -Wno-unused-parameter -Wno-missing-field-initializers \ + -Wno-sign-compare -Wno-unused-command-line-argument +} + +# Define SYSCONFDIR macro. This path is used as a last resort for the +# pg_service.conf file search (see libpq/fe-connect.c for details). +# +# The whole idea feels utterly broken (hello cross-compilation) so we will +# just do bare minimum and wait and see. +# +# @@ We should probably allow to configure this macros via configuration +# variable config.libpq.sysconfdir. +# +if ($tclass == "windows") +{ + # win32.mak from the original package does this. + # + sysconfdir = "" +} +else +{ + # For the original package if the resulted sysconfdir path doesn't contain + # the 'postgres' or 'pgsql' substring then the '/postgresql' suffix is + # automatically appended (see the original INSTALL file for details). Note + # that the same rule is applied for the datadir and docdir paths. Also if + # the root directory is /usr, then the resulting sysconfdir path is + # /etc/postgresql (rather than /usr/etc/postgresql). + # + # Let's do the same for the sysconfdir to increase the chance that libpq + # will find the configuration file. Note that we don't install anything at + # this path and don't amend the install.data and install.doc path variables. + # We also use the same default path as the original package. + # + if ($install.root != [null]) + { + root = $install.resolve($install.root) + sysconfdir = ($root != /usr ? $root/etc : /etc) + + if! $regex.match("$sysconfdir", '.*(pgsql|postgresql).*') + sysconfdir = $sysconfdir/postgresql + } + else + sysconfdir = /usr/local/pgsql/etc +} + +# If we ever enable National Language Support (ENABLE_NLS macro) then we will +# need to define the LOCALEDIR macro as well. It refers to the locale data +# directory and should be $install.data/locale by default. We will also need +# to install this directory (see configure script --enable-nls options and the +# src/interfaces/libpq/po directory in the original package for details). +# +obj{fe-connect}: c.poptions += -DSYSCONFDIR="\"$sysconfdir\"" + +if ($tclass != "windows") +{ + c.libs += -lpthread +} +else +{ + def = $src_base/win32/libpqdll.def + + if ($tsys == "mingw32") + { + libs{pq}: c.loptions += $def + c.libs += -lsecur32 -lws2_32 -ladvapi32 + } + else + { + libs{pq}: c.loptions += "/DEF:$def" + c.libs += secur32.lib ws2_32.lib advapi32.lib + } + + # The original package also adds the resource file to the library. The file + # contains only the version information. First, libpq.rc is produced from + # libpq.rc.in with the following command: + # + # sed -e 's/\(VERSION.*\),0 *$/\1,'`date '+%y%j' | \ + # sed 's/^0*//'`'/' libpq.rc.in >libpq.rc + # + # Then libpq.rc is compiled with: + # + # windres -i libpq.rc -o libpqrc.o + # + # Afterwards libpqrc.o is linked to the library. + # + # @@ Currently we don't have support for the first two steps. +} + +lib{pq}: cc.export.poptions = "-I$src_base" "-I$src_base/postgresql" + +# Internal header (see above). +# +h{version}: install = false + +file{pg_service.conf.sample}@./: install = data/ diff --git a/libpq/chklocale.c b/libpq/chklocale.c new file mode 100644 index 0000000..3c0ef6a --- /dev/null +++ b/libpq/chklocale.c @@ -0,0 +1,445 @@ +/*------------------------------------------------------------------------- + * + * chklocale.c + * Functions for handling locale-related info + * + * + * Copyright (c) 1996-2016, PostgreSQL Global Development Group + * + * + * IDENTIFICATION + * src/port/chklocale.c + * + *------------------------------------------------------------------------- + */ + +#ifndef FRONTEND +#include "postgres.h" +#else +#include "postgres_fe.h" +#endif + +#if defined(WIN32) && (_MSC_VER >= 1900) +#include +#endif + +#include +#ifdef HAVE_LANGINFO_H +#include +#endif + +#include "mb/pg_wchar.h" + + +/* + * This table needs to recognize all the CODESET spellings for supported + * backend encodings, as well as frontend-only encodings where possible + * (the latter case is currently only needed for initdb to recognize + * error situations). On Windows, we rely on entries for codepage + * numbers (CPnnn). + * + * Note that we search the table with pg_strcasecmp(), so variant + * capitalizations don't need their own entries. + */ +struct encoding_match +{ + enum pg_enc pg_enc_code; + const char *system_enc_name; +}; + +static const struct encoding_match encoding_match_list[] = { + {PG_EUC_JP, "EUC-JP"}, + {PG_EUC_JP, "eucJP"}, + {PG_EUC_JP, "IBM-eucJP"}, + {PG_EUC_JP, "sdeckanji"}, + {PG_EUC_JP, "CP20932"}, + + {PG_EUC_CN, "EUC-CN"}, + {PG_EUC_CN, "eucCN"}, + {PG_EUC_CN, "IBM-eucCN"}, + {PG_EUC_CN, "GB2312"}, + {PG_EUC_CN, "dechanzi"}, + {PG_EUC_CN, "CP20936"}, + + {PG_EUC_KR, "EUC-KR"}, + {PG_EUC_KR, "eucKR"}, + {PG_EUC_KR, "IBM-eucKR"}, + {PG_EUC_KR, "deckorean"}, + {PG_EUC_KR, "5601"}, + {PG_EUC_KR, "CP51949"}, + + {PG_EUC_TW, "EUC-TW"}, + {PG_EUC_TW, "eucTW"}, + {PG_EUC_TW, "IBM-eucTW"}, + {PG_EUC_TW, "cns11643"}, + /* No codepage for EUC-TW ? */ + + {PG_UTF8, "UTF-8"}, + {PG_UTF8, "utf8"}, + {PG_UTF8, "CP65001"}, + + {PG_LATIN1, "ISO-8859-1"}, + {PG_LATIN1, "ISO8859-1"}, + {PG_LATIN1, "iso88591"}, + {PG_LATIN1, "CP28591"}, + + {PG_LATIN2, "ISO-8859-2"}, + {PG_LATIN2, "ISO8859-2"}, + {PG_LATIN2, "iso88592"}, + {PG_LATIN2, "CP28592"}, + + {PG_LATIN3, "ISO-8859-3"}, + {PG_LATIN3, "ISO8859-3"}, + {PG_LATIN3, "iso88593"}, + {PG_LATIN3, "CP28593"}, + + {PG_LATIN4, "ISO-8859-4"}, + {PG_LATIN4, "ISO8859-4"}, + {PG_LATIN4, "iso88594"}, + {PG_LATIN4, "CP28594"}, + + {PG_LATIN5, "ISO-8859-9"}, + {PG_LATIN5, "ISO8859-9"}, + {PG_LATIN5, "iso88599"}, + {PG_LATIN5, "CP28599"}, + + {PG_LATIN6, "ISO-8859-10"}, + {PG_LATIN6, "ISO8859-10"}, + {PG_LATIN6, "iso885910"}, + + {PG_LATIN7, "ISO-8859-13"}, + {PG_LATIN7, "ISO8859-13"}, + {PG_LATIN7, "iso885913"}, + + {PG_LATIN8, "ISO-8859-14"}, + {PG_LATIN8, "ISO8859-14"}, + {PG_LATIN8, "iso885914"}, + + {PG_LATIN9, "ISO-8859-15"}, + {PG_LATIN9, "ISO8859-15"}, + {PG_LATIN9, "iso885915"}, + {PG_LATIN9, "CP28605"}, + + {PG_LATIN10, "ISO-8859-16"}, + {PG_LATIN10, "ISO8859-16"}, + {PG_LATIN10, "iso885916"}, + + {PG_KOI8R, "KOI8-R"}, + {PG_KOI8R, "CP20866"}, + + {PG_KOI8U, "KOI8-U"}, + {PG_KOI8U, "CP21866"}, + + {PG_WIN866, "CP866"}, + {PG_WIN874, "CP874"}, + {PG_WIN1250, "CP1250"}, + {PG_WIN1251, "CP1251"}, + {PG_WIN1251, "ansi-1251"}, + {PG_WIN1252, "CP1252"}, + {PG_WIN1253, "CP1253"}, + {PG_WIN1254, "CP1254"}, + {PG_WIN1255, "CP1255"}, + {PG_WIN1256, "CP1256"}, + {PG_WIN1257, "CP1257"}, + {PG_WIN1258, "CP1258"}, + + {PG_ISO_8859_5, "ISO-8859-5"}, + {PG_ISO_8859_5, "ISO8859-5"}, + {PG_ISO_8859_5, "iso88595"}, + {PG_ISO_8859_5, "CP28595"}, + + {PG_ISO_8859_6, "ISO-8859-6"}, + {PG_ISO_8859_6, "ISO8859-6"}, + {PG_ISO_8859_6, "iso88596"}, + {PG_ISO_8859_6, "CP28596"}, + + {PG_ISO_8859_7, "ISO-8859-7"}, + {PG_ISO_8859_7, "ISO8859-7"}, + {PG_ISO_8859_7, "iso88597"}, + {PG_ISO_8859_7, "CP28597"}, + + {PG_ISO_8859_8, "ISO-8859-8"}, + {PG_ISO_8859_8, "ISO8859-8"}, + {PG_ISO_8859_8, "iso88598"}, + {PG_ISO_8859_8, "CP28598"}, + + {PG_SJIS, "SJIS"}, + {PG_SJIS, "PCK"}, + {PG_SJIS, "CP932"}, + {PG_SJIS, "SHIFT_JIS"}, + + {PG_BIG5, "BIG5"}, + {PG_BIG5, "BIG5HKSCS"}, + {PG_BIG5, "Big5-HKSCS"}, + {PG_BIG5, "CP950"}, + + {PG_GBK, "GBK"}, + {PG_GBK, "CP936"}, + + {PG_UHC, "UHC"}, + {PG_UHC, "CP949"}, + + {PG_JOHAB, "JOHAB"}, + {PG_JOHAB, "CP1361"}, + + {PG_GB18030, "GB18030"}, + {PG_GB18030, "CP54936"}, + + {PG_SHIFT_JIS_2004, "SJIS_2004"}, + + {PG_SQL_ASCII, "US-ASCII"}, + + {PG_SQL_ASCII, NULL} /* end marker */ +}; + +#ifdef WIN32 +/* + * On Windows, use CP instead of the nl_langinfo() result + * + * Visual Studio 2012 expanded the set of valid LC_CTYPE values, so have its + * locale machinery determine the code page. See comments at IsoLocaleName(). + * For other compilers, follow the locale's predictable format. + * + * Visual Studio 2015 should still be able to do the same, but the declaration + * of lc_codepage is missing in _locale_t, causing this code compilation to + * fail, hence this falls back instead on GetLocaleInfoEx. VS 2015 may be an + * exception and post-VS2015 versions should be able to handle properly the + * codepage number using _create_locale(). So, instead of the same logic as + * VS 2012 and VS 2013, this routine uses GetLocaleInfoEx to parse short + * locale names like "de-DE", "fr-FR", etc. If those cannot be parsed correctly + * process falls back to the pre-VS-2010 manual parsing done with + * using _. as a base. + * + * Returns a malloc()'d string for the caller to free. + */ +static char * +win32_langinfo(const char *ctype) +{ + char *r = NULL; + +#if (_MSC_VER >= 1700) && (_MSC_VER < 1900) + _locale_t loct = NULL; + + loct = _create_locale(LC_CTYPE, ctype); + if (loct != NULL) + { + r = malloc(16); /* excess */ + if (r != NULL) + sprintf(r, "CP%u", loct->locinfo->lc_codepage); + _free_locale(loct); + } +#else + char *codepage; + +#if (_MSC_VER >= 1900) + uint32 cp; + WCHAR wctype[LOCALE_NAME_MAX_LENGTH]; + + memset(wctype, 0, sizeof(wctype)); + MultiByteToWideChar(CP_ACP, 0, ctype, -1, wctype, LOCALE_NAME_MAX_LENGTH); + + if (GetLocaleInfoEx(wctype, + LOCALE_IDEFAULTANSICODEPAGE | LOCALE_RETURN_NUMBER, + (LPWSTR) &cp, sizeof(cp) / sizeof(WCHAR)) > 0) + { + r = malloc(16); /* excess */ + if (r != NULL) + sprintf(r, "CP%u", cp); + } + else +#endif + { + /* + * Locale format on Win32 is _. . For + * example, English_United States.1252. + */ + codepage = strrchr(ctype, '.'); + if (codepage != NULL) + { + int ln; + + codepage++; + ln = strlen(codepage); + r = malloc(ln + 3); + if (r != NULL) + sprintf(r, "CP%s", codepage); + } + + } +#endif + + return r; +} + +#ifndef FRONTEND +/* + * Given a Windows code page identifier, find the corresponding PostgreSQL + * encoding. Issue a warning and return -1 if none found. + */ +int +pg_codepage_to_encoding(UINT cp) +{ + char sys[16]; + int i; + + sprintf(sys, "CP%u", cp); + + /* Check the table */ + for (i = 0; encoding_match_list[i].system_enc_name; i++) + if (pg_strcasecmp(sys, encoding_match_list[i].system_enc_name) == 0) + return encoding_match_list[i].pg_enc_code; + + ereport(WARNING, + (errmsg("could not determine encoding for codeset \"%s\"", sys), + errdetail("Please report this to ."))); + + return -1; +} +#endif +#endif /* WIN32 */ + +#if (defined(HAVE_LANGINFO_H) && defined(CODESET)) || defined(WIN32) + +/* + * Given a setting for LC_CTYPE, return the Postgres ID of the associated + * encoding, if we can determine it. Return -1 if we can't determine it. + * + * Pass in NULL to get the encoding for the current locale setting. + * Pass "" to get the encoding selected by the server's environment. + * + * If the result is PG_SQL_ASCII, callers should treat it as being compatible + * with any desired encoding. + * + * If running in the backend and write_message is false, this function must + * cope with the possibility that elog() and palloc() are not yet usable. + */ +int +pg_get_encoding_from_locale(const char *ctype, bool write_message) +{ + char *sys; + int i; + + /* Get the CODESET property, and also LC_CTYPE if not passed in */ + if (ctype) + { + char *save; + char *name; + + /* If locale is C or POSIX, we can allow all encodings */ + if (pg_strcasecmp(ctype, "C") == 0 || + pg_strcasecmp(ctype, "POSIX") == 0) + return PG_SQL_ASCII; + + save = setlocale(LC_CTYPE, NULL); + if (!save) + return -1; /* setlocale() broken? */ + /* must copy result, or it might change after setlocale */ + save = strdup(save); + if (!save) + return -1; /* out of memory; unlikely */ + + name = setlocale(LC_CTYPE, ctype); + if (!name) + { + free(save); + return -1; /* bogus ctype passed in? */ + } + +#ifndef WIN32 + sys = nl_langinfo(CODESET); + if (sys) + sys = strdup(sys); +#else + sys = win32_langinfo(name); +#endif + + setlocale(LC_CTYPE, save); + free(save); + } + else + { + /* much easier... */ + ctype = setlocale(LC_CTYPE, NULL); + if (!ctype) + return -1; /* setlocale() broken? */ + + /* If locale is C or POSIX, we can allow all encodings */ + if (pg_strcasecmp(ctype, "C") == 0 || + pg_strcasecmp(ctype, "POSIX") == 0) + return PG_SQL_ASCII; + +#ifndef WIN32 + sys = nl_langinfo(CODESET); + if (sys) + sys = strdup(sys); +#else + sys = win32_langinfo(ctype); +#endif + } + + if (!sys) + return -1; /* out of memory; unlikely */ + + /* Check the table */ + for (i = 0; encoding_match_list[i].system_enc_name; i++) + { + if (pg_strcasecmp(sys, encoding_match_list[i].system_enc_name) == 0) + { + free(sys); + return encoding_match_list[i].pg_enc_code; + } + } + + /* Special-case kluges for particular platforms go here */ + +#ifdef __darwin__ + + /* + * Current OS X has many locales that report an empty string for CODESET, + * but they all seem to actually use UTF-8. + */ + if (strlen(sys) == 0) + { + free(sys); + return PG_UTF8; + } +#endif + + /* + * We print a warning if we got a CODESET string but couldn't recognize + * it. This means we need another entry in the table. + */ + if (write_message) + { +#ifdef FRONTEND + fprintf(stderr, _("could not determine encoding for locale \"%s\": codeset is \"%s\""), + ctype, sys); + /* keep newline separate so there's only one translatable string */ + fputc('\n', stderr); +#else + ereport(WARNING, + (errmsg("could not determine encoding for locale \"%s\": codeset is \"%s\"", + ctype, sys), + errdetail("Please report this to ."))); +#endif + } + + free(sys); + return -1; +} +#else /* (HAVE_LANGINFO_H && CODESET) || WIN32 */ + +/* + * stub if no multi-language platform support + * + * Note: we could return -1 here, but that would have the effect of + * forcing users to specify an encoding to initdb on such platforms. + * It seems better to silently default to SQL_ASCII. + */ +int +pg_get_encoding_from_locale(const char *ctype, bool write_message) +{ + return PG_SQL_ASCII; +} + +#endif /* (HAVE_LANGINFO_H && CODESET) || WIN32 */ diff --git a/libpq/encnames.c b/libpq/encnames.c new file mode 100644 index 0000000..11099b8 --- /dev/null +++ b/libpq/encnames.c @@ -0,0 +1,553 @@ +/* + * Encoding names and routines for work with it. All + * in this file is shared between FE and BE. + * + * src/backend/utils/mb/encnames.c + */ +#ifdef FRONTEND +#include "postgres_fe.h" +#else +#include "postgres.h" +#include "utils/builtins.h" +#endif + +#include +#include + +#include "mb/pg_wchar.h" + + +/* ---------- + * All encoding names, sorted: *** A L P H A B E T I C *** + * + * All names must be without irrelevant chars, search routines use + * isalnum() chars only. It means ISO-8859-1, iso_8859-1 and Iso8859_1 + * are always converted to 'iso88591'. All must be lower case. + * + * The table doesn't contain 'cs' aliases (like csISOLatin1). It's needed? + * + * Karel Zak, Aug 2001 + * ---------- + */ +typedef struct pg_encname +{ + const char *name; + pg_enc encoding; +} pg_encname; + +static const pg_encname pg_encname_tbl[] = +{ + { + "abc", PG_WIN1258 + }, /* alias for WIN1258 */ + { + "alt", PG_WIN866 + }, /* IBM866 */ + { + "big5", PG_BIG5 + }, /* Big5; Chinese for Taiwan multibyte set */ + { + "euccn", PG_EUC_CN + }, /* EUC-CN; Extended Unix Code for simplified + * Chinese */ + { + "eucjis2004", PG_EUC_JIS_2004 + }, /* EUC-JIS-2004; Extended UNIX Code fixed + * Width for Japanese, standard JIS X 0213 */ + { + "eucjp", PG_EUC_JP + }, /* EUC-JP; Extended UNIX Code fixed Width for + * Japanese, standard OSF */ + { + "euckr", PG_EUC_KR + }, /* EUC-KR; Extended Unix Code for Korean , KS + * X 1001 standard */ + { + "euctw", PG_EUC_TW + }, /* EUC-TW; Extended Unix Code for + * + * traditional Chinese */ + { + "gb18030", PG_GB18030 + }, /* GB18030;GB18030 */ + { + "gbk", PG_GBK + }, /* GBK; Chinese Windows CodePage 936 + * simplified Chinese */ + { + "iso88591", PG_LATIN1 + }, /* ISO-8859-1; RFC1345,KXS2 */ + { + "iso885910", PG_LATIN6 + }, /* ISO-8859-10; RFC1345,KXS2 */ + { + "iso885913", PG_LATIN7 + }, /* ISO-8859-13; RFC1345,KXS2 */ + { + "iso885914", PG_LATIN8 + }, /* ISO-8859-14; RFC1345,KXS2 */ + { + "iso885915", PG_LATIN9 + }, /* ISO-8859-15; RFC1345,KXS2 */ + { + "iso885916", PG_LATIN10 + }, /* ISO-8859-16; RFC1345,KXS2 */ + { + "iso88592", PG_LATIN2 + }, /* ISO-8859-2; RFC1345,KXS2 */ + { + "iso88593", PG_LATIN3 + }, /* ISO-8859-3; RFC1345,KXS2 */ + { + "iso88594", PG_LATIN4 + }, /* ISO-8859-4; RFC1345,KXS2 */ + { + "iso88595", PG_ISO_8859_5 + }, /* ISO-8859-5; RFC1345,KXS2 */ + { + "iso88596", PG_ISO_8859_6 + }, /* ISO-8859-6; RFC1345,KXS2 */ + { + "iso88597", PG_ISO_8859_7 + }, /* ISO-8859-7; RFC1345,KXS2 */ + { + "iso88598", PG_ISO_8859_8 + }, /* ISO-8859-8; RFC1345,KXS2 */ + { + "iso88599", PG_LATIN5 + }, /* ISO-8859-9; RFC1345,KXS2 */ + { + "johab", PG_JOHAB + }, /* JOHAB; Extended Unix Code for simplified + * Chinese */ + { + "koi8", PG_KOI8R + }, /* _dirty_ alias for KOI8-R (backward + * compatibility) */ + { + "koi8r", PG_KOI8R + }, /* KOI8-R; RFC1489 */ + { + "koi8u", PG_KOI8U + }, /* KOI8-U; RFC2319 */ + { + "latin1", PG_LATIN1 + }, /* alias for ISO-8859-1 */ + { + "latin10", PG_LATIN10 + }, /* alias for ISO-8859-16 */ + { + "latin2", PG_LATIN2 + }, /* alias for ISO-8859-2 */ + { + "latin3", PG_LATIN3 + }, /* alias for ISO-8859-3 */ + { + "latin4", PG_LATIN4 + }, /* alias for ISO-8859-4 */ + { + "latin5", PG_LATIN5 + }, /* alias for ISO-8859-9 */ + { + "latin6", PG_LATIN6 + }, /* alias for ISO-8859-10 */ + { + "latin7", PG_LATIN7 + }, /* alias for ISO-8859-13 */ + { + "latin8", PG_LATIN8 + }, /* alias for ISO-8859-14 */ + { + "latin9", PG_LATIN9 + }, /* alias for ISO-8859-15 */ + { + "mskanji", PG_SJIS + }, /* alias for Shift_JIS */ + { + "muleinternal", PG_MULE_INTERNAL + }, + { + "shiftjis", PG_SJIS + }, /* Shift_JIS; JIS X 0202-1991 */ + + { + "shiftjis2004", PG_SHIFT_JIS_2004 + }, /* SHIFT-JIS-2004; Shift JIS for Japanese, + * standard JIS X 0213 */ + { + "sjis", PG_SJIS + }, /* alias for Shift_JIS */ + { + "sqlascii", PG_SQL_ASCII + }, + { + "tcvn", PG_WIN1258 + }, /* alias for WIN1258 */ + { + "tcvn5712", PG_WIN1258 + }, /* alias for WIN1258 */ + { + "uhc", PG_UHC + }, /* UHC; Korean Windows CodePage 949 */ + { + "unicode", PG_UTF8 + }, /* alias for UTF8 */ + { + "utf8", PG_UTF8 + }, /* alias for UTF8 */ + { + "vscii", PG_WIN1258 + }, /* alias for WIN1258 */ + { + "win", PG_WIN1251 + }, /* _dirty_ alias for windows-1251 (backward + * compatibility) */ + { + "win1250", PG_WIN1250 + }, /* alias for Windows-1250 */ + { + "win1251", PG_WIN1251 + }, /* alias for Windows-1251 */ + { + "win1252", PG_WIN1252 + }, /* alias for Windows-1252 */ + { + "win1253", PG_WIN1253 + }, /* alias for Windows-1253 */ + { + "win1254", PG_WIN1254 + }, /* alias for Windows-1254 */ + { + "win1255", PG_WIN1255 + }, /* alias for Windows-1255 */ + { + "win1256", PG_WIN1256 + }, /* alias for Windows-1256 */ + { + "win1257", PG_WIN1257 + }, /* alias for Windows-1257 */ + { + "win1258", PG_WIN1258 + }, /* alias for Windows-1258 */ + { + "win866", PG_WIN866 + }, /* IBM866 */ + { + "win874", PG_WIN874 + }, /* alias for Windows-874 */ + { + "win932", PG_SJIS + }, /* alias for Shift_JIS */ + { + "win936", PG_GBK + }, /* alias for GBK */ + { + "win949", PG_UHC + }, /* alias for UHC */ + { + "win950", PG_BIG5 + }, /* alias for BIG5 */ + { + "windows1250", PG_WIN1250 + }, /* Windows-1251; Microsoft */ + { + "windows1251", PG_WIN1251 + }, /* Windows-1251; Microsoft */ + { + "windows1252", PG_WIN1252 + }, /* Windows-1252; Microsoft */ + { + "windows1253", PG_WIN1253 + }, /* Windows-1253; Microsoft */ + { + "windows1254", PG_WIN1254 + }, /* Windows-1254; Microsoft */ + { + "windows1255", PG_WIN1255 + }, /* Windows-1255; Microsoft */ + { + "windows1256", PG_WIN1256 + }, /* Windows-1256; Microsoft */ + { + "windows1257", PG_WIN1257 + }, /* Windows-1257; Microsoft */ + { + "windows1258", PG_WIN1258 + }, /* Windows-1258; Microsoft */ + { + "windows866", PG_WIN866 + }, /* IBM866 */ + { + "windows874", PG_WIN874 + }, /* Windows-874; Microsoft */ + { + "windows932", PG_SJIS + }, /* alias for Shift_JIS */ + { + "windows936", PG_GBK + }, /* alias for GBK */ + { + "windows949", PG_UHC + }, /* alias for UHC */ + { + "windows950", PG_BIG5 + } /* alias for BIG5 */ +}; + +/* ---------- + * These are "official" encoding names. + * XXX must be sorted by the same order as enum pg_enc (in mb/pg_wchar.h) + * ---------- + */ +#ifndef WIN32 +#define DEF_ENC2NAME(name, codepage) { #name, PG_##name } +#else +#define DEF_ENC2NAME(name, codepage) { #name, PG_##name, codepage } +#endif +const pg_enc2name pg_enc2name_tbl[] = +{ + DEF_ENC2NAME(SQL_ASCII, 0), + DEF_ENC2NAME(EUC_JP, 20932), + DEF_ENC2NAME(EUC_CN, 20936), + DEF_ENC2NAME(EUC_KR, 51949), + DEF_ENC2NAME(EUC_TW, 0), + DEF_ENC2NAME(EUC_JIS_2004, 20932), + DEF_ENC2NAME(UTF8, 65001), + DEF_ENC2NAME(MULE_INTERNAL, 0), + DEF_ENC2NAME(LATIN1, 28591), + DEF_ENC2NAME(LATIN2, 28592), + DEF_ENC2NAME(LATIN3, 28593), + DEF_ENC2NAME(LATIN4, 28594), + DEF_ENC2NAME(LATIN5, 28599), + DEF_ENC2NAME(LATIN6, 0), + DEF_ENC2NAME(LATIN7, 0), + DEF_ENC2NAME(LATIN8, 0), + DEF_ENC2NAME(LATIN9, 28605), + DEF_ENC2NAME(LATIN10, 0), + DEF_ENC2NAME(WIN1256, 1256), + DEF_ENC2NAME(WIN1258, 1258), + DEF_ENC2NAME(WIN866, 866), + DEF_ENC2NAME(WIN874, 874), + DEF_ENC2NAME(KOI8R, 20866), + DEF_ENC2NAME(WIN1251, 1251), + DEF_ENC2NAME(WIN1252, 1252), + DEF_ENC2NAME(ISO_8859_5, 28595), + DEF_ENC2NAME(ISO_8859_6, 28596), + DEF_ENC2NAME(ISO_8859_7, 28597), + DEF_ENC2NAME(ISO_8859_8, 28598), + DEF_ENC2NAME(WIN1250, 1250), + DEF_ENC2NAME(WIN1253, 1253), + DEF_ENC2NAME(WIN1254, 1254), + DEF_ENC2NAME(WIN1255, 1255), + DEF_ENC2NAME(WIN1257, 1257), + DEF_ENC2NAME(KOI8U, 21866), + DEF_ENC2NAME(SJIS, 932), + DEF_ENC2NAME(BIG5, 950), + DEF_ENC2NAME(GBK, 936), + DEF_ENC2NAME(UHC, 949), + DEF_ENC2NAME(GB18030, 54936), + DEF_ENC2NAME(JOHAB, 0), + DEF_ENC2NAME(SHIFT_JIS_2004, 932) +}; + +/* ---------- + * These are encoding names for gettext. + * + * This covers all encodings except MULE_INTERNAL, which is alien to gettext. + * ---------- + */ +const pg_enc2gettext pg_enc2gettext_tbl[] = +{ + {PG_SQL_ASCII, "US-ASCII"}, + {PG_UTF8, "UTF-8"}, + {PG_LATIN1, "LATIN1"}, + {PG_LATIN2, "LATIN2"}, + {PG_LATIN3, "LATIN3"}, + {PG_LATIN4, "LATIN4"}, + {PG_ISO_8859_5, "ISO-8859-5"}, + {PG_ISO_8859_6, "ISO_8859-6"}, + {PG_ISO_8859_7, "ISO-8859-7"}, + {PG_ISO_8859_8, "ISO-8859-8"}, + {PG_LATIN5, "LATIN5"}, + {PG_LATIN6, "LATIN6"}, + {PG_LATIN7, "LATIN7"}, + {PG_LATIN8, "LATIN8"}, + {PG_LATIN9, "LATIN-9"}, + {PG_LATIN10, "LATIN10"}, + {PG_KOI8R, "KOI8-R"}, + {PG_KOI8U, "KOI8-U"}, + {PG_WIN1250, "CP1250"}, + {PG_WIN1251, "CP1251"}, + {PG_WIN1252, "CP1252"}, + {PG_WIN1253, "CP1253"}, + {PG_WIN1254, "CP1254"}, + {PG_WIN1255, "CP1255"}, + {PG_WIN1256, "CP1256"}, + {PG_WIN1257, "CP1257"}, + {PG_WIN1258, "CP1258"}, + {PG_WIN866, "CP866"}, + {PG_WIN874, "CP874"}, + {PG_EUC_CN, "EUC-CN"}, + {PG_EUC_JP, "EUC-JP"}, + {PG_EUC_KR, "EUC-KR"}, + {PG_EUC_TW, "EUC-TW"}, + {PG_EUC_JIS_2004, "EUC-JP"}, + {PG_SJIS, "SHIFT-JIS"}, + {PG_BIG5, "BIG5"}, + {PG_GBK, "GBK"}, + {PG_UHC, "UHC"}, + {PG_GB18030, "GB18030"}, + {PG_JOHAB, "JOHAB"}, + {PG_SHIFT_JIS_2004, "SHIFT_JISX0213"}, + {0, NULL} +}; + + +/* ---------- + * Encoding checks, for error returns -1 else encoding id + * ---------- + */ +int +pg_valid_client_encoding(const char *name) +{ + int enc; + + if ((enc = pg_char_to_encoding(name)) < 0) + return -1; + + if (!PG_VALID_FE_ENCODING(enc)) + return -1; + + return enc; +} + +int +pg_valid_server_encoding(const char *name) +{ + int enc; + + if ((enc = pg_char_to_encoding(name)) < 0) + return -1; + + if (!PG_VALID_BE_ENCODING(enc)) + return -1; + + return enc; +} + +int +pg_valid_server_encoding_id(int encoding) +{ + return PG_VALID_BE_ENCODING(encoding); +} + +/* ---------- + * Remove irrelevant chars from encoding name + * ---------- + */ +static char * +clean_encoding_name(const char *key, char *newkey) +{ + const char *p; + char *np; + + for (p = key, np = newkey; *p != '\0'; p++) + { + if (isalnum((unsigned char) *p)) + { + if (*p >= 'A' && *p <= 'Z') + *np++ = *p + 'a' - 'A'; + else + *np++ = *p; + } + } + *np = '\0'; + return newkey; +} + +/* ---------- + * Search encoding by encoding name + * + * Returns encoding ID, or -1 for error + * ---------- + */ +int +pg_char_to_encoding(const char *name) +{ + unsigned int nel = lengthof(pg_encname_tbl); + const pg_encname *base = pg_encname_tbl, + *last = base + nel - 1, + *position; + int result; + char buff[NAMEDATALEN], + *key; + + if (name == NULL || *name == '\0') + return -1; + + if (strlen(name) >= NAMEDATALEN) + { +#ifdef FRONTEND + fprintf(stderr, "encoding name too long\n"); + return -1; +#else + ereport(ERROR, + (errcode(ERRCODE_NAME_TOO_LONG), + errmsg("encoding name too long"))); +#endif + } + key = clean_encoding_name(name, buff); + + while (last >= base) + { + position = base + ((last - base) >> 1); + result = key[0] - position->name[0]; + + if (result == 0) + { + result = strcmp(key, position->name); + if (result == 0) + return position->encoding; + } + if (result < 0) + last = position - 1; + else + base = position + 1; + } + return -1; +} + +#ifndef FRONTEND +Datum +PG_char_to_encoding(PG_FUNCTION_ARGS) +{ + Name s = PG_GETARG_NAME(0); + + PG_RETURN_INT32(pg_char_to_encoding(NameStr(*s))); +} +#endif + +const char * +pg_encoding_to_char(int encoding) +{ + if (PG_VALID_ENCODING(encoding)) + { + const pg_enc2name *p = &pg_enc2name_tbl[encoding]; + + Assert(encoding == p->encoding); + return p->name; + } + return ""; +} + +#ifndef FRONTEND +Datum +PG_encoding_to_char(PG_FUNCTION_ARGS) +{ + int32 encoding = PG_GETARG_INT32(0); + const char *encoding_name = pg_encoding_to_char(encoding); + + return DirectFunctionCall1(namein, CStringGetDatum(encoding_name)); +} + +#endif diff --git a/libpq/fe-auth.c b/libpq/fe-auth.c new file mode 100644 index 0000000..ab057e9 --- /dev/null +++ b/libpq/fe-auth.c @@ -0,0 +1,841 @@ +/*------------------------------------------------------------------------- + * + * fe-auth.c + * The front-end (client) authorization routines + * + * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * IDENTIFICATION + * src/interfaces/libpq/fe-auth.c + * + *------------------------------------------------------------------------- + */ + +/* + * INTERFACE ROUTINES + * frontend (client) routines: + * pg_fe_sendauth send authentication information + * pg_fe_getauthname get user's name according to the client side + * of the authentication system + */ + +#include "postgres_fe.h" + +#ifdef WIN32 +#include "win32.h" +#else +#include +#include +#include /* for MAXHOSTNAMELEN on most */ +#include +#ifdef HAVE_SYS_UCRED_H +#include +#endif +#ifndef MAXHOSTNAMELEN +#include /* for MAXHOSTNAMELEN on some */ +#endif +#include +#endif + +#include "libpq-fe.h" +#include "fe-auth.h" +#include "libpq/md5.h" + + +#ifdef ENABLE_GSS +/* + * GSSAPI authentication system. + */ + +#if defined(WIN32) && !defined(WIN32_ONLY_COMPILER) +/* + * MIT Kerberos GSSAPI DLL doesn't properly export the symbols for MingW + * that contain the OIDs required. Redefine here, values copied + * from src/athena/auth/krb5/src/lib/gssapi/generic/gssapi_generic.c + */ +static const gss_OID_desc GSS_C_NT_HOSTBASED_SERVICE_desc = +{10, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x04"}; +static GSS_DLLIMP gss_OID GSS_C_NT_HOSTBASED_SERVICE = &GSS_C_NT_HOSTBASED_SERVICE_desc; +#endif + +/* + * Fetch all errors of a specific type and append to "str". + */ +static void +pg_GSS_error_int(PQExpBuffer str, const char *mprefix, + OM_uint32 stat, int type) +{ + OM_uint32 lmin_s; + gss_buffer_desc lmsg; + OM_uint32 msg_ctx = 0; + + do + { + gss_display_status(&lmin_s, stat, type, + GSS_C_NO_OID, &msg_ctx, &lmsg); + appendPQExpBuffer(str, "%s: %s\n", mprefix, (char *) lmsg.value); + gss_release_buffer(&lmin_s, &lmsg); + } while (msg_ctx); +} + +/* + * GSSAPI errors contain two parts; put both into conn->errorMessage. + */ +static void +pg_GSS_error(const char *mprefix, PGconn *conn, + OM_uint32 maj_stat, OM_uint32 min_stat) +{ + resetPQExpBuffer(&conn->errorMessage); + + /* Fetch major error codes */ + pg_GSS_error_int(&conn->errorMessage, mprefix, maj_stat, GSS_C_GSS_CODE); + + /* Add the minor codes as well */ + pg_GSS_error_int(&conn->errorMessage, mprefix, min_stat, GSS_C_MECH_CODE); +} + +/* + * Continue GSS authentication with next token as needed. + */ +static int +pg_GSS_continue(PGconn *conn) +{ + OM_uint32 maj_stat, + min_stat, + lmin_s; + + maj_stat = gss_init_sec_context(&min_stat, + GSS_C_NO_CREDENTIAL, + &conn->gctx, + conn->gtarg_nam, + GSS_C_NO_OID, + GSS_C_MUTUAL_FLAG, + 0, + GSS_C_NO_CHANNEL_BINDINGS, + (conn->gctx == GSS_C_NO_CONTEXT) ? GSS_C_NO_BUFFER : &conn->ginbuf, + NULL, + &conn->goutbuf, + NULL, + NULL); + + if (conn->gctx != GSS_C_NO_CONTEXT) + { + free(conn->ginbuf.value); + conn->ginbuf.value = NULL; + conn->ginbuf.length = 0; + } + + if (conn->goutbuf.length != 0) + { + /* + * GSS generated data to send to the server. We don't care if it's the + * first or subsequent packet, just send the same kind of password + * packet. + */ + if (pqPacketSend(conn, 'p', + conn->goutbuf.value, conn->goutbuf.length) + != STATUS_OK) + { + gss_release_buffer(&lmin_s, &conn->goutbuf); + return STATUS_ERROR; + } + } + gss_release_buffer(&lmin_s, &conn->goutbuf); + + if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED) + { + pg_GSS_error(libpq_gettext("GSSAPI continuation error"), + conn, + maj_stat, min_stat); + gss_release_name(&lmin_s, &conn->gtarg_nam); + if (conn->gctx) + gss_delete_sec_context(&lmin_s, &conn->gctx, GSS_C_NO_BUFFER); + return STATUS_ERROR; + } + + if (maj_stat == GSS_S_COMPLETE) + gss_release_name(&lmin_s, &conn->gtarg_nam); + + return STATUS_OK; +} + +/* + * Send initial GSS authentication token + */ +static int +pg_GSS_startup(PGconn *conn) +{ + OM_uint32 maj_stat, + min_stat; + int maxlen; + gss_buffer_desc temp_gbuf; + + if (!(conn->pghost && conn->pghost[0] != '\0')) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("host name must be specified\n")); + return STATUS_ERROR; + } + + if (conn->gctx) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("duplicate GSS authentication request\n")); + return STATUS_ERROR; + } + + /* + * Import service principal name so the proper ticket can be acquired by + * the GSSAPI system. + */ + maxlen = NI_MAXHOST + strlen(conn->krbsrvname) + 2; + temp_gbuf.value = (char *) malloc(maxlen); + if (!temp_gbuf.value) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("out of memory\n")); + return STATUS_ERROR; + } + snprintf(temp_gbuf.value, maxlen, "%s@%s", + conn->krbsrvname, conn->pghost); + temp_gbuf.length = strlen(temp_gbuf.value); + + maj_stat = gss_import_name(&min_stat, &temp_gbuf, + GSS_C_NT_HOSTBASED_SERVICE, &conn->gtarg_nam); + free(temp_gbuf.value); + + if (maj_stat != GSS_S_COMPLETE) + { + pg_GSS_error(libpq_gettext("GSSAPI name import error"), + conn, + maj_stat, min_stat); + return STATUS_ERROR; + } + + /* + * Initial packet is the same as a continuation packet with no initial + * context. + */ + conn->gctx = GSS_C_NO_CONTEXT; + + return pg_GSS_continue(conn); +} +#endif /* ENABLE_GSS */ + + +#ifdef ENABLE_SSPI +/* + * SSPI authentication system (Windows only) + */ + +static void +pg_SSPI_error(PGconn *conn, const char *mprefix, SECURITY_STATUS r) +{ + char sysmsg[256]; + + if (FormatMessage(FORMAT_MESSAGE_IGNORE_INSERTS | + FORMAT_MESSAGE_FROM_SYSTEM, + NULL, r, 0, + sysmsg, sizeof(sysmsg), NULL) == 0) + printfPQExpBuffer(&conn->errorMessage, "%s: SSPI error %x\n", + mprefix, (unsigned int) r); + else + printfPQExpBuffer(&conn->errorMessage, "%s: %s (%x)\n", + mprefix, sysmsg, (unsigned int) r); +} + +/* + * Continue SSPI authentication with next token as needed. + */ +static int +pg_SSPI_continue(PGconn *conn) +{ + SECURITY_STATUS r; + CtxtHandle newContext; + ULONG contextAttr; + SecBufferDesc inbuf; + SecBufferDesc outbuf; + SecBuffer OutBuffers[1]; + SecBuffer InBuffers[1]; + + if (conn->sspictx != NULL) + { + /* + * On runs other than the first we have some data to send. Put this + * data in a SecBuffer type structure. + */ + inbuf.ulVersion = SECBUFFER_VERSION; + inbuf.cBuffers = 1; + inbuf.pBuffers = InBuffers; + InBuffers[0].pvBuffer = conn->ginbuf.value; + InBuffers[0].cbBuffer = conn->ginbuf.length; + InBuffers[0].BufferType = SECBUFFER_TOKEN; + } + + OutBuffers[0].pvBuffer = NULL; + OutBuffers[0].BufferType = SECBUFFER_TOKEN; + OutBuffers[0].cbBuffer = 0; + outbuf.cBuffers = 1; + outbuf.pBuffers = OutBuffers; + outbuf.ulVersion = SECBUFFER_VERSION; + + r = InitializeSecurityContext(conn->sspicred, + conn->sspictx, + conn->sspitarget, + ISC_REQ_ALLOCATE_MEMORY, + 0, + SECURITY_NETWORK_DREP, + (conn->sspictx == NULL) ? NULL : &inbuf, + 0, + &newContext, + &outbuf, + &contextAttr, + NULL); + + if (r != SEC_E_OK && r != SEC_I_CONTINUE_NEEDED) + { + pg_SSPI_error(conn, libpq_gettext("SSPI continuation error"), r); + + return STATUS_ERROR; + } + + if (conn->sspictx == NULL) + { + /* On first run, transfer retrieved context handle */ + conn->sspictx = malloc(sizeof(CtxtHandle)); + if (conn->sspictx == NULL) + { + printfPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory\n")); + return STATUS_ERROR; + } + memcpy(conn->sspictx, &newContext, sizeof(CtxtHandle)); + } + else + { + /* + * On subsequent runs when we had data to send, free buffers that + * contained this data. + */ + free(conn->ginbuf.value); + conn->ginbuf.value = NULL; + conn->ginbuf.length = 0; + } + + /* + * If SSPI returned any data to be sent to the server (as it normally + * would), send this data as a password packet. + */ + if (outbuf.cBuffers > 0) + { + if (outbuf.cBuffers != 1) + { + /* + * This should never happen, at least not for Kerberos + * authentication. Keep check in case it shows up with other + * authentication methods later. + */ + printfPQExpBuffer(&conn->errorMessage, "SSPI returned invalid number of output buffers\n"); + return STATUS_ERROR; + } + + /* + * If the negotiation is complete, there may be zero bytes to send. + * The server is at this point not expecting any more data, so don't + * send it. + */ + if (outbuf.pBuffers[0].cbBuffer > 0) + { + if (pqPacketSend(conn, 'p', + outbuf.pBuffers[0].pvBuffer, outbuf.pBuffers[0].cbBuffer)) + { + FreeContextBuffer(outbuf.pBuffers[0].pvBuffer); + return STATUS_ERROR; + } + } + FreeContextBuffer(outbuf.pBuffers[0].pvBuffer); + } + + /* Cleanup is handled by the code in freePGconn() */ + return STATUS_OK; +} + +/* + * Send initial SSPI authentication token. + * If use_negotiate is 0, use kerberos authentication package which is + * compatible with Unix. If use_negotiate is 1, use the negotiate package + * which supports both kerberos and NTLM, but is not compatible with Unix. + */ +static int +pg_SSPI_startup(PGconn *conn, int use_negotiate) +{ + SECURITY_STATUS r; + TimeStamp expire; + + if (conn->sspictx) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("duplicate SSPI authentication request\n")); + return STATUS_ERROR; + } + + /* + * Retrieve credentials handle + */ + conn->sspicred = malloc(sizeof(CredHandle)); + if (conn->sspicred == NULL) + { + printfPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory\n")); + return STATUS_ERROR; + } + + r = AcquireCredentialsHandle(NULL, + use_negotiate ? "negotiate" : "kerberos", + SECPKG_CRED_OUTBOUND, + NULL, + NULL, + NULL, + NULL, + conn->sspicred, + &expire); + if (r != SEC_E_OK) + { + pg_SSPI_error(conn, libpq_gettext("could not acquire SSPI credentials"), r); + free(conn->sspicred); + conn->sspicred = NULL; + return STATUS_ERROR; + } + + /* + * Compute target principal name. SSPI has a different format from GSSAPI, + * but not more complex. We can skip the @REALM part, because Windows will + * fill that in for us automatically. + */ + if (!(conn->pghost && conn->pghost[0] != '\0')) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("host name must be specified\n")); + return STATUS_ERROR; + } + conn->sspitarget = malloc(strlen(conn->krbsrvname) + strlen(conn->pghost) + 2); + if (!conn->sspitarget) + { + printfPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory\n")); + return STATUS_ERROR; + } + sprintf(conn->sspitarget, "%s/%s", conn->krbsrvname, conn->pghost); + + /* + * Indicate that we're in SSPI authentication mode to make sure that + * pg_SSPI_continue is called next time in the negotiation. + */ + conn->usesspi = 1; + + return pg_SSPI_continue(conn); +} +#endif /* ENABLE_SSPI */ + +/* + * Respond to AUTH_REQ_SCM_CREDS challenge. + * + * Note: this is dead code as of Postgres 9.1, because current backends will + * never send this challenge. But we must keep it as long as libpq needs to + * interoperate with pre-9.1 servers. It is believed to be needed only on + * Debian/kFreeBSD (ie, FreeBSD kernel with Linux userland, so that the + * getpeereid() function isn't provided by libc). + */ +static int +pg_local_sendauth(PGconn *conn) +{ +#ifdef HAVE_STRUCT_CMSGCRED + char buf; + struct iovec iov; + struct msghdr msg; + struct cmsghdr *cmsg; + union + { + struct cmsghdr hdr; + unsigned char buf[CMSG_SPACE(sizeof(struct cmsgcred))]; + } cmsgbuf; + + /* + * The backend doesn't care what we send here, but it wants exactly one + * character to force recvmsg() to block and wait for us. + */ + buf = '\0'; + iov.iov_base = &buf; + iov.iov_len = 1; + + memset(&msg, 0, sizeof(msg)); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + /* We must set up a message that will be filled in by kernel */ + memset(&cmsgbuf, 0, sizeof(cmsgbuf)); + msg.msg_control = &cmsgbuf.buf; + msg.msg_controllen = sizeof(cmsgbuf.buf); + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_len = CMSG_LEN(sizeof(struct cmsgcred)); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_CREDS; + + if (sendmsg(conn->sock, &msg, 0) == -1) + { + char sebuf[256]; + + printfPQExpBuffer(&conn->errorMessage, + "pg_local_sendauth: sendmsg: %s\n", + pqStrerror(errno, sebuf, sizeof(sebuf))); + return STATUS_ERROR; + } + return STATUS_OK; +#else + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("SCM_CRED authentication method not supported\n")); + return STATUS_ERROR; +#endif +} + +static int +pg_password_sendauth(PGconn *conn, const char *password, AuthRequest areq) +{ + int ret; + char *crypt_pwd = NULL; + const char *pwd_to_send; + + /* Encrypt the password if needed. */ + + switch (areq) + { + case AUTH_REQ_MD5: + { + char *crypt_pwd2; + + /* Allocate enough space for two MD5 hashes */ + crypt_pwd = malloc(2 * (MD5_PASSWD_LEN + 1)); + if (!crypt_pwd) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("out of memory\n")); + return STATUS_ERROR; + } + + crypt_pwd2 = crypt_pwd + MD5_PASSWD_LEN + 1; + if (!pg_md5_encrypt(password, conn->pguser, + strlen(conn->pguser), crypt_pwd2)) + { + free(crypt_pwd); + return STATUS_ERROR; + } + if (!pg_md5_encrypt(crypt_pwd2 + strlen("md5"), conn->md5Salt, + sizeof(conn->md5Salt), crypt_pwd)) + { + free(crypt_pwd); + return STATUS_ERROR; + } + + pwd_to_send = crypt_pwd; + break; + } + case AUTH_REQ_PASSWORD: + pwd_to_send = password; + break; + default: + return STATUS_ERROR; + } + /* Packet has a message type as of protocol 3.0 */ + if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3) + ret = pqPacketSend(conn, 'p', pwd_to_send, strlen(pwd_to_send) + 1); + else + ret = pqPacketSend(conn, 0, pwd_to_send, strlen(pwd_to_send) + 1); + if (crypt_pwd) + free(crypt_pwd); + return ret; +} + +/* + * pg_fe_sendauth + * client demux routine for outgoing authentication information + */ +int +pg_fe_sendauth(AuthRequest areq, PGconn *conn) +{ + switch (areq) + { + case AUTH_REQ_OK: + break; + + case AUTH_REQ_KRB4: + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("Kerberos 4 authentication not supported\n")); + return STATUS_ERROR; + + case AUTH_REQ_KRB5: + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("Kerberos 5 authentication not supported\n")); + return STATUS_ERROR; + +#if defined(ENABLE_GSS) || defined(ENABLE_SSPI) + case AUTH_REQ_GSS: +#if !defined(ENABLE_SSPI) + /* no native SSPI, so use GSSAPI library for it */ + case AUTH_REQ_SSPI: +#endif + { + int r; + + pglock_thread(); + + /* + * If we have both GSS and SSPI support compiled in, use SSPI + * support by default. This is overridable by a connection + * string parameter. Note that when using SSPI we still leave + * the negotiate parameter off, since we want SSPI to use the + * GSSAPI kerberos protocol. For actual SSPI negotiate + * protocol, we use AUTH_REQ_SSPI. + */ +#if defined(ENABLE_GSS) && defined(ENABLE_SSPI) + if (conn->gsslib && (pg_strcasecmp(conn->gsslib, "gssapi") == 0)) + r = pg_GSS_startup(conn); + else + r = pg_SSPI_startup(conn, 0); +#elif defined(ENABLE_GSS) && !defined(ENABLE_SSPI) + r = pg_GSS_startup(conn); +#elif !defined(ENABLE_GSS) && defined(ENABLE_SSPI) + r = pg_SSPI_startup(conn, 0); +#endif + if (r != STATUS_OK) + { + /* Error message already filled in. */ + pgunlock_thread(); + return STATUS_ERROR; + } + pgunlock_thread(); + } + break; + + case AUTH_REQ_GSS_CONT: + { + int r; + + pglock_thread(); +#if defined(ENABLE_GSS) && defined(ENABLE_SSPI) + if (conn->usesspi) + r = pg_SSPI_continue(conn); + else + r = pg_GSS_continue(conn); +#elif defined(ENABLE_GSS) && !defined(ENABLE_SSPI) + r = pg_GSS_continue(conn); +#elif !defined(ENABLE_GSS) && defined(ENABLE_SSPI) + r = pg_SSPI_continue(conn); +#endif + if (r != STATUS_OK) + { + /* Error message already filled in. */ + pgunlock_thread(); + return STATUS_ERROR; + } + pgunlock_thread(); + } + break; +#else /* defined(ENABLE_GSS) || defined(ENABLE_SSPI) */ + /* No GSSAPI *or* SSPI support */ + case AUTH_REQ_GSS: + case AUTH_REQ_GSS_CONT: + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("GSSAPI authentication not supported\n")); + return STATUS_ERROR; +#endif /* defined(ENABLE_GSS) || defined(ENABLE_SSPI) */ + +#ifdef ENABLE_SSPI + case AUTH_REQ_SSPI: + + /* + * SSPI has it's own startup message so libpq can decide which + * method to use. Indicate to pg_SSPI_startup that we want SSPI + * negotiation instead of Kerberos. + */ + pglock_thread(); + if (pg_SSPI_startup(conn, 1) != STATUS_OK) + { + /* Error message already filled in. */ + pgunlock_thread(); + return STATUS_ERROR; + } + pgunlock_thread(); + break; +#else + + /* + * No SSPI support. However, if we have GSSAPI but not SSPI + * support, AUTH_REQ_SSPI will have been handled in the codepath + * for AUTH_REQ_GSSAPI above, so don't duplicate the case label in + * that case. + */ +#if !defined(ENABLE_GSS) + case AUTH_REQ_SSPI: + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("SSPI authentication not supported\n")); + return STATUS_ERROR; +#endif /* !define(ENABLE_GSSAPI) */ +#endif /* ENABLE_SSPI */ + + + case AUTH_REQ_CRYPT: + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("Crypt authentication not supported\n")); + return STATUS_ERROR; + + case AUTH_REQ_MD5: + case AUTH_REQ_PASSWORD: + conn->password_needed = true; + if (conn->pgpass == NULL || conn->pgpass[0] == '\0') + { + printfPQExpBuffer(&conn->errorMessage, + PQnoPasswordSupplied); + return STATUS_ERROR; + } + if (pg_password_sendauth(conn, conn->pgpass, areq) != STATUS_OK) + { + printfPQExpBuffer(&conn->errorMessage, + "fe_sendauth: error sending password authentication\n"); + return STATUS_ERROR; + } + break; + + case AUTH_REQ_SCM_CREDS: + if (pg_local_sendauth(conn) != STATUS_OK) + return STATUS_ERROR; + break; + + /* + * SASL authentication was introduced in version 10. Older + * versions recognize the request only to give a nicer error + * message. We call it "SCRAM authentication" in the error, rather + * than SASL, because SCRAM is more familiar to users, and it's + * the only SASL authentication mechanism that has been + * implemented as of this writing, anyway. + */ + case AUTH_REQ_SASL: + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("SCRAM authentication requires libpq version 10 or above\n")); + return STATUS_ERROR; + + default: + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("authentication method %u not supported\n"), areq); + return STATUS_ERROR; + } + + return STATUS_OK; +} + + +/* + * pg_fe_getauthname + * + * Returns a pointer to malloc'd space containing whatever name the user + * has authenticated to the system. If there is an error, return NULL, + * and put a suitable error message in *errorMessage if that's not NULL. + */ +char * +pg_fe_getauthname(PQExpBuffer errorMessage) +{ + char *result = NULL; + const char *name = NULL; + +#ifdef WIN32 + /* Microsoft recommends buffer size of UNLEN+1, where UNLEN = 256 */ + char username[256 + 1]; + DWORD namesize = sizeof(username); +#else + uid_t user_id = geteuid(); + char pwdbuf[BUFSIZ]; + struct passwd pwdstr; + struct passwd *pw = NULL; + int pwerr; +#endif + + /* + * Some users are using configure --enable-thread-safety-force, so we + * might as well do the locking within our library to protect + * pqGetpwuid(). In fact, application developers can use getpwuid() in + * their application if they use the locking call we provide, or install + * their own locking function using PQregisterThreadLock(). + */ + pglock_thread(); + +#ifdef WIN32 + if (GetUserName(username, &namesize)) + name = username; + else if (errorMessage) + printfPQExpBuffer(errorMessage, + libpq_gettext("user name lookup failure: error code %lu\n"), + GetLastError()); +#else + pwerr = pqGetpwuid(user_id, &pwdstr, pwdbuf, sizeof(pwdbuf), &pw); + if (pw != NULL) + name = pw->pw_name; + else if (errorMessage) + { + if (pwerr != 0) + printfPQExpBuffer(errorMessage, + libpq_gettext("could not look up local user ID %d: %s\n"), + (int) user_id, + pqStrerror(pwerr, pwdbuf, sizeof(pwdbuf))); + else + printfPQExpBuffer(errorMessage, + libpq_gettext("local user with ID %d does not exist\n"), + (int) user_id); + } +#endif + + if (name) + { + result = strdup(name); + if (result == NULL && errorMessage) + printfPQExpBuffer(errorMessage, + libpq_gettext("out of memory\n")); + } + + pgunlock_thread(); + + return result; +} + + +/* + * PQencryptPassword -- exported routine to encrypt a password + * + * This is intended to be used by client applications that wish to send + * commands like ALTER USER joe PASSWORD 'pwd'. The password need not + * be sent in cleartext if it is encrypted on the client side. This is + * good because it ensures the cleartext password won't end up in logs, + * pg_stat displays, etc. We export the function so that clients won't + * be dependent on low-level details like whether the encryption is MD5 + * or something else. + * + * Arguments are the cleartext password, and the SQL name of the user it + * is for. + * + * Return value is a malloc'd string, or NULL if out-of-memory. The client + * may assume the string doesn't contain any special characters that would + * require escaping. + */ +char * +PQencryptPassword(const char *passwd, const char *user) +{ + char *crypt_pwd; + + crypt_pwd = malloc(MD5_PASSWD_LEN + 1); + if (!crypt_pwd) + return NULL; + + if (!pg_md5_encrypt(passwd, user, strlen(user), crypt_pwd)) + { + free(crypt_pwd); + return NULL; + } + + return crypt_pwd; +} diff --git a/libpq/fe-auth.h b/libpq/fe-auth.h new file mode 100644 index 0000000..9d11654 --- /dev/null +++ b/libpq/fe-auth.h @@ -0,0 +1,24 @@ +/*------------------------------------------------------------------------- + * + * fe-auth.h + * + * Definitions for network authentication routines + * + * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/interfaces/libpq/fe-auth.h + * + *------------------------------------------------------------------------- + */ +#ifndef FE_AUTH_H +#define FE_AUTH_H + +#include "libpq-fe.h" +#include "libpq-int.h" + + +extern int pg_fe_sendauth(AuthRequest areq, PGconn *conn); +extern char *pg_fe_getauthname(PQExpBuffer errorMessage); + +#endif /* FE_AUTH_H */ diff --git a/libpq/fe-connect.c b/libpq/fe-connect.c new file mode 100644 index 0000000..e61ed7d --- /dev/null +++ b/libpq/fe-connect.c @@ -0,0 +1,6002 @@ +/*------------------------------------------------------------------------- + * + * fe-connect.c + * functions related to setting up a connection to the backend + * + * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/interfaces/libpq/fe-connect.c + * + *------------------------------------------------------------------------- + */ + +#include "postgres_fe.h" + +#include +#include +#include +#include +#include +#include + +#include "libpq-fe.h" +#include "libpq-int.h" +#include "fe-auth.h" +#include "pg_config_paths.h" + +#ifdef WIN32 +#include "win32.h" +#ifdef _WIN32_IE +#undef _WIN32_IE +#endif +#define _WIN32_IE 0x0500 +#ifdef near +#undef near +#endif +#define near +#include +#ifdef WIN32_ONLY_COMPILER /* mstcpip.h is missing on mingw */ +#include +#endif +#else +#include +#include +#include +#ifdef HAVE_NETINET_TCP_H +#include +#endif +#include +#endif + +#ifdef ENABLE_THREAD_SAFETY +#ifdef WIN32 +#include "pthread-win32.h" +#else +#include +#endif +#endif + +#ifdef USE_LDAP +#ifdef WIN32 +#include +#else +/* OpenLDAP deprecates RFC 1823, but we want standard conformance */ +#define LDAP_DEPRECATED 1 +#include +typedef struct timeval LDAP_TIMEVAL; +#endif +static int ldapServiceLookup(const char *purl, PQconninfoOption *options, + PQExpBuffer errorMessage); +#endif + +#include "libpq/ip.h" +#include "mb/pg_wchar.h" + +#ifndef FD_CLOEXEC +#define FD_CLOEXEC 1 +#endif + + +#ifndef WIN32 +#define PGPASSFILE ".pgpass" +#else +#define PGPASSFILE "pgpass.conf" +#endif + +/* + * Pre-9.0 servers will return this SQLSTATE if asked to set + * application_name in a startup packet. We hard-wire the value rather + * than looking into errcodes.h since it reflects historical behavior + * rather than that of the current code. + */ +#define ERRCODE_APPNAME_UNKNOWN "42704" + +/* This is part of the protocol so just define it */ +#define ERRCODE_INVALID_PASSWORD "28P01" +/* This too */ +#define ERRCODE_CANNOT_CONNECT_NOW "57P03" + +/* + * Cope with the various platform-specific ways to spell TCP keepalive socket + * options. This doesn't cover Windows, which as usual does its own thing. + */ +#if defined(TCP_KEEPIDLE) +/* TCP_KEEPIDLE is the name of this option on Linux and *BSD */ +#define PG_TCP_KEEPALIVE_IDLE TCP_KEEPIDLE +#define PG_TCP_KEEPALIVE_IDLE_STR "TCP_KEEPIDLE" +#elif defined(TCP_KEEPALIVE_THRESHOLD) +/* TCP_KEEPALIVE_THRESHOLD is the name of this option on Solaris >= 11 */ +#define PG_TCP_KEEPALIVE_IDLE TCP_KEEPALIVE_THRESHOLD +#define PG_TCP_KEEPALIVE_IDLE_STR "TCP_KEEPALIVE_THRESHOLD" +#elif defined(TCP_KEEPALIVE) && defined(__darwin__) +/* TCP_KEEPALIVE is the name of this option on macOS */ +/* Caution: Solaris has this symbol but it means something different */ +#define PG_TCP_KEEPALIVE_IDLE TCP_KEEPALIVE +#define PG_TCP_KEEPALIVE_IDLE_STR "TCP_KEEPALIVE" +#endif + +/* + * fall back options if they are not specified by arguments or defined + * by environment variables + */ +#define DefaultHost "localhost" +#define DefaultTty "" +#define DefaultOption "" +#define DefaultAuthtype "" +#define DefaultPassword "" +#ifdef USE_SSL +#define DefaultSSLMode "prefer" +#else +#define DefaultSSLMode "disable" +#endif + +/* ---------- + * Definition of the conninfo parameters and their fallback resources. + * + * If Environment-Var and Compiled-in are specified as NULL, no + * fallback is available. If after all no value can be determined + * for an option, an error is returned. + * + * The value for the username is treated specially in conninfo_add_defaults. + * If the value is not obtained any other way, the username is determined + * by pg_fe_getauthname(). + * + * The Label and Disp-Char entries are provided for applications that + * want to use PQconndefaults() to create a generic database connection + * dialog. Disp-Char is defined as follows: + * "" Normal input field + * "*" Password field - hide value + * "D" Debug option - don't show by default + * + * PQconninfoOptions[] is a constant static array that we use to initialize + * a dynamically allocated working copy. All the "val" fields in + * PQconninfoOptions[] *must* be NULL. In a working copy, non-null "val" + * fields point to malloc'd strings that should be freed when the working + * array is freed (see PQconninfoFree). + * + * The first part of each struct is identical to the one in libpq-fe.h, + * which is required since we memcpy() data between the two! + * ---------- + */ +typedef struct _internalPQconninfoOption +{ + char *keyword; /* The keyword of the option */ + char *envvar; /* Fallback environment variable name */ + char *compiled; /* Fallback compiled in default value */ + char *val; /* Option's current value, or NULL */ + char *label; /* Label for field in connect dialog */ + char *dispchar; /* Indicates how to display this field in a + * connect dialog. Values are: "" Display + * entered value as is "*" Password field - + * hide value "D" Debug option - don't show + * by default */ + int dispsize; /* Field size in characters for dialog */ + /* --- + * Anything above this comment must be synchronized with + * PQconninfoOption in libpq-fe.h, since we memcpy() data + * between them! + * --- + */ + off_t connofs; /* Offset into PGconn struct, -1 if not there */ +} internalPQconninfoOption; + +static const internalPQconninfoOption PQconninfoOptions[] = { + /* + * "authtype" is no longer used, so mark it "don't show". We keep it in + * the array so as not to reject conninfo strings from old apps that might + * still try to set it. + */ + {"authtype", "PGAUTHTYPE", DefaultAuthtype, NULL, + "Database-Authtype", "D", 20, -1}, + + {"service", "PGSERVICE", NULL, NULL, + "Database-Service", "", 20, -1}, + + {"user", "PGUSER", NULL, NULL, + "Database-User", "", 20, + offsetof(struct pg_conn, pguser)}, + + {"password", "PGPASSWORD", NULL, NULL, + "Database-Password", "*", 20, + offsetof(struct pg_conn, pgpass)}, + + {"connect_timeout", "PGCONNECT_TIMEOUT", NULL, NULL, + "Connect-timeout", "", 10, /* strlen(INT32_MAX) == 10 */ + offsetof(struct pg_conn, connect_timeout)}, + + {"dbname", "PGDATABASE", NULL, NULL, + "Database-Name", "", 20, + offsetof(struct pg_conn, dbName)}, + + {"host", "PGHOST", NULL, NULL, + "Database-Host", "", 40, + offsetof(struct pg_conn, pghost)}, + + {"hostaddr", "PGHOSTADDR", NULL, NULL, + "Database-Host-IP-Address", "", 45, + offsetof(struct pg_conn, pghostaddr)}, + + {"port", "PGPORT", DEF_PGPORT_STR, NULL, + "Database-Port", "", 6, + offsetof(struct pg_conn, pgport)}, + + {"client_encoding", "PGCLIENTENCODING", NULL, NULL, + "Client-Encoding", "", 10, + offsetof(struct pg_conn, client_encoding_initial)}, + + /* + * "tty" is no longer used either, but keep it present for backwards + * compatibility. + */ + {"tty", "PGTTY", DefaultTty, NULL, + "Backend-Debug-TTY", "D", 40, + offsetof(struct pg_conn, pgtty)}, + + {"options", "PGOPTIONS", DefaultOption, NULL, + "Backend-Debug-Options", "D", 40, + offsetof(struct pg_conn, pgoptions)}, + + {"application_name", "PGAPPNAME", NULL, NULL, + "Application-Name", "", 64, + offsetof(struct pg_conn, appname)}, + + {"fallback_application_name", NULL, NULL, NULL, + "Fallback-Application-Name", "", 64, + offsetof(struct pg_conn, fbappname)}, + + {"keepalives", NULL, NULL, NULL, + "TCP-Keepalives", "", 1, /* should be just '0' or '1' */ + offsetof(struct pg_conn, keepalives)}, + + {"keepalives_idle", NULL, NULL, NULL, + "TCP-Keepalives-Idle", "", 10, /* strlen(INT32_MAX) == 10 */ + offsetof(struct pg_conn, keepalives_idle)}, + + {"keepalives_interval", NULL, NULL, NULL, + "TCP-Keepalives-Interval", "", 10, /* strlen(INT32_MAX) == 10 */ + offsetof(struct pg_conn, keepalives_interval)}, + + {"keepalives_count", NULL, NULL, NULL, + "TCP-Keepalives-Count", "", 10, /* strlen(INT32_MAX) == 10 */ + offsetof(struct pg_conn, keepalives_count)}, + + /* + * ssl options are allowed even without client SSL support because the + * client can still handle SSL modes "disable" and "allow". Other + * parameters have no effect on non-SSL connections, so there is no reason + * to exclude them since none of them are mandatory. + */ + {"sslmode", "PGSSLMODE", DefaultSSLMode, NULL, + "SSL-Mode", "", 12, /* sizeof("verify-full") == 12 */ + offsetof(struct pg_conn, sslmode)}, + + {"sslcompression", "PGSSLCOMPRESSION", "1", NULL, + "SSL-Compression", "", 1, + offsetof(struct pg_conn, sslcompression)}, + + {"sslcert", "PGSSLCERT", NULL, NULL, + "SSL-Client-Cert", "", 64, + offsetof(struct pg_conn, sslcert)}, + + {"sslkey", "PGSSLKEY", NULL, NULL, + "SSL-Client-Key", "", 64, + offsetof(struct pg_conn, sslkey)}, + + {"sslrootcert", "PGSSLROOTCERT", NULL, NULL, + "SSL-Root-Certificate", "", 64, + offsetof(struct pg_conn, sslrootcert)}, + + {"sslcrl", "PGSSLCRL", NULL, NULL, + "SSL-Revocation-List", "", 64, + offsetof(struct pg_conn, sslcrl)}, + + {"requirepeer", "PGREQUIREPEER", NULL, NULL, + "Require-Peer", "", 10, + offsetof(struct pg_conn, requirepeer)}, + +#if defined(ENABLE_GSS) || defined(ENABLE_SSPI) + /* Kerberos and GSSAPI authentication support specifying the service name */ + {"krbsrvname", "PGKRBSRVNAME", PG_KRB_SRVNAM, NULL, + "Kerberos-service-name", "", 20, + offsetof(struct pg_conn, krbsrvname)}, +#endif + +#if defined(ENABLE_GSS) && defined(ENABLE_SSPI) + + /* + * GSSAPI and SSPI both enabled, give a way to override which is used by + * default + */ + {"gsslib", "PGGSSLIB", NULL, NULL, + "GSS-library", "", 7, /* sizeof("gssapi") = 7 */ + offsetof(struct pg_conn, gsslib)}, +#endif + + {"replication", NULL, NULL, NULL, + "Replication", "D", 5, + offsetof(struct pg_conn, replication)}, + + /* Terminating entry --- MUST BE LAST */ + {NULL, NULL, NULL, NULL, + NULL, NULL, 0} +}; + +static const PQEnvironmentOption EnvironmentOptions[] = +{ + /* common user-interface settings */ + { + "PGDATESTYLE", "datestyle" + }, + { + "PGTZ", "timezone" + }, + /* internal performance-related settings */ + { + "PGGEQO", "geqo" + }, + { + NULL, NULL + } +}; + +/* The connection URI must start with either of the following designators: */ +static const char uri_designator[] = "postgresql://"; +static const char short_uri_designator[] = "postgres://"; + +static bool connectOptions1(PGconn *conn, const char *conninfo); +static bool connectOptions2(PGconn *conn); +static int connectDBStart(PGconn *conn); +static int connectDBComplete(PGconn *conn); +static PGPing internal_ping(PGconn *conn); +static PGconn *makeEmptyPGconn(void); +static bool fillPGconn(PGconn *conn, PQconninfoOption *connOptions); +static void freePGconn(PGconn *conn); +static void closePGconn(PGconn *conn); +static PQconninfoOption *conninfo_init(PQExpBuffer errorMessage); +static PQconninfoOption *parse_connection_string(const char *conninfo, + PQExpBuffer errorMessage, bool use_defaults); +static int uri_prefix_length(const char *connstr); +static bool recognized_connection_string(const char *connstr); +static PQconninfoOption *conninfo_parse(const char *conninfo, + PQExpBuffer errorMessage, bool use_defaults); +static PQconninfoOption *conninfo_array_parse(const char *const * keywords, + const char *const * values, PQExpBuffer errorMessage, + bool use_defaults, int expand_dbname); +static bool conninfo_add_defaults(PQconninfoOption *options, + PQExpBuffer errorMessage); +static PQconninfoOption *conninfo_uri_parse(const char *uri, + PQExpBuffer errorMessage, bool use_defaults); +static bool conninfo_uri_parse_options(PQconninfoOption *options, + const char *uri, PQExpBuffer errorMessage); +static bool conninfo_uri_parse_params(char *params, + PQconninfoOption *connOptions, + PQExpBuffer errorMessage); +static char *conninfo_uri_decode(const char *str, PQExpBuffer errorMessage); +static bool get_hexdigit(char digit, int *value); +static const char *conninfo_getval(PQconninfoOption *connOptions, + const char *keyword); +static PQconninfoOption *conninfo_storeval(PQconninfoOption *connOptions, + const char *keyword, const char *value, + PQExpBuffer errorMessage, bool ignoreMissing, bool uri_decode); +static PQconninfoOption *conninfo_find(PQconninfoOption *connOptions, + const char *keyword); +static void defaultNoticeReceiver(void *arg, const PGresult *res); +static void defaultNoticeProcessor(void *arg, const char *message); +static int parseServiceInfo(PQconninfoOption *options, + PQExpBuffer errorMessage); +static int parseServiceFile(const char *serviceFile, + const char *service, + PQconninfoOption *options, + PQExpBuffer errorMessage, + bool *group_found); +static char *pwdfMatchesString(char *buf, char *token); +static char *PasswordFromFile(char *hostname, char *port, char *dbname, + char *username); +static bool getPgPassFilename(char *pgpassfile); +static void dot_pg_pass_warning(PGconn *conn); +static void default_threadlock(int acquire); + + +/* global variable because fe-auth.c needs to access it */ +pgthreadlock_t pg_g_threadlock = default_threadlock; + + +/* + * pqDropConnection + * + * Close any physical connection to the server, and reset associated + * state inside the connection object. We don't release state that + * would be needed to reconnect, though. + * + * We can always flush the output buffer, since there's no longer any hope + * of sending that data. However, unprocessed input data might still be + * valuable, so the caller must tell us whether to flush that or not. + */ +void +pqDropConnection(PGconn *conn, bool flushInput) +{ + /* Drop any SSL state */ + pqsecure_close(conn); + + /* Close the socket itself */ + if (conn->sock != PGINVALID_SOCKET) + closesocket(conn->sock); + conn->sock = PGINVALID_SOCKET; + + /* Optionally discard any unread data */ + if (flushInput) + conn->inStart = conn->inCursor = conn->inEnd = 0; + + /* Always discard any unsent data */ + conn->outCount = 0; + + /* Free authentication state */ +#ifdef ENABLE_GSS + { + OM_uint32 min_s; + + if (conn->gctx) + gss_delete_sec_context(&min_s, &conn->gctx, GSS_C_NO_BUFFER); + if (conn->gtarg_nam) + gss_release_name(&min_s, &conn->gtarg_nam); + if (conn->ginbuf.length) + gss_release_buffer(&min_s, &conn->ginbuf); + if (conn->goutbuf.length) + gss_release_buffer(&min_s, &conn->goutbuf); + } +#endif +#ifdef ENABLE_SSPI + if (conn->ginbuf.length) + free(conn->ginbuf.value); + conn->ginbuf.length = 0; + conn->ginbuf.value = NULL; + if (conn->sspitarget) + free(conn->sspitarget); + conn->sspitarget = NULL; + if (conn->sspicred) + { + FreeCredentialsHandle(conn->sspicred); + free(conn->sspicred); + conn->sspicred = NULL; + } + if (conn->sspictx) + { + DeleteSecurityContext(conn->sspictx); + free(conn->sspictx); + conn->sspictx = NULL; + } + conn->usesspi = 0; +#endif +} + + +/* + * Connecting to a Database + * + * There are now six different ways a user of this API can connect to the + * database. Two are not recommended for use in new code, because of their + * lack of extensibility with respect to the passing of options to the + * backend. These are PQsetdb and PQsetdbLogin (the former now being a macro + * to the latter). + * + * If it is desired to connect in a synchronous (blocking) manner, use the + * function PQconnectdb or PQconnectdbParams. The former accepts a string of + * option = value pairs (or a URI) which must be parsed; the latter takes two + * NULL terminated arrays instead. + * + * To connect in an asynchronous (non-blocking) manner, use the functions + * PQconnectStart or PQconnectStartParams (which differ in the same way as + * PQconnectdb and PQconnectdbParams) and PQconnectPoll. + * + * Internally, the static functions connectDBStart, connectDBComplete + * are part of the connection procedure. + */ + +/* + * PQconnectdbParams + * + * establishes a connection to a postgres backend through the postmaster + * using connection information in two arrays. + * + * The keywords array is defined as + * + * const char *params[] = {"option1", "option2", NULL} + * + * The values array is defined as + * + * const char *values[] = {"value1", "value2", NULL} + * + * Returns a PGconn* which is needed for all subsequent libpq calls, or NULL + * if a memory allocation failed. + * If the status field of the connection returned is CONNECTION_BAD, + * then some fields may be null'ed out instead of having valid values. + * + * You should call PQfinish (if conn is not NULL) regardless of whether this + * call succeeded. + */ +PGconn * +PQconnectdbParams(const char *const * keywords, + const char *const * values, + int expand_dbname) +{ + PGconn *conn = PQconnectStartParams(keywords, values, expand_dbname); + + if (conn && conn->status != CONNECTION_BAD) + (void) connectDBComplete(conn); + + return conn; + +} + +/* + * PQpingParams + * + * check server status, accepting parameters identical to PQconnectdbParams + */ +PGPing +PQpingParams(const char *const * keywords, + const char *const * values, + int expand_dbname) +{ + PGconn *conn = PQconnectStartParams(keywords, values, expand_dbname); + PGPing ret; + + ret = internal_ping(conn); + PQfinish(conn); + + return ret; +} + +/* + * PQconnectdb + * + * establishes a connection to a postgres backend through the postmaster + * using connection information in a string. + * + * The conninfo string is either a whitespace-separated list of + * + * option = value + * + * definitions or a URI (refer to the documentation for details.) Value + * might be a single value containing no whitespaces or a single quoted + * string. If a single quote should appear anywhere in the value, it must be + * escaped with a backslash like \' + * + * Returns a PGconn* which is needed for all subsequent libpq calls, or NULL + * if a memory allocation failed. + * If the status field of the connection returned is CONNECTION_BAD, + * then some fields may be null'ed out instead of having valid values. + * + * You should call PQfinish (if conn is not NULL) regardless of whether this + * call succeeded. + */ +PGconn * +PQconnectdb(const char *conninfo) +{ + PGconn *conn = PQconnectStart(conninfo); + + if (conn && conn->status != CONNECTION_BAD) + (void) connectDBComplete(conn); + + return conn; +} + +/* + * PQping + * + * check server status, accepting parameters identical to PQconnectdb + */ +PGPing +PQping(const char *conninfo) +{ + PGconn *conn = PQconnectStart(conninfo); + PGPing ret; + + ret = internal_ping(conn); + PQfinish(conn); + + return ret; +} + +/* + * PQconnectStartParams + * + * Begins the establishment of a connection to a postgres backend through the + * postmaster using connection information in a struct. + * + * See comment for PQconnectdbParams for the definition of the string format. + * + * Returns a PGconn*. If NULL is returned, a malloc error has occurred, and + * you should not attempt to proceed with this connection. If the status + * field of the connection returned is CONNECTION_BAD, an error has + * occurred. In this case you should call PQfinish on the result, (perhaps + * inspecting the error message first). Other fields of the structure may not + * be valid if that occurs. If the status field is not CONNECTION_BAD, then + * this stage has succeeded - call PQconnectPoll, using select(2) to see when + * this is necessary. + * + * See PQconnectPoll for more info. + */ +PGconn * +PQconnectStartParams(const char *const * keywords, + const char *const * values, + int expand_dbname) +{ + PGconn *conn; + PQconninfoOption *connOptions; + + /* + * Allocate memory for the conn structure + */ + conn = makeEmptyPGconn(); + if (conn == NULL) + return NULL; + + /* + * Parse the conninfo arrays + */ + connOptions = conninfo_array_parse(keywords, values, + &conn->errorMessage, + true, expand_dbname); + if (connOptions == NULL) + { + conn->status = CONNECTION_BAD; + /* errorMessage is already set */ + return conn; + } + + /* + * Move option values into conn structure + */ + if (!fillPGconn(conn, connOptions)) + { + PQconninfoFree(connOptions); + return conn; + } + + /* + * Free the option info - all is in conn now + */ + PQconninfoFree(connOptions); + + /* + * Compute derived options + */ + if (!connectOptions2(conn)) + return conn; + + /* + * Connect to the database + */ + if (!connectDBStart(conn)) + { + /* Just in case we failed to set it in connectDBStart */ + conn->status = CONNECTION_BAD; + } + + return conn; +} + +/* + * PQconnectStart + * + * Begins the establishment of a connection to a postgres backend through the + * postmaster using connection information in a string. + * + * See comment for PQconnectdb for the definition of the string format. + * + * Returns a PGconn*. If NULL is returned, a malloc error has occurred, and + * you should not attempt to proceed with this connection. If the status + * field of the connection returned is CONNECTION_BAD, an error has + * occurred. In this case you should call PQfinish on the result, (perhaps + * inspecting the error message first). Other fields of the structure may not + * be valid if that occurs. If the status field is not CONNECTION_BAD, then + * this stage has succeeded - call PQconnectPoll, using select(2) to see when + * this is necessary. + * + * See PQconnectPoll for more info. + */ +PGconn * +PQconnectStart(const char *conninfo) +{ + PGconn *conn; + + /* + * Allocate memory for the conn structure + */ + conn = makeEmptyPGconn(); + if (conn == NULL) + return NULL; + + /* + * Parse the conninfo string + */ + if (!connectOptions1(conn, conninfo)) + return conn; + + /* + * Compute derived options + */ + if (!connectOptions2(conn)) + return conn; + + /* + * Connect to the database + */ + if (!connectDBStart(conn)) + { + /* Just in case we failed to set it in connectDBStart */ + conn->status = CONNECTION_BAD; + } + + return conn; +} + +/* + * Move option values into conn structure + * + * Don't put anything cute here --- intelligence should be in + * connectOptions2 ... + * + * Returns true on success. On failure, returns false and sets error message. + */ +static bool +fillPGconn(PGconn *conn, PQconninfoOption *connOptions) +{ + const internalPQconninfoOption *option; + + for (option = PQconninfoOptions; option->keyword; option++) + { + if (option->connofs >= 0) + { + const char *tmp = conninfo_getval(connOptions, option->keyword); + + if (tmp) + { + char **connmember = (char **) ((char *) conn + option->connofs); + + if (*connmember) + free(*connmember); + *connmember = strdup(tmp); + if (*connmember == NULL) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("out of memory\n")); + return false; + } + } + } + } + + return true; +} + +/* + * connectOptions1 + * + * Internal subroutine to set up connection parameters given an already- + * created PGconn and a conninfo string. Derived settings should be + * processed by calling connectOptions2 next. (We split them because + * PQsetdbLogin overrides defaults in between.) + * + * Returns true if OK, false if trouble (in which case errorMessage is set + * and so is conn->status). + */ +static bool +connectOptions1(PGconn *conn, const char *conninfo) +{ + PQconninfoOption *connOptions; + + /* + * Parse the conninfo string + */ + connOptions = parse_connection_string(conninfo, &conn->errorMessage, true); + if (connOptions == NULL) + { + conn->status = CONNECTION_BAD; + /* errorMessage is already set */ + return false; + } + + /* + * Move option values into conn structure + */ + if (!fillPGconn(conn, connOptions)) + { + conn->status = CONNECTION_BAD; + PQconninfoFree(connOptions); + return false; + } + + /* + * Free the option info - all is in conn now + */ + PQconninfoFree(connOptions); + + return true; +} + +/* + * connectOptions2 + * + * Compute derived connection options after absorbing all user-supplied info. + * + * Returns true if OK, false if trouble (in which case errorMessage is set + * and so is conn->status). + */ +static bool +connectOptions2(PGconn *conn) +{ + /* + * If user name was not given, fetch it. (Most likely, the fetch will + * fail, since the only way we get here is if pg_fe_getauthname() failed + * during conninfo_add_defaults(). But now we want an error message.) + */ + if (conn->pguser == NULL || conn->pguser[0] == '\0') + { + if (conn->pguser) + free(conn->pguser); + conn->pguser = pg_fe_getauthname(&conn->errorMessage); + if (!conn->pguser) + { + conn->status = CONNECTION_BAD; + return false; + } + } + + /* + * If database name was not given, default it to equal user name + */ + if (conn->dbName == NULL || conn->dbName[0] == '\0') + { + if (conn->dbName) + free(conn->dbName); + conn->dbName = strdup(conn->pguser); + if (!conn->dbName) + goto oom_error; + } + + /* + * Supply default password if none given + */ + if (conn->pgpass == NULL || conn->pgpass[0] == '\0') + { + if (conn->pgpass) + free(conn->pgpass); + conn->pgpass = PasswordFromFile(conn->pghost, conn->pgport, + conn->dbName, conn->pguser); + if (conn->pgpass == NULL) + { + conn->pgpass = strdup(DefaultPassword); + if (!conn->pgpass) + goto oom_error; + } + else + conn->dot_pgpass_used = true; + } + + /* + * Allow unix socket specification in the host name + */ + if (conn->pghost && is_absolute_path(conn->pghost)) + { + if (conn->pgunixsocket) + free(conn->pgunixsocket); + conn->pgunixsocket = conn->pghost; + conn->pghost = NULL; + } + + /* + * validate sslmode option + */ + if (conn->sslmode) + { + if (strcmp(conn->sslmode, "disable") != 0 + && strcmp(conn->sslmode, "allow") != 0 + && strcmp(conn->sslmode, "prefer") != 0 + && strcmp(conn->sslmode, "require") != 0 + && strcmp(conn->sslmode, "verify-ca") != 0 + && strcmp(conn->sslmode, "verify-full") != 0) + { + conn->status = CONNECTION_BAD; + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("invalid sslmode value: \"%s\"\n"), + conn->sslmode); + return false; + } + +#ifndef USE_SSL + switch (conn->sslmode[0]) + { + case 'a': /* "allow" */ + case 'p': /* "prefer" */ + + /* + * warn user that an SSL connection will never be negotiated + * since SSL was not compiled in? + */ + break; + + case 'r': /* "require" */ + case 'v': /* "verify-ca" or "verify-full" */ + conn->status = CONNECTION_BAD; + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("sslmode value \"%s\" invalid when SSL support is not compiled in\n"), + conn->sslmode); + return false; + } +#endif + } + else + { + conn->sslmode = strdup(DefaultSSLMode); + if (!conn->sslmode) + goto oom_error; + } + + /* + * Resolve special "auto" client_encoding from the locale + */ + if (conn->client_encoding_initial && + strcmp(conn->client_encoding_initial, "auto") == 0) + { + free(conn->client_encoding_initial); + conn->client_encoding_initial = strdup(pg_encoding_to_char(pg_get_encoding_from_locale(NULL, true))); + if (!conn->client_encoding_initial) + goto oom_error; + } + + /* + * Only if we get this far is it appropriate to try to connect. (We need a + * state flag, rather than just the boolean result of this function, in + * case someone tries to PQreset() the PGconn.) + */ + conn->options_valid = true; + + return true; + +oom_error: + conn->status = CONNECTION_BAD; + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("out of memory\n")); + return false; +} + +/* + * PQconndefaults + * + * Construct a default connection options array, which identifies all the + * available options and shows any default values that are available from the + * environment etc. On error (eg out of memory), NULL is returned. + * + * Using this function, an application may determine all possible options + * and their current default values. + * + * NOTE: as of PostgreSQL 7.0, the returned array is dynamically allocated + * and should be freed when no longer needed via PQconninfoFree(). (In prior + * versions, the returned array was static, but that's not thread-safe.) + * Pre-7.0 applications that use this function will see a small memory leak + * until they are updated to call PQconninfoFree. + */ +PQconninfoOption * +PQconndefaults(void) +{ + PQExpBufferData errorBuf; + PQconninfoOption *connOptions; + + /* We don't actually report any errors here, but callees want a buffer */ + initPQExpBuffer(&errorBuf); + if (PQExpBufferDataBroken(errorBuf)) + return NULL; /* out of memory already :-( */ + + connOptions = conninfo_init(&errorBuf); + if (connOptions != NULL) + { + /* pass NULL errorBuf to ignore errors */ + if (!conninfo_add_defaults(connOptions, NULL)) + { + PQconninfoFree(connOptions); + connOptions = NULL; + } + } + + termPQExpBuffer(&errorBuf); + return connOptions; +} + +/* ---------------- + * PQsetdbLogin + * + * establishes a connection to a postgres backend through the postmaster + * at the specified host and port. + * + * returns a PGconn* which is needed for all subsequent libpq calls + * + * if the status field of the connection returned is CONNECTION_BAD, + * then only the errorMessage is likely to be useful. + * ---------------- + */ +PGconn * +PQsetdbLogin(const char *pghost, const char *pgport, const char *pgoptions, + const char *pgtty, const char *dbName, const char *login, + const char *pwd) +{ + PGconn *conn; + + /* + * Allocate memory for the conn structure + */ + conn = makeEmptyPGconn(); + if (conn == NULL) + return NULL; + + /* + * If the dbName parameter contains what looks like a connection string, + * parse it into conn struct using connectOptions1. + */ + if (dbName && recognized_connection_string(dbName)) + { + if (!connectOptions1(conn, dbName)) + return conn; + } + else + { + /* + * Old-style path: first, parse an empty conninfo string in order to + * set up the same defaults that PQconnectdb() would use. + */ + if (!connectOptions1(conn, "")) + return conn; + + /* Insert dbName parameter value into struct */ + if (dbName && dbName[0] != '\0') + { + if (conn->dbName) + free(conn->dbName); + conn->dbName = strdup(dbName); + if (!conn->dbName) + goto oom_error; + } + } + + /* + * Insert remaining parameters into struct, overriding defaults (as well + * as any conflicting data from dbName taken as a conninfo). + */ + if (pghost && pghost[0] != '\0') + { + if (conn->pghost) + free(conn->pghost); + conn->pghost = strdup(pghost); + if (!conn->pghost) + goto oom_error; + } + + if (pgport && pgport[0] != '\0') + { + if (conn->pgport) + free(conn->pgport); + conn->pgport = strdup(pgport); + if (!conn->pgport) + goto oom_error; + } + + if (pgoptions && pgoptions[0] != '\0') + { + if (conn->pgoptions) + free(conn->pgoptions); + conn->pgoptions = strdup(pgoptions); + if (!conn->pgoptions) + goto oom_error; + } + + if (pgtty && pgtty[0] != '\0') + { + if (conn->pgtty) + free(conn->pgtty); + conn->pgtty = strdup(pgtty); + if (!conn->pgtty) + goto oom_error; + } + + if (login && login[0] != '\0') + { + if (conn->pguser) + free(conn->pguser); + conn->pguser = strdup(login); + if (!conn->pguser) + goto oom_error; + } + + if (pwd && pwd[0] != '\0') + { + if (conn->pgpass) + free(conn->pgpass); + conn->pgpass = strdup(pwd); + if (!conn->pgpass) + goto oom_error; + } + + /* + * Compute derived options + */ + if (!connectOptions2(conn)) + return conn; + + /* + * Connect to the database + */ + if (connectDBStart(conn)) + (void) connectDBComplete(conn); + + return conn; + +oom_error: + conn->status = CONNECTION_BAD; + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("out of memory\n")); + return conn; +} + + +/* ---------- + * connectNoDelay - + * Sets the TCP_NODELAY socket option. + * Returns 1 if successful, 0 if not. + * ---------- + */ +static int +connectNoDelay(PGconn *conn) +{ +#ifdef TCP_NODELAY + int on = 1; + + if (setsockopt(conn->sock, IPPROTO_TCP, TCP_NODELAY, + (char *) &on, + sizeof(on)) < 0) + { + char sebuf[256]; + + appendPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not set socket to TCP no delay mode: %s\n"), + SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf))); + return 0; + } +#endif + + return 1; +} + + +/* ---------- + * connectFailureMessage - + * create a friendly error message on connection failure. + * ---------- + */ +static void +connectFailureMessage(PGconn *conn, int errorno) +{ + char sebuf[256]; + +#ifdef HAVE_UNIX_SOCKETS + if (IS_AF_UNIX(conn->raddr.addr.ss_family)) + { + char service[NI_MAXHOST]; + + pg_getnameinfo_all(&conn->raddr.addr, conn->raddr.salen, + NULL, 0, + service, sizeof(service), + NI_NUMERICSERV); + appendPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not connect to server: %s\n" + "\tIs the server running locally and accepting\n" + "\tconnections on Unix domain socket \"%s\"?\n"), + SOCK_STRERROR(errorno, sebuf, sizeof(sebuf)), + service); + } + else +#endif /* HAVE_UNIX_SOCKETS */ + { + char host_addr[NI_MAXHOST]; + const char *displayed_host; + struct sockaddr_storage *addr = &conn->raddr.addr; + + /* + * Optionally display the network address with the hostname. This is + * useful to distinguish between IPv4 and IPv6 connections. + */ + if (conn->pghostaddr != NULL) + strlcpy(host_addr, conn->pghostaddr, NI_MAXHOST); + else if (addr->ss_family == AF_INET) + { + if (inet_net_ntop(AF_INET, + &((struct sockaddr_in *) addr)->sin_addr.s_addr, + 32, + host_addr, sizeof(host_addr)) == NULL) + strcpy(host_addr, "???"); + } +#ifdef HAVE_IPV6 + else if (addr->ss_family == AF_INET6) + { + if (inet_net_ntop(AF_INET6, + &((struct sockaddr_in6 *) addr)->sin6_addr.s6_addr, + 128, + host_addr, sizeof(host_addr)) == NULL) + strcpy(host_addr, "???"); + } +#endif + else + strcpy(host_addr, "???"); + + if (conn->pghostaddr && conn->pghostaddr[0] != '\0') + displayed_host = conn->pghostaddr; + else if (conn->pghost && conn->pghost[0] != '\0') + displayed_host = conn->pghost; + else + displayed_host = DefaultHost; + + /* + * If the user did not supply an IP address using 'hostaddr', and + * 'host' was missing or does not match our lookup, display the + * looked-up IP address. + */ + if ((conn->pghostaddr == NULL) && + (conn->pghost == NULL || strcmp(conn->pghost, host_addr) != 0)) + appendPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not connect to server: %s\n" + "\tIs the server running on host \"%s\" (%s) and accepting\n" + "\tTCP/IP connections on port %s?\n"), + SOCK_STRERROR(errorno, sebuf, sizeof(sebuf)), + displayed_host, + host_addr, + conn->pgport); + else + appendPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not connect to server: %s\n" + "\tIs the server running on host \"%s\" and accepting\n" + "\tTCP/IP connections on port %s?\n"), + SOCK_STRERROR(errorno, sebuf, sizeof(sebuf)), + displayed_host, + conn->pgport); + } +} + +/* + * Should we use keepalives? Returns 1 if yes, 0 if no, and -1 if + * conn->keepalives is set to a value which is not parseable as an + * integer. + */ +static int +useKeepalives(PGconn *conn) +{ + char *ep; + int val; + + if (conn->keepalives == NULL) + return 1; + val = strtol(conn->keepalives, &ep, 10); + if (*ep) + return -1; + return val != 0 ? 1 : 0; +} + +#ifndef WIN32 +/* + * Set the keepalive idle timer. + */ +static int +setKeepalivesIdle(PGconn *conn) +{ + int idle; + + if (conn->keepalives_idle == NULL) + return 1; + + idle = atoi(conn->keepalives_idle); + if (idle < 0) + idle = 0; + +#ifdef PG_TCP_KEEPALIVE_IDLE + if (setsockopt(conn->sock, IPPROTO_TCP, PG_TCP_KEEPALIVE_IDLE, + (char *) &idle, sizeof(idle)) < 0) + { + char sebuf[256]; + + appendPQExpBuffer(&conn->errorMessage, + libpq_gettext("setsockopt(%s) failed: %s\n"), + PG_TCP_KEEPALIVE_IDLE_STR, + SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf))); + return 0; + } +#endif + + return 1; +} + +/* + * Set the keepalive interval. + */ +static int +setKeepalivesInterval(PGconn *conn) +{ + int interval; + + if (conn->keepalives_interval == NULL) + return 1; + + interval = atoi(conn->keepalives_interval); + if (interval < 0) + interval = 0; + +#ifdef TCP_KEEPINTVL + if (setsockopt(conn->sock, IPPROTO_TCP, TCP_KEEPINTVL, + (char *) &interval, sizeof(interval)) < 0) + { + char sebuf[256]; + + appendPQExpBuffer(&conn->errorMessage, + libpq_gettext("setsockopt(%s) failed: %s\n"), + "TCP_KEEPINTVL", + SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf))); + return 0; + } +#endif + + return 1; +} + +/* + * Set the count of lost keepalive packets that will trigger a connection + * break. + */ +static int +setKeepalivesCount(PGconn *conn) +{ + int count; + + if (conn->keepalives_count == NULL) + return 1; + + count = atoi(conn->keepalives_count); + if (count < 0) + count = 0; + +#ifdef TCP_KEEPCNT + if (setsockopt(conn->sock, IPPROTO_TCP, TCP_KEEPCNT, + (char *) &count, sizeof(count)) < 0) + { + char sebuf[256]; + + appendPQExpBuffer(&conn->errorMessage, + libpq_gettext("setsockopt(%s) failed: %s\n"), + "TCP_KEEPCNT", + SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf))); + return 0; + } +#endif + + return 1; +} +#else /* WIN32 */ +#ifdef SIO_KEEPALIVE_VALS +/* + * Enable keepalives and set the keepalive values on Win32, + * where they are always set in one batch. + */ +static int +setKeepalivesWin32(PGconn *conn) +{ + struct tcp_keepalive ka; + DWORD retsize; + int idle = 0; + int interval = 0; + + if (conn->keepalives_idle) + idle = atoi(conn->keepalives_idle); + if (idle <= 0) + idle = 2 * 60 * 60; /* 2 hours = default */ + + if (conn->keepalives_interval) + interval = atoi(conn->keepalives_interval); + if (interval <= 0) + interval = 1; /* 1 second = default */ + + ka.onoff = 1; + ka.keepalivetime = idle * 1000; + ka.keepaliveinterval = interval * 1000; + + if (WSAIoctl(conn->sock, + SIO_KEEPALIVE_VALS, + (LPVOID) &ka, + sizeof(ka), + NULL, + 0, + &retsize, + NULL, + NULL) + != 0) + { + appendPQExpBuffer(&conn->errorMessage, + libpq_gettext("WSAIoctl(SIO_KEEPALIVE_VALS) failed: %ui\n"), + WSAGetLastError()); + return 0; + } + return 1; +} +#endif /* SIO_KEEPALIVE_VALS */ +#endif /* WIN32 */ + +/* ---------- + * connectDBStart - + * Begin the process of making a connection to the backend. + * + * Returns 1 if successful, 0 if not. + * ---------- + */ +static int +connectDBStart(PGconn *conn) +{ + int portnum; + char portstr[MAXPGPATH]; + struct addrinfo *addrs = NULL; + struct addrinfo hint; + const char *node; + int ret; + + if (!conn) + return 0; + + if (!conn->options_valid) + goto connect_errReturn; + + /* Ensure our buffers are empty */ + conn->inStart = conn->inCursor = conn->inEnd = 0; + conn->outCount = 0; + + /* + * Determine the parameters to pass to pg_getaddrinfo_all. + */ + + /* Initialize hint structure */ + MemSet(&hint, 0, sizeof(hint)); + hint.ai_socktype = SOCK_STREAM; + hint.ai_family = AF_UNSPEC; + + /* Set up port number as a string */ + if (conn->pgport != NULL && conn->pgport[0] != '\0') + { + portnum = atoi(conn->pgport); + if (portnum < 1 || portnum > 65535) + { + appendPQExpBuffer(&conn->errorMessage, + libpq_gettext("invalid port number: \"%s\"\n"), + conn->pgport); + conn->options_valid = false; + goto connect_errReturn; + } + } + else + portnum = DEF_PGPORT; + snprintf(portstr, sizeof(portstr), "%d", portnum); + + if (conn->pghostaddr != NULL && conn->pghostaddr[0] != '\0') + { + /* Using pghostaddr avoids a hostname lookup */ + node = conn->pghostaddr; + hint.ai_family = AF_UNSPEC; + hint.ai_flags = AI_NUMERICHOST; + } + else if (conn->pghost != NULL && conn->pghost[0] != '\0') + { + /* Using pghost, so we have to look-up the hostname */ + node = conn->pghost; + hint.ai_family = AF_UNSPEC; + } + else + { +#ifdef HAVE_UNIX_SOCKETS + /* pghostaddr and pghost are NULL, so use Unix domain socket */ + node = NULL; + hint.ai_family = AF_UNIX; + UNIXSOCK_PATH(portstr, portnum, conn->pgunixsocket); + if (strlen(portstr) >= UNIXSOCK_PATH_BUFLEN) + { + appendPQExpBuffer(&conn->errorMessage, + libpq_gettext("Unix-domain socket path \"%s\" is too long (maximum %d bytes)\n"), + portstr, + (int) (UNIXSOCK_PATH_BUFLEN - 1)); + conn->options_valid = false; + goto connect_errReturn; + } +#else + /* Without Unix sockets, default to localhost instead */ + node = DefaultHost; + hint.ai_family = AF_UNSPEC; +#endif /* HAVE_UNIX_SOCKETS */ + } + + /* Use pg_getaddrinfo_all() to resolve the address */ + ret = pg_getaddrinfo_all(node, portstr, &hint, &addrs); + if (ret || !addrs) + { + if (node) + appendPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not translate host name \"%s\" to address: %s\n"), + node, gai_strerror(ret)); + else + appendPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not translate Unix-domain socket path \"%s\" to address: %s\n"), + portstr, gai_strerror(ret)); + if (addrs) + pg_freeaddrinfo_all(hint.ai_family, addrs); + conn->options_valid = false; + goto connect_errReturn; + } + +#ifdef USE_SSL + /* setup values based on SSL mode */ + if (conn->sslmode[0] == 'd') /* "disable" */ + conn->allow_ssl_try = false; + else if (conn->sslmode[0] == 'a') /* "allow" */ + conn->wait_ssl_try = true; +#endif + + /* + * Set up to try to connect, with protocol 3.0 as the first attempt. + */ + conn->addrlist = addrs; + conn->addr_cur = addrs; + conn->addrlist_family = hint.ai_family; + conn->pversion = PG_PROTOCOL(3, 0); + conn->send_appname = true; + conn->status = CONNECTION_NEEDED; + + /* + * The code for processing CONNECTION_NEEDED state is in PQconnectPoll(), + * so that it can easily be re-executed if needed again during the + * asynchronous startup process. However, we must run it once here, + * because callers expect a success return from this routine to mean that + * we are in PGRES_POLLING_WRITING connection state. + */ + if (PQconnectPoll(conn) == PGRES_POLLING_WRITING) + return 1; + +connect_errReturn: + pqDropConnection(conn, true); + conn->status = CONNECTION_BAD; + return 0; +} + + +/* + * connectDBComplete + * + * Block and complete a connection. + * + * Returns 1 on success, 0 on failure. + */ +static int +connectDBComplete(PGconn *conn) +{ + PostgresPollingStatusType flag = PGRES_POLLING_WRITING; + time_t finish_time = ((time_t) -1); + + if (conn == NULL || conn->status == CONNECTION_BAD) + return 0; + + /* + * Set up a time limit, if connect_timeout isn't zero. + */ + if (conn->connect_timeout != NULL) + { + int timeout = atoi(conn->connect_timeout); + + if (timeout > 0) + { + /* + * Rounding could cause connection to fail; need at least 2 secs + */ + if (timeout < 2) + timeout = 2; + /* calculate the finish time based on start + timeout */ + finish_time = time(NULL) + timeout; + } + } + + for (;;) + { + /* + * Wait, if necessary. Note that the initial state (just after + * PQconnectStart) is to wait for the socket to select for writing. + */ + switch (flag) + { + case PGRES_POLLING_OK: + + /* + * Reset stored error messages since we now have a working + * connection + */ + resetPQExpBuffer(&conn->errorMessage); + return 1; /* success! */ + + case PGRES_POLLING_READING: + if (pqWaitTimed(1, 0, conn, finish_time)) + { + conn->status = CONNECTION_BAD; + return 0; + } + break; + + case PGRES_POLLING_WRITING: + if (pqWaitTimed(0, 1, conn, finish_time)) + { + conn->status = CONNECTION_BAD; + return 0; + } + break; + + default: + /* Just in case we failed to set it in PQconnectPoll */ + conn->status = CONNECTION_BAD; + return 0; + } + + /* + * Now try to advance the state machine. + */ + flag = PQconnectPoll(conn); + } +} + +/* ---------------- + * PQconnectPoll + * + * Poll an asynchronous connection. + * + * Returns a PostgresPollingStatusType. + * Before calling this function, use select(2) to determine when data + * has arrived.. + * + * You must call PQfinish whether or not this fails. + * + * This function and PQconnectStart are intended to allow connections to be + * made without blocking the execution of your program on remote I/O. However, + * there are a number of caveats: + * + * o If you call PQtrace, ensure that the stream object into which you trace + * will not block. + * o If you do not supply an IP address for the remote host (i.e. you + * supply a host name instead) then PQconnectStart will block on + * gethostbyname. You will be fine if using Unix sockets (i.e. by + * supplying neither a host name nor a host address). + * o If your backend wants to use Kerberos authentication then you must + * supply both a host name and a host address, otherwise this function + * may block on gethostname. + * + * ---------------- + */ +PostgresPollingStatusType +PQconnectPoll(PGconn *conn) +{ + PGresult *res; + char sebuf[256]; + int optval; + + if (conn == NULL) + return PGRES_POLLING_FAILED; + + /* Get the new data */ + switch (conn->status) + { + /* + * We really shouldn't have been polled in these two cases, but we + * can handle it. + */ + case CONNECTION_BAD: + return PGRES_POLLING_FAILED; + case CONNECTION_OK: + return PGRES_POLLING_OK; + + /* These are reading states */ + case CONNECTION_AWAITING_RESPONSE: + case CONNECTION_AUTH_OK: + { + /* Load waiting data */ + int n = pqReadData(conn); + + if (n < 0) + goto error_return; + if (n == 0) + return PGRES_POLLING_READING; + + break; + } + + /* These are writing states, so we just proceed. */ + case CONNECTION_STARTED: + case CONNECTION_MADE: + break; + + /* We allow pqSetenvPoll to decide whether to proceed. */ + case CONNECTION_SETENV: + break; + + /* Special cases: proceed without waiting. */ + case CONNECTION_SSL_STARTUP: + case CONNECTION_NEEDED: + break; + + default: + appendPQExpBufferStr(&conn->errorMessage, + libpq_gettext( + "invalid connection state, " + "probably indicative of memory corruption\n" + )); + goto error_return; + } + + +keep_going: /* We will come back to here until there is + * nothing left to do. */ + switch (conn->status) + { + case CONNECTION_NEEDED: + { + /* + * Try to initiate a connection to one of the addresses + * returned by pg_getaddrinfo_all(). conn->addr_cur is the + * next one to try. We fail when we run out of addresses. + */ + while (conn->addr_cur != NULL) + { + struct addrinfo *addr_cur = conn->addr_cur; + + /* Remember current address for possible error msg */ + memcpy(&conn->raddr.addr, addr_cur->ai_addr, + addr_cur->ai_addrlen); + conn->raddr.salen = addr_cur->ai_addrlen; + + conn->sock = socket(addr_cur->ai_family, SOCK_STREAM, 0); + if (conn->sock == PGINVALID_SOCKET) + { + /* + * ignore socket() failure if we have more addresses + * to try + */ + if (addr_cur->ai_next != NULL) + { + conn->addr_cur = addr_cur->ai_next; + continue; + } + appendPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not create socket: %s\n"), + SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf))); + break; + } + + /* + * Select socket options: no delay of outgoing data for + * TCP sockets, nonblock mode, close-on-exec. Fail if any + * of this fails. + */ + if (!IS_AF_UNIX(addr_cur->ai_family)) + { + if (!connectNoDelay(conn)) + { + pqDropConnection(conn, true); + conn->addr_cur = addr_cur->ai_next; + continue; + } + } + if (!pg_set_noblock(conn->sock)) + { + appendPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not set socket to nonblocking mode: %s\n"), + SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf))); + pqDropConnection(conn, true); + conn->addr_cur = addr_cur->ai_next; + continue; + } + +#ifdef F_SETFD + if (fcntl(conn->sock, F_SETFD, FD_CLOEXEC) == -1) + { + appendPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not set socket to close-on-exec mode: %s\n"), + SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf))); + pqDropConnection(conn, true); + conn->addr_cur = addr_cur->ai_next; + continue; + } +#endif /* F_SETFD */ + + if (!IS_AF_UNIX(addr_cur->ai_family)) + { +#ifndef WIN32 + int on = 1; +#endif + int usekeepalives = useKeepalives(conn); + int err = 0; + + if (usekeepalives < 0) + { + appendPQExpBufferStr(&conn->errorMessage, + libpq_gettext("keepalives parameter must be an integer\n")); + err = 1; + } + else if (usekeepalives == 0) + { + /* Do nothing */ + } +#ifndef WIN32 + else if (setsockopt(conn->sock, + SOL_SOCKET, SO_KEEPALIVE, + (char *) &on, sizeof(on)) < 0) + { + appendPQExpBuffer(&conn->errorMessage, + libpq_gettext("setsockopt(%s) failed: %s\n"), + "SO_KEEPALIVE", + SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf))); + err = 1; + } + else if (!setKeepalivesIdle(conn) + || !setKeepalivesInterval(conn) + || !setKeepalivesCount(conn)) + err = 1; +#else /* WIN32 */ +#ifdef SIO_KEEPALIVE_VALS + else if (!setKeepalivesWin32(conn)) + err = 1; +#endif /* SIO_KEEPALIVE_VALS */ +#endif /* WIN32 */ + + if (err) + { + pqDropConnection(conn, true); + conn->addr_cur = addr_cur->ai_next; + continue; + } + } + + /*---------- + * We have three methods of blocking SIGPIPE during + * send() calls to this socket: + * + * - setsockopt(sock, SO_NOSIGPIPE) + * - send(sock, ..., MSG_NOSIGNAL) + * - setting the signal mask to SIG_IGN during send() + * + * The third method requires three syscalls per send, + * so we prefer either of the first two, but they are + * less portable. The state is tracked in the following + * members of PGconn: + * + * conn->sigpipe_so - we have set up SO_NOSIGPIPE + * conn->sigpipe_flag - we're specifying MSG_NOSIGNAL + * + * If we can use SO_NOSIGPIPE, then set sigpipe_so here + * and we're done. Otherwise, set sigpipe_flag so that + * we will try MSG_NOSIGNAL on sends. If we get an error + * with MSG_NOSIGNAL, we'll clear that flag and revert to + * signal masking. + *---------- + */ + conn->sigpipe_so = false; +#ifdef MSG_NOSIGNAL + conn->sigpipe_flag = true; +#else + conn->sigpipe_flag = false; +#endif /* MSG_NOSIGNAL */ + +#ifdef SO_NOSIGPIPE + optval = 1; + if (setsockopt(conn->sock, SOL_SOCKET, SO_NOSIGPIPE, + (char *) &optval, sizeof(optval)) == 0) + { + conn->sigpipe_so = true; + conn->sigpipe_flag = false; + } +#endif /* SO_NOSIGPIPE */ + + /* + * Start/make connection. This should not block, since we + * are in nonblock mode. If it does, well, too bad. + */ + if (connect(conn->sock, addr_cur->ai_addr, + addr_cur->ai_addrlen) < 0) + { + if (SOCK_ERRNO == EINPROGRESS || +#ifdef WIN32 + SOCK_ERRNO == EWOULDBLOCK || +#endif + SOCK_ERRNO == EINTR) + { + /* + * This is fine - we're in non-blocking mode, and + * the connection is in progress. Tell caller to + * wait for write-ready on socket. + */ + conn->status = CONNECTION_STARTED; + return PGRES_POLLING_WRITING; + } + /* otherwise, trouble */ + } + else + { + /* + * Hm, we're connected already --- seems the "nonblock + * connection" wasn't. Advance the state machine and + * go do the next stuff. + */ + conn->status = CONNECTION_STARTED; + goto keep_going; + } + + /* + * This connection failed --- set up error report, then + * close socket (do it this way in case close() affects + * the value of errno...). We will ignore the connect() + * failure and keep going if there are more addresses. + */ + connectFailureMessage(conn, SOCK_ERRNO); + pqDropConnection(conn, true); + + /* + * Try the next address, if any. + */ + conn->addr_cur = addr_cur->ai_next; + } /* loop over addresses */ + + /* + * Ooops, no more addresses. An appropriate error message is + * already set up, so just set the right status. + */ + goto error_return; + } + + case CONNECTION_STARTED: + { + ACCEPT_TYPE_ARG3 optlen = sizeof(optval); + + /* + * Write ready, since we've made it here, so the connection + * has been made ... or has failed. + */ + + /* + * Now check (using getsockopt) that there is not an error + * state waiting for us on the socket. + */ + + if (getsockopt(conn->sock, SOL_SOCKET, SO_ERROR, + (char *) &optval, &optlen) == -1) + { + appendPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not get socket error status: %s\n"), + SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf))); + goto error_return; + } + else if (optval != 0) + { + /* + * When using a nonblocking connect, we will typically see + * connect failures at this point, so provide a friendly + * error message. + */ + connectFailureMessage(conn, optval); + pqDropConnection(conn, true); + + /* + * If more addresses remain, keep trying, just as in the + * case where connect() returned failure immediately. + */ + if (conn->addr_cur->ai_next != NULL) + { + conn->addr_cur = conn->addr_cur->ai_next; + conn->status = CONNECTION_NEEDED; + goto keep_going; + } + goto error_return; + } + + /* Fill in the client address */ + conn->laddr.salen = sizeof(conn->laddr.addr); + if (getsockname(conn->sock, + (struct sockaddr *) & conn->laddr.addr, + &conn->laddr.salen) < 0) + { + appendPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not get client address from socket: %s\n"), + SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf))); + goto error_return; + } + + /* + * Make sure we can write before advancing to next step. + */ + conn->status = CONNECTION_MADE; + return PGRES_POLLING_WRITING; + } + + case CONNECTION_MADE: + { + char *startpacket; + int packetlen; + +#ifdef HAVE_UNIX_SOCKETS + + /* + * Implement requirepeer check, if requested and it's a + * Unix-domain socket. + */ + if (conn->requirepeer && conn->requirepeer[0] && + IS_AF_UNIX(conn->raddr.addr.ss_family)) + { + char pwdbuf[BUFSIZ]; + struct passwd pass_buf; + struct passwd *pass; + int passerr; + uid_t uid; + gid_t gid; + + errno = 0; + if (getpeereid(conn->sock, &uid, &gid) != 0) + { + /* + * Provide special error message if getpeereid is a + * stub + */ + if (errno == ENOSYS) + appendPQExpBufferStr(&conn->errorMessage, + libpq_gettext("requirepeer parameter is not supported on this platform\n")); + else + appendPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not get peer credentials: %s\n"), + pqStrerror(errno, sebuf, sizeof(sebuf))); + goto error_return; + } + + passerr = pqGetpwuid(uid, &pass_buf, pwdbuf, sizeof(pwdbuf), &pass); + if (pass == NULL) + { + if (passerr != 0) + appendPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not look up local user ID %d: %s\n"), + (int) uid, + pqStrerror(passerr, sebuf, sizeof(sebuf))); + else + appendPQExpBuffer(&conn->errorMessage, + libpq_gettext("local user with ID %d does not exist\n"), + (int) uid); + goto error_return; + } + + if (strcmp(pass->pw_name, conn->requirepeer) != 0) + { + appendPQExpBuffer(&conn->errorMessage, + libpq_gettext("requirepeer specifies \"%s\", but actual peer user name is \"%s\"\n"), + conn->requirepeer, pass->pw_name); + goto error_return; + } + } +#endif /* HAVE_UNIX_SOCKETS */ + +#ifdef USE_SSL + + /* + * If SSL is enabled and we haven't already got it running, + * request it instead of sending the startup message. + */ + if (IS_AF_UNIX(conn->raddr.addr.ss_family)) + { + /* Don't bother requesting SSL over a Unix socket */ + conn->allow_ssl_try = false; + } + if (conn->allow_ssl_try && !conn->wait_ssl_try && + !conn->ssl_in_use) + { + ProtocolVersion pv; + + /* + * Send the SSL request packet. + * + * Theoretically, this could block, but it really + * shouldn't since we only got here if the socket is + * write-ready. + */ + pv = htonl(NEGOTIATE_SSL_CODE); + if (pqPacketSend(conn, 0, &pv, sizeof(pv)) != STATUS_OK) + { + appendPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not send SSL negotiation packet: %s\n"), + SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf))); + goto error_return; + } + /* Ok, wait for response */ + conn->status = CONNECTION_SSL_STARTUP; + return PGRES_POLLING_READING; + } +#endif /* USE_SSL */ + + /* + * Build the startup packet. + */ + if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3) + startpacket = pqBuildStartupPacket3(conn, &packetlen, + EnvironmentOptions); + else + startpacket = pqBuildStartupPacket2(conn, &packetlen, + EnvironmentOptions); + if (!startpacket) + { + /* + * will not appendbuffer here, since it's likely to also + * run out of memory + */ + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("out of memory\n")); + goto error_return; + } + + /* + * Send the startup packet. + * + * Theoretically, this could block, but it really shouldn't + * since we only got here if the socket is write-ready. + */ + if (pqPacketSend(conn, 0, startpacket, packetlen) != STATUS_OK) + { + appendPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not send startup packet: %s\n"), + SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf))); + free(startpacket); + goto error_return; + } + + free(startpacket); + + conn->status = CONNECTION_AWAITING_RESPONSE; + return PGRES_POLLING_READING; + } + + /* + * Handle SSL negotiation: wait for postmaster messages and + * respond as necessary. + */ + case CONNECTION_SSL_STARTUP: + { +#ifdef USE_SSL + PostgresPollingStatusType pollres; + + /* + * On first time through, get the postmaster's response to our + * SSL negotiation packet. + */ + if (!conn->ssl_in_use) + { + /* + * We use pqReadData here since it has the logic to + * distinguish no-data-yet from connection closure. Since + * conn->ssl isn't set, a plain recv() will occur. + */ + char SSLok; + int rdresult; + + rdresult = pqReadData(conn); + if (rdresult < 0) + { + /* errorMessage is already filled in */ + goto error_return; + } + if (rdresult == 0) + { + /* caller failed to wait for data */ + return PGRES_POLLING_READING; + } + if (pqGetc(&SSLok, conn) < 0) + { + /* should not happen really */ + return PGRES_POLLING_READING; + } + if (SSLok == 'S') + { + /* mark byte consumed */ + conn->inStart = conn->inCursor; + /* Set up global SSL state if required */ + if (pqsecure_initialize(conn) != 0) + goto error_return; + } + else if (SSLok == 'N') + { + /* mark byte consumed */ + conn->inStart = conn->inCursor; + /* OK to do without SSL? */ + if (conn->sslmode[0] == 'r' || /* "require" */ + conn->sslmode[0] == 'v') /* "verify-ca" or + * "verify-full" */ + { + /* Require SSL, but server does not want it */ + appendPQExpBufferStr(&conn->errorMessage, + libpq_gettext("server does not support SSL, but SSL was required\n")); + goto error_return; + } + /* Otherwise, proceed with normal startup */ + conn->allow_ssl_try = false; + conn->status = CONNECTION_MADE; + return PGRES_POLLING_WRITING; + } + else if (SSLok == 'E') + { + /* + * Server failure of some sort, such as failure to + * fork a backend process. We need to process and + * report the error message, which might be formatted + * according to either protocol 2 or protocol 3. + * Rather than duplicate the code for that, we flip + * into AWAITING_RESPONSE state and let the code there + * deal with it. Note we have *not* consumed the "E" + * byte here. + */ + conn->status = CONNECTION_AWAITING_RESPONSE; + goto keep_going; + } + else + { + appendPQExpBuffer(&conn->errorMessage, + libpq_gettext("received invalid response to SSL negotiation: %c\n"), + SSLok); + goto error_return; + } + } + + /* + * Begin or continue the SSL negotiation process. + */ + pollres = pqsecure_open_client(conn); + if (pollres == PGRES_POLLING_OK) + { + /* SSL handshake done, ready to send startup packet */ + conn->status = CONNECTION_MADE; + return PGRES_POLLING_WRITING; + } + if (pollres == PGRES_POLLING_FAILED) + { + /* + * Failed ... if sslmode is "prefer" then do a non-SSL + * retry + */ + if (conn->sslmode[0] == 'p' /* "prefer" */ + && conn->allow_ssl_try /* redundant? */ + && !conn->wait_ssl_try) /* redundant? */ + { + /* only retry once */ + conn->allow_ssl_try = false; + /* Must drop the old connection */ + pqDropConnection(conn, true); + conn->status = CONNECTION_NEEDED; + goto keep_going; + } + } + return pollres; +#else /* !USE_SSL */ + /* can't get here */ + goto error_return; +#endif /* USE_SSL */ + } + + /* + * Handle authentication exchange: wait for postmaster messages + * and respond as necessary. + */ + case CONNECTION_AWAITING_RESPONSE: + { + char beresp; + int msgLength; + int avail; + AuthRequest areq; + + /* + * Scan the message from current point (note that if we find + * the message is incomplete, we will return without advancing + * inStart, and resume here next time). + */ + conn->inCursor = conn->inStart; + + /* Read type byte */ + if (pqGetc(&beresp, conn)) + { + /* We'll come back when there is more data */ + return PGRES_POLLING_READING; + } + + /* + * Validate message type: we expect only an authentication + * request or an error here. Anything else probably means + * it's not Postgres on the other end at all. + */ + if (!(beresp == 'R' || beresp == 'E')) + { + appendPQExpBuffer(&conn->errorMessage, + libpq_gettext( + "expected authentication request from " + "server, but received %c\n"), + beresp); + goto error_return; + } + + if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3) + { + /* Read message length word */ + if (pqGetInt(&msgLength, 4, conn)) + { + /* We'll come back when there is more data */ + return PGRES_POLLING_READING; + } + } + else + { + /* Set phony message length to disable checks below */ + msgLength = 8; + } + + /* + * Try to validate message length before using it. + * Authentication requests can't be very large, although GSS + * auth requests may not be that small. Errors can be a + * little larger, but not huge. If we see a large apparent + * length in an error, it means we're really talking to a + * pre-3.0-protocol server; cope. + */ + if (beresp == 'R' && (msgLength < 8 || msgLength > 2000)) + { + appendPQExpBuffer(&conn->errorMessage, + libpq_gettext( + "expected authentication request from " + "server, but received %c\n"), + beresp); + goto error_return; + } + + if (beresp == 'E' && (msgLength < 8 || msgLength > 30000)) + { + /* Handle error from a pre-3.0 server */ + conn->inCursor = conn->inStart + 1; /* reread data */ + if (pqGets_append(&conn->errorMessage, conn)) + { + /* We'll come back when there is more data */ + return PGRES_POLLING_READING; + } + /* OK, we read the message; mark data consumed */ + conn->inStart = conn->inCursor; + + /* + * The postmaster typically won't end its message with a + * newline, so add one to conform to libpq conventions. + */ + appendPQExpBufferChar(&conn->errorMessage, '\n'); + + /* + * If we tried to open the connection in 3.0 protocol, + * fall back to 2.0 protocol. + */ + if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3) + { + conn->pversion = PG_PROTOCOL(2, 0); + /* Must drop the old connection */ + pqDropConnection(conn, true); + conn->status = CONNECTION_NEEDED; + goto keep_going; + } + + goto error_return; + } + + /* + * Can't process if message body isn't all here yet. + * + * (In protocol 2.0 case, we are assuming messages carry at + * least 4 bytes of data.) + */ + msgLength -= 4; + avail = conn->inEnd - conn->inCursor; + if (avail < msgLength) + { + /* + * Before returning, try to enlarge the input buffer if + * needed to hold the whole message; see notes in + * pqParseInput3. + */ + if (pqCheckInBufferSpace(conn->inCursor + (size_t) msgLength, + conn)) + goto error_return; + /* We'll come back when there is more data */ + return PGRES_POLLING_READING; + } + + /* Handle errors. */ + if (beresp == 'E') + { + if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3) + { + if (pqGetErrorNotice3(conn, true)) + { + /* We'll come back when there is more data */ + return PGRES_POLLING_READING; + } + } + else + { + if (pqGets_append(&conn->errorMessage, conn)) + { + /* We'll come back when there is more data */ + return PGRES_POLLING_READING; + } + } + /* OK, we read the message; mark data consumed */ + conn->inStart = conn->inCursor; + +#ifdef USE_SSL + + /* + * if sslmode is "allow" and we haven't tried an SSL + * connection already, then retry with an SSL connection + */ + if (conn->sslmode[0] == 'a' /* "allow" */ + && !conn->ssl_in_use + && conn->allow_ssl_try + && conn->wait_ssl_try) + { + /* only retry once */ + conn->wait_ssl_try = false; + /* Must drop the old connection */ + pqDropConnection(conn, true); + conn->status = CONNECTION_NEEDED; + goto keep_going; + } + + /* + * if sslmode is "prefer" and we're in an SSL connection, + * then do a non-SSL retry + */ + if (conn->sslmode[0] == 'p' /* "prefer" */ + && conn->allow_ssl_try + && !conn->wait_ssl_try) /* redundant? */ + { + /* only retry once */ + conn->allow_ssl_try = false; + /* Must drop the old connection */ + pqDropConnection(conn, true); + conn->status = CONNECTION_NEEDED; + goto keep_going; + } +#endif + + goto error_return; + } + + /* It is an authentication request. */ + conn->auth_req_received = true; + + /* Get the type of request. */ + if (pqGetInt((int *) &areq, 4, conn)) + { + /* We'll come back when there are more data */ + return PGRES_POLLING_READING; + } + + /* Get the password salt if there is one. */ + if (areq == AUTH_REQ_MD5) + { + if (pqGetnchar(conn->md5Salt, + sizeof(conn->md5Salt), conn)) + { + /* We'll come back when there are more data */ + return PGRES_POLLING_READING; + } + } +#if defined(ENABLE_GSS) || defined(ENABLE_SSPI) + + /* + * Continue GSSAPI/SSPI authentication + */ + if (areq == AUTH_REQ_GSS_CONT) + { + int llen = msgLength - 4; + + /* + * We can be called repeatedly for the same buffer. Avoid + * re-allocating the buffer in this case - just re-use the + * old buffer. + */ + if (llen != conn->ginbuf.length) + { + if (conn->ginbuf.value) + free(conn->ginbuf.value); + + conn->ginbuf.length = llen; + conn->ginbuf.value = malloc(llen); + if (!conn->ginbuf.value) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("out of memory allocating GSSAPI buffer (%d)"), + llen); + goto error_return; + } + } + + if (pqGetnchar(conn->ginbuf.value, llen, conn)) + { + /* We'll come back when there is more data. */ + return PGRES_POLLING_READING; + } + } +#endif + + /* + * OK, we successfully read the message; mark data consumed + */ + conn->inStart = conn->inCursor; + + /* Respond to the request if necessary. */ + + /* + * Note that conn->pghost must be non-NULL if we are going to + * avoid the Kerberos code doing a hostname look-up. + */ + + if (pg_fe_sendauth(areq, conn) != STATUS_OK) + { + conn->errorMessage.len = strlen(conn->errorMessage.data); + goto error_return; + } + conn->errorMessage.len = strlen(conn->errorMessage.data); + + /* + * Just make sure that any data sent by pg_fe_sendauth is + * flushed out. Although this theoretically could block, it + * really shouldn't since we don't send large auth responses. + */ + if (pqFlush(conn)) + goto error_return; + + if (areq == AUTH_REQ_OK) + { + /* We are done with authentication exchange */ + conn->status = CONNECTION_AUTH_OK; + + /* + * Set asyncStatus so that PQgetResult will think that + * what comes back next is the result of a query. See + * below. + */ + conn->asyncStatus = PGASYNC_BUSY; + } + + /* Look to see if we have more data yet. */ + goto keep_going; + } + + case CONNECTION_AUTH_OK: + { + /* + * Now we expect to hear from the backend. A ReadyForQuery + * message indicates that startup is successful, but we might + * also get an Error message indicating failure. (Notice + * messages indicating nonfatal warnings are also allowed by + * the protocol, as are ParameterStatus and BackendKeyData + * messages.) Easiest way to handle this is to let + * PQgetResult() read the messages. We just have to fake it + * out about the state of the connection, by setting + * asyncStatus = PGASYNC_BUSY (done above). + */ + + if (PQisBusy(conn)) + return PGRES_POLLING_READING; + + res = PQgetResult(conn); + + /* + * NULL return indicating we have gone to IDLE state is + * expected + */ + if (res) + { + if (res->resultStatus != PGRES_FATAL_ERROR) + appendPQExpBufferStr(&conn->errorMessage, + libpq_gettext("unexpected message from server during startup\n")); + else if (conn->send_appname && + (conn->appname || conn->fbappname)) + { + /* + * If we tried to send application_name, check to see + * if the error is about that --- pre-9.0 servers will + * reject it at this stage of the process. If so, + * close the connection and retry without sending + * application_name. We could possibly get a false + * SQLSTATE match here and retry uselessly, but there + * seems no great harm in that; we'll just get the + * same error again if it's unrelated. + */ + const char *sqlstate; + + sqlstate = PQresultErrorField(res, PG_DIAG_SQLSTATE); + if (sqlstate && + strcmp(sqlstate, ERRCODE_APPNAME_UNKNOWN) == 0) + { + PQclear(res); + conn->send_appname = false; + /* Must drop the old connection */ + pqDropConnection(conn, true); + conn->status = CONNECTION_NEEDED; + goto keep_going; + } + } + + /* + * if the resultStatus is FATAL, then conn->errorMessage + * already has a copy of the error; needn't copy it back. + * But add a newline if it's not there already, since + * postmaster error messages may not have one. + */ + if (conn->errorMessage.len <= 0 || + conn->errorMessage.data[conn->errorMessage.len - 1] != '\n') + appendPQExpBufferChar(&conn->errorMessage, '\n'); + PQclear(res); + goto error_return; + } + + /* We can release the address list now. */ + pg_freeaddrinfo_all(conn->addrlist_family, conn->addrlist); + conn->addrlist = NULL; + conn->addr_cur = NULL; + + /* Fire up post-connection housekeeping if needed */ + if (PG_PROTOCOL_MAJOR(conn->pversion) < 3) + { + conn->status = CONNECTION_SETENV; + conn->setenv_state = SETENV_STATE_CLIENT_ENCODING_SEND; + conn->next_eo = EnvironmentOptions; + return PGRES_POLLING_WRITING; + } + + /* Otherwise, we are open for business! */ + conn->status = CONNECTION_OK; + return PGRES_POLLING_OK; + } + + case CONNECTION_SETENV: + + /* + * Do post-connection housekeeping (only needed in protocol 2.0). + * + * We pretend that the connection is OK for the duration of these + * queries. + */ + conn->status = CONNECTION_OK; + + switch (pqSetenvPoll(conn)) + { + case PGRES_POLLING_OK: /* Success */ + break; + + case PGRES_POLLING_READING: /* Still going */ + conn->status = CONNECTION_SETENV; + return PGRES_POLLING_READING; + + case PGRES_POLLING_WRITING: /* Still going */ + conn->status = CONNECTION_SETENV; + return PGRES_POLLING_WRITING; + + default: + goto error_return; + } + + /* We are open for business! */ + conn->status = CONNECTION_OK; + return PGRES_POLLING_OK; + + default: + appendPQExpBuffer(&conn->errorMessage, + libpq_gettext("invalid connection state %d, " + "probably indicative of memory corruption\n"), + conn->status); + goto error_return; + } + + /* Unreachable */ + +error_return: + + dot_pg_pass_warning(conn); + + /* + * We used to close the socket at this point, but that makes it awkward + * for those above us if they wish to remove this socket from their own + * records (an fd_set for example). We'll just have this socket closed + * when PQfinish is called (which is compulsory even after an error, since + * the connection structure must be freed). + */ + conn->status = CONNECTION_BAD; + return PGRES_POLLING_FAILED; +} + + +/* + * internal_ping + * Determine if a server is running and if we can connect to it. + * + * The argument is a connection that's been started, but not completed. + */ +static PGPing +internal_ping(PGconn *conn) +{ + /* Say "no attempt" if we never got to PQconnectPoll */ + if (!conn || !conn->options_valid) + return PQPING_NO_ATTEMPT; + + /* Attempt to complete the connection */ + if (conn->status != CONNECTION_BAD) + (void) connectDBComplete(conn); + + /* Definitely OK if we succeeded */ + if (conn->status != CONNECTION_BAD) + return PQPING_OK; + + /* + * Here begins the interesting part of "ping": determine the cause of the + * failure in sufficient detail to decide what to return. We do not want + * to report that the server is not up just because we didn't have a valid + * password, for example. In fact, any sort of authentication request + * implies the server is up. (We need this check since the libpq side of + * things might have pulled the plug on the connection before getting an + * error as such from the postmaster.) + */ + if (conn->auth_req_received) + return PQPING_OK; + + /* + * If we failed to get any ERROR response from the postmaster, report + * PQPING_NO_RESPONSE. This result could be somewhat misleading for a + * pre-7.4 server, since it won't send back a SQLSTATE, but those are long + * out of support. Another corner case where the server could return a + * failure without a SQLSTATE is fork failure, but NO_RESPONSE isn't + * totally unreasonable for that anyway. We expect that every other + * failure case in a modern server will produce a report with a SQLSTATE. + * + * NOTE: whenever we get around to making libpq generate SQLSTATEs for + * client-side errors, we should either not store those into + * last_sqlstate, or add an extra flag so we can tell client-side errors + * apart from server-side ones. + */ + if (strlen(conn->last_sqlstate) != 5) + return PQPING_NO_RESPONSE; + + /* + * Report PQPING_REJECT if server says it's not accepting connections. (We + * distinguish this case mainly for the convenience of pg_ctl.) + */ + if (strcmp(conn->last_sqlstate, ERRCODE_CANNOT_CONNECT_NOW) == 0) + return PQPING_REJECT; + + /* + * Any other SQLSTATE can be taken to indicate that the server is up. + * Presumably it didn't like our username, password, or database name; or + * perhaps it had some transient failure, but that should not be taken as + * meaning "it's down". + */ + return PQPING_OK; +} + + +/* + * makeEmptyPGconn + * - create a PGconn data structure with (as yet) no interesting data + */ +static PGconn * +makeEmptyPGconn(void) +{ + PGconn *conn; + +#ifdef WIN32 + + /* + * Make sure socket support is up and running. + */ + WSADATA wsaData; + + if (WSAStartup(MAKEWORD(1, 1), &wsaData)) + return NULL; + WSASetLastError(0); +#endif + + conn = (PGconn *) malloc(sizeof(PGconn)); + if (conn == NULL) + { +#ifdef WIN32 + WSACleanup(); +#endif + return conn; + } + + /* Zero all pointers and booleans */ + MemSet(conn, 0, sizeof(PGconn)); + + /* install default notice hooks */ + conn->noticeHooks.noticeRec = defaultNoticeReceiver; + conn->noticeHooks.noticeProc = defaultNoticeProcessor; + + conn->status = CONNECTION_BAD; + conn->asyncStatus = PGASYNC_IDLE; + conn->xactStatus = PQTRANS_IDLE; + conn->options_valid = false; + conn->nonblocking = false; + conn->setenv_state = SETENV_STATE_IDLE; + conn->client_encoding = PG_SQL_ASCII; + conn->std_strings = false; /* unless server says differently */ + conn->verbosity = PQERRORS_DEFAULT; + conn->show_context = PQSHOW_CONTEXT_ERRORS; + conn->sock = PGINVALID_SOCKET; + conn->auth_req_received = false; + conn->password_needed = false; + conn->dot_pgpass_used = false; +#ifdef USE_SSL + conn->allow_ssl_try = true; + conn->wait_ssl_try = false; + conn->ssl_in_use = false; +#endif + + /* + * We try to send at least 8K at a time, which is the usual size of pipe + * buffers on Unix systems. That way, when we are sending a large amount + * of data, we avoid incurring extra kernel context swaps for partial + * bufferloads. The output buffer is initially made 16K in size, and we + * try to dump it after accumulating 8K. + * + * With the same goal of minimizing context swaps, the input buffer will + * be enlarged anytime it has less than 8K free, so we initially allocate + * twice that. + */ + conn->inBufSize = 16 * 1024; + conn->inBuffer = (char *) malloc(conn->inBufSize); + conn->outBufSize = 16 * 1024; + conn->outBuffer = (char *) malloc(conn->outBufSize); + conn->rowBufLen = 32; + conn->rowBuf = (PGdataValue *) malloc(conn->rowBufLen * sizeof(PGdataValue)); + initPQExpBuffer(&conn->errorMessage); + initPQExpBuffer(&conn->workBuffer); + + if (conn->inBuffer == NULL || + conn->outBuffer == NULL || + conn->rowBuf == NULL || + PQExpBufferBroken(&conn->errorMessage) || + PQExpBufferBroken(&conn->workBuffer)) + { + /* out of memory already :-( */ + freePGconn(conn); + conn = NULL; + } + + return conn; +} + +/* + * freePGconn + * - free an idle (closed) PGconn data structure + * + * NOTE: this should not overlap any functionality with closePGconn(). + * Clearing/resetting of transient state belongs there; what we do here is + * release data that is to be held for the life of the PGconn structure. + * If a value ought to be cleared/freed during PQreset(), do it there not here. + */ +static void +freePGconn(PGconn *conn) +{ + int i; + + /* let any event procs clean up their state data */ + for (i = 0; i < conn->nEvents; i++) + { + PGEventConnDestroy evt; + + evt.conn = conn; + (void) conn->events[i].proc(PGEVT_CONNDESTROY, &evt, + conn->events[i].passThrough); + free(conn->events[i].name); + } + + if (conn->client_encoding_initial) + free(conn->client_encoding_initial); + if (conn->events) + free(conn->events); + if (conn->pghost) + free(conn->pghost); + if (conn->pghostaddr) + free(conn->pghostaddr); + if (conn->pgport) + free(conn->pgport); + if (conn->pgunixsocket) + free(conn->pgunixsocket); + if (conn->pgtty) + free(conn->pgtty); + if (conn->connect_timeout) + free(conn->connect_timeout); + if (conn->pgoptions) + free(conn->pgoptions); + if (conn->appname) + free(conn->appname); + if (conn->fbappname) + free(conn->fbappname); + if (conn->dbName) + free(conn->dbName); + if (conn->replication) + free(conn->replication); + if (conn->pguser) + free(conn->pguser); + if (conn->pgpass) + free(conn->pgpass); + if (conn->keepalives) + free(conn->keepalives); + if (conn->keepalives_idle) + free(conn->keepalives_idle); + if (conn->keepalives_interval) + free(conn->keepalives_interval); + if (conn->keepalives_count) + free(conn->keepalives_count); + if (conn->sslmode) + free(conn->sslmode); + if (conn->sslcert) + free(conn->sslcert); + if (conn->sslkey) + free(conn->sslkey); + if (conn->sslrootcert) + free(conn->sslrootcert); + if (conn->sslcrl) + free(conn->sslcrl); + if (conn->sslcompression) + free(conn->sslcompression); + if (conn->requirepeer) + free(conn->requirepeer); +#if defined(ENABLE_GSS) || defined(ENABLE_SSPI) + if (conn->krbsrvname) + free(conn->krbsrvname); +#endif +#if defined(ENABLE_GSS) && defined(ENABLE_SSPI) + if (conn->gsslib) + free(conn->gsslib); +#endif + /* Note that conn->Pfdebug is not ours to close or free */ + if (conn->last_query) + free(conn->last_query); + if (conn->inBuffer) + free(conn->inBuffer); + if (conn->outBuffer) + free(conn->outBuffer); + if (conn->rowBuf) + free(conn->rowBuf); + termPQExpBuffer(&conn->errorMessage); + termPQExpBuffer(&conn->workBuffer); + + free(conn); + +#ifdef WIN32 + WSACleanup(); +#endif +} + +/* + * closePGconn + * - properly close a connection to the backend + * + * This should reset or release all transient state, but NOT the connection + * parameters. On exit, the PGconn should be in condition to start a fresh + * connection with the same parameters (see PQreset()). + */ +static void +closePGconn(PGconn *conn) +{ + PGnotify *notify; + pgParameterStatus *pstatus; + + /* + * Note that the protocol doesn't allow us to send Terminate messages + * during the startup phase. + */ + if (conn->sock != PGINVALID_SOCKET && conn->status == CONNECTION_OK) + { + /* + * Try to send "close connection" message to backend. Ignore any + * error. + */ + pqPutMsgStart('X', false, conn); + pqPutMsgEnd(conn); + (void) pqFlush(conn); + } + + /* + * Must reset the blocking status so a possible reconnect will work. + * + * Don't call PQsetnonblocking() because it will fail if it's unable to + * flush the connection. + */ + conn->nonblocking = FALSE; + + /* + * Close the connection, reset all transient state, flush I/O buffers. + */ + pqDropConnection(conn, true); + conn->status = CONNECTION_BAD; /* Well, not really _bad_ - just + * absent */ + conn->asyncStatus = PGASYNC_IDLE; + pqClearAsyncResult(conn); /* deallocate result */ + resetPQExpBuffer(&conn->errorMessage); + pg_freeaddrinfo_all(conn->addrlist_family, conn->addrlist); + conn->addrlist = NULL; + conn->addr_cur = NULL; + notify = conn->notifyHead; + while (notify != NULL) + { + PGnotify *prev = notify; + + notify = notify->next; + free(prev); + } + conn->notifyHead = conn->notifyTail = NULL; + pstatus = conn->pstatus; + while (pstatus != NULL) + { + pgParameterStatus *prev = pstatus; + + pstatus = pstatus->next; + free(prev); + } + conn->pstatus = NULL; + if (conn->lobjfuncs) + free(conn->lobjfuncs); + conn->lobjfuncs = NULL; +} + +/* + * PQfinish: properly close a connection to the backend. Also frees + * the PGconn data structure so it shouldn't be re-used after this. + */ +void +PQfinish(PGconn *conn) +{ + if (conn) + { + closePGconn(conn); + freePGconn(conn); + } +} + +/* + * PQreset: resets the connection to the backend by closing the + * existing connection and creating a new one. + */ +void +PQreset(PGconn *conn) +{ + if (conn) + { + closePGconn(conn); + + if (connectDBStart(conn) && connectDBComplete(conn)) + { + /* + * Notify event procs of successful reset. We treat an event proc + * failure as disabling the connection ... good idea? + */ + int i; + + for (i = 0; i < conn->nEvents; i++) + { + PGEventConnReset evt; + + evt.conn = conn; + if (!conn->events[i].proc(PGEVT_CONNRESET, &evt, + conn->events[i].passThrough)) + { + conn->status = CONNECTION_BAD; + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("PGEventProc \"%s\" failed during PGEVT_CONNRESET event\n"), + conn->events[i].name); + break; + } + } + } + } +} + + +/* + * PQresetStart: + * resets the connection to the backend + * closes the existing connection and makes a new one + * Returns 1 on success, 0 on failure. + */ +int +PQresetStart(PGconn *conn) +{ + if (conn) + { + closePGconn(conn); + + return connectDBStart(conn); + } + + return 0; +} + + +/* + * PQresetPoll: + * resets the connection to the backend + * closes the existing connection and makes a new one + */ +PostgresPollingStatusType +PQresetPoll(PGconn *conn) +{ + if (conn) + { + PostgresPollingStatusType status = PQconnectPoll(conn); + + if (status == PGRES_POLLING_OK) + { + /* + * Notify event procs of successful reset. We treat an event proc + * failure as disabling the connection ... good idea? + */ + int i; + + for (i = 0; i < conn->nEvents; i++) + { + PGEventConnReset evt; + + evt.conn = conn; + if (!conn->events[i].proc(PGEVT_CONNRESET, &evt, + conn->events[i].passThrough)) + { + conn->status = CONNECTION_BAD; + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("PGEventProc \"%s\" failed during PGEVT_CONNRESET event\n"), + conn->events[i].name); + return PGRES_POLLING_FAILED; + } + } + } + + return status; + } + + return PGRES_POLLING_FAILED; +} + +/* + * PQgetCancel: get a PGcancel structure corresponding to a connection. + * + * A copy is needed to be able to cancel a running query from a different + * thread. If the same structure is used all structure members would have + * to be individually locked (if the entire structure was locked, it would + * be impossible to cancel a synchronous query because the structure would + * have to stay locked for the duration of the query). + */ +PGcancel * +PQgetCancel(PGconn *conn) +{ + PGcancel *cancel; + + if (!conn) + return NULL; + + if (conn->sock == PGINVALID_SOCKET) + return NULL; + + cancel = malloc(sizeof(PGcancel)); + if (cancel == NULL) + return NULL; + + memcpy(&cancel->raddr, &conn->raddr, sizeof(SockAddr)); + cancel->be_pid = conn->be_pid; + cancel->be_key = conn->be_key; + + return cancel; +} + +/* PQfreeCancel: free a cancel structure */ +void +PQfreeCancel(PGcancel *cancel) +{ + if (cancel) + free(cancel); +} + + +/* + * PQcancel and PQrequestCancel: attempt to request cancellation of the + * current operation. + * + * The return value is TRUE if the cancel request was successfully + * dispatched, FALSE if not (in which case an error message is available). + * Note: successful dispatch is no guarantee that there will be any effect at + * the backend. The application must read the operation result as usual. + * + * CAUTION: we want this routine to be safely callable from a signal handler + * (for example, an application might want to call it in a SIGINT handler). + * This means we cannot use any C library routine that might be non-reentrant. + * malloc/free are often non-reentrant, and anything that might call them is + * just as dangerous. We avoid sprintf here for that reason. Building up + * error messages with strcpy/strcat is tedious but should be quite safe. + * We also save/restore errno in case the signal handler support doesn't. + * + * internal_cancel() is an internal helper function to make code-sharing + * between the two versions of the cancel function possible. + */ +static int +internal_cancel(SockAddr *raddr, int be_pid, int be_key, + char *errbuf, int errbufsize) +{ + int save_errno = SOCK_ERRNO; + pgsocket tmpsock = PGINVALID_SOCKET; + char sebuf[256]; + int maxlen; + struct + { + uint32 packetlen; + CancelRequestPacket cp; + } crp; + + /* + * We need to open a temporary connection to the postmaster. Do this with + * only kernel calls. + */ + if ((tmpsock = socket(raddr->addr.ss_family, SOCK_STREAM, 0)) == PGINVALID_SOCKET) + { + strlcpy(errbuf, "PQcancel() -- socket() failed: ", errbufsize); + goto cancel_errReturn; + } +retry3: + if (connect(tmpsock, (struct sockaddr *) & raddr->addr, + raddr->salen) < 0) + { + if (SOCK_ERRNO == EINTR) + /* Interrupted system call - we'll just try again */ + goto retry3; + strlcpy(errbuf, "PQcancel() -- connect() failed: ", errbufsize); + goto cancel_errReturn; + } + + /* + * We needn't set nonblocking I/O or NODELAY options here. + */ + + /* Create and send the cancel request packet. */ + + crp.packetlen = htonl((uint32) sizeof(crp)); + crp.cp.cancelRequestCode = (MsgType) htonl(CANCEL_REQUEST_CODE); + crp.cp.backendPID = htonl(be_pid); + crp.cp.cancelAuthCode = htonl(be_key); + +retry4: + if (send(tmpsock, (char *) &crp, sizeof(crp), 0) != (int) sizeof(crp)) + { + if (SOCK_ERRNO == EINTR) + /* Interrupted system call - we'll just try again */ + goto retry4; + strlcpy(errbuf, "PQcancel() -- send() failed: ", errbufsize); + goto cancel_errReturn; + } + + /* + * Wait for the postmaster to close the connection, which indicates that + * it's processed the request. Without this delay, we might issue another + * command only to find that our cancel zaps that command instead of the + * one we thought we were canceling. Note we don't actually expect this + * read to obtain any data, we are just waiting for EOF to be signaled. + */ +retry5: + if (recv(tmpsock, (char *) &crp, 1, 0) < 0) + { + if (SOCK_ERRNO == EINTR) + /* Interrupted system call - we'll just try again */ + goto retry5; + /* we ignore other error conditions */ + } + + /* All done */ + closesocket(tmpsock); + SOCK_ERRNO_SET(save_errno); + return TRUE; + +cancel_errReturn: + + /* + * Make sure we don't overflow the error buffer. Leave space for the \n at + * the end, and for the terminating zero. + */ + maxlen = errbufsize - strlen(errbuf) - 2; + if (maxlen >= 0) + { + strncat(errbuf, SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)), + maxlen); + strcat(errbuf, "\n"); + } + if (tmpsock != PGINVALID_SOCKET) + closesocket(tmpsock); + SOCK_ERRNO_SET(save_errno); + return FALSE; +} + +/* + * PQcancel: request query cancel + * + * Returns TRUE if able to send the cancel request, FALSE if not. + * + * On failure, an error message is stored in *errbuf, which must be of size + * errbufsize (recommended size is 256 bytes). *errbuf is not changed on + * success return. + */ +int +PQcancel(PGcancel *cancel, char *errbuf, int errbufsize) +{ + if (!cancel) + { + strlcpy(errbuf, "PQcancel() -- no cancel object supplied", errbufsize); + return FALSE; + } + + return internal_cancel(&cancel->raddr, cancel->be_pid, cancel->be_key, + errbuf, errbufsize); +} + +/* + * PQrequestCancel: old, not thread-safe function for requesting query cancel + * + * Returns TRUE if able to send the cancel request, FALSE if not. + * + * On failure, the error message is saved in conn->errorMessage; this means + * that this can't be used when there might be other active operations on + * the connection object. + * + * NOTE: error messages will be cut off at the current size of the + * error message buffer, since we dare not try to expand conn->errorMessage! + */ +int +PQrequestCancel(PGconn *conn) +{ + int r; + + /* Check we have an open connection */ + if (!conn) + return FALSE; + + if (conn->sock == PGINVALID_SOCKET) + { + strlcpy(conn->errorMessage.data, + "PQrequestCancel() -- connection is not open\n", + conn->errorMessage.maxlen); + conn->errorMessage.len = strlen(conn->errorMessage.data); + + return FALSE; + } + + r = internal_cancel(&conn->raddr, conn->be_pid, conn->be_key, + conn->errorMessage.data, conn->errorMessage.maxlen); + + if (!r) + conn->errorMessage.len = strlen(conn->errorMessage.data); + + return r; +} + + +/* + * pqPacketSend() -- convenience routine to send a message to server. + * + * pack_type: the single-byte message type code. (Pass zero for startup + * packets, which have no message type code.) + * + * buf, buf_len: contents of message. The given length includes only what + * is in buf; the message type and message length fields are added here. + * + * RETURNS: STATUS_ERROR if the write fails, STATUS_OK otherwise. + * SIDE_EFFECTS: may block. + * + * Note: all messages sent with this routine have a length word, whether + * it's protocol 2.0 or 3.0. + */ +int +pqPacketSend(PGconn *conn, char pack_type, + const void *buf, size_t buf_len) +{ + /* Start the message. */ + if (pqPutMsgStart(pack_type, true, conn)) + return STATUS_ERROR; + + /* Send the message body. */ + if (pqPutnchar(buf, buf_len, conn)) + return STATUS_ERROR; + + /* Finish the message. */ + if (pqPutMsgEnd(conn)) + return STATUS_ERROR; + + /* Flush to ensure backend gets it. */ + if (pqFlush(conn)) + return STATUS_ERROR; + + return STATUS_OK; +} + +#ifdef USE_LDAP + +#define LDAP_URL "ldap://" +#define LDAP_DEF_PORT 389 +#define PGLDAP_TIMEOUT 2 + +#define ld_is_sp_tab(x) ((x) == ' ' || (x) == '\t') +#define ld_is_nl_cr(x) ((x) == '\r' || (x) == '\n') + + +/* + * ldapServiceLookup + * + * Search the LDAP URL passed as first argument, treat the result as a + * string of connection options that are parsed and added to the array of + * options passed as second argument. + * + * LDAP URLs must conform to RFC 1959 without escape sequences. + * ldap://host:port/dn?attributes?scope?filter?extensions + * + * Returns + * 0 if the lookup was successful, + * 1 if the connection to the LDAP server could be established but + * the search was unsuccessful, + * 2 if a connection could not be established, and + * 3 if a fatal error occurred. + * + * An error message is returned in the third argument for return codes 1 and 3. + */ +static int +ldapServiceLookup(const char *purl, PQconninfoOption *options, + PQExpBuffer errorMessage) +{ + int port = LDAP_DEF_PORT, + scope, + rc, + size, + state, + oldstate, + i; +#ifndef WIN32 + int msgid; +#endif + bool found_keyword; + char *url, + *hostname, + *portstr, + *endptr, + *dn, + *scopestr, + *filter, + *result, + *p, + *p1 = NULL, + *optname = NULL, + *optval = NULL; + char *attrs[2] = {NULL, NULL}; + LDAP *ld = NULL; + LDAPMessage *res, + *entry; + struct berval **values; + LDAP_TIMEVAL time = {PGLDAP_TIMEOUT, 0}; + + if ((url = strdup(purl)) == NULL) + { + printfPQExpBuffer(errorMessage, libpq_gettext("out of memory\n")); + return 3; + } + + /* + * Parse URL components, check for correctness. Basically, url has '\0' + * placed at component boundaries and variables are pointed at each + * component. + */ + + if (pg_strncasecmp(url, LDAP_URL, strlen(LDAP_URL)) != 0) + { + printfPQExpBuffer(errorMessage, + libpq_gettext("invalid LDAP URL \"%s\": scheme must be ldap://\n"), purl); + free(url); + return 3; + } + + /* hostname */ + hostname = url + strlen(LDAP_URL); + if (*hostname == '/') /* no hostname? */ + hostname = DefaultHost; /* the default */ + + /* dn, "distinguished name" */ + p = strchr(url + strlen(LDAP_URL), '/'); + if (p == NULL || *(p + 1) == '\0' || *(p + 1) == '?') + { + printfPQExpBuffer(errorMessage, libpq_gettext( + "invalid LDAP URL \"%s\": missing distinguished name\n"), purl); + free(url); + return 3; + } + *p = '\0'; /* terminate hostname */ + dn = p + 1; + + /* attribute */ + if ((p = strchr(dn, '?')) == NULL || *(p + 1) == '\0' || *(p + 1) == '?') + { + printfPQExpBuffer(errorMessage, libpq_gettext( + "invalid LDAP URL \"%s\": must have exactly one attribute\n"), purl); + free(url); + return 3; + } + *p = '\0'; + attrs[0] = p + 1; + + /* scope */ + if ((p = strchr(attrs[0], '?')) == NULL || *(p + 1) == '\0' || *(p + 1) == '?') + { + printfPQExpBuffer(errorMessage, libpq_gettext("invalid LDAP URL \"%s\": must have search scope (base/one/sub)\n"), purl); + free(url); + return 3; + } + *p = '\0'; + scopestr = p + 1; + + /* filter */ + if ((p = strchr(scopestr, '?')) == NULL || *(p + 1) == '\0' || *(p + 1) == '?') + { + printfPQExpBuffer(errorMessage, + libpq_gettext("invalid LDAP URL \"%s\": no filter\n"), purl); + free(url); + return 3; + } + *p = '\0'; + filter = p + 1; + if ((p = strchr(filter, '?')) != NULL) + *p = '\0'; + + /* port number? */ + if ((p1 = strchr(hostname, ':')) != NULL) + { + long lport; + + *p1 = '\0'; + portstr = p1 + 1; + errno = 0; + lport = strtol(portstr, &endptr, 10); + if (*portstr == '\0' || *endptr != '\0' || errno || lport < 0 || lport > 65535) + { + printfPQExpBuffer(errorMessage, libpq_gettext( + "invalid LDAP URL \"%s\": invalid port number\n"), purl); + free(url); + return 3; + } + port = (int) lport; + } + + /* Allow only one attribute */ + if (strchr(attrs[0], ',') != NULL) + { + printfPQExpBuffer(errorMessage, libpq_gettext( + "invalid LDAP URL \"%s\": must have exactly one attribute\n"), purl); + free(url); + return 3; + } + + /* set scope */ + if (pg_strcasecmp(scopestr, "base") == 0) + scope = LDAP_SCOPE_BASE; + else if (pg_strcasecmp(scopestr, "one") == 0) + scope = LDAP_SCOPE_ONELEVEL; + else if (pg_strcasecmp(scopestr, "sub") == 0) + scope = LDAP_SCOPE_SUBTREE; + else + { + printfPQExpBuffer(errorMessage, libpq_gettext("invalid LDAP URL \"%s\": must have search scope (base/one/sub)\n"), purl); + free(url); + return 3; + } + + /* initialize LDAP structure */ + if ((ld = ldap_init(hostname, port)) == NULL) + { + printfPQExpBuffer(errorMessage, + libpq_gettext("could not create LDAP structure\n")); + free(url); + return 3; + } + + /* + * Perform an explicit anonymous bind. + * + * LDAP does not require that an anonymous bind is performed explicitly, + * but we want to distinguish between the case where LDAP bind does not + * succeed within PGLDAP_TIMEOUT seconds (return 2 to continue parsing the + * service control file) and the case where querying the LDAP server fails + * (return 1 to end parsing). + * + * Unfortunately there is no way of setting a timeout that works for both + * Windows and OpenLDAP. + */ +#ifdef WIN32 + /* the nonstandard ldap_connect function performs an anonymous bind */ + if (ldap_connect(ld, &time) != LDAP_SUCCESS) + { + /* error or timeout in ldap_connect */ + free(url); + ldap_unbind(ld); + return 2; + } +#else /* !WIN32 */ + /* in OpenLDAP, use the LDAP_OPT_NETWORK_TIMEOUT option */ + if (ldap_set_option(ld, LDAP_OPT_NETWORK_TIMEOUT, &time) != LDAP_SUCCESS) + { + free(url); + ldap_unbind(ld); + return 3; + } + + /* anonymous bind */ + if ((msgid = ldap_simple_bind(ld, NULL, NULL)) == -1) + { + /* error or network timeout */ + free(url); + ldap_unbind(ld); + return 2; + } + + /* wait some time for the connection to succeed */ + res = NULL; + if ((rc = ldap_result(ld, msgid, LDAP_MSG_ALL, &time, &res)) == -1 || + res == NULL) + { + /* error or timeout */ + if (res != NULL) + ldap_msgfree(res); + free(url); + ldap_unbind(ld); + return 2; + } + ldap_msgfree(res); + + /* reset timeout */ + time.tv_sec = -1; + if (ldap_set_option(ld, LDAP_OPT_NETWORK_TIMEOUT, &time) != LDAP_SUCCESS) + { + free(url); + ldap_unbind(ld); + return 3; + } +#endif /* WIN32 */ + + /* search */ + res = NULL; + if ((rc = ldap_search_st(ld, dn, scope, filter, attrs, 0, &time, &res)) + != LDAP_SUCCESS) + { + if (res != NULL) + ldap_msgfree(res); + printfPQExpBuffer(errorMessage, + libpq_gettext("lookup on LDAP server failed: %s\n"), + ldap_err2string(rc)); + ldap_unbind(ld); + free(url); + return 1; + } + + /* complain if there was not exactly one result */ + if ((rc = ldap_count_entries(ld, res)) != 1) + { + printfPQExpBuffer(errorMessage, + rc ? libpq_gettext("more than one entry found on LDAP lookup\n") + : libpq_gettext("no entry found on LDAP lookup\n")); + ldap_msgfree(res); + ldap_unbind(ld); + free(url); + return 1; + } + + /* get entry */ + if ((entry = ldap_first_entry(ld, res)) == NULL) + { + /* should never happen */ + printfPQExpBuffer(errorMessage, + libpq_gettext("no entry found on LDAP lookup\n")); + ldap_msgfree(res); + ldap_unbind(ld); + free(url); + return 1; + } + + /* get values */ + if ((values = ldap_get_values_len(ld, entry, attrs[0])) == NULL) + { + printfPQExpBuffer(errorMessage, + libpq_gettext("attribute has no values on LDAP lookup\n")); + ldap_msgfree(res); + ldap_unbind(ld); + free(url); + return 1; + } + + ldap_msgfree(res); + free(url); + + if (values[0] == NULL) + { + printfPQExpBuffer(errorMessage, + libpq_gettext("attribute has no values on LDAP lookup\n")); + ldap_value_free_len(values); + ldap_unbind(ld); + return 1; + } + + /* concatenate values into a single string with newline terminators */ + size = 1; /* for the trailing null */ + for (i = 0; values[i] != NULL; i++) + size += values[i]->bv_len + 1; + if ((result = malloc(size)) == NULL) + { + printfPQExpBuffer(errorMessage, + libpq_gettext("out of memory\n")); + ldap_value_free_len(values); + ldap_unbind(ld); + return 3; + } + p = result; + for (i = 0; values[i] != NULL; i++) + { + memcpy(p, values[i]->bv_val, values[i]->bv_len); + p += values[i]->bv_len; + *(p++) = '\n'; + } + *p = '\0'; + + ldap_value_free_len(values); + ldap_unbind(ld); + + /* parse result string */ + oldstate = state = 0; + for (p = result; *p != '\0'; ++p) + { + switch (state) + { + case 0: /* between entries */ + if (!ld_is_sp_tab(*p) && !ld_is_nl_cr(*p)) + { + optname = p; + state = 1; + } + break; + case 1: /* in option name */ + if (ld_is_sp_tab(*p)) + { + *p = '\0'; + state = 2; + } + else if (ld_is_nl_cr(*p)) + { + printfPQExpBuffer(errorMessage, libpq_gettext( + "missing \"=\" after \"%s\" in connection info string\n"), + optname); + free(result); + return 3; + } + else if (*p == '=') + { + *p = '\0'; + state = 3; + } + break; + case 2: /* after option name */ + if (*p == '=') + { + state = 3; + } + else if (!ld_is_sp_tab(*p)) + { + printfPQExpBuffer(errorMessage, libpq_gettext( + "missing \"=\" after \"%s\" in connection info string\n"), + optname); + free(result); + return 3; + } + break; + case 3: /* before option value */ + if (*p == '\'') + { + optval = p + 1; + p1 = p + 1; + state = 5; + } + else if (ld_is_nl_cr(*p)) + { + optval = optname + strlen(optname); /* empty */ + state = 0; + } + else if (!ld_is_sp_tab(*p)) + { + optval = p; + state = 4; + } + break; + case 4: /* in unquoted option value */ + if (ld_is_sp_tab(*p) || ld_is_nl_cr(*p)) + { + *p = '\0'; + state = 0; + } + break; + case 5: /* in quoted option value */ + if (*p == '\'') + { + *p1 = '\0'; + state = 0; + } + else if (*p == '\\') + state = 6; + else + *(p1++) = *p; + break; + case 6: /* in quoted option value after escape */ + *(p1++) = *p; + state = 5; + break; + } + + if (state == 0 && oldstate != 0) + { + found_keyword = false; + for (i = 0; options[i].keyword; i++) + { + if (strcmp(options[i].keyword, optname) == 0) + { + if (options[i].val == NULL) + { + options[i].val = strdup(optval); + if (!options[i].val) + { + printfPQExpBuffer(errorMessage, + libpq_gettext("out of memory\n")); + free(result); + return 3; + } + } + found_keyword = true; + break; + } + } + if (!found_keyword) + { + printfPQExpBuffer(errorMessage, + libpq_gettext("invalid connection option \"%s\"\n"), + optname); + free(result); + return 1; + } + optname = NULL; + optval = NULL; + } + oldstate = state; + } + + free(result); + + if (state == 5 || state == 6) + { + printfPQExpBuffer(errorMessage, libpq_gettext( + "unterminated quoted string in connection info string\n")); + return 3; + } + + return 0; +} + +#endif /* USE_LDAP */ + +#define MAXBUFSIZE 256 + +static int +parseServiceInfo(PQconninfoOption *options, PQExpBuffer errorMessage) +{ + const char *service = conninfo_getval(options, "service"); + char serviceFile[MAXPGPATH]; + char *env; + bool group_found = false; + int status; + struct stat stat_buf; + + /* + * We have to special-case the environment variable PGSERVICE here, since + * this is and should be called before inserting environment defaults for + * other connection options. + */ + if (service == NULL) + service = getenv("PGSERVICE"); + + if (service == NULL) + return 0; + + if ((env = getenv("PGSERVICEFILE")) != NULL) + strlcpy(serviceFile, env, sizeof(serviceFile)); + else + { + char homedir[MAXPGPATH]; + + if (!pqGetHomeDirectory(homedir, sizeof(homedir))) + { + printfPQExpBuffer(errorMessage, libpq_gettext("could not get home directory to locate service definition file")); + return 1; + } + snprintf(serviceFile, MAXPGPATH, "%s/%s", homedir, ".pg_service.conf"); + errno = 0; + if (stat(serviceFile, &stat_buf) != 0 && errno == ENOENT) + goto next_file; + } + + status = parseServiceFile(serviceFile, service, options, errorMessage, &group_found); + if (group_found || status != 0) + return status; + +next_file: + + /* + * This could be used by any application so we can't use the binary + * location to find our config files. + */ + snprintf(serviceFile, MAXPGPATH, "%s/pg_service.conf", + getenv("PGSYSCONFDIR") ? getenv("PGSYSCONFDIR") : SYSCONFDIR); + errno = 0; + if (stat(serviceFile, &stat_buf) != 0 && errno == ENOENT) + goto last_file; + + status = parseServiceFile(serviceFile, service, options, errorMessage, &group_found); + if (status != 0) + return status; + +last_file: + if (!group_found) + { + printfPQExpBuffer(errorMessage, + libpq_gettext("definition of service \"%s\" not found\n"), service); + return 3; + } + + return 0; +} + +static int +parseServiceFile(const char *serviceFile, + const char *service, + PQconninfoOption *options, + PQExpBuffer errorMessage, + bool *group_found) +{ + int linenr = 0, + i; + FILE *f; + char buf[MAXBUFSIZE], + *line; + + f = fopen(serviceFile, "r"); + if (f == NULL) + { + printfPQExpBuffer(errorMessage, libpq_gettext("service file \"%s\" not found\n"), + serviceFile); + return 1; + } + + while ((line = fgets(buf, sizeof(buf), f)) != NULL) + { + linenr++; + + if (strlen(line) >= sizeof(buf) - 1) + { + fclose(f); + printfPQExpBuffer(errorMessage, + libpq_gettext("line %d too long in service file \"%s\"\n"), + linenr, + serviceFile); + return 2; + } + + /* ignore EOL at end of line */ + if (strlen(line) && line[strlen(line) - 1] == '\n') + line[strlen(line) - 1] = 0; + + /* ignore leading blanks */ + while (*line && isspace((unsigned char) line[0])) + line++; + + /* ignore comments and empty lines */ + if (strlen(line) == 0 || line[0] == '#') + continue; + + /* Check for right groupname */ + if (line[0] == '[') + { + if (*group_found) + { + /* group info already read */ + fclose(f); + return 0; + } + + if (strncmp(line + 1, service, strlen(service)) == 0 && + line[strlen(service) + 1] == ']') + *group_found = true; + else + *group_found = false; + } + else + { + if (*group_found) + { + /* + * Finally, we are in the right group and can parse the line + */ + char *key, + *val; + bool found_keyword; + +#ifdef USE_LDAP + if (strncmp(line, "ldap", 4) == 0) + { + int rc = ldapServiceLookup(line, options, errorMessage); + + /* if rc = 2, go on reading for fallback */ + switch (rc) + { + case 0: + fclose(f); + return 0; + case 1: + case 3: + fclose(f); + return 3; + case 2: + continue; + } + } +#endif + + key = line; + val = strchr(line, '='); + if (val == NULL) + { + printfPQExpBuffer(errorMessage, + libpq_gettext("syntax error in service file \"%s\", line %d\n"), + serviceFile, + linenr); + fclose(f); + return 3; + } + *val++ = '\0'; + + if (strcmp(key, "service") == 0) + { + printfPQExpBuffer(errorMessage, + libpq_gettext("nested service specifications not supported in service file \"%s\", line %d\n"), + serviceFile, + linenr); + fclose(f); + return 3; + } + + /* + * Set the parameter --- but don't override any previous + * explicit setting. + */ + found_keyword = false; + for (i = 0; options[i].keyword; i++) + { + if (strcmp(options[i].keyword, key) == 0) + { + if (options[i].val == NULL) + options[i].val = strdup(val); + if (!options[i].val) + { + printfPQExpBuffer(errorMessage, + libpq_gettext("out of memory\n")); + fclose(f); + return 3; + } + found_keyword = true; + break; + } + } + + if (!found_keyword) + { + printfPQExpBuffer(errorMessage, + libpq_gettext("syntax error in service file \"%s\", line %d\n"), + serviceFile, + linenr); + fclose(f); + return 3; + } + } + } + } + + fclose(f); + + return 0; +} + + +/* + * PQconninfoParse + * + * Parse a string like PQconnectdb() would do and return the + * resulting connection options array. NULL is returned on failure. + * The result contains only options specified directly in the string, + * not any possible default values. + * + * If errmsg isn't NULL, *errmsg is set to NULL on success, or a malloc'd + * string on failure (use PQfreemem to free it). In out-of-memory conditions + * both *errmsg and the result could be NULL. + * + * NOTE: the returned array is dynamically allocated and should + * be freed when no longer needed via PQconninfoFree(). + */ +PQconninfoOption * +PQconninfoParse(const char *conninfo, char **errmsg) +{ + PQExpBufferData errorBuf; + PQconninfoOption *connOptions; + + if (errmsg) + *errmsg = NULL; /* default */ + initPQExpBuffer(&errorBuf); + if (PQExpBufferDataBroken(errorBuf)) + return NULL; /* out of memory already :-( */ + connOptions = parse_connection_string(conninfo, &errorBuf, false); + if (connOptions == NULL && errmsg) + *errmsg = errorBuf.data; + else + termPQExpBuffer(&errorBuf); + return connOptions; +} + +/* + * Build a working copy of the constant PQconninfoOptions array. + */ +static PQconninfoOption * +conninfo_init(PQExpBuffer errorMessage) +{ + PQconninfoOption *options; + PQconninfoOption *opt_dest; + const internalPQconninfoOption *cur_opt; + + /* + * Get enough memory for all options in PQconninfoOptions, even if some + * end up being filtered out. + */ + options = (PQconninfoOption *) malloc(sizeof(PQconninfoOption) * sizeof(PQconninfoOptions) / sizeof(PQconninfoOptions[0])); + if (options == NULL) + { + printfPQExpBuffer(errorMessage, + libpq_gettext("out of memory\n")); + return NULL; + } + opt_dest = options; + + for (cur_opt = PQconninfoOptions; cur_opt->keyword; cur_opt++) + { + /* Only copy the public part of the struct, not the full internal */ + memcpy(opt_dest, cur_opt, sizeof(PQconninfoOption)); + opt_dest++; + } + MemSet(opt_dest, 0, sizeof(PQconninfoOption)); + + return options; +} + +/* + * Connection string parser + * + * Returns a malloc'd PQconninfoOption array, if parsing is successful. + * Otherwise, NULL is returned and an error message is left in errorMessage. + * + * If use_defaults is TRUE, default values are filled in (from a service file, + * environment variables, etc). + */ +static PQconninfoOption * +parse_connection_string(const char *connstr, PQExpBuffer errorMessage, + bool use_defaults) +{ + /* Parse as URI if connection string matches URI prefix */ + if (uri_prefix_length(connstr) != 0) + return conninfo_uri_parse(connstr, errorMessage, use_defaults); + + /* Parse as default otherwise */ + return conninfo_parse(connstr, errorMessage, use_defaults); +} + +/* + * Checks if connection string starts with either of the valid URI prefix + * designators. + * + * Returns the URI prefix length, 0 if the string doesn't contain a URI prefix. + * + * XXX this is duplicated in psql/common.c. + */ +static int +uri_prefix_length(const char *connstr) +{ + if (strncmp(connstr, uri_designator, + sizeof(uri_designator) - 1) == 0) + return sizeof(uri_designator) - 1; + + if (strncmp(connstr, short_uri_designator, + sizeof(short_uri_designator) - 1) == 0) + return sizeof(short_uri_designator) - 1; + + return 0; +} + +/* + * Recognized connection string either starts with a valid URI prefix or + * contains a "=" in it. + * + * Must be consistent with parse_connection_string: anything for which this + * returns true should at least look like it's parseable by that routine. + * + * XXX this is duplicated in psql/common.c + */ +static bool +recognized_connection_string(const char *connstr) +{ + return uri_prefix_length(connstr) != 0 || strchr(connstr, '=') != NULL; +} + +/* + * Subroutine for parse_connection_string + * + * Deal with a string containing key=value pairs. + */ +static PQconninfoOption * +conninfo_parse(const char *conninfo, PQExpBuffer errorMessage, + bool use_defaults) +{ + char *pname; + char *pval; + char *buf; + char *cp; + char *cp2; + PQconninfoOption *options; + + /* Make a working copy of PQconninfoOptions */ + options = conninfo_init(errorMessage); + if (options == NULL) + return NULL; + + /* Need a modifiable copy of the input string */ + if ((buf = strdup(conninfo)) == NULL) + { + printfPQExpBuffer(errorMessage, + libpq_gettext("out of memory\n")); + PQconninfoFree(options); + return NULL; + } + cp = buf; + + while (*cp) + { + /* Skip blanks before the parameter name */ + if (isspace((unsigned char) *cp)) + { + cp++; + continue; + } + + /* Get the parameter name */ + pname = cp; + while (*cp) + { + if (*cp == '=') + break; + if (isspace((unsigned char) *cp)) + { + *cp++ = '\0'; + while (*cp) + { + if (!isspace((unsigned char) *cp)) + break; + cp++; + } + break; + } + cp++; + } + + /* Check that there is a following '=' */ + if (*cp != '=') + { + printfPQExpBuffer(errorMessage, + libpq_gettext("missing \"=\" after \"%s\" in connection info string\n"), + pname); + PQconninfoFree(options); + free(buf); + return NULL; + } + *cp++ = '\0'; + + /* Skip blanks after the '=' */ + while (*cp) + { + if (!isspace((unsigned char) *cp)) + break; + cp++; + } + + /* Get the parameter value */ + pval = cp; + + if (*cp != '\'') + { + cp2 = pval; + while (*cp) + { + if (isspace((unsigned char) *cp)) + { + *cp++ = '\0'; + break; + } + if (*cp == '\\') + { + cp++; + if (*cp != '\0') + *cp2++ = *cp++; + } + else + *cp2++ = *cp++; + } + *cp2 = '\0'; + } + else + { + cp2 = pval; + cp++; + for (;;) + { + if (*cp == '\0') + { + printfPQExpBuffer(errorMessage, + libpq_gettext("unterminated quoted string in connection info string\n")); + PQconninfoFree(options); + free(buf); + return NULL; + } + if (*cp == '\\') + { + cp++; + if (*cp != '\0') + *cp2++ = *cp++; + continue; + } + if (*cp == '\'') + { + *cp2 = '\0'; + cp++; + break; + } + *cp2++ = *cp++; + } + } + + /* + * Now that we have the name and the value, store the record. + */ + if (!conninfo_storeval(options, pname, pval, errorMessage, false, false)) + { + PQconninfoFree(options); + free(buf); + return NULL; + } + } + + /* Done with the modifiable input string */ + free(buf); + + /* + * Add in defaults if the caller wants that. + */ + if (use_defaults) + { + if (!conninfo_add_defaults(options, errorMessage)) + { + PQconninfoFree(options); + return NULL; + } + } + + return options; +} + +/* + * Conninfo array parser routine + * + * If successful, a malloc'd PQconninfoOption array is returned. + * If not successful, NULL is returned and an error message is + * left in errorMessage. + * Defaults are supplied (from a service file, environment variables, etc) + * for unspecified options, but only if use_defaults is TRUE. + * + * If expand_dbname is non-zero, and the value passed for the first occurrence + * of "dbname" keyword is a connection string (as indicated by + * recognized_connection_string) then parse and process it, overriding any + * previously processed conflicting keywords. Subsequent keywords will take + * precedence, however. In-tree programs generally specify expand_dbname=true, + * so command-line arguments naming a database can use a connection string. + * Some code acquires arbitrary database names from known-literal sources like + * PQdb(), PQconninfoParse() and pg_database.datname. When connecting to such + * a database, in-tree code first wraps the name in a connection string. + */ +static PQconninfoOption * +conninfo_array_parse(const char *const * keywords, const char *const * values, + PQExpBuffer errorMessage, bool use_defaults, + int expand_dbname) +{ + PQconninfoOption *options; + PQconninfoOption *dbname_options = NULL; + PQconninfoOption *option; + int i = 0; + + /* + * If expand_dbname is non-zero, check keyword "dbname" to see if val is + * actually a recognized connection string. + */ + while (expand_dbname && keywords[i]) + { + const char *pname = keywords[i]; + const char *pvalue = values[i]; + + /* first find "dbname" if any */ + if (strcmp(pname, "dbname") == 0 && pvalue) + { + /* + * If value is a connection string, parse it, but do not use + * defaults here -- those get picked up later. We only want to + * override for those parameters actually passed. + */ + if (recognized_connection_string(pvalue)) + { + dbname_options = parse_connection_string(pvalue, errorMessage, false); + if (dbname_options == NULL) + return NULL; + } + break; + } + ++i; + } + + /* Make a working copy of PQconninfoOptions */ + options = conninfo_init(errorMessage); + if (options == NULL) + { + PQconninfoFree(dbname_options); + return NULL; + } + + /* Parse the keywords/values arrays */ + i = 0; + while (keywords[i]) + { + const char *pname = keywords[i]; + const char *pvalue = values[i]; + + if (pvalue != NULL && pvalue[0] != '\0') + { + /* Search for the param record */ + for (option = options; option->keyword != NULL; option++) + { + if (strcmp(option->keyword, pname) == 0) + break; + } + + /* Check for invalid connection option */ + if (option->keyword == NULL) + { + printfPQExpBuffer(errorMessage, + libpq_gettext("invalid connection option \"%s\"\n"), + pname); + PQconninfoFree(options); + PQconninfoFree(dbname_options); + return NULL; + } + + /* + * If we are on the first dbname parameter, and we have a parsed + * connection string, copy those parameters across, overriding any + * existing previous settings. + */ + if (strcmp(pname, "dbname") == 0 && dbname_options) + { + PQconninfoOption *str_option; + + for (str_option = dbname_options; str_option->keyword != NULL; str_option++) + { + if (str_option->val != NULL) + { + int k; + + for (k = 0; options[k].keyword; k++) + { + if (strcmp(options[k].keyword, str_option->keyword) == 0) + { + if (options[k].val) + free(options[k].val); + options[k].val = strdup(str_option->val); + if (!options[k].val) + { + printfPQExpBuffer(errorMessage, + libpq_gettext("out of memory\n")); + PQconninfoFree(options); + PQconninfoFree(dbname_options); + return NULL; + } + break; + } + } + } + } + + /* + * Forget the parsed connection string, so that any subsequent + * dbname parameters will not be expanded. + */ + PQconninfoFree(dbname_options); + dbname_options = NULL; + } + else + { + /* + * Store the value, overriding previous settings + */ + if (option->val) + free(option->val); + option->val = strdup(pvalue); + if (!option->val) + { + printfPQExpBuffer(errorMessage, + libpq_gettext("out of memory\n")); + PQconninfoFree(options); + PQconninfoFree(dbname_options); + return NULL; + } + } + } + ++i; + } + PQconninfoFree(dbname_options); + + /* + * Add in defaults if the caller wants that. + */ + if (use_defaults) + { + if (!conninfo_add_defaults(options, errorMessage)) + { + PQconninfoFree(options); + return NULL; + } + } + + return options; +} + +/* + * Add the default values for any unspecified options to the connection + * options array. + * + * Defaults are obtained from a service file, environment variables, etc. + * + * Returns TRUE if successful, otherwise FALSE; errorMessage, if supplied, + * is filled in upon failure. Note that failure to locate a default value + * is not an error condition here --- we just leave the option's value as + * NULL. + */ +static bool +conninfo_add_defaults(PQconninfoOption *options, PQExpBuffer errorMessage) +{ + PQconninfoOption *option; + char *tmp; + + /* + * If there's a service spec, use it to obtain any not-explicitly-given + * parameters. Ignore error if no error message buffer is passed because + * there is no way to pass back the failure message. + */ + if (parseServiceInfo(options, errorMessage) != 0 && errorMessage) + return false; + + /* + * Get the fallback resources for parameters not specified in the conninfo + * string nor the service. + */ + for (option = options; option->keyword != NULL; option++) + { + if (option->val != NULL) + continue; /* Value was in conninfo or service */ + + /* + * Try to get the environment variable fallback + */ + if (option->envvar != NULL) + { + if ((tmp = getenv(option->envvar)) != NULL) + { + option->val = strdup(tmp); + if (!option->val) + { + if (errorMessage) + printfPQExpBuffer(errorMessage, + libpq_gettext("out of memory\n")); + return false; + } + continue; + } + } + + /* + * Interpret the deprecated PGREQUIRESSL environment variable. Per + * tradition, translate values starting with "1" to sslmode=require, + * and ignore other values. Given both PGREQUIRESSL=1 and PGSSLMODE, + * PGSSLMODE takes precedence; the opposite was true before v9.3. + */ + if (strcmp(option->keyword, "sslmode") == 0) + { + const char *requiresslenv = getenv("PGREQUIRESSL"); + + if (requiresslenv != NULL && requiresslenv[0] == '1') + { + option->val = strdup("require"); + if (!option->val) + { + if (errorMessage) + printfPQExpBuffer(errorMessage, + libpq_gettext("out of memory\n")); + return false; + } + continue; + } + } + + /* + * No environment variable specified or the variable isn't set - try + * compiled-in default + */ + if (option->compiled != NULL) + { + option->val = strdup(option->compiled); + if (!option->val) + { + if (errorMessage) + printfPQExpBuffer(errorMessage, + libpq_gettext("out of memory\n")); + return false; + } + continue; + } + + /* + * Special handling for "user" option. Note that if pg_fe_getauthname + * fails, we just leave the value as NULL; there's no need for this to + * be an error condition if the caller provides a user name. The only + * reason we do this now at all is so that callers of PQconndefaults + * will see a correct default (barring error, of course). + */ + if (strcmp(option->keyword, "user") == 0) + { + option->val = pg_fe_getauthname(NULL); + continue; + } + } + + return true; +} + +/* + * Subroutine for parse_connection_string + * + * Deal with a URI connection string. + */ +static PQconninfoOption * +conninfo_uri_parse(const char *uri, PQExpBuffer errorMessage, + bool use_defaults) +{ + PQconninfoOption *options; + + /* Make a working copy of PQconninfoOptions */ + options = conninfo_init(errorMessage); + if (options == NULL) + return NULL; + + if (!conninfo_uri_parse_options(options, uri, errorMessage)) + { + PQconninfoFree(options); + return NULL; + } + + /* + * Add in defaults if the caller wants that. + */ + if (use_defaults) + { + if (!conninfo_add_defaults(options, errorMessage)) + { + PQconninfoFree(options); + return NULL; + } + } + + return options; +} + +/* + * conninfo_uri_parse_options + * Actual URI parser. + * + * If successful, returns true while the options array is filled with parsed + * options from the URI. + * If not successful, returns false and fills errorMessage accordingly. + * + * Parses the connection URI string in 'uri' according to the URI syntax (RFC + * 3986): + * + * postgresql://[user[:password]@][netloc][:port][/dbname][?param1=value1&...] + * + * where "netloc" is a hostname, an IPv4 address, or an IPv6 address surrounded + * by literal square brackets. + * + * Any of the URI parts might use percent-encoding (%xy). + */ +static bool +conninfo_uri_parse_options(PQconninfoOption *options, const char *uri, + PQExpBuffer errorMessage) +{ + int prefix_len; + char *p; + char *buf; + char *start; + char prevchar = '\0'; + char *user = NULL; + char *host = NULL; + bool retval = false; + + /* need a modifiable copy of the input URI */ + buf = strdup(uri); + if (buf == NULL) + { + printfPQExpBuffer(errorMessage, + libpq_gettext("out of memory\n")); + return false; + } + start = buf; + + /* Skip the URI prefix */ + prefix_len = uri_prefix_length(uri); + if (prefix_len == 0) + { + /* Should never happen */ + printfPQExpBuffer(errorMessage, + libpq_gettext("invalid URI propagated to internal parser routine: \"%s\"\n"), + uri); + goto cleanup; + } + start += prefix_len; + p = start; + + /* Look ahead for possible user credentials designator */ + while (*p && *p != '@' && *p != '/') + ++p; + if (*p == '@') + { + /* + * Found username/password designator, so URI should be of the form + * "scheme://user[:password]@[netloc]". + */ + user = start; + + p = user; + while (*p != ':' && *p != '@') + ++p; + + /* Save last char and cut off at end of user name */ + prevchar = *p; + *p = '\0'; + + if (*user && + !conninfo_storeval(options, "user", user, + errorMessage, false, true)) + goto cleanup; + + if (prevchar == ':') + { + const char *password = p + 1; + + while (*p != '@') + ++p; + *p = '\0'; + + if (*password && + !conninfo_storeval(options, "password", password, + errorMessage, false, true)) + goto cleanup; + } + + /* Advance past end of parsed user name or password token */ + ++p; + } + else + { + /* + * No username/password designator found. Reset to start of URI. + */ + p = start; + } + + /* + * "p" has been incremented past optional URI credential information at + * this point and now points at the "netloc" part of the URI. + * + * Look for IPv6 address. + */ + if (*p == '[') + { + host = ++p; + while (*p && *p != ']') + ++p; + if (!*p) + { + printfPQExpBuffer(errorMessage, + libpq_gettext("end of string reached when looking for matching \"]\" in IPv6 host address in URI: \"%s\"\n"), + uri); + goto cleanup; + } + if (p == host) + { + printfPQExpBuffer(errorMessage, + libpq_gettext("IPv6 host address may not be empty in URI: \"%s\"\n"), + uri); + goto cleanup; + } + + /* Cut off the bracket and advance */ + *(p++) = '\0'; + + /* + * The address may be followed by a port specifier or a slash or a + * query. + */ + if (*p && *p != ':' && *p != '/' && *p != '?') + { + printfPQExpBuffer(errorMessage, + libpq_gettext("unexpected character \"%c\" at position %d in URI (expected \":\" or \"/\"): \"%s\"\n"), + *p, (int) (p - buf + 1), uri); + goto cleanup; + } + } + else + { + /* not an IPv6 address: DNS-named or IPv4 netloc */ + host = p; + + /* + * Look for port specifier (colon) or end of host specifier (slash), + * or query (question mark). + */ + while (*p && *p != ':' && *p != '/' && *p != '?') + ++p; + } + + /* Save the hostname terminator before we null it */ + prevchar = *p; + *p = '\0'; + + if (*host && + !conninfo_storeval(options, "host", host, + errorMessage, false, true)) + goto cleanup; + + + if (prevchar == ':') + { + const char *port = ++p; /* advance past host terminator */ + + while (*p && *p != '/' && *p != '?') + ++p; + + prevchar = *p; + *p = '\0'; + + if (*port && + !conninfo_storeval(options, "port", port, + errorMessage, false, true)) + goto cleanup; + } + + if (prevchar && prevchar != '?') + { + const char *dbname = ++p; /* advance past host terminator */ + + /* Look for query parameters */ + while (*p && *p != '?') + ++p; + + prevchar = *p; + *p = '\0'; + + /* + * Avoid setting dbname to an empty string, as it forces the default + * value (username) and ignores $PGDATABASE, as opposed to not setting + * it at all. + */ + if (*dbname && + !conninfo_storeval(options, "dbname", dbname, + errorMessage, false, true)) + goto cleanup; + } + + if (prevchar) + { + ++p; /* advance past terminator */ + + if (!conninfo_uri_parse_params(p, options, errorMessage)) + goto cleanup; + } + + /* everything parsed okay */ + retval = true; + +cleanup: + free(buf); + return retval; +} + +/* + * Connection URI parameters parser routine + * + * If successful, returns true while connOptions is filled with parsed + * parameters. Otherwise, returns false and fills errorMessage appropriately. + * + * Destructively modifies 'params' buffer. + */ +static bool +conninfo_uri_parse_params(char *params, + PQconninfoOption *connOptions, + PQExpBuffer errorMessage) +{ + while (*params) + { + char *keyword = params; + char *value = NULL; + char *p = params; + bool malloced = false; + + /* + * Scan the params string for '=' and '&', marking the end of keyword + * and value respectively. + */ + for (;;) + { + if (*p == '=') + { + /* Was there '=' already? */ + if (value != NULL) + { + printfPQExpBuffer(errorMessage, + libpq_gettext("extra key/value separator \"=\" in URI query parameter: \"%s\"\n"), + keyword); + return false; + } + /* Cut off keyword, advance to value */ + *p++ = '\0'; + value = p; + } + else if (*p == '&' || *p == '\0') + { + /* + * If not at the end, cut off value and advance; leave p + * pointing to start of the next parameter, if any. + */ + if (*p != '\0') + *p++ = '\0'; + /* Was there '=' at all? */ + if (value == NULL) + { + printfPQExpBuffer(errorMessage, + libpq_gettext("missing key/value separator \"=\" in URI query parameter: \"%s\"\n"), + keyword); + return false; + } + /* Got keyword and value, go process them. */ + break; + } + else + ++p; /* Advance over all other bytes. */ + } + + keyword = conninfo_uri_decode(keyword, errorMessage); + if (keyword == NULL) + { + /* conninfo_uri_decode already set an error message */ + return false; + } + value = conninfo_uri_decode(value, errorMessage); + if (value == NULL) + { + /* conninfo_uri_decode already set an error message */ + free(keyword); + return false; + } + malloced = true; + + /* + * Special keyword handling for improved JDBC compatibility. + */ + if (strcmp(keyword, "ssl") == 0 && + strcmp(value, "true") == 0) + { + free(keyword); + free(value); + malloced = false; + + keyword = "sslmode"; + value = "require"; + } + + /* + * Store the value if the corresponding option exists; ignore + * otherwise. At this point both keyword and value are not + * URI-encoded. + */ + if (!conninfo_storeval(connOptions, keyword, value, + errorMessage, true, false)) + { + /* Insert generic message if conninfo_storeval didn't give one. */ + if (errorMessage->len == 0) + printfPQExpBuffer(errorMessage, + libpq_gettext("invalid URI query parameter: \"%s\"\n"), + keyword); + /* And fail. */ + if (malloced) + { + free(keyword); + free(value); + } + return false; + } + + if (malloced) + { + free(keyword); + free(value); + } + + /* Proceed to next key=value pair, if any */ + params = p; + } + + return true; +} + +/* + * Connection URI decoder routine + * + * If successful, returns the malloc'd decoded string. + * If not successful, returns NULL and fills errorMessage accordingly. + * + * The string is decoded by replacing any percent-encoded tokens with + * corresponding characters, while preserving any non-encoded characters. A + * percent-encoded token is a character triplet: a percent sign, followed by a + * pair of hexadecimal digits (0-9A-F), where lower- and upper-case letters are + * treated identically. + */ +static char * +conninfo_uri_decode(const char *str, PQExpBuffer errorMessage) +{ + char *buf; + char *p; + const char *q = str; + + buf = malloc(strlen(str) + 1); + if (buf == NULL) + { + printfPQExpBuffer(errorMessage, libpq_gettext("out of memory\n")); + return NULL; + } + p = buf; + + for (;;) + { + if (*q != '%') + { + /* copy and check for NUL terminator */ + if (!(*(p++) = *(q++))) + break; + } + else + { + int hi; + int lo; + int c; + + ++q; /* skip the percent sign itself */ + + /* + * Possible EOL will be caught by the first call to + * get_hexdigit(), so we never dereference an invalid q pointer. + */ + if (!(get_hexdigit(*q++, &hi) && get_hexdigit(*q++, &lo))) + { + printfPQExpBuffer(errorMessage, + libpq_gettext("invalid percent-encoded token: \"%s\"\n"), + str); + free(buf); + return NULL; + } + + c = (hi << 4) | lo; + if (c == 0) + { + printfPQExpBuffer(errorMessage, + libpq_gettext("forbidden value %%00 in percent-encoded value: \"%s\"\n"), + str); + free(buf); + return NULL; + } + *(p++) = c; + } + } + + return buf; +} + +/* + * Convert hexadecimal digit character to its integer value. + * + * If successful, returns true and value is filled with digit's base 16 value. + * If not successful, returns false. + * + * Lower- and upper-case letters in the range A-F are treated identically. + */ +static bool +get_hexdigit(char digit, int *value) +{ + if ('0' <= digit && digit <= '9') + *value = digit - '0'; + else if ('A' <= digit && digit <= 'F') + *value = digit - 'A' + 10; + else if ('a' <= digit && digit <= 'f') + *value = digit - 'a' + 10; + else + return false; + + return true; +} + +/* + * Find an option value corresponding to the keyword in the connOptions array. + * + * If successful, returns a pointer to the corresponding option's value. + * If not successful, returns NULL. + */ +static const char * +conninfo_getval(PQconninfoOption *connOptions, + const char *keyword) +{ + PQconninfoOption *option; + + option = conninfo_find(connOptions, keyword); + + return option ? option->val : NULL; +} + +/* + * Store a (new) value for an option corresponding to the keyword in + * connOptions array. + * + * If uri_decode is true, the value is URI-decoded. The keyword is always + * assumed to be non URI-encoded. + * + * If successful, returns a pointer to the corresponding PQconninfoOption, + * which value is replaced with a strdup'd copy of the passed value string. + * The existing value for the option is free'd before replacing, if any. + * + * If not successful, returns NULL and fills errorMessage accordingly. + * However, if the reason of failure is an invalid keyword being passed and + * ignoreMissing is TRUE, errorMessage will be left untouched. + */ +static PQconninfoOption * +conninfo_storeval(PQconninfoOption *connOptions, + const char *keyword, const char *value, + PQExpBuffer errorMessage, bool ignoreMissing, + bool uri_decode) +{ + PQconninfoOption *option; + char *value_copy; + + /* + * For backwards compatibility, requiressl=1 gets translated to + * sslmode=require, and requiressl=0 gets translated to sslmode=prefer + * (which is the default for sslmode). + */ + if (strcmp(keyword, "requiressl") == 0) + { + keyword = "sslmode"; + if (value[0] == '1') + value = "require"; + else + value = "prefer"; + } + + option = conninfo_find(connOptions, keyword); + if (option == NULL) + { + if (!ignoreMissing) + printfPQExpBuffer(errorMessage, + libpq_gettext("invalid connection option \"%s\"\n"), + keyword); + return NULL; + } + + if (uri_decode) + { + value_copy = conninfo_uri_decode(value, errorMessage); + if (value_copy == NULL) + /* conninfo_uri_decode already set an error message */ + return NULL; + } + else + { + value_copy = strdup(value); + if (value_copy == NULL) + { + printfPQExpBuffer(errorMessage, libpq_gettext("out of memory\n")); + return NULL; + } + } + + if (option->val) + free(option->val); + option->val = value_copy; + + return option; +} + +/* + * Find a PQconninfoOption option corresponding to the keyword in the + * connOptions array. + * + * If successful, returns a pointer to the corresponding PQconninfoOption + * structure. + * If not successful, returns NULL. + */ +static PQconninfoOption * +conninfo_find(PQconninfoOption *connOptions, const char *keyword) +{ + PQconninfoOption *option; + + for (option = connOptions; option->keyword != NULL; option++) + { + if (strcmp(option->keyword, keyword) == 0) + return option; + } + + return NULL; +} + + +/* + * Return the connection options used for the connection + */ +PQconninfoOption * +PQconninfo(PGconn *conn) +{ + PQExpBufferData errorBuf; + PQconninfoOption *connOptions; + + if (conn == NULL) + return NULL; + + /* We don't actually report any errors here, but callees want a buffer */ + initPQExpBuffer(&errorBuf); + if (PQExpBufferDataBroken(errorBuf)) + return NULL; /* out of memory already :-( */ + + connOptions = conninfo_init(&errorBuf); + + if (connOptions != NULL) + { + const internalPQconninfoOption *option; + + for (option = PQconninfoOptions; option->keyword; option++) + { + char **connmember; + + if (option->connofs < 0) + continue; + + connmember = (char **) ((char *) conn + option->connofs); + + if (*connmember) + conninfo_storeval(connOptions, option->keyword, *connmember, + &errorBuf, true, false); + } + } + + termPQExpBuffer(&errorBuf); + + return connOptions; +} + + +void +PQconninfoFree(PQconninfoOption *connOptions) +{ + PQconninfoOption *option; + + if (connOptions == NULL) + return; + + for (option = connOptions; option->keyword != NULL; option++) + { + if (option->val != NULL) + free(option->val); + } + free(connOptions); +} + + +/* =========== accessor functions for PGconn ========= */ +char * +PQdb(const PGconn *conn) +{ + if (!conn) + return NULL; + return conn->dbName; +} + +char * +PQuser(const PGconn *conn) +{ + if (!conn) + return NULL; + return conn->pguser; +} + +char * +PQpass(const PGconn *conn) +{ + if (!conn) + return NULL; + return conn->pgpass; +} + +char * +PQhost(const PGconn *conn) +{ + if (!conn) + return NULL; + if (conn->pghost != NULL && conn->pghost[0] != '\0') + return conn->pghost; + else + { +#ifdef HAVE_UNIX_SOCKETS + if (conn->pgunixsocket != NULL && conn->pgunixsocket[0] != '\0') + return conn->pgunixsocket; + else + return DEFAULT_PGSOCKET_DIR; +#else + return DefaultHost; +#endif + } +} + +char * +PQport(const PGconn *conn) +{ + if (!conn) + return NULL; + return conn->pgport; +} + +char * +PQtty(const PGconn *conn) +{ + if (!conn) + return NULL; + return conn->pgtty; +} + +char * +PQoptions(const PGconn *conn) +{ + if (!conn) + return NULL; + return conn->pgoptions; +} + +ConnStatusType +PQstatus(const PGconn *conn) +{ + if (!conn) + return CONNECTION_BAD; + return conn->status; +} + +PGTransactionStatusType +PQtransactionStatus(const PGconn *conn) +{ + if (!conn || conn->status != CONNECTION_OK) + return PQTRANS_UNKNOWN; + if (conn->asyncStatus != PGASYNC_IDLE) + return PQTRANS_ACTIVE; + return conn->xactStatus; +} + +const char * +PQparameterStatus(const PGconn *conn, const char *paramName) +{ + const pgParameterStatus *pstatus; + + if (!conn || !paramName) + return NULL; + for (pstatus = conn->pstatus; pstatus != NULL; pstatus = pstatus->next) + { + if (strcmp(pstatus->name, paramName) == 0) + return pstatus->value; + } + return NULL; +} + +int +PQprotocolVersion(const PGconn *conn) +{ + if (!conn) + return 0; + if (conn->status == CONNECTION_BAD) + return 0; + return PG_PROTOCOL_MAJOR(conn->pversion); +} + +int +PQserverVersion(const PGconn *conn) +{ + if (!conn) + return 0; + if (conn->status == CONNECTION_BAD) + return 0; + return conn->sversion; +} + +char * +PQerrorMessage(const PGconn *conn) +{ + if (!conn) + return libpq_gettext("connection pointer is NULL\n"); + + return conn->errorMessage.data; +} + +/* + * In Windows, socket values are unsigned, and an invalid socket value + * (INVALID_SOCKET) is ~0, which equals -1 in comparisons (with no compiler + * warning). Ideally we would return an unsigned value for PQsocket() on + * Windows, but that would cause the function's return value to differ from + * Unix, so we just return -1 for invalid sockets. + * http://msdn.microsoft.com/en-us/library/windows/desktop/cc507522%28v=vs.85%29.aspx + * http://stackoverflow.com/questions/10817252/why-is-invalid-socket-defined-as-0-in-winsock2-h-c + */ +int +PQsocket(const PGconn *conn) +{ + if (!conn) + return -1; + return (conn->sock != PGINVALID_SOCKET) ? conn->sock : -1; +} + +int +PQbackendPID(const PGconn *conn) +{ + if (!conn || conn->status != CONNECTION_OK) + return 0; + return conn->be_pid; +} + +int +PQconnectionNeedsPassword(const PGconn *conn) +{ + if (!conn) + return false; + if (conn->password_needed && + (conn->pgpass == NULL || conn->pgpass[0] == '\0')) + return true; + else + return false; +} + +int +PQconnectionUsedPassword(const PGconn *conn) +{ + if (!conn) + return false; + if (conn->password_needed) + return true; + else + return false; +} + +int +PQclientEncoding(const PGconn *conn) +{ + if (!conn || conn->status != CONNECTION_OK) + return -1; + return conn->client_encoding; +} + +int +PQsetClientEncoding(PGconn *conn, const char *encoding) +{ + char qbuf[128]; + static const char query[] = "set client_encoding to '%s'"; + PGresult *res; + int status; + + if (!conn || conn->status != CONNECTION_OK) + return -1; + + if (!encoding) + return -1; + + /* Resolve special "auto" value from the locale */ + if (strcmp(encoding, "auto") == 0) + encoding = pg_encoding_to_char(pg_get_encoding_from_locale(NULL, true)); + + /* check query buffer overflow */ + if (sizeof(qbuf) < (sizeof(query) + strlen(encoding))) + return -1; + + /* ok, now send a query */ + sprintf(qbuf, query, encoding); + res = PQexec(conn, qbuf); + + if (res == NULL) + return -1; + if (res->resultStatus != PGRES_COMMAND_OK) + status = -1; + else + { + /* + * In protocol 2 we have to assume the setting will stick, and adjust + * our state immediately. In protocol 3 and up we can rely on the + * backend to report the parameter value, and we'll change state at + * that time. + */ + if (PG_PROTOCOL_MAJOR(conn->pversion) < 3) + pqSaveParameterStatus(conn, "client_encoding", encoding); + status = 0; /* everything is ok */ + } + PQclear(res); + return status; +} + +PGVerbosity +PQsetErrorVerbosity(PGconn *conn, PGVerbosity verbosity) +{ + PGVerbosity old; + + if (!conn) + return PQERRORS_DEFAULT; + old = conn->verbosity; + conn->verbosity = verbosity; + return old; +} + +PGContextVisibility +PQsetErrorContextVisibility(PGconn *conn, PGContextVisibility show_context) +{ + PGContextVisibility old; + + if (!conn) + return PQSHOW_CONTEXT_ERRORS; + old = conn->show_context; + conn->show_context = show_context; + return old; +} + +void +PQtrace(PGconn *conn, FILE *debug_port) +{ + if (conn == NULL) + return; + PQuntrace(conn); + conn->Pfdebug = debug_port; +} + +void +PQuntrace(PGconn *conn) +{ + if (conn == NULL) + return; + if (conn->Pfdebug) + { + fflush(conn->Pfdebug); + conn->Pfdebug = NULL; + } +} + +PQnoticeReceiver +PQsetNoticeReceiver(PGconn *conn, PQnoticeReceiver proc, void *arg) +{ + PQnoticeReceiver old; + + if (conn == NULL) + return NULL; + + old = conn->noticeHooks.noticeRec; + if (proc) + { + conn->noticeHooks.noticeRec = proc; + conn->noticeHooks.noticeRecArg = arg; + } + return old; +} + +PQnoticeProcessor +PQsetNoticeProcessor(PGconn *conn, PQnoticeProcessor proc, void *arg) +{ + PQnoticeProcessor old; + + if (conn == NULL) + return NULL; + + old = conn->noticeHooks.noticeProc; + if (proc) + { + conn->noticeHooks.noticeProc = proc; + conn->noticeHooks.noticeProcArg = arg; + } + return old; +} + +/* + * The default notice message receiver just gets the standard notice text + * and sends it to the notice processor. This two-level setup exists + * mostly for backwards compatibility; perhaps we should deprecate use of + * PQsetNoticeProcessor? + */ +static void +defaultNoticeReceiver(void *arg, const PGresult *res) +{ + (void) arg; /* not used */ + if (res->noticeHooks.noticeProc != NULL) + (*res->noticeHooks.noticeProc) (res->noticeHooks.noticeProcArg, + PQresultErrorMessage(res)); +} + +/* + * The default notice message processor just prints the + * message on stderr. Applications can override this if they + * want the messages to go elsewhere (a window, for example). + * Note that simply discarding notices is probably a bad idea. + */ +static void +defaultNoticeProcessor(void *arg, const char *message) +{ + (void) arg; /* not used */ + /* Note: we expect the supplied string to end with a newline already. */ + fprintf(stderr, "%s", message); +} + +/* + * returns a pointer to the next token or NULL if the current + * token doesn't match + */ +static char * +pwdfMatchesString(char *buf, char *token) +{ + char *tbuf, + *ttok; + bool bslash = false; + + if (buf == NULL || token == NULL) + return NULL; + tbuf = buf; + ttok = token; + if (tbuf[0] == '*' && tbuf[1] == ':') + return tbuf + 2; + while (*tbuf != 0) + { + if (*tbuf == '\\' && !bslash) + { + tbuf++; + bslash = true; + } + if (*tbuf == ':' && *ttok == 0 && !bslash) + return tbuf + 1; + bslash = false; + if (*ttok == 0) + return NULL; + if (*tbuf == *ttok) + { + tbuf++; + ttok++; + } + else + return NULL; + } + return NULL; +} + +/* Get a password from the password file. Return value is malloc'd. */ +static char * +PasswordFromFile(char *hostname, char *port, char *dbname, char *username) +{ + FILE *fp; + char pgpassfile[MAXPGPATH]; + struct stat stat_buf; + +#define LINELEN NAMEDATALEN*5 + char buf[LINELEN]; + + if (dbname == NULL || strlen(dbname) == 0) + return NULL; + + if (username == NULL || strlen(username) == 0) + return NULL; + + /* 'localhost' matches pghost of '' or the default socket directory */ + if (hostname == NULL) + hostname = DefaultHost; + else if (is_absolute_path(hostname)) + + /* + * We should probably use canonicalize_path(), but then we have to + * bring path.c into libpq, and it doesn't seem worth it. + */ + if (strcmp(hostname, DEFAULT_PGSOCKET_DIR) == 0) + hostname = DefaultHost; + + if (port == NULL) + port = DEF_PGPORT_STR; + + if (!getPgPassFilename(pgpassfile)) + return NULL; + + /* If password file cannot be opened, ignore it. */ + if (stat(pgpassfile, &stat_buf) != 0) + return NULL; + +#ifndef WIN32 + if (!S_ISREG(stat_buf.st_mode)) + { + fprintf(stderr, + libpq_gettext("WARNING: password file \"%s\" is not a plain file\n"), + pgpassfile); + return NULL; + } + + /* If password file is insecure, alert the user and ignore it. */ + if (stat_buf.st_mode & (S_IRWXG | S_IRWXO)) + { + fprintf(stderr, + libpq_gettext("WARNING: password file \"%s\" has group or world access; permissions should be u=rw (0600) or less\n"), + pgpassfile); + return NULL; + } +#else + + /* + * On Win32, the directory is protected, so we don't have to check the + * file. + */ +#endif + + fp = fopen(pgpassfile, "r"); + if (fp == NULL) + return NULL; + + while (!feof(fp) && !ferror(fp)) + { + char *t = buf, + *ret, + *p1, + *p2; + int len; + + if (fgets(buf, sizeof(buf), fp) == NULL) + break; + + len = strlen(buf); + + /* Remove trailing newline */ + if (len > 0 && buf[len - 1] == '\n') + { + buf[--len] = '\0'; + /* Handle DOS-style line endings, too, even when not on Windows */ + if (len > 0 && buf[len - 1] == '\r') + buf[--len] = '\0'; + } + + if (len == 0) + continue; + + if ((t = pwdfMatchesString(t, hostname)) == NULL || + (t = pwdfMatchesString(t, port)) == NULL || + (t = pwdfMatchesString(t, dbname)) == NULL || + (t = pwdfMatchesString(t, username)) == NULL) + continue; + + /* Found a match. */ + ret = strdup(t); + fclose(fp); + + if (!ret) + { + /* Out of memory. XXX: an error message would be nice. */ + return NULL; + } + + /* De-escape password. */ + for (p1 = p2 = ret; *p1 != ':' && *p1 != '\0'; ++p1, ++p2) + { + if (*p1 == '\\' && p1[1] != '\0') + ++p1; + *p2 = *p1; + } + *p2 = '\0'; + + return ret; + } + + fclose(fp); + return NULL; + +#undef LINELEN +} + + +static bool +getPgPassFilename(char *pgpassfile) +{ + char *passfile_env; + + if ((passfile_env = getenv("PGPASSFILE")) != NULL) + /* use the literal path from the environment, if set */ + strlcpy(pgpassfile, passfile_env, MAXPGPATH); + else + { + char homedir[MAXPGPATH]; + + if (!pqGetHomeDirectory(homedir, sizeof(homedir))) + return false; + snprintf(pgpassfile, MAXPGPATH, "%s/%s", homedir, PGPASSFILE); + } + return true; +} + +/* + * If the connection failed, we should mention if + * we got the password from .pgpass in case that + * password is wrong. + */ +static void +dot_pg_pass_warning(PGconn *conn) +{ + /* If it was 'invalid authorization', add .pgpass mention */ + /* only works with >= 9.0 servers */ + if (conn->dot_pgpass_used && conn->password_needed && conn->result && + strcmp(PQresultErrorField(conn->result, PG_DIAG_SQLSTATE), + ERRCODE_INVALID_PASSWORD) == 0) + { + char pgpassfile[MAXPGPATH]; + + if (!getPgPassFilename(pgpassfile)) + return; + appendPQExpBuffer(&conn->errorMessage, + libpq_gettext("password retrieved from file \"%s\"\n"), + pgpassfile); + } +} + + +/* + * Obtain user's home directory, return in given buffer + * + * On Unix, this actually returns the user's home directory. On Windows + * it returns the PostgreSQL-specific application data folder. + * + * This is essentially the same as get_home_path(), but we don't use that + * because we don't want to pull path.c into libpq (it pollutes application + * namespace) + */ +bool +pqGetHomeDirectory(char *buf, int bufsize) +{ +#ifndef WIN32 + char pwdbuf[BUFSIZ]; + struct passwd pwdstr; + struct passwd *pwd = NULL; + + (void) pqGetpwuid(geteuid(), &pwdstr, pwdbuf, sizeof(pwdbuf), &pwd); + if (pwd == NULL) + return false; + strlcpy(buf, pwd->pw_dir, bufsize); + return true; +#else + char tmppath[MAX_PATH]; + + ZeroMemory(tmppath, sizeof(tmppath)); + if (SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, 0, tmppath) != S_OK) + return false; + snprintf(buf, bufsize, "%s/postgresql", tmppath); + return true; +#endif +} + +/* + * To keep the API consistent, the locking stubs are always provided, even + * if they are not required. + */ + +static void +default_threadlock(int acquire) +{ +#ifdef ENABLE_THREAD_SAFETY +#ifndef WIN32 + static pthread_mutex_t singlethread_lock = PTHREAD_MUTEX_INITIALIZER; +#else + static pthread_mutex_t singlethread_lock = NULL; + static long mutex_initlock = 0; + + if (singlethread_lock == NULL) + { + while (InterlockedExchange(&mutex_initlock, 1) == 1) + /* loop, another thread own the lock */ ; + if (singlethread_lock == NULL) + { + if (pthread_mutex_init(&singlethread_lock, NULL)) + PGTHREAD_ERROR("failed to initialize mutex"); + } + InterlockedExchange(&mutex_initlock, 0); + } +#endif + if (acquire) + { + if (pthread_mutex_lock(&singlethread_lock)) + PGTHREAD_ERROR("failed to lock mutex"); + } + else + { + if (pthread_mutex_unlock(&singlethread_lock)) + PGTHREAD_ERROR("failed to unlock mutex"); + } +#endif +} + +pgthreadlock_t +PQregisterThreadLock(pgthreadlock_t newhandler) +{ + pgthreadlock_t prev = pg_g_threadlock; + + if (newhandler) + pg_g_threadlock = newhandler; + else + pg_g_threadlock = default_threadlock; + + return prev; +} diff --git a/libpq/fe-exec.c b/libpq/fe-exec.c new file mode 100644 index 0000000..87ff565 --- /dev/null +++ b/libpq/fe-exec.c @@ -0,0 +1,3764 @@ +/*------------------------------------------------------------------------- + * + * fe-exec.c + * functions related to sending a query down to the backend + * + * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/interfaces/libpq/fe-exec.c + * + *------------------------------------------------------------------------- + */ +#include "postgres_fe.h" + +#include +#include + +#include "libpq-fe.h" +#include "libpq-int.h" + +#include "mb/pg_wchar.h" + +#ifdef WIN32 +#include "win32.h" +#else +#include +#endif + +/* keep this in same order as ExecStatusType in libpq-fe.h */ +char *const pgresStatus[] = { + "PGRES_EMPTY_QUERY", + "PGRES_COMMAND_OK", + "PGRES_TUPLES_OK", + "PGRES_COPY_OUT", + "PGRES_COPY_IN", + "PGRES_BAD_RESPONSE", + "PGRES_NONFATAL_ERROR", + "PGRES_FATAL_ERROR", + "PGRES_COPY_BOTH", + "PGRES_SINGLE_TUPLE" +}; + +/* + * static state needed by PQescapeString and PQescapeBytea; initialize to + * values that result in backward-compatible behavior + */ +static int static_client_encoding = PG_SQL_ASCII; +static bool static_std_strings = false; + + +static PGEvent *dupEvents(PGEvent *events, int count); +static bool pqAddTuple(PGresult *res, PGresAttValue *tup); +static bool PQsendQueryStart(PGconn *conn); +static int PQsendQueryGuts(PGconn *conn, + const char *command, + const char *stmtName, + int nParams, + const Oid *paramTypes, + const char *const * paramValues, + const int *paramLengths, + const int *paramFormats, + int resultFormat); +static void parseInput(PGconn *conn); +static PGresult *getCopyResult(PGconn *conn, ExecStatusType copytype); +static bool PQexecStart(PGconn *conn); +static PGresult *PQexecFinish(PGconn *conn); +static int PQsendDescribe(PGconn *conn, char desc_type, + const char *desc_target); +static int check_field_number(const PGresult *res, int field_num); + + +/* ---------------- + * Space management for PGresult. + * + * Formerly, libpq did a separate malloc() for each field of each tuple + * returned by a query. This was remarkably expensive --- malloc/free + * consumed a sizable part of the application's runtime. And there is + * no real need to keep track of the fields separately, since they will + * all be freed together when the PGresult is released. So now, we grab + * large blocks of storage from malloc and allocate space for query data + * within these blocks, using a trivially simple allocator. This reduces + * the number of malloc/free calls dramatically, and it also avoids + * fragmentation of the malloc storage arena. + * The PGresult structure itself is still malloc'd separately. We could + * combine it with the first allocation block, but that would waste space + * for the common case that no extra storage is actually needed (that is, + * the SQL command did not return tuples). + * + * We also malloc the top-level array of tuple pointers separately, because + * we need to be able to enlarge it via realloc, and our trivial space + * allocator doesn't handle that effectively. (Too bad the FE/BE protocol + * doesn't tell us up front how many tuples will be returned.) + * All other subsidiary storage for a PGresult is kept in PGresult_data blocks + * of size PGRESULT_DATA_BLOCKSIZE. The overhead at the start of each block + * is just a link to the next one, if any. Free-space management info is + * kept in the owning PGresult. + * A query returning a small amount of data will thus require three malloc + * calls: one for the PGresult, one for the tuples pointer array, and one + * PGresult_data block. + * + * Only the most recently allocated PGresult_data block is a candidate to + * have more stuff added to it --- any extra space left over in older blocks + * is wasted. We could be smarter and search the whole chain, but the point + * here is to be simple and fast. Typical applications do not keep a PGresult + * around very long anyway, so some wasted space within one is not a problem. + * + * Tuning constants for the space allocator are: + * PGRESULT_DATA_BLOCKSIZE: size of a standard allocation block, in bytes + * PGRESULT_ALIGN_BOUNDARY: assumed alignment requirement for binary data + * PGRESULT_SEP_ALLOC_THRESHOLD: objects bigger than this are given separate + * blocks, instead of being crammed into a regular allocation block. + * Requirements for correct function are: + * PGRESULT_ALIGN_BOUNDARY must be a multiple of the alignment requirements + * of all machine data types. (Currently this is set from configure + * tests, so it should be OK automatically.) + * PGRESULT_SEP_ALLOC_THRESHOLD + PGRESULT_BLOCK_OVERHEAD <= + * PGRESULT_DATA_BLOCKSIZE + * pqResultAlloc assumes an object smaller than the threshold will fit + * in a new block. + * The amount of space wasted at the end of a block could be as much as + * PGRESULT_SEP_ALLOC_THRESHOLD, so it doesn't pay to make that too large. + * ---------------- + */ + +#define PGRESULT_DATA_BLOCKSIZE 2048 +#define PGRESULT_ALIGN_BOUNDARY MAXIMUM_ALIGNOF /* from configure */ +#define PGRESULT_BLOCK_OVERHEAD Max(sizeof(PGresult_data), PGRESULT_ALIGN_BOUNDARY) +#define PGRESULT_SEP_ALLOC_THRESHOLD (PGRESULT_DATA_BLOCKSIZE / 2) + + +/* + * PQmakeEmptyPGresult + * returns a newly allocated, initialized PGresult with given status. + * If conn is not NULL and status indicates an error, the conn's + * errorMessage is copied. Also, any PGEvents are copied from the conn. + */ +PGresult * +PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status) +{ + PGresult *result; + + result = (PGresult *) malloc(sizeof(PGresult)); + if (!result) + return NULL; + + result->ntups = 0; + result->numAttributes = 0; + result->attDescs = NULL; + result->tuples = NULL; + result->tupArrSize = 0; + result->numParameters = 0; + result->paramDescs = NULL; + result->resultStatus = status; + result->cmdStatus[0] = '\0'; + result->binary = 0; + result->events = NULL; + result->nEvents = 0; + result->errMsg = NULL; + result->errFields = NULL; + result->errQuery = NULL; + result->null_field[0] = '\0'; + result->curBlock = NULL; + result->curOffset = 0; + result->spaceLeft = 0; + + if (conn) + { + /* copy connection data we might need for operations on PGresult */ + result->noticeHooks = conn->noticeHooks; + result->client_encoding = conn->client_encoding; + + /* consider copying conn's errorMessage */ + switch (status) + { + case PGRES_EMPTY_QUERY: + case PGRES_COMMAND_OK: + case PGRES_TUPLES_OK: + case PGRES_COPY_OUT: + case PGRES_COPY_IN: + case PGRES_COPY_BOTH: + case PGRES_SINGLE_TUPLE: + /* non-error cases */ + break; + default: + pqSetResultError(result, conn->errorMessage.data); + break; + } + + /* copy events last; result must be valid if we need to PQclear */ + if (conn->nEvents > 0) + { + result->events = dupEvents(conn->events, conn->nEvents); + if (!result->events) + { + PQclear(result); + return NULL; + } + result->nEvents = conn->nEvents; + } + } + else + { + /* defaults... */ + result->noticeHooks.noticeRec = NULL; + result->noticeHooks.noticeRecArg = NULL; + result->noticeHooks.noticeProc = NULL; + result->noticeHooks.noticeProcArg = NULL; + result->client_encoding = PG_SQL_ASCII; + } + + return result; +} + +/* + * PQsetResultAttrs + * + * Set the attributes for a given result. This function fails if there are + * already attributes contained in the provided result. The call is + * ignored if numAttributes is zero or attDescs is NULL. If the + * function fails, it returns zero. If the function succeeds, it + * returns a non-zero value. + */ +int +PQsetResultAttrs(PGresult *res, int numAttributes, PGresAttDesc *attDescs) +{ + int i; + + /* If attrs already exist, they cannot be overwritten. */ + if (!res || res->numAttributes > 0) + return FALSE; + + /* ignore no-op request */ + if (numAttributes <= 0 || !attDescs) + return TRUE; + + res->attDescs = (PGresAttDesc *) + PQresultAlloc(res, numAttributes * sizeof(PGresAttDesc)); + + if (!res->attDescs) + return FALSE; + + res->numAttributes = numAttributes; + memcpy(res->attDescs, attDescs, numAttributes * sizeof(PGresAttDesc)); + + /* deep-copy the attribute names, and determine format */ + res->binary = 1; + for (i = 0; i < res->numAttributes; i++) + { + if (res->attDescs[i].name) + res->attDescs[i].name = pqResultStrdup(res, res->attDescs[i].name); + else + res->attDescs[i].name = res->null_field; + + if (!res->attDescs[i].name) + return FALSE; + + if (res->attDescs[i].format == 0) + res->binary = 0; + } + + return TRUE; +} + +/* + * PQcopyResult + * + * Returns a deep copy of the provided 'src' PGresult, which cannot be NULL. + * The 'flags' argument controls which portions of the result will or will + * NOT be copied. The created result is always put into the + * PGRES_TUPLES_OK status. The source result error message is not copied, + * although cmdStatus is. + * + * To set custom attributes, use PQsetResultAttrs. That function requires + * that there are no attrs contained in the result, so to use that + * function you cannot use the PG_COPYRES_ATTRS or PG_COPYRES_TUPLES + * options with this function. + * + * Options: + * PG_COPYRES_ATTRS - Copy the source result's attributes + * + * PG_COPYRES_TUPLES - Copy the source result's tuples. This implies + * copying the attrs, seeing how the attrs are needed by the tuples. + * + * PG_COPYRES_EVENTS - Copy the source result's events. + * + * PG_COPYRES_NOTICEHOOKS - Copy the source result's notice hooks. + */ +PGresult * +PQcopyResult(const PGresult *src, int flags) +{ + PGresult *dest; + int i; + + if (!src) + return NULL; + + dest = PQmakeEmptyPGresult(NULL, PGRES_TUPLES_OK); + if (!dest) + return NULL; + + /* Always copy these over. Is cmdStatus really useful here? */ + dest->client_encoding = src->client_encoding; + strcpy(dest->cmdStatus, src->cmdStatus); + + /* Wants attrs? */ + if (flags & (PG_COPYRES_ATTRS | PG_COPYRES_TUPLES)) + { + if (!PQsetResultAttrs(dest, src->numAttributes, src->attDescs)) + { + PQclear(dest); + return NULL; + } + } + + /* Wants to copy tuples? */ + if (flags & PG_COPYRES_TUPLES) + { + int tup, + field; + + for (tup = 0; tup < src->ntups; tup++) + { + for (field = 0; field < src->numAttributes; field++) + { + if (!PQsetvalue(dest, tup, field, + src->tuples[tup][field].value, + src->tuples[tup][field].len)) + { + PQclear(dest); + return NULL; + } + } + } + } + + /* Wants to copy notice hooks? */ + if (flags & PG_COPYRES_NOTICEHOOKS) + dest->noticeHooks = src->noticeHooks; + + /* Wants to copy PGEvents? */ + if ((flags & PG_COPYRES_EVENTS) && src->nEvents > 0) + { + dest->events = dupEvents(src->events, src->nEvents); + if (!dest->events) + { + PQclear(dest); + return NULL; + } + dest->nEvents = src->nEvents; + } + + /* Okay, trigger PGEVT_RESULTCOPY event */ + for (i = 0; i < dest->nEvents; i++) + { + if (src->events[i].resultInitialized) + { + PGEventResultCopy evt; + + evt.src = src; + evt.dest = dest; + if (!dest->events[i].proc(PGEVT_RESULTCOPY, &evt, + dest->events[i].passThrough)) + { + PQclear(dest); + return NULL; + } + dest->events[i].resultInitialized = TRUE; + } + } + + return dest; +} + +/* + * Copy an array of PGEvents (with no extra space for more). + * Does not duplicate the event instance data, sets this to NULL. + * Also, the resultInitialized flags are all cleared. + */ +static PGEvent * +dupEvents(PGEvent *events, int count) +{ + PGEvent *newEvents; + int i; + + if (!events || count <= 0) + return NULL; + + newEvents = (PGEvent *) malloc(count * sizeof(PGEvent)); + if (!newEvents) + return NULL; + + for (i = 0; i < count; i++) + { + newEvents[i].proc = events[i].proc; + newEvents[i].passThrough = events[i].passThrough; + newEvents[i].data = NULL; + newEvents[i].resultInitialized = FALSE; + newEvents[i].name = strdup(events[i].name); + if (!newEvents[i].name) + { + while (--i >= 0) + free(newEvents[i].name); + free(newEvents); + return NULL; + } + } + + return newEvents; +} + + +/* + * Sets the value for a tuple field. The tup_num must be less than or + * equal to PQntuples(res). If it is equal, a new tuple is created and + * added to the result. + * Returns a non-zero value for success and zero for failure. + */ +int +PQsetvalue(PGresult *res, int tup_num, int field_num, char *value, int len) +{ + PGresAttValue *attval; + + if (!check_field_number(res, field_num)) + return FALSE; + + /* Invalid tup_num, must be <= ntups */ + if (tup_num < 0 || tup_num > res->ntups) + return FALSE; + + /* need to allocate a new tuple? */ + if (tup_num == res->ntups) + { + PGresAttValue *tup; + int i; + + tup = (PGresAttValue *) + pqResultAlloc(res, res->numAttributes * sizeof(PGresAttValue), + TRUE); + + if (!tup) + return FALSE; + + /* initialize each column to NULL */ + for (i = 0; i < res->numAttributes; i++) + { + tup[i].len = NULL_LEN; + tup[i].value = res->null_field; + } + + /* add it to the array */ + if (!pqAddTuple(res, tup)) + return FALSE; + } + + attval = &res->tuples[tup_num][field_num]; + + /* treat either NULL_LEN or NULL value pointer as a NULL field */ + if (len == NULL_LEN || value == NULL) + { + attval->len = NULL_LEN; + attval->value = res->null_field; + } + else if (len <= 0) + { + attval->len = 0; + attval->value = res->null_field; + } + else + { + attval->value = (char *) pqResultAlloc(res, len + 1, TRUE); + if (!attval->value) + return FALSE; + attval->len = len; + memcpy(attval->value, value, len); + attval->value[len] = '\0'; + } + + return TRUE; +} + +/* + * pqResultAlloc - exported routine to allocate local storage in a PGresult. + * + * We force all such allocations to be maxaligned, since we don't know + * whether the value might be binary. + */ +void * +PQresultAlloc(PGresult *res, size_t nBytes) +{ + return pqResultAlloc(res, nBytes, TRUE); +} + +/* + * pqResultAlloc - + * Allocate subsidiary storage for a PGresult. + * + * nBytes is the amount of space needed for the object. + * If isBinary is true, we assume that we need to align the object on + * a machine allocation boundary. + * If isBinary is false, we assume the object is a char string and can + * be allocated on any byte boundary. + */ +void * +pqResultAlloc(PGresult *res, size_t nBytes, bool isBinary) +{ + char *space; + PGresult_data *block; + + if (!res) + return NULL; + + if (nBytes <= 0) + return res->null_field; + + /* + * If alignment is needed, round up the current position to an alignment + * boundary. + */ + if (isBinary) + { + int offset = res->curOffset % PGRESULT_ALIGN_BOUNDARY; + + if (offset) + { + res->curOffset += PGRESULT_ALIGN_BOUNDARY - offset; + res->spaceLeft -= PGRESULT_ALIGN_BOUNDARY - offset; + } + } + + /* If there's enough space in the current block, no problem. */ + if (nBytes <= (size_t) res->spaceLeft) + { + space = res->curBlock->space + res->curOffset; + res->curOffset += nBytes; + res->spaceLeft -= nBytes; + return space; + } + + /* + * If the requested object is very large, give it its own block; this + * avoids wasting what might be most of the current block to start a new + * block. (We'd have to special-case requests bigger than the block size + * anyway.) The object is always given binary alignment in this case. + */ + if (nBytes >= PGRESULT_SEP_ALLOC_THRESHOLD) + { + block = (PGresult_data *) malloc(nBytes + PGRESULT_BLOCK_OVERHEAD); + if (!block) + return NULL; + space = block->space + PGRESULT_BLOCK_OVERHEAD; + if (res->curBlock) + { + /* + * Tuck special block below the active block, so that we don't + * have to waste the free space in the active block. + */ + block->next = res->curBlock->next; + res->curBlock->next = block; + } + else + { + /* Must set up the new block as the first active block. */ + block->next = NULL; + res->curBlock = block; + res->spaceLeft = 0; /* be sure it's marked full */ + } + return space; + } + + /* Otherwise, start a new block. */ + block = (PGresult_data *) malloc(PGRESULT_DATA_BLOCKSIZE); + if (!block) + return NULL; + block->next = res->curBlock; + res->curBlock = block; + if (isBinary) + { + /* object needs full alignment */ + res->curOffset = PGRESULT_BLOCK_OVERHEAD; + res->spaceLeft = PGRESULT_DATA_BLOCKSIZE - PGRESULT_BLOCK_OVERHEAD; + } + else + { + /* we can cram it right after the overhead pointer */ + res->curOffset = sizeof(PGresult_data); + res->spaceLeft = PGRESULT_DATA_BLOCKSIZE - sizeof(PGresult_data); + } + + space = block->space + res->curOffset; + res->curOffset += nBytes; + res->spaceLeft -= nBytes; + return space; +} + +/* + * pqResultStrdup - + * Like strdup, but the space is subsidiary PGresult space. + */ +char * +pqResultStrdup(PGresult *res, const char *str) +{ + char *space = (char *) pqResultAlloc(res, strlen(str) + 1, FALSE); + + if (space) + strcpy(space, str); + return space; +} + +/* + * pqSetResultError - + * assign a new error message to a PGresult + */ +void +pqSetResultError(PGresult *res, const char *msg) +{ + if (!res) + return; + if (msg && *msg) + res->errMsg = pqResultStrdup(res, msg); + else + res->errMsg = NULL; +} + +/* + * pqCatenateResultError - + * concatenate a new error message to the one already in a PGresult + */ +void +pqCatenateResultError(PGresult *res, const char *msg) +{ + PQExpBufferData errorBuf; + + if (!res || !msg) + return; + initPQExpBuffer(&errorBuf); + if (res->errMsg) + appendPQExpBufferStr(&errorBuf, res->errMsg); + appendPQExpBufferStr(&errorBuf, msg); + pqSetResultError(res, errorBuf.data); + termPQExpBuffer(&errorBuf); +} + +/* + * PQclear - + * free's the memory associated with a PGresult + */ +void +PQclear(PGresult *res) +{ + PGresult_data *block; + int i; + + if (!res) + return; + + for (i = 0; i < res->nEvents; i++) + { + /* only send DESTROY to successfully-initialized event procs */ + if (res->events[i].resultInitialized) + { + PGEventResultDestroy evt; + + evt.result = res; + (void) res->events[i].proc(PGEVT_RESULTDESTROY, &evt, + res->events[i].passThrough); + } + free(res->events[i].name); + } + + if (res->events) + free(res->events); + + /* Free all the subsidiary blocks */ + while ((block = res->curBlock) != NULL) + { + res->curBlock = block->next; + free(block); + } + + /* Free the top-level tuple pointer array */ + if (res->tuples) + free(res->tuples); + + /* zero out the pointer fields to catch programming errors */ + res->attDescs = NULL; + res->tuples = NULL; + res->paramDescs = NULL; + res->errFields = NULL; + res->events = NULL; + res->nEvents = 0; + /* res->curBlock was zeroed out earlier */ + + /* Free the PGresult structure itself */ + free(res); +} + +/* + * Handy subroutine to deallocate any partially constructed async result. + * + * Any "next" result gets cleared too. + */ +void +pqClearAsyncResult(PGconn *conn) +{ + if (conn->result) + PQclear(conn->result); + conn->result = NULL; + if (conn->next_result) + PQclear(conn->next_result); + conn->next_result = NULL; +} + +/* + * This subroutine deletes any existing async result, sets conn->result + * to a PGresult with status PGRES_FATAL_ERROR, and stores the current + * contents of conn->errorMessage into that result. It differs from a + * plain call on PQmakeEmptyPGresult() in that if there is already an + * async result with status PGRES_FATAL_ERROR, the current error message + * is APPENDED to the old error message instead of replacing it. This + * behavior lets us report multiple error conditions properly, if necessary. + * (An example where this is needed is when the backend sends an 'E' message + * and immediately closes the connection --- we want to report both the + * backend error and the connection closure error.) + */ +void +pqSaveErrorResult(PGconn *conn) +{ + /* + * If no old async result, just let PQmakeEmptyPGresult make one. Likewise + * if old result is not an error message. + */ + if (conn->result == NULL || + conn->result->resultStatus != PGRES_FATAL_ERROR || + conn->result->errMsg == NULL) + { + pqClearAsyncResult(conn); + conn->result = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR); + } + else + { + /* Else, concatenate error message to existing async result. */ + pqCatenateResultError(conn->result, conn->errorMessage.data); + } +} + +/* + * This subroutine prepares an async result object for return to the caller. + * If there is not already an async result object, build an error object + * using whatever is in conn->errorMessage. In any case, clear the async + * result storage and make sure PQerrorMessage will agree with the result's + * error string. + */ +PGresult * +pqPrepareAsyncResult(PGconn *conn) +{ + PGresult *res; + + /* + * conn->result is the PGresult to return. If it is NULL (which probably + * shouldn't happen) we assume there is an appropriate error message in + * conn->errorMessage. + */ + res = conn->result; + if (!res) + res = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR); + else + { + /* + * Make sure PQerrorMessage agrees with result; it could be different + * if we have concatenated messages. + */ + resetPQExpBuffer(&conn->errorMessage); + appendPQExpBufferStr(&conn->errorMessage, + PQresultErrorMessage(res)); + } + + /* + * Replace conn->result with next_result, if any. In the normal case + * there isn't a next result and we're just dropping ownership of the + * current result. In single-row mode this restores the situation to what + * it was before we created the current single-row result. + */ + conn->result = conn->next_result; + conn->next_result = NULL; + + return res; +} + +/* + * pqInternalNotice - produce an internally-generated notice message + * + * A format string and optional arguments can be passed. Note that we do + * libpq_gettext() here, so callers need not. + * + * The supplied text is taken as primary message (ie., it should not include + * a trailing newline, and should not be more than one line). + */ +void +pqInternalNotice(const PGNoticeHooks *hooks, const char *fmt,...) +{ + char msgBuf[1024]; + va_list args; + PGresult *res; + + if (hooks->noticeRec == NULL) + return; /* nobody home to receive notice? */ + + /* Format the message */ + va_start(args, fmt); + vsnprintf(msgBuf, sizeof(msgBuf), libpq_gettext(fmt), args); + va_end(args); + msgBuf[sizeof(msgBuf) - 1] = '\0'; /* make real sure it's terminated */ + + /* Make a PGresult to pass to the notice receiver */ + res = PQmakeEmptyPGresult(NULL, PGRES_NONFATAL_ERROR); + if (!res) + return; + res->noticeHooks = *hooks; + + /* + * Set up fields of notice. + */ + pqSaveMessageField(res, PG_DIAG_MESSAGE_PRIMARY, msgBuf); + pqSaveMessageField(res, PG_DIAG_SEVERITY, libpq_gettext("NOTICE")); + pqSaveMessageField(res, PG_DIAG_SEVERITY_NONLOCALIZED, "NOTICE"); + /* XXX should provide a SQLSTATE too? */ + + /* + * Result text is always just the primary message + newline. If we can't + * allocate it, don't bother invoking the receiver. + */ + res->errMsg = (char *) pqResultAlloc(res, strlen(msgBuf) + 2, FALSE); + if (res->errMsg) + { + sprintf(res->errMsg, "%s\n", msgBuf); + + /* + * Pass to receiver, then free it. + */ + (*res->noticeHooks.noticeRec) (res->noticeHooks.noticeRecArg, res); + } + PQclear(res); +} + +/* + * pqAddTuple + * add a row pointer to the PGresult structure, growing it if necessary + * Returns TRUE if OK, FALSE if not enough memory to add the row + */ +static bool +pqAddTuple(PGresult *res, PGresAttValue *tup) +{ + if (res->ntups >= res->tupArrSize) + { + /* + * Try to grow the array. + * + * We can use realloc because shallow copying of the structure is + * okay. Note that the first time through, res->tuples is NULL. While + * ANSI says that realloc() should act like malloc() in that case, + * some old C libraries (like SunOS 4.1.x) coredump instead. On + * failure realloc is supposed to return NULL without damaging the + * existing allocation. Note that the positions beyond res->ntups are + * garbage, not necessarily NULL. + */ + int newSize = (res->tupArrSize > 0) ? res->tupArrSize * 2 : 128; + PGresAttValue **newTuples; + + if (res->tuples == NULL) + newTuples = (PGresAttValue **) + malloc(newSize * sizeof(PGresAttValue *)); + else + newTuples = (PGresAttValue **) + realloc(res->tuples, newSize * sizeof(PGresAttValue *)); + if (!newTuples) + return FALSE; /* malloc or realloc failed */ + res->tupArrSize = newSize; + res->tuples = newTuples; + } + res->tuples[res->ntups] = tup; + res->ntups++; + return TRUE; +} + +/* + * pqSaveMessageField - save one field of an error or notice message + */ +void +pqSaveMessageField(PGresult *res, char code, const char *value) +{ + PGMessageField *pfield; + + pfield = (PGMessageField *) + pqResultAlloc(res, + offsetof(PGMessageField, contents) + + strlen(value) + 1, + TRUE); + if (!pfield) + return; /* out of memory? */ + pfield->code = code; + strcpy(pfield->contents, value); + pfield->next = res->errFields; + res->errFields = pfield; +} + +/* + * pqSaveParameterStatus - remember parameter status sent by backend + */ +void +pqSaveParameterStatus(PGconn *conn, const char *name, const char *value) +{ + pgParameterStatus *pstatus; + pgParameterStatus *prev; + + if (conn->Pfdebug) + fprintf(conn->Pfdebug, "pqSaveParameterStatus: '%s' = '%s'\n", + name, value); + + /* + * Forget any old information about the parameter + */ + for (pstatus = conn->pstatus, prev = NULL; + pstatus != NULL; + prev = pstatus, pstatus = pstatus->next) + { + if (strcmp(pstatus->name, name) == 0) + { + if (prev) + prev->next = pstatus->next; + else + conn->pstatus = pstatus->next; + free(pstatus); /* frees name and value strings too */ + break; + } + } + + /* + * Store new info as a single malloc block + */ + pstatus = (pgParameterStatus *) malloc(sizeof(pgParameterStatus) + + strlen(name) +strlen(value) + 2); + if (pstatus) + { + char *ptr; + + ptr = ((char *) pstatus) + sizeof(pgParameterStatus); + pstatus->name = ptr; + strcpy(ptr, name); + ptr += strlen(name) + 1; + pstatus->value = ptr; + strcpy(ptr, value); + pstatus->next = conn->pstatus; + conn->pstatus = pstatus; + } + + /* + * Special hacks: remember client_encoding and + * standard_conforming_strings, and convert server version to a numeric + * form. We keep the first two of these in static variables as well, so + * that PQescapeString and PQescapeBytea can behave somewhat sanely (at + * least in single-connection-using programs). + */ + if (strcmp(name, "client_encoding") == 0) + { + conn->client_encoding = pg_char_to_encoding(value); + /* if we don't recognize the encoding name, fall back to SQL_ASCII */ + if (conn->client_encoding < 0) + conn->client_encoding = PG_SQL_ASCII; + static_client_encoding = conn->client_encoding; + } + else if (strcmp(name, "standard_conforming_strings") == 0) + { + conn->std_strings = (strcmp(value, "on") == 0); + static_std_strings = conn->std_strings; + } + else if (strcmp(name, "server_version") == 0) + { + int cnt; + int vmaj, + vmin, + vrev; + + cnt = sscanf(value, "%d.%d.%d", &vmaj, &vmin, &vrev); + + if (cnt == 3) + { + /* old style, e.g. 9.6.1 */ + conn->sversion = (100 * vmaj + vmin) * 100 + vrev; + } + else if (cnt == 2) + { + if (vmaj >= 10) + { + /* new style, e.g. 10.1 */ + conn->sversion = 100 * 100 * vmaj + vmin; + } + else + { + /* old style without minor version, e.g. 9.6devel */ + conn->sversion = (100 * vmaj + vmin) * 100; + } + } + else if (cnt == 1) + { + /* new style without minor version, e.g. 10devel */ + conn->sversion = 100 * 100 * vmaj; + } + else + conn->sversion = 0; /* unknown */ + } +} + + +/* + * pqRowProcessor + * Add the received row to the current async result (conn->result). + * Returns 1 if OK, 0 if error occurred. + * + * On error, *errmsgp can be set to an error string to be returned. + * If it is left NULL, the error is presumed to be "out of memory". + * + * In single-row mode, we create a new result holding just the current row, + * stashing the previous result in conn->next_result so that it becomes + * active again after pqPrepareAsyncResult(). This allows the result metadata + * (column descriptions) to be carried forward to each result row. + */ +int +pqRowProcessor(PGconn *conn, const char **errmsgp) +{ + PGresult *res = conn->result; + int nfields = res->numAttributes; + const PGdataValue *columns = conn->rowBuf; + PGresAttValue *tup; + int i; + + /* + * In single-row mode, make a new PGresult that will hold just this one + * row; the original conn->result is left unchanged so that it can be used + * again as the template for future rows. + */ + if (conn->singleRowMode) + { + /* Copy everything that should be in the result at this point */ + res = PQcopyResult(res, + PG_COPYRES_ATTRS | PG_COPYRES_EVENTS | + PG_COPYRES_NOTICEHOOKS); + if (!res) + return 0; + } + + /* + * Basically we just allocate space in the PGresult for each field and + * copy the data over. + * + * Note: on malloc failure, we return 0 leaving *errmsgp still NULL, which + * caller will take to mean "out of memory". This is preferable to trying + * to set up such a message here, because evidently there's not enough + * memory for gettext() to do anything. + */ + tup = (PGresAttValue *) + pqResultAlloc(res, nfields * sizeof(PGresAttValue), TRUE); + if (tup == NULL) + goto fail; + + for (i = 0; i < nfields; i++) + { + int clen = columns[i].len; + + if (clen < 0) + { + /* null field */ + tup[i].len = NULL_LEN; + tup[i].value = res->null_field; + } + else + { + bool isbinary = (res->attDescs[i].format != 0); + char *val; + + val = (char *) pqResultAlloc(res, clen + 1, isbinary); + if (val == NULL) + goto fail; + + /* copy and zero-terminate the data (even if it's binary) */ + memcpy(val, columns[i].value, clen); + val[clen] = '\0'; + + tup[i].len = clen; + tup[i].value = val; + } + } + + /* And add the tuple to the PGresult's tuple array */ + if (!pqAddTuple(res, tup)) + goto fail; + + /* + * Success. In single-row mode, make the result available to the client + * immediately. + */ + if (conn->singleRowMode) + { + /* Change result status to special single-row value */ + res->resultStatus = PGRES_SINGLE_TUPLE; + /* Stash old result for re-use later */ + conn->next_result = conn->result; + conn->result = res; + /* And mark the result ready to return */ + conn->asyncStatus = PGASYNC_READY; + } + + return 1; + +fail: + /* release locally allocated PGresult, if we made one */ + if (res != conn->result) + PQclear(res); + return 0; +} + + +/* + * PQsendQuery + * Submit a query, but don't wait for it to finish + * + * Returns: 1 if successfully submitted + * 0 if error (conn->errorMessage is set) + */ +int +PQsendQuery(PGconn *conn, const char *query) +{ + if (!PQsendQueryStart(conn)) + return 0; + + /* check the argument */ + if (!query) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("command string is a null pointer\n")); + return 0; + } + + /* construct the outgoing Query message */ + if (pqPutMsgStart('Q', false, conn) < 0 || + pqPuts(query, conn) < 0 || + pqPutMsgEnd(conn) < 0) + { + pqHandleSendFailure(conn); + return 0; + } + + /* remember we are using simple query protocol */ + conn->queryclass = PGQUERY_SIMPLE; + + /* and remember the query text too, if possible */ + /* if insufficient memory, last_query just winds up NULL */ + if (conn->last_query) + free(conn->last_query); + conn->last_query = strdup(query); + + /* + * Give the data a push. In nonblock mode, don't complain if we're unable + * to send it all; PQgetResult() will do any additional flushing needed. + */ + if (pqFlush(conn) < 0) + { + pqHandleSendFailure(conn); + return 0; + } + + /* OK, it's launched! */ + conn->asyncStatus = PGASYNC_BUSY; + return 1; +} + +/* + * PQsendQueryParams + * Like PQsendQuery, but use protocol 3.0 so we can pass parameters + */ +int +PQsendQueryParams(PGconn *conn, + const char *command, + int nParams, + const Oid *paramTypes, + const char *const * paramValues, + const int *paramLengths, + const int *paramFormats, + int resultFormat) +{ + if (!PQsendQueryStart(conn)) + return 0; + + /* check the arguments */ + if (!command) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("command string is a null pointer\n")); + return 0; + } + if (nParams < 0 || nParams > 65535) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("number of parameters must be between 0 and 65535\n")); + return 0; + } + + return PQsendQueryGuts(conn, + command, + "", /* use unnamed statement */ + nParams, + paramTypes, + paramValues, + paramLengths, + paramFormats, + resultFormat); +} + +/* + * PQsendPrepare + * Submit a Parse message, but don't wait for it to finish + * + * Returns: 1 if successfully submitted + * 0 if error (conn->errorMessage is set) + */ +int +PQsendPrepare(PGconn *conn, + const char *stmtName, const char *query, + int nParams, const Oid *paramTypes) +{ + if (!PQsendQueryStart(conn)) + return 0; + + /* check the arguments */ + if (!stmtName) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("statement name is a null pointer\n")); + return 0; + } + if (!query) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("command string is a null pointer\n")); + return 0; + } + if (nParams < 0 || nParams > 65535) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("number of parameters must be between 0 and 65535\n")); + return 0; + } + + /* This isn't gonna work on a 2.0 server */ + if (PG_PROTOCOL_MAJOR(conn->pversion) < 3) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("function requires at least protocol version 3.0\n")); + return 0; + } + + /* construct the Parse message */ + if (pqPutMsgStart('P', false, conn) < 0 || + pqPuts(stmtName, conn) < 0 || + pqPuts(query, conn) < 0) + goto sendFailed; + + if (nParams > 0 && paramTypes) + { + int i; + + if (pqPutInt(nParams, 2, conn) < 0) + goto sendFailed; + for (i = 0; i < nParams; i++) + { + if (pqPutInt(paramTypes[i], 4, conn) < 0) + goto sendFailed; + } + } + else + { + if (pqPutInt(0, 2, conn) < 0) + goto sendFailed; + } + if (pqPutMsgEnd(conn) < 0) + goto sendFailed; + + /* construct the Sync message */ + if (pqPutMsgStart('S', false, conn) < 0 || + pqPutMsgEnd(conn) < 0) + goto sendFailed; + + /* remember we are doing just a Parse */ + conn->queryclass = PGQUERY_PREPARE; + + /* and remember the query text too, if possible */ + /* if insufficient memory, last_query just winds up NULL */ + if (conn->last_query) + free(conn->last_query); + conn->last_query = strdup(query); + + /* + * Give the data a push. In nonblock mode, don't complain if we're unable + * to send it all; PQgetResult() will do any additional flushing needed. + */ + if (pqFlush(conn) < 0) + goto sendFailed; + + /* OK, it's launched! */ + conn->asyncStatus = PGASYNC_BUSY; + return 1; + +sendFailed: + pqHandleSendFailure(conn); + return 0; +} + +/* + * PQsendQueryPrepared + * Like PQsendQuery, but execute a previously prepared statement, + * using protocol 3.0 so we can pass parameters + */ +int +PQsendQueryPrepared(PGconn *conn, + const char *stmtName, + int nParams, + const char *const * paramValues, + const int *paramLengths, + const int *paramFormats, + int resultFormat) +{ + if (!PQsendQueryStart(conn)) + return 0; + + /* check the arguments */ + if (!stmtName) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("statement name is a null pointer\n")); + return 0; + } + if (nParams < 0 || nParams > 65535) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("number of parameters must be between 0 and 65535\n")); + return 0; + } + + return PQsendQueryGuts(conn, + NULL, /* no command to parse */ + stmtName, + nParams, + NULL, /* no param types */ + paramValues, + paramLengths, + paramFormats, + resultFormat); +} + +/* + * Common startup code for PQsendQuery and sibling routines + */ +static bool +PQsendQueryStart(PGconn *conn) +{ + if (!conn) + return false; + + /* clear the error string */ + resetPQExpBuffer(&conn->errorMessage); + + /* Don't try to send if we know there's no live connection. */ + if (conn->status != CONNECTION_OK) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("no connection to the server\n")); + return false; + } + /* Can't send while already busy, either. */ + if (conn->asyncStatus != PGASYNC_IDLE) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("another command is already in progress\n")); + return false; + } + + /* initialize async result-accumulation state */ + pqClearAsyncResult(conn); + + /* reset single-row processing mode */ + conn->singleRowMode = false; + + /* ready to send command message */ + return true; +} + +/* + * PQsendQueryGuts + * Common code for protocol-3.0 query sending + * PQsendQueryStart should be done already + * + * command may be NULL to indicate we use an already-prepared statement + */ +static int +PQsendQueryGuts(PGconn *conn, + const char *command, + const char *stmtName, + int nParams, + const Oid *paramTypes, + const char *const * paramValues, + const int *paramLengths, + const int *paramFormats, + int resultFormat) +{ + int i; + + /* This isn't gonna work on a 2.0 server */ + if (PG_PROTOCOL_MAJOR(conn->pversion) < 3) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("function requires at least protocol version 3.0\n")); + return 0; + } + + /* + * We will send Parse (if needed), Bind, Describe Portal, Execute, Sync, + * using specified statement name and the unnamed portal. + */ + + if (command) + { + /* construct the Parse message */ + if (pqPutMsgStart('P', false, conn) < 0 || + pqPuts(stmtName, conn) < 0 || + pqPuts(command, conn) < 0) + goto sendFailed; + if (nParams > 0 && paramTypes) + { + if (pqPutInt(nParams, 2, conn) < 0) + goto sendFailed; + for (i = 0; i < nParams; i++) + { + if (pqPutInt(paramTypes[i], 4, conn) < 0) + goto sendFailed; + } + } + else + { + if (pqPutInt(0, 2, conn) < 0) + goto sendFailed; + } + if (pqPutMsgEnd(conn) < 0) + goto sendFailed; + } + + /* Construct the Bind message */ + if (pqPutMsgStart('B', false, conn) < 0 || + pqPuts("", conn) < 0 || + pqPuts(stmtName, conn) < 0) + goto sendFailed; + + /* Send parameter formats */ + if (nParams > 0 && paramFormats) + { + if (pqPutInt(nParams, 2, conn) < 0) + goto sendFailed; + for (i = 0; i < nParams; i++) + { + if (pqPutInt(paramFormats[i], 2, conn) < 0) + goto sendFailed; + } + } + else + { + if (pqPutInt(0, 2, conn) < 0) + goto sendFailed; + } + + if (pqPutInt(nParams, 2, conn) < 0) + goto sendFailed; + + /* Send parameters */ + for (i = 0; i < nParams; i++) + { + if (paramValues && paramValues[i]) + { + int nbytes; + + if (paramFormats && paramFormats[i] != 0) + { + /* binary parameter */ + if (paramLengths) + nbytes = paramLengths[i]; + else + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("length must be given for binary parameter\n")); + goto sendFailed; + } + } + else + { + /* text parameter, do not use paramLengths */ + nbytes = strlen(paramValues[i]); + } + if (pqPutInt(nbytes, 4, conn) < 0 || + pqPutnchar(paramValues[i], nbytes, conn) < 0) + goto sendFailed; + } + else + { + /* take the param as NULL */ + if (pqPutInt(-1, 4, conn) < 0) + goto sendFailed; + } + } + if (pqPutInt(1, 2, conn) < 0 || + pqPutInt(resultFormat, 2, conn)) + goto sendFailed; + if (pqPutMsgEnd(conn) < 0) + goto sendFailed; + + /* construct the Describe Portal message */ + if (pqPutMsgStart('D', false, conn) < 0 || + pqPutc('P', conn) < 0 || + pqPuts("", conn) < 0 || + pqPutMsgEnd(conn) < 0) + goto sendFailed; + + /* construct the Execute message */ + if (pqPutMsgStart('E', false, conn) < 0 || + pqPuts("", conn) < 0 || + pqPutInt(0, 4, conn) < 0 || + pqPutMsgEnd(conn) < 0) + goto sendFailed; + + /* construct the Sync message */ + if (pqPutMsgStart('S', false, conn) < 0 || + pqPutMsgEnd(conn) < 0) + goto sendFailed; + + /* remember we are using extended query protocol */ + conn->queryclass = PGQUERY_EXTENDED; + + /* and remember the query text too, if possible */ + /* if insufficient memory, last_query just winds up NULL */ + if (conn->last_query) + free(conn->last_query); + if (command) + conn->last_query = strdup(command); + else + conn->last_query = NULL; + + /* + * Give the data a push. In nonblock mode, don't complain if we're unable + * to send it all; PQgetResult() will do any additional flushing needed. + */ + if (pqFlush(conn) < 0) + goto sendFailed; + + /* OK, it's launched! */ + conn->asyncStatus = PGASYNC_BUSY; + return 1; + +sendFailed: + pqHandleSendFailure(conn); + return 0; +} + +/* + * pqHandleSendFailure: try to clean up after failure to send command. + * + * Primarily, what we want to accomplish here is to process any ERROR or + * NOTICE messages that the backend might have sent just before it died. + * Since we're in IDLE state, all such messages will get sent to the notice + * processor. + * + * NOTE: this routine should only be called in PGASYNC_IDLE state. + */ +void +pqHandleSendFailure(PGconn *conn) +{ + /* + * Accept and parse any available input data, ignoring I/O errors. Note + * that if pqReadData decides the backend has closed the channel, it will + * close our side of the socket --- that's just what we want here. + */ + while (pqReadData(conn) > 0) + parseInput(conn); + + /* + * Be sure to parse available input messages even if we read no data. + * (Note: calling parseInput within the above loop isn't really necessary, + * but it prevents buffer bloat if there's a lot of data available.) + */ + parseInput(conn); +} + +/* + * Select row-by-row processing mode + */ +int +PQsetSingleRowMode(PGconn *conn) +{ + /* + * Only allow setting the flag when we have launched a query and not yet + * received any results. + */ + if (!conn) + return 0; + if (conn->asyncStatus != PGASYNC_BUSY) + return 0; + if (conn->queryclass != PGQUERY_SIMPLE && + conn->queryclass != PGQUERY_EXTENDED) + return 0; + if (conn->result) + return 0; + + /* OK, set flag */ + conn->singleRowMode = true; + return 1; +} + +/* + * Consume any available input from the backend + * 0 return: some kind of trouble + * 1 return: no problem + */ +int +PQconsumeInput(PGconn *conn) +{ + if (!conn) + return 0; + + /* + * for non-blocking connections try to flush the send-queue, otherwise we + * may never get a response for something that may not have already been + * sent because it's in our write buffer! + */ + if (pqIsnonblocking(conn)) + { + if (pqFlush(conn) < 0) + return 0; + } + + /* + * Load more data, if available. We do this no matter what state we are + * in, since we are probably getting called because the application wants + * to get rid of a read-select condition. Note that we will NOT block + * waiting for more input. + */ + if (pqReadData(conn) < 0) + return 0; + + /* Parsing of the data waits till later. */ + return 1; +} + + +/* + * parseInput: if appropriate, parse input data from backend + * until input is exhausted or a stopping state is reached. + * Note that this function will NOT attempt to read more data from the backend. + */ +static void +parseInput(PGconn *conn) +{ + if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3) + pqParseInput3(conn); + else + pqParseInput2(conn); +} + +/* + * PQisBusy + * Return TRUE if PQgetResult would block waiting for input. + */ + +int +PQisBusy(PGconn *conn) +{ + if (!conn) + return FALSE; + + /* Parse any available data, if our state permits. */ + parseInput(conn); + + /* PQgetResult will return immediately in all states except BUSY. */ + return conn->asyncStatus == PGASYNC_BUSY; +} + + +/* + * PQgetResult + * Get the next PGresult produced by a query. Returns NULL if no + * query work remains or an error has occurred (e.g. out of + * memory). + */ + +PGresult * +PQgetResult(PGconn *conn) +{ + PGresult *res; + + if (!conn) + return NULL; + + /* Parse any available data, if our state permits. */ + parseInput(conn); + + /* If not ready to return something, block until we are. */ + while (conn->asyncStatus == PGASYNC_BUSY) + { + int flushResult; + + /* + * If data remains unsent, send it. Else we might be waiting for the + * result of a command the backend hasn't even got yet. + */ + while ((flushResult = pqFlush(conn)) > 0) + { + if (pqWait(FALSE, TRUE, conn)) + { + flushResult = -1; + break; + } + } + + /* Wait for some more data, and load it. */ + if (flushResult || + pqWait(TRUE, FALSE, conn) || + pqReadData(conn) < 0) + { + /* + * conn->errorMessage has been set by pqWait or pqReadData. We + * want to append it to any already-received error message. + */ + pqSaveErrorResult(conn); + conn->asyncStatus = PGASYNC_IDLE; + return pqPrepareAsyncResult(conn); + } + + /* Parse it. */ + parseInput(conn); + } + + /* Return the appropriate thing. */ + switch (conn->asyncStatus) + { + case PGASYNC_IDLE: + res = NULL; /* query is complete */ + break; + case PGASYNC_READY: + res = pqPrepareAsyncResult(conn); + /* Set the state back to BUSY, allowing parsing to proceed. */ + conn->asyncStatus = PGASYNC_BUSY; + break; + case PGASYNC_COPY_IN: + res = getCopyResult(conn, PGRES_COPY_IN); + break; + case PGASYNC_COPY_OUT: + res = getCopyResult(conn, PGRES_COPY_OUT); + break; + case PGASYNC_COPY_BOTH: + res = getCopyResult(conn, PGRES_COPY_BOTH); + break; + default: + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("unexpected asyncStatus: %d\n"), + (int) conn->asyncStatus); + res = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR); + break; + } + + if (res) + { + int i; + + for (i = 0; i < res->nEvents; i++) + { + PGEventResultCreate evt; + + evt.conn = conn; + evt.result = res; + if (!res->events[i].proc(PGEVT_RESULTCREATE, &evt, + res->events[i].passThrough)) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("PGEventProc \"%s\" failed during PGEVT_RESULTCREATE event\n"), + res->events[i].name); + pqSetResultError(res, conn->errorMessage.data); + res->resultStatus = PGRES_FATAL_ERROR; + break; + } + res->events[i].resultInitialized = TRUE; + } + } + + return res; +} + +/* + * getCopyResult + * Helper for PQgetResult: generate result for COPY-in-progress cases + */ +static PGresult * +getCopyResult(PGconn *conn, ExecStatusType copytype) +{ + /* + * If the server connection has been lost, don't pretend everything is + * hunky-dory; instead return a PGRES_FATAL_ERROR result, and reset the + * asyncStatus to idle (corresponding to what we'd do if we'd detected I/O + * error in the earlier steps in PQgetResult). The text returned in the + * result is whatever is in conn->errorMessage; we hope that was filled + * with something relevant when the lost connection was detected. + */ + if (conn->status != CONNECTION_OK) + { + pqSaveErrorResult(conn); + conn->asyncStatus = PGASYNC_IDLE; + return pqPrepareAsyncResult(conn); + } + + /* If we have an async result for the COPY, return that */ + if (conn->result && conn->result->resultStatus == copytype) + return pqPrepareAsyncResult(conn); + + /* Otherwise, invent a suitable PGresult */ + return PQmakeEmptyPGresult(conn, copytype); +} + + +/* + * PQexec + * send a query to the backend and package up the result in a PGresult + * + * If the query was not even sent, return NULL; conn->errorMessage is set to + * a relevant message. + * If the query was sent, a new PGresult is returned (which could indicate + * either success or failure). + * The user is responsible for freeing the PGresult via PQclear() + * when done with it. + */ +PGresult * +PQexec(PGconn *conn, const char *query) +{ + if (!PQexecStart(conn)) + return NULL; + if (!PQsendQuery(conn, query)) + return NULL; + return PQexecFinish(conn); +} + +/* + * PQexecParams + * Like PQexec, but use protocol 3.0 so we can pass parameters + */ +PGresult * +PQexecParams(PGconn *conn, + const char *command, + int nParams, + const Oid *paramTypes, + const char *const * paramValues, + const int *paramLengths, + const int *paramFormats, + int resultFormat) +{ + if (!PQexecStart(conn)) + return NULL; + if (!PQsendQueryParams(conn, command, + nParams, paramTypes, paramValues, paramLengths, + paramFormats, resultFormat)) + return NULL; + return PQexecFinish(conn); +} + +/* + * PQprepare + * Creates a prepared statement by issuing a v3.0 parse message. + * + * If the query was not even sent, return NULL; conn->errorMessage is set to + * a relevant message. + * If the query was sent, a new PGresult is returned (which could indicate + * either success or failure). + * The user is responsible for freeing the PGresult via PQclear() + * when done with it. + */ +PGresult * +PQprepare(PGconn *conn, + const char *stmtName, const char *query, + int nParams, const Oid *paramTypes) +{ + if (!PQexecStart(conn)) + return NULL; + if (!PQsendPrepare(conn, stmtName, query, nParams, paramTypes)) + return NULL; + return PQexecFinish(conn); +} + +/* + * PQexecPrepared + * Like PQexec, but execute a previously prepared statement, + * using protocol 3.0 so we can pass parameters + */ +PGresult * +PQexecPrepared(PGconn *conn, + const char *stmtName, + int nParams, + const char *const * paramValues, + const int *paramLengths, + const int *paramFormats, + int resultFormat) +{ + if (!PQexecStart(conn)) + return NULL; + if (!PQsendQueryPrepared(conn, stmtName, + nParams, paramValues, paramLengths, + paramFormats, resultFormat)) + return NULL; + return PQexecFinish(conn); +} + +/* + * Common code for PQexec and sibling routines: prepare to send command + */ +static bool +PQexecStart(PGconn *conn) +{ + PGresult *result; + + if (!conn) + return false; + + /* + * Silently discard any prior query result that application didn't eat. + * This is probably poor design, but it's here for backward compatibility. + */ + while ((result = PQgetResult(conn)) != NULL) + { + ExecStatusType resultStatus = result->resultStatus; + + PQclear(result); /* only need its status */ + if (resultStatus == PGRES_COPY_IN) + { + if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3) + { + /* In protocol 3, we can get out of a COPY IN state */ + if (PQputCopyEnd(conn, + libpq_gettext("COPY terminated by new PQexec")) < 0) + return false; + /* keep waiting to swallow the copy's failure message */ + } + else + { + /* In older protocols we have to punt */ + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("COPY IN state must be terminated first\n")); + return false; + } + } + else if (resultStatus == PGRES_COPY_OUT) + { + if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3) + { + /* + * In protocol 3, we can get out of a COPY OUT state: we just + * switch back to BUSY and allow the remaining COPY data to be + * dropped on the floor. + */ + conn->asyncStatus = PGASYNC_BUSY; + /* keep waiting to swallow the copy's completion message */ + } + else + { + /* In older protocols we have to punt */ + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("COPY OUT state must be terminated first\n")); + return false; + } + } + else if (resultStatus == PGRES_COPY_BOTH) + { + /* We don't allow PQexec during COPY BOTH */ + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("PQexec not allowed during COPY BOTH\n")); + return false; + } + /* check for loss of connection, too */ + if (conn->status == CONNECTION_BAD) + return false; + } + + /* OK to send a command */ + return true; +} + +/* + * Common code for PQexec and sibling routines: wait for command result + */ +static PGresult * +PQexecFinish(PGconn *conn) +{ + PGresult *result; + PGresult *lastResult; + + /* + * For backwards compatibility, return the last result if there are more + * than one --- but merge error messages if we get more than one error + * result. + * + * We have to stop if we see copy in/out/both, however. We will resume + * parsing after application performs the data transfer. + * + * Also stop if the connection is lost (else we'll loop infinitely). + */ + lastResult = NULL; + while ((result = PQgetResult(conn)) != NULL) + { + if (lastResult) + { + if (lastResult->resultStatus == PGRES_FATAL_ERROR && + result->resultStatus == PGRES_FATAL_ERROR) + { + pqCatenateResultError(lastResult, result->errMsg); + PQclear(result); + result = lastResult; + + /* + * Make sure PQerrorMessage agrees with concatenated result + */ + resetPQExpBuffer(&conn->errorMessage); + appendPQExpBufferStr(&conn->errorMessage, result->errMsg); + } + else + PQclear(lastResult); + } + lastResult = result; + if (result->resultStatus == PGRES_COPY_IN || + result->resultStatus == PGRES_COPY_OUT || + result->resultStatus == PGRES_COPY_BOTH || + conn->status == CONNECTION_BAD) + break; + } + + return lastResult; +} + +/* + * PQdescribePrepared + * Obtain information about a previously prepared statement + * + * If the query was not even sent, return NULL; conn->errorMessage is set to + * a relevant message. + * If the query was sent, a new PGresult is returned (which could indicate + * either success or failure). On success, the PGresult contains status + * PGRES_COMMAND_OK, and its parameter and column-heading fields describe + * the statement's inputs and outputs respectively. + * The user is responsible for freeing the PGresult via PQclear() + * when done with it. + */ +PGresult * +PQdescribePrepared(PGconn *conn, const char *stmt) +{ + if (!PQexecStart(conn)) + return NULL; + if (!PQsendDescribe(conn, 'S', stmt)) + return NULL; + return PQexecFinish(conn); +} + +/* + * PQdescribePortal + * Obtain information about a previously created portal + * + * This is much like PQdescribePrepared, except that no parameter info is + * returned. Note that at the moment, libpq doesn't really expose portals + * to the client; but this can be used with a portal created by a SQL + * DECLARE CURSOR command. + */ +PGresult * +PQdescribePortal(PGconn *conn, const char *portal) +{ + if (!PQexecStart(conn)) + return NULL; + if (!PQsendDescribe(conn, 'P', portal)) + return NULL; + return PQexecFinish(conn); +} + +/* + * PQsendDescribePrepared + * Submit a Describe Statement command, but don't wait for it to finish + * + * Returns: 1 if successfully submitted + * 0 if error (conn->errorMessage is set) + */ +int +PQsendDescribePrepared(PGconn *conn, const char *stmt) +{ + return PQsendDescribe(conn, 'S', stmt); +} + +/* + * PQsendDescribePortal + * Submit a Describe Portal command, but don't wait for it to finish + * + * Returns: 1 if successfully submitted + * 0 if error (conn->errorMessage is set) + */ +int +PQsendDescribePortal(PGconn *conn, const char *portal) +{ + return PQsendDescribe(conn, 'P', portal); +} + +/* + * PQsendDescribe + * Common code to send a Describe command + * + * Available options for desc_type are + * 'S' to describe a prepared statement; or + * 'P' to describe a portal. + * Returns 1 on success and 0 on failure. + */ +static int +PQsendDescribe(PGconn *conn, char desc_type, const char *desc_target) +{ + /* Treat null desc_target as empty string */ + if (!desc_target) + desc_target = ""; + + if (!PQsendQueryStart(conn)) + return 0; + + /* This isn't gonna work on a 2.0 server */ + if (PG_PROTOCOL_MAJOR(conn->pversion) < 3) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("function requires at least protocol version 3.0\n")); + return 0; + } + + /* construct the Describe message */ + if (pqPutMsgStart('D', false, conn) < 0 || + pqPutc(desc_type, conn) < 0 || + pqPuts(desc_target, conn) < 0 || + pqPutMsgEnd(conn) < 0) + goto sendFailed; + + /* construct the Sync message */ + if (pqPutMsgStart('S', false, conn) < 0 || + pqPutMsgEnd(conn) < 0) + goto sendFailed; + + /* remember we are doing a Describe */ + conn->queryclass = PGQUERY_DESCRIBE; + + /* reset last-query string (not relevant now) */ + if (conn->last_query) + { + free(conn->last_query); + conn->last_query = NULL; + } + + /* + * Give the data a push. In nonblock mode, don't complain if we're unable + * to send it all; PQgetResult() will do any additional flushing needed. + */ + if (pqFlush(conn) < 0) + goto sendFailed; + + /* OK, it's launched! */ + conn->asyncStatus = PGASYNC_BUSY; + return 1; + +sendFailed: + pqHandleSendFailure(conn); + return 0; +} + +/* + * PQnotifies + * returns a PGnotify* structure of the latest async notification + * that has not yet been handled + * + * returns NULL, if there is currently + * no unhandled async notification from the backend + * + * the CALLER is responsible for FREE'ing the structure returned + */ +PGnotify * +PQnotifies(PGconn *conn) +{ + PGnotify *event; + + if (!conn) + return NULL; + + /* Parse any available data to see if we can extract NOTIFY messages. */ + parseInput(conn); + + event = conn->notifyHead; + if (event) + { + conn->notifyHead = event->next; + if (!conn->notifyHead) + conn->notifyTail = NULL; + event->next = NULL; /* don't let app see the internal state */ + } + return event; +} + +/* + * PQputCopyData - send some data to the backend during COPY IN or COPY BOTH + * + * Returns 1 if successful, 0 if data could not be sent (only possible + * in nonblock mode), or -1 if an error occurs. + */ +int +PQputCopyData(PGconn *conn, const char *buffer, int nbytes) +{ + if (!conn) + return -1; + if (conn->asyncStatus != PGASYNC_COPY_IN && + conn->asyncStatus != PGASYNC_COPY_BOTH) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("no COPY in progress\n")); + return -1; + } + + /* + * Process any NOTICE or NOTIFY messages that might be pending in the + * input buffer. Since the server might generate many notices during the + * COPY, we want to clean those out reasonably promptly to prevent + * indefinite expansion of the input buffer. (Note: the actual read of + * input data into the input buffer happens down inside pqSendSome, but + * it's not authorized to get rid of the data again.) + */ + parseInput(conn); + + if (nbytes > 0) + { + /* + * Try to flush any previously sent data in preference to growing the + * output buffer. If we can't enlarge the buffer enough to hold the + * data, return 0 in the nonblock case, else hard error. (For + * simplicity, always assume 5 bytes of overhead even in protocol 2.0 + * case.) + */ + if ((conn->outBufSize - conn->outCount - 5) < nbytes) + { + if (pqFlush(conn) < 0) + return -1; + if (pqCheckOutBufferSpace(conn->outCount + 5 + (size_t) nbytes, + conn)) + return pqIsnonblocking(conn) ? 0 : -1; + } + /* Send the data (too simple to delegate to fe-protocol files) */ + if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3) + { + if (pqPutMsgStart('d', false, conn) < 0 || + pqPutnchar(buffer, nbytes, conn) < 0 || + pqPutMsgEnd(conn) < 0) + return -1; + } + else + { + if (pqPutMsgStart(0, false, conn) < 0 || + pqPutnchar(buffer, nbytes, conn) < 0 || + pqPutMsgEnd(conn) < 0) + return -1; + } + } + return 1; +} + +/* + * PQputCopyEnd - send EOF indication to the backend during COPY IN + * + * After calling this, use PQgetResult() to check command completion status. + * + * Returns 1 if successful, 0 if data could not be sent (only possible + * in nonblock mode), or -1 if an error occurs. + */ +int +PQputCopyEnd(PGconn *conn, const char *errormsg) +{ + if (!conn) + return -1; + if (conn->asyncStatus != PGASYNC_COPY_IN && + conn->asyncStatus != PGASYNC_COPY_BOTH) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("no COPY in progress\n")); + return -1; + } + + /* + * Send the COPY END indicator. This is simple enough that we don't + * bother delegating it to the fe-protocol files. + */ + if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3) + { + if (errormsg) + { + /* Send COPY FAIL */ + if (pqPutMsgStart('f', false, conn) < 0 || + pqPuts(errormsg, conn) < 0 || + pqPutMsgEnd(conn) < 0) + return -1; + } + else + { + /* Send COPY DONE */ + if (pqPutMsgStart('c', false, conn) < 0 || + pqPutMsgEnd(conn) < 0) + return -1; + } + + /* + * If we sent the COPY command in extended-query mode, we must issue a + * Sync as well. + */ + if (conn->queryclass != PGQUERY_SIMPLE) + { + if (pqPutMsgStart('S', false, conn) < 0 || + pqPutMsgEnd(conn) < 0) + return -1; + } + } + else + { + if (errormsg) + { + /* Ooops, no way to do this in 2.0 */ + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("function requires at least protocol version 3.0\n")); + return -1; + } + else + { + /* Send old-style end-of-data marker */ + if (pqPutMsgStart(0, false, conn) < 0 || + pqPutnchar("\\.\n", 3, conn) < 0 || + pqPutMsgEnd(conn) < 0) + return -1; + } + } + + /* Return to active duty */ + if (conn->asyncStatus == PGASYNC_COPY_BOTH) + conn->asyncStatus = PGASYNC_COPY_OUT; + else + conn->asyncStatus = PGASYNC_BUSY; + resetPQExpBuffer(&conn->errorMessage); + + /* Try to flush data */ + if (pqFlush(conn) < 0) + return -1; + + return 1; +} + +/* + * PQgetCopyData - read a row of data from the backend during COPY OUT + * or COPY BOTH + * + * If successful, sets *buffer to point to a malloc'd row of data, and + * returns row length (always > 0) as result. + * Returns 0 if no row available yet (only possible if async is true), + * -1 if end of copy (consult PQgetResult), or -2 if error (consult + * PQerrorMessage). + */ +int +PQgetCopyData(PGconn *conn, char **buffer, int async) +{ + *buffer = NULL; /* for all failure cases */ + if (!conn) + return -2; + if (conn->asyncStatus != PGASYNC_COPY_OUT && + conn->asyncStatus != PGASYNC_COPY_BOTH) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("no COPY in progress\n")); + return -2; + } + if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3) + return pqGetCopyData3(conn, buffer, async); + else + return pqGetCopyData2(conn, buffer, async); +} + +/* + * PQgetline - gets a newline-terminated string from the backend. + * + * Chiefly here so that applications can use "COPY to stdout" + * and read the output string. Returns a null-terminated string in s. + * + * XXX this routine is now deprecated, because it can't handle binary data. + * If called during a COPY BINARY we return EOF. + * + * PQgetline reads up to maxlen-1 characters (like fgets(3)) but strips + * the terminating \n (like gets(3)). + * + * CAUTION: the caller is responsible for detecting the end-of-copy signal + * (a line containing just "\.") when using this routine. + * + * RETURNS: + * EOF if error (eg, invalid arguments are given) + * 0 if EOL is reached (i.e., \n has been read) + * (this is required for backward-compatibility -- this + * routine used to always return EOF or 0, assuming that + * the line ended within maxlen bytes.) + * 1 in other cases (i.e., the buffer was filled before \n is reached) + */ +int +PQgetline(PGconn *conn, char *s, int maxlen) +{ + if (!s || maxlen <= 0) + return EOF; + *s = '\0'; + /* maxlen must be at least 3 to hold the \. terminator! */ + if (maxlen < 3) + return EOF; + + if (!conn) + return EOF; + + if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3) + return pqGetline3(conn, s, maxlen); + else + return pqGetline2(conn, s, maxlen); +} + +/* + * PQgetlineAsync - gets a COPY data row without blocking. + * + * This routine is for applications that want to do "COPY to stdout" + * asynchronously, that is without blocking. Having issued the COPY command + * and gotten a PGRES_COPY_OUT response, the app should call PQconsumeInput + * and this routine until the end-of-data signal is detected. Unlike + * PQgetline, this routine takes responsibility for detecting end-of-data. + * + * On each call, PQgetlineAsync will return data if a complete data row + * is available in libpq's input buffer. Otherwise, no data is returned + * until the rest of the row arrives. + * + * If -1 is returned, the end-of-data signal has been recognized (and removed + * from libpq's input buffer). The caller *must* next call PQendcopy and + * then return to normal processing. + * + * RETURNS: + * -1 if the end-of-copy-data marker has been recognized + * 0 if no data is available + * >0 the number of bytes returned. + * + * The data returned will not extend beyond a data-row boundary. If possible + * a whole row will be returned at one time. But if the buffer offered by + * the caller is too small to hold a row sent by the backend, then a partial + * data row will be returned. In text mode this can be detected by testing + * whether the last returned byte is '\n' or not. + * + * The returned data is *not* null-terminated. + */ + +int +PQgetlineAsync(PGconn *conn, char *buffer, int bufsize) +{ + if (!conn) + return -1; + + if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3) + return pqGetlineAsync3(conn, buffer, bufsize); + else + return pqGetlineAsync2(conn, buffer, bufsize); +} + +/* + * PQputline -- sends a string to the backend during COPY IN. + * Returns 0 if OK, EOF if not. + * + * This is deprecated primarily because the return convention doesn't allow + * caller to tell the difference between a hard error and a nonblock-mode + * send failure. + */ +int +PQputline(PGconn *conn, const char *s) +{ + return PQputnbytes(conn, s, strlen(s)); +} + +/* + * PQputnbytes -- like PQputline, but buffer need not be null-terminated. + * Returns 0 if OK, EOF if not. + */ +int +PQputnbytes(PGconn *conn, const char *buffer, int nbytes) +{ + if (PQputCopyData(conn, buffer, nbytes) > 0) + return 0; + else + return EOF; +} + +/* + * PQendcopy + * After completing the data transfer portion of a copy in/out, + * the application must call this routine to finish the command protocol. + * + * When using protocol 3.0 this is deprecated; it's cleaner to use PQgetResult + * to get the transfer status. Note however that when using 2.0 protocol, + * recovering from a copy failure often requires a PQreset. PQendcopy will + * take care of that, PQgetResult won't. + * + * RETURNS: + * 0 on success + * 1 on failure + */ +int +PQendcopy(PGconn *conn) +{ + if (!conn) + return 0; + + if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3) + return pqEndcopy3(conn); + else + return pqEndcopy2(conn); +} + + +/* ---------------- + * PQfn - Send a function call to the POSTGRES backend. + * + * conn : backend connection + * fnid : OID of function to be called + * result_buf : pointer to result buffer + * result_len : actual length of result is returned here + * result_is_int : If the result is an integer, this must be 1, + * otherwise this should be 0 + * args : pointer to an array of function arguments + * (each has length, if integer, and value/pointer) + * nargs : # of arguments in args array. + * + * RETURNS + * PGresult with status = PGRES_COMMAND_OK if successful. + * *result_len is > 0 if there is a return value, 0 if not. + * PGresult with status = PGRES_FATAL_ERROR if backend returns an error. + * NULL on communications failure. conn->errorMessage will be set. + * ---------------- + */ + +PGresult * +PQfn(PGconn *conn, + int fnid, + int *result_buf, + int *result_len, + int result_is_int, + const PQArgBlock *args, + int nargs) +{ + *result_len = 0; + + if (!conn) + return NULL; + + /* clear the error string */ + resetPQExpBuffer(&conn->errorMessage); + + if (conn->sock == PGINVALID_SOCKET || conn->asyncStatus != PGASYNC_IDLE || + conn->result != NULL) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("connection in wrong state\n")); + return NULL; + } + + if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3) + return pqFunctionCall3(conn, fnid, + result_buf, result_len, + result_is_int, + args, nargs); + else + return pqFunctionCall2(conn, fnid, + result_buf, result_len, + result_is_int, + args, nargs); +} + + +/* ====== accessor funcs for PGresult ======== */ + +ExecStatusType +PQresultStatus(const PGresult *res) +{ + if (!res) + return PGRES_FATAL_ERROR; + return res->resultStatus; +} + +char * +PQresStatus(ExecStatusType status) +{ + if ((unsigned int) status >= sizeof pgresStatus / sizeof pgresStatus[0]) + return libpq_gettext("invalid ExecStatusType code"); + return pgresStatus[status]; +} + +char * +PQresultErrorMessage(const PGresult *res) +{ + if (!res || !res->errMsg) + return ""; + return res->errMsg; +} + +char * +PQresultVerboseErrorMessage(const PGresult *res, + PGVerbosity verbosity, + PGContextVisibility show_context) +{ + PQExpBufferData workBuf; + + /* + * Because the caller is expected to free the result string, we must + * strdup any constant result. We use plain strdup and document that + * callers should expect NULL if out-of-memory. + */ + if (!res || + (res->resultStatus != PGRES_FATAL_ERROR && + res->resultStatus != PGRES_NONFATAL_ERROR)) + return strdup(libpq_gettext("PGresult is not an error result\n")); + + initPQExpBuffer(&workBuf); + + /* + * Currently, we pass this off to fe-protocol3.c in all cases; it will + * behave reasonably sanely with an error reported by fe-protocol2.c as + * well. If necessary, we could record the protocol version in PGresults + * so as to be able to invoke a version-specific message formatter, but + * for now there's no need. + */ + pqBuildErrorMessage3(&workBuf, res, verbosity, show_context); + + /* If insufficient memory to format the message, fail cleanly */ + if (PQExpBufferDataBroken(workBuf)) + { + termPQExpBuffer(&workBuf); + return strdup(libpq_gettext("out of memory\n")); + } + + return workBuf.data; +} + +char * +PQresultErrorField(const PGresult *res, int fieldcode) +{ + PGMessageField *pfield; + + if (!res) + return NULL; + for (pfield = res->errFields; pfield != NULL; pfield = pfield->next) + { + if (pfield->code == fieldcode) + return pfield->contents; + } + return NULL; +} + +int +PQntuples(const PGresult *res) +{ + if (!res) + return 0; + return res->ntups; +} + +int +PQnfields(const PGresult *res) +{ + if (!res) + return 0; + return res->numAttributes; +} + +int +PQbinaryTuples(const PGresult *res) +{ + if (!res) + return 0; + return res->binary; +} + +/* + * Helper routines to range-check field numbers and tuple numbers. + * Return TRUE if OK, FALSE if not + */ + +static int +check_field_number(const PGresult *res, int field_num) +{ + if (!res) + return FALSE; /* no way to display error message... */ + if (field_num < 0 || field_num >= res->numAttributes) + { + pqInternalNotice(&res->noticeHooks, + "column number %d is out of range 0..%d", + field_num, res->numAttributes - 1); + return FALSE; + } + return TRUE; +} + +static int +check_tuple_field_number(const PGresult *res, + int tup_num, int field_num) +{ + if (!res) + return FALSE; /* no way to display error message... */ + if (tup_num < 0 || tup_num >= res->ntups) + { + pqInternalNotice(&res->noticeHooks, + "row number %d is out of range 0..%d", + tup_num, res->ntups - 1); + return FALSE; + } + if (field_num < 0 || field_num >= res->numAttributes) + { + pqInternalNotice(&res->noticeHooks, + "column number %d is out of range 0..%d", + field_num, res->numAttributes - 1); + return FALSE; + } + return TRUE; +} + +static int +check_param_number(const PGresult *res, int param_num) +{ + if (!res) + return FALSE; /* no way to display error message... */ + if (param_num < 0 || param_num >= res->numParameters) + { + pqInternalNotice(&res->noticeHooks, + "parameter number %d is out of range 0..%d", + param_num, res->numParameters - 1); + return FALSE; + } + + return TRUE; +} + +/* + * returns NULL if the field_num is invalid + */ +char * +PQfname(const PGresult *res, int field_num) +{ + if (!check_field_number(res, field_num)) + return NULL; + if (res->attDescs) + return res->attDescs[field_num].name; + else + return NULL; +} + +/* + * PQfnumber: find column number given column name + * + * The column name is parsed as if it were in a SQL statement, including + * case-folding and double-quote processing. But note a possible gotcha: + * downcasing in the frontend might follow different locale rules than + * downcasing in the backend... + * + * Returns -1 if no match. In the present backend it is also possible + * to have multiple matches, in which case the first one is found. + */ +int +PQfnumber(const PGresult *res, const char *field_name) +{ + char *field_case; + bool in_quotes; + bool all_lower = true; + const char *iptr; + char *optr; + int i; + + if (!res) + return -1; + + /* + * Note: it is correct to reject a zero-length input string; the proper + * input to match a zero-length field name would be "". + */ + if (field_name == NULL || + field_name[0] == '\0' || + res->attDescs == NULL) + return -1; + + /* + * Check if we can avoid the strdup() and related work because the + * passed-in string wouldn't be changed before we do the check anyway. + */ + for (iptr = field_name; *iptr; iptr++) + { + char c = *iptr; + + if (c == '"' || c != pg_tolower((unsigned char) c)) + { + all_lower = false; + break; + } + } + + if (all_lower) + for (i = 0; i < res->numAttributes; i++) + if (strcmp(field_name, res->attDescs[i].name) == 0) + return i; + + /* Fall through to the normal check if that didn't work out. */ + + /* + * Note: this code will not reject partially quoted strings, eg + * foo"BAR"foo will become fooBARfoo when it probably ought to be an error + * condition. + */ + field_case = strdup(field_name); + if (field_case == NULL) + return -1; /* grotty */ + + in_quotes = false; + optr = field_case; + for (iptr = field_case; *iptr; iptr++) + { + char c = *iptr; + + if (in_quotes) + { + if (c == '"') + { + if (iptr[1] == '"') + { + /* doubled quotes become a single quote */ + *optr++ = '"'; + iptr++; + } + else + in_quotes = false; + } + else + *optr++ = c; + } + else if (c == '"') + in_quotes = true; + else + { + c = pg_tolower((unsigned char) c); + *optr++ = c; + } + } + *optr = '\0'; + + for (i = 0; i < res->numAttributes; i++) + { + if (strcmp(field_case, res->attDescs[i].name) == 0) + { + free(field_case); + return i; + } + } + free(field_case); + return -1; +} + +Oid +PQftable(const PGresult *res, int field_num) +{ + if (!check_field_number(res, field_num)) + return InvalidOid; + if (res->attDescs) + return res->attDescs[field_num].tableid; + else + return InvalidOid; +} + +int +PQftablecol(const PGresult *res, int field_num) +{ + if (!check_field_number(res, field_num)) + return 0; + if (res->attDescs) + return res->attDescs[field_num].columnid; + else + return 0; +} + +int +PQfformat(const PGresult *res, int field_num) +{ + if (!check_field_number(res, field_num)) + return 0; + if (res->attDescs) + return res->attDescs[field_num].format; + else + return 0; +} + +Oid +PQftype(const PGresult *res, int field_num) +{ + if (!check_field_number(res, field_num)) + return InvalidOid; + if (res->attDescs) + return res->attDescs[field_num].typid; + else + return InvalidOid; +} + +int +PQfsize(const PGresult *res, int field_num) +{ + if (!check_field_number(res, field_num)) + return 0; + if (res->attDescs) + return res->attDescs[field_num].typlen; + else + return 0; +} + +int +PQfmod(const PGresult *res, int field_num) +{ + if (!check_field_number(res, field_num)) + return 0; + if (res->attDescs) + return res->attDescs[field_num].atttypmod; + else + return 0; +} + +char * +PQcmdStatus(PGresult *res) +{ + if (!res) + return NULL; + return res->cmdStatus; +} + +/* + * PQoidStatus - + * if the last command was an INSERT, return the oid string + * if not, return "" + */ +char * +PQoidStatus(const PGresult *res) +{ + /* + * This must be enough to hold the result. Don't laugh, this is better + * than what this function used to do. + */ + static char buf[24]; + + size_t len; + + if (!res || strncmp(res->cmdStatus, "INSERT ", 7) != 0) + return ""; + + len = strspn(res->cmdStatus + 7, "0123456789"); + if (len > sizeof(buf) - 1) + len = sizeof(buf) - 1; + memcpy(buf, res->cmdStatus + 7, len); + buf[len] = '\0'; + + return buf; +} + +/* + * PQoidValue - + * a perhaps preferable form of the above which just returns + * an Oid type + */ +Oid +PQoidValue(const PGresult *res) +{ + char *endptr = NULL; + unsigned long result; + + if (!res || + strncmp(res->cmdStatus, "INSERT ", 7) != 0 || + res->cmdStatus[7] < '0' || + res->cmdStatus[7] > '9') + return InvalidOid; + + result = strtoul(res->cmdStatus + 7, &endptr, 10); + + if (!endptr || (*endptr != ' ' && *endptr != '\0')) + return InvalidOid; + else + return (Oid) result; +} + + +/* + * PQcmdTuples - + * If the last command was INSERT/UPDATE/DELETE/MOVE/FETCH/COPY, return + * a string containing the number of inserted/affected tuples. If not, + * return "". + * + * XXX: this should probably return an int + */ +char * +PQcmdTuples(PGresult *res) +{ + char *p, + *c; + + if (!res) + return ""; + + if (strncmp(res->cmdStatus, "INSERT ", 7) == 0) + { + p = res->cmdStatus + 7; + /* INSERT: skip oid and space */ + while (*p && *p != ' ') + p++; + if (*p == 0) + goto interpret_error; /* no space? */ + p++; + } + else if (strncmp(res->cmdStatus, "SELECT ", 7) == 0 || + strncmp(res->cmdStatus, "DELETE ", 7) == 0 || + strncmp(res->cmdStatus, "UPDATE ", 7) == 0) + p = res->cmdStatus + 7; + else if (strncmp(res->cmdStatus, "FETCH ", 6) == 0) + p = res->cmdStatus + 6; + else if (strncmp(res->cmdStatus, "MOVE ", 5) == 0 || + strncmp(res->cmdStatus, "COPY ", 5) == 0) + p = res->cmdStatus + 5; + else + return ""; + + /* check that we have an integer (at least one digit, nothing else) */ + for (c = p; *c; c++) + { + if (!isdigit((unsigned char) *c)) + goto interpret_error; + } + if (c == p) + goto interpret_error; + + return p; + +interpret_error: + pqInternalNotice(&res->noticeHooks, + "could not interpret result from server: %s", + res->cmdStatus); + return ""; +} + +/* + * PQgetvalue: + * return the value of field 'field_num' of row 'tup_num' + */ +char * +PQgetvalue(const PGresult *res, int tup_num, int field_num) +{ + if (!check_tuple_field_number(res, tup_num, field_num)) + return NULL; + return res->tuples[tup_num][field_num].value; +} + +/* PQgetlength: + * returns the actual length of a field value in bytes. + */ +int +PQgetlength(const PGresult *res, int tup_num, int field_num) +{ + if (!check_tuple_field_number(res, tup_num, field_num)) + return 0; + if (res->tuples[tup_num][field_num].len != NULL_LEN) + return res->tuples[tup_num][field_num].len; + else + return 0; +} + +/* PQgetisnull: + * returns the null status of a field value. + */ +int +PQgetisnull(const PGresult *res, int tup_num, int field_num) +{ + if (!check_tuple_field_number(res, tup_num, field_num)) + return 1; /* pretend it is null */ + if (res->tuples[tup_num][field_num].len == NULL_LEN) + return 1; + else + return 0; +} + +/* PQnparams: + * returns the number of input parameters of a prepared statement. + */ +int +PQnparams(const PGresult *res) +{ + if (!res) + return 0; + return res->numParameters; +} + +/* PQparamtype: + * returns type Oid of the specified statement parameter. + */ +Oid +PQparamtype(const PGresult *res, int param_num) +{ + if (!check_param_number(res, param_num)) + return InvalidOid; + if (res->paramDescs) + return res->paramDescs[param_num].typid; + else + return InvalidOid; +} + + +/* PQsetnonblocking: + * sets the PGconn's database connection non-blocking if the arg is TRUE + * or makes it blocking if the arg is FALSE, this will not protect + * you from PQexec(), you'll only be safe when using the non-blocking API. + * Needs to be called only on a connected database connection. + */ +int +PQsetnonblocking(PGconn *conn, int arg) +{ + bool barg; + + if (!conn || conn->status == CONNECTION_BAD) + return -1; + + barg = (arg ? TRUE : FALSE); + + /* early out if the socket is already in the state requested */ + if (barg == conn->nonblocking) + return 0; + + /* + * to guarantee constancy for flushing/query/result-polling behavior we + * need to flush the send queue at this point in order to guarantee proper + * behavior. this is ok because either they are making a transition _from_ + * or _to_ blocking mode, either way we can block them. + */ + /* if we are going from blocking to non-blocking flush here */ + if (pqFlush(conn)) + return -1; + + conn->nonblocking = barg; + + return 0; +} + +/* + * return the blocking status of the database connection + * TRUE == nonblocking, FALSE == blocking + */ +int +PQisnonblocking(const PGconn *conn) +{ + return pqIsnonblocking(conn); +} + +/* libpq is thread-safe? */ +int +PQisthreadsafe(void) +{ +#ifdef ENABLE_THREAD_SAFETY + return true; +#else + return false; +#endif +} + + +/* try to force data out, really only useful for non-blocking users */ +int +PQflush(PGconn *conn) +{ + return pqFlush(conn); +} + + +/* + * PQfreemem - safely frees memory allocated + * + * Needed mostly by Win32, unless multithreaded DLL (/MD in VC6) + * Used for freeing memory from PQescapeByte()a/PQunescapeBytea() + */ +void +PQfreemem(void *ptr) +{ + free(ptr); +} + +/* + * PQfreeNotify - free's the memory associated with a PGnotify + * + * This function is here only for binary backward compatibility. + * New code should use PQfreemem(). A macro will automatically map + * calls to PQfreemem. It should be removed in the future. bjm 2003-03-24 + */ + +#undef PQfreeNotify +void PQfreeNotify(PGnotify *notify); + +void +PQfreeNotify(PGnotify *notify) +{ + PQfreemem(notify); +} + + +/* + * Escaping arbitrary strings to get valid SQL literal strings. + * + * Replaces "'" with "''", and if not std_strings, replaces "\" with "\\". + * + * length is the length of the source string. (Note: if a terminating NUL + * is encountered sooner, PQescapeString stops short of "length"; the behavior + * is thus rather like strncpy.) + * + * For safety the buffer at "to" must be at least 2*length + 1 bytes long. + * A terminating NUL character is added to the output string, whether the + * input is NUL-terminated or not. + * + * Returns the actual length of the output (not counting the terminating NUL). + */ +static size_t +PQescapeStringInternal(PGconn *conn, + char *to, const char *from, size_t length, + int *error, + int encoding, bool std_strings) +{ + const char *source = from; + char *target = to; + size_t remaining = length; + + if (error) + *error = 0; + + while (remaining > 0 && *source != '\0') + { + char c = *source; + int len; + int i; + + /* Fast path for plain ASCII */ + if (!IS_HIGHBIT_SET(c)) + { + /* Apply quoting if needed */ + if (SQL_STR_DOUBLE(c, !std_strings)) + *target++ = c; + /* Copy the character */ + *target++ = c; + source++; + remaining--; + continue; + } + + /* Slow path for possible multibyte characters */ + len = pg_encoding_mblen(encoding, source); + + /* Copy the character */ + for (i = 0; i < len; i++) + { + if (remaining == 0 || *source == '\0') + break; + *target++ = *source++; + remaining--; + } + + /* + * If we hit premature end of string (ie, incomplete multibyte + * character), try to pad out to the correct length with spaces. We + * may not be able to pad completely, but we will always be able to + * insert at least one pad space (since we'd not have quoted a + * multibyte character). This should be enough to make a string that + * the server will error out on. + */ + if (i < len) + { + if (error) + *error = 1; + if (conn) + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("incomplete multibyte character\n")); + for (; i < len; i++) + { + if (((size_t) (target - to)) / 2 >= length) + break; + *target++ = ' '; + } + break; + } + } + + /* Write the terminating NUL character. */ + *target = '\0'; + + return target - to; +} + +size_t +PQescapeStringConn(PGconn *conn, + char *to, const char *from, size_t length, + int *error) +{ + if (!conn) + { + /* force empty-string result */ + *to = '\0'; + if (error) + *error = 1; + return 0; + } + return PQescapeStringInternal(conn, to, from, length, error, + conn->client_encoding, + conn->std_strings); +} + +size_t +PQescapeString(char *to, const char *from, size_t length) +{ + return PQescapeStringInternal(NULL, to, from, length, NULL, + static_client_encoding, + static_std_strings); +} + + +/* + * Escape arbitrary strings. If as_ident is true, we escape the result + * as an identifier; if false, as a literal. The result is returned in + * a newly allocated buffer. If we fail due to an encoding violation or out + * of memory condition, we return NULL, storing an error message into conn. + */ +static char * +PQescapeInternal(PGconn *conn, const char *str, size_t len, bool as_ident) +{ + const char *s; + char *result; + char *rp; + int num_quotes = 0; /* single or double, depending on as_ident */ + int num_backslashes = 0; + int input_len; + int result_size; + char quote_char = as_ident ? '"' : '\''; + + /* We must have a connection, else fail immediately. */ + if (!conn) + return NULL; + + /* Scan the string for characters that must be escaped. */ + for (s = str; (s - str) < len && *s != '\0'; ++s) + { + if (*s == quote_char) + ++num_quotes; + else if (*s == '\\') + ++num_backslashes; + else if (IS_HIGHBIT_SET(*s)) + { + int charlen; + + /* Slow path for possible multibyte characters */ + charlen = pg_encoding_mblen(conn->client_encoding, s); + + /* Multibyte character overruns allowable length. */ + if ((s - str) + charlen > len || memchr(s, 0, charlen) != NULL) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("incomplete multibyte character\n")); + return NULL; + } + + /* Adjust s, bearing in mind that for loop will increment it. */ + s += charlen - 1; + } + } + + /* Allocate output buffer. */ + input_len = s - str; + result_size = input_len + num_quotes + 3; /* two quotes, plus a NUL */ + if (!as_ident && num_backslashes > 0) + result_size += num_backslashes + 2; + result = rp = (char *) malloc(result_size); + if (rp == NULL) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("out of memory\n")); + return NULL; + } + + /* + * If we are escaping a literal that contains backslashes, we use the + * escape string syntax so that the result is correct under either value + * of standard_conforming_strings. We also emit a leading space in this + * case, to guard against the possibility that the result might be + * interpolated immediately following an identifier. + */ + if (!as_ident && num_backslashes > 0) + { + *rp++ = ' '; + *rp++ = 'E'; + } + + /* Opening quote. */ + *rp++ = quote_char; + + /* + * Use fast path if possible. + * + * We've already verified that the input string is well-formed in the + * current encoding. If it contains no quotes and, in the case of + * literal-escaping, no backslashes, then we can just copy it directly to + * the output buffer, adding the necessary quotes. + * + * If not, we must rescan the input and process each character + * individually. + */ + if (num_quotes == 0 && (num_backslashes == 0 || as_ident)) + { + memcpy(rp, str, input_len); + rp += input_len; + } + else + { + for (s = str; s - str < input_len; ++s) + { + if (*s == quote_char || (!as_ident && *s == '\\')) + { + *rp++ = *s; + *rp++ = *s; + } + else if (!IS_HIGHBIT_SET(*s)) + *rp++ = *s; + else + { + int i = pg_encoding_mblen(conn->client_encoding, s); + + while (1) + { + *rp++ = *s; + if (--i == 0) + break; + ++s; /* for loop will provide the final increment */ + } + } + } + } + + /* Closing quote and terminating NUL. */ + *rp++ = quote_char; + *rp = '\0'; + + return result; +} + +char * +PQescapeLiteral(PGconn *conn, const char *str, size_t len) +{ + return PQescapeInternal(conn, str, len, false); +} + +char * +PQescapeIdentifier(PGconn *conn, const char *str, size_t len) +{ + return PQescapeInternal(conn, str, len, true); +} + +/* HEX encoding support for bytea */ +static const char hextbl[] = "0123456789abcdef"; + +static const int8 hexlookup[128] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, + -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +}; + +static inline char +get_hex(char c) +{ + int res = -1; + + if (c > 0 && c < 127) + res = hexlookup[(unsigned char) c]; + + return (char) res; +} + + +/* + * PQescapeBytea - converts from binary string to the + * minimal encoding necessary to include the string in an SQL + * INSERT statement with a bytea type column as the target. + * + * We can use either hex or escape (traditional) encoding. + * In escape mode, the following transformations are applied: + * '\0' == ASCII 0 == \000 + * '\'' == ASCII 39 == '' + * '\\' == ASCII 92 == \\ + * anything < 0x20, or > 0x7e ---> \ooo + * (where ooo is an octal expression) + * + * If not std_strings, all backslashes sent to the output are doubled. + */ +static unsigned char * +PQescapeByteaInternal(PGconn *conn, + const unsigned char *from, size_t from_length, + size_t *to_length, bool std_strings, bool use_hex) +{ + const unsigned char *vp; + unsigned char *rp; + unsigned char *result; + size_t i; + size_t len; + size_t bslash_len = (std_strings ? 1 : 2); + + /* + * empty string has 1 char ('\0') + */ + len = 1; + + if (use_hex) + { + len += bslash_len + 1 + 2 * from_length; + } + else + { + vp = from; + for (i = from_length; i > 0; i--, vp++) + { + if (*vp < 0x20 || *vp > 0x7e) + len += bslash_len + 3; + else if (*vp == '\'') + len += 2; + else if (*vp == '\\') + len += bslash_len + bslash_len; + else + len++; + } + } + + *to_length = len; + rp = result = (unsigned char *) malloc(len); + if (rp == NULL) + { + if (conn) + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("out of memory\n")); + return NULL; + } + + if (use_hex) + { + if (!std_strings) + *rp++ = '\\'; + *rp++ = '\\'; + *rp++ = 'x'; + } + + vp = from; + for (i = from_length; i > 0; i--, vp++) + { + unsigned char c = *vp; + + if (use_hex) + { + *rp++ = hextbl[(c >> 4) & 0xF]; + *rp++ = hextbl[c & 0xF]; + } + else if (c < 0x20 || c > 0x7e) + { + if (!std_strings) + *rp++ = '\\'; + *rp++ = '\\'; + *rp++ = (c >> 6) + '0'; + *rp++ = ((c >> 3) & 07) + '0'; + *rp++ = (c & 07) + '0'; + } + else if (c == '\'') + { + *rp++ = '\''; + *rp++ = '\''; + } + else if (c == '\\') + { + if (!std_strings) + { + *rp++ = '\\'; + *rp++ = '\\'; + } + *rp++ = '\\'; + *rp++ = '\\'; + } + else + *rp++ = c; + } + *rp = '\0'; + + return result; +} + +unsigned char * +PQescapeByteaConn(PGconn *conn, + const unsigned char *from, size_t from_length, + size_t *to_length) +{ + if (!conn) + return NULL; + return PQescapeByteaInternal(conn, from, from_length, to_length, + conn->std_strings, + (conn->sversion >= 90000)); +} + +unsigned char * +PQescapeBytea(const unsigned char *from, size_t from_length, size_t *to_length) +{ + return PQescapeByteaInternal(NULL, from, from_length, to_length, + static_std_strings, + false /* can't use hex */ ); +} + + +#define ISFIRSTOCTDIGIT(CH) ((CH) >= '0' && (CH) <= '3') +#define ISOCTDIGIT(CH) ((CH) >= '0' && (CH) <= '7') +#define OCTVAL(CH) ((CH) - '0') + +/* + * PQunescapeBytea - converts the null terminated string representation + * of a bytea, strtext, into binary, filling a buffer. It returns a + * pointer to the buffer (or NULL on error), and the size of the + * buffer in retbuflen. The pointer may subsequently be used as an + * argument to the function PQfreemem. + * + * The following transformations are made: + * \\ == ASCII 92 == \ + * \ooo == a byte whose value = ooo (ooo is an octal number) + * \x == x (x is any character not matched by the above transformations) + */ +unsigned char * +PQunescapeBytea(const unsigned char *strtext, size_t *retbuflen) +{ + size_t strtextlen, + buflen; + unsigned char *buffer, + *tmpbuf; + size_t i, + j; + + if (strtext == NULL) + return NULL; + + strtextlen = strlen((const char *) strtext); + + if (strtext[0] == '\\' && strtext[1] == 'x') + { + const unsigned char *s; + unsigned char *p; + + buflen = (strtextlen - 2) / 2; + /* Avoid unportable malloc(0) */ + buffer = (unsigned char *) malloc(buflen > 0 ? buflen : 1); + if (buffer == NULL) + return NULL; + + s = strtext + 2; + p = buffer; + while (*s) + { + char v1, + v2; + + /* + * Bad input is silently ignored. Note that this includes + * whitespace between hex pairs, which is allowed by byteain. + */ + v1 = get_hex(*s++); + if (!*s || v1 == (char) -1) + continue; + v2 = get_hex(*s++); + if (v2 != (char) -1) + *p++ = (v1 << 4) | v2; + } + + buflen = p - buffer; + } + else + { + /* + * Length of input is max length of output, but add one to avoid + * unportable malloc(0) if input is zero-length. + */ + buffer = (unsigned char *) malloc(strtextlen + 1); + if (buffer == NULL) + return NULL; + + for (i = j = 0; i < strtextlen;) + { + switch (strtext[i]) + { + case '\\': + i++; + if (strtext[i] == '\\') + buffer[j++] = strtext[i++]; + else + { + if ((ISFIRSTOCTDIGIT(strtext[i])) && + (ISOCTDIGIT(strtext[i + 1])) && + (ISOCTDIGIT(strtext[i + 2]))) + { + int byte; + + byte = OCTVAL(strtext[i++]); + byte = (byte << 3) + OCTVAL(strtext[i++]); + byte = (byte << 3) + OCTVAL(strtext[i++]); + buffer[j++] = byte; + } + } + + /* + * Note: if we see '\' followed by something that isn't a + * recognized escape sequence, we loop around having done + * nothing except advance i. Therefore the something will + * be emitted as ordinary data on the next cycle. Corner + * case: '\' at end of string will just be discarded. + */ + break; + + default: + buffer[j++] = strtext[i++]; + break; + } + } + buflen = j; /* buflen is the length of the dequoted data */ + } + + /* Shrink the buffer to be no larger than necessary */ + /* +1 avoids unportable behavior when buflen==0 */ + tmpbuf = realloc(buffer, buflen + 1); + + /* It would only be a very brain-dead realloc that could fail, but... */ + if (!tmpbuf) + { + free(buffer); + return NULL; + } + + *retbuflen = buflen; + return tmpbuf; +} diff --git a/libpq/fe-lobj.c b/libpq/fe-lobj.c new file mode 100644 index 0000000..3b08768 --- /dev/null +++ b/libpq/fe-lobj.c @@ -0,0 +1,1103 @@ +/*------------------------------------------------------------------------- + * + * fe-lobj.c + * Front-end large object interface + * + * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/interfaces/libpq/fe-lobj.c + * + *------------------------------------------------------------------------- + */ + +#ifdef WIN32 +/* + * As unlink/rename are #define'd in port.h (via postgres_fe.h), io.h + * must be included first on MS C. Might as well do it for all WIN32's + * here. + */ +#include +#endif + +#include "postgres_fe.h" + +#ifdef WIN32 +#include "win32.h" +#else +#include +#endif + +#include +#include +#include +#include /* for ntohl/htonl */ +#include + +#include "libpq-fe.h" +#include "libpq-int.h" +#include "libpq/libpq-fs.h" /* must come after sys/stat.h */ + +#define LO_BUFSIZE 8192 + +static int lo_initialize(PGconn *conn); +static Oid lo_import_internal(PGconn *conn, const char *filename, Oid oid); +static pg_int64 lo_hton64(pg_int64 host64); +static pg_int64 lo_ntoh64(pg_int64 net64); + +/* + * lo_open + * opens an existing large object + * + * returns the file descriptor for use in later lo_* calls + * return -1 upon failure. + */ +int +lo_open(PGconn *conn, Oid lobjId, int mode) +{ + int fd; + int result_len; + PQArgBlock argv[2]; + PGresult *res; + + if (conn == NULL || conn->lobjfuncs == NULL) + { + if (lo_initialize(conn) < 0) + return -1; + } + + argv[0].isint = 1; + argv[0].len = 4; + argv[0].u.integer = lobjId; + + argv[1].isint = 1; + argv[1].len = 4; + argv[1].u.integer = mode; + + res = PQfn(conn, conn->lobjfuncs->fn_lo_open, &fd, &result_len, 1, argv, 2); + if (PQresultStatus(res) == PGRES_COMMAND_OK) + { + PQclear(res); + return fd; + } + else + { + PQclear(res); + return -1; + } +} + +/* + * lo_close + * closes an existing large object + * + * returns 0 upon success + * returns -1 upon failure. + */ +int +lo_close(PGconn *conn, int fd) +{ + PQArgBlock argv[1]; + PGresult *res; + int retval; + int result_len; + + if (conn == NULL || conn->lobjfuncs == NULL) + { + if (lo_initialize(conn) < 0) + return -1; + } + + argv[0].isint = 1; + argv[0].len = 4; + argv[0].u.integer = fd; + res = PQfn(conn, conn->lobjfuncs->fn_lo_close, + &retval, &result_len, 1, argv, 1); + if (PQresultStatus(res) == PGRES_COMMAND_OK) + { + PQclear(res); + return retval; + } + else + { + PQclear(res); + return -1; + } +} + +/* + * lo_truncate + * truncates an existing large object to the given size + * + * returns 0 upon success + * returns -1 upon failure + */ +int +lo_truncate(PGconn *conn, int fd, size_t len) +{ + PQArgBlock argv[2]; + PGresult *res; + int retval; + int result_len; + + if (conn == NULL || conn->lobjfuncs == NULL) + { + if (lo_initialize(conn) < 0) + return -1; + } + + /* Must check this on-the-fly because it's not there pre-8.3 */ + if (conn->lobjfuncs->fn_lo_truncate == 0) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("cannot determine OID of function lo_truncate\n")); + return -1; + } + + /* + * Long ago, somebody thought it'd be a good idea to declare this function + * as taking size_t ... but the underlying backend function only accepts a + * signed int32 length. So throw error if the given value overflows + * int32. (A possible alternative is to automatically redirect the call + * to lo_truncate64; but if the caller wanted to rely on that backend + * function being available, he could have called lo_truncate64 for + * himself.) + */ + if (len > (size_t) INT_MAX) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("argument of lo_truncate exceeds integer range\n")); + return -1; + } + + argv[0].isint = 1; + argv[0].len = 4; + argv[0].u.integer = fd; + + argv[1].isint = 1; + argv[1].len = 4; + argv[1].u.integer = (int) len; + + res = PQfn(conn, conn->lobjfuncs->fn_lo_truncate, + &retval, &result_len, 1, argv, 2); + + if (PQresultStatus(res) == PGRES_COMMAND_OK) + { + PQclear(res); + return retval; + } + else + { + PQclear(res); + return -1; + } +} + +/* + * lo_truncate64 + * truncates an existing large object to the given size + * + * returns 0 upon success + * returns -1 upon failure + */ +int +lo_truncate64(PGconn *conn, int fd, pg_int64 len) +{ + PQArgBlock argv[2]; + PGresult *res; + int retval; + int result_len; + + if (conn == NULL || conn->lobjfuncs == NULL) + { + if (lo_initialize(conn) < 0) + return -1; + } + + if (conn->lobjfuncs->fn_lo_truncate64 == 0) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("cannot determine OID of function lo_truncate64\n")); + return -1; + } + + argv[0].isint = 1; + argv[0].len = 4; + argv[0].u.integer = fd; + + len = lo_hton64(len); + argv[1].isint = 0; + argv[1].len = 8; + argv[1].u.ptr = (int *) &len; + + res = PQfn(conn, conn->lobjfuncs->fn_lo_truncate64, + &retval, &result_len, 1, argv, 2); + + if (PQresultStatus(res) == PGRES_COMMAND_OK) + { + PQclear(res); + return retval; + } + else + { + PQclear(res); + return -1; + } +} + +/* + * lo_read + * read len bytes of the large object into buf + * + * returns the number of bytes read, or -1 on failure. + * the CALLER must have allocated enough space to hold the result returned + */ + +int +lo_read(PGconn *conn, int fd, char *buf, size_t len) +{ + PQArgBlock argv[2]; + PGresult *res; + int result_len; + + if (conn == NULL || conn->lobjfuncs == NULL) + { + if (lo_initialize(conn) < 0) + return -1; + } + + /* + * Long ago, somebody thought it'd be a good idea to declare this function + * as taking size_t ... but the underlying backend function only accepts a + * signed int32 length. So throw error if the given value overflows + * int32. + */ + if (len > (size_t) INT_MAX) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("argument of lo_read exceeds integer range\n")); + return -1; + } + + argv[0].isint = 1; + argv[0].len = 4; + argv[0].u.integer = fd; + + argv[1].isint = 1; + argv[1].len = 4; + argv[1].u.integer = (int) len; + + res = PQfn(conn, conn->lobjfuncs->fn_lo_read, + (void *) buf, &result_len, 0, argv, 2); + if (PQresultStatus(res) == PGRES_COMMAND_OK) + { + PQclear(res); + return result_len; + } + else + { + PQclear(res); + return -1; + } +} + +/* + * lo_write + * write len bytes of buf into the large object fd + * + * returns the number of bytes written, or -1 on failure. + */ +int +lo_write(PGconn *conn, int fd, const char *buf, size_t len) +{ + PQArgBlock argv[2]; + PGresult *res; + int result_len; + int retval; + + if (conn == NULL || conn->lobjfuncs == NULL) + { + if (lo_initialize(conn) < 0) + return -1; + } + + /* + * Long ago, somebody thought it'd be a good idea to declare this function + * as taking size_t ... but the underlying backend function only accepts a + * signed int32 length. So throw error if the given value overflows + * int32. + */ + if (len > (size_t) INT_MAX) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("argument of lo_write exceeds integer range\n")); + return -1; + } + + argv[0].isint = 1; + argv[0].len = 4; + argv[0].u.integer = fd; + + argv[1].isint = 0; + argv[1].len = (int) len; + argv[1].u.ptr = (int *) buf; + + res = PQfn(conn, conn->lobjfuncs->fn_lo_write, + &retval, &result_len, 1, argv, 2); + if (PQresultStatus(res) == PGRES_COMMAND_OK) + { + PQclear(res); + return retval; + } + else + { + PQclear(res); + return -1; + } +} + +/* + * lo_lseek + * change the current read or write location on a large object + */ +int +lo_lseek(PGconn *conn, int fd, int offset, int whence) +{ + PQArgBlock argv[3]; + PGresult *res; + int retval; + int result_len; + + if (conn == NULL || conn->lobjfuncs == NULL) + { + if (lo_initialize(conn) < 0) + return -1; + } + + argv[0].isint = 1; + argv[0].len = 4; + argv[0].u.integer = fd; + + argv[1].isint = 1; + argv[1].len = 4; + argv[1].u.integer = offset; + + argv[2].isint = 1; + argv[2].len = 4; + argv[2].u.integer = whence; + + res = PQfn(conn, conn->lobjfuncs->fn_lo_lseek, + &retval, &result_len, 1, argv, 3); + if (PQresultStatus(res) == PGRES_COMMAND_OK) + { + PQclear(res); + return retval; + } + else + { + PQclear(res); + return -1; + } +} + +/* + * lo_lseek64 + * change the current read or write location on a large object + */ +pg_int64 +lo_lseek64(PGconn *conn, int fd, pg_int64 offset, int whence) +{ + PQArgBlock argv[3]; + PGresult *res; + pg_int64 retval; + int result_len; + + if (conn == NULL || conn->lobjfuncs == NULL) + { + if (lo_initialize(conn) < 0) + return -1; + } + + if (conn->lobjfuncs->fn_lo_lseek64 == 0) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("cannot determine OID of function lo_lseek64\n")); + return -1; + } + + argv[0].isint = 1; + argv[0].len = 4; + argv[0].u.integer = fd; + + offset = lo_hton64(offset); + argv[1].isint = 0; + argv[1].len = 8; + argv[1].u.ptr = (int *) &offset; + + argv[2].isint = 1; + argv[2].len = 4; + argv[2].u.integer = whence; + + res = PQfn(conn, conn->lobjfuncs->fn_lo_lseek64, + (void *) &retval, &result_len, 0, argv, 3); + if (PQresultStatus(res) == PGRES_COMMAND_OK && result_len == 8) + { + PQclear(res); + return lo_ntoh64(retval); + } + else + { + PQclear(res); + return -1; + } +} + +/* + * lo_creat + * create a new large object + * the mode is ignored (once upon a time it had a use) + * + * returns the oid of the large object created or + * InvalidOid upon failure + */ +Oid +lo_creat(PGconn *conn, int mode) +{ + PQArgBlock argv[1]; + PGresult *res; + int retval; + int result_len; + + if (conn == NULL || conn->lobjfuncs == NULL) + { + if (lo_initialize(conn) < 0) + return InvalidOid; + } + + argv[0].isint = 1; + argv[0].len = 4; + argv[0].u.integer = mode; + res = PQfn(conn, conn->lobjfuncs->fn_lo_creat, + &retval, &result_len, 1, argv, 1); + if (PQresultStatus(res) == PGRES_COMMAND_OK) + { + PQclear(res); + return (Oid) retval; + } + else + { + PQclear(res); + return InvalidOid; + } +} + +/* + * lo_create + * create a new large object + * if lobjId isn't InvalidOid, it specifies the OID to (attempt to) create + * + * returns the oid of the large object created or + * InvalidOid upon failure + */ +Oid +lo_create(PGconn *conn, Oid lobjId) +{ + PQArgBlock argv[1]; + PGresult *res; + int retval; + int result_len; + + if (conn == NULL || conn->lobjfuncs == NULL) + { + if (lo_initialize(conn) < 0) + return InvalidOid; + } + + /* Must check this on-the-fly because it's not there pre-8.1 */ + if (conn->lobjfuncs->fn_lo_create == 0) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("cannot determine OID of function lo_create\n")); + return InvalidOid; + } + + argv[0].isint = 1; + argv[0].len = 4; + argv[0].u.integer = lobjId; + res = PQfn(conn, conn->lobjfuncs->fn_lo_create, + &retval, &result_len, 1, argv, 1); + if (PQresultStatus(res) == PGRES_COMMAND_OK) + { + PQclear(res); + return (Oid) retval; + } + else + { + PQclear(res); + return InvalidOid; + } +} + + +/* + * lo_tell + * returns the current seek location of the large object + */ +int +lo_tell(PGconn *conn, int fd) +{ + int retval; + PQArgBlock argv[1]; + PGresult *res; + int result_len; + + if (conn == NULL || conn->lobjfuncs == NULL) + { + if (lo_initialize(conn) < 0) + return -1; + } + + argv[0].isint = 1; + argv[0].len = 4; + argv[0].u.integer = fd; + + res = PQfn(conn, conn->lobjfuncs->fn_lo_tell, + &retval, &result_len, 1, argv, 1); + if (PQresultStatus(res) == PGRES_COMMAND_OK) + { + PQclear(res); + return retval; + } + else + { + PQclear(res); + return -1; + } +} + +/* + * lo_tell64 + * returns the current seek location of the large object + */ +pg_int64 +lo_tell64(PGconn *conn, int fd) +{ + pg_int64 retval; + PQArgBlock argv[1]; + PGresult *res; + int result_len; + + if (conn == NULL || conn->lobjfuncs == NULL) + { + if (lo_initialize(conn) < 0) + return -1; + } + + if (conn->lobjfuncs->fn_lo_tell64 == 0) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("cannot determine OID of function lo_tell64\n")); + return -1; + } + + argv[0].isint = 1; + argv[0].len = 4; + argv[0].u.integer = fd; + + res = PQfn(conn, conn->lobjfuncs->fn_lo_tell64, + (void *) &retval, &result_len, 0, argv, 1); + if (PQresultStatus(res) == PGRES_COMMAND_OK && result_len == 8) + { + PQclear(res); + return lo_ntoh64(retval); + } + else + { + PQclear(res); + return -1; + } +} + +/* + * lo_unlink + * delete a file + */ + +int +lo_unlink(PGconn *conn, Oid lobjId) +{ + PQArgBlock argv[1]; + PGresult *res; + int result_len; + int retval; + + if (conn == NULL || conn->lobjfuncs == NULL) + { + if (lo_initialize(conn) < 0) + return -1; + } + + argv[0].isint = 1; + argv[0].len = 4; + argv[0].u.integer = lobjId; + + res = PQfn(conn, conn->lobjfuncs->fn_lo_unlink, + &retval, &result_len, 1, argv, 1); + if (PQresultStatus(res) == PGRES_COMMAND_OK) + { + PQclear(res); + return retval; + } + else + { + PQclear(res); + return -1; + } +} + +/* + * lo_import - + * imports a file as an (inversion) large object. + * + * returns the oid of that object upon success, + * returns InvalidOid upon failure + */ + +Oid +lo_import(PGconn *conn, const char *filename) +{ + return lo_import_internal(conn, filename, InvalidOid); +} + +/* + * lo_import_with_oid - + * imports a file as an (inversion) large object. + * large object id can be specified. + * + * returns the oid of that object upon success, + * returns InvalidOid upon failure + */ + +Oid +lo_import_with_oid(PGconn *conn, const char *filename, Oid lobjId) +{ + return lo_import_internal(conn, filename, lobjId); +} + +static Oid +lo_import_internal(PGconn *conn, const char *filename, Oid oid) +{ + int fd; + int nbytes, + tmp; + char buf[LO_BUFSIZE]; + Oid lobjOid; + int lobj; + char sebuf[256]; + + /* + * open the file to be read in + */ + fd = open(filename, O_RDONLY | PG_BINARY, 0666); + if (fd < 0) + { /* error */ + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not open file \"%s\": %s\n"), + filename, pqStrerror(errno, sebuf, sizeof(sebuf))); + return InvalidOid; + } + + /* + * create an inversion object + */ + if (oid == InvalidOid) + lobjOid = lo_creat(conn, INV_READ | INV_WRITE); + else + lobjOid = lo_create(conn, oid); + + if (lobjOid == InvalidOid) + { + /* we assume lo_create() already set a suitable error message */ + (void) close(fd); + return InvalidOid; + } + + lobj = lo_open(conn, lobjOid, INV_WRITE); + if (lobj == -1) + { + /* we assume lo_open() already set a suitable error message */ + (void) close(fd); + return InvalidOid; + } + + /* + * read in from the file and write to the large object + */ + while ((nbytes = read(fd, buf, LO_BUFSIZE)) > 0) + { + tmp = lo_write(conn, lobj, buf, nbytes); + if (tmp != nbytes) + { + /* + * If lo_write() failed, we are now in an aborted transaction so + * there's no need for lo_close(); furthermore, if we tried it + * we'd overwrite the useful error result with a useless one. So + * just nail the doors shut and get out of town. + */ + (void) close(fd); + return InvalidOid; + } + } + + if (nbytes < 0) + { + /* We must do lo_close before setting the errorMessage */ + int save_errno = errno; + + (void) lo_close(conn, lobj); + (void) close(fd); + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not read from file \"%s\": %s\n"), + filename, + pqStrerror(save_errno, sebuf, sizeof(sebuf))); + return InvalidOid; + } + + (void) close(fd); + + if (lo_close(conn, lobj) != 0) + { + /* we assume lo_close() already set a suitable error message */ + return InvalidOid; + } + + return lobjOid; +} + +/* + * lo_export - + * exports an (inversion) large object. + * returns -1 upon failure, 1 if OK + */ +int +lo_export(PGconn *conn, Oid lobjId, const char *filename) +{ + int result = 1; + int fd; + int nbytes, + tmp; + char buf[LO_BUFSIZE]; + int lobj; + char sebuf[256]; + + /* + * open the large object. + */ + lobj = lo_open(conn, lobjId, INV_READ); + if (lobj == -1) + { + /* we assume lo_open() already set a suitable error message */ + return -1; + } + + /* + * create the file to be written to + */ + fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC | PG_BINARY, 0666); + if (fd < 0) + { + /* We must do lo_close before setting the errorMessage */ + int save_errno = errno; + + (void) lo_close(conn, lobj); + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not open file \"%s\": %s\n"), + filename, + pqStrerror(save_errno, sebuf, sizeof(sebuf))); + return -1; + } + + /* + * read in from the large object and write to the file + */ + while ((nbytes = lo_read(conn, lobj, buf, LO_BUFSIZE)) > 0) + { + tmp = write(fd, buf, nbytes); + if (tmp != nbytes) + { + /* We must do lo_close before setting the errorMessage */ + int save_errno = errno; + + (void) lo_close(conn, lobj); + (void) close(fd); + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not write to file \"%s\": %s\n"), + filename, + pqStrerror(save_errno, sebuf, sizeof(sebuf))); + return -1; + } + } + + /* + * If lo_read() failed, we are now in an aborted transaction so there's no + * need for lo_close(); furthermore, if we tried it we'd overwrite the + * useful error result with a useless one. So skip lo_close() if we got a + * failure result. + */ + if (nbytes < 0 || + lo_close(conn, lobj) != 0) + { + /* assume lo_read() or lo_close() left a suitable error message */ + result = -1; + } + + /* if we already failed, don't overwrite that msg with a close error */ + if (close(fd) && result >= 0) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not write to file \"%s\": %s\n"), + filename, pqStrerror(errno, sebuf, sizeof(sebuf))); + result = -1; + } + + return result; +} + + +/* + * lo_initialize + * + * Initialize the large object interface for an existing connection. + * We ask the backend about the functions OID's in pg_proc for all + * functions that are required for large object operations. + */ +static int +lo_initialize(PGconn *conn) +{ + PGresult *res; + PGlobjfuncs *lobjfuncs; + int n; + const char *query; + const char *fname; + Oid foid; + + if (!conn) + return -1; + + /* + * Allocate the structure to hold the functions OID's + */ + lobjfuncs = (PGlobjfuncs *) malloc(sizeof(PGlobjfuncs)); + if (lobjfuncs == NULL) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("out of memory\n")); + return -1; + } + MemSet((char *) lobjfuncs, 0, sizeof(PGlobjfuncs)); + + /* + * Execute the query to get all the functions at once. In 7.3 and later + * we need to be schema-safe. lo_create only exists in 8.1 and up. + * lo_truncate only exists in 8.3 and up. + */ + if (conn->sversion >= 70300) + query = "select proname, oid from pg_catalog.pg_proc " + "where proname in (" + "'lo_open', " + "'lo_close', " + "'lo_creat', " + "'lo_create', " + "'lo_unlink', " + "'lo_lseek', " + "'lo_lseek64', " + "'lo_tell', " + "'lo_tell64', " + "'lo_truncate', " + "'lo_truncate64', " + "'loread', " + "'lowrite') " + "and pronamespace = (select oid from pg_catalog.pg_namespace " + "where nspname = 'pg_catalog')"; + else + query = "select proname, oid from pg_proc " + "where proname = 'lo_open' " + "or proname = 'lo_close' " + "or proname = 'lo_creat' " + "or proname = 'lo_unlink' " + "or proname = 'lo_lseek' " + "or proname = 'lo_tell' " + "or proname = 'loread' " + "or proname = 'lowrite'"; + + res = PQexec(conn, query); + if (res == NULL) + { + free(lobjfuncs); + return -1; + } + + if (res->resultStatus != PGRES_TUPLES_OK) + { + free(lobjfuncs); + PQclear(res); + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("query to initialize large object functions did not return data\n")); + return -1; + } + + /* + * Examine the result and put the OID's into the struct + */ + for (n = 0; n < PQntuples(res); n++) + { + fname = PQgetvalue(res, n, 0); + foid = (Oid) atoi(PQgetvalue(res, n, 1)); + if (strcmp(fname, "lo_open") == 0) + lobjfuncs->fn_lo_open = foid; + else if (strcmp(fname, "lo_close") == 0) + lobjfuncs->fn_lo_close = foid; + else if (strcmp(fname, "lo_creat") == 0) + lobjfuncs->fn_lo_creat = foid; + else if (strcmp(fname, "lo_create") == 0) + lobjfuncs->fn_lo_create = foid; + else if (strcmp(fname, "lo_unlink") == 0) + lobjfuncs->fn_lo_unlink = foid; + else if (strcmp(fname, "lo_lseek") == 0) + lobjfuncs->fn_lo_lseek = foid; + else if (strcmp(fname, "lo_lseek64") == 0) + lobjfuncs->fn_lo_lseek64 = foid; + else if (strcmp(fname, "lo_tell") == 0) + lobjfuncs->fn_lo_tell = foid; + else if (strcmp(fname, "lo_tell64") == 0) + lobjfuncs->fn_lo_tell64 = foid; + else if (strcmp(fname, "lo_truncate") == 0) + lobjfuncs->fn_lo_truncate = foid; + else if (strcmp(fname, "lo_truncate64") == 0) + lobjfuncs->fn_lo_truncate64 = foid; + else if (strcmp(fname, "loread") == 0) + lobjfuncs->fn_lo_read = foid; + else if (strcmp(fname, "lowrite") == 0) + lobjfuncs->fn_lo_write = foid; + } + + PQclear(res); + + /* + * Finally check that we got all required large object interface functions + * (ones that have been added later than the stone age are instead checked + * only if used) + */ + if (lobjfuncs->fn_lo_open == 0) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("cannot determine OID of function lo_open\n")); + free(lobjfuncs); + return -1; + } + if (lobjfuncs->fn_lo_close == 0) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("cannot determine OID of function lo_close\n")); + free(lobjfuncs); + return -1; + } + if (lobjfuncs->fn_lo_creat == 0) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("cannot determine OID of function lo_creat\n")); + free(lobjfuncs); + return -1; + } + if (lobjfuncs->fn_lo_unlink == 0) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("cannot determine OID of function lo_unlink\n")); + free(lobjfuncs); + return -1; + } + if (lobjfuncs->fn_lo_lseek == 0) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("cannot determine OID of function lo_lseek\n")); + free(lobjfuncs); + return -1; + } + if (lobjfuncs->fn_lo_tell == 0) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("cannot determine OID of function lo_tell\n")); + free(lobjfuncs); + return -1; + } + if (lobjfuncs->fn_lo_read == 0) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("cannot determine OID of function loread\n")); + free(lobjfuncs); + return -1; + } + if (lobjfuncs->fn_lo_write == 0) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("cannot determine OID of function lowrite\n")); + free(lobjfuncs); + return -1; + } + + /* + * Put the structure into the connection control + */ + conn->lobjfuncs = lobjfuncs; + return 0; +} + +/* + * lo_hton64 + * converts a 64-bit integer from host byte order to network byte order + */ +static pg_int64 +lo_hton64(pg_int64 host64) +{ + union + { + pg_int64 i64; + uint32 i32[2]; + } swap; + uint32 t; + + /* High order half first, since we're doing MSB-first */ + t = (uint32) (host64 >> 32); + swap.i32[0] = htonl(t); + + /* Now the low order half */ + t = (uint32) host64; + swap.i32[1] = htonl(t); + + return swap.i64; +} + +/* + * lo_ntoh64 + * converts a 64-bit integer from network byte order to host byte order + */ +static pg_int64 +lo_ntoh64(pg_int64 net64) +{ + union + { + pg_int64 i64; + uint32 i32[2]; + } swap; + pg_int64 result; + + swap.i64 = net64; + + result = (uint32) ntohl(swap.i32[0]); + result <<= 32; + result |= (uint32) ntohl(swap.i32[1]); + + return result; +} diff --git a/libpq/fe-misc.c b/libpq/fe-misc.c new file mode 100644 index 0000000..32da8ca --- /dev/null +++ b/libpq/fe-misc.c @@ -0,0 +1,1267 @@ +/*------------------------------------------------------------------------- + * + * FILE + * fe-misc.c + * + * DESCRIPTION + * miscellaneous useful functions + * + * The communication routines here are analogous to the ones in + * backend/libpq/pqcomm.c and backend/libpq/pqcomprim.c, but operate + * in the considerably different environment of the frontend libpq. + * In particular, we work with a bare nonblock-mode socket, rather than + * a stdio stream, so that we can avoid unwanted blocking of the application. + * + * XXX: MOVE DEBUG PRINTOUT TO HIGHER LEVEL. As is, block and restart + * will cause repeat printouts. + * + * We must speak the same transmitted data representations as the backend + * routines. + * + * + * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * IDENTIFICATION + * src/interfaces/libpq/fe-misc.c + * + *------------------------------------------------------------------------- + */ + +#include "postgres_fe.h" + +#include +#include + +#include +#include + +#ifdef WIN32 +#include "win32.h" +#else +#include +#include +#endif + +#ifdef HAVE_POLL_H +#include +#endif +#ifdef HAVE_SYS_POLL_H +#include +#endif +#ifdef HAVE_SYS_SELECT_H +#include +#endif + +#include "libpq-fe.h" +#include "libpq-int.h" +#include "mb/pg_wchar.h" +#include "pg_config_paths.h" + + +static int pqPutMsgBytes(const void *buf, size_t len, PGconn *conn); +static int pqSendSome(PGconn *conn, int len); +static int pqSocketCheck(PGconn *conn, int forRead, int forWrite, + time_t end_time); +static int pqSocketPoll(int sock, int forRead, int forWrite, time_t end_time); + +/* + * PQlibVersion: return the libpq version number + */ +int +PQlibVersion(void) +{ + return PG_VERSION_NUM; +} + +/* + * fputnbytes: print exactly N bytes to a file + * + * We avoid using %.*s here because it can misbehave if the data + * is not valid in what libc thinks is the prevailing encoding. + */ +static void +fputnbytes(FILE *f, const char *str, size_t n) +{ + while (n-- > 0) + fputc(*str++, f); +} + + +/* + * pqGetc: get 1 character from the connection + * + * All these routines return 0 on success, EOF on error. + * Note that for the Get routines, EOF only means there is not enough + * data in the buffer, not that there is necessarily a hard error. + */ +int +pqGetc(char *result, PGconn *conn) +{ + if (conn->inCursor >= conn->inEnd) + return EOF; + + *result = conn->inBuffer[conn->inCursor++]; + + if (conn->Pfdebug) + fprintf(conn->Pfdebug, "From backend> %c\n", *result); + + return 0; +} + + +/* + * pqPutc: write 1 char to the current message + */ +int +pqPutc(char c, PGconn *conn) +{ + if (pqPutMsgBytes(&c, 1, conn)) + return EOF; + + if (conn->Pfdebug) + fprintf(conn->Pfdebug, "To backend> %c\n", c); + + return 0; +} + + +/* + * pqGets[_append]: + * get a null-terminated string from the connection, + * and store it in an expansible PQExpBuffer. + * If we run out of memory, all of the string is still read, + * but the excess characters are silently discarded. + */ +static int +pqGets_internal(PQExpBuffer buf, PGconn *conn, bool resetbuffer) +{ + /* Copy conn data to locals for faster search loop */ + char *inBuffer = conn->inBuffer; + int inCursor = conn->inCursor; + int inEnd = conn->inEnd; + int slen; + + while (inCursor < inEnd && inBuffer[inCursor]) + inCursor++; + + if (inCursor >= inEnd) + return EOF; + + slen = inCursor - conn->inCursor; + + if (resetbuffer) + resetPQExpBuffer(buf); + + appendBinaryPQExpBuffer(buf, inBuffer + conn->inCursor, slen); + + conn->inCursor = ++inCursor; + + if (conn->Pfdebug) + fprintf(conn->Pfdebug, "From backend> \"%s\"\n", + buf->data); + + return 0; +} + +int +pqGets(PQExpBuffer buf, PGconn *conn) +{ + return pqGets_internal(buf, conn, true); +} + +int +pqGets_append(PQExpBuffer buf, PGconn *conn) +{ + return pqGets_internal(buf, conn, false); +} + + +/* + * pqPuts: write a null-terminated string to the current message + */ +int +pqPuts(const char *s, PGconn *conn) +{ + if (pqPutMsgBytes(s, strlen(s) + 1, conn)) + return EOF; + + if (conn->Pfdebug) + fprintf(conn->Pfdebug, "To backend> \"%s\"\n", s); + + return 0; +} + +/* + * pqGetnchar: + * get a string of exactly len bytes in buffer s, no null termination + */ +int +pqGetnchar(char *s, size_t len, PGconn *conn) +{ + if (len > (size_t) (conn->inEnd - conn->inCursor)) + return EOF; + + memcpy(s, conn->inBuffer + conn->inCursor, len); + /* no terminating null */ + + conn->inCursor += len; + + if (conn->Pfdebug) + { + fprintf(conn->Pfdebug, "From backend (%lu)> ", (unsigned long) len); + fputnbytes(conn->Pfdebug, s, len); + fprintf(conn->Pfdebug, "\n"); + } + + return 0; +} + +/* + * pqSkipnchar: + * skip over len bytes in input buffer. + * + * Note: this is primarily useful for its debug output, which should + * be exactly the same as for pqGetnchar. We assume the data in question + * will actually be used, but just isn't getting copied anywhere as yet. + */ +int +pqSkipnchar(size_t len, PGconn *conn) +{ + if (len > (size_t) (conn->inEnd - conn->inCursor)) + return EOF; + + if (conn->Pfdebug) + { + fprintf(conn->Pfdebug, "From backend (%lu)> ", (unsigned long) len); + fputnbytes(conn->Pfdebug, conn->inBuffer + conn->inCursor, len); + fprintf(conn->Pfdebug, "\n"); + } + + conn->inCursor += len; + + return 0; +} + +/* + * pqPutnchar: + * write exactly len bytes to the current message + */ +int +pqPutnchar(const char *s, size_t len, PGconn *conn) +{ + if (pqPutMsgBytes(s, len, conn)) + return EOF; + + if (conn->Pfdebug) + { + fprintf(conn->Pfdebug, "To backend> "); + fputnbytes(conn->Pfdebug, s, len); + fprintf(conn->Pfdebug, "\n"); + } + + return 0; +} + +/* + * pqGetInt + * read a 2 or 4 byte integer and convert from network byte order + * to local byte order + */ +int +pqGetInt(int *result, size_t bytes, PGconn *conn) +{ + uint16 tmp2; + uint32 tmp4; + + switch (bytes) + { + case 2: + if (conn->inCursor + 2 > conn->inEnd) + return EOF; + memcpy(&tmp2, conn->inBuffer + conn->inCursor, 2); + conn->inCursor += 2; + *result = (int) ntohs(tmp2); + break; + case 4: + if (conn->inCursor + 4 > conn->inEnd) + return EOF; + memcpy(&tmp4, conn->inBuffer + conn->inCursor, 4); + conn->inCursor += 4; + *result = (int) ntohl(tmp4); + break; + default: + pqInternalNotice(&conn->noticeHooks, + "integer of size %lu not supported by pqGetInt", + (unsigned long) bytes); + return EOF; + } + + if (conn->Pfdebug) + fprintf(conn->Pfdebug, "From backend (#%lu)> %d\n", (unsigned long) bytes, *result); + + return 0; +} + +/* + * pqPutInt + * write an integer of 2 or 4 bytes, converting from host byte order + * to network byte order. + */ +int +pqPutInt(int value, size_t bytes, PGconn *conn) +{ + uint16 tmp2; + uint32 tmp4; + + switch (bytes) + { + case 2: + tmp2 = htons((uint16) value); + if (pqPutMsgBytes((const char *) &tmp2, 2, conn)) + return EOF; + break; + case 4: + tmp4 = htonl((uint32) value); + if (pqPutMsgBytes((const char *) &tmp4, 4, conn)) + return EOF; + break; + default: + pqInternalNotice(&conn->noticeHooks, + "integer of size %lu not supported by pqPutInt", + (unsigned long) bytes); + return EOF; + } + + if (conn->Pfdebug) + fprintf(conn->Pfdebug, "To backend (%lu#)> %d\n", (unsigned long) bytes, value); + + return 0; +} + +/* + * Make sure conn's output buffer can hold bytes_needed bytes (caller must + * include already-stored data into the value!) + * + * Returns 0 on success, EOF if failed to enlarge buffer + */ +int +pqCheckOutBufferSpace(size_t bytes_needed, PGconn *conn) +{ + int newsize = conn->outBufSize; + char *newbuf; + + /* Quick exit if we have enough space */ + if (bytes_needed <= (size_t) newsize) + return 0; + + /* + * If we need to enlarge the buffer, we first try to double it in size; if + * that doesn't work, enlarge in multiples of 8K. This avoids thrashing + * the malloc pool by repeated small enlargements. + * + * Note: tests for newsize > 0 are to catch integer overflow. + */ + do + { + newsize *= 2; + } while (newsize > 0 && bytes_needed > (size_t) newsize); + + if (newsize > 0 && bytes_needed <= (size_t) newsize) + { + newbuf = realloc(conn->outBuffer, newsize); + if (newbuf) + { + /* realloc succeeded */ + conn->outBuffer = newbuf; + conn->outBufSize = newsize; + return 0; + } + } + + newsize = conn->outBufSize; + do + { + newsize += 8192; + } while (newsize > 0 && bytes_needed > (size_t) newsize); + + if (newsize > 0 && bytes_needed <= (size_t) newsize) + { + newbuf = realloc(conn->outBuffer, newsize); + if (newbuf) + { + /* realloc succeeded */ + conn->outBuffer = newbuf; + conn->outBufSize = newsize; + return 0; + } + } + + /* realloc failed. Probably out of memory */ + printfPQExpBuffer(&conn->errorMessage, + "cannot allocate memory for output buffer\n"); + return EOF; +} + +/* + * Make sure conn's input buffer can hold bytes_needed bytes (caller must + * include already-stored data into the value!) + * + * Returns 0 on success, EOF if failed to enlarge buffer + */ +int +pqCheckInBufferSpace(size_t bytes_needed, PGconn *conn) +{ + int newsize = conn->inBufSize; + char *newbuf; + + /* Quick exit if we have enough space */ + if (bytes_needed <= (size_t) newsize) + return 0; + + /* + * Before concluding that we need to enlarge the buffer, left-justify + * whatever is in it and recheck. The caller's value of bytes_needed + * includes any data to the left of inStart, but we can delete that in + * preference to enlarging the buffer. It's slightly ugly to have this + * function do this, but it's better than making callers worry about it. + */ + bytes_needed -= conn->inStart; + + if (conn->inStart < conn->inEnd) + { + if (conn->inStart > 0) + { + memmove(conn->inBuffer, conn->inBuffer + conn->inStart, + conn->inEnd - conn->inStart); + conn->inEnd -= conn->inStart; + conn->inCursor -= conn->inStart; + conn->inStart = 0; + } + } + else + { + /* buffer is logically empty, reset it */ + conn->inStart = conn->inCursor = conn->inEnd = 0; + } + + /* Recheck whether we have enough space */ + if (bytes_needed <= (size_t) newsize) + return 0; + + /* + * If we need to enlarge the buffer, we first try to double it in size; if + * that doesn't work, enlarge in multiples of 8K. This avoids thrashing + * the malloc pool by repeated small enlargements. + * + * Note: tests for newsize > 0 are to catch integer overflow. + */ + do + { + newsize *= 2; + } while (newsize > 0 && bytes_needed > (size_t) newsize); + + if (newsize > 0 && bytes_needed <= (size_t) newsize) + { + newbuf = realloc(conn->inBuffer, newsize); + if (newbuf) + { + /* realloc succeeded */ + conn->inBuffer = newbuf; + conn->inBufSize = newsize; + return 0; + } + } + + newsize = conn->inBufSize; + do + { + newsize += 8192; + } while (newsize > 0 && bytes_needed > (size_t) newsize); + + if (newsize > 0 && bytes_needed <= (size_t) newsize) + { + newbuf = realloc(conn->inBuffer, newsize); + if (newbuf) + { + /* realloc succeeded */ + conn->inBuffer = newbuf; + conn->inBufSize = newsize; + return 0; + } + } + + /* realloc failed. Probably out of memory */ + printfPQExpBuffer(&conn->errorMessage, + "cannot allocate memory for input buffer\n"); + return EOF; +} + +/* + * pqPutMsgStart: begin construction of a message to the server + * + * msg_type is the message type byte, or 0 for a message without type byte + * (only startup messages have no type byte) + * + * force_len forces the message to have a length word; otherwise, we add + * a length word if protocol 3. + * + * Returns 0 on success, EOF on error + * + * The idea here is that we construct the message in conn->outBuffer, + * beginning just past any data already in outBuffer (ie, at + * outBuffer+outCount). We enlarge the buffer as needed to hold the message. + * When the message is complete, we fill in the length word (if needed) and + * then advance outCount past the message, making it eligible to send. + * + * The state variable conn->outMsgStart points to the incomplete message's + * length word: it is either outCount or outCount+1 depending on whether + * there is a type byte. If we are sending a message without length word + * (pre protocol 3.0 only), then outMsgStart is -1. The state variable + * conn->outMsgEnd is the end of the data collected so far. + */ +int +pqPutMsgStart(char msg_type, bool force_len, PGconn *conn) +{ + int lenPos; + int endPos; + + /* allow room for message type byte */ + if (msg_type) + endPos = conn->outCount + 1; + else + endPos = conn->outCount; + + /* do we want a length word? */ + if (force_len || PG_PROTOCOL_MAJOR(conn->pversion) >= 3) + { + lenPos = endPos; + /* allow room for message length */ + endPos += 4; + } + else + lenPos = -1; + + /* make sure there is room for message header */ + if (pqCheckOutBufferSpace(endPos, conn)) + return EOF; + /* okay, save the message type byte if any */ + if (msg_type) + conn->outBuffer[conn->outCount] = msg_type; + /* set up the message pointers */ + conn->outMsgStart = lenPos; + conn->outMsgEnd = endPos; + /* length word, if needed, will be filled in by pqPutMsgEnd */ + + if (conn->Pfdebug) + fprintf(conn->Pfdebug, "To backend> Msg %c\n", + msg_type ? msg_type : ' '); + + return 0; +} + +/* + * pqPutMsgBytes: add bytes to a partially-constructed message + * + * Returns 0 on success, EOF on error + */ +static int +pqPutMsgBytes(const void *buf, size_t len, PGconn *conn) +{ + /* make sure there is room for it */ + if (pqCheckOutBufferSpace(conn->outMsgEnd + len, conn)) + return EOF; + /* okay, save the data */ + memcpy(conn->outBuffer + conn->outMsgEnd, buf, len); + conn->outMsgEnd += len; + /* no Pfdebug call here, caller should do it */ + return 0; +} + +/* + * pqPutMsgEnd: finish constructing a message and possibly send it + * + * Returns 0 on success, EOF on error + * + * We don't actually send anything here unless we've accumulated at least + * 8K worth of data (the typical size of a pipe buffer on Unix systems). + * This avoids sending small partial packets. The caller must use pqFlush + * when it's important to flush all the data out to the server. + */ +int +pqPutMsgEnd(PGconn *conn) +{ + if (conn->Pfdebug) + fprintf(conn->Pfdebug, "To backend> Msg complete, length %u\n", + conn->outMsgEnd - conn->outCount); + + /* Fill in length word if needed */ + if (conn->outMsgStart >= 0) + { + uint32 msgLen = conn->outMsgEnd - conn->outMsgStart; + + msgLen = htonl(msgLen); + memcpy(conn->outBuffer + conn->outMsgStart, &msgLen, 4); + } + + /* Make message eligible to send */ + conn->outCount = conn->outMsgEnd; + + if (conn->outCount >= 8192) + { + int toSend = conn->outCount - (conn->outCount % 8192); + + if (pqSendSome(conn, toSend) < 0) + return EOF; + /* in nonblock mode, don't complain if unable to send it all */ + } + + return 0; +} + +/* ---------- + * pqReadData: read more data, if any is available + * Possible return values: + * 1: successfully loaded at least one more byte + * 0: no data is presently available, but no error detected + * -1: error detected (including EOF = connection closure); + * conn->errorMessage set + * NOTE: callers must not assume that pointers or indexes into conn->inBuffer + * remain valid across this call! + * ---------- + */ +int +pqReadData(PGconn *conn) +{ + int someread = 0; + int nread; + + if (conn->sock == PGINVALID_SOCKET) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("connection not open\n")); + return -1; + } + + /* Left-justify any data in the buffer to make room */ + if (conn->inStart < conn->inEnd) + { + if (conn->inStart > 0) + { + memmove(conn->inBuffer, conn->inBuffer + conn->inStart, + conn->inEnd - conn->inStart); + conn->inEnd -= conn->inStart; + conn->inCursor -= conn->inStart; + conn->inStart = 0; + } + } + else + { + /* buffer is logically empty, reset it */ + conn->inStart = conn->inCursor = conn->inEnd = 0; + } + + /* + * If the buffer is fairly full, enlarge it. We need to be able to enlarge + * the buffer in case a single message exceeds the initial buffer size. We + * enlarge before filling the buffer entirely so as to avoid asking the + * kernel for a partial packet. The magic constant here should be large + * enough for a TCP packet or Unix pipe bufferload. 8K is the usual pipe + * buffer size, so... + */ + if (conn->inBufSize - conn->inEnd < 8192) + { + if (pqCheckInBufferSpace(conn->inEnd + (size_t) 8192, conn)) + { + /* + * We don't insist that the enlarge worked, but we need some room + */ + if (conn->inBufSize - conn->inEnd < 100) + return -1; /* errorMessage already set */ + } + } + + /* OK, try to read some data */ +retry3: + nread = pqsecure_read(conn, conn->inBuffer + conn->inEnd, + conn->inBufSize - conn->inEnd); + if (nread < 0) + { + if (SOCK_ERRNO == EINTR) + goto retry3; + /* Some systems return EAGAIN/EWOULDBLOCK for no data */ +#ifdef EAGAIN + if (SOCK_ERRNO == EAGAIN) + return someread; +#endif +#if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN)) + if (SOCK_ERRNO == EWOULDBLOCK) + return someread; +#endif + /* We might get ECONNRESET here if using TCP and backend died */ +#ifdef ECONNRESET + if (SOCK_ERRNO == ECONNRESET) + goto definitelyFailed; +#endif + /* pqsecure_read set the error message for us */ + return -1; + } + if (nread > 0) + { + conn->inEnd += nread; + + /* + * Hack to deal with the fact that some kernels will only give us back + * 1 packet per recv() call, even if we asked for more and there is + * more available. If it looks like we are reading a long message, + * loop back to recv() again immediately, until we run out of data or + * buffer space. Without this, the block-and-restart behavior of + * libpq's higher levels leads to O(N^2) performance on long messages. + * + * Since we left-justified the data above, conn->inEnd gives the + * amount of data already read in the current message. We consider + * the message "long" once we have acquired 32k ... + */ + if (conn->inEnd > 32768 && + (conn->inBufSize - conn->inEnd) >= 8192) + { + someread = 1; + goto retry3; + } + return 1; + } + + if (someread) + return 1; /* got a zero read after successful tries */ + + /* + * A return value of 0 could mean just that no data is now available, or + * it could mean EOF --- that is, the server has closed the connection. + * Since we have the socket in nonblock mode, the only way to tell the + * difference is to see if select() is saying that the file is ready. + * Grumble. Fortunately, we don't expect this path to be taken much, + * since in normal practice we should not be trying to read data unless + * the file selected for reading already. + * + * In SSL mode it's even worse: SSL_read() could say WANT_READ and then + * data could arrive before we make the pqReadReady() test, but the second + * SSL_read() could still say WANT_READ because the data received was not + * a complete SSL record. So we must play dumb and assume there is more + * data, relying on the SSL layer to detect true EOF. + */ + +#ifdef USE_SSL + if (conn->ssl_in_use) + return 0; +#endif + + switch (pqReadReady(conn)) + { + case 0: + /* definitely no data available */ + return 0; + case 1: + /* ready for read */ + break; + default: + /* we override pqReadReady's message with something more useful */ + goto definitelyEOF; + } + + /* + * Still not sure that it's EOF, because some data could have just + * arrived. + */ +retry4: + nread = pqsecure_read(conn, conn->inBuffer + conn->inEnd, + conn->inBufSize - conn->inEnd); + if (nread < 0) + { + if (SOCK_ERRNO == EINTR) + goto retry4; + /* Some systems return EAGAIN/EWOULDBLOCK for no data */ +#ifdef EAGAIN + if (SOCK_ERRNO == EAGAIN) + return 0; +#endif +#if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN)) + if (SOCK_ERRNO == EWOULDBLOCK) + return 0; +#endif + /* We might get ECONNRESET here if using TCP and backend died */ +#ifdef ECONNRESET + if (SOCK_ERRNO == ECONNRESET) + goto definitelyFailed; +#endif + /* pqsecure_read set the error message for us */ + return -1; + } + if (nread > 0) + { + conn->inEnd += nread; + return 1; + } + + /* + * OK, we are getting a zero read even though select() says ready. This + * means the connection has been closed. Cope. + */ +definitelyEOF: + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext( + "server closed the connection unexpectedly\n" + "\tThis probably means the server terminated abnormally\n" + "\tbefore or while processing the request.\n")); + + /* Come here if lower-level code already set a suitable errorMessage */ +definitelyFailed: + /* Do *not* drop any already-read data; caller still wants it */ + pqDropConnection(conn, false); + conn->status = CONNECTION_BAD; /* No more connection to backend */ + return -1; +} + +/* + * pqSendSome: send data waiting in the output buffer. + * + * len is how much to try to send (typically equal to outCount, but may + * be less). + * + * Return 0 on success, -1 on failure and 1 when not all data could be sent + * because the socket would block and the connection is non-blocking. + */ +static int +pqSendSome(PGconn *conn, int len) +{ + char *ptr = conn->outBuffer; + int remaining = conn->outCount; + int result = 0; + + if (conn->sock == PGINVALID_SOCKET) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("connection not open\n")); + /* Discard queued data; no chance it'll ever be sent */ + conn->outCount = 0; + return -1; + } + + /* while there's still data to send */ + while (len > 0) + { + int sent; + +#ifndef WIN32 + sent = pqsecure_write(conn, ptr, len); +#else + + /* + * Windows can fail on large sends, per KB article Q201213. The + * failure-point appears to be different in different versions of + * Windows, but 64k should always be safe. + */ + sent = pqsecure_write(conn, ptr, Min(len, 65536)); +#endif + + if (sent < 0) + { + /* Anything except EAGAIN/EWOULDBLOCK/EINTR is trouble */ + switch (SOCK_ERRNO) + { +#ifdef EAGAIN + case EAGAIN: + break; +#endif +#if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN)) + case EWOULDBLOCK: + break; +#endif + case EINTR: + continue; + + default: + /* pqsecure_write set the error message for us */ + + /* + * We used to close the socket here, but that's a bad idea + * since there might be unread data waiting (typically, a + * NOTICE message from the backend telling us it's + * committing hara-kiri...). Leave the socket open until + * pqReadData finds no more data can be read. But abandon + * attempt to send data. + */ + conn->outCount = 0; + return -1; + } + } + else + { + ptr += sent; + len -= sent; + remaining -= sent; + } + + if (len > 0) + { + /* + * We didn't send it all, wait till we can send more. + * + * There are scenarios in which we can't send data because the + * communications channel is full, but we cannot expect the server + * to clear the channel eventually because it's blocked trying to + * send data to us. (This can happen when we are sending a large + * amount of COPY data, and the server has generated lots of + * NOTICE responses.) To avoid a deadlock situation, we must be + * prepared to accept and buffer incoming data before we try + * again. Furthermore, it is possible that such incoming data + * might not arrive until after we've gone to sleep. Therefore, + * we wait for either read ready or write ready. + * + * In non-blocking mode, we don't wait here directly, but return 1 + * to indicate that data is still pending. The caller should wait + * for both read and write ready conditions, and call + * PQconsumeInput() on read ready, but just in case it doesn't, we + * call pqReadData() ourselves before returning. That's not + * enough if the data has not arrived yet, but it's the best we + * can do, and works pretty well in practice. (The documentation + * used to say that you only need to wait for write-ready, so + * there are still plenty of applications like that out there.) + */ + if (pqReadData(conn) < 0) + { + result = -1; /* error message already set up */ + break; + } + + if (pqIsnonblocking(conn)) + { + result = 1; + break; + } + + if (pqWait(TRUE, TRUE, conn)) + { + result = -1; + break; + } + } + } + + /* shift the remaining contents of the buffer */ + if (remaining > 0) + memmove(conn->outBuffer, ptr, remaining); + conn->outCount = remaining; + + return result; +} + + +/* + * pqFlush: send any data waiting in the output buffer + * + * Return 0 on success, -1 on failure and 1 when not all data could be sent + * because the socket would block and the connection is non-blocking. + */ +int +pqFlush(PGconn *conn) +{ + if (conn->Pfdebug) + fflush(conn->Pfdebug); + + if (conn->outCount > 0) + return pqSendSome(conn, conn->outCount); + + return 0; +} + + +/* + * pqWait: wait until we can read or write the connection socket + * + * JAB: If SSL enabled and used and forRead, buffered bytes short-circuit the + * call to select(). + * + * We also stop waiting and return if the kernel flags an exception condition + * on the socket. The actual error condition will be detected and reported + * when the caller tries to read or write the socket. + */ +int +pqWait(int forRead, int forWrite, PGconn *conn) +{ + return pqWaitTimed(forRead, forWrite, conn, (time_t) -1); +} + +/* + * pqWaitTimed: wait, but not past finish_time. + * + * If finish_time is exceeded then we return failure (EOF). This is like + * the response for a kernel exception because we don't want the caller + * to try to read/write in that case. + * + * finish_time = ((time_t) -1) disables the wait limit. + */ +int +pqWaitTimed(int forRead, int forWrite, PGconn *conn, time_t finish_time) +{ + int result; + + result = pqSocketCheck(conn, forRead, forWrite, finish_time); + + if (result < 0) + return EOF; /* errorMessage is already set */ + + if (result == 0) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("timeout expired\n")); + return EOF; + } + + return 0; +} + +/* + * pqReadReady: is select() saying the file is ready to read? + * Returns -1 on failure, 0 if not ready, 1 if ready. + */ +int +pqReadReady(PGconn *conn) +{ + return pqSocketCheck(conn, 1, 0, (time_t) 0); +} + +/* + * pqWriteReady: is select() saying the file is ready to write? + * Returns -1 on failure, 0 if not ready, 1 if ready. + */ +int +pqWriteReady(PGconn *conn) +{ + return pqSocketCheck(conn, 0, 1, (time_t) 0); +} + +/* + * Checks a socket, using poll or select, for data to be read, written, + * or both. Returns >0 if one or more conditions are met, 0 if it timed + * out, -1 if an error occurred. + * + * If SSL is in use, the SSL buffer is checked prior to checking the socket + * for read data directly. + */ +static int +pqSocketCheck(PGconn *conn, int forRead, int forWrite, time_t end_time) +{ + int result; + + if (!conn) + return -1; + if (conn->sock == PGINVALID_SOCKET) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("invalid socket\n")); + return -1; + } + +#ifdef USE_SSL + /* Check for SSL library buffering read bytes */ + if (forRead && conn->ssl_in_use && pgtls_read_pending(conn) > 0) + { + /* short-circuit the select */ + return 1; + } +#endif + + /* We will retry as long as we get EINTR */ + do + result = pqSocketPoll(conn->sock, forRead, forWrite, end_time); + while (result < 0 && SOCK_ERRNO == EINTR); + + if (result < 0) + { + char sebuf[256]; + + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("select() failed: %s\n"), + SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf))); + } + + return result; +} + + +/* + * Check a file descriptor for read and/or write data, possibly waiting. + * If neither forRead nor forWrite are set, immediately return a timeout + * condition (without waiting). Return >0 if condition is met, 0 + * if a timeout occurred, -1 if an error or interrupt occurred. + * + * Timeout is infinite if end_time is -1. Timeout is immediate (no blocking) + * if end_time is 0 (or indeed, any time before now). + */ +static int +pqSocketPoll(int sock, int forRead, int forWrite, time_t end_time) +{ + /* We use poll(2) if available, otherwise select(2) */ +#ifdef HAVE_POLL + struct pollfd input_fd; + int timeout_ms; + + if (!forRead && !forWrite) + return 0; + + input_fd.fd = sock; + input_fd.events = POLLERR; + input_fd.revents = 0; + + if (forRead) + input_fd.events |= POLLIN; + if (forWrite) + input_fd.events |= POLLOUT; + + /* Compute appropriate timeout interval */ + if (end_time == ((time_t) -1)) + timeout_ms = -1; + else + { + time_t now = time(NULL); + + if (end_time > now) + timeout_ms = (end_time - now) * 1000; + else + timeout_ms = 0; + } + + return poll(&input_fd, 1, timeout_ms); +#else /* !HAVE_POLL */ + + fd_set input_mask; + fd_set output_mask; + fd_set except_mask; + struct timeval timeout; + struct timeval *ptr_timeout; + + if (!forRead && !forWrite) + return 0; + + FD_ZERO(&input_mask); + FD_ZERO(&output_mask); + FD_ZERO(&except_mask); + if (forRead) + FD_SET(sock, &input_mask); + + if (forWrite) + FD_SET(sock, &output_mask); + FD_SET(sock, &except_mask); + + /* Compute appropriate timeout interval */ + if (end_time == ((time_t) -1)) + ptr_timeout = NULL; + else + { + time_t now = time(NULL); + + if (end_time > now) + timeout.tv_sec = end_time - now; + else + timeout.tv_sec = 0; + timeout.tv_usec = 0; + ptr_timeout = &timeout; + } + + return select(sock + 1, &input_mask, &output_mask, + &except_mask, ptr_timeout); +#endif /* HAVE_POLL */ +} + + +/* + * A couple of "miscellaneous" multibyte related functions. They used + * to be in fe-print.c but that file is doomed. + */ + +/* + * returns the byte length of the character beginning at s, using the + * specified encoding. + */ +int +PQmblen(const char *s, int encoding) +{ + return pg_encoding_mblen(encoding, s); +} + +/* + * returns the display length of the character beginning at s, using the + * specified encoding. + */ +int +PQdsplen(const char *s, int encoding) +{ + return pg_encoding_dsplen(encoding, s); +} + +/* + * Get encoding id from environment variable PGCLIENTENCODING. + */ +int +PQenv2encoding(void) +{ + char *str; + int encoding = PG_SQL_ASCII; + + str = getenv("PGCLIENTENCODING"); + if (str && *str != '\0') + { + encoding = pg_char_to_encoding(str); + if (encoding < 0) + encoding = PG_SQL_ASCII; + } + return encoding; +} + + +#ifdef ENABLE_NLS + +static void +libpq_binddomain() +{ + static bool already_bound = false; + + if (!already_bound) + { + /* bindtextdomain() does not preserve errno */ +#ifdef WIN32 + int save_errno = GetLastError(); +#else + int save_errno = errno; +#endif + const char *ldir; + + already_bound = true; + /* No relocatable lookup here because the binary could be anywhere */ + ldir = getenv("PGLOCALEDIR"); + if (!ldir) + ldir = LOCALEDIR; + bindtextdomain(PG_TEXTDOMAIN("libpq"), ldir); +#ifdef WIN32 + SetLastError(save_errno); +#else + errno = save_errno; +#endif + } +} + +char * +libpq_gettext(const char *msgid) +{ + libpq_binddomain(); + return dgettext(PG_TEXTDOMAIN("libpq"), msgid); +} + +char * +libpq_ngettext(const char *msgid, const char *msgid_plural, unsigned long n) +{ + libpq_binddomain(); + return dngettext(PG_TEXTDOMAIN("libpq"), msgid, msgid_plural, n); +} + +#endif /* ENABLE_NLS */ diff --git a/libpq/fe-print.c b/libpq/fe-print.c new file mode 100644 index 0000000..e596a51 --- /dev/null +++ b/libpq/fe-print.c @@ -0,0 +1,761 @@ +/*------------------------------------------------------------------------- + * + * fe-print.c + * functions for pretty-printing query results + * + * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * These functions were formerly part of fe-exec.c, but they + * didn't really belong there. + * + * IDENTIFICATION + * src/interfaces/libpq/fe-print.c + * + *------------------------------------------------------------------------- + */ +#include "postgres_fe.h" + +#include + +#ifdef WIN32 +#include "win32.h" +#else +#include +#include +#endif + +#ifdef HAVE_TERMIOS_H +#include +#else +#ifndef WIN32 +#include +#endif +#endif + +#include "libpq-fe.h" +#include "libpq-int.h" + + +static void do_field(const PQprintOpt *po, const PGresult *res, + const int i, const int j, const int fs_len, + char **fields, + const int nFields, const char **fieldNames, + unsigned char *fieldNotNum, int *fieldMax, + const int fieldMaxLen, FILE *fout); +static char *do_header(FILE *fout, const PQprintOpt *po, const int nFields, + int *fieldMax, const char **fieldNames, unsigned char *fieldNotNum, + const int fs_len, const PGresult *res); +static void output_row(FILE *fout, const PQprintOpt *po, const int nFields, char **fields, + unsigned char *fieldNotNum, int *fieldMax, char *border, + const int row_index); +static void fill(int length, int max, char filler, FILE *fp); + +/* + * PQprint() + * + * Format results of a query for printing. + * + * PQprintOpt is a typedef (structure) that contains + * various flags and options. consult libpq-fe.h for + * details + * + * This function should probably be removed sometime since psql + * doesn't use it anymore. It is unclear to what extent this is used + * by external clients, however. + */ +void +PQprint(FILE *fout, const PGresult *res, const PQprintOpt *po) +{ + int nFields; + + nFields = PQnfields(res); + + if (nFields > 0) + { /* only print rows with at least 1 field. */ + int i, + j; + int nTups; + int *fieldMax = NULL; /* in case we don't use them */ + unsigned char *fieldNotNum = NULL; + char *border = NULL; + char **fields = NULL; + const char **fieldNames; + int fieldMaxLen = 0; + int numFieldName; + int fs_len = strlen(po->fieldSep); + int total_line_length = 0; + int usePipe = 0; + char *pagerenv; + +#if defined(ENABLE_THREAD_SAFETY) && !defined(WIN32) + sigset_t osigset; + bool sigpipe_masked = false; + bool sigpipe_pending; +#endif +#if !defined(ENABLE_THREAD_SAFETY) && !defined(WIN32) + pqsigfunc oldsigpipehandler = NULL; +#endif + +#ifdef TIOCGWINSZ + struct winsize screen_size; +#else + struct winsize + { + int ws_row; + int ws_col; + } screen_size; +#endif + + nTups = PQntuples(res); + if (!(fieldNames = (const char **) calloc(nFields, sizeof(char *)))) + { + fprintf(stderr, libpq_gettext("out of memory\n")); + abort(); + } + if (!(fieldNotNum = (unsigned char *) calloc(nFields, 1))) + { + fprintf(stderr, libpq_gettext("out of memory\n")); + abort(); + } + if (!(fieldMax = (int *) calloc(nFields, sizeof(int)))) + { + fprintf(stderr, libpq_gettext("out of memory\n")); + abort(); + } + for (numFieldName = 0; + po->fieldName && po->fieldName[numFieldName]; + numFieldName++) + ; + for (j = 0; j < nFields; j++) + { + int len; + const char *s = (j < numFieldName && po->fieldName[j][0]) ? + po->fieldName[j] : PQfname(res, j); + + fieldNames[j] = s; + len = s ? strlen(s) : 0; + fieldMax[j] = len; + len += fs_len; + if (len > fieldMaxLen) + fieldMaxLen = len; + total_line_length += len; + } + + total_line_length += nFields * strlen(po->fieldSep) + 1; + + if (fout == NULL) + fout = stdout; + if (po->pager && fout == stdout && isatty(fileno(stdin)) && + isatty(fileno(stdout))) + { + /* + * If we think there'll be more than one screen of output, try to + * pipe to the pager program. + */ +#ifdef TIOCGWINSZ + if (ioctl(fileno(stdout), TIOCGWINSZ, &screen_size) == -1 || + screen_size.ws_col == 0 || + screen_size.ws_row == 0) + { + screen_size.ws_row = 24; + screen_size.ws_col = 80; + } +#else + screen_size.ws_row = 24; + screen_size.ws_col = 80; +#endif + pagerenv = getenv("PAGER"); + /* if PAGER is unset, empty or all-white-space, don't use pager */ + if (pagerenv != NULL && + strspn(pagerenv, " \t\r\n") != strlen(pagerenv) && + !po->html3 && + ((po->expanded && + nTups * (nFields + 1) >= screen_size.ws_row) || + (!po->expanded && + nTups * (total_line_length / screen_size.ws_col + 1) * + (1 + (po->standard != 0)) >= screen_size.ws_row - + (po->header != 0) * + (total_line_length / screen_size.ws_col + 1) * 2 + - (po->header != 0) * 2 /* row count and newline */ + ))) + { + fout = popen(pagerenv, "w"); + if (fout) + { + usePipe = 1; +#ifndef WIN32 +#ifdef ENABLE_THREAD_SAFETY + if (pq_block_sigpipe(&osigset, &sigpipe_pending) == 0) + sigpipe_masked = true; +#else + oldsigpipehandler = pqsignal(SIGPIPE, SIG_IGN); +#endif /* ENABLE_THREAD_SAFETY */ +#endif /* WIN32 */ + } + else + fout = stdout; + } + } + + if (!po->expanded && (po->align || po->html3)) + { + if (!(fields = (char **) calloc(nFields * (nTups + 1), sizeof(char *)))) + { + fprintf(stderr, libpq_gettext("out of memory\n")); + abort(); + } + } + else if (po->header && !po->html3) + { + if (po->expanded) + { + if (po->align) + fprintf(fout, libpq_gettext("%-*s%s Value\n"), + fieldMaxLen - fs_len, libpq_gettext("Field"), po->fieldSep); + else + fprintf(fout, libpq_gettext("%s%sValue\n"), libpq_gettext("Field"), po->fieldSep); + } + else + { + int len = 0; + + for (j = 0; j < nFields; j++) + { + const char *s = fieldNames[j]; + + fputs(s, fout); + len += strlen(s) + fs_len; + if ((j + 1) < nFields) + fputs(po->fieldSep, fout); + } + fputc('\n', fout); + for (len -= fs_len; len--; fputc('-', fout)); + fputc('\n', fout); + } + } + if (po->expanded && po->html3) + { + if (po->caption) + fprintf(fout, "

%s

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

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

\n", + nTups, nFields); + } + for (i = 0; i < nTups; i++) + { + if (po->expanded) + { + if (po->html3) + fprintf(fout, + "\n", + po->tableOpt ? po->tableOpt : "", i); + else + fprintf(fout, libpq_gettext("-- RECORD %d --\n"), i); + } + for (j = 0; j < nFields; j++) + do_field(po, res, i, j, fs_len, fields, nFields, + fieldNames, fieldNotNum, + fieldMax, fieldMaxLen, fout); + if (po->html3 && po->expanded) + fputs("
%d
\n", fout); + } + if (!po->expanded && (po->align || po->html3)) + { + if (po->html3) + { + if (po->header) + { + if (po->caption) + fprintf(fout, + "\n", + po->tableOpt ? po->tableOpt : "", + po->caption); + else + fprintf(fout, + "
%s
\n", + po->tableOpt ? po->tableOpt : "", nTups, nFields); + } + else + fprintf(fout, "
" + "Retrieved %d rows * %d fields" + "
", po->tableOpt ? po->tableOpt : ""); + } + if (po->header) + border = do_header(fout, po, nFields, fieldMax, fieldNames, + fieldNotNum, fs_len, res); + for (i = 0; i < nTups; i++) + output_row(fout, po, nFields, fields, + fieldNotNum, fieldMax, border, i); + free(fields); + if (border) + free(border); + } + if (po->header && !po->html3) + fprintf(fout, "(%d row%s)\n\n", PQntuples(res), + (PQntuples(res) == 1) ? "" : "s"); + free(fieldMax); + free(fieldNotNum); + free((void *) fieldNames); + if (usePipe) + { +#ifdef WIN32 + _pclose(fout); +#else + pclose(fout); + +#ifdef ENABLE_THREAD_SAFETY + /* we can't easily verify if EPIPE occurred, so say it did */ + if (sigpipe_masked) + pq_reset_sigpipe(&osigset, sigpipe_pending, true); +#else + pqsignal(SIGPIPE, oldsigpipehandler); +#endif /* ENABLE_THREAD_SAFETY */ +#endif /* WIN32 */ + } + if (po->html3 && !po->expanded) + fputs("
\n", fout); + } +} + + +static void +do_field(const PQprintOpt *po, const PGresult *res, + const int i, const int j, const int fs_len, + char **fields, + const int nFields, char const ** fieldNames, + unsigned char *fieldNotNum, int *fieldMax, + const int fieldMaxLen, FILE *fout) +{ + const char *pval, + *p; + int plen; + bool skipit; + + plen = PQgetlength(res, i, j); + pval = PQgetvalue(res, i, j); + + if (plen < 1 || !pval || !*pval) + { + if (po->align || po->expanded) + skipit = true; + else + { + skipit = false; + goto efield; + } + } + else + skipit = false; + + if (!skipit) + { + if (po->align && !fieldNotNum[j]) + { + /* Detect whether field contains non-numeric data */ + char ch = '0'; + + for (p = pval; *p; p += PQmblen(p, res->client_encoding)) + { + ch = *p; + if (!((ch >= '0' && ch <= '9') || + ch == '.' || + ch == 'E' || + ch == 'e' || + ch == ' ' || + ch == '-')) + { + fieldNotNum[j] = 1; + break; + } + } + + /* + * Above loop will believe E in first column is numeric; also, we + * insist on a digit in the last column for a numeric. This test + * is still not bulletproof but it handles most cases. + */ + if (*pval == 'E' || *pval == 'e' || + !(ch >= '0' && ch <= '9')) + fieldNotNum[j] = 1; + } + + if (!po->expanded && (po->align || po->html3)) + { + if (plen > fieldMax[j]) + fieldMax[j] = plen; + if (!(fields[i * nFields + j] = (char *) malloc(plen + 1))) + { + fprintf(stderr, libpq_gettext("out of memory\n")); + abort(); + } + strcpy(fields[i * nFields + j], pval); + } + else + { + if (po->expanded) + { + if (po->html3) + fprintf(fout, + "%s" + "%s\n", + fieldNames[j], + fieldNotNum[j] ? "left" : "right", + pval); + else + { + if (po->align) + fprintf(fout, + "%-*s%s %s\n", + fieldMaxLen - fs_len, fieldNames[j], + po->fieldSep, + pval); + else + fprintf(fout, + "%s%s%s\n", + fieldNames[j], po->fieldSep, pval); + } + } + else + { + if (!po->html3) + { + fputs(pval, fout); + efield: + if ((j + 1) < nFields) + fputs(po->fieldSep, fout); + else + fputc('\n', fout); + } + } + } + } +} + + +static char * +do_header(FILE *fout, const PQprintOpt *po, const int nFields, int *fieldMax, + const char **fieldNames, unsigned char *fieldNotNum, + const int fs_len, const PGresult *res) +{ + int j; /* for loop index */ + char *border = NULL; + + if (po->html3) + fputs("", fout); + else + { + int tot = 0; + int n = 0; + char *p = NULL; + + for (; n < nFields; n++) + tot += fieldMax[n] + fs_len + (po->standard ? 2 : 0); + if (po->standard) + tot += fs_len * 2 + 2; + border = malloc(tot + 1); + if (!border) + { + fprintf(stderr, libpq_gettext("out of memory\n")); + abort(); + } + p = border; + if (po->standard) + { + char *fs = po->fieldSep; + + while (*fs++) + *p++ = '+'; + } + for (j = 0; j < nFields; j++) + { + int len; + + for (len = fieldMax[j] + (po->standard ? 2 : 0); len--; *p++ = '-'); + if (po->standard || (j + 1) < nFields) + { + char *fs = po->fieldSep; + + while (*fs++) + *p++ = '+'; + } + } + *p = '\0'; + if (po->standard) + fprintf(fout, "%s\n", border); + } + if (po->standard) + fputs(po->fieldSep, fout); + for (j = 0; j < nFields; j++) + { + const char *s = PQfname(res, j); + + if (po->html3) + { + fprintf(fout, "%s", + fieldNotNum[j] ? "left" : "right", fieldNames[j]); + } + else + { + int n = strlen(s); + + if (n > fieldMax[j]) + fieldMax[j] = n; + if (po->standard) + fprintf(fout, + fieldNotNum[j] ? " %-*s " : " %*s ", + fieldMax[j], s); + else + fprintf(fout, fieldNotNum[j] ? "%-*s" : "%*s", fieldMax[j], s); + if (po->standard || (j + 1) < nFields) + fputs(po->fieldSep, fout); + } + } + if (po->html3) + fputs("\n", fout); + else + fprintf(fout, "\n%s\n", border); + return border; +} + + +static void +output_row(FILE *fout, const PQprintOpt *po, const int nFields, char **fields, + unsigned char *fieldNotNum, int *fieldMax, char *border, + const int row_index) +{ + int field_index; /* for loop index */ + + if (po->html3) + fputs("", fout); + else if (po->standard) + fputs(po->fieldSep, fout); + for (field_index = 0; field_index < nFields; field_index++) + { + char *p = fields[row_index * nFields + field_index]; + + if (po->html3) + fprintf(fout, "%s", + fieldNotNum[field_index] ? "left" : "right", p ? p : ""); + else + { + fprintf(fout, + fieldNotNum[field_index] ? + (po->standard ? " %-*s " : "%-*s") : + (po->standard ? " %*s " : "%*s"), + fieldMax[field_index], + p ? p : ""); + if (po->standard || field_index + 1 < nFields) + fputs(po->fieldSep, fout); + } + if (p) + free(p); + } + if (po->html3) + fputs("", fout); + else if (po->standard) + fprintf(fout, "\n%s", border); + fputc('\n', fout); +} + + + +/* + * really old printing routines + */ + +void +PQdisplayTuples(const PGresult *res, + FILE *fp, /* where to send the output */ + int fillAlign, /* pad the fields with spaces */ + const char *fieldSep, /* field separator */ + int printHeader, /* display headers? */ + int quiet +) +{ +#define DEFAULT_FIELD_SEP " " + + int i, + j; + int nFields; + int nTuples; + int *fLength = NULL; + + if (fieldSep == NULL) + fieldSep = DEFAULT_FIELD_SEP; + + /* Get some useful info about the results */ + nFields = PQnfields(res); + nTuples = PQntuples(res); + + if (fp == NULL) + fp = stdout; + + /* Figure the field lengths to align to */ + /* will be somewhat time consuming for very large results */ + if (fillAlign) + { + fLength = (int *) malloc(nFields * sizeof(int)); + if (!fLength) + { + fprintf(stderr, libpq_gettext("out of memory\n")); + abort(); + } + + for (j = 0; j < nFields; j++) + { + fLength[j] = strlen(PQfname(res, j)); + for (i = 0; i < nTuples; i++) + { + int flen = PQgetlength(res, i, j); + + if (flen > fLength[j]) + fLength[j] = flen; + } + } + } + + if (printHeader) + { + /* first, print out the attribute names */ + for (i = 0; i < nFields; i++) + { + fputs(PQfname(res, i), fp); + if (fillAlign) + fill(strlen(PQfname(res, i)), fLength[i], ' ', fp); + fputs(fieldSep, fp); + } + fprintf(fp, "\n"); + + /* Underline the attribute names */ + for (i = 0; i < nFields; i++) + { + if (fillAlign) + fill(0, fLength[i], '-', fp); + fputs(fieldSep, fp); + } + fprintf(fp, "\n"); + } + + /* next, print out the instances */ + for (i = 0; i < nTuples; i++) + { + for (j = 0; j < nFields; j++) + { + fprintf(fp, "%s", PQgetvalue(res, i, j)); + if (fillAlign) + fill(strlen(PQgetvalue(res, i, j)), fLength[j], ' ', fp); + fputs(fieldSep, fp); + } + fprintf(fp, "\n"); + } + + if (!quiet) + fprintf(fp, "\nQuery returned %d row%s.\n", PQntuples(res), + (PQntuples(res) == 1) ? "" : "s"); + + fflush(fp); + + if (fLength) + free(fLength); +} + + + +void +PQprintTuples(const PGresult *res, + FILE *fout, /* output stream */ + int PrintAttNames, /* print attribute names or not */ + int TerseOutput, /* delimiter bars or not? */ + int colWidth /* width of column, if 0, use variable width */ +) +{ + int nFields; + int nTups; + int i, + j; + char formatString[80]; + char *tborder = NULL; + + nFields = PQnfields(res); + nTups = PQntuples(res); + + if (colWidth > 0) + sprintf(formatString, "%%s %%-%ds", colWidth); + else + sprintf(formatString, "%%s %%s"); + + if (nFields > 0) + { /* only print rows with at least 1 field. */ + + if (!TerseOutput) + { + int width; + + width = nFields * 14; + tborder = (char *) malloc(width + 1); + if (!tborder) + { + fprintf(stderr, libpq_gettext("out of memory\n")); + abort(); + } + for (i = 0; i < width; i++) + tborder[i] = '-'; + tborder[width] = '\0'; + fprintf(fout, "%s\n", tborder); + } + + for (i = 0; i < nFields; i++) + { + if (PrintAttNames) + { + fprintf(fout, formatString, + TerseOutput ? "" : "|", + PQfname(res, i)); + } + } + + if (PrintAttNames) + { + if (TerseOutput) + fprintf(fout, "\n"); + else + fprintf(fout, "|\n%s\n", tborder); + } + + for (i = 0; i < nTups; i++) + { + for (j = 0; j < nFields; j++) + { + const char *pval = PQgetvalue(res, i, j); + + fprintf(fout, formatString, + TerseOutput ? "" : "|", + pval ? pval : ""); + } + if (TerseOutput) + fprintf(fout, "\n"); + else + fprintf(fout, "|\n%s\n", tborder); + } + } + + if (tborder) + free(tborder); +} + + +/* simply send out max-length number of filler characters to fp */ + +static void +fill(int length, int max, char filler, FILE *fp) +{ + int count; + + count = max - length; + while (count-- >= 0) + putc(filler, fp); +} diff --git a/libpq/fe-protocol2.c b/libpq/fe-protocol2.c new file mode 100644 index 0000000..f1b90f3 --- /dev/null +++ b/libpq/fe-protocol2.c @@ -0,0 +1,1623 @@ +/*------------------------------------------------------------------------- + * + * fe-protocol2.c + * functions that are specific to frontend/backend protocol version 2 + * + * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/interfaces/libpq/fe-protocol2.c + * + *------------------------------------------------------------------------- + */ +#include "postgres_fe.h" + +#include +#include + +#include "libpq-fe.h" +#include "libpq-int.h" + + +#ifdef WIN32 +#include "win32.h" +#else +#include +#include +#ifdef HAVE_NETINET_TCP_H +#include +#endif +#include +#endif + + +static int getRowDescriptions(PGconn *conn); +static int getAnotherTuple(PGconn *conn, bool binary); +static int pqGetErrorNotice2(PGconn *conn, bool isError); +static void checkXactStatus(PGconn *conn, const char *cmdTag); +static int getNotify(PGconn *conn); + + +/* + * pqSetenvPoll + * + * Polls the process of passing the values of a standard set of environment + * variables to the backend. + */ +PostgresPollingStatusType +pqSetenvPoll(PGconn *conn) +{ + PGresult *res; + + if (conn == NULL || conn->status == CONNECTION_BAD) + return PGRES_POLLING_FAILED; + + /* Check whether there are any data for us */ + switch (conn->setenv_state) + { + /* These are reading states */ + case SETENV_STATE_CLIENT_ENCODING_WAIT: + case SETENV_STATE_OPTION_WAIT: + case SETENV_STATE_QUERY1_WAIT: + case SETENV_STATE_QUERY2_WAIT: + { + /* Load waiting data */ + int n = pqReadData(conn); + + if (n < 0) + goto error_return; + if (n == 0) + return PGRES_POLLING_READING; + + break; + } + + /* These are writing states, so we just proceed. */ + case SETENV_STATE_CLIENT_ENCODING_SEND: + case SETENV_STATE_OPTION_SEND: + case SETENV_STATE_QUERY1_SEND: + case SETENV_STATE_QUERY2_SEND: + break; + + /* Should we raise an error if called when not active? */ + case SETENV_STATE_IDLE: + return PGRES_POLLING_OK; + + default: + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext( + "invalid setenv state %c, " + "probably indicative of memory corruption\n" + ), + conn->setenv_state); + goto error_return; + } + + /* We will loop here until there is nothing left to do in this call. */ + for (;;) + { + switch (conn->setenv_state) + { + /* + * The _CLIENT_ENCODING_SEND code is slightly different from + * _OPTION_SEND below (e.g., no getenv() call), which is why a + * different state is used. + */ + case SETENV_STATE_CLIENT_ENCODING_SEND: + { + char setQuery[100]; /* note length limit in + * sprintf below */ + const char *val = conn->client_encoding_initial; + + if (val) + { + if (pg_strcasecmp(val, "default") == 0) + sprintf(setQuery, "SET client_encoding = DEFAULT"); + else + sprintf(setQuery, "SET client_encoding = '%.60s'", + val); +#ifdef CONNECTDEBUG + fprintf(stderr, + "Sending client_encoding with %s\n", + setQuery); +#endif + if (!PQsendQuery(conn, setQuery)) + goto error_return; + + conn->setenv_state = SETENV_STATE_CLIENT_ENCODING_WAIT; + } + else + conn->setenv_state = SETENV_STATE_OPTION_SEND; + break; + } + + case SETENV_STATE_OPTION_SEND: + { + /* + * Send SET commands for stuff directed by Environment + * Options. Note: we assume that SET commands won't start + * transaction blocks, even in a 7.3 server with + * autocommit off. + */ + char setQuery[100]; /* note length limit in + * sprintf below */ + + if (conn->next_eo->envName) + { + const char *val; + + if ((val = getenv(conn->next_eo->envName))) + { + if (pg_strcasecmp(val, "default") == 0) + sprintf(setQuery, "SET %s = DEFAULT", + conn->next_eo->pgName); + else + sprintf(setQuery, "SET %s = '%.60s'", + conn->next_eo->pgName, val); +#ifdef CONNECTDEBUG + fprintf(stderr, + "Use environment variable %s to send %s\n", + conn->next_eo->envName, setQuery); +#endif + if (!PQsendQuery(conn, setQuery)) + goto error_return; + + conn->setenv_state = SETENV_STATE_OPTION_WAIT; + } + else + conn->next_eo++; + } + else + { + /* No more options to send, so move on to querying */ + conn->setenv_state = SETENV_STATE_QUERY1_SEND; + } + break; + } + + case SETENV_STATE_CLIENT_ENCODING_WAIT: + { + if (PQisBusy(conn)) + return PGRES_POLLING_READING; + + res = PQgetResult(conn); + + if (res) + { + if (PQresultStatus(res) != PGRES_COMMAND_OK) + { + PQclear(res); + goto error_return; + } + PQclear(res); + /* Keep reading until PQgetResult returns NULL */ + } + else + { + /* Query finished, so send the next option */ + conn->setenv_state = SETENV_STATE_OPTION_SEND; + } + break; + } + + case SETENV_STATE_OPTION_WAIT: + { + if (PQisBusy(conn)) + return PGRES_POLLING_READING; + + res = PQgetResult(conn); + + if (res) + { + if (PQresultStatus(res) != PGRES_COMMAND_OK) + { + PQclear(res); + goto error_return; + } + PQclear(res); + /* Keep reading until PQgetResult returns NULL */ + } + else + { + /* Query finished, so send the next option */ + conn->next_eo++; + conn->setenv_state = SETENV_STATE_OPTION_SEND; + } + break; + } + + case SETENV_STATE_QUERY1_SEND: + { + /* + * Issue query to get information we need. Here we must + * use begin/commit in case autocommit is off by default + * in a 7.3 server. + * + * Note: version() exists in all protocol-2.0-supporting + * backends. In 7.3 it would be safer to write + * pg_catalog.version(), but we can't do that without + * causing problems on older versions. + */ + if (!PQsendQuery(conn, "begin; select version(); end")) + goto error_return; + + conn->setenv_state = SETENV_STATE_QUERY1_WAIT; + return PGRES_POLLING_READING; + } + + case SETENV_STATE_QUERY1_WAIT: + { + if (PQisBusy(conn)) + return PGRES_POLLING_READING; + + res = PQgetResult(conn); + + if (res) + { + char *val; + + if (PQresultStatus(res) == PGRES_COMMAND_OK) + { + /* ignore begin/commit command results */ + PQclear(res); + continue; + } + + if (PQresultStatus(res) != PGRES_TUPLES_OK || + PQntuples(res) != 1) + { + PQclear(res); + goto error_return; + } + + /* + * Extract server version and save as if + * ParameterStatus + */ + val = PQgetvalue(res, 0, 0); + if (val && strncmp(val, "PostgreSQL ", 11) == 0) + { + char *ptr; + + /* strip off PostgreSQL part */ + val += 11; + + /* + * strip off platform part (scribbles on result, + * naughty naughty) + */ + ptr = strchr(val, ' '); + if (ptr) + *ptr = '\0'; + + pqSaveParameterStatus(conn, "server_version", + val); + } + + PQclear(res); + /* Keep reading until PQgetResult returns NULL */ + } + else + { + /* Query finished, move to next */ + conn->setenv_state = SETENV_STATE_QUERY2_SEND; + } + break; + } + + case SETENV_STATE_QUERY2_SEND: + { + const char *query; + + /* + * pg_client_encoding does not exist in pre-7.2 servers. + * So we need to be prepared for an error here. Do *not* + * start a transaction block, except in 7.3 servers where + * we need to prevent autocommit-off from starting a + * transaction anyway. + */ + if (conn->sversion >= 70300 && + conn->sversion < 70400) + query = "begin; select pg_catalog.pg_client_encoding(); end"; + else + query = "select pg_client_encoding()"; + if (!PQsendQuery(conn, query)) + goto error_return; + + conn->setenv_state = SETENV_STATE_QUERY2_WAIT; + return PGRES_POLLING_READING; + } + + case SETENV_STATE_QUERY2_WAIT: + { + if (PQisBusy(conn)) + return PGRES_POLLING_READING; + + res = PQgetResult(conn); + + if (res) + { + const char *val; + + if (PQresultStatus(res) == PGRES_COMMAND_OK) + { + /* ignore begin/commit command results */ + PQclear(res); + continue; + } + + if (PQresultStatus(res) == PGRES_TUPLES_OK && + PQntuples(res) == 1) + { + /* Extract client encoding and save it */ + val = PQgetvalue(res, 0, 0); + if (val && *val) /* null should not happen, but */ + pqSaveParameterStatus(conn, "client_encoding", + val); + } + else + { + /* + * Error: presumably function not available, so + * use PGCLIENTENCODING or SQL_ASCII as the + * fallback. + */ + val = getenv("PGCLIENTENCODING"); + if (val && *val) + pqSaveParameterStatus(conn, "client_encoding", + val); + else + pqSaveParameterStatus(conn, "client_encoding", + "SQL_ASCII"); + } + + PQclear(res); + /* Keep reading until PQgetResult returns NULL */ + } + else + { + /* Query finished, so we're done */ + conn->setenv_state = SETENV_STATE_IDLE; + return PGRES_POLLING_OK; + } + break; + } + + default: + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("invalid state %c, " + "probably indicative of memory corruption\n"), + conn->setenv_state); + goto error_return; + } + } + + /* Unreachable */ + +error_return: + conn->setenv_state = SETENV_STATE_IDLE; + return PGRES_POLLING_FAILED; +} + + +/* + * parseInput: if appropriate, parse input data from backend + * until input is exhausted or a stopping state is reached. + * Note that this function will NOT attempt to read more data from the backend. + */ +void +pqParseInput2(PGconn *conn) +{ + char id; + + /* + * Loop to parse successive complete messages available in the buffer. + */ + for (;;) + { + /* + * Quit if in COPY_OUT state: we expect raw data from the server until + * PQendcopy is called. Don't try to parse it according to the normal + * protocol. (This is bogus. The data lines ought to be part of the + * protocol and have identifying leading characters.) + */ + if (conn->asyncStatus == PGASYNC_COPY_OUT) + return; + + /* + * OK to try to read a message type code. + */ + conn->inCursor = conn->inStart; + if (pqGetc(&id, conn)) + return; + + /* + * NOTIFY and NOTICE messages can happen in any state besides COPY + * OUT; always process them right away. + * + * Most other messages should only be processed while in BUSY state. + * (In particular, in READY state we hold off further parsing until + * the application collects the current PGresult.) + * + * However, if the state is IDLE then we got trouble; we need to deal + * with the unexpected message somehow. + */ + if (id == 'A') + { + if (getNotify(conn)) + return; + } + else if (id == 'N') + { + if (pqGetErrorNotice2(conn, false)) + return; + } + else if (conn->asyncStatus != PGASYNC_BUSY) + { + /* If not IDLE state, just wait ... */ + if (conn->asyncStatus != PGASYNC_IDLE) + return; + + /* + * Unexpected message in IDLE state; need to recover somehow. + * ERROR messages are displayed using the notice processor; + * anything else is just dropped on the floor after displaying a + * suitable warning notice. (An ERROR is very possibly the + * backend telling us why it is about to close the connection, so + * we don't want to just discard it...) + */ + if (id == 'E') + { + if (pqGetErrorNotice2(conn, false /* treat as notice */ )) + return; + } + else + { + pqInternalNotice(&conn->noticeHooks, + "message type 0x%02x arrived from server while idle", + id); + /* Discard the unexpected message; good idea?? */ + conn->inStart = conn->inEnd; + break; + } + } + else + { + /* + * In BUSY state, we can process everything. + */ + switch (id) + { + case 'C': /* command complete */ + if (pqGets(&conn->workBuffer, conn)) + return; + if (conn->result == NULL) + { + conn->result = PQmakeEmptyPGresult(conn, + PGRES_COMMAND_OK); + if (!conn->result) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("out of memory")); + pqSaveErrorResult(conn); + } + } + if (conn->result) + { + strlcpy(conn->result->cmdStatus, conn->workBuffer.data, + CMDSTATUS_LEN); + } + checkXactStatus(conn, conn->workBuffer.data); + conn->asyncStatus = PGASYNC_READY; + break; + case 'E': /* error return */ + if (pqGetErrorNotice2(conn, true)) + return; + conn->asyncStatus = PGASYNC_READY; + break; + case 'Z': /* backend is ready for new query */ + conn->asyncStatus = PGASYNC_IDLE; + break; + case 'I': /* empty query */ + /* read and throw away the closing '\0' */ + if (pqGetc(&id, conn)) + return; + if (id != '\0') + pqInternalNotice(&conn->noticeHooks, + "unexpected character %c following empty query response (\"I\" message)", + id); + if (conn->result == NULL) + { + conn->result = PQmakeEmptyPGresult(conn, + PGRES_EMPTY_QUERY); + if (!conn->result) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("out of memory")); + pqSaveErrorResult(conn); + } + } + conn->asyncStatus = PGASYNC_READY; + break; + case 'K': /* secret key data from the backend */ + + /* + * This is expected only during backend startup, but it's + * just as easy to handle it as part of the main loop. + * Save the data and continue processing. + */ + if (pqGetInt(&(conn->be_pid), 4, conn)) + return; + if (pqGetInt(&(conn->be_key), 4, conn)) + return; + break; + case 'P': /* synchronous (normal) portal */ + if (pqGets(&conn->workBuffer, conn)) + return; + /* We pretty much ignore this message type... */ + break; + case 'T': /* row descriptions (start of query results) */ + if (conn->result == NULL) + { + /* First 'T' in a query sequence */ + if (getRowDescriptions(conn)) + return; + /* getRowDescriptions() moves inStart itself */ + continue; + } + else + { + /* + * A new 'T' message is treated as the start of + * another PGresult. (It is not clear that this is + * really possible with the current backend.) We stop + * parsing until the application accepts the current + * result. + */ + conn->asyncStatus = PGASYNC_READY; + return; + } + break; + case 'D': /* ASCII data tuple */ + if (conn->result != NULL) + { + /* Read another tuple of a normal query response */ + if (getAnotherTuple(conn, FALSE)) + return; + /* getAnotherTuple() moves inStart itself */ + continue; + } + else + { + pqInternalNotice(&conn->noticeHooks, + "server sent data (\"D\" message) without prior row description (\"T\" message)"); + /* Discard the unexpected message; good idea?? */ + conn->inStart = conn->inEnd; + return; + } + break; + case 'B': /* Binary data tuple */ + if (conn->result != NULL) + { + /* Read another tuple of a normal query response */ + if (getAnotherTuple(conn, TRUE)) + return; + /* getAnotherTuple() moves inStart itself */ + continue; + } + else + { + pqInternalNotice(&conn->noticeHooks, + "server sent binary data (\"B\" message) without prior row description (\"T\" message)"); + /* Discard the unexpected message; good idea?? */ + conn->inStart = conn->inEnd; + return; + } + break; + case 'G': /* Start Copy In */ + conn->asyncStatus = PGASYNC_COPY_IN; + break; + case 'H': /* Start Copy Out */ + conn->asyncStatus = PGASYNC_COPY_OUT; + break; + + /* + * Don't need to process CopyBothResponse here because it + * never arrives from the server during protocol 2.0. + */ + default: + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext( + "unexpected response from server; first received character was \"%c\"\n"), + id); + /* build an error result holding the error message */ + pqSaveErrorResult(conn); + /* Discard the unexpected message; good idea?? */ + conn->inStart = conn->inEnd; + conn->asyncStatus = PGASYNC_READY; + return; + } /* switch on protocol character */ + } + /* Successfully consumed this message */ + conn->inStart = conn->inCursor; + } +} + +/* + * parseInput subroutine to read a 'T' (row descriptions) message. + * We build a PGresult structure containing the attribute data. + * Returns: 0 if completed message, EOF if error or not enough data + * received yet. + * + * Note that if we run out of data, we have to suspend and reprocess + * the message after more data is received. Otherwise, conn->inStart + * must get advanced past the processed data. + */ +static int +getRowDescriptions(PGconn *conn) +{ + PGresult *result; + int nfields; + const char *errmsg; + int i; + + result = PQmakeEmptyPGresult(conn, PGRES_TUPLES_OK); + if (!result) + { + errmsg = NULL; /* means "out of memory", see below */ + goto advance_and_error; + } + + /* parseInput already read the 'T' label. */ + /* the next two bytes are the number of fields */ + if (pqGetInt(&(result->numAttributes), 2, conn)) + goto EOFexit; + nfields = result->numAttributes; + + /* allocate space for the attribute descriptors */ + if (nfields > 0) + { + result->attDescs = (PGresAttDesc *) + pqResultAlloc(result, nfields * sizeof(PGresAttDesc), TRUE); + if (!result->attDescs) + { + errmsg = NULL; /* means "out of memory", see below */ + goto advance_and_error; + } + MemSet(result->attDescs, 0, nfields * sizeof(PGresAttDesc)); + } + + /* get type info */ + for (i = 0; i < nfields; i++) + { + int typid; + int typlen; + int atttypmod; + + if (pqGets(&conn->workBuffer, conn) || + pqGetInt(&typid, 4, conn) || + pqGetInt(&typlen, 2, conn) || + pqGetInt(&atttypmod, 4, conn)) + goto EOFexit; + + /* + * Since pqGetInt treats 2-byte integers as unsigned, we need to + * coerce the result to signed form. + */ + typlen = (int) ((int16) typlen); + + result->attDescs[i].name = pqResultStrdup(result, + conn->workBuffer.data); + if (!result->attDescs[i].name) + { + errmsg = NULL; /* means "out of memory", see below */ + goto advance_and_error; + } + result->attDescs[i].tableid = 0; + result->attDescs[i].columnid = 0; + result->attDescs[i].format = 0; + result->attDescs[i].typid = typid; + result->attDescs[i].typlen = typlen; + result->attDescs[i].atttypmod = atttypmod; + } + + /* Success! */ + conn->result = result; + + /* Advance inStart to show that the "T" message has been processed. */ + conn->inStart = conn->inCursor; + + /* + * We could perform additional setup for the new result set here, but for + * now there's nothing else to do. + */ + + /* And we're done. */ + return 0; + +advance_and_error: + + /* + * Discard the failed message. Unfortunately we don't know for sure where + * the end is, so just throw away everything in the input buffer. This is + * not very desirable but it's the best we can do in protocol v2. + */ + conn->inStart = conn->inEnd; + + /* + * Replace partially constructed result with an error result. First + * discard the old result to try to win back some memory. + */ + pqClearAsyncResult(conn); + + /* + * If preceding code didn't provide an error message, assume "out of + * memory" was meant. The advantage of having this special case is that + * freeing the old result first greatly improves the odds that gettext() + * will succeed in providing a translation. + */ + if (!errmsg) + errmsg = libpq_gettext("out of memory for query result"); + + printfPQExpBuffer(&conn->errorMessage, "%s\n", errmsg); + + /* + * XXX: if PQmakeEmptyPGresult() fails, there's probably not much we can + * do to recover... + */ + conn->result = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR); + conn->asyncStatus = PGASYNC_READY; + +EOFexit: + if (result && result != conn->result) + PQclear(result); + return EOF; +} + +/* + * parseInput subroutine to read a 'B' or 'D' (row data) message. + * We fill rowbuf with column pointers and then call the row processor. + * Returns: 0 if completed message, EOF if error or not enough data + * received yet. + * + * Note that if we run out of data, we have to suspend and reprocess + * the message after more data is received. Otherwise, conn->inStart + * must get advanced past the processed data. + */ +static int +getAnotherTuple(PGconn *conn, bool binary) +{ + PGresult *result = conn->result; + int nfields = result->numAttributes; + const char *errmsg; + PGdataValue *rowbuf; + + /* the backend sends us a bitmap of which attributes are null */ + char std_bitmap[64]; /* used unless it doesn't fit */ + char *bitmap = std_bitmap; + int i; + size_t nbytes; /* the number of bytes in bitmap */ + char bmap; /* One byte of the bitmap */ + int bitmap_index; /* Its index */ + int bitcnt; /* number of bits examined in current byte */ + int vlen; /* length of the current field value */ + + /* Resize row buffer if needed */ + rowbuf = conn->rowBuf; + if (nfields > conn->rowBufLen) + { + rowbuf = (PGdataValue *) realloc(rowbuf, + nfields * sizeof(PGdataValue)); + if (!rowbuf) + { + errmsg = NULL; /* means "out of memory", see below */ + goto advance_and_error; + } + conn->rowBuf = rowbuf; + conn->rowBufLen = nfields; + } + + /* Save format specifier */ + result->binary = binary; + + /* + * If it's binary, fix the column format indicators. We assume the + * backend will consistently send either B or D, not a mix. + */ + if (binary) + { + for (i = 0; i < nfields; i++) + result->attDescs[i].format = 1; + } + + /* Get the null-value bitmap */ + nbytes = (nfields + BITS_PER_BYTE - 1) / BITS_PER_BYTE; + /* malloc() only for unusually large field counts... */ + if (nbytes > sizeof(std_bitmap)) + { + bitmap = (char *) malloc(nbytes); + if (!bitmap) + { + errmsg = NULL; /* means "out of memory", see below */ + goto advance_and_error; + } + } + + if (pqGetnchar(bitmap, nbytes, conn)) + goto EOFexit; + + /* Scan the fields */ + bitmap_index = 0; + bmap = bitmap[bitmap_index]; + bitcnt = 0; + + for (i = 0; i < nfields; i++) + { + /* get the value length */ + if (!(bmap & 0200)) + vlen = NULL_LEN; + else if (pqGetInt(&vlen, 4, conn)) + goto EOFexit; + else + { + if (!binary) + vlen = vlen - 4; + if (vlen < 0) + vlen = 0; + } + rowbuf[i].len = vlen; + + /* + * rowbuf[i].value always points to the next address in the data + * buffer even if the value is NULL. This allows row processors to + * estimate data sizes more easily. + */ + rowbuf[i].value = conn->inBuffer + conn->inCursor; + + /* Skip over the data value */ + if (vlen > 0) + { + if (pqSkipnchar(vlen, conn)) + goto EOFexit; + } + + /* advance the bitmap stuff */ + bitcnt++; + if (bitcnt == BITS_PER_BYTE) + { + bitmap_index++; + bmap = bitmap[bitmap_index]; + bitcnt = 0; + } + else + bmap <<= 1; + } + + /* Release bitmap now if we allocated it */ + if (bitmap != std_bitmap) + free(bitmap); + bitmap = NULL; + + /* Advance inStart to show that the "D" message has been processed. */ + conn->inStart = conn->inCursor; + + /* Process the collected row */ + errmsg = NULL; + if (pqRowProcessor(conn, &errmsg)) + return 0; /* normal, successful exit */ + + goto set_error_result; /* pqRowProcessor failed, report it */ + +advance_and_error: + + /* + * Discard the failed message. Unfortunately we don't know for sure where + * the end is, so just throw away everything in the input buffer. This is + * not very desirable but it's the best we can do in protocol v2. + */ + conn->inStart = conn->inEnd; + +set_error_result: + + /* + * Replace partially constructed result with an error result. First + * discard the old result to try to win back some memory. + */ + pqClearAsyncResult(conn); + + /* + * If preceding code didn't provide an error message, assume "out of + * memory" was meant. The advantage of having this special case is that + * freeing the old result first greatly improves the odds that gettext() + * will succeed in providing a translation. + */ + if (!errmsg) + errmsg = libpq_gettext("out of memory for query result"); + + printfPQExpBuffer(&conn->errorMessage, "%s\n", errmsg); + + /* + * XXX: if PQmakeEmptyPGresult() fails, there's probably not much we can + * do to recover... + */ + conn->result = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR); + conn->asyncStatus = PGASYNC_READY; + +EOFexit: + if (bitmap != NULL && bitmap != std_bitmap) + free(bitmap); + return EOF; +} + + +/* + * Attempt to read an Error or Notice response message. + * This is possible in several places, so we break it out as a subroutine. + * Entry: 'E' or 'N' message type has already been consumed. + * Exit: returns 0 if successfully consumed message. + * returns EOF if not enough data. + */ +static int +pqGetErrorNotice2(PGconn *conn, bool isError) +{ + PGresult *res = NULL; + PQExpBufferData workBuf; + char *startp; + char *splitp; + + /* + * Since the message might be pretty long, we create a temporary + * PQExpBuffer rather than using conn->workBuffer. workBuffer is intended + * for stuff that is expected to be short. + */ + initPQExpBuffer(&workBuf); + if (pqGets(&workBuf, conn)) + goto failure; + + /* + * Make a PGresult to hold the message. We temporarily lie about the + * result status, so that PQmakeEmptyPGresult doesn't uselessly copy + * conn->errorMessage. + * + * NB: This allocation can fail, if you run out of memory. The rest of the + * function handles that gracefully, and we still try to set the error + * message as the connection's error message. + */ + res = PQmakeEmptyPGresult(conn, PGRES_EMPTY_QUERY); + if (res) + { + res->resultStatus = isError ? PGRES_FATAL_ERROR : PGRES_NONFATAL_ERROR; + res->errMsg = pqResultStrdup(res, workBuf.data); + } + + /* + * Break the message into fields. We can't do very much here, but we can + * split the severity code off, and remove trailing newlines. Also, we use + * the heuristic that the primary message extends only to the first + * newline --- anything after that is detail message. (In some cases it'd + * be better classed as hint, but we can hardly be expected to guess that + * here.) + */ + while (workBuf.len > 0 && workBuf.data[workBuf.len - 1] == '\n') + workBuf.data[--workBuf.len] = '\0'; + splitp = strstr(workBuf.data, ": "); + if (splitp) + { + /* what comes before the colon is severity */ + *splitp = '\0'; + pqSaveMessageField(res, PG_DIAG_SEVERITY, workBuf.data); + startp = splitp + 3; + } + else + { + /* can't find a colon? oh well... */ + startp = workBuf.data; + } + splitp = strchr(startp, '\n'); + if (splitp) + { + /* what comes before the newline is primary message */ + *splitp++ = '\0'; + pqSaveMessageField(res, PG_DIAG_MESSAGE_PRIMARY, startp); + /* the rest is detail; strip any leading whitespace */ + while (*splitp && isspace((unsigned char) *splitp)) + splitp++; + pqSaveMessageField(res, PG_DIAG_MESSAGE_DETAIL, splitp); + } + else + { + /* single-line message, so all primary */ + pqSaveMessageField(res, PG_DIAG_MESSAGE_PRIMARY, startp); + } + + /* + * Either save error as current async result, or just emit the notice. + * Also, if it's an error and we were in a transaction block, assume the + * server has now gone to error-in-transaction state. + */ + if (isError) + { + pqClearAsyncResult(conn); + conn->result = res; + resetPQExpBuffer(&conn->errorMessage); + if (res && !PQExpBufferDataBroken(workBuf) && res->errMsg) + appendPQExpBufferStr(&conn->errorMessage, res->errMsg); + else + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("out of memory")); + if (conn->xactStatus == PQTRANS_INTRANS) + conn->xactStatus = PQTRANS_INERROR; + } + else + { + if (res) + { + if (res->noticeHooks.noticeRec != NULL) + (*res->noticeHooks.noticeRec) (res->noticeHooks.noticeRecArg, res); + PQclear(res); + } + } + + termPQExpBuffer(&workBuf); + return 0; + +failure: + if (res) + PQclear(res); + termPQExpBuffer(&workBuf); + return EOF; +} + +/* + * checkXactStatus - attempt to track transaction-block status of server + * + * This is called each time we receive a command-complete message. By + * watching for messages from BEGIN/COMMIT/ROLLBACK commands, we can do + * a passable job of tracking the server's xact status. BUT: this does + * not work at all on 7.3 servers with AUTOCOMMIT OFF. (Man, was that + * feature ever a mistake.) Caveat user. + * + * The tags known here are all those used as far back as 7.0; is it worth + * adding those from even-older servers? + */ +static void +checkXactStatus(PGconn *conn, const char *cmdTag) +{ + if (strcmp(cmdTag, "BEGIN") == 0) + conn->xactStatus = PQTRANS_INTRANS; + else if (strcmp(cmdTag, "COMMIT") == 0) + conn->xactStatus = PQTRANS_IDLE; + else if (strcmp(cmdTag, "ROLLBACK") == 0) + conn->xactStatus = PQTRANS_IDLE; + else if (strcmp(cmdTag, "START TRANSACTION") == 0) /* 7.3 only */ + conn->xactStatus = PQTRANS_INTRANS; + + /* + * Normally we get into INERROR state by detecting an Error message. + * However, if we see one of these tags then we know for sure the server + * is in abort state ... + */ + else if (strcmp(cmdTag, "*ABORT STATE*") == 0) /* pre-7.3 only */ + conn->xactStatus = PQTRANS_INERROR; +} + +/* + * Attempt to read a Notify response message. + * This is possible in several places, so we break it out as a subroutine. + * Entry: 'A' message type and length have already been consumed. + * Exit: returns 0 if successfully consumed Notify message. + * returns EOF if not enough data. + */ +static int +getNotify(PGconn *conn) +{ + int be_pid; + int nmlen; + PGnotify *newNotify; + + if (pqGetInt(&be_pid, 4, conn)) + return EOF; + if (pqGets(&conn->workBuffer, conn)) + return EOF; + + /* + * Store the relation name right after the PQnotify structure so it can + * all be freed at once. We don't use NAMEDATALEN because we don't want + * to tie this interface to a specific server name length. + */ + nmlen = strlen(conn->workBuffer.data); + newNotify = (PGnotify *) malloc(sizeof(PGnotify) + nmlen + 1); + if (newNotify) + { + newNotify->relname = (char *) newNotify + sizeof(PGnotify); + strcpy(newNotify->relname, conn->workBuffer.data); + /* fake up an empty-string extra field */ + newNotify->extra = newNotify->relname + nmlen; + newNotify->be_pid = be_pid; + newNotify->next = NULL; + if (conn->notifyTail) + conn->notifyTail->next = newNotify; + else + conn->notifyHead = newNotify; + conn->notifyTail = newNotify; + } + + return 0; +} + + +/* + * PQgetCopyData - read a row of data from the backend during COPY OUT + * + * If successful, sets *buffer to point to a malloc'd row of data, and + * returns row length (always > 0) as result. + * Returns 0 if no row available yet (only possible if async is true), + * -1 if end of copy (consult PQgetResult), or -2 if error (consult + * PQerrorMessage). + */ +int +pqGetCopyData2(PGconn *conn, char **buffer, int async) +{ + bool found; + int msgLength; + + for (;;) + { + /* + * Do we have a complete line of data? + */ + conn->inCursor = conn->inStart; + found = false; + while (conn->inCursor < conn->inEnd) + { + char c = conn->inBuffer[conn->inCursor++]; + + if (c == '\n') + { + found = true; + break; + } + } + if (!found) + goto nodata; + msgLength = conn->inCursor - conn->inStart; + + /* + * If it's the end-of-data marker, consume it, exit COPY_OUT mode, and + * let caller read status with PQgetResult(). + */ + if (msgLength == 3 && + strncmp(&conn->inBuffer[conn->inStart], "\\.\n", 3) == 0) + { + conn->inStart = conn->inCursor; + conn->asyncStatus = PGASYNC_BUSY; + return -1; + } + + /* + * Pass the line back to the caller. + */ + *buffer = (char *) malloc(msgLength + 1); + if (*buffer == NULL) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("out of memory\n")); + return -2; + } + memcpy(*buffer, &conn->inBuffer[conn->inStart], msgLength); + (*buffer)[msgLength] = '\0'; /* Add terminating null */ + + /* Mark message consumed */ + conn->inStart = conn->inCursor; + + return msgLength; + +nodata: + /* Don't block if async read requested */ + if (async) + return 0; + /* Need to load more data */ + if (pqWait(TRUE, FALSE, conn) || + pqReadData(conn) < 0) + return -2; + } +} + + +/* + * PQgetline - gets a newline-terminated string from the backend. + * + * See fe-exec.c for documentation. + */ +int +pqGetline2(PGconn *conn, char *s, int maxlen) +{ + int result = 1; /* return value if buffer overflows */ + + if (conn->sock == PGINVALID_SOCKET || + conn->asyncStatus != PGASYNC_COPY_OUT) + { + *s = '\0'; + return EOF; + } + + /* + * Since this is a purely synchronous routine, we don't bother to maintain + * conn->inCursor; there is no need to back up. + */ + while (maxlen > 1) + { + if (conn->inStart < conn->inEnd) + { + char c = conn->inBuffer[conn->inStart++]; + + if (c == '\n') + { + result = 0; /* success exit */ + break; + } + *s++ = c; + maxlen--; + } + else + { + /* need to load more data */ + if (pqWait(TRUE, FALSE, conn) || + pqReadData(conn) < 0) + { + result = EOF; + break; + } + } + } + *s = '\0'; + + return result; +} + +/* + * PQgetlineAsync - gets a COPY data row without blocking. + * + * See fe-exec.c for documentation. + */ +int +pqGetlineAsync2(PGconn *conn, char *buffer, int bufsize) +{ + int avail; + + if (conn->asyncStatus != PGASYNC_COPY_OUT) + return -1; /* we are not doing a copy... */ + + /* + * Move data from libpq's buffer to the caller's. We want to accept data + * only in units of whole lines, not partial lines. This ensures that we + * can recognize the terminator line "\\.\n". (Otherwise, if it happened + * to cross a packet/buffer boundary, we might hand the first one or two + * characters off to the caller, which we shouldn't.) + */ + + conn->inCursor = conn->inStart; + + avail = bufsize; + while (avail > 0 && conn->inCursor < conn->inEnd) + { + char c = conn->inBuffer[conn->inCursor++]; + + *buffer++ = c; + --avail; + if (c == '\n') + { + /* Got a complete line; mark the data removed from libpq */ + conn->inStart = conn->inCursor; + /* Is it the endmarker line? */ + if (bufsize - avail == 3 && buffer[-3] == '\\' && buffer[-2] == '.') + return -1; + /* No, return the data line to the caller */ + return bufsize - avail; + } + } + + /* + * We don't have a complete line. We'd prefer to leave it in libpq's + * buffer until the rest arrives, but there is a special case: what if the + * line is longer than the buffer the caller is offering us? In that case + * we'd better hand over a partial line, else we'd get into an infinite + * loop. Do this in a way that ensures we can't misrecognize a terminator + * line later: leave last 3 characters in libpq buffer. + */ + if (avail == 0 && bufsize > 3) + { + conn->inStart = conn->inCursor - 3; + return bufsize - 3; + } + return 0; +} + +/* + * PQendcopy + * + * See fe-exec.c for documentation. + */ +int +pqEndcopy2(PGconn *conn) +{ + PGresult *result; + + if (conn->asyncStatus != PGASYNC_COPY_IN && + conn->asyncStatus != PGASYNC_COPY_OUT) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("no COPY in progress\n")); + return 1; + } + + /* + * make sure no data is waiting to be sent, abort if we are non-blocking + * and the flush fails + */ + if (pqFlush(conn) && pqIsnonblocking(conn)) + return 1; + + /* non blocking connections may have to abort at this point. */ + if (pqIsnonblocking(conn) && PQisBusy(conn)) + return 1; + + /* Return to active duty */ + conn->asyncStatus = PGASYNC_BUSY; + resetPQExpBuffer(&conn->errorMessage); + + /* Wait for the completion response */ + result = PQgetResult(conn); + + /* Expecting a successful result */ + if (result && result->resultStatus == PGRES_COMMAND_OK) + { + PQclear(result); + return 0; + } + + /* + * Trouble. For backwards-compatibility reasons, we issue the error + * message as if it were a notice (would be nice to get rid of this + * silliness, but too many apps probably don't handle errors from + * PQendcopy reasonably). Note that the app can still obtain the error + * status from the PGconn object. + */ + if (conn->errorMessage.len > 0) + { + /* We have to strip the trailing newline ... pain in neck... */ + char svLast = conn->errorMessage.data[conn->errorMessage.len - 1]; + + if (svLast == '\n') + conn->errorMessage.data[conn->errorMessage.len - 1] = '\0'; + pqInternalNotice(&conn->noticeHooks, "%s", conn->errorMessage.data); + conn->errorMessage.data[conn->errorMessage.len - 1] = svLast; + } + + PQclear(result); + + /* + * The worst case is that we've lost sync with the backend entirely due to + * application screwup of the copy in/out protocol. To recover, reset the + * connection (talk about using a sledgehammer...) + */ + pqInternalNotice(&conn->noticeHooks, + "lost synchronization with server, resetting connection"); + + /* + * Users doing non-blocking connections need to handle the reset + * themselves, they'll need to check the connection status if we return an + * error. + */ + if (pqIsnonblocking(conn)) + PQresetStart(conn); + else + PQreset(conn); + + return 1; +} + + +/* + * PQfn - Send a function call to the POSTGRES backend. + * + * See fe-exec.c for documentation. + */ +PGresult * +pqFunctionCall2(PGconn *conn, Oid fnid, + int *result_buf, int *actual_result_len, + int result_is_int, + const PQArgBlock *args, int nargs) +{ + bool needInput = false; + ExecStatusType status = PGRES_FATAL_ERROR; + char id; + int i; + + /* PQfn already validated connection state */ + + if (pqPutMsgStart('F', false, conn) < 0 || /* function call msg */ + pqPuts(" ", conn) < 0 || /* dummy string */ + pqPutInt(fnid, 4, conn) != 0 || /* function id */ + pqPutInt(nargs, 4, conn) != 0) /* # of args */ + { + pqHandleSendFailure(conn); + return NULL; + } + + for (i = 0; i < nargs; ++i) + { /* len.int4 + contents */ + if (pqPutInt(args[i].len, 4, conn)) + { + pqHandleSendFailure(conn); + return NULL; + } + + if (args[i].isint) + { + if (pqPutInt(args[i].u.integer, 4, conn)) + { + pqHandleSendFailure(conn); + return NULL; + } + } + else + { + if (pqPutnchar((char *) args[i].u.ptr, args[i].len, conn)) + { + pqHandleSendFailure(conn); + return NULL; + } + } + } + + if (pqPutMsgEnd(conn) < 0 || + pqFlush(conn)) + { + pqHandleSendFailure(conn); + return NULL; + } + + for (;;) + { + if (needInput) + { + /* Wait for some data to arrive (or for the channel to close) */ + if (pqWait(TRUE, FALSE, conn) || + pqReadData(conn) < 0) + break; + } + + /* + * Scan the message. If we run out of data, loop around to try again. + */ + conn->inCursor = conn->inStart; + needInput = true; + + if (pqGetc(&id, conn)) + continue; + + /* + * We should see V or E response to the command, but might get N + * and/or A notices first. We also need to swallow the final Z before + * returning. + */ + switch (id) + { + case 'V': /* function result */ + if (pqGetc(&id, conn)) + continue; + if (id == 'G') + { + /* function returned nonempty value */ + if (pqGetInt(actual_result_len, 4, conn)) + continue; + if (result_is_int) + { + if (pqGetInt(result_buf, 4, conn)) + continue; + } + else + { + if (pqGetnchar((char *) result_buf, + *actual_result_len, + conn)) + continue; + } + if (pqGetc(&id, conn)) /* get the last '0' */ + continue; + } + if (id == '0') + { + /* correctly finished function result message */ + status = PGRES_COMMAND_OK; + } + else + { + /* The backend violates the protocol. */ + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("protocol error: id=0x%x\n"), + id); + pqSaveErrorResult(conn); + conn->inStart = conn->inCursor; + return pqPrepareAsyncResult(conn); + } + break; + case 'E': /* error return */ + if (pqGetErrorNotice2(conn, true)) + continue; + status = PGRES_FATAL_ERROR; + break; + case 'A': /* notify message */ + /* handle notify and go back to processing return values */ + if (getNotify(conn)) + continue; + break; + case 'N': /* notice */ + /* handle notice and go back to processing return values */ + if (pqGetErrorNotice2(conn, false)) + continue; + break; + case 'Z': /* backend is ready for new query */ + /* consume the message and exit */ + conn->inStart = conn->inCursor; + /* if we saved a result object (probably an error), use it */ + if (conn->result) + return pqPrepareAsyncResult(conn); + return PQmakeEmptyPGresult(conn, status); + default: + /* The backend violates the protocol. */ + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("protocol error: id=0x%x\n"), + id); + pqSaveErrorResult(conn); + conn->inStart = conn->inCursor; + return pqPrepareAsyncResult(conn); + } + /* Completed this message, keep going */ + conn->inStart = conn->inCursor; + needInput = false; + } + + /* + * We fall out of the loop only upon failing to read data. + * conn->errorMessage has been set by pqWait or pqReadData. We want to + * append it to any already-received error message. + */ + pqSaveErrorResult(conn); + return pqPrepareAsyncResult(conn); +} + + +/* + * Construct startup packet + * + * Returns a malloc'd packet buffer, or NULL if out of memory + */ +char * +pqBuildStartupPacket2(PGconn *conn, int *packetlen, + const PQEnvironmentOption *options) +{ + StartupPacket *startpacket; + + *packetlen = sizeof(StartupPacket); + startpacket = (StartupPacket *) malloc(sizeof(StartupPacket)); + if (!startpacket) + return NULL; + + MemSet(startpacket, 0, sizeof(StartupPacket)); + + startpacket->protoVersion = htonl(conn->pversion); + + /* strncpy is safe here: postmaster will handle full fields correctly */ + strncpy(startpacket->user, conn->pguser, SM_USER); + strncpy(startpacket->database, conn->dbName, SM_DATABASE); + strncpy(startpacket->tty, conn->pgtty, SM_TTY); + + if (conn->pgoptions) + strncpy(startpacket->options, conn->pgoptions, SM_OPTIONS); + + return (char *) startpacket; +} diff --git a/libpq/fe-protocol3.c b/libpq/fe-protocol3.c new file mode 100644 index 0000000..a8a987a --- /dev/null +++ b/libpq/fe-protocol3.c @@ -0,0 +1,2204 @@ +/*------------------------------------------------------------------------- + * + * fe-protocol3.c + * functions that are specific to frontend/backend protocol version 3 + * + * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/interfaces/libpq/fe-protocol3.c + * + *------------------------------------------------------------------------- + */ +#include "postgres_fe.h" + +#include +#include + +#include "libpq-fe.h" +#include "libpq-int.h" + +#include "mb/pg_wchar.h" + +#ifdef WIN32 +#include "win32.h" +#else +#include +#include +#ifdef HAVE_NETINET_TCP_H +#include +#endif +#include +#endif + + +/* + * This macro lists the backend message types that could be "long" (more + * than a couple of kilobytes). + */ +#define VALID_LONG_MESSAGE_TYPE(id) \ + ((id) == 'T' || (id) == 'D' || (id) == 'd' || (id) == 'V' || \ + (id) == 'E' || (id) == 'N' || (id) == 'A') + + +static void handleSyncLoss(PGconn *conn, char id, int msgLength); +static int getRowDescriptions(PGconn *conn, int msgLength); +static int getParamDescriptions(PGconn *conn, int msgLength); +static int getAnotherTuple(PGconn *conn, int msgLength); +static int getParameterStatus(PGconn *conn); +static int getNotify(PGconn *conn); +static int getCopyStart(PGconn *conn, ExecStatusType copytype); +static int getReadyForQuery(PGconn *conn); +static void reportErrorPosition(PQExpBuffer msg, const char *query, + int loc, int encoding); +static int build_startup_packet(const PGconn *conn, char *packet, + const PQEnvironmentOption *options); + + +/* + * parseInput: if appropriate, parse input data from backend + * until input is exhausted or a stopping state is reached. + * Note that this function will NOT attempt to read more data from the backend. + */ +void +pqParseInput3(PGconn *conn) +{ + char id; + int msgLength; + int avail; + + /* + * Loop to parse successive complete messages available in the buffer. + */ + for (;;) + { + /* + * Try to read a message. First get the type code and length. Return + * if not enough data. + */ + conn->inCursor = conn->inStart; + if (pqGetc(&id, conn)) + return; + if (pqGetInt(&msgLength, 4, conn)) + return; + + /* + * Try to validate message type/length here. A length less than 4 is + * definitely broken. Large lengths should only be believed for a few + * message types. + */ + if (msgLength < 4) + { + handleSyncLoss(conn, id, msgLength); + return; + } + if (msgLength > 30000 && !VALID_LONG_MESSAGE_TYPE(id)) + { + handleSyncLoss(conn, id, msgLength); + return; + } + + /* + * Can't process if message body isn't all here yet. + */ + msgLength -= 4; + avail = conn->inEnd - conn->inCursor; + if (avail < msgLength) + { + /* + * Before returning, enlarge the input buffer if needed to hold + * the whole message. This is better than leaving it to + * pqReadData because we can avoid multiple cycles of realloc() + * when the message is large; also, we can implement a reasonable + * recovery strategy if we are unable to make the buffer big + * enough. + */ + if (pqCheckInBufferSpace(conn->inCursor + (size_t) msgLength, + conn)) + { + /* + * XXX add some better recovery code... plan is to skip over + * the message using its length, then report an error. For the + * moment, just treat this like loss of sync (which indeed it + * might be!) + */ + handleSyncLoss(conn, id, msgLength); + } + return; + } + + /* + * NOTIFY and NOTICE messages can happen in any state; always process + * them right away. + * + * Most other messages should only be processed while in BUSY state. + * (In particular, in READY state we hold off further parsing until + * the application collects the current PGresult.) + * + * However, if the state is IDLE then we got trouble; we need to deal + * with the unexpected message somehow. + * + * ParameterStatus ('S') messages are a special case: in IDLE state we + * must process 'em (this case could happen if a new value was adopted + * from config file due to SIGHUP), but otherwise we hold off until + * BUSY state. + */ + if (id == 'A') + { + if (getNotify(conn)) + return; + } + else if (id == 'N') + { + if (pqGetErrorNotice3(conn, false)) + return; + } + else if (conn->asyncStatus != PGASYNC_BUSY) + { + /* If not IDLE state, just wait ... */ + if (conn->asyncStatus != PGASYNC_IDLE) + return; + + /* + * Unexpected message in IDLE state; need to recover somehow. + * ERROR messages are handled using the notice processor; + * ParameterStatus is handled normally; anything else is just + * dropped on the floor after displaying a suitable warning + * notice. (An ERROR is very possibly the backend telling us why + * it is about to close the connection, so we don't want to just + * discard it...) + */ + if (id == 'E') + { + if (pqGetErrorNotice3(conn, false /* treat as notice */ )) + return; + } + else if (id == 'S') + { + if (getParameterStatus(conn)) + return; + } + else + { + pqInternalNotice(&conn->noticeHooks, + "message type 0x%02x arrived from server while idle", + id); + /* Discard the unexpected message */ + conn->inCursor += msgLength; + } + } + else + { + /* + * In BUSY state, we can process everything. + */ + switch (id) + { + case 'C': /* command complete */ + if (pqGets(&conn->workBuffer, conn)) + return; + if (conn->result == NULL) + { + conn->result = PQmakeEmptyPGresult(conn, + PGRES_COMMAND_OK); + if (!conn->result) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("out of memory")); + pqSaveErrorResult(conn); + } + } + if (conn->result) + strlcpy(conn->result->cmdStatus, conn->workBuffer.data, + CMDSTATUS_LEN); + conn->asyncStatus = PGASYNC_READY; + break; + case 'E': /* error return */ + if (pqGetErrorNotice3(conn, true)) + return; + conn->asyncStatus = PGASYNC_READY; + break; + case 'Z': /* backend is ready for new query */ + if (getReadyForQuery(conn)) + return; + conn->asyncStatus = PGASYNC_IDLE; + break; + case 'I': /* empty query */ + if (conn->result == NULL) + { + conn->result = PQmakeEmptyPGresult(conn, + PGRES_EMPTY_QUERY); + if (!conn->result) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("out of memory")); + pqSaveErrorResult(conn); + } + } + conn->asyncStatus = PGASYNC_READY; + break; + case '1': /* Parse Complete */ + /* If we're doing PQprepare, we're done; else ignore */ + if (conn->queryclass == PGQUERY_PREPARE) + { + if (conn->result == NULL) + { + conn->result = PQmakeEmptyPGresult(conn, + PGRES_COMMAND_OK); + if (!conn->result) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("out of memory")); + pqSaveErrorResult(conn); + } + } + conn->asyncStatus = PGASYNC_READY; + } + break; + case '2': /* Bind Complete */ + case '3': /* Close Complete */ + /* Nothing to do for these message types */ + break; + case 'S': /* parameter status */ + if (getParameterStatus(conn)) + return; + break; + case 'K': /* secret key data from the backend */ + + /* + * This is expected only during backend startup, but it's + * just as easy to handle it as part of the main loop. + * Save the data and continue processing. + */ + if (pqGetInt(&(conn->be_pid), 4, conn)) + return; + if (pqGetInt(&(conn->be_key), 4, conn)) + return; + break; + case 'T': /* Row Description */ + if (conn->result != NULL && + conn->result->resultStatus == PGRES_FATAL_ERROR) + { + /* + * We've already choked for some reason. Just discard + * the data till we get to the end of the query. + */ + conn->inCursor += msgLength; + } + else if (conn->result == NULL || + conn->queryclass == PGQUERY_DESCRIBE) + { + /* First 'T' in a query sequence */ + if (getRowDescriptions(conn, msgLength)) + return; + /* getRowDescriptions() moves inStart itself */ + continue; + } + else + { + /* + * A new 'T' message is treated as the start of + * another PGresult. (It is not clear that this is + * really possible with the current backend.) We stop + * parsing until the application accepts the current + * result. + */ + conn->asyncStatus = PGASYNC_READY; + return; + } + break; + case 'n': /* No Data */ + + /* + * NoData indicates that we will not be seeing a + * RowDescription message because the statement or portal + * inquired about doesn't return rows. + * + * If we're doing a Describe, we have to pass something + * back to the client, so set up a COMMAND_OK result, + * instead of TUPLES_OK. Otherwise we can just ignore + * this message. + */ + if (conn->queryclass == PGQUERY_DESCRIBE) + { + if (conn->result == NULL) + { + conn->result = PQmakeEmptyPGresult(conn, + PGRES_COMMAND_OK); + if (!conn->result) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("out of memory")); + pqSaveErrorResult(conn); + } + } + conn->asyncStatus = PGASYNC_READY; + } + break; + case 't': /* Parameter Description */ + if (getParamDescriptions(conn, msgLength)) + return; + /* getParamDescriptions() moves inStart itself */ + continue; + case 'D': /* Data Row */ + if (conn->result != NULL && + conn->result->resultStatus == PGRES_TUPLES_OK) + { + /* Read another tuple of a normal query response */ + if (getAnotherTuple(conn, msgLength)) + return; + /* getAnotherTuple() moves inStart itself */ + continue; + } + else if (conn->result != NULL && + conn->result->resultStatus == PGRES_FATAL_ERROR) + { + /* + * We've already choked for some reason. Just discard + * tuples till we get to the end of the query. + */ + conn->inCursor += msgLength; + } + else + { + /* Set up to report error at end of query */ + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("server sent data (\"D\" message) without prior row description (\"T\" message)\n")); + pqSaveErrorResult(conn); + /* Discard the unexpected message */ + conn->inCursor += msgLength; + } + break; + case 'G': /* Start Copy In */ + if (getCopyStart(conn, PGRES_COPY_IN)) + return; + conn->asyncStatus = PGASYNC_COPY_IN; + break; + case 'H': /* Start Copy Out */ + if (getCopyStart(conn, PGRES_COPY_OUT)) + return; + conn->asyncStatus = PGASYNC_COPY_OUT; + conn->copy_already_done = 0; + break; + case 'W': /* Start Copy Both */ + if (getCopyStart(conn, PGRES_COPY_BOTH)) + return; + conn->asyncStatus = PGASYNC_COPY_BOTH; + conn->copy_already_done = 0; + break; + case 'd': /* Copy Data */ + + /* + * If we see Copy Data, just silently drop it. This would + * only occur if application exits COPY OUT mode too + * early. + */ + conn->inCursor += msgLength; + break; + case 'c': /* Copy Done */ + + /* + * If we see Copy Done, just silently drop it. This is + * the normal case during PQendcopy. We will keep + * swallowing data, expecting to see command-complete for + * the COPY command. + */ + break; + default: + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext( + "unexpected response from server; first received character was \"%c\"\n"), + id); + /* build an error result holding the error message */ + pqSaveErrorResult(conn); + /* not sure if we will see more, so go to ready state */ + conn->asyncStatus = PGASYNC_READY; + /* Discard the unexpected message */ + conn->inCursor += msgLength; + break; + } /* switch on protocol character */ + } + /* Successfully consumed this message */ + if (conn->inCursor == conn->inStart + 5 + msgLength) + { + /* Normal case: parsing agrees with specified length */ + conn->inStart = conn->inCursor; + } + else + { + /* Trouble --- report it */ + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("message contents do not agree with length in message type \"%c\"\n"), + id); + /* build an error result holding the error message */ + pqSaveErrorResult(conn); + conn->asyncStatus = PGASYNC_READY; + /* trust the specified message length as what to skip */ + conn->inStart += 5 + msgLength; + } + } +} + +/* + * handleSyncLoss: clean up after loss of message-boundary sync + * + * There isn't really a lot we can do here except abandon the connection. + */ +static void +handleSyncLoss(PGconn *conn, char id, int msgLength) +{ + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext( + "lost synchronization with server: got message type \"%c\", length %d\n"), + id, msgLength); + /* build an error result holding the error message */ + pqSaveErrorResult(conn); + conn->asyncStatus = PGASYNC_READY; /* drop out of GetResult wait loop */ + /* flush input data since we're giving up on processing it */ + pqDropConnection(conn, true); + conn->status = CONNECTION_BAD; /* No more connection to backend */ +} + +/* + * parseInput subroutine to read a 'T' (row descriptions) message. + * We'll build a new PGresult structure (unless called for a Describe + * command for a prepared statement) containing the attribute data. + * Returns: 0 if processed message successfully, EOF to suspend parsing + * (the latter case is not actually used currently). + * In the former case, conn->inStart has been advanced past the message. + */ +static int +getRowDescriptions(PGconn *conn, int msgLength) +{ + PGresult *result; + int nfields; + const char *errmsg; + int i; + + /* + * When doing Describe for a prepared statement, there'll already be a + * PGresult created by getParamDescriptions, and we should fill data into + * that. Otherwise, create a new, empty PGresult. + */ + if (conn->queryclass == PGQUERY_DESCRIBE) + { + if (conn->result) + result = conn->result; + else + result = PQmakeEmptyPGresult(conn, PGRES_COMMAND_OK); + } + else + result = PQmakeEmptyPGresult(conn, PGRES_TUPLES_OK); + if (!result) + { + errmsg = NULL; /* means "out of memory", see below */ + goto advance_and_error; + } + + /* parseInput already read the 'T' label and message length. */ + /* the next two bytes are the number of fields */ + if (pqGetInt(&(result->numAttributes), 2, conn)) + { + /* We should not run out of data here, so complain */ + errmsg = libpq_gettext("insufficient data in \"T\" message"); + goto advance_and_error; + } + nfields = result->numAttributes; + + /* allocate space for the attribute descriptors */ + if (nfields > 0) + { + result->attDescs = (PGresAttDesc *) + pqResultAlloc(result, nfields * sizeof(PGresAttDesc), TRUE); + if (!result->attDescs) + { + errmsg = NULL; /* means "out of memory", see below */ + goto advance_and_error; + } + MemSet(result->attDescs, 0, nfields * sizeof(PGresAttDesc)); + } + + /* result->binary is true only if ALL columns are binary */ + result->binary = (nfields > 0) ? 1 : 0; + + /* get type info */ + for (i = 0; i < nfields; i++) + { + int tableid; + int columnid; + int typid; + int typlen; + int atttypmod; + int format; + + if (pqGets(&conn->workBuffer, conn) || + pqGetInt(&tableid, 4, conn) || + pqGetInt(&columnid, 2, conn) || + pqGetInt(&typid, 4, conn) || + pqGetInt(&typlen, 2, conn) || + pqGetInt(&atttypmod, 4, conn) || + pqGetInt(&format, 2, conn)) + { + /* We should not run out of data here, so complain */ + errmsg = libpq_gettext("insufficient data in \"T\" message"); + goto advance_and_error; + } + + /* + * Since pqGetInt treats 2-byte integers as unsigned, we need to + * coerce these results to signed form. + */ + columnid = (int) ((int16) columnid); + typlen = (int) ((int16) typlen); + format = (int) ((int16) format); + + result->attDescs[i].name = pqResultStrdup(result, + conn->workBuffer.data); + if (!result->attDescs[i].name) + { + errmsg = NULL; /* means "out of memory", see below */ + goto advance_and_error; + } + result->attDescs[i].tableid = tableid; + result->attDescs[i].columnid = columnid; + result->attDescs[i].format = format; + result->attDescs[i].typid = typid; + result->attDescs[i].typlen = typlen; + result->attDescs[i].atttypmod = atttypmod; + + if (format != 1) + result->binary = 0; + } + + /* Sanity check that we absorbed all the data */ + if (conn->inCursor != conn->inStart + 5 + msgLength) + { + errmsg = libpq_gettext("extraneous data in \"T\" message"); + goto advance_and_error; + } + + /* Success! */ + conn->result = result; + + /* Advance inStart to show that the "T" message has been processed. */ + conn->inStart = conn->inCursor; + + /* + * If we're doing a Describe, we're done, and ready to pass the result + * back to the client. + */ + if (conn->queryclass == PGQUERY_DESCRIBE) + { + conn->asyncStatus = PGASYNC_READY; + return 0; + } + + /* + * We could perform additional setup for the new result set here, but for + * now there's nothing else to do. + */ + + /* And we're done. */ + return 0; + +advance_and_error: + /* Discard unsaved result, if any */ + if (result && result != conn->result) + PQclear(result); + + /* Discard the failed message by pretending we read it */ + conn->inStart += 5 + msgLength; + + /* + * Replace partially constructed result with an error result. First + * discard the old result to try to win back some memory. + */ + pqClearAsyncResult(conn); + + /* + * If preceding code didn't provide an error message, assume "out of + * memory" was meant. The advantage of having this special case is that + * freeing the old result first greatly improves the odds that gettext() + * will succeed in providing a translation. + */ + if (!errmsg) + errmsg = libpq_gettext("out of memory for query result"); + + printfPQExpBuffer(&conn->errorMessage, "%s\n", errmsg); + pqSaveErrorResult(conn); + + /* + * Return zero to allow input parsing to continue. Subsequent "D" + * messages will be ignored until we get to end of data, since an error + * result is already set up. + */ + return 0; +} + +/* + * parseInput subroutine to read a 't' (ParameterDescription) message. + * We'll build a new PGresult structure containing the parameter data. + * Returns: 0 if completed message, EOF if not enough data yet. + * In the former case, conn->inStart has been advanced past the message. + * + * Note that if we run out of data, we have to release the partially + * constructed PGresult, and rebuild it again next time. Fortunately, + * that shouldn't happen often, since 't' messages usually fit in a packet. + */ +static int +getParamDescriptions(PGconn *conn, int msgLength) +{ + PGresult *result; + const char *errmsg = NULL; /* means "out of memory", see below */ + int nparams; + int i; + + result = PQmakeEmptyPGresult(conn, PGRES_COMMAND_OK); + if (!result) + goto advance_and_error; + + /* parseInput already read the 't' label and message length. */ + /* the next two bytes are the number of parameters */ + if (pqGetInt(&(result->numParameters), 2, conn)) + goto not_enough_data; + nparams = result->numParameters; + + /* allocate space for the parameter descriptors */ + if (nparams > 0) + { + result->paramDescs = (PGresParamDesc *) + pqResultAlloc(result, nparams * sizeof(PGresParamDesc), TRUE); + if (!result->paramDescs) + goto advance_and_error; + MemSet(result->paramDescs, 0, nparams * sizeof(PGresParamDesc)); + } + + /* get parameter info */ + for (i = 0; i < nparams; i++) + { + int typid; + + if (pqGetInt(&typid, 4, conn)) + goto not_enough_data; + result->paramDescs[i].typid = typid; + } + + /* Sanity check that we absorbed all the data */ + if (conn->inCursor != conn->inStart + 5 + msgLength) + { + errmsg = libpq_gettext("extraneous data in \"t\" message"); + goto advance_and_error; + } + + /* Success! */ + conn->result = result; + + /* Advance inStart to show that the "t" message has been processed. */ + conn->inStart = conn->inCursor; + + return 0; + +not_enough_data: + PQclear(result); + return EOF; + +advance_and_error: + /* Discard unsaved result, if any */ + if (result && result != conn->result) + PQclear(result); + + /* Discard the failed message by pretending we read it */ + conn->inStart += 5 + msgLength; + + /* + * Replace partially constructed result with an error result. First + * discard the old result to try to win back some memory. + */ + pqClearAsyncResult(conn); + + /* + * If preceding code didn't provide an error message, assume "out of + * memory" was meant. The advantage of having this special case is that + * freeing the old result first greatly improves the odds that gettext() + * will succeed in providing a translation. + */ + if (!errmsg) + errmsg = libpq_gettext("out of memory"); + printfPQExpBuffer(&conn->errorMessage, "%s\n", errmsg); + pqSaveErrorResult(conn); + + /* + * Return zero to allow input parsing to continue. Essentially, we've + * replaced the COMMAND_OK result with an error result, but since this + * doesn't affect the protocol state, it's fine. + */ + return 0; +} + +/* + * parseInput subroutine to read a 'D' (row data) message. + * We fill rowbuf with column pointers and then call the row processor. + * Returns: 0 if processed message successfully, EOF to suspend parsing + * (the latter case is not actually used currently). + * In the former case, conn->inStart has been advanced past the message. + */ +static int +getAnotherTuple(PGconn *conn, int msgLength) +{ + PGresult *result = conn->result; + int nfields = result->numAttributes; + const char *errmsg; + PGdataValue *rowbuf; + int tupnfields; /* # fields from tuple */ + int vlen; /* length of the current field value */ + int i; + + /* Get the field count and make sure it's what we expect */ + if (pqGetInt(&tupnfields, 2, conn)) + { + /* We should not run out of data here, so complain */ + errmsg = libpq_gettext("insufficient data in \"D\" message"); + goto advance_and_error; + } + + if (tupnfields != nfields) + { + errmsg = libpq_gettext("unexpected field count in \"D\" message"); + goto advance_and_error; + } + + /* Resize row buffer if needed */ + rowbuf = conn->rowBuf; + if (nfields > conn->rowBufLen) + { + rowbuf = (PGdataValue *) realloc(rowbuf, + nfields * sizeof(PGdataValue)); + if (!rowbuf) + { + errmsg = NULL; /* means "out of memory", see below */ + goto advance_and_error; + } + conn->rowBuf = rowbuf; + conn->rowBufLen = nfields; + } + + /* Scan the fields */ + for (i = 0; i < nfields; i++) + { + /* get the value length */ + if (pqGetInt(&vlen, 4, conn)) + { + /* We should not run out of data here, so complain */ + errmsg = libpq_gettext("insufficient data in \"D\" message"); + goto advance_and_error; + } + rowbuf[i].len = vlen; + + /* + * rowbuf[i].value always points to the next address in the data + * buffer even if the value is NULL. This allows row processors to + * estimate data sizes more easily. + */ + rowbuf[i].value = conn->inBuffer + conn->inCursor; + + /* Skip over the data value */ + if (vlen > 0) + { + if (pqSkipnchar(vlen, conn)) + { + /* We should not run out of data here, so complain */ + errmsg = libpq_gettext("insufficient data in \"D\" message"); + goto advance_and_error; + } + } + } + + /* Sanity check that we absorbed all the data */ + if (conn->inCursor != conn->inStart + 5 + msgLength) + { + errmsg = libpq_gettext("extraneous data in \"D\" message"); + goto advance_and_error; + } + + /* Advance inStart to show that the "D" message has been processed. */ + conn->inStart = conn->inCursor; + + /* Process the collected row */ + errmsg = NULL; + if (pqRowProcessor(conn, &errmsg)) + return 0; /* normal, successful exit */ + + goto set_error_result; /* pqRowProcessor failed, report it */ + +advance_and_error: + /* Discard the failed message by pretending we read it */ + conn->inStart += 5 + msgLength; + +set_error_result: + + /* + * Replace partially constructed result with an error result. First + * discard the old result to try to win back some memory. + */ + pqClearAsyncResult(conn); + + /* + * If preceding code didn't provide an error message, assume "out of + * memory" was meant. The advantage of having this special case is that + * freeing the old result first greatly improves the odds that gettext() + * will succeed in providing a translation. + */ + if (!errmsg) + errmsg = libpq_gettext("out of memory for query result"); + + printfPQExpBuffer(&conn->errorMessage, "%s\n", errmsg); + pqSaveErrorResult(conn); + + /* + * Return zero to allow input parsing to continue. Subsequent "D" + * messages will be ignored until we get to end of data, since an error + * result is already set up. + */ + return 0; +} + + +/* + * Attempt to read an Error or Notice response message. + * This is possible in several places, so we break it out as a subroutine. + * Entry: 'E' or 'N' message type and length have already been consumed. + * Exit: returns 0 if successfully consumed message. + * returns EOF if not enough data. + */ +int +pqGetErrorNotice3(PGconn *conn, bool isError) +{ + PGresult *res = NULL; + bool have_position = false; + PQExpBufferData workBuf; + char id; + + /* + * Since the fields might be pretty long, we create a temporary + * PQExpBuffer rather than using conn->workBuffer. workBuffer is intended + * for stuff that is expected to be short. We shouldn't use + * conn->errorMessage either, since this might be only a notice. + */ + initPQExpBuffer(&workBuf); + + /* + * Make a PGresult to hold the accumulated fields. We temporarily lie + * about the result status, so that PQmakeEmptyPGresult doesn't uselessly + * copy conn->errorMessage. + * + * NB: This allocation can fail, if you run out of memory. The rest of the + * function handles that gracefully, and we still try to set the error + * message as the connection's error message. + */ + res = PQmakeEmptyPGresult(conn, PGRES_EMPTY_QUERY); + if (res) + res->resultStatus = isError ? PGRES_FATAL_ERROR : PGRES_NONFATAL_ERROR; + + /* + * Read the fields and save into res. + * + * While at it, save the SQLSTATE in conn->last_sqlstate, and note whether + * we saw a PG_DIAG_STATEMENT_POSITION field. + */ + for (;;) + { + if (pqGetc(&id, conn)) + goto fail; + if (id == '\0') + break; /* terminator found */ + if (pqGets(&workBuf, conn)) + goto fail; + pqSaveMessageField(res, id, workBuf.data); + if (id == PG_DIAG_SQLSTATE) + strlcpy(conn->last_sqlstate, workBuf.data, + sizeof(conn->last_sqlstate)); + else if (id == PG_DIAG_STATEMENT_POSITION) + have_position = true; + } + + /* + * Save the active query text, if any, into res as well; but only if we + * might need it for an error cursor display, which is only true if there + * is a PG_DIAG_STATEMENT_POSITION field. + */ + if (have_position && conn->last_query && res) + res->errQuery = pqResultStrdup(res, conn->last_query); + + /* + * Now build the "overall" error message for PQresultErrorMessage. + */ + resetPQExpBuffer(&workBuf); + pqBuildErrorMessage3(&workBuf, res, conn->verbosity, conn->show_context); + + /* + * Either save error as current async result, or just emit the notice. + */ + if (isError) + { + if (res) + res->errMsg = pqResultStrdup(res, workBuf.data); + pqClearAsyncResult(conn); + conn->result = res; + if (PQExpBufferDataBroken(workBuf)) + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("out of memory")); + else + appendPQExpBufferStr(&conn->errorMessage, workBuf.data); + } + else + { + /* if we couldn't allocate the result set, just discard the NOTICE */ + if (res) + { + /* We can cheat a little here and not copy the message. */ + res->errMsg = workBuf.data; + if (res->noticeHooks.noticeRec != NULL) + (*res->noticeHooks.noticeRec) (res->noticeHooks.noticeRecArg, res); + PQclear(res); + } + } + + termPQExpBuffer(&workBuf); + return 0; + +fail: + PQclear(res); + termPQExpBuffer(&workBuf); + return EOF; +} + +/* + * Construct an error message from the fields in the given PGresult, + * appending it to the contents of "msg". + */ +void +pqBuildErrorMessage3(PQExpBuffer msg, const PGresult *res, + PGVerbosity verbosity, PGContextVisibility show_context) +{ + const char *val; + const char *querytext = NULL; + int querypos = 0; + + /* If we couldn't allocate a PGresult, just say "out of memory" */ + if (res == NULL) + { + appendPQExpBuffer(msg, libpq_gettext("out of memory\n")); + return; + } + + /* + * If we don't have any broken-down fields, just return the base message. + * This mainly applies if we're given a libpq-generated error result. + */ + if (res->errFields == NULL) + { + if (res->errMsg && res->errMsg[0]) + appendPQExpBufferStr(msg, res->errMsg); + else + appendPQExpBuffer(msg, libpq_gettext("no error message available\n")); + return; + } + + /* Else build error message from relevant fields */ + val = PQresultErrorField(res, PG_DIAG_SEVERITY); + if (val) + appendPQExpBuffer(msg, "%s: ", val); + if (verbosity == PQERRORS_VERBOSE) + { + val = PQresultErrorField(res, PG_DIAG_SQLSTATE); + if (val) + appendPQExpBuffer(msg, "%s: ", val); + } + val = PQresultErrorField(res, PG_DIAG_MESSAGE_PRIMARY); + if (val) + appendPQExpBufferStr(msg, val); + val = PQresultErrorField(res, PG_DIAG_STATEMENT_POSITION); + if (val) + { + if (verbosity != PQERRORS_TERSE && res->errQuery != NULL) + { + /* emit position as a syntax cursor display */ + querytext = res->errQuery; + querypos = atoi(val); + } + else + { + /* emit position as text addition to primary message */ + /* translator: %s represents a digit string */ + appendPQExpBuffer(msg, libpq_gettext(" at character %s"), + val); + } + } + else + { + val = PQresultErrorField(res, PG_DIAG_INTERNAL_POSITION); + if (val) + { + querytext = PQresultErrorField(res, PG_DIAG_INTERNAL_QUERY); + if (verbosity != PQERRORS_TERSE && querytext != NULL) + { + /* emit position as a syntax cursor display */ + querypos = atoi(val); + } + else + { + /* emit position as text addition to primary message */ + /* translator: %s represents a digit string */ + appendPQExpBuffer(msg, libpq_gettext(" at character %s"), + val); + } + } + } + appendPQExpBufferChar(msg, '\n'); + if (verbosity != PQERRORS_TERSE) + { + if (querytext && querypos > 0) + reportErrorPosition(msg, querytext, querypos, + res->client_encoding); + val = PQresultErrorField(res, PG_DIAG_MESSAGE_DETAIL); + if (val) + appendPQExpBuffer(msg, libpq_gettext("DETAIL: %s\n"), val); + val = PQresultErrorField(res, PG_DIAG_MESSAGE_HINT); + if (val) + appendPQExpBuffer(msg, libpq_gettext("HINT: %s\n"), val); + val = PQresultErrorField(res, PG_DIAG_INTERNAL_QUERY); + if (val) + appendPQExpBuffer(msg, libpq_gettext("QUERY: %s\n"), val); + if (show_context == PQSHOW_CONTEXT_ALWAYS || + (show_context == PQSHOW_CONTEXT_ERRORS && + res->resultStatus == PGRES_FATAL_ERROR)) + { + val = PQresultErrorField(res, PG_DIAG_CONTEXT); + if (val) + appendPQExpBuffer(msg, libpq_gettext("CONTEXT: %s\n"), + val); + } + } + if (verbosity == PQERRORS_VERBOSE) + { + val = PQresultErrorField(res, PG_DIAG_SCHEMA_NAME); + if (val) + appendPQExpBuffer(msg, + libpq_gettext("SCHEMA NAME: %s\n"), val); + val = PQresultErrorField(res, PG_DIAG_TABLE_NAME); + if (val) + appendPQExpBuffer(msg, + libpq_gettext("TABLE NAME: %s\n"), val); + val = PQresultErrorField(res, PG_DIAG_COLUMN_NAME); + if (val) + appendPQExpBuffer(msg, + libpq_gettext("COLUMN NAME: %s\n"), val); + val = PQresultErrorField(res, PG_DIAG_DATATYPE_NAME); + if (val) + appendPQExpBuffer(msg, + libpq_gettext("DATATYPE NAME: %s\n"), val); + val = PQresultErrorField(res, PG_DIAG_CONSTRAINT_NAME); + if (val) + appendPQExpBuffer(msg, + libpq_gettext("CONSTRAINT NAME: %s\n"), val); + } + if (verbosity == PQERRORS_VERBOSE) + { + const char *valf; + const char *vall; + + valf = PQresultErrorField(res, PG_DIAG_SOURCE_FILE); + vall = PQresultErrorField(res, PG_DIAG_SOURCE_LINE); + val = PQresultErrorField(res, PG_DIAG_SOURCE_FUNCTION); + if (val || valf || vall) + { + appendPQExpBufferStr(msg, libpq_gettext("LOCATION: ")); + if (val) + appendPQExpBuffer(msg, libpq_gettext("%s, "), val); + if (valf && vall) /* unlikely we'd have just one */ + appendPQExpBuffer(msg, libpq_gettext("%s:%s"), + valf, vall); + appendPQExpBufferChar(msg, '\n'); + } + } +} + +/* + * Add an error-location display to the error message under construction. + * + * The cursor location is measured in logical characters; the query string + * is presumed to be in the specified encoding. + */ +static void +reportErrorPosition(PQExpBuffer msg, const char *query, int loc, int encoding) +{ +#define DISPLAY_SIZE 60 /* screen width limit, in screen cols */ +#define MIN_RIGHT_CUT 10 /* try to keep this far away from EOL */ + + char *wquery; + int slen, + cno, + i, + *qidx, + *scridx, + qoffset, + scroffset, + ibeg, + iend, + loc_line; + bool mb_encoding, + beg_trunc, + end_trunc; + + /* Convert loc from 1-based to 0-based; no-op if out of range */ + loc--; + if (loc < 0) + return; + + /* Need a writable copy of the query */ + wquery = strdup(query); + if (wquery == NULL) + return; /* fail silently if out of memory */ + + /* + * Each character might occupy multiple physical bytes in the string, and + * in some Far Eastern character sets it might take more than one screen + * column as well. We compute the starting byte offset and starting + * screen column of each logical character, and store these in qidx[] and + * scridx[] respectively. + */ + + /* we need a safe allocation size... */ + slen = strlen(wquery) + 1; + + qidx = (int *) malloc(slen * sizeof(int)); + if (qidx == NULL) + { + free(wquery); + return; + } + scridx = (int *) malloc(slen * sizeof(int)); + if (scridx == NULL) + { + free(qidx); + free(wquery); + return; + } + + /* We can optimize a bit if it's a single-byte encoding */ + mb_encoding = (pg_encoding_max_length(encoding) != 1); + + /* + * Within the scanning loop, cno is the current character's logical + * number, qoffset is its offset in wquery, and scroffset is its starting + * logical screen column (all indexed from 0). "loc" is the logical + * character number of the error location. We scan to determine loc_line + * (the 1-based line number containing loc) and ibeg/iend (first character + * number and last+1 character number of the line containing loc). Note + * that qidx[] and scridx[] are filled only as far as iend. + */ + qoffset = 0; + scroffset = 0; + loc_line = 1; + ibeg = 0; + iend = -1; /* -1 means not set yet */ + + for (cno = 0; wquery[qoffset] != '\0'; cno++) + { + char ch = wquery[qoffset]; + + qidx[cno] = qoffset; + scridx[cno] = scroffset; + + /* + * Replace tabs with spaces in the writable copy. (Later we might + * want to think about coping with their variable screen width, but + * not today.) + */ + if (ch == '\t') + wquery[qoffset] = ' '; + + /* + * If end-of-line, count lines and mark positions. Each \r or \n + * counts as a line except when \r \n appear together. + */ + else if (ch == '\r' || ch == '\n') + { + if (cno < loc) + { + if (ch == '\r' || + cno == 0 || + wquery[qidx[cno - 1]] != '\r') + loc_line++; + /* extract beginning = last line start before loc. */ + ibeg = cno + 1; + } + else + { + /* set extract end. */ + iend = cno; + /* done scanning. */ + break; + } + } + + /* Advance */ + if (mb_encoding) + { + int w; + + w = pg_encoding_dsplen(encoding, &wquery[qoffset]); + /* treat any non-tab control chars as width 1 */ + if (w <= 0) + w = 1; + scroffset += w; + qoffset += pg_encoding_mblen(encoding, &wquery[qoffset]); + } + else + { + /* We assume wide chars only exist in multibyte encodings */ + scroffset++; + qoffset++; + } + } + /* Fix up if we didn't find an end-of-line after loc */ + if (iend < 0) + { + iend = cno; /* query length in chars, +1 */ + qidx[iend] = qoffset; + scridx[iend] = scroffset; + } + + /* Print only if loc is within computed query length */ + if (loc <= cno) + { + /* If the line extracted is too long, we truncate it. */ + beg_trunc = false; + end_trunc = false; + if (scridx[iend] - scridx[ibeg] > DISPLAY_SIZE) + { + /* + * We first truncate right if it is enough. This code might be + * off a space or so on enforcing MIN_RIGHT_CUT if there's a wide + * character right there, but that should be okay. + */ + if (scridx[ibeg] + DISPLAY_SIZE >= scridx[loc] + MIN_RIGHT_CUT) + { + while (scridx[iend] - scridx[ibeg] > DISPLAY_SIZE) + iend--; + end_trunc = true; + } + else + { + /* Truncate right if not too close to loc. */ + while (scridx[loc] + MIN_RIGHT_CUT < scridx[iend]) + { + iend--; + end_trunc = true; + } + + /* Truncate left if still too long. */ + while (scridx[iend] - scridx[ibeg] > DISPLAY_SIZE) + { + ibeg++; + beg_trunc = true; + } + } + } + + /* truncate working copy at desired endpoint */ + wquery[qidx[iend]] = '\0'; + + /* Begin building the finished message. */ + i = msg->len; + appendPQExpBuffer(msg, libpq_gettext("LINE %d: "), loc_line); + if (beg_trunc) + appendPQExpBufferStr(msg, "..."); + + /* + * While we have the prefix in the msg buffer, compute its screen + * width. + */ + scroffset = 0; + for (; i < msg->len; i += pg_encoding_mblen(encoding, &msg->data[i])) + { + int w = pg_encoding_dsplen(encoding, &msg->data[i]); + + if (w <= 0) + w = 1; + scroffset += w; + } + + /* Finish up the LINE message line. */ + appendPQExpBufferStr(msg, &wquery[qidx[ibeg]]); + if (end_trunc) + appendPQExpBufferStr(msg, "..."); + appendPQExpBufferChar(msg, '\n'); + + /* Now emit the cursor marker line. */ + scroffset += scridx[loc] - scridx[ibeg]; + for (i = 0; i < scroffset; i++) + appendPQExpBufferChar(msg, ' '); + appendPQExpBufferChar(msg, '^'); + appendPQExpBufferChar(msg, '\n'); + } + + /* Clean up. */ + free(scridx); + free(qidx); + free(wquery); +} + + +/* + * Attempt to read a ParameterStatus message. + * This is possible in several places, so we break it out as a subroutine. + * Entry: 'S' message type and length have already been consumed. + * Exit: returns 0 if successfully consumed message. + * returns EOF if not enough data. + */ +static int +getParameterStatus(PGconn *conn) +{ + PQExpBufferData valueBuf; + + /* Get the parameter name */ + if (pqGets(&conn->workBuffer, conn)) + return EOF; + /* Get the parameter value (could be large) */ + initPQExpBuffer(&valueBuf); + if (pqGets(&valueBuf, conn)) + { + termPQExpBuffer(&valueBuf); + return EOF; + } + /* And save it */ + pqSaveParameterStatus(conn, conn->workBuffer.data, valueBuf.data); + termPQExpBuffer(&valueBuf); + return 0; +} + + +/* + * Attempt to read a Notify response message. + * This is possible in several places, so we break it out as a subroutine. + * Entry: 'A' message type and length have already been consumed. + * Exit: returns 0 if successfully consumed Notify message. + * returns EOF if not enough data. + */ +static int +getNotify(PGconn *conn) +{ + int be_pid; + char *svname; + int nmlen; + int extralen; + PGnotify *newNotify; + + if (pqGetInt(&be_pid, 4, conn)) + return EOF; + if (pqGets(&conn->workBuffer, conn)) + return EOF; + /* must save name while getting extra string */ + svname = strdup(conn->workBuffer.data); + if (!svname) + return EOF; + if (pqGets(&conn->workBuffer, conn)) + { + free(svname); + return EOF; + } + + /* + * Store the strings right after the PQnotify structure so it can all be + * freed at once. We don't use NAMEDATALEN because we don't want to tie + * this interface to a specific server name length. + */ + nmlen = strlen(svname); + extralen = strlen(conn->workBuffer.data); + newNotify = (PGnotify *) malloc(sizeof(PGnotify) + nmlen + extralen + 2); + if (newNotify) + { + newNotify->relname = (char *) newNotify + sizeof(PGnotify); + strcpy(newNotify->relname, svname); + newNotify->extra = newNotify->relname + nmlen + 1; + strcpy(newNotify->extra, conn->workBuffer.data); + newNotify->be_pid = be_pid; + newNotify->next = NULL; + if (conn->notifyTail) + conn->notifyTail->next = newNotify; + else + conn->notifyHead = newNotify; + conn->notifyTail = newNotify; + } + + free(svname); + return 0; +} + +/* + * getCopyStart - process CopyInResponse, CopyOutResponse or + * CopyBothResponse message + * + * parseInput already read the message type and length. + */ +static int +getCopyStart(PGconn *conn, ExecStatusType copytype) +{ + PGresult *result; + int nfields; + int i; + + result = PQmakeEmptyPGresult(conn, copytype); + if (!result) + goto failure; + + if (pqGetc(&conn->copy_is_binary, conn)) + goto failure; + result->binary = conn->copy_is_binary; + /* the next two bytes are the number of fields */ + if (pqGetInt(&(result->numAttributes), 2, conn)) + goto failure; + nfields = result->numAttributes; + + /* allocate space for the attribute descriptors */ + if (nfields > 0) + { + result->attDescs = (PGresAttDesc *) + pqResultAlloc(result, nfields * sizeof(PGresAttDesc), TRUE); + if (!result->attDescs) + goto failure; + MemSet(result->attDescs, 0, nfields * sizeof(PGresAttDesc)); + } + + for (i = 0; i < nfields; i++) + { + int format; + + if (pqGetInt(&format, 2, conn)) + goto failure; + + /* + * Since pqGetInt treats 2-byte integers as unsigned, we need to + * coerce these results to signed form. + */ + format = (int) ((int16) format); + result->attDescs[i].format = format; + } + + /* Success! */ + conn->result = result; + return 0; + +failure: + PQclear(result); + return EOF; +} + +/* + * getReadyForQuery - process ReadyForQuery message + */ +static int +getReadyForQuery(PGconn *conn) +{ + char xact_status; + + if (pqGetc(&xact_status, conn)) + return EOF; + switch (xact_status) + { + case 'I': + conn->xactStatus = PQTRANS_IDLE; + break; + case 'T': + conn->xactStatus = PQTRANS_INTRANS; + break; + case 'E': + conn->xactStatus = PQTRANS_INERROR; + break; + default: + conn->xactStatus = PQTRANS_UNKNOWN; + break; + } + + return 0; +} + +/* + * getCopyDataMessage - fetch next CopyData message, process async messages + * + * Returns length word of CopyData message (> 0), or 0 if no complete + * message available, -1 if end of copy, -2 if error. + */ +static int +getCopyDataMessage(PGconn *conn) +{ + char id; + int msgLength; + int avail; + + for (;;) + { + /* + * Do we have the next input message? To make life simpler for async + * callers, we keep returning 0 until the next message is fully + * available, even if it is not Copy Data. + */ + conn->inCursor = conn->inStart; + if (pqGetc(&id, conn)) + return 0; + if (pqGetInt(&msgLength, 4, conn)) + return 0; + if (msgLength < 4) + { + handleSyncLoss(conn, id, msgLength); + return -2; + } + avail = conn->inEnd - conn->inCursor; + if (avail < msgLength - 4) + { + /* + * Before returning, enlarge the input buffer if needed to hold + * the whole message. See notes in parseInput. + */ + if (pqCheckInBufferSpace(conn->inCursor + (size_t) msgLength - 4, + conn)) + { + /* + * XXX add some better recovery code... plan is to skip over + * the message using its length, then report an error. For the + * moment, just treat this like loss of sync (which indeed it + * might be!) + */ + handleSyncLoss(conn, id, msgLength); + return -2; + } + return 0; + } + + /* + * If it's a legitimate async message type, process it. (NOTIFY + * messages are not currently possible here, but we handle them for + * completeness.) Otherwise, if it's anything except Copy Data, + * report end-of-copy. + */ + switch (id) + { + case 'A': /* NOTIFY */ + if (getNotify(conn)) + return 0; + break; + case 'N': /* NOTICE */ + if (pqGetErrorNotice3(conn, false)) + return 0; + break; + case 'S': /* ParameterStatus */ + if (getParameterStatus(conn)) + return 0; + break; + case 'd': /* Copy Data, pass it back to caller */ + return msgLength; + case 'c': + + /* + * If this is a CopyDone message, exit COPY_OUT mode and let + * caller read status with PQgetResult(). If we're in + * COPY_BOTH mode, return to COPY_IN mode. + */ + if (conn->asyncStatus == PGASYNC_COPY_BOTH) + conn->asyncStatus = PGASYNC_COPY_IN; + else + conn->asyncStatus = PGASYNC_BUSY; + return -1; + default: /* treat as end of copy */ + + /* + * Any other message terminates either COPY_IN or COPY_BOTH + * mode. + */ + conn->asyncStatus = PGASYNC_BUSY; + return -1; + } + + /* Drop the processed message and loop around for another */ + conn->inStart = conn->inCursor; + } +} + +/* + * PQgetCopyData - read a row of data from the backend during COPY OUT + * or COPY BOTH + * + * If successful, sets *buffer to point to a malloc'd row of data, and + * returns row length (always > 0) as result. + * Returns 0 if no row available yet (only possible if async is true), + * -1 if end of copy (consult PQgetResult), or -2 if error (consult + * PQerrorMessage). + */ +int +pqGetCopyData3(PGconn *conn, char **buffer, int async) +{ + int msgLength; + + for (;;) + { + /* + * Collect the next input message. To make life simpler for async + * callers, we keep returning 0 until the next message is fully + * available, even if it is not Copy Data. + */ + msgLength = getCopyDataMessage(conn); + if (msgLength < 0) + return msgLength; /* end-of-copy or error */ + if (msgLength == 0) + { + /* Don't block if async read requested */ + if (async) + return 0; + /* Need to load more data */ + if (pqWait(TRUE, FALSE, conn) || + pqReadData(conn) < 0) + return -2; + continue; + } + + /* + * Drop zero-length messages (shouldn't happen anyway). Otherwise + * pass the data back to the caller. + */ + msgLength -= 4; + if (msgLength > 0) + { + *buffer = (char *) malloc(msgLength + 1); + if (*buffer == NULL) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("out of memory\n")); + return -2; + } + memcpy(*buffer, &conn->inBuffer[conn->inCursor], msgLength); + (*buffer)[msgLength] = '\0'; /* Add terminating null */ + + /* Mark message consumed */ + conn->inStart = conn->inCursor + msgLength; + + return msgLength; + } + + /* Empty, so drop it and loop around for another */ + conn->inStart = conn->inCursor; + } +} + +/* + * PQgetline - gets a newline-terminated string from the backend. + * + * See fe-exec.c for documentation. + */ +int +pqGetline3(PGconn *conn, char *s, int maxlen) +{ + int status; + + if (conn->sock == PGINVALID_SOCKET || + (conn->asyncStatus != PGASYNC_COPY_OUT && + conn->asyncStatus != PGASYNC_COPY_BOTH) || + conn->copy_is_binary) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("PQgetline: not doing text COPY OUT\n")); + *s = '\0'; + return EOF; + } + + while ((status = PQgetlineAsync(conn, s, maxlen - 1)) == 0) + { + /* need to load more data */ + if (pqWait(TRUE, FALSE, conn) || + pqReadData(conn) < 0) + { + *s = '\0'; + return EOF; + } + } + + if (status < 0) + { + /* End of copy detected; gin up old-style terminator */ + strcpy(s, "\\."); + return 0; + } + + /* Add null terminator, and strip trailing \n if present */ + if (s[status - 1] == '\n') + { + s[status - 1] = '\0'; + return 0; + } + else + { + s[status] = '\0'; + return 1; + } +} + +/* + * PQgetlineAsync - gets a COPY data row without blocking. + * + * See fe-exec.c for documentation. + */ +int +pqGetlineAsync3(PGconn *conn, char *buffer, int bufsize) +{ + int msgLength; + int avail; + + if (conn->asyncStatus != PGASYNC_COPY_OUT + && conn->asyncStatus != PGASYNC_COPY_BOTH) + return -1; /* we are not doing a copy... */ + + /* + * Recognize the next input message. To make life simpler for async + * callers, we keep returning 0 until the next message is fully available + * even if it is not Copy Data. This should keep PQendcopy from blocking. + * (Note: unlike pqGetCopyData3, we do not change asyncStatus here.) + */ + msgLength = getCopyDataMessage(conn); + if (msgLength < 0) + return -1; /* end-of-copy or error */ + if (msgLength == 0) + return 0; /* no data yet */ + + /* + * Move data from libpq's buffer to the caller's. In the case where a + * prior call found the caller's buffer too small, we use + * conn->copy_already_done to remember how much of the row was already + * returned to the caller. + */ + conn->inCursor += conn->copy_already_done; + avail = msgLength - 4 - conn->copy_already_done; + if (avail <= bufsize) + { + /* Able to consume the whole message */ + memcpy(buffer, &conn->inBuffer[conn->inCursor], avail); + /* Mark message consumed */ + conn->inStart = conn->inCursor + avail; + /* Reset state for next time */ + conn->copy_already_done = 0; + return avail; + } + else + { + /* We must return a partial message */ + memcpy(buffer, &conn->inBuffer[conn->inCursor], bufsize); + /* The message is NOT consumed from libpq's buffer */ + conn->copy_already_done += bufsize; + return bufsize; + } +} + +/* + * PQendcopy + * + * See fe-exec.c for documentation. + */ +int +pqEndcopy3(PGconn *conn) +{ + PGresult *result; + + if (conn->asyncStatus != PGASYNC_COPY_IN && + conn->asyncStatus != PGASYNC_COPY_OUT && + conn->asyncStatus != PGASYNC_COPY_BOTH) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("no COPY in progress\n")); + return 1; + } + + /* Send the CopyDone message if needed */ + if (conn->asyncStatus == PGASYNC_COPY_IN || + conn->asyncStatus == PGASYNC_COPY_BOTH) + { + if (pqPutMsgStart('c', false, conn) < 0 || + pqPutMsgEnd(conn) < 0) + return 1; + + /* + * If we sent the COPY command in extended-query mode, we must issue a + * Sync as well. + */ + if (conn->queryclass != PGQUERY_SIMPLE) + { + if (pqPutMsgStart('S', false, conn) < 0 || + pqPutMsgEnd(conn) < 0) + return 1; + } + } + + /* + * make sure no data is waiting to be sent, abort if we are non-blocking + * and the flush fails + */ + if (pqFlush(conn) && pqIsnonblocking(conn)) + return 1; + + /* Return to active duty */ + conn->asyncStatus = PGASYNC_BUSY; + resetPQExpBuffer(&conn->errorMessage); + + /* + * Non blocking connections may have to abort at this point. If everyone + * played the game there should be no problem, but in error scenarios the + * expected messages may not have arrived yet. (We are assuming that the + * backend's packetizing will ensure that CommandComplete arrives along + * with the CopyDone; are there corner cases where that doesn't happen?) + */ + if (pqIsnonblocking(conn) && PQisBusy(conn)) + return 1; + + /* Wait for the completion response */ + result = PQgetResult(conn); + + /* Expecting a successful result */ + if (result && result->resultStatus == PGRES_COMMAND_OK) + { + PQclear(result); + return 0; + } + + /* + * Trouble. For backwards-compatibility reasons, we issue the error + * message as if it were a notice (would be nice to get rid of this + * silliness, but too many apps probably don't handle errors from + * PQendcopy reasonably). Note that the app can still obtain the error + * status from the PGconn object. + */ + if (conn->errorMessage.len > 0) + { + /* We have to strip the trailing newline ... pain in neck... */ + char svLast = conn->errorMessage.data[conn->errorMessage.len - 1]; + + if (svLast == '\n') + conn->errorMessage.data[conn->errorMessage.len - 1] = '\0'; + pqInternalNotice(&conn->noticeHooks, "%s", conn->errorMessage.data); + conn->errorMessage.data[conn->errorMessage.len - 1] = svLast; + } + + PQclear(result); + + return 1; +} + + +/* + * PQfn - Send a function call to the POSTGRES backend. + * + * See fe-exec.c for documentation. + */ +PGresult * +pqFunctionCall3(PGconn *conn, Oid fnid, + int *result_buf, int *actual_result_len, + int result_is_int, + const PQArgBlock *args, int nargs) +{ + bool needInput = false; + ExecStatusType status = PGRES_FATAL_ERROR; + char id; + int msgLength; + int avail; + int i; + + /* PQfn already validated connection state */ + + if (pqPutMsgStart('F', false, conn) < 0 || /* function call msg */ + pqPutInt(fnid, 4, conn) < 0 || /* function id */ + pqPutInt(1, 2, conn) < 0 || /* # of format codes */ + pqPutInt(1, 2, conn) < 0 || /* format code: BINARY */ + pqPutInt(nargs, 2, conn) < 0) /* # of args */ + { + pqHandleSendFailure(conn); + return NULL; + } + + for (i = 0; i < nargs; ++i) + { /* len.int4 + contents */ + if (pqPutInt(args[i].len, 4, conn)) + { + pqHandleSendFailure(conn); + return NULL; + } + if (args[i].len == -1) + continue; /* it's NULL */ + + if (args[i].isint) + { + if (pqPutInt(args[i].u.integer, args[i].len, conn)) + { + pqHandleSendFailure(conn); + return NULL; + } + } + else + { + if (pqPutnchar((char *) args[i].u.ptr, args[i].len, conn)) + { + pqHandleSendFailure(conn); + return NULL; + } + } + } + + if (pqPutInt(1, 2, conn) < 0) /* result format code: BINARY */ + { + pqHandleSendFailure(conn); + return NULL; + } + + if (pqPutMsgEnd(conn) < 0 || + pqFlush(conn)) + { + pqHandleSendFailure(conn); + return NULL; + } + + for (;;) + { + if (needInput) + { + /* Wait for some data to arrive (or for the channel to close) */ + if (pqWait(TRUE, FALSE, conn) || + pqReadData(conn) < 0) + break; + } + + /* + * Scan the message. If we run out of data, loop around to try again. + */ + needInput = true; + + conn->inCursor = conn->inStart; + if (pqGetc(&id, conn)) + continue; + if (pqGetInt(&msgLength, 4, conn)) + continue; + + /* + * Try to validate message type/length here. A length less than 4 is + * definitely broken. Large lengths should only be believed for a few + * message types. + */ + if (msgLength < 4) + { + handleSyncLoss(conn, id, msgLength); + break; + } + if (msgLength > 30000 && !VALID_LONG_MESSAGE_TYPE(id)) + { + handleSyncLoss(conn, id, msgLength); + break; + } + + /* + * Can't process if message body isn't all here yet. + */ + msgLength -= 4; + avail = conn->inEnd - conn->inCursor; + if (avail < msgLength) + { + /* + * Before looping, enlarge the input buffer if needed to hold the + * whole message. See notes in parseInput. + */ + if (pqCheckInBufferSpace(conn->inCursor + (size_t) msgLength, + conn)) + { + /* + * XXX add some better recovery code... plan is to skip over + * the message using its length, then report an error. For the + * moment, just treat this like loss of sync (which indeed it + * might be!) + */ + handleSyncLoss(conn, id, msgLength); + break; + } + continue; + } + + /* + * We should see V or E response to the command, but might get N + * and/or A notices first. We also need to swallow the final Z before + * returning. + */ + switch (id) + { + case 'V': /* function result */ + if (pqGetInt(actual_result_len, 4, conn)) + continue; + if (*actual_result_len != -1) + { + if (result_is_int) + { + if (pqGetInt(result_buf, *actual_result_len, conn)) + continue; + } + else + { + if (pqGetnchar((char *) result_buf, + *actual_result_len, + conn)) + continue; + } + } + /* correctly finished function result message */ + status = PGRES_COMMAND_OK; + break; + case 'E': /* error return */ + if (pqGetErrorNotice3(conn, true)) + continue; + status = PGRES_FATAL_ERROR; + break; + case 'A': /* notify message */ + /* handle notify and go back to processing return values */ + if (getNotify(conn)) + continue; + break; + case 'N': /* notice */ + /* handle notice and go back to processing return values */ + if (pqGetErrorNotice3(conn, false)) + continue; + break; + case 'Z': /* backend is ready for new query */ + if (getReadyForQuery(conn)) + continue; + /* consume the message and exit */ + conn->inStart += 5 + msgLength; + /* if we saved a result object (probably an error), use it */ + if (conn->result) + return pqPrepareAsyncResult(conn); + return PQmakeEmptyPGresult(conn, status); + case 'S': /* parameter status */ + if (getParameterStatus(conn)) + continue; + break; + default: + /* The backend violates the protocol. */ + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("protocol error: id=0x%x\n"), + id); + pqSaveErrorResult(conn); + /* trust the specified message length as what to skip */ + conn->inStart += 5 + msgLength; + return pqPrepareAsyncResult(conn); + } + /* Completed this message, keep going */ + /* trust the specified message length as what to skip */ + conn->inStart += 5 + msgLength; + needInput = false; + } + + /* + * We fall out of the loop only upon failing to read data. + * conn->errorMessage has been set by pqWait or pqReadData. We want to + * append it to any already-received error message. + */ + pqSaveErrorResult(conn); + return pqPrepareAsyncResult(conn); +} + + +/* + * Construct startup packet + * + * Returns a malloc'd packet buffer, or NULL if out of memory + */ +char * +pqBuildStartupPacket3(PGconn *conn, int *packetlen, + const PQEnvironmentOption *options) +{ + char *startpacket; + + *packetlen = build_startup_packet(conn, NULL, options); + startpacket = (char *) malloc(*packetlen); + if (!startpacket) + return NULL; + *packetlen = build_startup_packet(conn, startpacket, options); + return startpacket; +} + +/* + * Build a startup packet given a filled-in PGconn structure. + * + * We need to figure out how much space is needed, then fill it in. + * To avoid duplicate logic, this routine is called twice: the first time + * (with packet == NULL) just counts the space needed, the second time + * (with packet == allocated space) fills it in. Return value is the number + * of bytes used. + */ +static int +build_startup_packet(const PGconn *conn, char *packet, + const PQEnvironmentOption *options) +{ + int packet_len = 0; + const PQEnvironmentOption *next_eo; + const char *val; + + /* Protocol version comes first. */ + if (packet) + { + ProtocolVersion pv = htonl(conn->pversion); + + memcpy(packet + packet_len, &pv, sizeof(ProtocolVersion)); + } + packet_len += sizeof(ProtocolVersion); + + /* Add user name, database name, options */ + +#define ADD_STARTUP_OPTION(optname, optval) \ + do { \ + if (packet) \ + strcpy(packet + packet_len, optname); \ + packet_len += strlen(optname) + 1; \ + if (packet) \ + strcpy(packet + packet_len, optval); \ + packet_len += strlen(optval) + 1; \ + } while(0) + + if (conn->pguser && conn->pguser[0]) + ADD_STARTUP_OPTION("user", conn->pguser); + if (conn->dbName && conn->dbName[0]) + ADD_STARTUP_OPTION("database", conn->dbName); + if (conn->replication && conn->replication[0]) + ADD_STARTUP_OPTION("replication", conn->replication); + if (conn->pgoptions && conn->pgoptions[0]) + ADD_STARTUP_OPTION("options", conn->pgoptions); + if (conn->send_appname) + { + /* Use appname if present, otherwise use fallback */ + val = conn->appname ? conn->appname : conn->fbappname; + if (val && val[0]) + ADD_STARTUP_OPTION("application_name", val); + } + + if (conn->client_encoding_initial && conn->client_encoding_initial[0]) + ADD_STARTUP_OPTION("client_encoding", conn->client_encoding_initial); + + /* Add any environment-driven GUC settings needed */ + for (next_eo = options; next_eo->envName; next_eo++) + { + if ((val = getenv(next_eo->envName)) != NULL) + { + if (pg_strcasecmp(val, "default") != 0) + ADD_STARTUP_OPTION(next_eo->pgName, val); + } + } + + /* Add trailing terminator */ + if (packet) + packet[packet_len] = '\0'; + packet_len++; + + return packet_len; +} diff --git a/libpq/fe-secure.c b/libpq/fe-secure.c new file mode 100644 index 0000000..94e47a5 --- /dev/null +++ b/libpq/fe-secure.c @@ -0,0 +1,505 @@ +/*------------------------------------------------------------------------- + * + * fe-secure.c + * functions related to setting up a secure connection to the backend. + * Secure connections are expected to provide confidentiality, + * message integrity and endpoint authentication. + * + * + * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/interfaces/libpq/fe-secure.c + * + * NOTES + * + * We don't provide informational callbacks here (like + * info_cb() in be-secure.c), since there's no good mechanism to + * display such information to the user. + * + *------------------------------------------------------------------------- + */ + +#include "postgres_fe.h" + +#include +#include +#include + +#include "libpq-fe.h" +#include "fe-auth.h" +#include "libpq-int.h" + +#ifdef WIN32 +#include "win32.h" +#else +#include +#include +#include +#include +#ifdef HAVE_NETINET_TCP_H +#include +#endif +#include +#endif + +#include + +#ifdef ENABLE_THREAD_SAFETY +#ifdef WIN32 +#include "pthread-win32.h" +#else +#include +#endif +#endif + +/* + * Macros to handle disabling and then restoring the state of SIGPIPE handling. + * On Windows, these are all no-ops since there's no SIGPIPEs. + */ + +#ifndef WIN32 + +#define SIGPIPE_MASKED(conn) ((conn)->sigpipe_so || (conn)->sigpipe_flag) + +#ifdef ENABLE_THREAD_SAFETY + +struct sigpipe_info +{ + sigset_t oldsigmask; + bool sigpipe_pending; + bool got_epipe; +}; + +#define DECLARE_SIGPIPE_INFO(spinfo) struct sigpipe_info spinfo + +#define DISABLE_SIGPIPE(conn, spinfo, failaction) \ + do { \ + (spinfo).got_epipe = false; \ + if (!SIGPIPE_MASKED(conn)) \ + { \ + if (pq_block_sigpipe(&(spinfo).oldsigmask, \ + &(spinfo).sigpipe_pending) < 0) \ + failaction; \ + } \ + } while (0) + +#define REMEMBER_EPIPE(spinfo, cond) \ + do { \ + if (cond) \ + (spinfo).got_epipe = true; \ + } while (0) + +#define RESTORE_SIGPIPE(conn, spinfo) \ + do { \ + if (!SIGPIPE_MASKED(conn)) \ + pq_reset_sigpipe(&(spinfo).oldsigmask, (spinfo).sigpipe_pending, \ + (spinfo).got_epipe); \ + } while (0) +#else /* !ENABLE_THREAD_SAFETY */ + +#define DECLARE_SIGPIPE_INFO(spinfo) pqsigfunc spinfo = NULL + +#define DISABLE_SIGPIPE(conn, spinfo, failaction) \ + do { \ + if (!SIGPIPE_MASKED(conn)) \ + spinfo = pqsignal(SIGPIPE, SIG_IGN); \ + } while (0) + +#define REMEMBER_EPIPE(spinfo, cond) + +#define RESTORE_SIGPIPE(conn, spinfo) \ + do { \ + if (!SIGPIPE_MASKED(conn)) \ + pqsignal(SIGPIPE, spinfo); \ + } while (0) +#endif /* ENABLE_THREAD_SAFETY */ +#else /* WIN32 */ + +#define DECLARE_SIGPIPE_INFO(spinfo) +#define DISABLE_SIGPIPE(conn, spinfo, failaction) +#define REMEMBER_EPIPE(spinfo, cond) +#define RESTORE_SIGPIPE(conn, spinfo) +#endif /* WIN32 */ + +/* ------------------------------------------------------------ */ +/* Procedures common to all secure sessions */ +/* ------------------------------------------------------------ */ + + +/* + * Exported function to allow application to tell us it's already + * initialized OpenSSL. + */ +void +PQinitSSL(int do_init) +{ +#ifdef USE_SSL + pgtls_init_library(do_init, do_init); +#endif +} + +/* + * Exported function to allow application to tell us it's already + * initialized OpenSSL and/or libcrypto. + */ +void +PQinitOpenSSL(int do_ssl, int do_crypto) +{ +#ifdef USE_SSL + pgtls_init_library(do_ssl, do_crypto); +#endif +} + +/* + * Initialize global SSL context + */ +int +pqsecure_initialize(PGconn *conn) +{ + int r = 0; + +#ifdef USE_SSL + r = pgtls_init(conn); +#endif + + return r; +} + +/* + * Begin or continue negotiating a secure session. + */ +PostgresPollingStatusType +pqsecure_open_client(PGconn *conn) +{ +#ifdef USE_SSL + return pgtls_open_client(conn); +#else + /* shouldn't get here */ + return PGRES_POLLING_FAILED; +#endif +} + +/* + * Close secure session. + */ +void +pqsecure_close(PGconn *conn) +{ +#ifdef USE_SSL + if (conn->ssl_in_use) + pgtls_close(conn); +#endif +} + +/* + * Read data from a secure connection. + * + * On failure, this function is responsible for putting a suitable message + * into conn->errorMessage. The caller must still inspect errno, but only + * to determine whether to continue/retry after error. + */ +ssize_t +pqsecure_read(PGconn *conn, void *ptr, size_t len) +{ + ssize_t n; + +#ifdef USE_SSL + if (conn->ssl_in_use) + { + n = pgtls_read(conn, ptr, len); + } + else +#endif + { + n = pqsecure_raw_read(conn, ptr, len); + } + + return n; +} + +ssize_t +pqsecure_raw_read(PGconn *conn, void *ptr, size_t len) +{ + ssize_t n; + int result_errno = 0; + char sebuf[256]; + + n = recv(conn->sock, ptr, len, 0); + + if (n < 0) + { + result_errno = SOCK_ERRNO; + + /* Set error message if appropriate */ + switch (result_errno) + { +#ifdef EAGAIN + case EAGAIN: +#endif +#if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN)) + case EWOULDBLOCK: +#endif + case EINTR: + /* no error message, caller is expected to retry */ + break; + +#ifdef ECONNRESET + case ECONNRESET: + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext( + "server closed the connection unexpectedly\n" + "\tThis probably means the server terminated abnormally\n" + "\tbefore or while processing the request.\n")); + break; +#endif + + default: + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not receive data from server: %s\n"), + SOCK_STRERROR(result_errno, + sebuf, sizeof(sebuf))); + break; + } + } + + /* ensure we return the intended errno to caller */ + SOCK_ERRNO_SET(result_errno); + + return n; +} + +/* + * Write data to a secure connection. + * + * On failure, this function is responsible for putting a suitable message + * into conn->errorMessage. The caller must still inspect errno, but only + * to determine whether to continue/retry after error. + */ +ssize_t +pqsecure_write(PGconn *conn, const void *ptr, size_t len) +{ + ssize_t n; + +#ifdef USE_SSL + if (conn->ssl_in_use) + { + n = pgtls_write(conn, ptr, len); + } + else +#endif + { + n = pqsecure_raw_write(conn, ptr, len); + } + + return n; +} + +ssize_t +pqsecure_raw_write(PGconn *conn, const void *ptr, size_t len) +{ + ssize_t n; + int flags = 0; + int result_errno = 0; + char sebuf[256]; + + DECLARE_SIGPIPE_INFO(spinfo); + +#ifdef MSG_NOSIGNAL + if (conn->sigpipe_flag) + flags |= MSG_NOSIGNAL; + +retry_masked: +#endif /* MSG_NOSIGNAL */ + + DISABLE_SIGPIPE(conn, spinfo, return -1); + + n = send(conn->sock, ptr, len, flags); + + if (n < 0) + { + result_errno = SOCK_ERRNO; + + /* + * If we see an EINVAL, it may be because MSG_NOSIGNAL isn't available + * on this machine. So, clear sigpipe_flag so we don't try the flag + * again, and retry the send(). + */ +#ifdef MSG_NOSIGNAL + if (flags != 0 && result_errno == EINVAL) + { + conn->sigpipe_flag = false; + flags = 0; + goto retry_masked; + } +#endif /* MSG_NOSIGNAL */ + + /* Set error message if appropriate */ + switch (result_errno) + { +#ifdef EAGAIN + case EAGAIN: +#endif +#if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN)) + case EWOULDBLOCK: +#endif + case EINTR: + /* no error message, caller is expected to retry */ + break; + + case EPIPE: + /* Set flag for EPIPE */ + REMEMBER_EPIPE(spinfo, true); + /* FALL THRU */ + +#ifdef ECONNRESET + case ECONNRESET: +#endif + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext( + "server closed the connection unexpectedly\n" + "\tThis probably means the server terminated abnormally\n" + "\tbefore or while processing the request.\n")); + break; + + default: + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not send data to server: %s\n"), + SOCK_STRERROR(result_errno, + sebuf, sizeof(sebuf))); + break; + } + } + + RESTORE_SIGPIPE(conn, spinfo); + + /* ensure we return the intended errno to caller */ + SOCK_ERRNO_SET(result_errno); + + return n; +} + +/* Dummy versions of SSL info functions, when built without SSL support */ +#ifndef USE_SSL + +int +PQsslInUse(PGconn *conn) +{ + return 0; +} + +void * +PQgetssl(PGconn *conn) +{ + return NULL; +} + +void * +PQsslStruct(PGconn *conn, const char *struct_name) +{ + return NULL; +} + +const char * +PQsslAttribute(PGconn *conn, const char *attribute_name) +{ + return NULL; +} + +const char *const * +PQsslAttributeNames(PGconn *conn) +{ + static const char *const result[] = {NULL}; + + return result; +} +#endif /* USE_SSL */ + + +#if defined(ENABLE_THREAD_SAFETY) && !defined(WIN32) + +/* + * Block SIGPIPE for this thread. This prevents send()/write() from exiting + * the application. + */ +int +pq_block_sigpipe(sigset_t *osigset, bool *sigpipe_pending) +{ + sigset_t sigpipe_sigset; + sigset_t sigset; + + sigemptyset(&sigpipe_sigset); + sigaddset(&sigpipe_sigset, SIGPIPE); + + /* Block SIGPIPE and save previous mask for later reset */ + SOCK_ERRNO_SET(pthread_sigmask(SIG_BLOCK, &sigpipe_sigset, osigset)); + if (SOCK_ERRNO) + return -1; + + /* We can have a pending SIGPIPE only if it was blocked before */ + if (sigismember(osigset, SIGPIPE)) + { + /* Is there a pending SIGPIPE? */ + if (sigpending(&sigset) != 0) + return -1; + + if (sigismember(&sigset, SIGPIPE)) + *sigpipe_pending = true; + else + *sigpipe_pending = false; + } + else + *sigpipe_pending = false; + + return 0; +} + +/* + * Discard any pending SIGPIPE and reset the signal mask. + * + * Note: we are effectively assuming here that the C library doesn't queue + * up multiple SIGPIPE events. If it did, then we'd accidentally leave + * ours in the queue when an event was already pending and we got another. + * As long as it doesn't queue multiple events, we're OK because the caller + * can't tell the difference. + * + * The caller should say got_epipe = FALSE if it is certain that it + * didn't get an EPIPE error; in that case we'll skip the clear operation + * and things are definitely OK, queuing or no. If it got one or might have + * gotten one, pass got_epipe = TRUE. + * + * We do not want this to change errno, since if it did that could lose + * the error code from a preceding send(). We essentially assume that if + * we were able to do pq_block_sigpipe(), this can't fail. + */ +void +pq_reset_sigpipe(sigset_t *osigset, bool sigpipe_pending, bool got_epipe) +{ + int save_errno = SOCK_ERRNO; + int signo; + sigset_t sigset; + + /* Clear SIGPIPE only if none was pending */ + if (got_epipe && !sigpipe_pending) + { + if (sigpending(&sigset) == 0 && + sigismember(&sigset, SIGPIPE)) + { + sigset_t sigpipe_sigset; + + sigemptyset(&sigpipe_sigset); + sigaddset(&sigpipe_sigset, SIGPIPE); + + sigwait(&sigpipe_sigset, &signo); + } + } + + /* Restore saved block mask */ + pthread_sigmask(SIG_SETMASK, osigset, NULL); + + SOCK_ERRNO_SET(save_errno); +} + +#endif /* ENABLE_THREAD_SAFETY && !WIN32 */ diff --git a/libpq/getpeereid.c b/libpq/getpeereid.c new file mode 100644 index 0000000..5f8c0be --- /dev/null +++ b/libpq/getpeereid.c @@ -0,0 +1,80 @@ +/*------------------------------------------------------------------------- + * + * getpeereid.c + * get peer userid for UNIX-domain socket connection + * + * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * + * + * IDENTIFICATION + * src/port/getpeereid.c + * + *------------------------------------------------------------------------- + */ + +#include "c.h" + +#include +#include +#include +#ifdef HAVE_SYS_UN_H +#include +#endif +#ifdef HAVE_UCRED_H +#include +#endif +#ifdef HAVE_SYS_UCRED_H +#include +#endif + + +/* + * BSD-style getpeereid() for platforms that lack it. + */ +int +getpeereid(int sock, uid_t *uid, gid_t *gid) +{ +#if defined(SO_PEERCRED) + /* Linux: use getsockopt(SO_PEERCRED) */ + struct ucred peercred; + ACCEPT_TYPE_ARG3 so_len = sizeof(peercred); + + if (getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &peercred, &so_len) != 0 || + so_len != sizeof(peercred)) + return -1; + *uid = peercred.uid; + *gid = peercred.gid; + return 0; +#elif defined(LOCAL_PEERCRED) + /* Debian with FreeBSD kernel: use getsockopt(LOCAL_PEERCRED) */ + struct xucred peercred; + ACCEPT_TYPE_ARG3 so_len = sizeof(peercred); + + if (getsockopt(sock, 0, LOCAL_PEERCRED, &peercred, &so_len) != 0 || + so_len != sizeof(peercred) || + peercred.cr_version != XUCRED_VERSION) + return -1; + *uid = peercred.cr_uid; + *gid = peercred.cr_gid; + return 0; +#elif defined(HAVE_GETPEERUCRED) + /* Solaris: use getpeerucred() */ + ucred_t *ucred; + + ucred = NULL; /* must be initialized to NULL */ + if (getpeerucred(sock, &ucred) == -1) + return -1; + + *uid = ucred_geteuid(ucred); + *gid = ucred_getegid(ucred); + ucred_free(ucred); + + if (*uid == (uid_t) (-1) || *gid == (gid_t) (-1)) + return -1; + return 0; +#else + /* No implementation available on this platform */ + errno = ENOSYS; + return -1; +#endif +} diff --git a/libpq/inet_net_ntop.c b/libpq/inet_net_ntop.c new file mode 100644 index 0000000..047895e --- /dev/null +++ b/libpq/inet_net_ntop.c @@ -0,0 +1,298 @@ +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996,1999 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT + * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * src/backend/utils/adt/inet_net_ntop.c + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static const char rcsid[] = "Id: inet_net_ntop.c,v 1.1.2.2 2004/03/09 09:17:27 marka Exp $"; +#endif + +#ifndef FRONTEND +#include "postgres.h" +#else +#include "postgres_fe.h" +#endif + +#include +#include +#include +#include + +#ifndef FRONTEND +#include "utils/inet.h" +#else +/* + * In a frontend build, we can't include inet.h, but we still need to have + * sensible definitions of these two constants. Note that inet_net_ntop() + * assumes that PGSQL_AF_INET is equal to AF_INET. + */ +#define PGSQL_AF_INET (AF_INET + 0) +#define PGSQL_AF_INET6 (AF_INET + 1) +#endif + + +#define NS_IN6ADDRSZ 16 +#define NS_INT16SZ 2 + +#ifdef SPRINTF_CHAR +#define SPRINTF(x) strlen(sprintf/**/x) +#else +#define SPRINTF(x) ((size_t)sprintf x) +#endif + +static char *inet_net_ntop_ipv4(const u_char *src, int bits, + char *dst, size_t size); +static char *inet_net_ntop_ipv6(const u_char *src, int bits, + char *dst, size_t size); + + +/* + * char * + * inet_net_ntop(af, src, bits, dst, size) + * convert host/network address from network to presentation format. + * "src"'s size is determined from its "af". + * return: + * pointer to dst, or NULL if an error occurred (check errno). + * note: + * 192.5.5.1/28 has a nonzero host part, which means it isn't a network + * as called for by inet_net_pton() but it can be a host address with + * an included netmask. + * author: + * Paul Vixie (ISC), October 1998 + */ +char * +inet_net_ntop(int af, const void *src, int bits, char *dst, size_t size) +{ + /* + * We need to cover both the address family constants used by the PG inet + * type (PGSQL_AF_INET and PGSQL_AF_INET6) and those used by the system + * libraries (AF_INET and AF_INET6). We can safely assume PGSQL_AF_INET + * == AF_INET, but the INET6 constants are very likely to be different. If + * AF_INET6 isn't defined, silently ignore it. + */ + switch (af) + { + case PGSQL_AF_INET: + return (inet_net_ntop_ipv4(src, bits, dst, size)); + case PGSQL_AF_INET6: +#if defined(AF_INET6) && AF_INET6 != PGSQL_AF_INET6 + case AF_INET6: +#endif + return (inet_net_ntop_ipv6(src, bits, dst, size)); + default: + errno = EAFNOSUPPORT; + return (NULL); + } +} + +/* + * static char * + * inet_net_ntop_ipv4(src, bits, dst, size) + * convert IPv4 network address from network to presentation format. + * "src"'s size is determined from its "af". + * return: + * pointer to dst, or NULL if an error occurred (check errno). + * note: + * network byte order assumed. this means 192.5.5.240/28 has + * 0b11110000 in its fourth octet. + * author: + * Paul Vixie (ISC), October 1998 + */ +static char * +inet_net_ntop_ipv4(const u_char *src, int bits, char *dst, size_t size) +{ + char *odst = dst; + char *t; + int len = 4; + int b; + + if (bits < 0 || bits > 32) + { + errno = EINVAL; + return (NULL); + } + + /* Always format all four octets, regardless of mask length. */ + for (b = len; b > 0; b--) + { + if (size <= sizeof ".255") + goto emsgsize; + t = dst; + if (dst != odst) + *dst++ = '.'; + dst += SPRINTF((dst, "%u", *src++)); + size -= (size_t) (dst - t); + } + + /* don't print masklen if 32 bits */ + if (bits != 32) + { + if (size <= sizeof "/32") + goto emsgsize; + dst += SPRINTF((dst, "/%u", bits)); + } + + return (odst); + +emsgsize: + errno = EMSGSIZE; + return (NULL); +} + +static int +decoct(const u_char *src, int bytes, char *dst, size_t size) +{ + char *odst = dst; + char *t; + int b; + + for (b = 1; b <= bytes; b++) + { + if (size <= sizeof "255.") + return (0); + t = dst; + dst += SPRINTF((dst, "%u", *src++)); + if (b != bytes) + { + *dst++ = '.'; + *dst = '\0'; + } + size -= (size_t) (dst - t); + } + return (dst - odst); +} + +static char * +inet_net_ntop_ipv6(const u_char *src, int bits, char *dst, size_t size) +{ + /* + * Note that int32_t and int16_t need only be "at least" large enough to + * contain a value of the specified size. On some systems, like Crays, + * there is no such thing as an integer variable with 16 bits. Keep this + * in mind if you think this function should have been coded to use + * pointer overlays. All the world's not a VAX. + */ + char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255/128"]; + char *tp; + struct + { + int base, + len; + } best, cur; + u_int words[NS_IN6ADDRSZ / NS_INT16SZ]; + int i; + + if ((bits < -1) || (bits > 128)) + { + errno = EINVAL; + return (NULL); + } + + /* + * Preprocess: Copy the input (bytewise) array into a wordwise array. Find + * the longest run of 0x00's in src[] for :: shorthanding. + */ + memset(words, '\0', sizeof words); + for (i = 0; i < NS_IN6ADDRSZ; i++) + words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3)); + best.base = -1; + cur.base = -1; + best.len = 0; + cur.len = 0; + for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) + { + if (words[i] == 0) + { + if (cur.base == -1) + cur.base = i, cur.len = 1; + else + cur.len++; + } + else + { + if (cur.base != -1) + { + if (best.base == -1 || cur.len > best.len) + best = cur; + cur.base = -1; + } + } + } + if (cur.base != -1) + { + if (best.base == -1 || cur.len > best.len) + best = cur; + } + if (best.base != -1 && best.len < 2) + best.base = -1; + + /* + * Format the result. + */ + tp = tmp; + for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) + { + /* Are we inside the best run of 0x00's? */ + if (best.base != -1 && i >= best.base && + i < (best.base + best.len)) + { + if (i == best.base) + *tp++ = ':'; + continue; + } + /* Are we following an initial run of 0x00s or any real hex? */ + if (i != 0) + *tp++ = ':'; + /* Is this address an encapsulated IPv4? */ + if (i == 6 && best.base == 0 && (best.len == 6 || + (best.len == 7 && words[7] != 0x0001) || + (best.len == 5 && words[5] == 0xffff))) + { + int n; + + n = decoct(src + 12, 4, tp, sizeof tmp - (tp - tmp)); + if (n == 0) + { + errno = EMSGSIZE; + return (NULL); + } + tp += strlen(tp); + break; + } + tp += SPRINTF((tp, "%x", words[i])); + } + + /* Was it a trailing run of 0x00's? */ + if (best.base != -1 && (best.base + best.len) == + (NS_IN6ADDRSZ / NS_INT16SZ)) + *tp++ = ':'; + *tp = '\0'; + + if (bits != -1 && bits != 128) + tp += SPRINTF((tp, "/%u", bits)); + + /* + * Check for overflow, copy, and we're done. + */ + if ((size_t) (tp - tmp) > size) + { + errno = EMSGSIZE; + return (NULL); + } + strcpy(dst, tmp); + return (dst); +} diff --git a/libpq/ip.c b/libpq/ip.c new file mode 100644 index 0000000..9591ed2 --- /dev/null +++ b/libpq/ip.c @@ -0,0 +1,819 @@ +/*------------------------------------------------------------------------- + * + * ip.c + * IPv6-aware network access. + * + * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/backend/libpq/ip.c + * + * This file and the IPV6 implementation were initially provided by + * Nigel Kukard , Linux Based Systems Design + * http://www.lbsd.net. + * + *------------------------------------------------------------------------- + */ + +/* This is intended to be used in both frontend and backend, so use c.h */ +#include "c.h" + +#include +#include +#include +#include +#include +#include +#ifdef HAVE_NETINET_TCP_H +#include +#endif +#include +#include + +#include "libpq/ip.h" + + +static int range_sockaddr_AF_INET(const struct sockaddr_in * addr, + const struct sockaddr_in * netaddr, + const struct sockaddr_in * netmask); + +#ifdef HAVE_IPV6 +static int range_sockaddr_AF_INET6(const struct sockaddr_in6 * addr, + const struct sockaddr_in6 * netaddr, + const struct sockaddr_in6 * netmask); +#endif + +#ifdef HAVE_UNIX_SOCKETS +static int getaddrinfo_unix(const char *path, + const struct addrinfo * hintsp, + struct addrinfo ** result); + +static int getnameinfo_unix(const struct sockaddr_un * sa, int salen, + char *node, int nodelen, + char *service, int servicelen, + int flags); +#endif + + +/* + * pg_getaddrinfo_all - get address info for Unix, IPv4 and IPv6 sockets + */ +int +pg_getaddrinfo_all(const char *hostname, const char *servname, + const struct addrinfo * hintp, struct addrinfo ** result) +{ + int rc; + + /* not all versions of getaddrinfo() zero *result on failure */ + *result = NULL; + +#ifdef HAVE_UNIX_SOCKETS + if (hintp->ai_family == AF_UNIX) + return getaddrinfo_unix(servname, hintp, result); +#endif + + /* NULL has special meaning to getaddrinfo(). */ + rc = getaddrinfo((!hostname || hostname[0] == '\0') ? NULL : hostname, + servname, hintp, result); + + return rc; +} + + +/* + * pg_freeaddrinfo_all - free addrinfo structures for IPv4, IPv6, or Unix + * + * Note: the ai_family field of the original hint structure must be passed + * so that we can tell whether the addrinfo struct was built by the system's + * getaddrinfo() routine or our own getaddrinfo_unix() routine. Some versions + * of getaddrinfo() might be willing to return AF_UNIX addresses, so it's + * not safe to look at ai_family in the addrinfo itself. + */ +void +pg_freeaddrinfo_all(int hint_ai_family, struct addrinfo * ai) +{ +#ifdef HAVE_UNIX_SOCKETS + if (hint_ai_family == AF_UNIX) + { + /* struct was built by getaddrinfo_unix (see pg_getaddrinfo_all) */ + while (ai != NULL) + { + struct addrinfo *p = ai; + + ai = ai->ai_next; + free(p->ai_addr); + free(p); + } + } + else +#endif /* HAVE_UNIX_SOCKETS */ + { + /* struct was built by getaddrinfo() */ + if (ai != NULL) + freeaddrinfo(ai); + } +} + + +/* + * pg_getnameinfo_all - get name info for Unix, IPv4 and IPv6 sockets + * + * The API of this routine differs from the standard getnameinfo() definition + * in two ways: first, the addr parameter is declared as sockaddr_storage + * rather than struct sockaddr, and second, the node and service fields are + * guaranteed to be filled with something even on failure return. + */ +int +pg_getnameinfo_all(const struct sockaddr_storage * addr, int salen, + char *node, int nodelen, + char *service, int servicelen, + int flags) +{ + int rc; + +#ifdef HAVE_UNIX_SOCKETS + if (addr && addr->ss_family == AF_UNIX) + rc = getnameinfo_unix((const struct sockaddr_un *) addr, salen, + node, nodelen, + service, servicelen, + flags); + else +#endif + rc = getnameinfo((const struct sockaddr *) addr, salen, + node, nodelen, + service, servicelen, + flags); + + if (rc != 0) + { + if (node) + strlcpy(node, "???", nodelen); + if (service) + strlcpy(service, "???", servicelen); + } + + return rc; +} + + +#if defined(HAVE_UNIX_SOCKETS) + +/* ------- + * getaddrinfo_unix - get unix socket info using IPv6-compatible API + * + * Bugs: only one addrinfo is set even though hintsp is NULL or + * ai_socktype is 0 + * AI_CANONNAME is not supported. + * ------- + */ +static int +getaddrinfo_unix(const char *path, const struct addrinfo * hintsp, + struct addrinfo ** result) +{ + struct addrinfo hints; + struct addrinfo *aip; + struct sockaddr_un *unp; + + *result = NULL; + + MemSet(&hints, 0, sizeof(hints)); + + if (strlen(path) >= sizeof(unp->sun_path)) + return EAI_FAIL; + + if (hintsp == NULL) + { + hints.ai_family = AF_UNIX; + hints.ai_socktype = SOCK_STREAM; + } + else + memcpy(&hints, hintsp, sizeof(hints)); + + if (hints.ai_socktype == 0) + hints.ai_socktype = SOCK_STREAM; + + if (hints.ai_family != AF_UNIX) + { + /* shouldn't have been called */ + return EAI_FAIL; + } + + aip = calloc(1, sizeof(struct addrinfo)); + if (aip == NULL) + return EAI_MEMORY; + + unp = calloc(1, sizeof(struct sockaddr_un)); + if (unp == NULL) + { + free(aip); + return EAI_MEMORY; + } + + aip->ai_family = AF_UNIX; + aip->ai_socktype = hints.ai_socktype; + aip->ai_protocol = hints.ai_protocol; + aip->ai_next = NULL; + aip->ai_canonname = NULL; + *result = aip; + + unp->sun_family = AF_UNIX; + aip->ai_addr = (struct sockaddr *) unp; + aip->ai_addrlen = sizeof(struct sockaddr_un); + + strcpy(unp->sun_path, path); + +#ifdef HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN + unp->sun_len = sizeof(struct sockaddr_un); +#endif + + return 0; +} + +/* + * Convert an address to a hostname. + */ +static int +getnameinfo_unix(const struct sockaddr_un * sa, int salen, + char *node, int nodelen, + char *service, int servicelen, + int flags) +{ + int ret = -1; + + /* Invalid arguments. */ + if (sa == NULL || sa->sun_family != AF_UNIX || + (node == NULL && service == NULL)) + return EAI_FAIL; + + if (node) + { + ret = snprintf(node, nodelen, "%s", "[local]"); + if (ret == -1 || ret > nodelen) + return EAI_MEMORY; + } + + if (service) + { + ret = snprintf(service, servicelen, "%s", sa->sun_path); + if (ret == -1 || ret > servicelen) + return EAI_MEMORY; + } + + return 0; +} +#endif /* HAVE_UNIX_SOCKETS */ + + +/* + * pg_range_sockaddr - is addr within the subnet specified by netaddr/netmask ? + * + * Note: caller must already have verified that all three addresses are + * in the same address family; and AF_UNIX addresses are not supported. + */ +int +pg_range_sockaddr(const struct sockaddr_storage * addr, + const struct sockaddr_storage * netaddr, + const struct sockaddr_storage * netmask) +{ + if (addr->ss_family == AF_INET) + return range_sockaddr_AF_INET((const struct sockaddr_in *) addr, + (const struct sockaddr_in *) netaddr, + (const struct sockaddr_in *) netmask); +#ifdef HAVE_IPV6 + else if (addr->ss_family == AF_INET6) + return range_sockaddr_AF_INET6((const struct sockaddr_in6 *) addr, + (const struct sockaddr_in6 *) netaddr, + (const struct sockaddr_in6 *) netmask); +#endif + else + return 0; +} + +static int +range_sockaddr_AF_INET(const struct sockaddr_in * addr, + const struct sockaddr_in * netaddr, + const struct sockaddr_in * netmask) +{ + if (((addr->sin_addr.s_addr ^ netaddr->sin_addr.s_addr) & + netmask->sin_addr.s_addr) == 0) + return 1; + else + return 0; +} + + +#ifdef HAVE_IPV6 + +static int +range_sockaddr_AF_INET6(const struct sockaddr_in6 * addr, + const struct sockaddr_in6 * netaddr, + const struct sockaddr_in6 * netmask) +{ + int i; + + for (i = 0; i < 16; i++) + { + if (((addr->sin6_addr.s6_addr[i] ^ netaddr->sin6_addr.s6_addr[i]) & + netmask->sin6_addr.s6_addr[i]) != 0) + return 0; + } + + return 1; +} +#endif /* HAVE_IPV6 */ + +/* + * pg_sockaddr_cidr_mask - make a network mask of the appropriate family + * and required number of significant bits + * + * numbits can be null, in which case the mask is fully set. + * + * The resulting mask is placed in *mask, which had better be big enough. + * + * Return value is 0 if okay, -1 if not. + */ +int +pg_sockaddr_cidr_mask(struct sockaddr_storage * mask, char *numbits, int family) +{ + long bits; + char *endptr; + + if (numbits == NULL) + { + bits = (family == AF_INET) ? 32 : 128; + } + else + { + bits = strtol(numbits, &endptr, 10); + if (*numbits == '\0' || *endptr != '\0') + return -1; + } + + switch (family) + { + case AF_INET: + { + struct sockaddr_in mask4; + long maskl; + + if (bits < 0 || bits > 32) + return -1; + memset(&mask4, 0, sizeof(mask4)); + /* avoid "x << 32", which is not portable */ + if (bits > 0) + maskl = (0xffffffffUL << (32 - (int) bits)) + & 0xffffffffUL; + else + maskl = 0; + mask4.sin_addr.s_addr = htonl(maskl); + memcpy(mask, &mask4, sizeof(mask4)); + break; + } + +#ifdef HAVE_IPV6 + case AF_INET6: + { + struct sockaddr_in6 mask6; + int i; + + if (bits < 0 || bits > 128) + return -1; + memset(&mask6, 0, sizeof(mask6)); + for (i = 0; i < 16; i++) + { + if (bits <= 0) + mask6.sin6_addr.s6_addr[i] = 0; + else if (bits >= 8) + mask6.sin6_addr.s6_addr[i] = 0xff; + else + { + mask6.sin6_addr.s6_addr[i] = + (0xff << (8 - (int) bits)) & 0xff; + } + bits -= 8; + } + memcpy(mask, &mask6, sizeof(mask6)); + break; + } +#endif + default: + return -1; + } + + mask->ss_family = family; + return 0; +} + + +/* + * Run the callback function for the addr/mask, after making sure the + * mask is sane for the addr. + */ +static void +run_ifaddr_callback(PgIfAddrCallback callback, void *cb_data, + struct sockaddr * addr, struct sockaddr * mask) +{ + struct sockaddr_storage fullmask; + + if (!addr) + return; + + /* Check that the mask is valid */ + if (mask) + { + if (mask->sa_family != addr->sa_family) + { + mask = NULL; + } + else if (mask->sa_family == AF_INET) + { + if (((struct sockaddr_in *) mask)->sin_addr.s_addr == INADDR_ANY) + mask = NULL; + } +#ifdef HAVE_IPV6 + else if (mask->sa_family == AF_INET6) + { + if (IN6_IS_ADDR_UNSPECIFIED(&((struct sockaddr_in6 *) mask)->sin6_addr)) + mask = NULL; + } +#endif + } + + /* If mask is invalid, generate our own fully-set mask */ + if (!mask) + { + pg_sockaddr_cidr_mask(&fullmask, NULL, addr->sa_family); + mask = (struct sockaddr *) & fullmask; + } + + (*callback) (addr, mask, cb_data); +} + +#ifdef WIN32 + +#include +#include + +/* + * Enumerate the system's network interface addresses and call the callback + * for each one. Returns 0 if successful, -1 if trouble. + * + * This version is for Win32. Uses the Winsock 2 functions (ie: ws2_32.dll) + */ +int +pg_foreach_ifaddr(PgIfAddrCallback callback, void *cb_data) +{ + INTERFACE_INFO *ptr, + *ii = NULL; + unsigned long length, + i; + unsigned long n_ii = 0; + SOCKET sock; + int error; + + sock = WSASocket(AF_INET, SOCK_DGRAM, 0, 0, 0, 0); + if (sock == INVALID_SOCKET) + return -1; + + while (n_ii < 1024) + { + n_ii += 64; + ptr = realloc(ii, sizeof(INTERFACE_INFO) * n_ii); + if (!ptr) + { + free(ii); + closesocket(sock); + errno = ENOMEM; + return -1; + } + + ii = ptr; + if (WSAIoctl(sock, SIO_GET_INTERFACE_LIST, 0, 0, + ii, n_ii * sizeof(INTERFACE_INFO), + &length, 0, 0) == SOCKET_ERROR) + { + error = WSAGetLastError(); + if (error == WSAEFAULT || error == WSAENOBUFS) + continue; /* need to make the buffer bigger */ + closesocket(sock); + free(ii); + return -1; + } + + break; + } + + for (i = 0; i < length / sizeof(INTERFACE_INFO); ++i) + run_ifaddr_callback(callback, cb_data, + (struct sockaddr *) & ii[i].iiAddress, + (struct sockaddr *) & ii[i].iiNetmask); + + closesocket(sock); + free(ii); + return 0; +} +#elif HAVE_GETIFADDRS /* && !WIN32 */ + +#ifdef HAVE_IFADDRS_H +#include +#endif + +/* + * Enumerate the system's network interface addresses and call the callback + * for each one. Returns 0 if successful, -1 if trouble. + * + * This version uses the getifaddrs() interface, which is available on + * BSDs, AIX, and modern Linux. + */ +int +pg_foreach_ifaddr(PgIfAddrCallback callback, void *cb_data) +{ + struct ifaddrs *ifa, + *l; + + if (getifaddrs(&ifa) < 0) + return -1; + + for (l = ifa; l; l = l->ifa_next) + run_ifaddr_callback(callback, cb_data, + l->ifa_addr, l->ifa_netmask); + + freeifaddrs(ifa); + return 0; +} +#else /* !HAVE_GETIFADDRS && !WIN32 */ + +#ifdef HAVE_SYS_IOCTL_H +#include +#endif + +#ifdef HAVE_NET_IF_H +#include +#endif + +#ifdef HAVE_SYS_SOCKIO_H +#include +#endif + +/* + * SIOCGIFCONF does not return IPv6 addresses on Solaris + * and HP/UX. So we prefer SIOCGLIFCONF if it's available. + * + * On HP/UX, however, it *only* returns IPv6 addresses, + * and the structs are named slightly differently too. + * We'd have to do another call with SIOCGIFCONF to get the + * IPv4 addresses as well. We don't currently bother, just + * fall back to SIOCGIFCONF on HP/UX. + */ + +#if defined(SIOCGLIFCONF) && !defined(__hpux) + +/* + * Enumerate the system's network interface addresses and call the callback + * for each one. Returns 0 if successful, -1 if trouble. + * + * This version uses ioctl(SIOCGLIFCONF). + */ +int +pg_foreach_ifaddr(PgIfAddrCallback callback, void *cb_data) +{ + struct lifconf lifc; + struct lifreq *lifr, + lmask; + struct sockaddr *addr, + *mask; + char *ptr, + *buffer = NULL; + size_t n_buffer = 1024; + pgsocket sock, + fd; + +#ifdef HAVE_IPV6 + pgsocket sock6; +#endif + int i, + total; + + sock = socket(AF_INET, SOCK_DGRAM, 0); + if (sock == PGINVALID_SOCKET) + return -1; + + while (n_buffer < 1024 * 100) + { + n_buffer += 1024; + ptr = realloc(buffer, n_buffer); + if (!ptr) + { + free(buffer); + close(sock); + errno = ENOMEM; + return -1; + } + + memset(&lifc, 0, sizeof(lifc)); + lifc.lifc_family = AF_UNSPEC; + lifc.lifc_buf = buffer = ptr; + lifc.lifc_len = n_buffer; + + if (ioctl(sock, SIOCGLIFCONF, &lifc) < 0) + { + if (errno == EINVAL) + continue; + free(buffer); + close(sock); + return -1; + } + + /* + * Some Unixes try to return as much data as possible, with no + * indication of whether enough space allocated. Don't believe we have + * it all unless there's lots of slop. + */ + if (lifc.lifc_len < n_buffer - 1024) + break; + } + +#ifdef HAVE_IPV6 + /* We'll need an IPv6 socket too for the SIOCGLIFNETMASK ioctls */ + sock6 = socket(AF_INET6, SOCK_DGRAM, 0); + if (sock6 == PGINVALID_SOCKET) + { + free(buffer); + close(sock); + return -1; + } +#endif + + total = lifc.lifc_len / sizeof(struct lifreq); + lifr = lifc.lifc_req; + for (i = 0; i < total; ++i) + { + addr = (struct sockaddr *) & lifr[i].lifr_addr; + memcpy(&lmask, &lifr[i], sizeof(struct lifreq)); +#ifdef HAVE_IPV6 + fd = (addr->sa_family == AF_INET6) ? sock6 : sock; +#else + fd = sock; +#endif + if (ioctl(fd, SIOCGLIFNETMASK, &lmask) < 0) + mask = NULL; + else + mask = (struct sockaddr *) & lmask.lifr_addr; + run_ifaddr_callback(callback, cb_data, addr, mask); + } + + free(buffer); + close(sock); +#ifdef HAVE_IPV6 + close(sock6); +#endif + return 0; +} +#elif defined(SIOCGIFCONF) + +/* + * Remaining Unixes use SIOCGIFCONF. Some only return IPv4 information + * here, so this is the least preferred method. Note that there is no + * standard way to iterate the struct ifreq returned in the array. + * On some OSs the structures are padded large enough for any address, + * on others you have to calculate the size of the struct ifreq. + */ + +/* Some OSs have _SIZEOF_ADDR_IFREQ, so just use that */ +#ifndef _SIZEOF_ADDR_IFREQ + +/* Calculate based on sockaddr.sa_len */ +#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN +#define _SIZEOF_ADDR_IFREQ(ifr) \ + ((ifr).ifr_addr.sa_len > sizeof(struct sockaddr) ? \ + (sizeof(struct ifreq) - sizeof(struct sockaddr) + \ + (ifr).ifr_addr.sa_len) : sizeof(struct ifreq)) + +/* Padded ifreq structure, simple */ +#else +#define _SIZEOF_ADDR_IFREQ(ifr) \ + sizeof (struct ifreq) +#endif +#endif /* !_SIZEOF_ADDR_IFREQ */ + +/* + * Enumerate the system's network interface addresses and call the callback + * for each one. Returns 0 if successful, -1 if trouble. + * + * This version uses ioctl(SIOCGIFCONF). + */ +int +pg_foreach_ifaddr(PgIfAddrCallback callback, void *cb_data) +{ + struct ifconf ifc; + struct ifreq *ifr, + *end, + addr, + mask; + char *ptr, + *buffer = NULL; + size_t n_buffer = 1024; + pgsocket sock; + + sock = socket(AF_INET, SOCK_DGRAM, 0); + if (sock == PGINVALID_SOCKET) + return -1; + + while (n_buffer < 1024 * 100) + { + n_buffer += 1024; + ptr = realloc(buffer, n_buffer); + if (!ptr) + { + free(buffer); + close(sock); + errno = ENOMEM; + return -1; + } + + memset(&ifc, 0, sizeof(ifc)); + ifc.ifc_buf = buffer = ptr; + ifc.ifc_len = n_buffer; + + if (ioctl(sock, SIOCGIFCONF, &ifc) < 0) + { + if (errno == EINVAL) + continue; + free(buffer); + close(sock); + return -1; + } + + /* + * Some Unixes try to return as much data as possible, with no + * indication of whether enough space allocated. Don't believe we have + * it all unless there's lots of slop. + */ + if (ifc.ifc_len < n_buffer - 1024) + break; + } + + end = (struct ifreq *) (buffer + ifc.ifc_len); + for (ifr = ifc.ifc_req; ifr < end;) + { + memcpy(&addr, ifr, sizeof(addr)); + memcpy(&mask, ifr, sizeof(mask)); + if (ioctl(sock, SIOCGIFADDR, &addr, sizeof(addr)) == 0 && + ioctl(sock, SIOCGIFNETMASK, &mask, sizeof(mask)) == 0) + run_ifaddr_callback(callback, cb_data, + &addr.ifr_addr, &mask.ifr_addr); + ifr = (struct ifreq *) ((char *) ifr + _SIZEOF_ADDR_IFREQ(*ifr)); + } + + free(buffer); + close(sock); + return 0; +} +#else /* !defined(SIOCGIFCONF) */ + +/* + * Enumerate the system's network interface addresses and call the callback + * for each one. Returns 0 if successful, -1 if trouble. + * + * This version is our fallback if there's no known way to get the + * interface addresses. Just return the standard loopback addresses. + */ +int +pg_foreach_ifaddr(PgIfAddrCallback callback, void *cb_data) +{ + struct sockaddr_in addr; + struct sockaddr_storage mask; + +#ifdef HAVE_IPV6 + struct sockaddr_in6 addr6; +#endif + + /* addr 127.0.0.1/8 */ + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = ntohl(0x7f000001); + memset(&mask, 0, sizeof(mask)); + pg_sockaddr_cidr_mask(&mask, "8", AF_INET); + run_ifaddr_callback(callback, cb_data, + (struct sockaddr *) & addr, + (struct sockaddr *) & mask); + +#ifdef HAVE_IPV6 + /* addr ::1/128 */ + memset(&addr6, 0, sizeof(addr6)); + addr6.sin6_family = AF_INET6; + addr6.sin6_addr.s6_addr[15] = 1; + memset(&mask, 0, sizeof(mask)); + pg_sockaddr_cidr_mask(&mask, "128", AF_INET6); + run_ifaddr_callback(callback, cb_data, + (struct sockaddr *) & addr6, + (struct sockaddr *) & mask); +#endif + + return 0; +} +#endif /* !defined(SIOCGIFCONF) */ + +#endif /* !HAVE_GETIFADDRS */ diff --git a/libpq/libpq-events.c b/libpq/libpq-events.c new file mode 100644 index 0000000..6db3e94 --- /dev/null +++ b/libpq/libpq-events.c @@ -0,0 +1,209 @@ +/*------------------------------------------------------------------------- + * + * libpq-events.c + * functions for supporting the libpq "events" API + * + * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/interfaces/libpq/libpq-events.c + * + *------------------------------------------------------------------------- + */ +#include "postgres_fe.h" + +#include "libpq-fe.h" +#include "libpq-int.h" + + +/* + * Registers an event proc with the given PGconn. + * + * The same proc can't be registered more than once in a PGconn. This + * restriction is required because we use the proc address to identify + * the event for purposes such as PQinstanceData(). + * + * The name argument is used within error messages to aid in debugging. + * A name must be supplied, but it needn't be unique. The string is + * copied, so the passed value needn't be long-lived. + * + * The passThrough argument is an application specific pointer and can be set + * to NULL if not required. It is passed through to the event proc whenever + * the event proc is called, and is not otherwise touched by libpq. + * + * The function returns a non-zero if successful. If the function fails, + * zero is returned. + */ +int +PQregisterEventProc(PGconn *conn, PGEventProc proc, + const char *name, void *passThrough) +{ + int i; + PGEventRegister regevt; + + if (!proc || !conn || !name || !*name) + return FALSE; /* bad arguments */ + + for (i = 0; i < conn->nEvents; i++) + { + if (conn->events[i].proc == proc) + return FALSE; /* already registered */ + } + + if (conn->nEvents >= conn->eventArraySize) + { + PGEvent *e; + int newSize; + + newSize = conn->eventArraySize ? conn->eventArraySize * 2 : 8; + if (conn->events) + e = (PGEvent *) realloc(conn->events, newSize * sizeof(PGEvent)); + else + e = (PGEvent *) malloc(newSize * sizeof(PGEvent)); + + if (!e) + return FALSE; + + conn->eventArraySize = newSize; + conn->events = e; + } + + conn->events[conn->nEvents].proc = proc; + conn->events[conn->nEvents].name = strdup(name); + if (!conn->events[conn->nEvents].name) + return FALSE; + conn->events[conn->nEvents].passThrough = passThrough; + conn->events[conn->nEvents].data = NULL; + conn->events[conn->nEvents].resultInitialized = FALSE; + conn->nEvents++; + + regevt.conn = conn; + if (!proc(PGEVT_REGISTER, ®evt, passThrough)) + { + conn->nEvents--; + free(conn->events[conn->nEvents].name); + return FALSE; + } + + return TRUE; +} + +/* + * Set some "instance data" for an event within a PGconn. + * Returns nonzero on success, zero on failure. + */ +int +PQsetInstanceData(PGconn *conn, PGEventProc proc, void *data) +{ + int i; + + if (!conn || !proc) + return FALSE; + + for (i = 0; i < conn->nEvents; i++) + { + if (conn->events[i].proc == proc) + { + conn->events[i].data = data; + return TRUE; + } + } + + return FALSE; +} + +/* + * Obtain the "instance data", if any, for the event. + */ +void * +PQinstanceData(const PGconn *conn, PGEventProc proc) +{ + int i; + + if (!conn || !proc) + return NULL; + + for (i = 0; i < conn->nEvents; i++) + { + if (conn->events[i].proc == proc) + return conn->events[i].data; + } + + return NULL; +} + +/* + * Set some "instance data" for an event within a PGresult. + * Returns nonzero on success, zero on failure. + */ +int +PQresultSetInstanceData(PGresult *result, PGEventProc proc, void *data) +{ + int i; + + if (!result || !proc) + return FALSE; + + for (i = 0; i < result->nEvents; i++) + { + if (result->events[i].proc == proc) + { + result->events[i].data = data; + return TRUE; + } + } + + return FALSE; +} + +/* + * Obtain the "instance data", if any, for the event. + */ +void * +PQresultInstanceData(const PGresult *result, PGEventProc proc) +{ + int i; + + if (!result || !proc) + return NULL; + + for (i = 0; i < result->nEvents; i++) + if (result->events[i].proc == proc) + return result->events[i].data; + + return NULL; +} + +/* + * Fire RESULTCREATE events for an application-created PGresult. + * + * The conn argument can be NULL if event procedures won't use it. + */ +int +PQfireResultCreateEvents(PGconn *conn, PGresult *res) +{ + int i; + + if (!res) + return FALSE; + + for (i = 0; i < res->nEvents; i++) + { + if (!res->events[i].resultInitialized) + { + PGEventResultCreate evt; + + evt.conn = conn; + evt.result = res; + if (!res->events[i].proc(PGEVT_RESULTCREATE, &evt, + res->events[i].passThrough)) + return FALSE; + + res->events[i].resultInitialized = TRUE; + } + } + + return TRUE; +} diff --git a/libpq/libpq-events.h b/libpq/libpq-events.h new file mode 100644 index 0000000..e2bdb28 --- /dev/null +++ b/libpq/libpq-events.h @@ -0,0 +1,94 @@ +/*------------------------------------------------------------------------- + * + * libpq-events.h + * This file contains definitions that are useful to applications + * that invoke the libpq "events" API, but are not interesting to + * ordinary users of libpq. + * + * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/interfaces/libpq/libpq-events.h + * + *------------------------------------------------------------------------- + */ + +#ifndef LIBPQ_EVENTS_H +#define LIBPQ_EVENTS_H + +#include "libpq-fe.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* Callback Event Ids */ +typedef enum +{ + PGEVT_REGISTER, + PGEVT_CONNRESET, + PGEVT_CONNDESTROY, + PGEVT_RESULTCREATE, + PGEVT_RESULTCOPY, + PGEVT_RESULTDESTROY +} PGEventId; + +typedef struct +{ + PGconn *conn; +} PGEventRegister; + +typedef struct +{ + PGconn *conn; +} PGEventConnReset; + +typedef struct +{ + PGconn *conn; +} PGEventConnDestroy; + +typedef struct +{ + PGconn *conn; + PGresult *result; +} PGEventResultCreate; + +typedef struct +{ + const PGresult *src; + PGresult *dest; +} PGEventResultCopy; + +typedef struct +{ + PGresult *result; +} PGEventResultDestroy; + +typedef int (*PGEventProc) (PGEventId evtId, void *evtInfo, void *passThrough); + +/* Registers an event proc with the given PGconn. */ +extern int PQregisterEventProc(PGconn *conn, PGEventProc proc, + const char *name, void *passThrough); + +/* Sets the PGconn instance data for the provided proc to data. */ +extern int PQsetInstanceData(PGconn *conn, PGEventProc proc, void *data); + +/* Gets the PGconn instance data for the provided proc. */ +extern void *PQinstanceData(const PGconn *conn, PGEventProc proc); + +/* Sets the PGresult instance data for the provided proc to data. */ +extern int PQresultSetInstanceData(PGresult *result, PGEventProc proc, void *data); + +/* Gets the PGresult instance data for the provided proc. */ +extern void *PQresultInstanceData(const PGresult *result, PGEventProc proc); + +/* Fires RESULTCREATE events for an application-created PGresult. */ +extern int PQfireResultCreateEvents(PGconn *conn, PGresult *res); + +#ifdef __cplusplus +} +#endif + +#endif /* LIBPQ_EVENTS_H */ diff --git a/libpq/libpq-fe.h b/libpq/libpq-fe.h new file mode 100644 index 0000000..9ca0756 --- /dev/null +++ b/libpq/libpq-fe.h @@ -0,0 +1,607 @@ +/*------------------------------------------------------------------------- + * + * libpq-fe.h + * This file contains definitions for structures and + * externs for functions used by frontend postgres applications. + * + * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/interfaces/libpq/libpq-fe.h + * + *------------------------------------------------------------------------- + */ + +#ifndef LIBPQ_FE_H +#define LIBPQ_FE_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include + +/* + * postgres_ext.h defines the backend's externally visible types, + * such as Oid. + */ +#include "postgres_ext.h" + +/* + * Option flags for PQcopyResult + */ +#define PG_COPYRES_ATTRS 0x01 +#define PG_COPYRES_TUPLES 0x02 /* Implies PG_COPYRES_ATTRS */ +#define PG_COPYRES_EVENTS 0x04 +#define PG_COPYRES_NOTICEHOOKS 0x08 + +/* Application-visible enum types */ + +/* + * Although it is okay to add to these lists, values which become unused + * should never be removed, nor should constants be redefined - that would + * break compatibility with existing code. + */ + +typedef enum +{ + CONNECTION_OK, + CONNECTION_BAD, + /* Non-blocking mode only below here */ + + /* + * The existence of these should never be relied upon - they should only + * be used for user feedback or similar purposes. + */ + CONNECTION_STARTED, /* Waiting for connection to be made. */ + CONNECTION_MADE, /* Connection OK; waiting to send. */ + CONNECTION_AWAITING_RESPONSE, /* Waiting for a response from the + * postmaster. */ + CONNECTION_AUTH_OK, /* Received authentication; waiting for + * backend startup. */ + CONNECTION_SETENV, /* Negotiating environment. */ + CONNECTION_SSL_STARTUP, /* Negotiating SSL. */ + CONNECTION_NEEDED /* Internal state: connect() needed */ +} ConnStatusType; + +typedef enum +{ + PGRES_POLLING_FAILED = 0, + PGRES_POLLING_READING, /* These two indicate that one may */ + PGRES_POLLING_WRITING, /* use select before polling again. */ + PGRES_POLLING_OK, + PGRES_POLLING_ACTIVE /* unused; keep for awhile for backwards + * compatibility */ +} PostgresPollingStatusType; + +typedef enum +{ + PGRES_EMPTY_QUERY = 0, /* empty query string was executed */ + PGRES_COMMAND_OK, /* a query command that doesn't return + * anything was executed properly by the + * backend */ + PGRES_TUPLES_OK, /* a query command that returns tuples was + * executed properly by the backend, PGresult + * contains the result tuples */ + PGRES_COPY_OUT, /* Copy Out data transfer in progress */ + PGRES_COPY_IN, /* Copy In data transfer in progress */ + PGRES_BAD_RESPONSE, /* an unexpected response was recv'd from the + * backend */ + PGRES_NONFATAL_ERROR, /* notice or warning message */ + PGRES_FATAL_ERROR, /* query failed */ + PGRES_COPY_BOTH, /* Copy In/Out data transfer in progress */ + PGRES_SINGLE_TUPLE /* single tuple from larger resultset */ +} ExecStatusType; + +typedef enum +{ + PQTRANS_IDLE, /* connection idle */ + PQTRANS_ACTIVE, /* command in progress */ + PQTRANS_INTRANS, /* idle, within transaction block */ + PQTRANS_INERROR, /* idle, within failed transaction */ + PQTRANS_UNKNOWN /* cannot determine status */ +} PGTransactionStatusType; + +typedef enum +{ + PQERRORS_TERSE, /* single-line error messages */ + PQERRORS_DEFAULT, /* recommended style */ + PQERRORS_VERBOSE /* all the facts, ma'am */ +} PGVerbosity; + +typedef enum +{ + PQSHOW_CONTEXT_NEVER, /* never show CONTEXT field */ + PQSHOW_CONTEXT_ERRORS, /* show CONTEXT for errors only (default) */ + PQSHOW_CONTEXT_ALWAYS /* always show CONTEXT field */ +} PGContextVisibility; + +/* + * PGPing - The ordering of this enum should not be altered because the + * values are exposed externally via pg_isready. + */ + +typedef enum +{ + PQPING_OK, /* server is accepting connections */ + PQPING_REJECT, /* server is alive but rejecting connections */ + PQPING_NO_RESPONSE, /* could not establish connection */ + PQPING_NO_ATTEMPT /* connection not attempted (bad params) */ +} PGPing; + +/* PGconn encapsulates a connection to the backend. + * The contents of this struct are not supposed to be known to applications. + */ +typedef struct pg_conn PGconn; + +/* PGresult encapsulates the result of a query (or more precisely, of a single + * SQL command --- a query string given to PQsendQuery can contain multiple + * commands and thus return multiple PGresult objects). + * The contents of this struct are not supposed to be known to applications. + */ +typedef struct pg_result PGresult; + +/* PGcancel encapsulates the information needed to cancel a running + * query on an existing connection. + * The contents of this struct are not supposed to be known to applications. + */ +typedef struct pg_cancel PGcancel; + +/* PGnotify represents the occurrence of a NOTIFY message. + * Ideally this would be an opaque typedef, but it's so simple that it's + * unlikely to change. + * NOTE: in Postgres 6.4 and later, the be_pid is the notifying backend's, + * whereas in earlier versions it was always your own backend's PID. + */ +typedef struct pgNotify +{ + char *relname; /* notification condition name */ + int be_pid; /* process ID of notifying server process */ + char *extra; /* notification parameter */ + /* Fields below here are private to libpq; apps should not use 'em */ + struct pgNotify *next; /* list link */ +} PGnotify; + +/* Function types for notice-handling callbacks */ +typedef void (*PQnoticeReceiver) (void *arg, const PGresult *res); +typedef void (*PQnoticeProcessor) (void *arg, const char *message); + +/* Print options for PQprint() */ +typedef char pqbool; + +typedef struct _PQprintOpt +{ + pqbool header; /* print output field headings and row count */ + pqbool align; /* fill align the fields */ + pqbool standard; /* old brain dead format */ + pqbool html3; /* output html tables */ + pqbool expanded; /* expand tables */ + pqbool pager; /* use pager for output if needed */ + char *fieldSep; /* field separator */ + char *tableOpt; /* insert to HTML */ + char *caption; /* HTML
*/ + char **fieldName; /* null terminated array of replacement field + * names */ +} PQprintOpt; + +/* ---------------- + * Structure for the conninfo parameter definitions returned by PQconndefaults + * or PQconninfoParse. + * + * All fields except "val" point at static strings which must not be altered. + * "val" is either NULL or a malloc'd current-value string. PQconninfoFree() + * will release both the val strings and the PQconninfoOption array itself. + * ---------------- + */ +typedef struct _PQconninfoOption +{ + char *keyword; /* The keyword of the option */ + char *envvar; /* Fallback environment variable name */ + char *compiled; /* Fallback compiled in default value */ + char *val; /* Option's current value, or NULL */ + char *label; /* Label for field in connect dialog */ + char *dispchar; /* Indicates how to display this field in a + * connect dialog. Values are: "" Display + * entered value as is "*" Password field - + * hide value "D" Debug option - don't show + * by default */ + int dispsize; /* Field size in characters for dialog */ +} PQconninfoOption; + +/* ---------------- + * PQArgBlock -- structure for PQfn() arguments + * ---------------- + */ +typedef struct +{ + int len; + int isint; + union + { + int *ptr; /* can't use void (dec compiler barfs) */ + int integer; + } u; +} PQArgBlock; + +/* ---------------- + * PGresAttDesc -- Data about a single attribute (column) of a query result + * ---------------- + */ +typedef struct pgresAttDesc +{ + char *name; /* column name */ + Oid tableid; /* source table, if known */ + int columnid; /* source column, if known */ + int format; /* format code for value (text/binary) */ + Oid typid; /* type id */ + int typlen; /* type size */ + int atttypmod; /* type-specific modifier info */ +} PGresAttDesc; + +/* ---------------- + * Exported functions of libpq + * ---------------- + */ + +/* === in fe-connect.c === */ + +/* make a new client connection to the backend */ +/* Asynchronous (non-blocking) */ +extern PGconn *PQconnectStart(const char *conninfo); +extern PGconn *PQconnectStartParams(const char *const * keywords, + const char *const * values, int expand_dbname); +extern PostgresPollingStatusType PQconnectPoll(PGconn *conn); + +/* Synchronous (blocking) */ +extern PGconn *PQconnectdb(const char *conninfo); +extern PGconn *PQconnectdbParams(const char *const * keywords, + const char *const * values, int expand_dbname); +extern PGconn *PQsetdbLogin(const char *pghost, const char *pgport, + const char *pgoptions, const char *pgtty, + const char *dbName, + const char *login, const char *pwd); + +#define PQsetdb(M_PGHOST,M_PGPORT,M_PGOPT,M_PGTTY,M_DBNAME) \ + PQsetdbLogin(M_PGHOST, M_PGPORT, M_PGOPT, M_PGTTY, M_DBNAME, NULL, NULL) + +/* close the current connection and free the PGconn data structure */ +extern void PQfinish(PGconn *conn); + +/* get info about connection options known to PQconnectdb */ +extern PQconninfoOption *PQconndefaults(void); + +/* parse connection options in same way as PQconnectdb */ +extern PQconninfoOption *PQconninfoParse(const char *conninfo, char **errmsg); + +/* return the connection options used by a live connection */ +extern PQconninfoOption *PQconninfo(PGconn *conn); + +/* free the data structure returned by PQconndefaults() or PQconninfoParse() */ +extern void PQconninfoFree(PQconninfoOption *connOptions); + +/* + * close the current connection and restablish a new one with the same + * parameters + */ +/* Asynchronous (non-blocking) */ +extern int PQresetStart(PGconn *conn); +extern PostgresPollingStatusType PQresetPoll(PGconn *conn); + +/* Synchronous (blocking) */ +extern void PQreset(PGconn *conn); + +/* request a cancel structure */ +extern PGcancel *PQgetCancel(PGconn *conn); + +/* free a cancel structure */ +extern void PQfreeCancel(PGcancel *cancel); + +/* issue a cancel request */ +extern int PQcancel(PGcancel *cancel, char *errbuf, int errbufsize); + +/* backwards compatible version of PQcancel; not thread-safe */ +extern int PQrequestCancel(PGconn *conn); + +/* Accessor functions for PGconn objects */ +extern char *PQdb(const PGconn *conn); +extern char *PQuser(const PGconn *conn); +extern char *PQpass(const PGconn *conn); +extern char *PQhost(const PGconn *conn); +extern char *PQport(const PGconn *conn); +extern char *PQtty(const PGconn *conn); +extern char *PQoptions(const PGconn *conn); +extern ConnStatusType PQstatus(const PGconn *conn); +extern PGTransactionStatusType PQtransactionStatus(const PGconn *conn); +extern const char *PQparameterStatus(const PGconn *conn, + const char *paramName); +extern int PQprotocolVersion(const PGconn *conn); +extern int PQserverVersion(const PGconn *conn); +extern char *PQerrorMessage(const PGconn *conn); +extern int PQsocket(const PGconn *conn); +extern int PQbackendPID(const PGconn *conn); +extern int PQconnectionNeedsPassword(const PGconn *conn); +extern int PQconnectionUsedPassword(const PGconn *conn); +extern int PQclientEncoding(const PGconn *conn); +extern int PQsetClientEncoding(PGconn *conn, const char *encoding); + +/* SSL information functions */ +extern int PQsslInUse(PGconn *conn); +extern void *PQsslStruct(PGconn *conn, const char *struct_name); +extern const char *PQsslAttribute(PGconn *conn, const char *attribute_name); +extern const char *const * PQsslAttributeNames(PGconn *conn); + +/* Get the OpenSSL structure associated with a connection. Returns NULL for + * unencrypted connections or if any other TLS library is in use. */ +extern void *PQgetssl(PGconn *conn); + +/* Tell libpq whether it needs to initialize OpenSSL */ +extern void PQinitSSL(int do_init); + +/* More detailed way to tell libpq whether it needs to initialize OpenSSL */ +extern void PQinitOpenSSL(int do_ssl, int do_crypto); + +/* Set verbosity for PQerrorMessage and PQresultErrorMessage */ +extern PGVerbosity PQsetErrorVerbosity(PGconn *conn, PGVerbosity verbosity); + +/* Set CONTEXT visibility for PQerrorMessage and PQresultErrorMessage */ +extern PGContextVisibility PQsetErrorContextVisibility(PGconn *conn, + PGContextVisibility show_context); + +/* Enable/disable tracing */ +extern void PQtrace(PGconn *conn, FILE *debug_port); +extern void PQuntrace(PGconn *conn); + +/* Override default notice handling routines */ +extern PQnoticeReceiver PQsetNoticeReceiver(PGconn *conn, + PQnoticeReceiver proc, + void *arg); +extern PQnoticeProcessor PQsetNoticeProcessor(PGconn *conn, + PQnoticeProcessor proc, + void *arg); + +/* + * Used to set callback that prevents concurrent access to + * non-thread safe functions that libpq needs. + * The default implementation uses a libpq internal mutex. + * Only required for multithreaded apps that use kerberos + * both within their app and for postgresql connections. + */ +typedef void (*pgthreadlock_t) (int acquire); + +extern pgthreadlock_t PQregisterThreadLock(pgthreadlock_t newhandler); + +/* === in fe-exec.c === */ + +/* Simple synchronous query */ +extern PGresult *PQexec(PGconn *conn, const char *query); +extern PGresult *PQexecParams(PGconn *conn, + const char *command, + int nParams, + const Oid *paramTypes, + const char *const * paramValues, + const int *paramLengths, + const int *paramFormats, + int resultFormat); +extern PGresult *PQprepare(PGconn *conn, const char *stmtName, + const char *query, int nParams, + const Oid *paramTypes); +extern PGresult *PQexecPrepared(PGconn *conn, + const char *stmtName, + int nParams, + const char *const * paramValues, + const int *paramLengths, + const int *paramFormats, + int resultFormat); + +/* Interface for multiple-result or asynchronous queries */ +extern int PQsendQuery(PGconn *conn, const char *query); +extern int PQsendQueryParams(PGconn *conn, + const char *command, + int nParams, + const Oid *paramTypes, + const char *const * paramValues, + const int *paramLengths, + const int *paramFormats, + int resultFormat); +extern int PQsendPrepare(PGconn *conn, const char *stmtName, + const char *query, int nParams, + const Oid *paramTypes); +extern int PQsendQueryPrepared(PGconn *conn, + const char *stmtName, + int nParams, + const char *const * paramValues, + const int *paramLengths, + const int *paramFormats, + int resultFormat); +extern int PQsetSingleRowMode(PGconn *conn); +extern PGresult *PQgetResult(PGconn *conn); + +/* Routines for managing an asynchronous query */ +extern int PQisBusy(PGconn *conn); +extern int PQconsumeInput(PGconn *conn); + +/* LISTEN/NOTIFY support */ +extern PGnotify *PQnotifies(PGconn *conn); + +/* Routines for copy in/out */ +extern int PQputCopyData(PGconn *conn, const char *buffer, int nbytes); +extern int PQputCopyEnd(PGconn *conn, const char *errormsg); +extern int PQgetCopyData(PGconn *conn, char **buffer, int async); + +/* Deprecated routines for copy in/out */ +extern int PQgetline(PGconn *conn, char *string, int length); +extern int PQputline(PGconn *conn, const char *string); +extern int PQgetlineAsync(PGconn *conn, char *buffer, int bufsize); +extern int PQputnbytes(PGconn *conn, const char *buffer, int nbytes); +extern int PQendcopy(PGconn *conn); + +/* Set blocking/nonblocking connection to the backend */ +extern int PQsetnonblocking(PGconn *conn, int arg); +extern int PQisnonblocking(const PGconn *conn); +extern int PQisthreadsafe(void); +extern PGPing PQping(const char *conninfo); +extern PGPing PQpingParams(const char *const * keywords, + const char *const * values, int expand_dbname); + +/* Force the write buffer to be written (or at least try) */ +extern int PQflush(PGconn *conn); + +/* + * "Fast path" interface --- not really recommended for application + * use + */ +extern PGresult *PQfn(PGconn *conn, + int fnid, + int *result_buf, + int *result_len, + int result_is_int, + const PQArgBlock *args, + int nargs); + +/* Accessor functions for PGresult objects */ +extern ExecStatusType PQresultStatus(const PGresult *res); +extern char *PQresStatus(ExecStatusType status); +extern char *PQresultErrorMessage(const PGresult *res); +extern char *PQresultVerboseErrorMessage(const PGresult *res, + PGVerbosity verbosity, + PGContextVisibility show_context); +extern char *PQresultErrorField(const PGresult *res, int fieldcode); +extern int PQntuples(const PGresult *res); +extern int PQnfields(const PGresult *res); +extern int PQbinaryTuples(const PGresult *res); +extern char *PQfname(const PGresult *res, int field_num); +extern int PQfnumber(const PGresult *res, const char *field_name); +extern Oid PQftable(const PGresult *res, int field_num); +extern int PQftablecol(const PGresult *res, int field_num); +extern int PQfformat(const PGresult *res, int field_num); +extern Oid PQftype(const PGresult *res, int field_num); +extern int PQfsize(const PGresult *res, int field_num); +extern int PQfmod(const PGresult *res, int field_num); +extern char *PQcmdStatus(PGresult *res); +extern char *PQoidStatus(const PGresult *res); /* old and ugly */ +extern Oid PQoidValue(const PGresult *res); /* new and improved */ +extern char *PQcmdTuples(PGresult *res); +extern char *PQgetvalue(const PGresult *res, int tup_num, int field_num); +extern int PQgetlength(const PGresult *res, int tup_num, int field_num); +extern int PQgetisnull(const PGresult *res, int tup_num, int field_num); +extern int PQnparams(const PGresult *res); +extern Oid PQparamtype(const PGresult *res, int param_num); + +/* Describe prepared statements and portals */ +extern PGresult *PQdescribePrepared(PGconn *conn, const char *stmt); +extern PGresult *PQdescribePortal(PGconn *conn, const char *portal); +extern int PQsendDescribePrepared(PGconn *conn, const char *stmt); +extern int PQsendDescribePortal(PGconn *conn, const char *portal); + +/* Delete a PGresult */ +extern void PQclear(PGresult *res); + +/* For freeing other alloc'd results, such as PGnotify structs */ +extern void PQfreemem(void *ptr); + +/* Exists for backward compatibility. bjm 2003-03-24 */ +#define PQfreeNotify(ptr) PQfreemem(ptr) + +/* Error when no password was given. */ +/* Note: depending on this is deprecated; use PQconnectionNeedsPassword(). */ +#define PQnoPasswordSupplied "fe_sendauth: no password supplied\n" + +/* Create and manipulate PGresults */ +extern PGresult *PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status); +extern PGresult *PQcopyResult(const PGresult *src, int flags); +extern int PQsetResultAttrs(PGresult *res, int numAttributes, PGresAttDesc *attDescs); +extern void *PQresultAlloc(PGresult *res, size_t nBytes); +extern int PQsetvalue(PGresult *res, int tup_num, int field_num, char *value, int len); + +/* Quoting strings before inclusion in queries. */ +extern size_t PQescapeStringConn(PGconn *conn, + char *to, const char *from, size_t length, + int *error); +extern char *PQescapeLiteral(PGconn *conn, const char *str, size_t len); +extern char *PQescapeIdentifier(PGconn *conn, const char *str, size_t len); +extern unsigned char *PQescapeByteaConn(PGconn *conn, + const unsigned char *from, size_t from_length, + size_t *to_length); +extern unsigned char *PQunescapeBytea(const unsigned char *strtext, + size_t *retbuflen); + +/* These forms are deprecated! */ +extern size_t PQescapeString(char *to, const char *from, size_t length); +extern unsigned char *PQescapeBytea(const unsigned char *from, size_t from_length, + size_t *to_length); + + + +/* === in fe-print.c === */ + +extern void PQprint(FILE *fout, /* output stream */ + const PGresult *res, + const PQprintOpt *ps); /* option structure */ + +/* + * really old printing routines + */ +extern void PQdisplayTuples(const PGresult *res, + FILE *fp, /* where to send the output */ + int fillAlign, /* pad the fields with spaces */ + const char *fieldSep, /* field separator */ + int printHeader, /* display headers? */ + int quiet); + +extern void PQprintTuples(const PGresult *res, + FILE *fout, /* output stream */ + int printAttName, /* print attribute names */ + int terseOutput, /* delimiter bars */ + int width); /* width of column, if 0, use variable width */ + + +/* === in fe-lobj.c === */ + +/* Large-object access routines */ +extern int lo_open(PGconn *conn, Oid lobjId, int mode); +extern int lo_close(PGconn *conn, int fd); +extern int lo_read(PGconn *conn, int fd, char *buf, size_t len); +extern int lo_write(PGconn *conn, int fd, const char *buf, size_t len); +extern int lo_lseek(PGconn *conn, int fd, int offset, int whence); +extern pg_int64 lo_lseek64(PGconn *conn, int fd, pg_int64 offset, int whence); +extern Oid lo_creat(PGconn *conn, int mode); +extern Oid lo_create(PGconn *conn, Oid lobjId); +extern int lo_tell(PGconn *conn, int fd); +extern pg_int64 lo_tell64(PGconn *conn, int fd); +extern int lo_truncate(PGconn *conn, int fd, size_t len); +extern int lo_truncate64(PGconn *conn, int fd, pg_int64 len); +extern int lo_unlink(PGconn *conn, Oid lobjId); +extern Oid lo_import(PGconn *conn, const char *filename); +extern Oid lo_import_with_oid(PGconn *conn, const char *filename, Oid lobjId); +extern int lo_export(PGconn *conn, Oid lobjId, const char *filename); + +/* === in fe-misc.c === */ + +/* Get the version of the libpq library in use */ +extern int PQlibVersion(void); + +/* Determine length of multibyte encoded char at *s */ +extern int PQmblen(const char *s, int encoding); + +/* Determine display length of multibyte encoded char at *s */ +extern int PQdsplen(const char *s, int encoding); + +/* Get encoding id from environment variable PGCLIENTENCODING */ +extern int PQenv2encoding(void); + +/* === in fe-auth.c === */ + +extern char *PQencryptPassword(const char *passwd, const char *user); + +/* === in encnames.c === */ + +extern int pg_char_to_encoding(const char *name); +extern const char *pg_encoding_to_char(int encoding); +extern int pg_valid_server_encoding_id(int encoding); + +#ifdef __cplusplus +} +#endif + +#endif /* LIBPQ_FE_H */ diff --git a/libpq/libpq-int.h b/libpq/libpq-int.h new file mode 100644 index 0000000..9e4e625 --- /dev/null +++ b/libpq/libpq-int.h @@ -0,0 +1,675 @@ +/*------------------------------------------------------------------------- + * + * libpq-int.h + * This file contains internal definitions meant to be used only by + * the frontend libpq library, not by applications that call it. + * + * An application can include this file if it wants to bypass the + * official API defined by libpq-fe.h, but code that does so is much + * more likely to break across PostgreSQL releases than code that uses + * only the official API. + * + * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/interfaces/libpq/libpq-int.h + * + *------------------------------------------------------------------------- + */ + +#ifndef LIBPQ_INT_H +#define LIBPQ_INT_H + +/* We assume libpq-fe.h has already been included. */ +#include "postgres_fe.h" +#include "libpq-events.h" + +#include +#include +#ifndef WIN32 +#include +#endif + +#ifdef ENABLE_THREAD_SAFETY +#ifdef WIN32 +#include "pthread-win32.h" +#else +#include +#endif +#include +#endif + +/* include stuff common to fe and be */ +#include "getaddrinfo.h" +#include "libpq/pqcomm.h" +/* include stuff found in fe only */ +#include "pqexpbuffer.h" + +#ifdef ENABLE_GSS +#if defined(HAVE_GSSAPI_H) +#include +#else +#include +#endif +#endif + +#ifdef ENABLE_SSPI +#define SECURITY_WIN32 +#if defined(WIN32) && !defined(WIN32_ONLY_COMPILER) +#include +#endif +#include +#undef SECURITY_WIN32 + +#ifndef ENABLE_GSS +/* + * Define a fake structure compatible with GSSAPI on Unix. + */ +typedef struct +{ + void *value; + int length; +} gss_buffer_desc; +#endif +#endif /* ENABLE_SSPI */ + +#ifdef USE_OPENSSL +#include +#include + +#if (OPENSSL_VERSION_NUMBER >= 0x00907000L) && !defined(OPENSSL_NO_ENGINE) +#define USE_SSL_ENGINE +#endif +#endif /* USE_OPENSSL */ + +/* + * POSTGRES backend dependent Constants. + */ +#define CMDSTATUS_LEN 64 /* should match COMPLETION_TAG_BUFSIZE */ + +/* + * PGresult and the subsidiary types PGresAttDesc, PGresAttValue + * represent the result of a query (or more precisely, of a single SQL + * command --- a query string given to PQexec can contain multiple commands). + * Note we assume that a single command can return at most one tuple group, + * hence there is no need for multiple descriptor sets. + */ + +/* Subsidiary-storage management structure for PGresult. + * See space management routines in fe-exec.c for details. + * Note that space[k] refers to the k'th byte starting from the physical + * head of the block --- it's a union, not a struct! + */ +typedef union pgresult_data PGresult_data; + +union pgresult_data +{ + PGresult_data *next; /* link to next block, or NULL */ + char space[1]; /* dummy for accessing block as bytes */ +}; + +/* Data about a single parameter of a prepared statement */ +typedef struct pgresParamDesc +{ + Oid typid; /* type id */ +} PGresParamDesc; + +/* + * Data for a single attribute of a single tuple + * + * We use char* for Attribute values. + * + * The value pointer always points to a null-terminated area; we add a + * null (zero) byte after whatever the backend sends us. This is only + * particularly useful for text values ... with a binary value, the + * value might have embedded nulls, so the application can't use C string + * operators on it. But we add a null anyway for consistency. + * Note that the value itself does not contain a length word. + * + * A NULL attribute is a special case in two ways: its len field is NULL_LEN + * and its value field points to null_field in the owning PGresult. All the + * NULL attributes in a query result point to the same place (there's no need + * to store a null string separately for each one). + */ + +#define NULL_LEN (-1) /* pg_result len for NULL value */ + +typedef struct pgresAttValue +{ + int len; /* length in bytes of the value */ + char *value; /* actual value, plus terminating zero byte */ +} PGresAttValue; + +/* Typedef for message-field list entries */ +typedef struct pgMessageField +{ + struct pgMessageField *next; /* list link */ + char code; /* field code */ + char contents[FLEXIBLE_ARRAY_MEMBER]; /* value, nul-terminated */ +} PGMessageField; + +/* Fields needed for notice handling */ +typedef struct +{ + PQnoticeReceiver noticeRec; /* notice message receiver */ + void *noticeRecArg; + PQnoticeProcessor noticeProc; /* notice message processor */ + void *noticeProcArg; +} PGNoticeHooks; + +typedef struct PGEvent +{ + PGEventProc proc; /* the function to call on events */ + char *name; /* used only for error messages */ + void *passThrough; /* pointer supplied at registration time */ + void *data; /* optional state (instance) data */ + bool resultInitialized; /* T if RESULTCREATE/COPY succeeded */ +} PGEvent; + +struct pg_result +{ + int ntups; + int numAttributes; + PGresAttDesc *attDescs; + PGresAttValue **tuples; /* each PGresTuple is an array of + * PGresAttValue's */ + int tupArrSize; /* allocated size of tuples array */ + int numParameters; + PGresParamDesc *paramDescs; + ExecStatusType resultStatus; + char cmdStatus[CMDSTATUS_LEN]; /* cmd status from the query */ + int binary; /* binary tuple values if binary == 1, + * otherwise text */ + + /* + * These fields are copied from the originating PGconn, so that operations + * on the PGresult don't have to reference the PGconn. + */ + PGNoticeHooks noticeHooks; + PGEvent *events; + int nEvents; + int client_encoding; /* encoding id */ + + /* + * Error information (all NULL if not an error result). errMsg is the + * "overall" error message returned by PQresultErrorMessage. If we have + * per-field info then it is stored in a linked list. + */ + char *errMsg; /* error message, or NULL if no error */ + PGMessageField *errFields; /* message broken into fields */ + char *errQuery; /* text of triggering query, if available */ + + /* All NULL attributes in the query result point to this null string */ + char null_field[1]; + + /* + * Space management information. Note that attDescs and error stuff, if + * not null, point into allocated blocks. But tuples points to a + * separately malloc'd block, so that we can realloc it. + */ + PGresult_data *curBlock; /* most recently allocated block */ + int curOffset; /* start offset of free space in block */ + int spaceLeft; /* number of free bytes remaining in block */ +}; + +/* PGAsyncStatusType defines the state of the query-execution state machine */ +typedef enum +{ + PGASYNC_IDLE, /* nothing's happening, dude */ + PGASYNC_BUSY, /* query in progress */ + PGASYNC_READY, /* result ready for PQgetResult */ + PGASYNC_COPY_IN, /* Copy In data transfer in progress */ + PGASYNC_COPY_OUT, /* Copy Out data transfer in progress */ + PGASYNC_COPY_BOTH /* Copy In/Out data transfer in progress */ +} PGAsyncStatusType; + +/* PGQueryClass tracks which query protocol we are now executing */ +typedef enum +{ + PGQUERY_SIMPLE, /* simple Query protocol (PQexec) */ + PGQUERY_EXTENDED, /* full Extended protocol (PQexecParams) */ + PGQUERY_PREPARE, /* Parse only (PQprepare) */ + PGQUERY_DESCRIBE /* Describe Statement or Portal */ +} PGQueryClass; + +/* PGSetenvStatusType defines the state of the PQSetenv state machine */ +/* (this is used only for 2.0-protocol connections) */ +typedef enum +{ + SETENV_STATE_CLIENT_ENCODING_SEND, /* About to send an Environment Option */ + SETENV_STATE_CLIENT_ENCODING_WAIT, /* Waiting for above send to complete */ + SETENV_STATE_OPTION_SEND, /* About to send an Environment Option */ + SETENV_STATE_OPTION_WAIT, /* Waiting for above send to complete */ + SETENV_STATE_QUERY1_SEND, /* About to send a status query */ + SETENV_STATE_QUERY1_WAIT, /* Waiting for query to complete */ + SETENV_STATE_QUERY2_SEND, /* About to send a status query */ + SETENV_STATE_QUERY2_WAIT, /* Waiting for query to complete */ + SETENV_STATE_IDLE +} PGSetenvStatusType; + +/* Typedef for the EnvironmentOptions[] array */ +typedef struct PQEnvironmentOption +{ + const char *envName, /* name of an environment variable */ + *pgName; /* name of corresponding SET variable */ +} PQEnvironmentOption; + +/* Typedef for parameter-status list entries */ +typedef struct pgParameterStatus +{ + struct pgParameterStatus *next; /* list link */ + char *name; /* parameter name */ + char *value; /* parameter value */ + /* Note: name and value are stored in same malloc block as struct is */ +} pgParameterStatus; + +/* large-object-access data ... allocated only if large-object code is used. */ +typedef struct pgLobjfuncs +{ + Oid fn_lo_open; /* OID of backend function lo_open */ + Oid fn_lo_close; /* OID of backend function lo_close */ + Oid fn_lo_creat; /* OID of backend function lo_creat */ + Oid fn_lo_create; /* OID of backend function lo_create */ + Oid fn_lo_unlink; /* OID of backend function lo_unlink */ + Oid fn_lo_lseek; /* OID of backend function lo_lseek */ + Oid fn_lo_lseek64; /* OID of backend function lo_lseek64 */ + Oid fn_lo_tell; /* OID of backend function lo_tell */ + Oid fn_lo_tell64; /* OID of backend function lo_tell64 */ + Oid fn_lo_truncate; /* OID of backend function lo_truncate */ + Oid fn_lo_truncate64; /* OID of function lo_truncate64 */ + Oid fn_lo_read; /* OID of backend function LOread */ + Oid fn_lo_write; /* OID of backend function LOwrite */ +} PGlobjfuncs; + +/* PGdataValue represents a data field value being passed to a row processor. + * It could be either text or binary data; text data is not zero-terminated. + * A SQL NULL is represented by len < 0; then value is still valid but there + * are no data bytes there. + */ +typedef struct pgDataValue +{ + int len; /* data length in bytes, or <0 if NULL */ + const char *value; /* data value, without zero-termination */ +} PGdataValue; + +/* + * PGconn stores all the state data associated with a single connection + * to a backend. + */ +struct pg_conn +{ + /* Saved values of connection options */ + char *pghost; /* the machine on which the server is running */ + char *pghostaddr; /* the numeric IP address of the machine on + * which the server is running. Takes + * precedence over above. */ + char *pgport; /* the server's communication port number */ + char *pgunixsocket; /* the directory of the server's Unix-domain + * socket; if NULL, use DEFAULT_PGSOCKET_DIR */ + char *pgtty; /* tty on which the backend messages is + * displayed (OBSOLETE, NOT USED) */ + char *connect_timeout; /* connection timeout (numeric string) */ + char *client_encoding_initial; /* encoding to use */ + char *pgoptions; /* options to start the backend with */ + char *appname; /* application name */ + char *fbappname; /* fallback application name */ + char *dbName; /* database name */ + char *replication; /* connect as the replication standby? */ + char *pguser; /* Postgres username and password, if any */ + char *pgpass; + char *keepalives; /* use TCP keepalives? */ + char *keepalives_idle; /* time between TCP keepalives */ + char *keepalives_interval; /* time between TCP keepalive + * retransmits */ + char *keepalives_count; /* maximum number of TCP keepalive + * retransmits */ + char *sslmode; /* SSL mode (require,prefer,allow,disable) */ + char *sslcompression; /* SSL compression (0 or 1) */ + char *sslkey; /* client key filename */ + char *sslcert; /* client certificate filename */ + char *sslrootcert; /* root certificate filename */ + char *sslcrl; /* certificate revocation list filename */ + char *requirepeer; /* required peer credentials for local sockets */ + +#if defined(ENABLE_GSS) || defined(ENABLE_SSPI) + char *krbsrvname; /* Kerberos service name */ +#endif + + /* Optional file to write trace info to */ + FILE *Pfdebug; + + /* Callback procedures for notice message processing */ + PGNoticeHooks noticeHooks; + + /* Event procs registered via PQregisterEventProc */ + PGEvent *events; /* expandable array of event data */ + int nEvents; /* number of active events */ + int eventArraySize; /* allocated array size */ + + /* Status indicators */ + ConnStatusType status; + PGAsyncStatusType asyncStatus; + PGTransactionStatusType xactStatus; /* never changes to ACTIVE */ + PGQueryClass queryclass; + char *last_query; /* last SQL command, or NULL if unknown */ + char last_sqlstate[6]; /* last reported SQLSTATE */ + bool options_valid; /* true if OK to attempt connection */ + bool nonblocking; /* whether this connection is using nonblock + * sending semantics */ + bool singleRowMode; /* return current query result row-by-row? */ + char copy_is_binary; /* 1 = copy binary, 0 = copy text */ + int copy_already_done; /* # bytes already returned in COPY + * OUT */ + PGnotify *notifyHead; /* oldest unreported Notify msg */ + PGnotify *notifyTail; /* newest unreported Notify msg */ + + /* Connection data */ + /* See PQconnectPoll() for how we use 'int' and not 'pgsocket'. */ + pgsocket sock; /* FD for socket, PGINVALID_SOCKET if + * unconnected */ + SockAddr laddr; /* Local address */ + SockAddr raddr; /* Remote address */ + ProtocolVersion pversion; /* FE/BE protocol version in use */ + int sversion; /* server version, e.g. 70401 for 7.4.1 */ + bool auth_req_received; /* true if any type of auth req + * received */ + bool password_needed; /* true if server demanded a password */ + bool dot_pgpass_used; /* true if used .pgpass */ + bool sigpipe_so; /* have we masked SIGPIPE via SO_NOSIGPIPE? */ + bool sigpipe_flag; /* can we mask SIGPIPE via MSG_NOSIGNAL? */ + + /* Transient state needed while establishing connection */ + struct addrinfo *addrlist; /* list of possible backend addresses */ + struct addrinfo *addr_cur; /* the one currently being tried */ + int addrlist_family; /* needed to know how to free addrlist */ + PGSetenvStatusType setenv_state; /* for 2.0 protocol only */ + const PQEnvironmentOption *next_eo; + bool send_appname; /* okay to send application_name? */ + + /* Miscellaneous stuff */ + int be_pid; /* PID of backend --- needed for cancels */ + int be_key; /* key of backend --- needed for cancels */ + char md5Salt[4]; /* password salt received from backend */ + pgParameterStatus *pstatus; /* ParameterStatus data */ + int client_encoding; /* encoding id */ + bool std_strings; /* standard_conforming_strings */ + PGVerbosity verbosity; /* error/notice message verbosity */ + PGContextVisibility show_context; /* whether to show CONTEXT field */ + PGlobjfuncs *lobjfuncs; /* private state for large-object access fns */ + + /* Buffer for data received from backend and not yet processed */ + char *inBuffer; /* currently allocated buffer */ + int inBufSize; /* allocated size of buffer */ + int inStart; /* offset to first unconsumed data in buffer */ + int inCursor; /* next byte to tentatively consume */ + int inEnd; /* offset to first position after avail data */ + + /* Buffer for data not yet sent to backend */ + char *outBuffer; /* currently allocated buffer */ + int outBufSize; /* allocated size of buffer */ + int outCount; /* number of chars waiting in buffer */ + + /* State for constructing messages in outBuffer */ + int outMsgStart; /* offset to msg start (length word); if -1, + * msg has no length word */ + int outMsgEnd; /* offset to msg end (so far) */ + + /* Row processor interface workspace */ + PGdataValue *rowBuf; /* array for passing values to rowProcessor */ + int rowBufLen; /* number of entries allocated in rowBuf */ + + /* Status for asynchronous result construction */ + PGresult *result; /* result being constructed */ + PGresult *next_result; /* next result (used in single-row mode) */ + + /* Assorted state for SSL, GSS, etc */ + +#ifdef USE_SSL + bool allow_ssl_try; /* Allowed to try SSL negotiation */ + bool wait_ssl_try; /* Delay SSL negotiation until after + * attempting normal connection */ + bool ssl_in_use; +#ifdef USE_OPENSSL + SSL *ssl; /* SSL status, if have SSL connection */ + X509 *peer; /* X509 cert of server */ +#ifdef USE_SSL_ENGINE + ENGINE *engine; /* SSL engine, if any */ +#else + void *engine; /* dummy field to keep struct the same if + * OpenSSL version changes */ +#endif +#endif /* USE_OPENSSL */ +#endif /* USE_SSL */ + +#ifdef ENABLE_GSS + gss_ctx_id_t gctx; /* GSS context */ + gss_name_t gtarg_nam; /* GSS target name */ + gss_buffer_desc ginbuf; /* GSS input token */ + gss_buffer_desc goutbuf; /* GSS output token */ +#endif + +#ifdef ENABLE_SSPI +#ifndef ENABLE_GSS + gss_buffer_desc ginbuf; /* GSS input token */ +#else + char *gsslib; /* What GSS librart to use ("gssapi" or + * "sspi") */ +#endif + CredHandle *sspicred; /* SSPI credentials handle */ + CtxtHandle *sspictx; /* SSPI context */ + char *sspitarget; /* SSPI target name */ + int usesspi; /* Indicate if SSPI is in use on the + * connection */ +#endif + + /* Buffer for current error message */ + PQExpBufferData errorMessage; /* expansible string */ + + /* Buffer for receiving various parts of messages */ + PQExpBufferData workBuffer; /* expansible string */ +}; + +/* PGcancel stores all data necessary to cancel a connection. A copy of this + * data is required to safely cancel a connection running on a different + * thread. + */ +struct pg_cancel +{ + SockAddr raddr; /* Remote address */ + int be_pid; /* PID of backend --- needed for cancels */ + int be_key; /* key of backend --- needed for cancels */ +}; + + +/* String descriptions of the ExecStatusTypes. + * direct use of this array is deprecated; call PQresStatus() instead. + */ +extern char *const pgresStatus[]; + + +#ifdef USE_SSL + +#ifndef WIN32 +#define USER_CERT_FILE ".postgresql/postgresql.crt" +#define USER_KEY_FILE ".postgresql/postgresql.key" +#define ROOT_CERT_FILE ".postgresql/root.crt" +#define ROOT_CRL_FILE ".postgresql/root.crl" +#else +/* On Windows, the "home" directory is already PostgreSQL-specific */ +#define USER_CERT_FILE "postgresql.crt" +#define USER_KEY_FILE "postgresql.key" +#define ROOT_CERT_FILE "root.crt" +#define ROOT_CRL_FILE "root.crl" +#endif + +#endif /* USE_SSL */ + +/* ---------------- + * Internal functions of libpq + * Functions declared here need to be visible across files of libpq, + * but are not intended to be called by applications. We use the + * convention "pqXXX" for internal functions, vs. the "PQxxx" names + * used for application-visible routines. + * ---------------- + */ + +/* === in fe-connect.c === */ + +extern void pqDropConnection(PGconn *conn, bool flushInput); +extern int pqPacketSend(PGconn *conn, char pack_type, + const void *buf, size_t buf_len); +extern bool pqGetHomeDirectory(char *buf, int bufsize); + +#ifdef ENABLE_THREAD_SAFETY +extern pgthreadlock_t pg_g_threadlock; + +#define PGTHREAD_ERROR(msg) \ + do { \ + fprintf(stderr, "%s\n", msg); \ + abort(); \ + } while (0) + + +#define pglock_thread() pg_g_threadlock(true) +#define pgunlock_thread() pg_g_threadlock(false) +#else +#define pglock_thread() ((void) 0) +#define pgunlock_thread() ((void) 0) +#endif + +/* === in fe-exec.c === */ + +extern void pqSetResultError(PGresult *res, const char *msg); +extern void pqCatenateResultError(PGresult *res, const char *msg); +extern void *pqResultAlloc(PGresult *res, size_t nBytes, bool isBinary); +extern char *pqResultStrdup(PGresult *res, const char *str); +extern void pqClearAsyncResult(PGconn *conn); +extern void pqSaveErrorResult(PGconn *conn); +extern PGresult *pqPrepareAsyncResult(PGconn *conn); +extern void pqInternalNotice(const PGNoticeHooks *hooks, const char *fmt,...) pg_attribute_printf(2, 3); +extern void pqSaveMessageField(PGresult *res, char code, + const char *value); +extern void pqSaveParameterStatus(PGconn *conn, const char *name, + const char *value); +extern int pqRowProcessor(PGconn *conn, const char **errmsgp); +extern void pqHandleSendFailure(PGconn *conn); + +/* === in fe-protocol2.c === */ + +extern PostgresPollingStatusType pqSetenvPoll(PGconn *conn); + +extern char *pqBuildStartupPacket2(PGconn *conn, int *packetlen, + const PQEnvironmentOption *options); +extern void pqParseInput2(PGconn *conn); +extern int pqGetCopyData2(PGconn *conn, char **buffer, int async); +extern int pqGetline2(PGconn *conn, char *s, int maxlen); +extern int pqGetlineAsync2(PGconn *conn, char *buffer, int bufsize); +extern int pqEndcopy2(PGconn *conn); +extern PGresult *pqFunctionCall2(PGconn *conn, Oid fnid, + int *result_buf, int *actual_result_len, + int result_is_int, + const PQArgBlock *args, int nargs); + +/* === in fe-protocol3.c === */ + +extern char *pqBuildStartupPacket3(PGconn *conn, int *packetlen, + const PQEnvironmentOption *options); +extern void pqParseInput3(PGconn *conn); +extern int pqGetErrorNotice3(PGconn *conn, bool isError); +extern void pqBuildErrorMessage3(PQExpBuffer msg, const PGresult *res, + PGVerbosity verbosity, PGContextVisibility show_context); +extern int pqGetCopyData3(PGconn *conn, char **buffer, int async); +extern int pqGetline3(PGconn *conn, char *s, int maxlen); +extern int pqGetlineAsync3(PGconn *conn, char *buffer, int bufsize); +extern int pqEndcopy3(PGconn *conn); +extern PGresult *pqFunctionCall3(PGconn *conn, Oid fnid, + int *result_buf, int *actual_result_len, + int result_is_int, + const PQArgBlock *args, int nargs); + +/* === in fe-misc.c === */ + + /* + * "Get" and "Put" routines return 0 if successful, EOF if not. Note that for + * Get, EOF merely means the buffer is exhausted, not that there is + * necessarily any error. + */ +extern int pqCheckOutBufferSpace(size_t bytes_needed, PGconn *conn); +extern int pqCheckInBufferSpace(size_t bytes_needed, PGconn *conn); +extern int pqGetc(char *result, PGconn *conn); +extern int pqPutc(char c, PGconn *conn); +extern int pqGets(PQExpBuffer buf, PGconn *conn); +extern int pqGets_append(PQExpBuffer buf, PGconn *conn); +extern int pqPuts(const char *s, PGconn *conn); +extern int pqGetnchar(char *s, size_t len, PGconn *conn); +extern int pqSkipnchar(size_t len, PGconn *conn); +extern int pqPutnchar(const char *s, size_t len, PGconn *conn); +extern int pqGetInt(int *result, size_t bytes, PGconn *conn); +extern int pqPutInt(int value, size_t bytes, PGconn *conn); +extern int pqPutMsgStart(char msg_type, bool force_len, PGconn *conn); +extern int pqPutMsgEnd(PGconn *conn); +extern int pqReadData(PGconn *conn); +extern int pqFlush(PGconn *conn); +extern int pqWait(int forRead, int forWrite, PGconn *conn); +extern int pqWaitTimed(int forRead, int forWrite, PGconn *conn, + time_t finish_time); +extern int pqReadReady(PGconn *conn); +extern int pqWriteReady(PGconn *conn); + +/* === in fe-secure.c === */ + +extern int pqsecure_initialize(PGconn *); +extern void pqsecure_destroy(void); +extern PostgresPollingStatusType pqsecure_open_client(PGconn *); +extern void pqsecure_close(PGconn *); +extern ssize_t pqsecure_read(PGconn *, void *ptr, size_t len); +extern ssize_t pqsecure_write(PGconn *, const void *ptr, size_t len); +extern ssize_t pqsecure_raw_read(PGconn *, void *ptr, size_t len); +extern ssize_t pqsecure_raw_write(PGconn *, const void *ptr, size_t len); + +#if defined(ENABLE_THREAD_SAFETY) && !defined(WIN32) +extern int pq_block_sigpipe(sigset_t *osigset, bool *sigpipe_pending); +extern void pq_reset_sigpipe(sigset_t *osigset, bool sigpipe_pending, + bool got_epipe); +#endif + +/* + * The SSL implementation provides these functions (fe-secure-openssl.c) + */ +extern void pgtls_init_library(bool do_ssl, int do_crypto); +extern int pgtls_init(PGconn *conn); +extern PostgresPollingStatusType pgtls_open_client(PGconn *conn); +extern void pgtls_close(PGconn *conn); +extern ssize_t pgtls_read(PGconn *conn, void *ptr, size_t len); +extern bool pgtls_read_pending(PGconn *conn); +extern ssize_t pgtls_write(PGconn *conn, const void *ptr, size_t len); + +/* + * this is so that we can check if a connection is non-blocking internally + * without the overhead of a function call + */ +#define pqIsnonblocking(conn) ((conn)->nonblocking) + +#ifdef ENABLE_NLS +extern char *libpq_gettext(const char *msgid) pg_attribute_format_arg(1); +extern char *libpq_ngettext(const char *msgid, const char *msgid_plural, unsigned long n) pg_attribute_format_arg(1) pg_attribute_format_arg(2); +#else +#define libpq_gettext(x) (x) +#define libpq_ngettext(s, p, n) ((n) == 1 ? (s) : (p)) +#endif + +/* + * These macros are needed to let error-handling code be portable between + * Unix and Windows. (ugh) + */ +#ifdef WIN32 +#define SOCK_ERRNO (WSAGetLastError()) +#define SOCK_STRERROR winsock_strerror +#define SOCK_ERRNO_SET(e) WSASetLastError(e) +#else +#define SOCK_ERRNO errno +#define SOCK_STRERROR pqStrerror +#define SOCK_ERRNO_SET(e) (errno = (e)) +#endif + +#endif /* LIBPQ_INT_H */ diff --git a/libpq/md5.c b/libpq/md5.c new file mode 100644 index 0000000..5af54e6 --- /dev/null +++ b/libpq/md5.c @@ -0,0 +1,345 @@ +/* + * md5.c + * + * Implements the MD5 Message-Digest Algorithm as specified in + * RFC 1321. This implementation is a simple one, in that it + * needs every input byte to be buffered before doing any + * calculations. I do not expect this file to be used for + * general purpose MD5'ing of large amounts of data, only for + * generating hashed passwords from limited input. + * + * Sverre H. Huseby + * + * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * IDENTIFICATION + * src/backend/libpq/md5.c + */ + +/* This is intended to be used in both frontend and backend, so use c.h */ +#include "c.h" + +#include "libpq/md5.h" + + +/* + * PRIVATE FUNCTIONS + */ + + +/* + * The returned array is allocated using malloc. the caller should free it + * when it is no longer needed. + */ +static uint8 * +createPaddedCopyWithLength(const uint8 *b, uint32 *l) +{ + uint8 *ret; + uint32 q; + uint32 len, + newLen448; + uint32 len_high, + len_low; /* 64-bit value split into 32-bit sections */ + + len = ((b == NULL) ? 0 : *l); + newLen448 = len + 64 - (len % 64) - 8; + if (newLen448 <= len) + newLen448 += 64; + + *l = newLen448 + 8; + if ((ret = (uint8 *) malloc(sizeof(uint8) * *l)) == NULL) + return NULL; + + if (b != NULL) + memcpy(ret, b, sizeof(uint8) * len); + + /* pad */ + ret[len] = 0x80; + for (q = len + 1; q < newLen448; q++) + ret[q] = 0x00; + + /* append length as a 64 bit bitcount */ + len_low = len; + /* split into two 32-bit values */ + /* we only look at the bottom 32-bits */ + len_high = len >> 29; + len_low <<= 3; + q = newLen448; + ret[q++] = (len_low & 0xff); + len_low >>= 8; + ret[q++] = (len_low & 0xff); + len_low >>= 8; + ret[q++] = (len_low & 0xff); + len_low >>= 8; + ret[q++] = (len_low & 0xff); + ret[q++] = (len_high & 0xff); + len_high >>= 8; + ret[q++] = (len_high & 0xff); + len_high >>= 8; + ret[q++] = (len_high & 0xff); + len_high >>= 8; + ret[q] = (len_high & 0xff); + + return ret; +} + +#define F(x, y, z) (((x) & (y)) | (~(x) & (z))) +#define G(x, y, z) (((x) & (z)) | ((y) & ~(z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | ~(z))) +#define ROT_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) + +static void +doTheRounds(uint32 X[16], uint32 state[4]) +{ + uint32 a, + b, + c, + d; + + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + + /* round 1 */ + a = b + ROT_LEFT((a + F(b, c, d) + X[0] + 0xd76aa478), 7); /* 1 */ + d = a + ROT_LEFT((d + F(a, b, c) + X[1] + 0xe8c7b756), 12); /* 2 */ + c = d + ROT_LEFT((c + F(d, a, b) + X[2] + 0x242070db), 17); /* 3 */ + b = c + ROT_LEFT((b + F(c, d, a) + X[3] + 0xc1bdceee), 22); /* 4 */ + a = b + ROT_LEFT((a + F(b, c, d) + X[4] + 0xf57c0faf), 7); /* 5 */ + d = a + ROT_LEFT((d + F(a, b, c) + X[5] + 0x4787c62a), 12); /* 6 */ + c = d + ROT_LEFT((c + F(d, a, b) + X[6] + 0xa8304613), 17); /* 7 */ + b = c + ROT_LEFT((b + F(c, d, a) + X[7] + 0xfd469501), 22); /* 8 */ + a = b + ROT_LEFT((a + F(b, c, d) + X[8] + 0x698098d8), 7); /* 9 */ + d = a + ROT_LEFT((d + F(a, b, c) + X[9] + 0x8b44f7af), 12); /* 10 */ + c = d + ROT_LEFT((c + F(d, a, b) + X[10] + 0xffff5bb1), 17); /* 11 */ + b = c + ROT_LEFT((b + F(c, d, a) + X[11] + 0x895cd7be), 22); /* 12 */ + a = b + ROT_LEFT((a + F(b, c, d) + X[12] + 0x6b901122), 7); /* 13 */ + d = a + ROT_LEFT((d + F(a, b, c) + X[13] + 0xfd987193), 12); /* 14 */ + c = d + ROT_LEFT((c + F(d, a, b) + X[14] + 0xa679438e), 17); /* 15 */ + b = c + ROT_LEFT((b + F(c, d, a) + X[15] + 0x49b40821), 22); /* 16 */ + + /* round 2 */ + a = b + ROT_LEFT((a + G(b, c, d) + X[1] + 0xf61e2562), 5); /* 17 */ + d = a + ROT_LEFT((d + G(a, b, c) + X[6] + 0xc040b340), 9); /* 18 */ + c = d + ROT_LEFT((c + G(d, a, b) + X[11] + 0x265e5a51), 14); /* 19 */ + b = c + ROT_LEFT((b + G(c, d, a) + X[0] + 0xe9b6c7aa), 20); /* 20 */ + a = b + ROT_LEFT((a + G(b, c, d) + X[5] + 0xd62f105d), 5); /* 21 */ + d = a + ROT_LEFT((d + G(a, b, c) + X[10] + 0x02441453), 9); /* 22 */ + c = d + ROT_LEFT((c + G(d, a, b) + X[15] + 0xd8a1e681), 14); /* 23 */ + b = c + ROT_LEFT((b + G(c, d, a) + X[4] + 0xe7d3fbc8), 20); /* 24 */ + a = b + ROT_LEFT((a + G(b, c, d) + X[9] + 0x21e1cde6), 5); /* 25 */ + d = a + ROT_LEFT((d + G(a, b, c) + X[14] + 0xc33707d6), 9); /* 26 */ + c = d + ROT_LEFT((c + G(d, a, b) + X[3] + 0xf4d50d87), 14); /* 27 */ + b = c + ROT_LEFT((b + G(c, d, a) + X[8] + 0x455a14ed), 20); /* 28 */ + a = b + ROT_LEFT((a + G(b, c, d) + X[13] + 0xa9e3e905), 5); /* 29 */ + d = a + ROT_LEFT((d + G(a, b, c) + X[2] + 0xfcefa3f8), 9); /* 30 */ + c = d + ROT_LEFT((c + G(d, a, b) + X[7] + 0x676f02d9), 14); /* 31 */ + b = c + ROT_LEFT((b + G(c, d, a) + X[12] + 0x8d2a4c8a), 20); /* 32 */ + + /* round 3 */ + a = b + ROT_LEFT((a + H(b, c, d) + X[5] + 0xfffa3942), 4); /* 33 */ + d = a + ROT_LEFT((d + H(a, b, c) + X[8] + 0x8771f681), 11); /* 34 */ + c = d + ROT_LEFT((c + H(d, a, b) + X[11] + 0x6d9d6122), 16); /* 35 */ + b = c + ROT_LEFT((b + H(c, d, a) + X[14] + 0xfde5380c), 23); /* 36 */ + a = b + ROT_LEFT((a + H(b, c, d) + X[1] + 0xa4beea44), 4); /* 37 */ + d = a + ROT_LEFT((d + H(a, b, c) + X[4] + 0x4bdecfa9), 11); /* 38 */ + c = d + ROT_LEFT((c + H(d, a, b) + X[7] + 0xf6bb4b60), 16); /* 39 */ + b = c + ROT_LEFT((b + H(c, d, a) + X[10] + 0xbebfbc70), 23); /* 40 */ + a = b + ROT_LEFT((a + H(b, c, d) + X[13] + 0x289b7ec6), 4); /* 41 */ + d = a + ROT_LEFT((d + H(a, b, c) + X[0] + 0xeaa127fa), 11); /* 42 */ + c = d + ROT_LEFT((c + H(d, a, b) + X[3] + 0xd4ef3085), 16); /* 43 */ + b = c + ROT_LEFT((b + H(c, d, a) + X[6] + 0x04881d05), 23); /* 44 */ + a = b + ROT_LEFT((a + H(b, c, d) + X[9] + 0xd9d4d039), 4); /* 45 */ + d = a + ROT_LEFT((d + H(a, b, c) + X[12] + 0xe6db99e5), 11); /* 46 */ + c = d + ROT_LEFT((c + H(d, a, b) + X[15] + 0x1fa27cf8), 16); /* 47 */ + b = c + ROT_LEFT((b + H(c, d, a) + X[2] + 0xc4ac5665), 23); /* 48 */ + + /* round 4 */ + a = b + ROT_LEFT((a + I(b, c, d) + X[0] + 0xf4292244), 6); /* 49 */ + d = a + ROT_LEFT((d + I(a, b, c) + X[7] + 0x432aff97), 10); /* 50 */ + c = d + ROT_LEFT((c + I(d, a, b) + X[14] + 0xab9423a7), 15); /* 51 */ + b = c + ROT_LEFT((b + I(c, d, a) + X[5] + 0xfc93a039), 21); /* 52 */ + a = b + ROT_LEFT((a + I(b, c, d) + X[12] + 0x655b59c3), 6); /* 53 */ + d = a + ROT_LEFT((d + I(a, b, c) + X[3] + 0x8f0ccc92), 10); /* 54 */ + c = d + ROT_LEFT((c + I(d, a, b) + X[10] + 0xffeff47d), 15); /* 55 */ + b = c + ROT_LEFT((b + I(c, d, a) + X[1] + 0x85845dd1), 21); /* 56 */ + a = b + ROT_LEFT((a + I(b, c, d) + X[8] + 0x6fa87e4f), 6); /* 57 */ + d = a + ROT_LEFT((d + I(a, b, c) + X[15] + 0xfe2ce6e0), 10); /* 58 */ + c = d + ROT_LEFT((c + I(d, a, b) + X[6] + 0xa3014314), 15); /* 59 */ + b = c + ROT_LEFT((b + I(c, d, a) + X[13] + 0x4e0811a1), 21); /* 60 */ + a = b + ROT_LEFT((a + I(b, c, d) + X[4] + 0xf7537e82), 6); /* 61 */ + d = a + ROT_LEFT((d + I(a, b, c) + X[11] + 0xbd3af235), 10); /* 62 */ + c = d + ROT_LEFT((c + I(d, a, b) + X[2] + 0x2ad7d2bb), 15); /* 63 */ + b = c + ROT_LEFT((b + I(c, d, a) + X[9] + 0xeb86d391), 21); /* 64 */ + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; +} + +static int +calculateDigestFromBuffer(const uint8 *b, uint32 len, uint8 sum[16]) +{ + register uint32 i, + j, + k, + newI; + uint32 l; + uint8 *input; + register uint32 *wbp; + uint32 workBuff[16], + state[4]; + + l = len; + + state[0] = 0x67452301; + state[1] = 0xEFCDAB89; + state[2] = 0x98BADCFE; + state[3] = 0x10325476; + + if ((input = createPaddedCopyWithLength(b, &l)) == NULL) + return 0; + + for (i = 0;;) + { + if ((newI = i + 16 * 4) > l) + break; + k = i + 3; + for (j = 0; j < 16; j++) + { + wbp = (workBuff + j); + *wbp = input[k--]; + *wbp <<= 8; + *wbp |= input[k--]; + *wbp <<= 8; + *wbp |= input[k--]; + *wbp <<= 8; + *wbp |= input[k]; + k += 7; + } + doTheRounds(workBuff, state); + i = newI; + } + free(input); + + j = 0; + for (i = 0; i < 4; i++) + { + k = state[i]; + sum[j++] = (k & 0xff); + k >>= 8; + sum[j++] = (k & 0xff); + k >>= 8; + sum[j++] = (k & 0xff); + k >>= 8; + sum[j++] = (k & 0xff); + } + return 1; +} + +static void +bytesToHex(uint8 b[16], char *s) +{ + static const char *hex = "0123456789abcdef"; + int q, + w; + + for (q = 0, w = 0; q < 16; q++) + { + s[w++] = hex[(b[q] >> 4) & 0x0F]; + s[w++] = hex[b[q] & 0x0F]; + } + s[w] = '\0'; +} + +/* + * PUBLIC FUNCTIONS + */ + +/* + * pg_md5_hash + * + * Calculates the MD5 sum of the bytes in a buffer. + * + * SYNOPSIS #include "md5.h" + * int pg_md5_hash(const void *buff, size_t len, char *hexsum) + * + * INPUT buff the buffer containing the bytes that you want + * the MD5 sum of. + * len number of bytes in the buffer. + * + * OUTPUT hexsum the MD5 sum as a '\0'-terminated string of + * hexadecimal digits. an MD5 sum is 16 bytes long. + * each byte is represented by two heaxadecimal + * characters. you thus need to provide an array + * of 33 characters, including the trailing '\0'. + * + * RETURNS false on failure (out of memory for internal buffers) or + * true on success. + * + * STANDARDS MD5 is described in RFC 1321. + * + * AUTHOR Sverre H. Huseby + * + */ +bool +pg_md5_hash(const void *buff, size_t len, char *hexsum) +{ + uint8 sum[16]; + + if (!calculateDigestFromBuffer(buff, len, sum)) + return false; + + bytesToHex(sum, hexsum); + return true; +} + +bool +pg_md5_binary(const void *buff, size_t len, void *outbuf) +{ + if (!calculateDigestFromBuffer(buff, len, outbuf)) + return false; + return true; +} + + +/* + * Computes MD5 checksum of "passwd" (a null-terminated string) followed + * by "salt" (which need not be null-terminated). + * + * Output format is "md5" followed by a 32-hex-digit MD5 checksum. + * Hence, the output buffer "buf" must be at least 36 bytes long. + * + * Returns TRUE if okay, FALSE on error (out of memory). + */ +bool +pg_md5_encrypt(const char *passwd, const char *salt, size_t salt_len, + char *buf) +{ + size_t passwd_len = strlen(passwd); + + /* +1 here is just to avoid risk of unportable malloc(0) */ + char *crypt_buf = malloc(passwd_len + salt_len + 1); + bool ret; + + if (!crypt_buf) + return false; + + /* + * Place salt at the end because it may be known by users trying to crack + * the MD5 output. + */ + memcpy(crypt_buf, passwd, passwd_len); + memcpy(crypt_buf + passwd_len, salt, salt_len); + + strcpy(buf, "md5"); + ret = pg_md5_hash(crypt_buf, passwd_len + salt_len, buf + 3); + + free(crypt_buf); + + return ret; +} diff --git a/libpq/noblock.c b/libpq/noblock.c new file mode 100644 index 0000000..959d3ad --- /dev/null +++ b/libpq/noblock.c @@ -0,0 +1,66 @@ +/*------------------------------------------------------------------------- + * + * noblock.c + * set a file descriptor as blocking or non-blocking + * + * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * IDENTIFICATION + * src/port/noblock.c + * + *------------------------------------------------------------------------- + */ + +#include "c.h" + +#include + + +/* + * Put socket into nonblock mode. + * Returns true on success, false on failure. + */ +bool +pg_set_noblock(pgsocket sock) +{ +#if !defined(WIN32) + int flags; + + flags = fcntl(sock, F_GETFL); + if (flags < 0) + return false; + if (fcntl(sock, F_SETFL, (flags | O_NONBLOCK)) == -1) + return false; + return true; +#else + unsigned long ioctlsocket_ret = 1; + + /* Returns non-0 on failure, while fcntl() returns -1 on failure */ + return (ioctlsocket(sock, FIONBIO, &ioctlsocket_ret) == 0); +#endif +} + +/* + * Put socket into blocking mode. + * Returns true on success, false on failure. + */ +bool +pg_set_block(pgsocket sock) +{ +#if !defined(WIN32) + int flags; + + flags = fcntl(sock, F_GETFL); + if (flags < 0) + return false; + if (fcntl(sock, F_SETFL, (flags & ~O_NONBLOCK)) == -1) + return false; + return true; +#else + unsigned long ioctlsocket_ret = 0; + + /* Returns non-0 on failure, while fcntl() returns -1 on failure */ + return (ioctlsocket(sock, FIONBIO, &ioctlsocket_ret) == 0); +#endif +} diff --git a/libpq/non-bsd/strlcpy.c b/libpq/non-bsd/strlcpy.c new file mode 100644 index 0000000..6aff65c --- /dev/null +++ b/libpq/non-bsd/strlcpy.c @@ -0,0 +1,71 @@ +/*------------------------------------------------------------------------- + * + * strlcpy.c + * strncpy done right + * + * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * + * + * IDENTIFICATION + * src/port/strlcpy.c + * + * This file was taken from OpenBSD and is used on platforms that don't + * provide strlcpy(). The OpenBSD copyright terms follow. + *------------------------------------------------------------------------- + */ + +/* $OpenBSD: strlcpy.c,v 1.11 2006/05/05 15:27:38 millert Exp $ */ + +/* + * Copyright (c) 1998 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "c.h" + + +/* + * Copy src to string dst of size siz. At most siz-1 characters + * will be copied. Always NUL terminates (unless siz == 0). + * Returns strlen(src); if retval >= siz, truncation occurred. + * Function creation history: http://www.gratisoft.us/todd/papers/strlcpy.html + */ +size_t +strlcpy(char *dst, const char *src, size_t siz) +{ + char *d = dst; + const char *s = src; + size_t n = siz; + + /* Copy as many bytes as will fit */ + if (n != 0) + { + while (--n != 0) + { + if ((*d++ = *s++) == '\0') + break; + } + } + + /* Not enough room in dst, add NUL and traverse rest of src */ + if (n == 0) + { + if (siz != 0) + *d = '\0'; /* NUL-terminate dst */ + while (*s++) + ; + } + + return (s - src - 1); /* count does not include NUL */ +} diff --git a/libpq/pg_service.conf.sample b/libpq/pg_service.conf.sample new file mode 100644 index 0000000..5a1c083 --- /dev/null +++ b/libpq/pg_service.conf.sample @@ -0,0 +1,17 @@ +# +# Connection configuration file +# +# A service is a set of named connection parameters. You may specify +# multiple services in this file. Each starts with a service name in +# brackets. Subsequent lines have connection configuration parameters of +# the pattern "param=value" or LDAP URLs starting with "ldap://" +# to look up such parameters. A sample configuration for postgres is +# included in this file. Lines beginning with '#' are comments. +# +# Copy this to your sysconf directory (typically /usr/local/pgsql/etc) and +# rename it pg_service.conf. +# +# +#[postgres] +#dbname=postgres +#user=postgres diff --git a/libpq/pgstrcasecmp.c b/libpq/pgstrcasecmp.c new file mode 100644 index 0000000..ee87a65 --- /dev/null +++ b/libpq/pgstrcasecmp.c @@ -0,0 +1,151 @@ +/*------------------------------------------------------------------------- + * + * pgstrcasecmp.c + * Portable SQL-like case-independent comparisons and conversions. + * + * SQL99 specifies Unicode-aware case normalization, which we don't yet + * have the infrastructure for. Instead we use tolower() to provide a + * locale-aware translation. However, there are some locales where this + * is not right either (eg, Turkish may do strange things with 'i' and + * 'I'). Our current compromise is to use tolower() for characters with + * the high bit set, and use an ASCII-only downcasing for 7-bit + * characters. + * + * NB: this code should match downcase_truncate_identifier() in scansup.c. + * + * We also provide strict ASCII-only case conversion functions, which can + * be used to implement C/POSIX case folding semantics no matter what the + * C library thinks the locale is. + * + * + * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * + * src/port/pgstrcasecmp.c + * + *------------------------------------------------------------------------- + */ +#include "c.h" + +#include + + +/* + * Case-independent comparison of two null-terminated strings. + */ +int +pg_strcasecmp(const char *s1, const char *s2) +{ + for (;;) + { + unsigned char ch1 = (unsigned char) *s1++; + unsigned char ch2 = (unsigned char) *s2++; + + if (ch1 != ch2) + { + if (ch1 >= 'A' && ch1 <= 'Z') + ch1 += 'a' - 'A'; + else if (IS_HIGHBIT_SET(ch1) && isupper(ch1)) + ch1 = tolower(ch1); + + if (ch2 >= 'A' && ch2 <= 'Z') + ch2 += 'a' - 'A'; + else if (IS_HIGHBIT_SET(ch2) && isupper(ch2)) + ch2 = tolower(ch2); + + if (ch1 != ch2) + return (int) ch1 - (int) ch2; + } + if (ch1 == 0) + break; + } + return 0; +} + +/* + * Case-independent comparison of two not-necessarily-null-terminated strings. + * At most n bytes will be examined from each string. + */ +int +pg_strncasecmp(const char *s1, const char *s2, size_t n) +{ + while (n-- > 0) + { + unsigned char ch1 = (unsigned char) *s1++; + unsigned char ch2 = (unsigned char) *s2++; + + if (ch1 != ch2) + { + if (ch1 >= 'A' && ch1 <= 'Z') + ch1 += 'a' - 'A'; + else if (IS_HIGHBIT_SET(ch1) && isupper(ch1)) + ch1 = tolower(ch1); + + if (ch2 >= 'A' && ch2 <= 'Z') + ch2 += 'a' - 'A'; + else if (IS_HIGHBIT_SET(ch2) && isupper(ch2)) + ch2 = tolower(ch2); + + if (ch1 != ch2) + return (int) ch1 - (int) ch2; + } + if (ch1 == 0) + break; + } + return 0; +} + +/* + * Fold a character to upper case. + * + * Unlike some versions of toupper(), this is safe to apply to characters + * that aren't lower case letters. Note however that the whole thing is + * a bit bogus for multibyte character sets. + */ +unsigned char +pg_toupper(unsigned char ch) +{ + if (ch >= 'a' && ch <= 'z') + ch += 'A' - 'a'; + else if (IS_HIGHBIT_SET(ch) && islower(ch)) + ch = toupper(ch); + return ch; +} + +/* + * Fold a character to lower case. + * + * Unlike some versions of tolower(), this is safe to apply to characters + * that aren't upper case letters. Note however that the whole thing is + * a bit bogus for multibyte character sets. + */ +unsigned char +pg_tolower(unsigned char ch) +{ + if (ch >= 'A' && ch <= 'Z') + ch += 'a' - 'A'; + else if (IS_HIGHBIT_SET(ch) && isupper(ch)) + ch = tolower(ch); + return ch; +} + +/* + * Fold a character to upper case, following C/POSIX locale rules. + */ +unsigned char +pg_ascii_toupper(unsigned char ch) +{ + if (ch >= 'a' && ch <= 'z') + ch += 'A' - 'a'; + return ch; +} + +/* + * Fold a character to lower case, following C/POSIX locale rules. + */ +unsigned char +pg_ascii_tolower(unsigned char ch) +{ + if (ch >= 'A' && ch <= 'Z') + ch += 'a' - 'A'; + return ch; +} diff --git a/libpq/postgresql/c.h b/libpq/postgresql/c.h new file mode 100644 index 0000000..726f0f3 --- /dev/null +++ b/libpq/postgresql/c.h @@ -0,0 +1,1107 @@ +/*------------------------------------------------------------------------- + * + * c.h + * Fundamental C definitions. This is included by every .c file in + * PostgreSQL (via either postgres.h or postgres_fe.h, as appropriate). + * + * Note that the definitions here are not intended to be exposed to clients + * of the frontend interface libraries --- so we don't worry much about + * polluting the namespace with lots of stuff... + * + * + * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/c.h + * + *------------------------------------------------------------------------- + */ +/* + *---------------------------------------------------------------- + * TABLE OF CONTENTS + * + * When adding stuff to this file, please try to put stuff + * into the relevant section, or add new sections as appropriate. + * + * section description + * ------- ------------------------------------------------ + * 0) pg_config.h and standard system headers + * 1) hacks to cope with non-ANSI C compilers + * 2) bool, true, false, TRUE, FALSE, NULL + * 3) standard system types + * 4) IsValid macros for system types + * 5) offsetof, lengthof, endof, alignment + * 6) assertions + * 7) widely useful macros + * 8) random stuff + * 9) system-specific hacks + * + * NOTE: since this file is included by both frontend and backend modules, it's + * almost certainly wrong to put an "extern" declaration here. typedefs and + * macros are the kind of thing that might go here. + * + *---------------------------------------------------------------- + */ +#ifndef C_H +#define C_H + +#include "postgres_ext.h" + +/* Must undef pg_config_ext.h symbols before including pg_config.h */ +#undef PG_INT64_TYPE + +#include "pg_config.h" +#include "pg_config_manual.h" /* must be after pg_config.h */ + +/* + * We always rely on the WIN32 macro being set by our build system, + * but _WIN32 is the compiler pre-defined macro. So make sure we define + * WIN32 whenever _WIN32 is set, to facilitate standalone building. + */ +#if defined(_WIN32) && !defined(WIN32) +#define WIN32 +#endif + +#if !defined(WIN32) && !defined(__CYGWIN__) /* win32 includes further down */ +#include "pg_config_os.h" /* must be before any system header files */ +#endif + +#if _MSC_VER >= 1400 || defined(HAVE_CRTDEFS_H) +#define errcode __msvc_errcode +#include +#undef errcode +#endif + +/* + * We have to include stdlib.h here because it defines many of these macros + * on some platforms, and we only want our definitions used if stdlib.h doesn't + * have its own. The same goes for stddef and stdarg if present. + */ + +#include +#include +#include +#include +#include +#ifdef HAVE_STRINGS_H +#include +#endif +#ifdef HAVE_STDINT_H +#include +#endif +#include + +#include +#if defined(WIN32) || defined(__CYGWIN__) +#include /* ensure O_BINARY is available */ +#endif + +#if defined(WIN32) || defined(__CYGWIN__) +/* We have to redefine some system functions after they are included above. */ +#include "pg_config_os.h" +#endif + +/* + * Force disable inlining if PG_FORCE_DISABLE_INLINE is defined. This is used + * to work around compiler bugs and might also be useful for investigatory + * purposes by defining the symbol in the platform's header.. + * + * This is done early (in slightly the wrong section) as functionality later + * in this file might want to rely on inline functions. + */ +#ifdef PG_FORCE_DISABLE_INLINE +#undef inline +#define inline +#endif + +/* Must be before gettext() games below */ +#include + +#define _(x) gettext(x) + +#ifdef ENABLE_NLS +#include +#else +#define gettext(x) (x) +#define dgettext(d,x) (x) +#define ngettext(s,p,n) ((n) == 1 ? (s) : (p)) +#define dngettext(d,s,p,n) ((n) == 1 ? (s) : (p)) +#endif + +/* + * Use this to mark string constants as needing translation at some later + * time, rather than immediately. This is useful for cases where you need + * access to the original string and translated string, and for cases where + * immediate translation is not possible, like when initializing global + * variables. + * http://www.gnu.org/software/autoconf/manual/gettext/Special-cases.html + */ +#define gettext_noop(x) (x) + + +/* ---------------------------------------------------------------- + * Section 1: hacks to cope with non-ANSI C compilers + * + * type prefixes (const, signed, volatile, inline) are handled in pg_config.h. + * ---------------------------------------------------------------- + */ + +/* + * CppAsString + * Convert the argument to a string, using the C preprocessor. + * CppConcat + * Concatenate two arguments together, using the C preprocessor. + * + * Note: There used to be support here for pre-ANSI C compilers that didn't + * support # and ##. Nowadays, these macros are just for clarity and/or + * backward compatibility with existing PostgreSQL code. + */ +#define CppAsString(identifier) #identifier +#define CppConcat(x, y) x##y + +/* + * dummyret is used to set return values in macros that use ?: to make + * assignments. gcc wants these to be void, other compilers like char + */ +#ifdef __GNUC__ /* GNU cc */ +#define dummyret void +#else +#define dummyret char +#endif + +/* Which __func__ symbol do we have, if any? */ +#ifdef HAVE_FUNCNAME__FUNC +#define PG_FUNCNAME_MACRO __func__ +#else +#ifdef HAVE_FUNCNAME__FUNCTION +#define PG_FUNCNAME_MACRO __FUNCTION__ +#else +#define PG_FUNCNAME_MACRO NULL +#endif +#endif + +/* ---------------------------------------------------------------- + * Section 2: bool, true, false, TRUE, FALSE, NULL + * ---------------------------------------------------------------- + */ + +/* + * bool + * Boolean value, either true or false. + * + * XXX for C++ compilers, we assume the compiler has a compatible + * built-in definition of bool. + */ + +#ifndef __cplusplus + +#ifndef bool +typedef char bool; +#endif + +#ifndef true +#define true ((bool) 1) +#endif + +#ifndef false +#define false ((bool) 0) +#endif +#endif /* not C++ */ + +typedef bool *BoolPtr; + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +/* + * NULL + * Null pointer. + */ +#ifndef NULL +#define NULL ((void *) 0) +#endif + + +/* ---------------------------------------------------------------- + * Section 3: standard system types + * ---------------------------------------------------------------- + */ + +/* + * Pointer + * Variable holding address of any memory resident object. + * + * XXX Pointer arithmetic is done with this, so it can't be void * + * under "true" ANSI compilers. + */ +typedef char *Pointer; + +/* + * intN + * Signed integer, EXACTLY N BITS IN SIZE, + * used for numerical computations and the + * frontend/backend protocol. + */ +#ifndef HAVE_INT8 +typedef signed char int8; /* == 8 bits */ +typedef signed short int16; /* == 16 bits */ +typedef signed int int32; /* == 32 bits */ +#endif /* not HAVE_INT8 */ + +/* + * uintN + * Unsigned integer, EXACTLY N BITS IN SIZE, + * used for numerical computations and the + * frontend/backend protocol. + */ +#ifndef HAVE_UINT8 +typedef unsigned char uint8; /* == 8 bits */ +typedef unsigned short uint16; /* == 16 bits */ +typedef unsigned int uint32; /* == 32 bits */ +#endif /* not HAVE_UINT8 */ + +/* + * bitsN + * Unit of bitwise operation, AT LEAST N BITS IN SIZE. + */ +typedef uint8 bits8; /* >= 8 bits */ +typedef uint16 bits16; /* >= 16 bits */ +typedef uint32 bits32; /* >= 32 bits */ + +/* + * 64-bit integers + */ +#ifdef HAVE_LONG_INT_64 +/* Plain "long int" fits, use it */ + +#ifndef HAVE_INT64 +typedef long int int64; +#endif +#ifndef HAVE_UINT64 +typedef unsigned long int uint64; +#endif +#elif defined(HAVE_LONG_LONG_INT_64) +/* We have working support for "long long int", use that */ + +#ifndef HAVE_INT64 +typedef long long int int64; +#endif +#ifndef HAVE_UINT64 +typedef unsigned long long int uint64; +#endif +#else +/* neither HAVE_LONG_INT_64 nor HAVE_LONG_LONG_INT_64 */ +#error must have a working 64-bit integer datatype +#endif + +/* Decide if we need to decorate 64-bit constants */ +#ifdef HAVE_LL_CONSTANTS +#define INT64CONST(x) ((int64) x##LL) +#define UINT64CONST(x) ((uint64) x##ULL) +#else +#define INT64CONST(x) ((int64) x) +#define UINT64CONST(x) ((uint64) x) +#endif + +/* snprintf format strings to use for 64-bit integers */ +#define INT64_FORMAT "%" INT64_MODIFIER "d" +#define UINT64_FORMAT "%" INT64_MODIFIER "u" + +/* + * 128-bit signed and unsigned integers + * There currently is only a limited support for the type. E.g. 128bit + * literals and snprintf are not supported; but math is. + */ +#if defined(PG_INT128_TYPE) +#define HAVE_INT128 +typedef PG_INT128_TYPE int128; +typedef unsigned PG_INT128_TYPE uint128; +#endif + +/* + * stdint.h limits aren't guaranteed to be present and aren't guaranteed to + * have compatible types with our fixed width types. So just define our own. + */ +#define PG_INT8_MIN (-0x7F-1) +#define PG_INT8_MAX (0x7F) +#define PG_UINT8_MAX (0xFF) +#define PG_INT16_MIN (-0x7FFF-1) +#define PG_INT16_MAX (0x7FFF) +#define PG_UINT16_MAX (0xFFFF) +#define PG_INT32_MIN (-0x7FFFFFFF-1) +#define PG_INT32_MAX (0x7FFFFFFF) +#define PG_UINT32_MAX (0xFFFFFFFF) +#define PG_INT64_MIN (-INT64CONST(0x7FFFFFFFFFFFFFFF) - 1) +#define PG_INT64_MAX INT64CONST(0x7FFFFFFFFFFFFFFF) +#define PG_UINT64_MAX UINT64CONST(0xFFFFFFFFFFFFFFFF) + +/* Select timestamp representation (float8 or int64) */ +#ifdef USE_INTEGER_DATETIMES +#define HAVE_INT64_TIMESTAMP +#endif + +/* + * Size + * Size of any memory resident object, as returned by sizeof. + */ +typedef size_t Size; + +/* + * Index + * Index into any memory resident array. + * + * Note: + * Indices are non negative. + */ +typedef unsigned int Index; + +/* + * Offset + * Offset into any memory resident array. + * + * Note: + * This differs from an Index in that an Index is always + * non negative, whereas Offset may be negative. + */ +typedef signed int Offset; + +/* + * Common Postgres datatype names (as used in the catalogs) + */ +typedef float float4; +typedef double float8; + +/* + * Oid, RegProcedure, TransactionId, SubTransactionId, MultiXactId, + * CommandId + */ + +/* typedef Oid is in postgres_ext.h */ + +/* + * regproc is the type name used in the include/catalog headers, but + * RegProcedure is the preferred name in C code. + */ +typedef Oid regproc; +typedef regproc RegProcedure; + +typedef uint32 TransactionId; + +typedef uint32 LocalTransactionId; + +typedef uint32 SubTransactionId; + +#define InvalidSubTransactionId ((SubTransactionId) 0) +#define TopSubTransactionId ((SubTransactionId) 1) + +/* MultiXactId must be equivalent to TransactionId, to fit in t_xmax */ +typedef TransactionId MultiXactId; + +typedef uint32 MultiXactOffset; + +typedef uint32 CommandId; + +#define FirstCommandId ((CommandId) 0) +#define InvalidCommandId (~(CommandId)0) + +/* + * Array indexing support + */ +#define MAXDIM 6 +typedef struct +{ + int indx[MAXDIM]; +} IntArray; + +/* ---------------- + * Variable-length datatypes all share the 'struct varlena' header. + * + * NOTE: for TOASTable types, this is an oversimplification, since the value + * may be compressed or moved out-of-line. However datatype-specific routines + * are mostly content to deal with de-TOASTed values only, and of course + * client-side routines should never see a TOASTed value. But even in a + * de-TOASTed value, beware of touching vl_len_ directly, as its representation + * is no longer convenient. It's recommended that code always use the VARDATA, + * VARSIZE, and SET_VARSIZE macros instead of relying on direct mentions of + * the struct fields. See postgres.h for details of the TOASTed form. + * ---------------- + */ +struct varlena +{ + char vl_len_[4]; /* Do not touch this field directly! */ + char vl_dat[FLEXIBLE_ARRAY_MEMBER]; /* Data content is here */ +}; + +#define VARHDRSZ ((int32) sizeof(int32)) + +/* + * These widely-used datatypes are just a varlena header and the data bytes. + * There is no terminating null or anything like that --- the data length is + * always VARSIZE(ptr) - VARHDRSZ. + */ +typedef struct varlena bytea; +typedef struct varlena text; +typedef struct varlena BpChar; /* blank-padded char, ie SQL char(n) */ +typedef struct varlena VarChar; /* var-length char, ie SQL varchar(n) */ + +/* + * Specialized array types. These are physically laid out just the same + * as regular arrays (so that the regular array subscripting code works + * with them). They exist as distinct types mostly for historical reasons: + * they have nonstandard I/O behavior which we don't want to change for fear + * of breaking applications that look at the system catalogs. There is also + * an implementation issue for oidvector: it's part of the primary key for + * pg_proc, and we can't use the normal btree array support routines for that + * without circularity. + */ +typedef struct +{ + int32 vl_len_; /* these fields must match ArrayType! */ + int ndim; /* always 1 for int2vector */ + int32 dataoffset; /* always 0 for int2vector */ + Oid elemtype; + int dim1; + int lbound1; + int16 values[FLEXIBLE_ARRAY_MEMBER]; +} int2vector; + +typedef struct +{ + int32 vl_len_; /* these fields must match ArrayType! */ + int ndim; /* always 1 for oidvector */ + int32 dataoffset; /* always 0 for oidvector */ + Oid elemtype; + int dim1; + int lbound1; + Oid values[FLEXIBLE_ARRAY_MEMBER]; +} oidvector; + +/* + * Representation of a Name: effectively just a C string, but null-padded to + * exactly NAMEDATALEN bytes. The use of a struct is historical. + */ +typedef struct nameData +{ + char data[NAMEDATALEN]; +} NameData; +typedef NameData *Name; + +#define NameStr(name) ((name).data) + +/* + * Support macros for escaping strings. escape_backslash should be TRUE + * if generating a non-standard-conforming string. Prefixing a string + * with ESCAPE_STRING_SYNTAX guarantees it is non-standard-conforming. + * Beware of multiple evaluation of the "ch" argument! + */ +#define SQL_STR_DOUBLE(ch, escape_backslash) \ + ((ch) == '\'' || ((ch) == '\\' && (escape_backslash))) + +#define ESCAPE_STRING_SYNTAX 'E' + +/* ---------------------------------------------------------------- + * Section 4: IsValid macros for system types + * ---------------------------------------------------------------- + */ +/* + * BoolIsValid + * True iff bool is valid. + */ +#define BoolIsValid(boolean) ((boolean) == false || (boolean) == true) + +/* + * PointerIsValid + * True iff pointer is valid. + */ +#define PointerIsValid(pointer) ((const void*)(pointer) != NULL) + +/* + * PointerIsAligned + * True iff pointer is properly aligned to point to the given type. + */ +#define PointerIsAligned(pointer, type) \ + (((uintptr_t)(pointer) % (sizeof (type))) == 0) + +#define OidIsValid(objectId) ((bool) ((objectId) != InvalidOid)) + +#define RegProcedureIsValid(p) OidIsValid(p) + + +/* ---------------------------------------------------------------- + * Section 5: offsetof, lengthof, endof, alignment + * ---------------------------------------------------------------- + */ +/* + * offsetof + * Offset of a structure/union field within that structure/union. + * + * XXX This is supposed to be part of stddef.h, but isn't on + * some systems (like SunOS 4). + */ +#ifndef offsetof +#define offsetof(type, field) ((long) &((type *)0)->field) +#endif /* offsetof */ + +/* + * lengthof + * Number of elements in an array. + */ +#define lengthof(array) (sizeof (array) / sizeof ((array)[0])) + +/* + * endof + * Address of the element one past the last in an array. + */ +#define endof(array) (&(array)[lengthof(array)]) + +/* ---------------- + * Alignment macros: align a length or address appropriately for a given type. + * The fooALIGN() macros round up to a multiple of the required alignment, + * while the fooALIGN_DOWN() macros round down. The latter are more useful + * for problems like "how many X-sized structures will fit in a page?". + * + * NOTE: TYPEALIGN[_DOWN] will not work if ALIGNVAL is not a power of 2. + * That case seems extremely unlikely to be needed in practice, however. + * ---------------- + */ + +#define TYPEALIGN(ALIGNVAL,LEN) \ + (((uintptr_t) (LEN) + ((ALIGNVAL) - 1)) & ~((uintptr_t) ((ALIGNVAL) - 1))) + +#define SHORTALIGN(LEN) TYPEALIGN(ALIGNOF_SHORT, (LEN)) +#define INTALIGN(LEN) TYPEALIGN(ALIGNOF_INT, (LEN)) +#define LONGALIGN(LEN) TYPEALIGN(ALIGNOF_LONG, (LEN)) +#define DOUBLEALIGN(LEN) TYPEALIGN(ALIGNOF_DOUBLE, (LEN)) +#define MAXALIGN(LEN) TYPEALIGN(MAXIMUM_ALIGNOF, (LEN)) +/* MAXALIGN covers only built-in types, not buffers */ +#define BUFFERALIGN(LEN) TYPEALIGN(ALIGNOF_BUFFER, (LEN)) +#define CACHELINEALIGN(LEN) TYPEALIGN(PG_CACHE_LINE_SIZE, (LEN)) + +#define TYPEALIGN_DOWN(ALIGNVAL,LEN) \ + (((uintptr_t) (LEN)) & ~((uintptr_t) ((ALIGNVAL) - 1))) + +#define SHORTALIGN_DOWN(LEN) TYPEALIGN_DOWN(ALIGNOF_SHORT, (LEN)) +#define INTALIGN_DOWN(LEN) TYPEALIGN_DOWN(ALIGNOF_INT, (LEN)) +#define LONGALIGN_DOWN(LEN) TYPEALIGN_DOWN(ALIGNOF_LONG, (LEN)) +#define DOUBLEALIGN_DOWN(LEN) TYPEALIGN_DOWN(ALIGNOF_DOUBLE, (LEN)) +#define MAXALIGN_DOWN(LEN) TYPEALIGN_DOWN(MAXIMUM_ALIGNOF, (LEN)) + +/* + * The above macros will not work with types wider than uintptr_t, like with + * uint64 on 32-bit platforms. That's not problem for the usual use where a + * pointer or a length is aligned, but for the odd case that you need to + * align something (potentially) wider, use TYPEALIGN64. + */ +#define TYPEALIGN64(ALIGNVAL,LEN) \ + (((uint64) (LEN) + ((ALIGNVAL) - 1)) & ~((uint64) ((ALIGNVAL) - 1))) + +/* we don't currently need wider versions of the other ALIGN macros */ +#define MAXALIGN64(LEN) TYPEALIGN64(MAXIMUM_ALIGNOF, (LEN)) + +/* ---------------- + * Attribute macros + * + * GCC: https://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html + * GCC: https://gcc.gnu.org/onlinedocs/gcc/Type-Attributes.html + * Sunpro: https://docs.oracle.com/cd/E18659_01/html/821-1384/gjzke.html + * XLC: http://www-01.ibm.com/support/knowledgecenter/SSGH2K_11.1.0/com.ibm.xlc111.aix.doc/language_ref/function_attributes.html + * XLC: http://www-01.ibm.com/support/knowledgecenter/SSGH2K_11.1.0/com.ibm.xlc111.aix.doc/language_ref/type_attrib.html + * ---------------- + */ + +/* only GCC supports the unused attribute */ +#ifdef __GNUC__ +#define pg_attribute_unused() __attribute__((unused)) +#else +#define pg_attribute_unused() +#endif + +/* GCC and XLC support format attributes */ +#if defined(__GNUC__) || defined(__IBMC__) +#define pg_attribute_format_arg(a) __attribute__((format_arg(a))) +#define pg_attribute_printf(f,a) __attribute__((format(PG_PRINTF_ATTRIBUTE, f, a))) +#else +#define pg_attribute_format_arg(a) +#define pg_attribute_printf(f,a) +#endif + +/* GCC, Sunpro and XLC support aligned, packed and noreturn */ +#if defined(__GNUC__) || defined(__SUNPRO_C) || defined(__IBMC__) +#define pg_attribute_aligned(a) __attribute__((aligned(a))) +#define pg_attribute_noreturn() __attribute__((noreturn)) +#define pg_attribute_packed() __attribute__((packed)) +#define HAVE_PG_ATTRIBUTE_NORETURN 1 +#else +/* + * NB: aligned and packed are not given default definitions because they + * affect code functionality; they *must* be implemented by the compiler + * if they are to be used. + */ +#define pg_attribute_noreturn() +#endif + +/* ---------------------------------------------------------------- + * Section 6: assertions + * ---------------------------------------------------------------- + */ + +/* + * USE_ASSERT_CHECKING, if defined, turns on all the assertions. + * - plai 9/5/90 + * + * It should _NOT_ be defined in releases or in benchmark copies + */ + +/* + * Assert() can be used in both frontend and backend code. In frontend code it + * just calls the standard assert, if it's available. If use of assertions is + * not configured, it does nothing. + */ +#ifndef USE_ASSERT_CHECKING + +#define Assert(condition) ((void)true) +#define AssertMacro(condition) ((void)true) +#define AssertArg(condition) ((void)true) +#define AssertState(condition) ((void)true) +#define AssertPointerAlignment(ptr, bndr) ((void)true) +#define Trap(condition, errorType) ((void)true) +#define TrapMacro(condition, errorType) (true) + +#elif defined(FRONTEND) + +#include +#define Assert(p) assert(p) +#define AssertMacro(p) ((void) assert(p)) +#define AssertArg(condition) assert(condition) +#define AssertState(condition) assert(condition) +#define AssertPointerAlignment(ptr, bndr) ((void)true) +#else /* USE_ASSERT_CHECKING && !FRONTEND */ + +/* + * Trap + * Generates an exception if the given condition is true. + */ +#define Trap(condition, errorType) \ + do { \ + if (condition) \ + ExceptionalCondition(CppAsString(condition), (errorType), \ + __FILE__, __LINE__); \ + } while (0) + +/* + * TrapMacro is the same as Trap but it's intended for use in macros: + * + * #define foo(x) (AssertMacro(x != 0), bar(x)) + * + * Isn't CPP fun? + */ +#define TrapMacro(condition, errorType) \ + ((bool) (! (condition) || \ + (ExceptionalCondition(CppAsString(condition), (errorType), \ + __FILE__, __LINE__), 0))) + +#define Assert(condition) \ + Trap(!(condition), "FailedAssertion") + +#define AssertMacro(condition) \ + ((void) TrapMacro(!(condition), "FailedAssertion")) + +#define AssertArg(condition) \ + Trap(!(condition), "BadArgument") + +#define AssertState(condition) \ + Trap(!(condition), "BadState") + +/* + * Check that `ptr' is `bndr' aligned. + */ +#define AssertPointerAlignment(ptr, bndr) \ + Trap(TYPEALIGN(bndr, (uintptr_t)(ptr)) != (uintptr_t)(ptr), \ + "UnalignedPointer") + +#endif /* USE_ASSERT_CHECKING && !FRONTEND */ + +/* + * Macros to support compile-time assertion checks. + * + * If the "condition" (a compile-time-constant expression) evaluates to false, + * throw a compile error using the "errmessage" (a string literal). + * + * gcc 4.6 and up supports _Static_assert(), but there are bizarre syntactic + * placement restrictions. These macros make it safe to use as a statement + * or in an expression, respectively. + * + * Otherwise we fall back on a kluge that assumes the compiler will complain + * about a negative width for a struct bit-field. This will not include a + * helpful error message, but it beats not getting an error at all. + */ +#ifdef HAVE__STATIC_ASSERT +#define StaticAssertStmt(condition, errmessage) \ + do { _Static_assert(condition, errmessage); } while(0) +#define StaticAssertExpr(condition, errmessage) \ + ({ StaticAssertStmt(condition, errmessage); true; }) +#else /* !HAVE__STATIC_ASSERT */ +#define StaticAssertStmt(condition, errmessage) \ + ((void) sizeof(struct { int static_assert_failure : (condition) ? 1 : -1; })) +#define StaticAssertExpr(condition, errmessage) \ + StaticAssertStmt(condition, errmessage) +#endif /* HAVE__STATIC_ASSERT */ + + +/* + * Compile-time checks that a variable (or expression) has the specified type. + * + * AssertVariableIsOfType() can be used as a statement. + * AssertVariableIsOfTypeMacro() is intended for use in macros, eg + * #define foo(x) (AssertVariableIsOfTypeMacro(x, int), bar(x)) + * + * If we don't have __builtin_types_compatible_p, we can still assert that + * the types have the same size. This is far from ideal (especially on 32-bit + * platforms) but it provides at least some coverage. + */ +#ifdef HAVE__BUILTIN_TYPES_COMPATIBLE_P +#define AssertVariableIsOfType(varname, typename) \ + StaticAssertStmt(__builtin_types_compatible_p(__typeof__(varname), typename), \ + CppAsString(varname) " does not have type " CppAsString(typename)) +#define AssertVariableIsOfTypeMacro(varname, typename) \ + ((void) StaticAssertExpr(__builtin_types_compatible_p(__typeof__(varname), typename), \ + CppAsString(varname) " does not have type " CppAsString(typename))) +#else /* !HAVE__BUILTIN_TYPES_COMPATIBLE_P */ +#define AssertVariableIsOfType(varname, typename) \ + StaticAssertStmt(sizeof(varname) == sizeof(typename), \ + CppAsString(varname) " does not have type " CppAsString(typename)) +#define AssertVariableIsOfTypeMacro(varname, typename) \ + ((void) StaticAssertExpr(sizeof(varname) == sizeof(typename), \ + CppAsString(varname) " does not have type " CppAsString(typename))) +#endif /* HAVE__BUILTIN_TYPES_COMPATIBLE_P */ + + +/* ---------------------------------------------------------------- + * Section 7: widely useful macros + * ---------------------------------------------------------------- + */ +/* + * Max + * Return the maximum of two numbers. + */ +#define Max(x, y) ((x) > (y) ? (x) : (y)) + +/* + * Min + * Return the minimum of two numbers. + */ +#define Min(x, y) ((x) < (y) ? (x) : (y)) + +/* + * Abs + * Return the absolute value of the argument. + */ +#define Abs(x) ((x) >= 0 ? (x) : -(x)) + +/* + * StrNCpy + * Like standard library function strncpy(), except that result string + * is guaranteed to be null-terminated --- that is, at most N-1 bytes + * of the source string will be kept. + * Also, the macro returns no result (too hard to do that without + * evaluating the arguments multiple times, which seems worse). + * + * BTW: when you need to copy a non-null-terminated string (like a text + * datum) and add a null, do not do it with StrNCpy(..., len+1). That + * might seem to work, but it fetches one byte more than there is in the + * text object. One fine day you'll have a SIGSEGV because there isn't + * another byte before the end of memory. Don't laugh, we've had real + * live bug reports from real live users over exactly this mistake. + * Do it honestly with "memcpy(dst,src,len); dst[len] = '\0';", instead. + */ +#define StrNCpy(dst,src,len) \ + do \ + { \ + char * _dst = (dst); \ + Size _len = (len); \ +\ + if (_len > 0) \ + { \ + strncpy(_dst, (src), _len); \ + _dst[_len-1] = '\0'; \ + } \ + } while (0) + + +/* Get a bit mask of the bits set in non-long aligned addresses */ +#define LONG_ALIGN_MASK (sizeof(long) - 1) + +/* + * MemSet + * Exactly the same as standard library function memset(), but considerably + * faster for zeroing small word-aligned structures (such as parsetree nodes). + * This has to be a macro because the main point is to avoid function-call + * overhead. However, we have also found that the loop is faster than + * native libc memset() on some platforms, even those with assembler + * memset() functions. More research needs to be done, perhaps with + * MEMSET_LOOP_LIMIT tests in configure. + */ +#define MemSet(start, val, len) \ + do \ + { \ + /* must be void* because we don't know if it is integer aligned yet */ \ + void *_vstart = (void *) (start); \ + int _val = (val); \ + Size _len = (len); \ +\ + if ((((uintptr_t) _vstart) & LONG_ALIGN_MASK) == 0 && \ + (_len & LONG_ALIGN_MASK) == 0 && \ + _val == 0 && \ + _len <= MEMSET_LOOP_LIMIT && \ + /* \ + * If MEMSET_LOOP_LIMIT == 0, optimizer should find \ + * the whole "if" false at compile time. \ + */ \ + MEMSET_LOOP_LIMIT != 0) \ + { \ + long *_start = (long *) _vstart; \ + long *_stop = (long *) ((char *) _start + _len); \ + while (_start < _stop) \ + *_start++ = 0; \ + } \ + else \ + memset(_vstart, _val, _len); \ + } while (0) + +/* + * MemSetAligned is the same as MemSet except it omits the test to see if + * "start" is word-aligned. This is okay to use if the caller knows a-priori + * that the pointer is suitably aligned (typically, because he just got it + * from palloc(), which always delivers a max-aligned pointer). + */ +#define MemSetAligned(start, val, len) \ + do \ + { \ + long *_start = (long *) (start); \ + int _val = (val); \ + Size _len = (len); \ +\ + if ((_len & LONG_ALIGN_MASK) == 0 && \ + _val == 0 && \ + _len <= MEMSET_LOOP_LIMIT && \ + MEMSET_LOOP_LIMIT != 0) \ + { \ + long *_stop = (long *) ((char *) _start + _len); \ + while (_start < _stop) \ + *_start++ = 0; \ + } \ + else \ + memset(_start, _val, _len); \ + } while (0) + + +/* + * MemSetTest/MemSetLoop are a variant version that allow all the tests in + * MemSet to be done at compile time in cases where "val" and "len" are + * constants *and* we know the "start" pointer must be word-aligned. + * If MemSetTest succeeds, then it is okay to use MemSetLoop, otherwise use + * MemSetAligned. Beware of multiple evaluations of the arguments when using + * this approach. + */ +#define MemSetTest(val, len) \ + ( ((len) & LONG_ALIGN_MASK) == 0 && \ + (len) <= MEMSET_LOOP_LIMIT && \ + MEMSET_LOOP_LIMIT != 0 && \ + (val) == 0 ) + +#define MemSetLoop(start, val, len) \ + do \ + { \ + long * _start = (long *) (start); \ + long * _stop = (long *) ((char *) _start + (Size) (len)); \ + \ + while (_start < _stop) \ + *_start++ = 0; \ + } while (0) + + +/* + * Mark a point as unreachable in a portable fashion. This should preferably + * be something that the compiler understands, to aid code generation. + * In assert-enabled builds, we prefer abort() for debugging reasons. + */ +#if defined(HAVE__BUILTIN_UNREACHABLE) && !defined(USE_ASSERT_CHECKING) +#define pg_unreachable() __builtin_unreachable() +#elif defined(_MSC_VER) && !defined(USE_ASSERT_CHECKING) +#define pg_unreachable() __assume(0) +#else +#define pg_unreachable() abort() +#endif + + +/* ---------------------------------------------------------------- + * Section 8: random stuff + * ---------------------------------------------------------------- + */ + +/* msb for char */ +#define HIGHBIT (0x80) +#define IS_HIGHBIT_SET(ch) ((unsigned char)(ch) & HIGHBIT) + +#define STATUS_OK (0) +#define STATUS_ERROR (-1) +#define STATUS_EOF (-2) +#define STATUS_FOUND (1) +#define STATUS_WAITING (2) + + +/* + * Append PG_USED_FOR_ASSERTS_ONLY to definitions of variables that are only + * used in assert-enabled builds, to avoid compiler warnings about unused + * variables in assert-disabled builds. + */ +#ifdef USE_ASSERT_CHECKING +#define PG_USED_FOR_ASSERTS_ONLY +#else +#define PG_USED_FOR_ASSERTS_ONLY pg_attribute_unused() +#endif + + +/* gettext domain name mangling */ + +/* + * To better support parallel installations of major PostgreSQL + * versions as well as parallel installations of major library soname + * versions, we mangle the gettext domain name by appending those + * version numbers. The coding rule ought to be that wherever the + * domain name is mentioned as a literal, it must be wrapped into + * PG_TEXTDOMAIN(). The macros below do not work on non-literals; but + * that is somewhat intentional because it avoids having to worry + * about multiple states of premangling and postmangling as the values + * are being passed around. + * + * Make sure this matches the installation rules in nls-global.mk. + */ + +/* need a second indirection because we want to stringize the macro value, not the name */ +#define CppAsString2(x) CppAsString(x) + +#ifdef SO_MAJOR_VERSION +#define PG_TEXTDOMAIN(domain) (domain CppAsString2(SO_MAJOR_VERSION) "-" PG_MAJORVERSION) +#else +#define PG_TEXTDOMAIN(domain) (domain "-" PG_MAJORVERSION) +#endif + + +/* ---------------------------------------------------------------- + * Section 9: system-specific hacks + * + * This should be limited to things that absolutely have to be + * included in every source file. The port-specific header file + * is usually a better place for this sort of thing. + * ---------------------------------------------------------------- + */ + +/* + * NOTE: this is also used for opening text files. + * WIN32 treats Control-Z as EOF in files opened in text mode. + * Therefore, we open files in binary mode on Win32 so we can read + * literal control-Z. The other affect is that we see CRLF, but + * that is OK because we can already handle those cleanly. + */ +#if defined(WIN32) || defined(__CYGWIN__) +#define PG_BINARY O_BINARY +#define PG_BINARY_A "ab" +#define PG_BINARY_R "rb" +#define PG_BINARY_W "wb" +#else +#define PG_BINARY 0 +#define PG_BINARY_A "a" +#define PG_BINARY_R "r" +#define PG_BINARY_W "w" +#endif + +/* + * Provide prototypes for routines not present in a particular machine's + * standard C library. + */ + +#if !HAVE_DECL_SNPRINTF +extern int snprintf(char *str, size_t count, const char *fmt,...) pg_attribute_printf(3, 4); +#endif + +#if !HAVE_DECL_VSNPRINTF +extern int vsnprintf(char *str, size_t count, const char *fmt, va_list args); +#endif + +#if !defined(HAVE_MEMMOVE) && !defined(memmove) +#define memmove(d, s, c) bcopy(s, d, c) +#endif + +/* no special DLL markers on most ports */ +#ifndef PGDLLIMPORT +#define PGDLLIMPORT +#endif +#ifndef PGDLLEXPORT +#define PGDLLEXPORT +#endif + +/* + * The following is used as the arg list for signal handlers. Any ports + * that take something other than an int argument should override this in + * their pg_config_os.h file. Note that variable names are required + * because it is used in both the prototypes as well as the definitions. + * Note also the long name. We expect that this won't collide with + * other names causing compiler warnings. + */ + +#ifndef SIGNAL_ARGS +#define SIGNAL_ARGS int postgres_signal_arg +#endif + +/* + * When there is no sigsetjmp, its functionality is provided by plain + * setjmp. Incidentally, nothing provides setjmp's functionality in + * that case. We now support the case only on Windows. + */ +#ifdef WIN32 +#define sigjmp_buf jmp_buf +#define sigsetjmp(x,y) setjmp(x) +#define siglongjmp longjmp +#endif + +#if defined(HAVE_FDATASYNC) && !HAVE_DECL_FDATASYNC +extern int fdatasync(int fildes); +#endif + +/* If strtoq() exists, rename it to the more standard strtoll() */ +#if defined(HAVE_LONG_LONG_INT_64) && !defined(HAVE_STRTOLL) && defined(HAVE_STRTOQ) +#define strtoll strtoq +#define HAVE_STRTOLL 1 +#endif + +/* If strtouq() exists, rename it to the more standard strtoull() */ +#if defined(HAVE_LONG_LONG_INT_64) && !defined(HAVE_STRTOULL) && defined(HAVE_STRTOUQ) +#define strtoull strtouq +#define HAVE_STRTOULL 1 +#endif + +/* + * We assume if we have these two functions, we have their friends too, and + * can use the wide-character functions. + */ +#if defined(HAVE_WCSTOMBS) && defined(HAVE_TOWLOWER) +#define USE_WIDE_UPPER_LOWER +#endif + +/* EXEC_BACKEND defines */ +#ifdef EXEC_BACKEND +#define NON_EXEC_STATIC +#else +#define NON_EXEC_STATIC static +#endif + +/* /port compatibility functions */ +#include "port.h" + +#endif /* C_H */ diff --git a/libpq/postgresql/common/fe_memutils.h b/libpq/postgresql/common/fe_memutils.h new file mode 100644 index 0000000..b4ce3d4 --- /dev/null +++ b/libpq/postgresql/common/fe_memutils.h @@ -0,0 +1,44 @@ +/* + * fe_memutils.h + * memory management support for frontend code + * + * Copyright (c) 2003-2016, PostgreSQL Global Development Group + * + * src/include/common/fe_memutils.h + */ +#ifndef FE_MEMUTILS_H +#define FE_MEMUTILS_H + +/* + * Flags for pg_malloc_extended and palloc_extended, deliberately named + * the same as the backend flags. + */ +#define MCXT_ALLOC_HUGE 0x01 /* allow huge allocation (> 1 GB) not + * actually used for frontends */ +#define MCXT_ALLOC_NO_OOM 0x02 /* no failure if out-of-memory */ +#define MCXT_ALLOC_ZERO 0x04 /* zero allocated memory */ + +/* + * "Safe" memory allocation functions --- these exit(1) on failure + * (except pg_malloc_extended with MCXT_ALLOC_NO_OOM) + */ +extern char *pg_strdup(const char *in); +extern void *pg_malloc(size_t size); +extern void *pg_malloc0(size_t size); +extern void *pg_malloc_extended(size_t size, int flags); +extern void *pg_realloc(void *pointer, size_t size); +extern void pg_free(void *pointer); + +/* Equivalent functions, deliberately named the same as backend functions */ +extern char *pstrdup(const char *in); +extern void *palloc(Size size); +extern void *palloc0(Size size); +extern void *palloc_extended(Size size, int flags); +extern void *repalloc(void *pointer, Size size); +extern void pfree(void *pointer); + +/* sprintf into a palloc'd buffer --- these are in psprintf.c */ +extern char *psprintf(const char *fmt,...) pg_attribute_printf(1, 2); +extern size_t pvsnprintf(char *buf, size_t len, const char *fmt, va_list args) pg_attribute_printf(3, 0); + +#endif /* FE_MEMUTILS_H */ diff --git a/libpq/postgresql/getaddrinfo.h b/libpq/postgresql/getaddrinfo.h new file mode 100644 index 0000000..46aad59 --- /dev/null +++ b/libpq/postgresql/getaddrinfo.h @@ -0,0 +1,164 @@ +/*------------------------------------------------------------------------- + * + * getaddrinfo.h + * Support getaddrinfo() on platforms that don't have it. + * + * Note: we use our own routines on platforms that don't HAVE_STRUCT_ADDRINFO, + * whether or not the library routine getaddrinfo() can be found. This + * policy is needed because on some platforms a manually installed libbind.a + * may provide getaddrinfo(), yet the system headers may not provide the + * struct definitions needed to call it. To avoid conflict with the libbind + * definition in such cases, we rename our routines to pg_xxx() via macros. + * + * This code will also work on platforms where struct addrinfo is defined + * in the system headers but no getaddrinfo() can be located. + * + * Copyright (c) 2003-2016, PostgreSQL Global Development Group + * + * src/include/getaddrinfo.h + * + *------------------------------------------------------------------------- + */ +#ifndef GETADDRINFO_H +#define GETADDRINFO_H + +#include +#include + + +/* Various macros that ought to be in , but might not be */ + +#ifndef EAI_FAIL +#ifndef WIN32 +#define EAI_BADFLAGS (-1) +#define EAI_NONAME (-2) +#define EAI_AGAIN (-3) +#define EAI_FAIL (-4) +#define EAI_FAMILY (-6) +#define EAI_SOCKTYPE (-7) +#define EAI_SERVICE (-8) +#define EAI_MEMORY (-10) +#define EAI_SYSTEM (-11) +#else /* WIN32 */ +#ifdef WIN32_ONLY_COMPILER +#ifndef WSA_NOT_ENOUGH_MEMORY +#define WSA_NOT_ENOUGH_MEMORY (WSAENOBUFS) +#endif +#ifndef __BORLANDC__ +#define WSATYPE_NOT_FOUND (WSABASEERR+109) +#endif +#endif +#define EAI_AGAIN WSATRY_AGAIN +#define EAI_BADFLAGS WSAEINVAL +#define EAI_FAIL WSANO_RECOVERY +#define EAI_FAMILY WSAEAFNOSUPPORT +#define EAI_MEMORY WSA_NOT_ENOUGH_MEMORY +#define EAI_NODATA WSANO_DATA +#define EAI_NONAME WSAHOST_NOT_FOUND +#define EAI_SERVICE WSATYPE_NOT_FOUND +#define EAI_SOCKTYPE WSAESOCKTNOSUPPORT +#endif /* !WIN32 */ +#endif /* !EAI_FAIL */ + +#ifndef AI_PASSIVE +#define AI_PASSIVE 0x0001 +#endif + +#ifndef AI_NUMERICHOST +/* + * some platforms don't support AI_NUMERICHOST; define as zero if using + * the system version of getaddrinfo... + */ +#if defined(HAVE_STRUCT_ADDRINFO) && defined(HAVE_GETADDRINFO) +#define AI_NUMERICHOST 0 +#else +#define AI_NUMERICHOST 0x0004 +#endif +#endif + +#ifndef NI_NUMERICHOST +#define NI_NUMERICHOST 1 +#endif +#ifndef NI_NUMERICSERV +#define NI_NUMERICSERV 2 +#endif +#ifndef NI_NAMEREQD +#define NI_NAMEREQD 4 +#endif + +#ifndef NI_MAXHOST +#define NI_MAXHOST 1025 +#endif +#ifndef NI_MAXSERV +#define NI_MAXSERV 32 +#endif + + +#ifndef HAVE_STRUCT_ADDRINFO + +#ifndef WIN32 +struct addrinfo +{ + int ai_flags; + int ai_family; + int ai_socktype; + int ai_protocol; + size_t ai_addrlen; + struct sockaddr *ai_addr; + char *ai_canonname; + struct addrinfo *ai_next; +}; +#else +/* + * The order of the structure elements on Win32 doesn't match the + * order specified in the standard, but we have to match it for + * IPv6 to work. + */ +struct addrinfo +{ + int ai_flags; + int ai_family; + int ai_socktype; + int ai_protocol; + size_t ai_addrlen; + char *ai_canonname; + struct sockaddr *ai_addr; + struct addrinfo *ai_next; +}; +#endif +#endif /* HAVE_STRUCT_ADDRINFO */ + + +#ifndef HAVE_GETADDRINFO + +/* Rename private copies per comments above */ +#ifdef getaddrinfo +#undef getaddrinfo +#endif +#define getaddrinfo pg_getaddrinfo + +#ifdef freeaddrinfo +#undef freeaddrinfo +#endif +#define freeaddrinfo pg_freeaddrinfo + +#ifdef gai_strerror +#undef gai_strerror +#endif +#define gai_strerror pg_gai_strerror + +#ifdef getnameinfo +#undef getnameinfo +#endif +#define getnameinfo pg_getnameinfo + +extern int getaddrinfo(const char *node, const char *service, + const struct addrinfo * hints, struct addrinfo ** res); +extern void freeaddrinfo(struct addrinfo * res); +extern const char *gai_strerror(int errcode); +extern int getnameinfo(const struct sockaddr * sa, int salen, + char *node, int nodelen, + char *service, int servicelen, int flags); +#endif /* HAVE_GETADDRINFO */ + +#endif /* GETADDRINFO_H */ diff --git a/libpq/postgresql/libpq/ip.h b/libpq/postgresql/libpq/ip.h new file mode 100644 index 0000000..ce9bc6e --- /dev/null +++ b/libpq/postgresql/libpq/ip.h @@ -0,0 +1,51 @@ +/*------------------------------------------------------------------------- + * + * ip.h + * Definitions for IPv6-aware network access. + * + * These definitions are used by both frontend and backend code. Be careful + * what you include here! + * + * Copyright (c) 2003-2016, PostgreSQL Global Development Group + * + * src/include/libpq/ip.h + * + *------------------------------------------------------------------------- + */ +#ifndef IP_H +#define IP_H + +#include "getaddrinfo.h" /* pgrminclude ignore */ +#include "libpq/pqcomm.h" /* pgrminclude ignore */ + + +#ifdef HAVE_UNIX_SOCKETS +#define IS_AF_UNIX(fam) ((fam) == AF_UNIX) +#else +#define IS_AF_UNIX(fam) (0) +#endif + +typedef void (*PgIfAddrCallback) (struct sockaddr * addr, + struct sockaddr * netmask, + void *cb_data); + +extern int pg_getaddrinfo_all(const char *hostname, const char *servname, + const struct addrinfo * hintp, + struct addrinfo ** result); +extern void pg_freeaddrinfo_all(int hint_ai_family, struct addrinfo * ai); + +extern int pg_getnameinfo_all(const struct sockaddr_storage * addr, int salen, + char *node, int nodelen, + char *service, int servicelen, + int flags); + +extern int pg_range_sockaddr(const struct sockaddr_storage * addr, + const struct sockaddr_storage * netaddr, + const struct sockaddr_storage * netmask); + +extern int pg_sockaddr_cidr_mask(struct sockaddr_storage * mask, + char *numbits, int family); + +extern int pg_foreach_ifaddr(PgIfAddrCallback callback, void *cb_data); + +#endif /* IP_H */ diff --git a/libpq/postgresql/libpq/libpq-fs.h b/libpq/postgresql/libpq/libpq-fs.h new file mode 100644 index 0000000..5134ce6 --- /dev/null +++ b/libpq/postgresql/libpq/libpq-fs.h @@ -0,0 +1,24 @@ +/*------------------------------------------------------------------------- + * + * libpq-fs.h + * definitions for using Inversion file system routines (ie, large objects) + * + * + * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/libpq/libpq-fs.h + * + *------------------------------------------------------------------------- + */ +#ifndef LIBPQ_FS_H +#define LIBPQ_FS_H + +/* + * Read/write mode flags for inversion (large object) calls + */ + +#define INV_WRITE 0x00020000 +#define INV_READ 0x00040000 + +#endif /* LIBPQ_FS_H */ diff --git a/libpq/postgresql/libpq/md5.h b/libpq/postgresql/libpq/md5.h new file mode 100644 index 0000000..f3eec8b --- /dev/null +++ b/libpq/postgresql/libpq/md5.h @@ -0,0 +1,30 @@ +/*------------------------------------------------------------------------- + * + * md5.h + * Interface to libpq/md5.c + * + * These definitions are needed by both frontend and backend code to work + * with MD5-encrypted passwords. + * + * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/libpq/md5.h + * + *------------------------------------------------------------------------- + */ +#ifndef PG_MD5_H +#define PG_MD5_H + +#define MD5_PASSWD_LEN 35 + +#define isMD5(passwd) (strncmp(passwd, "md5", 3) == 0 && \ + strlen(passwd) == MD5_PASSWD_LEN) + + +extern bool pg_md5_hash(const void *buff, size_t len, char *hexsum); +extern bool pg_md5_binary(const void *buff, size_t len, void *outbuf); +extern bool pg_md5_encrypt(const char *passwd, const char *salt, + size_t salt_len, char *buf); + +#endif diff --git a/libpq/postgresql/libpq/pqcomm.h b/libpq/postgresql/libpq/pqcomm.h new file mode 100644 index 0000000..1d063d1 --- /dev/null +++ b/libpq/postgresql/libpq/pqcomm.h @@ -0,0 +1,206 @@ +/*------------------------------------------------------------------------- + * + * pqcomm.h + * Definitions common to frontends and backends. + * + * NOTE: for historical reasons, this does not correspond to pqcomm.c. + * pqcomm.c's routines are declared in libpq.h. + * + * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/libpq/pqcomm.h + * + *------------------------------------------------------------------------- + */ +#ifndef PQCOMM_H +#define PQCOMM_H + +#include +#include +#ifdef HAVE_SYS_UN_H +#include +#endif +#include + +#ifdef HAVE_STRUCT_SOCKADDR_STORAGE + +#ifndef HAVE_STRUCT_SOCKADDR_STORAGE_SS_FAMILY +#ifdef HAVE_STRUCT_SOCKADDR_STORAGE___SS_FAMILY +#define ss_family __ss_family +#else +#error struct sockaddr_storage does not provide an ss_family member +#endif +#endif + +#ifdef HAVE_STRUCT_SOCKADDR_STORAGE___SS_LEN +#define ss_len __ss_len +#define HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN 1 +#endif +#else /* !HAVE_STRUCT_SOCKADDR_STORAGE */ + +/* Define a struct sockaddr_storage if we don't have one. */ + +struct sockaddr_storage +{ + union + { + struct sockaddr sa; /* get the system-dependent fields */ + int64 ss_align; /* ensures struct is properly aligned */ + char ss_pad[128]; /* ensures struct has desired size */ + } ss_stuff; +}; + +#define ss_family ss_stuff.sa.sa_family +/* It should have an ss_len field if sockaddr has sa_len. */ +#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN +#define ss_len ss_stuff.sa.sa_len +#define HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN 1 +#endif +#endif /* HAVE_STRUCT_SOCKADDR_STORAGE */ + +typedef struct +{ + struct sockaddr_storage addr; + ACCEPT_TYPE_ARG3 salen; +} SockAddr; + +/* Configure the UNIX socket location for the well known port. */ + +#define UNIXSOCK_PATH(path, port, sockdir) \ + snprintf(path, sizeof(path), "%s/.s.PGSQL.%d", \ + ((sockdir) && *(sockdir) != '\0') ? (sockdir) : \ + DEFAULT_PGSOCKET_DIR, \ + (port)) + +/* + * The maximum workable length of a socket path is what will fit into + * struct sockaddr_un. This is usually only 100 or so bytes :-(. + * + * For consistency, always pass a MAXPGPATH-sized buffer to UNIXSOCK_PATH(), + * then complain if the resulting string is >= UNIXSOCK_PATH_BUFLEN bytes. + * (Because the standard API for getaddrinfo doesn't allow it to complain in + * a useful way when the socket pathname is too long, we have to test for + * this explicitly, instead of just letting the subroutine return an error.) + */ +#define UNIXSOCK_PATH_BUFLEN sizeof(((struct sockaddr_un *) NULL)->sun_path) + + +/* + * These manipulate the frontend/backend protocol version number. + * + * The major number should be incremented for incompatible changes. The minor + * number should be incremented for compatible changes (eg. additional + * functionality). + * + * If a backend supports version m.n of the protocol it must actually support + * versions m.[0..n]. Backend support for version m-1 can be dropped after a + * `reasonable' length of time. + * + * A frontend isn't required to support anything other than the current + * version. + */ + +#define PG_PROTOCOL_MAJOR(v) ((v) >> 16) +#define PG_PROTOCOL_MINOR(v) ((v) & 0x0000ffff) +#define PG_PROTOCOL(m,n) (((m) << 16) | (n)) + +/* The earliest and latest frontend/backend protocol version supported. */ + +#define PG_PROTOCOL_EARLIEST PG_PROTOCOL(1,0) +#define PG_PROTOCOL_LATEST PG_PROTOCOL(3,0) + +typedef uint32 ProtocolVersion; /* FE/BE protocol version number */ + +typedef ProtocolVersion MsgType; + + +/* + * Packet lengths are 4 bytes in network byte order. + * + * The initial length is omitted from the packet layouts appearing below. + */ + +typedef uint32 PacketLen; + + +/* + * Old-style startup packet layout with fixed-width fields. This is used in + * protocol 1.0 and 2.0, but not in later versions. Note that the fields + * in this layout are '\0' terminated only if there is room. + */ + +#define SM_DATABASE 64 +#define SM_USER 32 +/* We append database name if db_user_namespace true. */ +#define SM_DATABASE_USER (SM_DATABASE+SM_USER+1) /* +1 for @ */ +#define SM_OPTIONS 64 +#define SM_UNUSED 64 +#define SM_TTY 64 + +typedef struct StartupPacket +{ + ProtocolVersion protoVersion; /* Protocol version */ + char database[SM_DATABASE]; /* Database name */ + /* Db_user_namespace appends dbname */ + char user[SM_USER]; /* User name */ + char options[SM_OPTIONS]; /* Optional additional args */ + char unused[SM_UNUSED]; /* Unused */ + char tty[SM_TTY]; /* Tty for debug output */ +} StartupPacket; + +extern bool Db_user_namespace; + +/* + * In protocol 3.0 and later, the startup packet length is not fixed, but + * we set an arbitrary limit on it anyway. This is just to prevent simple + * denial-of-service attacks via sending enough data to run the server + * out of memory. + */ +#define MAX_STARTUP_PACKET_LENGTH 10000 + + +/* These are the authentication request codes sent by the backend. */ + +#define AUTH_REQ_OK 0 /* User is authenticated */ +#define AUTH_REQ_KRB4 1 /* Kerberos V4. Not supported any more. */ +#define AUTH_REQ_KRB5 2 /* Kerberos V5. Not supported any more. */ +#define AUTH_REQ_PASSWORD 3 /* Password */ +#define AUTH_REQ_CRYPT 4 /* crypt password. Not supported any more. */ +#define AUTH_REQ_MD5 5 /* md5 password */ +#define AUTH_REQ_SCM_CREDS 6 /* transfer SCM credentials */ +#define AUTH_REQ_GSS 7 /* GSSAPI without wrap() */ +#define AUTH_REQ_GSS_CONT 8 /* Continue GSS exchanges */ +#define AUTH_REQ_SSPI 9 /* SSPI negotiate without wrap() */ +#define AUTH_REQ_SASL 10 /* SASL authentication. Not supported before + * libpq version 10. */ + +typedef uint32 AuthRequest; + + +/* + * A client can also send a cancel-current-operation request to the postmaster. + * This is uglier than sending it directly to the client's backend, but it + * avoids depending on out-of-band communication facilities. + * + * The cancel request code must not match any protocol version number + * we're ever likely to use. This random choice should do. + */ +#define CANCEL_REQUEST_CODE PG_PROTOCOL(1234,5678) + +typedef struct CancelRequestPacket +{ + /* Note that each field is stored in network byte order! */ + MsgType cancelRequestCode; /* code to identify a cancel request */ + uint32 backendPID; /* PID of client's backend */ + uint32 cancelAuthCode; /* secret key to authorize cancel */ +} CancelRequestPacket; + + +/* + * A client can also start by sending a SSL negotiation request, to get a + * secure channel. + */ +#define NEGOTIATE_SSL_CODE PG_PROTOCOL(1234,5679) + +#endif /* PQCOMM_H */ diff --git a/libpq/postgresql/mb/pg_wchar.h b/libpq/postgresql/mb/pg_wchar.h new file mode 100644 index 0000000..24e8d0d --- /dev/null +++ b/libpq/postgresql/mb/pg_wchar.h @@ -0,0 +1,561 @@ +/*------------------------------------------------------------------------- + * + * pg_wchar.h + * multibyte-character support + * + * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/mb/pg_wchar.h + * + * NOTES + * This is used both by the backend and by libpq, but should not be + * included by libpq client programs. In particular, a libpq client + * should not assume that the encoding IDs used by the version of libpq + * it's linked to match up with the IDs declared here. + * + *------------------------------------------------------------------------- + */ +#ifndef PG_WCHAR_H +#define PG_WCHAR_H + +/* + * The pg_wchar type + */ +typedef unsigned int pg_wchar; + +/* + * Maximum byte length of multibyte characters in any backend encoding + */ +#define MAX_MULTIBYTE_CHAR_LEN 4 + +/* + * various definitions for EUC + */ +#define SS2 0x8e /* single shift 2 (JIS0201) */ +#define SS3 0x8f /* single shift 3 (JIS0212) */ + +/* + * SJIS validation macros + */ +#define ISSJISHEAD(c) (((c) >= 0x81 && (c) <= 0x9f) || ((c) >= 0xe0 && (c) <= 0xfc)) +#define ISSJISTAIL(c) (((c) >= 0x40 && (c) <= 0x7e) || ((c) >= 0x80 && (c) <= 0xfc)) + +/*---------------------------------------------------- + * MULE Internal Encoding (MIC) + * + * This encoding follows the design used within XEmacs; it is meant to + * subsume many externally-defined character sets. Each character includes + * identification of the character set it belongs to, so the encoding is + * general but somewhat bulky. + * + * Currently PostgreSQL supports 5 types of MULE character sets: + * + * 1) 1-byte ASCII characters. Each byte is below 0x80. + * + * 2) "Official" single byte charsets such as ISO-8859-1 (Latin1). + * Each MULE character consists of 2 bytes: LC1 + C1, where LC1 is + * an identifier for the charset (in the range 0x81 to 0x8d) and C1 + * is the character code (in the range 0xa0 to 0xff). + * + * 3) "Private" single byte charsets such as SISHENG. Each MULE + * character consists of 3 bytes: LCPRV1 + LC12 + C1, where LCPRV1 + * is a private-charset flag, LC12 is an identifier for the charset, + * and C1 is the character code (in the range 0xa0 to 0xff). + * LCPRV1 is either 0x9a (if LC12 is in the range 0xa0 to 0xdf) + * or 0x9b (if LC12 is in the range 0xe0 to 0xef). + * + * 4) "Official" multibyte charsets such as JIS X0208. Each MULE + * character consists of 3 bytes: LC2 + C1 + C2, where LC2 is + * an identifier for the charset (in the range 0x90 to 0x99) and C1 + * and C2 form the character code (each in the range 0xa0 to 0xff). + * + * 5) "Private" multibyte charsets such as CNS 11643-1992 Plane 3. + * Each MULE character consists of 4 bytes: LCPRV2 + LC22 + C1 + C2, + * where LCPRV2 is a private-charset flag, LC22 is an identifier for + * the charset, and C1 and C2 form the character code (each in the range + * 0xa0 to 0xff). LCPRV2 is either 0x9c (if LC22 is in the range 0xf0 + * to 0xf4) or 0x9d (if LC22 is in the range 0xf5 to 0xfe). + * + * "Official" encodings are those that have been assigned code numbers by + * the XEmacs project; "private" encodings have Postgres-specific charset + * identifiers. + * + * See the "XEmacs Internals Manual", available at http://www.xemacs.org, + * for more details. Note that for historical reasons, Postgres' + * private-charset flag values do not match what XEmacs says they should be, + * so this isn't really exactly MULE (not that private charsets would be + * interoperable anyway). + * + * Note that XEmacs's implementation is different from what emacs does. + * We follow emacs's implementation, rather than XEmacs's. + *---------------------------------------------------- + */ + +/* + * Charset identifiers (also called "leading bytes" in the MULE documentation) + */ + +/* + * Charset IDs for official single byte encodings (0x81-0x8e) + */ +#define LC_ISO8859_1 0x81 /* ISO8859 Latin 1 */ +#define LC_ISO8859_2 0x82 /* ISO8859 Latin 2 */ +#define LC_ISO8859_3 0x83 /* ISO8859 Latin 3 */ +#define LC_ISO8859_4 0x84 /* ISO8859 Latin 4 */ +#define LC_TIS620 0x85 /* Thai (not supported yet) */ +#define LC_ISO8859_7 0x86 /* Greek (not supported yet) */ +#define LC_ISO8859_6 0x87 /* Arabic (not supported yet) */ +#define LC_ISO8859_8 0x88 /* Hebrew (not supported yet) */ +#define LC_JISX0201K 0x89 /* Japanese 1 byte kana */ +#define LC_JISX0201R 0x8a /* Japanese 1 byte Roman */ +/* Note that 0x8b seems to be unused as of Emacs 20.7. + * However, there might be a chance that 0x8b could be used + * in later versions of Emacs. + */ +#define LC_KOI8_R 0x8b /* Cyrillic KOI8-R */ +#define LC_ISO8859_5 0x8c /* ISO8859 Cyrillic */ +#define LC_ISO8859_9 0x8d /* ISO8859 Latin 5 (not supported yet) */ +#define LC_ISO8859_15 0x8e /* ISO8859 Latin 15 (not supported yet) */ +/* #define CONTROL_1 0x8f control characters (unused) */ + +/* Is a leading byte for "official" single byte encodings? */ +#define IS_LC1(c) ((unsigned char)(c) >= 0x81 && (unsigned char)(c) <= 0x8d) + +/* + * Charset IDs for official multibyte encodings (0x90-0x99) + * 0x9a-0x9d are free. 0x9e and 0x9f are reserved. + */ +#define LC_JISX0208_1978 0x90 /* Japanese Kanji, old JIS (not supported) */ +#define LC_GB2312_80 0x91 /* Chinese */ +#define LC_JISX0208 0x92 /* Japanese Kanji (JIS X 0208) */ +#define LC_KS5601 0x93 /* Korean */ +#define LC_JISX0212 0x94 /* Japanese Kanji (JIS X 0212) */ +#define LC_CNS11643_1 0x95 /* CNS 11643-1992 Plane 1 */ +#define LC_CNS11643_2 0x96 /* CNS 11643-1992 Plane 2 */ +#define LC_JISX0213_1 0x97/* Japanese Kanji (JIS X 0213 Plane 1) (not + * supported) */ +#define LC_BIG5_1 0x98 /* Plane 1 Chinese traditional (not supported) */ +#define LC_BIG5_2 0x99 /* Plane 1 Chinese traditional (not supported) */ + +/* Is a leading byte for "official" multibyte encodings? */ +#define IS_LC2(c) ((unsigned char)(c) >= 0x90 && (unsigned char)(c) <= 0x99) + +/* + * Postgres-specific prefix bytes for "private" single byte encodings + * (According to the MULE docs, we should be using 0x9e for this) + */ +#define LCPRV1_A 0x9a +#define LCPRV1_B 0x9b +#define IS_LCPRV1(c) ((unsigned char)(c) == LCPRV1_A || (unsigned char)(c) == LCPRV1_B) +#define IS_LCPRV1_A_RANGE(c) \ + ((unsigned char)(c) >= 0xa0 && (unsigned char)(c) <= 0xdf) +#define IS_LCPRV1_B_RANGE(c) \ + ((unsigned char)(c) >= 0xe0 && (unsigned char)(c) <= 0xef) + +/* + * Postgres-specific prefix bytes for "private" multibyte encodings + * (According to the MULE docs, we should be using 0x9f for this) + */ +#define LCPRV2_A 0x9c +#define LCPRV2_B 0x9d +#define IS_LCPRV2(c) ((unsigned char)(c) == LCPRV2_A || (unsigned char)(c) == LCPRV2_B) +#define IS_LCPRV2_A_RANGE(c) \ + ((unsigned char)(c) >= 0xf0 && (unsigned char)(c) <= 0xf4) +#define IS_LCPRV2_B_RANGE(c) \ + ((unsigned char)(c) >= 0xf5 && (unsigned char)(c) <= 0xfe) + +/* + * Charset IDs for private single byte encodings (0xa0-0xef) + */ +#define LC_SISHENG 0xa0/* Chinese SiSheng characters for + * PinYin/ZhuYin (not supported) */ +#define LC_IPA 0xa1/* IPA (International Phonetic Association) + * (not supported) */ +#define LC_VISCII_LOWER 0xa2/* Vietnamese VISCII1.1 lower-case (not + * supported) */ +#define LC_VISCII_UPPER 0xa3/* Vietnamese VISCII1.1 upper-case (not + * supported) */ +#define LC_ARABIC_DIGIT 0xa4 /* Arabic digit (not supported) */ +#define LC_ARABIC_1_COLUMN 0xa5 /* Arabic 1-column (not supported) */ +#define LC_ASCII_RIGHT_TO_LEFT 0xa6 /* ASCII (left half of ISO8859-1) with + * right-to-left direction (not + * supported) */ +#define LC_LAO 0xa7/* Lao characters (ISO10646 0E80..0EDF) (not + * supported) */ +#define LC_ARABIC_2_COLUMN 0xa8 /* Arabic 1-column (not supported) */ + +/* + * Charset IDs for private multibyte encodings (0xf0-0xff) + */ +#define LC_INDIAN_1_COLUMN 0xf0/* Indian charset for 1-column width glyphs + * (not supported) */ +#define LC_TIBETAN_1_COLUMN 0xf1/* Tibetan 1-column width glyphs (not + * supported) */ +#define LC_UNICODE_SUBSET_2 0xf2/* Unicode characters of the range + * U+2500..U+33FF. (not supported) */ +#define LC_UNICODE_SUBSET_3 0xf3/* Unicode characters of the range + * U+E000..U+FFFF. (not supported) */ +#define LC_UNICODE_SUBSET 0xf4/* Unicode characters of the range + * U+0100..U+24FF. (not supported) */ +#define LC_ETHIOPIC 0xf5 /* Ethiopic characters (not supported) */ +#define LC_CNS11643_3 0xf6 /* CNS 11643-1992 Plane 3 */ +#define LC_CNS11643_4 0xf7 /* CNS 11643-1992 Plane 4 */ +#define LC_CNS11643_5 0xf8 /* CNS 11643-1992 Plane 5 */ +#define LC_CNS11643_6 0xf9 /* CNS 11643-1992 Plane 6 */ +#define LC_CNS11643_7 0xfa /* CNS 11643-1992 Plane 7 */ +#define LC_INDIAN_2_COLUMN 0xfb/* Indian charset for 2-column width glyphs + * (not supported) */ +#define LC_TIBETAN 0xfc /* Tibetan (not supported) */ +/* #define FREE 0xfd free (unused) */ +/* #define FREE 0xfe free (unused) */ +/* #define FREE 0xff free (unused) */ + +/*---------------------------------------------------- + * end of MULE stuff + *---------------------------------------------------- + */ + +/* + * PostgreSQL encoding identifiers + * + * WARNING: the order of this enum must be same as order of entries + * in the pg_enc2name_tbl[] array (in mb/encnames.c), and + * in the pg_wchar_table[] array (in mb/wchar.c)! + * + * If you add some encoding don't forget to check + * PG_ENCODING_BE_LAST macro. + * + * PG_SQL_ASCII is default encoding and must be = 0. + * + * XXX We must avoid renumbering any backend encoding until libpq's major + * version number is increased beyond 5; it turns out that the backend + * encoding IDs are effectively part of libpq's ABI as far as 8.2 initdb and + * psql are concerned. + */ +typedef enum pg_enc +{ + PG_SQL_ASCII = 0, /* SQL/ASCII */ + PG_EUC_JP, /* EUC for Japanese */ + PG_EUC_CN, /* EUC for Chinese */ + PG_EUC_KR, /* EUC for Korean */ + PG_EUC_TW, /* EUC for Taiwan */ + PG_EUC_JIS_2004, /* EUC-JIS-2004 */ + PG_UTF8, /* Unicode UTF8 */ + PG_MULE_INTERNAL, /* Mule internal code */ + PG_LATIN1, /* ISO-8859-1 Latin 1 */ + PG_LATIN2, /* ISO-8859-2 Latin 2 */ + PG_LATIN3, /* ISO-8859-3 Latin 3 */ + PG_LATIN4, /* ISO-8859-4 Latin 4 */ + PG_LATIN5, /* ISO-8859-9 Latin 5 */ + PG_LATIN6, /* ISO-8859-10 Latin6 */ + PG_LATIN7, /* ISO-8859-13 Latin7 */ + PG_LATIN8, /* ISO-8859-14 Latin8 */ + PG_LATIN9, /* ISO-8859-15 Latin9 */ + PG_LATIN10, /* ISO-8859-16 Latin10 */ + PG_WIN1256, /* windows-1256 */ + PG_WIN1258, /* Windows-1258 */ + PG_WIN866, /* (MS-DOS CP866) */ + PG_WIN874, /* windows-874 */ + PG_KOI8R, /* KOI8-R */ + PG_WIN1251, /* windows-1251 */ + PG_WIN1252, /* windows-1252 */ + PG_ISO_8859_5, /* ISO-8859-5 */ + PG_ISO_8859_6, /* ISO-8859-6 */ + PG_ISO_8859_7, /* ISO-8859-7 */ + PG_ISO_8859_8, /* ISO-8859-8 */ + PG_WIN1250, /* windows-1250 */ + PG_WIN1253, /* windows-1253 */ + PG_WIN1254, /* windows-1254 */ + PG_WIN1255, /* windows-1255 */ + PG_WIN1257, /* windows-1257 */ + PG_KOI8U, /* KOI8-U */ + /* PG_ENCODING_BE_LAST points to the above entry */ + + /* followings are for client encoding only */ + PG_SJIS, /* Shift JIS (Windows-932) */ + PG_BIG5, /* Big5 (Windows-950) */ + PG_GBK, /* GBK (Windows-936) */ + PG_UHC, /* UHC (Windows-949) */ + PG_GB18030, /* GB18030 */ + PG_JOHAB, /* EUC for Korean JOHAB */ + PG_SHIFT_JIS_2004, /* Shift-JIS-2004 */ + _PG_LAST_ENCODING_ /* mark only */ + +} pg_enc; + +#define PG_ENCODING_BE_LAST PG_KOI8U + +/* + * Please use these tests before access to pg_encconv_tbl[] + * or to other places... + */ +#define PG_VALID_BE_ENCODING(_enc) \ + ((_enc) >= 0 && (_enc) <= PG_ENCODING_BE_LAST) + +#define PG_ENCODING_IS_CLIENT_ONLY(_enc) \ + ((_enc) > PG_ENCODING_BE_LAST && (_enc) < _PG_LAST_ENCODING_) + +#define PG_VALID_ENCODING(_enc) \ + ((_enc) >= 0 && (_enc) < _PG_LAST_ENCODING_) + +/* On FE are possible all encodings */ +#define PG_VALID_FE_ENCODING(_enc) PG_VALID_ENCODING(_enc) + +/* + * Table for mapping an encoding number to official encoding name and + * possibly other subsidiary data. Be careful to check encoding number + * before accessing a table entry! + * + * if (PG_VALID_ENCODING(encoding)) + * pg_enc2name_tbl[ encoding ]; + */ +typedef struct pg_enc2name +{ + const char *name; + pg_enc encoding; +#ifdef WIN32 + unsigned codepage; /* codepage for WIN32 */ +#endif +} pg_enc2name; + +extern const pg_enc2name pg_enc2name_tbl[]; + +/* + * Encoding names for gettext + */ +typedef struct pg_enc2gettext +{ + pg_enc encoding; + const char *name; +} pg_enc2gettext; + +extern const pg_enc2gettext pg_enc2gettext_tbl[]; + +/* + * pg_wchar stuff + */ +typedef int (*mb2wchar_with_len_converter) (const unsigned char *from, + pg_wchar *to, + int len); + +typedef int (*wchar2mb_with_len_converter) (const pg_wchar *from, + unsigned char *to, + int len); + +typedef int (*mblen_converter) (const unsigned char *mbstr); + +typedef int (*mbdisplaylen_converter) (const unsigned char *mbstr); + +typedef bool (*mbcharacter_incrementer) (unsigned char *mbstr, int len); + +typedef int (*mbverifier) (const unsigned char *mbstr, int len); + +typedef struct +{ + mb2wchar_with_len_converter mb2wchar_with_len; /* convert a multibyte + * string to a wchar */ + wchar2mb_with_len_converter wchar2mb_with_len; /* convert a wchar + * string to a multibyte */ + mblen_converter mblen; /* get byte length of a char */ + mbdisplaylen_converter dsplen; /* get display width of a char */ + mbverifier mbverify; /* verify multibyte sequence */ + int maxmblen; /* max bytes for a char in this encoding */ +} pg_wchar_tbl; + +extern const pg_wchar_tbl pg_wchar_table[]; + +/* + * Data structures for conversions between UTF-8 and other encodings + * (UtfToLocal() and LocalToUtf()). In these data structures, characters of + * either encoding are represented by uint32 words; hence we can only support + * characters up to 4 bytes long. For example, the byte sequence 0xC2 0x89 + * would be represented by 0x0000C289, and 0xE8 0xA2 0xB4 by 0x00E8A2B4. + * + * Maps are arrays of these structs, which must be in order by the lookup key + * (so that bsearch() can be used). + * + * UTF-8 to local code conversion map + */ +typedef struct +{ + uint32 utf; /* UTF-8 */ + uint32 code; /* local code */ +} pg_utf_to_local; + +/* + * local code to UTF-8 conversion map + */ +typedef struct +{ + uint32 code; /* local code */ + uint32 utf; /* UTF-8 */ +} pg_local_to_utf; + +/* + * UTF-8 to local code conversion map (for combined characters) + */ +typedef struct +{ + uint32 utf1; /* UTF-8 code 1 */ + uint32 utf2; /* UTF-8 code 2 */ + uint32 code; /* local code */ +} pg_utf_to_local_combined; + +/* + * local code to UTF-8 conversion map (for combined characters) + */ +typedef struct +{ + uint32 code; /* local code */ + uint32 utf1; /* UTF-8 code 1 */ + uint32 utf2; /* UTF-8 code 2 */ +} pg_local_to_utf_combined; + +/* + * callback function for algorithmic encoding conversions (in either direction) + * + * if function returns zero, it does not know how to convert the code + */ +typedef uint32 (*utf_local_conversion_func) (uint32 code); + +/* + * Support macro for encoding conversion functions to validate their + * arguments. (This could be made more compact if we included fmgr.h + * here, but we don't want to do that because this header file is also + * used by frontends.) + */ +#define CHECK_ENCODING_CONVERSION_ARGS(srcencoding,destencoding) \ + check_encoding_conversion_args(PG_GETARG_INT32(0), \ + PG_GETARG_INT32(1), \ + PG_GETARG_INT32(4), \ + (srcencoding), \ + (destencoding)) + + +/* + * These functions are considered part of libpq's exported API and + * are also declared in libpq-fe.h. + */ +extern int pg_char_to_encoding(const char *name); +extern const char *pg_encoding_to_char(int encoding); +extern int pg_valid_server_encoding_id(int encoding); + +/* + * Remaining functions are not considered part of libpq's API, though many + * of them do exist inside libpq. + */ +extern int pg_mb2wchar(const char *from, pg_wchar *to); +extern int pg_mb2wchar_with_len(const char *from, pg_wchar *to, int len); +extern int pg_encoding_mb2wchar_with_len(int encoding, + const char *from, pg_wchar *to, int len); +extern int pg_wchar2mb(const pg_wchar *from, char *to); +extern int pg_wchar2mb_with_len(const pg_wchar *from, char *to, int len); +extern int pg_encoding_wchar2mb_with_len(int encoding, + const pg_wchar *from, char *to, int len); +extern int pg_char_and_wchar_strcmp(const char *s1, const pg_wchar *s2); +extern int pg_wchar_strncmp(const pg_wchar *s1, const pg_wchar *s2, size_t n); +extern int pg_char_and_wchar_strncmp(const char *s1, const pg_wchar *s2, size_t n); +extern size_t pg_wchar_strlen(const pg_wchar *wstr); +extern int pg_mblen(const char *mbstr); +extern int pg_dsplen(const char *mbstr); +extern int pg_encoding_mblen(int encoding, const char *mbstr); +extern int pg_encoding_dsplen(int encoding, const char *mbstr); +extern int pg_encoding_verifymb(int encoding, const char *mbstr, int len); +extern int pg_mule_mblen(const unsigned char *mbstr); +extern int pg_mic_mblen(const unsigned char *mbstr); +extern int pg_mbstrlen(const char *mbstr); +extern int pg_mbstrlen_with_len(const char *mbstr, int len); +extern int pg_mbcliplen(const char *mbstr, int len, int limit); +extern int pg_encoding_mbcliplen(int encoding, const char *mbstr, + int len, int limit); +extern int pg_mbcharcliplen(const char *mbstr, int len, int imit); +extern int pg_encoding_max_length(int encoding); +extern int pg_database_encoding_max_length(void); +extern mbcharacter_incrementer pg_database_encoding_character_incrementer(void); + +extern int PrepareClientEncoding(int encoding); +extern int SetClientEncoding(int encoding); +extern void InitializeClientEncoding(void); +extern int pg_get_client_encoding(void); +extern const char *pg_get_client_encoding_name(void); + +extern void SetDatabaseEncoding(int encoding); +extern int GetDatabaseEncoding(void); +extern const char *GetDatabaseEncodingName(void); +extern void SetMessageEncoding(int encoding); +extern int GetMessageEncoding(void); + +#ifdef ENABLE_NLS +extern int pg_bind_textdomain_codeset(const char *domainname); +#endif + +extern int pg_valid_client_encoding(const char *name); +extern int pg_valid_server_encoding(const char *name); + +extern unsigned char *unicode_to_utf8(pg_wchar c, unsigned char *utf8string); +extern pg_wchar utf8_to_unicode(const unsigned char *c); +extern int pg_utf_mblen(const unsigned char *); +extern unsigned char *pg_do_encoding_conversion(unsigned char *src, int len, + int src_encoding, + int dest_encoding); + +extern char *pg_client_to_server(const char *s, int len); +extern char *pg_server_to_client(const char *s, int len); +extern char *pg_any_to_server(const char *s, int len, int encoding); +extern char *pg_server_to_any(const char *s, int len, int encoding); + +extern unsigned short BIG5toCNS(unsigned short big5, unsigned char *lc); +extern unsigned short CNStoBIG5(unsigned short cns, unsigned char lc); + +extern void UtfToLocal(const unsigned char *utf, int len, + unsigned char *iso, + const pg_utf_to_local *map, int mapsize, + const pg_utf_to_local_combined *cmap, int cmapsize, + utf_local_conversion_func conv_func, + int encoding); +extern void LocalToUtf(const unsigned char *iso, int len, + unsigned char *utf, + const pg_local_to_utf *map, int mapsize, + const pg_local_to_utf_combined *cmap, int cmapsize, + utf_local_conversion_func conv_func, + int encoding); + +extern bool pg_verifymbstr(const char *mbstr, int len, bool noError); +extern bool pg_verify_mbstr(int encoding, const char *mbstr, int len, + bool noError); +extern int pg_verify_mbstr_len(int encoding, const char *mbstr, int len, + bool noError); + +extern void check_encoding_conversion_args(int src_encoding, + int dest_encoding, + int len, + int expected_src_encoding, + int expected_dest_encoding); + +extern void report_invalid_encoding(int encoding, const char *mbstr, int len) pg_attribute_noreturn(); +extern void report_untranslatable_char(int src_encoding, int dest_encoding, + const char *mbstr, int len) pg_attribute_noreturn(); + +extern void local2local(const unsigned char *l, unsigned char *p, int len, + int src_encoding, int dest_encoding, const unsigned char *tab); +extern void pg_ascii2mic(const unsigned char *l, unsigned char *p, int len); +extern void pg_mic2ascii(const unsigned char *mic, unsigned char *p, int len); +extern void latin2mic(const unsigned char *l, unsigned char *p, int len, + int lc, int encoding); +extern void mic2latin(const unsigned char *mic, unsigned char *p, int len, + int lc, int encoding); +extern void latin2mic_with_table(const unsigned char *l, unsigned char *p, + int len, int lc, int encoding, + const unsigned char *tab); +extern void mic2latin_with_table(const unsigned char *mic, unsigned char *p, + int len, int lc, int encoding, + const unsigned char *tab); + +extern bool pg_utf8_islegal(const unsigned char *source, int length); + +#ifdef WIN32 +extern WCHAR *pgwin32_message_to_UTF16(const char *str, int len, int *utf16len); +#endif + +#endif /* PG_WCHAR_H */ diff --git a/libpq/postgresql/pg_config.h b/libpq/postgresql/pg_config.h new file mode 100644 index 0000000..ee4408a --- /dev/null +++ b/libpq/postgresql/pg_config.h @@ -0,0 +1,280 @@ +/* file : libpq/postgresql/pg_config.h -*- C -*- + * copyright : Copyright (c) 2016-2017 Code Synthesis Ltd + * license : PostgreSQL License; see accompanying COPYRIGHT file + */ + +/* + * For the semanics of the following macros refer to + * libpq/postgresql/pg_config.h.in.orig and + * libpq/postgresql/pg_config.h.win32.orig files. + * + * Note that we will explicitly undefine macros that are present in the libpq + * source code but should not be defined. While this is not technically + * required, it simplifies the change tracking (see README-DEV). As a bonus we + * also make sure that they are not get eventually defined by some system + * headers. + */ + +#include /* offsetof() */ + +/* + * Version. + */ +#undef PG_MAJORVERSION +#undef PG_VERSION_NUM +#include + +/* + * Types, type sizes and alignments. + */ +#define ALIGNOF_(type) offsetof (struct {char c; type m;}, m) +#define ALIGNOF_DOUBLE ALIGNOF_ (double) +#define ALIGNOF_INT ALIGNOF_ (int) +#define ALIGNOF_LONG ALIGNOF_ (long) +#define ALIGNOF_SHORT ALIGNOF_ (short) + +/* + * GCC and Clang provide __SIZEOF_*__ and __*_TYPE__ predefined macros that we + * use to define the required libpq macros. Note that on Windows long and + * long long types are always of 32 and 64 bits width respectively. + */ +#ifndef _WIN32 +# if __SIZEOF_LONG__ == 8 +# define HAVE_LONG_INT_64 1 +# endif +# if __SIZEOF_LONG_LONG__ == 8 +# define HAVE_LONG_LONG_INT_64 1 +# endif +# if __SIZEOF_LONG_LONG__ > __SIZEOF_DOUBLE__ +# define MAXIMUM_ALIGNOF __SIZEOF_LONG_LONG__ +# else +# define MAXIMUM_ALIGNOF __SIZEOF_DOUBLE__ +# endif +# ifdef __SIZEOF_INT128__ +# define PG_INT128_TYPE __int128 +# endif +# define PG_INT64_TYPE __INT64_TYPE__ +# define ACCEPT_TYPE_ARG3 socklen_t +# define SIZEOF_SIZE_T __SIZEOF_SIZE_T__ +#else +# define HAVE_LONG_LONG_INT_64 1 +# define MAXIMUM_ALIGNOF 8 +# define PG_INT64_TYPE long long int +# define ACCEPT_TYPE_ARG3 int +# ifdef _WIN64 +# define SIZEOF_SIZE_T 8 +# else +# define SIZEOF_SIZE_T 4 +# endif +#endif + +#define INT64_MODIFIER "ll" + +/* + * Specific for FreeBSD. + */ +#ifdef __FreeBSD__ +# define HAVE_STRUCT_CMSGCRED 1 +#endif + +/* + * Specific for Mac OS. + */ +#ifdef __apple_build_version__ +# define HAVE_DECL_F_FULLFSYNC 1 +#else +# define HAVE_DECL_F_FULLFSYNC 0 +#endif + +/* + * Specific for FreeBSD and Mac OS. + */ +#if defined(__FreeBSD__) || defined(__apple_build_version__) +# define HAVE_DECL_STRLCAT 1 +# define HAVE_DECL_STRLCPY 1 +# define STRERROR_R_INT 1 +# define HAVE_FLS 1 +# define HAVE_GETPEEREID 1 +# define HAVE_STRTOQ 1 +# define HAVE_STRTOUQ 1 +# define HAVE_STRUCT_SOCKADDR_SA_LEN 1 +# define HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN 1 +# define HAVE_SYS_SOCKIO_H 1 +# define HAVE_SYS_UCRED_H 1 +# define HAVE_UNION_SEMUN 1 +#else +# define HAVE_DECL_STRLCAT 0 +# define HAVE_DECL_STRLCPY 0 +#endif + +/* + * Specific for POSIX. + */ +#ifndef _WIN32 +# define HAVE_CRYPT 1 +# define HAVE_DECL_FDATASYNC 1 +# define HAVE_FDATASYNC 1 +# define HAVE_GETADDRINFO 1 +# define HAVE_GETIFADDRS 1 +# define HAVE_IFADDRS_H 1 +# define HAVE_GETPWUID_R 1 +# define HAVE_INET_ATON 1 +# define HAVE_LANGINFO_H 1 +# define HAVE_MKDTEMP 1 +# define HAVE_NETINET_TCP_H 1 +# define HAVE_NET_IF_H 1 +# define HAVE_DECL_POSIX_FADVISE 1 +# define HAVE_POSIX_FADVISE 1 +# define HAVE_RANDOM 1 +# define HAVE_SRANDOM 1 +# define HAVE_STRERROR_R 1 +# define HAVE_STRINGS_H 1 +# define HAVE_SYS_IOCTL_H 1 +# define HAVE_POLL 1 +# define HAVE_POLL_H 1 +# define HAVE_SYS_POLL_H 1 +# define HAVE_SYS_SELECT_H 1 +# define HAVE_SYS_UN_H 1 +# define HAVE_TERMIOS_H 1 +# define HAVE_UNIX_SOCKETS 1 +# define HAVE_UNSETENV 1 +# define USE_INTEGER_DATETIMES 1 +/* + * Specific for Windows. + */ +#else +# define HAVE_DECL_FDATASYNC 0 +# define HAVE_DECL_POSIX_FADVISE 0 +# define HAVE_GETTIMEOFDAY 1 +# define HAVE_ISINF 1 +# define HAVE_FUNCNAME__FUNCTION 1 +# define USE_REPL_SNPRINTF 1 +#endif + +/* + * Specific for GNU C Library. + */ +#ifdef __GLIBC__ +# define HAVE_GETHOSTBYNAME_R 1 +#endif + +/* + * Specific for (non-) VC. + */ +#ifndef _MSC_VER +# define HAVE__BUILTIN_TYPES_COMPATIBLE_P 1 +# define HAVE__BUILTIN_UNREACHABLE 1 +#endif + +/* + * Common for all supported OSes/compilers. + */ +#define ENABLE_THREAD_SAFETY 1 +#define HAVE_MEMMOVE 1 +#define HAVE_RINT 1 +#define HAVE_DECL_SNPRINTF 1 +#define HAVE_DECL_VSNPRINTF 1 +#define HAVE_FSEEKO 1 +#define HAVE_FUNCNAME__FUNC 1 +#define HAVE_IPV6 1 +#define HAVE_STDINT_H 1 +#define HAVE_STRTOLL 1 +#define HAVE_STRTOULL 1 +#define HAVE_TOWLOWER 1 +#define HAVE_WCSTOMBS 1 +#define HAVE_SSL_GET_CURRENT_COMPRESSION 1 +#define HAVE_STRUCT_ADDRINFO 1 +#define HAVE_STRUCT_SOCKADDR_STORAGE 1 +#define HAVE_STRUCT_SOCKADDR_STORAGE_SS_FAMILY 1 +#define BLCKSZ 8192 +#define PG_KRB_SRVNAM "postgres" +#define PG_PRINTF_ATTRIBUTE printf +#define FLEXIBLE_ARRAY_MEMBER +#define MEMSET_LOOP_LIMIT 1024 +#define DEF_PGPORT 5432 +#define DEF_PGPORT_STR "5432" + +/* + * _Static_assert() was introduced in C11. However, all the latest major + * compilers support it for C99 as well. + */ +#define HAVE__STATIC_ASSERT 1 + +/* + * Undefined macros. + */ + +/* + * The following features are disabled by default, so we also disable them. + */ +#undef USE_ASSERT_CHECKING +#undef ENABLE_NLS +#undef ENABLE_GSS +#undef USE_OPENSSL +#undef USE_LDAP + +/* + * Is meaningless if NLS support is disabled (see above and libpq/buildfile for + * details). + */ +#undef LOCALEDIR + +/* + * Is meaningless if GSSAPI support is disabled (see above). It also seems that + * for modern systems including or will work both + * ( just includes ). + */ +#undef HAVE_GSSAPI_H + +/* + * Integer literal LL suffix is optional for C99. + */ +#undef HAVE_LL_CONSTANTS + +/* + * Windows-specific. is included for the latest (>= 1400) VC + * unconditionally. + */ +#undef HAVE_CRTDEFS_H + +/* + * Solaris-specific (getpeerucred() function). + */ +#undef HAVE_GETPEERUCRED + +/* + * Hard to even find any records of these types. + */ +#undef HAVE_INT64 +#undef HAVE_INT8 +#undef HAVE_UINT64 +#undef HAVE_UINT8 + +/* + * Something optimization-related for PowerPC machines (see + * libpq/postgresql/pg_config_manual.h for more details). + */ +#undef HAVE_PPC_LWARX_MUTEX_HINT + +/* + * None of the supported platforms has the '__' prefix for the mentioned + * sockaddr_storage struct members. + */ +#undef HAVE_STRUCT_SOCKADDR_STORAGE___SS_FAMILY +#undef HAVE_STRUCT_SOCKADDR_STORAGE___SS_LEN + +/* + * Let's follow Linux man page advise for sync_file_range() function: + * + * This system call is Linux-specific, and should be avoided in portable + * programs. + * + * The macro also seems to be backend-specific. + */ +#undef HAVE_SYNC_FILE_RANGE + +/* + * None of the supported OSes have . FreeBSD and Mac OS have + * (HAVE_SYS_UCRED_H macro). + */ +#undef HAVE_UCRED_H diff --git a/libpq/postgresql/pg_config.h.in.orig b/libpq/postgresql/pg_config.h.in.orig new file mode 100644 index 0000000..7dbfa90 --- /dev/null +++ b/libpq/postgresql/pg_config.h.in.orig @@ -0,0 +1,931 @@ +/* src/include/pg_config.h.in. Generated from configure.in by autoheader. */ + +/* Define to the type of arg 1 of 'accept' */ +#undef ACCEPT_TYPE_ARG1 + +/* Define to the type of arg 2 of 'accept' */ +#undef ACCEPT_TYPE_ARG2 + +/* Define to the type of arg 3 of 'accept' */ +#undef ACCEPT_TYPE_ARG3 + +/* Define to the return type of 'accept' */ +#undef ACCEPT_TYPE_RETURN + +/* Define if building universal (internal helper macro) */ +#undef AC_APPLE_UNIVERSAL_BUILD + +/* The normal alignment of `double', in bytes. */ +#undef ALIGNOF_DOUBLE + +/* The normal alignment of `int', in bytes. */ +#undef ALIGNOF_INT + +/* The normal alignment of `long', in bytes. */ +#undef ALIGNOF_LONG + +/* The normal alignment of `long long int', in bytes. */ +#undef ALIGNOF_LONG_LONG_INT + +/* The normal alignment of `short', in bytes. */ +#undef ALIGNOF_SHORT + +/* Size of a disk block --- this also limits the size of a tuple. You can set + it bigger if you need bigger tuples (although TOAST should reduce the need + to have large tuples, since fields can be spread across multiple tuples). + BLCKSZ must be a power of 2. The maximum possible value of BLCKSZ is + currently 2^15 (32768). This is determined by the 15-bit widths of the + lp_off and lp_len fields in ItemIdData (see include/storage/itemid.h). + Changing BLCKSZ requires an initdb. */ +#undef BLCKSZ + +/* Define to the default TCP port number on which the server listens and to + which clients will try to connect. This can be overridden at run-time, but + it's convenient if your clients have the right default compiled in. + (--with-pgport=PORTNUM) */ +#undef DEF_PGPORT + +/* Define to the default TCP port number as a string constant. */ +#undef DEF_PGPORT_STR + +/* Define to build with GSSAPI support. (--with-gssapi) */ +#undef ENABLE_GSS + +/* Define to 1 if you want National Language Support. (--enable-nls) */ +#undef ENABLE_NLS + +/* Define to 1 to build client libraries as thread-safe code. + (--enable-thread-safety) */ +#undef ENABLE_THREAD_SAFETY + +/* Define to nothing if C supports flexible array members, and to 1 if it does + not. That way, with a declaration like `struct s { int n; double + d[FLEXIBLE_ARRAY_MEMBER]; };', the struct hack can be used with pre-C99 + compilers. When computing the size of such an object, don't use 'sizeof + (struct s)' as it overestimates the size. Use 'offsetof (struct s, d)' + instead. Don't use 'offsetof (struct s, d[0])', as this doesn't work with + MSVC and with C++ compilers. */ +#undef FLEXIBLE_ARRAY_MEMBER + +/* float4 values are passed by value if 'true', by reference if 'false' */ +#undef FLOAT4PASSBYVAL + +/* float8, int8, and related values are passed by value if 'true', by + reference if 'false' */ +#undef FLOAT8PASSBYVAL + +/* Define to 1 if gettimeofday() takes only 1 argument. */ +#undef GETTIMEOFDAY_1ARG + +#ifdef GETTIMEOFDAY_1ARG +# define gettimeofday(a,b) gettimeofday(a) +#endif + +/* Define to 1 if you have the `append_history' function. */ +#undef HAVE_APPEND_HISTORY + +/* Define to 1 if you have the `ASN1_STRING_get0_data' function. */ +#undef HAVE_ASN1_STRING_GET0_DATA + +/* Define to 1 if you want to use atomics if available. */ +#undef HAVE_ATOMICS + +/* Define to 1 if you have the header file. */ +#undef HAVE_ATOMIC_H + +/* Define to 1 if you have the `BIO_get_data' function. */ +#undef HAVE_BIO_GET_DATA + +/* Define to 1 if you have the `BIO_meth_new' function. */ +#undef HAVE_BIO_METH_NEW + +/* Define to 1 if you have the `cbrt' function. */ +#undef HAVE_CBRT + +/* Define to 1 if you have the `class' function. */ +#undef HAVE_CLASS + +/* Define to 1 if you have the header file. */ +#undef HAVE_CRTDEFS_H + +/* Define to 1 if you have the `crypt' function. */ +#undef HAVE_CRYPT + +/* Define to 1 if you have the `CRYPTO_lock' function. */ +#undef HAVE_CRYPTO_LOCK + +/* Define to 1 if you have the header file. */ +#undef HAVE_CRYPT_H + +/* Define to 1 if you have the declaration of `fdatasync', and to 0 if you + don't. */ +#undef HAVE_DECL_FDATASYNC + +/* Define to 1 if you have the declaration of `F_FULLFSYNC', and to 0 if you + don't. */ +#undef HAVE_DECL_F_FULLFSYNC + +/* Define to 1 if you have the declaration of `posix_fadvise', and to 0 if you + don't. */ +#undef HAVE_DECL_POSIX_FADVISE + +/* Define to 1 if you have the declaration of `snprintf', and to 0 if you + don't. */ +#undef HAVE_DECL_SNPRINTF + +/* Define to 1 if you have the declaration of `strlcat', and to 0 if you + don't. */ +#undef HAVE_DECL_STRLCAT + +/* Define to 1 if you have the declaration of `strlcpy', and to 0 if you + don't. */ +#undef HAVE_DECL_STRLCPY + +/* Define to 1 if you have the declaration of `sys_siglist', and to 0 if you + don't. */ +#undef HAVE_DECL_SYS_SIGLIST + +/* Define to 1 if you have the declaration of `vsnprintf', and to 0 if you + don't. */ +#undef HAVE_DECL_VSNPRINTF + +/* Define to 1 if you have the header file. */ +#undef HAVE_DLD_H + +/* Define to 1 if you have the `dlopen' function. */ +#undef HAVE_DLOPEN + +/* Define to 1 if you have the header file. */ +#undef HAVE_EDITLINE_HISTORY_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_EDITLINE_READLINE_H + +/* Define to 1 if you have the `fdatasync' function. */ +#undef HAVE_FDATASYNC + +/* Define to 1 if you have the `fls' function. */ +#undef HAVE_FLS + +/* Define to 1 if you have the `fpclass' function. */ +#undef HAVE_FPCLASS + +/* Define to 1 if you have the `fp_class' function. */ +#undef HAVE_FP_CLASS + +/* Define to 1 if you have the `fp_class_d' function. */ +#undef HAVE_FP_CLASS_D + +/* Define to 1 if you have the header file. */ +#undef HAVE_FP_CLASS_H + +/* Define to 1 if fseeko (and presumably ftello) exists and is declared. */ +#undef HAVE_FSEEKO + +/* Define to 1 if your compiler understands __func__. */ +#undef HAVE_FUNCNAME__FUNC + +/* Define to 1 if your compiler understands __FUNCTION__. */ +#undef HAVE_FUNCNAME__FUNCTION + +/* Define to 1 if you have __atomic_compare_exchange_n(int *, int *, int). */ +#undef HAVE_GCC__ATOMIC_INT32_CAS + +/* Define to 1 if you have __atomic_compare_exchange_n(int64 *, int *, int64). + */ +#undef HAVE_GCC__ATOMIC_INT64_CAS + +/* Define to 1 if you have __sync_lock_test_and_set(char *) and friends. */ +#undef HAVE_GCC__SYNC_CHAR_TAS + +/* Define to 1 if you have __sync_compare_and_swap(int *, int, int). */ +#undef HAVE_GCC__SYNC_INT32_CAS + +/* Define to 1 if you have __sync_lock_test_and_set(int *) and friends. */ +#undef HAVE_GCC__SYNC_INT32_TAS + +/* Define to 1 if you have __sync_compare_and_swap(int64 *, int64, int64). */ +#undef HAVE_GCC__SYNC_INT64_CAS + +/* Define to 1 if you have the `getaddrinfo' function. */ +#undef HAVE_GETADDRINFO + +/* Define to 1 if you have the `gethostbyname_r' function. */ +#undef HAVE_GETHOSTBYNAME_R + +/* Define to 1 if you have the `getifaddrs' function. */ +#undef HAVE_GETIFADDRS + +/* Define to 1 if you have the `getopt' function. */ +#undef HAVE_GETOPT + +/* Define to 1 if you have the header file. */ +#undef HAVE_GETOPT_H + +/* Define to 1 if you have the `getopt_long' function. */ +#undef HAVE_GETOPT_LONG + +/* Define to 1 if you have the `getpeereid' function. */ +#undef HAVE_GETPEEREID + +/* Define to 1 if you have the `getpeerucred' function. */ +#undef HAVE_GETPEERUCRED + +/* Define to 1 if you have the `getpwuid_r' function. */ +#undef HAVE_GETPWUID_R + +/* Define to 1 if you have the `getrlimit' function. */ +#undef HAVE_GETRLIMIT + +/* Define to 1 if you have the `getrusage' function. */ +#undef HAVE_GETRUSAGE + +/* Define to 1 if you have the `gettimeofday' function. */ +#undef HAVE_GETTIMEOFDAY + +/* Define to 1 if you have the header file. */ +#undef HAVE_GSSAPI_GSSAPI_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_GSSAPI_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_HISTORY_H + +/* Define to 1 if you have the `history_truncate_file' function. */ +#undef HAVE_HISTORY_TRUNCATE_FILE + +/* Define to 1 if you have the header file. */ +#undef HAVE_IEEEFP_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_IFADDRS_H + +/* Define to 1 if you have the `inet_aton' function. */ +#undef HAVE_INET_ATON + +/* Define to 1 if the system has the type `int64'. */ +#undef HAVE_INT64 + +/* Define to 1 if the system has the type `int8'. */ +#undef HAVE_INT8 + +/* Define to 1 if the system has the type `intptr_t'. */ +#undef HAVE_INTPTR_T + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the global variable 'int opterr'. */ +#undef HAVE_INT_OPTERR + +/* Define to 1 if you have the global variable 'int optreset'. */ +#undef HAVE_INT_OPTRESET + +/* Define to 1 if you have the global variable 'int timezone'. */ +#undef HAVE_INT_TIMEZONE + +/* Define to 1 if you have support for IPv6. */ +#undef HAVE_IPV6 + +/* Define to 1 if you have isinf(). */ +#undef HAVE_ISINF + +/* Define to 1 if you have the header file. */ +#undef HAVE_LANGINFO_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_LDAP_H + +/* Define to 1 if you have the `crypto' library (-lcrypto). */ +#undef HAVE_LIBCRYPTO + +/* Define to 1 if you have the `ldap' library (-lldap). */ +#undef HAVE_LIBLDAP + +/* Define to 1 if you have the `ldap_r' library (-lldap_r). */ +#undef HAVE_LIBLDAP_R + +/* Define to 1 if you have the `m' library (-lm). */ +#undef HAVE_LIBM + +/* Define to 1 if you have the `pam' library (-lpam). */ +#undef HAVE_LIBPAM + +/* Define if you have a function readline library */ +#undef HAVE_LIBREADLINE + +/* Define to 1 if you have the `selinux' library (-lselinux). */ +#undef HAVE_LIBSELINUX + +/* Define to 1 if you have the `ssl' library (-lssl). */ +#undef HAVE_LIBSSL + +/* Define to 1 if you have the `wldap32' library (-lwldap32). */ +#undef HAVE_LIBWLDAP32 + +/* Define to 1 if you have the `xml2' library (-lxml2). */ +#undef HAVE_LIBXML2 + +/* Define to 1 if you have the `xslt' library (-lxslt). */ +#undef HAVE_LIBXSLT + +/* Define to 1 if you have the `z' library (-lz). */ +#undef HAVE_LIBZ + +/* Define to 1 if constants of type 'long long int' should have the suffix LL. + */ +#undef HAVE_LL_CONSTANTS + +/* Define to 1 if the system has the type `locale_t'. */ +#undef HAVE_LOCALE_T + +/* Define to 1 if `long int' works and is 64 bits. */ +#undef HAVE_LONG_INT_64 + +/* Define to 1 if the system has the type `long long int'. */ +#undef HAVE_LONG_LONG_INT + +/* Define to 1 if `long long int' works and is 64 bits. */ +#undef HAVE_LONG_LONG_INT_64 + +/* Define to 1 if you have the header file. */ +#undef HAVE_MBARRIER_H + +/* Define to 1 if you have the `mbstowcs_l' function. */ +#undef HAVE_MBSTOWCS_L + +/* Define to 1 if you have the `memmove' function. */ +#undef HAVE_MEMMOVE + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if the system has the type `MINIDUMP_TYPE'. */ +#undef HAVE_MINIDUMP_TYPE + +/* Define to 1 if you have the `mkdtemp' function. */ +#undef HAVE_MKDTEMP + +/* Define to 1 if you have the header file. */ +#undef HAVE_NETINET_IN_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_NETINET_TCP_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_NET_IF_H + +/* Define to 1 if you have the `OPENSSL_init_ssl' function. */ +#undef HAVE_OPENSSL_INIT_SSL + +/* Define to 1 if you have the header file. */ +#undef HAVE_OSSP_UUID_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_PAM_PAM_APPL_H + +/* Define to 1 if you have the `poll' function. */ +#undef HAVE_POLL + +/* Define to 1 if you have the header file. */ +#undef HAVE_POLL_H + +/* Define to 1 if you have the `posix_fadvise' function. */ +#undef HAVE_POSIX_FADVISE + +/* Define to 1 if the assembler supports PPC's LWARX mutex hint bit. */ +#undef HAVE_PPC_LWARX_MUTEX_HINT + +/* Define to 1 if you have the `pstat' function. */ +#undef HAVE_PSTAT + +/* Define to 1 if the PS_STRINGS thing exists. */ +#undef HAVE_PS_STRINGS + +/* Define if you have POSIX threads libraries and header files. */ +#undef HAVE_PTHREAD + +/* Define to 1 if you have the `pthread_is_threaded_np' function. */ +#undef HAVE_PTHREAD_IS_THREADED_NP + +/* Have PTHREAD_PRIO_INHERIT. */ +#undef HAVE_PTHREAD_PRIO_INHERIT + +/* Define to 1 if you have the header file. */ +#undef HAVE_PWD_H + +/* Define to 1 if you have the `random' function. */ +#undef HAVE_RANDOM + +/* Define to 1 if you have the `RAND_OpenSSL' function. */ +#undef HAVE_RAND_OPENSSL + +/* Define to 1 if you have the header file. */ +#undef HAVE_READLINE_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_READLINE_HISTORY_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_READLINE_READLINE_H + +/* Define to 1 if you have the `readlink' function. */ +#undef HAVE_READLINK + +/* Define to 1 if you have the `rint' function. */ +#undef HAVE_RINT + +/* Define to 1 if you have the global variable + 'rl_completion_append_character'. */ +#undef HAVE_RL_COMPLETION_APPEND_CHARACTER + +/* Define to 1 if you have the `rl_completion_matches' function. */ +#undef HAVE_RL_COMPLETION_MATCHES + +/* Define to 1 if you have the `rl_filename_completion_function' function. */ +#undef HAVE_RL_FILENAME_COMPLETION_FUNCTION + +/* Define to 1 if you have the `rl_reset_screen_size' function. */ +#undef HAVE_RL_RESET_SCREEN_SIZE + +/* Define to 1 if you have the header file. */ +#undef HAVE_SECURITY_PAM_APPL_H + +/* Define to 1 if you have the `setproctitle' function. */ +#undef HAVE_SETPROCTITLE + +/* Define to 1 if you have the `setsid' function. */ +#undef HAVE_SETSID + +/* Define to 1 if you have the `shm_open' function. */ +#undef HAVE_SHM_OPEN + +/* Define to 1 if you have the `snprintf' function. */ +#undef HAVE_SNPRINTF + +/* Define to 1 if you have spinlocks. */ +#undef HAVE_SPINLOCKS + +/* Define to 1 if you have the `srandom' function. */ +#undef HAVE_SRANDOM + +/* Define to 1 if you have the `SSL_get_current_compression' function. */ +#undef HAVE_SSL_GET_CURRENT_COMPRESSION + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the `strerror' function. */ +#undef HAVE_STRERROR + +/* Define to 1 if you have the `strerror_r' function. */ +#undef HAVE_STRERROR_R + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the `strlcat' function. */ +#undef HAVE_STRLCAT + +/* Define to 1 if you have the `strlcpy' function. */ +#undef HAVE_STRLCPY + +/* Define to 1 if you have the `strtoll' function. */ +#undef HAVE_STRTOLL + +/* Define to 1 if you have the `strtoq' function. */ +#undef HAVE_STRTOQ + +/* Define to 1 if you have the `strtoull' function. */ +#undef HAVE_STRTOULL + +/* Define to 1 if you have the `strtouq' function. */ +#undef HAVE_STRTOUQ + +/* Define to 1 if the system has the type `struct addrinfo'. */ +#undef HAVE_STRUCT_ADDRINFO + +/* Define to 1 if the system has the type `struct cmsgcred'. */ +#undef HAVE_STRUCT_CMSGCRED + +/* Define to 1 if the system has the type `struct option'. */ +#undef HAVE_STRUCT_OPTION + +/* Define to 1 if `sa_len' is a member of `struct sockaddr'. */ +#undef HAVE_STRUCT_SOCKADDR_SA_LEN + +/* Define to 1 if the system has the type `struct sockaddr_storage'. */ +#undef HAVE_STRUCT_SOCKADDR_STORAGE + +/* Define to 1 if `ss_family' is a member of `struct sockaddr_storage'. */ +#undef HAVE_STRUCT_SOCKADDR_STORAGE_SS_FAMILY + +/* Define to 1 if `ss_len' is a member of `struct sockaddr_storage'. */ +#undef HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN + +/* Define to 1 if `__ss_family' is a member of `struct sockaddr_storage'. */ +#undef HAVE_STRUCT_SOCKADDR_STORAGE___SS_FAMILY + +/* Define to 1 if `__ss_len' is a member of `struct sockaddr_storage'. */ +#undef HAVE_STRUCT_SOCKADDR_STORAGE___SS_LEN + +/* Define to 1 if `tm_zone' is a member of `struct tm'. */ +#undef HAVE_STRUCT_TM_TM_ZONE + +/* Define to 1 if you have the `symlink' function. */ +#undef HAVE_SYMLINK + +/* Define to 1 if you have the `sync_file_range' function. */ +#undef HAVE_SYNC_FILE_RANGE + +/* Define to 1 if you have the syslog interface. */ +#undef HAVE_SYSLOG + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_EPOLL_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_IOCTL_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_IPC_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_POLL_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_PSTAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_RESOURCE_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_SELECT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_SEM_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_SHM_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_SOCKET_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_SOCKIO_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TAS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TIME_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_UCRED_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_UN_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_TERMIOS_H + +/* Define to 1 if your `struct tm' has `tm_zone'. Deprecated, use + `HAVE_STRUCT_TM_TM_ZONE' instead. */ +#undef HAVE_TM_ZONE + +/* Define to 1 if you have the `towlower' function. */ +#undef HAVE_TOWLOWER + +/* Define to 1 if you have the external array `tzname'. */ +#undef HAVE_TZNAME + +/* Define to 1 if you have the header file. */ +#undef HAVE_UCRED_H + +/* Define to 1 if the system has the type `uint64'. */ +#undef HAVE_UINT64 + +/* Define to 1 if the system has the type `uint8'. */ +#undef HAVE_UINT8 + +/* Define to 1 if the system has the type `uintptr_t'. */ +#undef HAVE_UINTPTR_T + +/* Define to 1 if the system has the type `union semun'. */ +#undef HAVE_UNION_SEMUN + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define to 1 if you have unix sockets. */ +#undef HAVE_UNIX_SOCKETS + +/* Define to 1 if you have the `unsetenv' function. */ +#undef HAVE_UNSETENV + +/* Define to 1 if the system has the type `unsigned long long int'. */ +#undef HAVE_UNSIGNED_LONG_LONG_INT + +/* Define to 1 if you have the `utime' function. */ +#undef HAVE_UTIME + +/* Define to 1 if you have the `utimes' function. */ +#undef HAVE_UTIMES + +/* Define to 1 if you have the header file. */ +#undef HAVE_UTIME_H + +/* Define to 1 if you have BSD UUID support. */ +#undef HAVE_UUID_BSD + +/* Define to 1 if you have E2FS UUID support. */ +#undef HAVE_UUID_E2FS + +/* Define to 1 if you have the header file. */ +#undef HAVE_UUID_H + +/* Define to 1 if you have OSSP UUID support. */ +#undef HAVE_UUID_OSSP + +/* Define to 1 if you have the header file. */ +#undef HAVE_UUID_UUID_H + +/* Define to 1 if you have the `vsnprintf' function. */ +#undef HAVE_VSNPRINTF + +/* Define to 1 if you have the header file. */ +#undef HAVE_WCHAR_H + +/* Define to 1 if you have the `wcstombs' function. */ +#undef HAVE_WCSTOMBS + +/* Define to 1 if you have the `wcstombs_l' function. */ +#undef HAVE_WCSTOMBS_L + +/* Define to 1 if you have the header file. */ +#undef HAVE_WCTYPE_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_WINLDAP_H + +/* Define to 1 if your compiler understands __builtin_bswap32. */ +#undef HAVE__BUILTIN_BSWAP32 + +/* Define to 1 if your compiler understands __builtin_bswap64. */ +#undef HAVE__BUILTIN_BSWAP64 + +/* Define to 1 if your compiler understands __builtin_constant_p. */ +#undef HAVE__BUILTIN_CONSTANT_P + +/* Define to 1 if your compiler understands __builtin_types_compatible_p. */ +#undef HAVE__BUILTIN_TYPES_COMPATIBLE_P + +/* Define to 1 if your compiler understands __builtin_unreachable. */ +#undef HAVE__BUILTIN_UNREACHABLE + +/* Define to 1 if you have __cpuid. */ +#undef HAVE__CPUID + +/* Define to 1 if you have __get_cpuid. */ +#undef HAVE__GET_CPUID + +/* Define to 1 if your compiler understands _Static_assert. */ +#undef HAVE__STATIC_ASSERT + +/* Define to 1 if your compiler understands __VA_ARGS__ in macros. */ +#undef HAVE__VA_ARGS + +/* Define to the appropriate snprintf length modifier for 64-bit ints. */ +#undef INT64_MODIFIER + +/* Define to 1 if `locale_t' requires . */ +#undef LOCALE_T_IN_XLOCALE + +/* Define as the maximum alignment requirement of any C data type. */ +#undef MAXIMUM_ALIGNOF + +/* Define bytes to use libc memset(). */ +#undef MEMSET_LOOP_LIMIT + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the home page for this package. */ +#undef PACKAGE_URL + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* Define to the name of a signed 128-bit integer type. */ +#undef PG_INT128_TYPE + +/* Define to the name of a signed 64-bit integer type. */ +#undef PG_INT64_TYPE + +/* Define to the name of the default PostgreSQL service principal in Kerberos + (GSSAPI). (--with-krb-srvnam=NAME) */ +#undef PG_KRB_SRVNAM + +/* PostgreSQL major version as a string */ +#undef PG_MAJORVERSION + +/* Define to gnu_printf if compiler supports it, else printf. */ +#undef PG_PRINTF_ATTRIBUTE + +/* PostgreSQL version as a string */ +#undef PG_VERSION + +/* PostgreSQL version as a number */ +#undef PG_VERSION_NUM + +/* A string containing the version number, platform, and C compiler */ +#undef PG_VERSION_STR + +/* Define to 1 to allow profiling output to be saved separately for each + process. */ +#undef PROFILE_PID_DIR + +/* Define to necessary symbol if this constant uses a non-standard name on + your system. */ +#undef PTHREAD_CREATE_JOINABLE + +/* RELSEG_SIZE is the maximum number of blocks allowed in one disk file. Thus, + the maximum size of a single file is RELSEG_SIZE * BLCKSZ; relations bigger + than that are divided into multiple files. RELSEG_SIZE * BLCKSZ must be + less than your OS' limit on file size. This is often 2 GB or 4GB in a + 32-bit operating system, unless you have large file support enabled. By + default, we make the limit 1 GB to avoid any possible integer-overflow + problems within the OS. A limit smaller than necessary only means we divide + a large relation into more chunks than necessary, so it seems best to err + in the direction of a small limit. A power-of-2 value is recommended to + save a few cycles in md.c, but is not absolutely required. Changing + RELSEG_SIZE requires an initdb. */ +#undef RELSEG_SIZE + +/* The size of `long', as computed by sizeof. */ +#undef SIZEOF_LONG + +/* The size of `off_t', as computed by sizeof. */ +#undef SIZEOF_OFF_T + +/* The size of `size_t', as computed by sizeof. */ +#undef SIZEOF_SIZE_T + +/* The size of `void *', as computed by sizeof. */ +#undef SIZEOF_VOID_P + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Define to 1 if strerror_r() returns a int. */ +#undef STRERROR_R_INT + +/* Define to 1 if your declares `struct tm'. */ +#undef TM_IN_SYS_TIME + +/* Define to 1 to build with assertion checks. (--enable-cassert) */ +#undef USE_ASSERT_CHECKING + +/* Define to 1 to build with Bonjour support. (--with-bonjour) */ +#undef USE_BONJOUR + +/* Define to 1 to build with BSD Authentication support. (--with-bsd-auth) */ +#undef USE_BSD_AUTH + +/* Define to 1 if you want float4 values to be passed by value. + (--enable-float4-byval) */ +#undef USE_FLOAT4_BYVAL + +/* Define to 1 if you want float8, int8, etc values to be passed by value. + (--enable-float8-byval) */ +#undef USE_FLOAT8_BYVAL + +/* Define to 1 if you want 64-bit integer timestamp and interval support. + (--enable-integer-datetimes) */ +#undef USE_INTEGER_DATETIMES + +/* Define to 1 to build with LDAP support. (--with-ldap) */ +#undef USE_LDAP + +/* Define to 1 to build with XML support. (--with-libxml) */ +#undef USE_LIBXML + +/* Define to 1 to use XSLT support when building contrib/xml2. + (--with-libxslt) */ +#undef USE_LIBXSLT + +/* Define to select named POSIX semaphores. */ +#undef USE_NAMED_POSIX_SEMAPHORES + +/* Define to build with OpenSSL support. (--with-openssl) */ +#undef USE_OPENSSL + +/* Define to 1 to build with PAM support. (--with-pam) */ +#undef USE_PAM + +/* Use replacement snprintf() functions. */ +#undef USE_REPL_SNPRINTF + +/* Define to 1 to use Intel SSE 4.2 CRC instructions with a runtime check. */ +#undef USE_SLICING_BY_8_CRC32C + +/* Define to 1 use Intel SSE 4.2 CRC instructions. */ +#undef USE_SSE42_CRC32C + +/* Define to 1 to use Intel SSSE 4.2 CRC instructions with a runtime check. */ +#undef USE_SSE42_CRC32C_WITH_RUNTIME_CHECK + +/* Define to build with systemd support. (--with-systemd) */ +#undef USE_SYSTEMD + +/* Define to select SysV-style semaphores. */ +#undef USE_SYSV_SEMAPHORES + +/* Define to select SysV-style shared memory. */ +#undef USE_SYSV_SHARED_MEMORY + +/* Define to select unnamed POSIX semaphores. */ +#undef USE_UNNAMED_POSIX_SEMAPHORES + +/* Define to select Win32-style semaphores. */ +#undef USE_WIN32_SEMAPHORES + +/* Define to select Win32-style shared memory. */ +#undef USE_WIN32_SHARED_MEMORY + +/* Define to 1 if `wcstombs_l' requires . */ +#undef WCSTOMBS_L_IN_XLOCALE + +/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most + significant byte first (like Motorola and SPARC, unlike Intel). */ +#if defined AC_APPLE_UNIVERSAL_BUILD +# if defined __BIG_ENDIAN__ +# define WORDS_BIGENDIAN 1 +# endif +#else +# ifndef WORDS_BIGENDIAN +# undef WORDS_BIGENDIAN +# endif +#endif + +/* Size of a WAL file block. This need have no particular relation to BLCKSZ. + XLOG_BLCKSZ must be a power of 2, and if your system supports O_DIRECT I/O, + XLOG_BLCKSZ must be a multiple of the alignment requirement for direct-I/O + buffers, else direct I/O may fail. Changing XLOG_BLCKSZ requires an initdb. + */ +#undef XLOG_BLCKSZ + +/* XLOG_SEG_SIZE is the size of a single WAL file. This must be a power of 2 + and larger than XLOG_BLCKSZ (preferably, a great deal larger than + XLOG_BLCKSZ). Changing XLOG_SEG_SIZE requires an initdb. */ +#undef XLOG_SEG_SIZE + + + +/* Number of bits in a file offset, on hosts where this is settable. */ +#undef _FILE_OFFSET_BITS + +/* Define to 1 to make fseeko visible on some hosts (e.g. glibc 2.2). */ +#undef _LARGEFILE_SOURCE + +/* Define for large files, on AIX-style hosts. */ +#undef _LARGE_FILES + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef __cplusplus +#undef inline +#endif + +/* Define to the type of a signed integer type wide enough to hold a pointer, + if such a type exists, and if the system does not define it. */ +#undef intptr_t + +/* Define to empty if the C compiler does not understand signed types. */ +#undef signed + +/* Define to the type of an unsigned integer type wide enough to hold a + pointer, if such a type exists, and if the system does not define it. */ +#undef uintptr_t diff --git a/libpq/postgresql/pg_config.h.win32.orig b/libpq/postgresql/pg_config.h.win32.orig new file mode 100644 index 0000000..0e17ccb --- /dev/null +++ b/libpq/postgresql/pg_config.h.win32.orig @@ -0,0 +1,682 @@ +/* src/include/pg_config.h. Generated by configure. */ +/* src/include/pg_config.h.in. Generated from configure.in by autoheader. */ + +/* This file is generated from MingW ./configure, and with the following + * changes to be valid for Visual C++ (and compatible): + * + * HAVE_CBRT, HAVE_FUNCNAME_FUNC, HAVE_GETOPT, HAVE_GETOPT_H, HAVE_INTTYPES_H, + * HAVE_GETOPT_LONG, HAVE_LOCALE_T, HAVE_RINT, HAVE_STRINGS_H, HAVE_STRTOLL, + * HAVE_STRTOULL, HAVE_STRUCT_OPTION, ENABLE_THREAD_SAFETY, + * inline, USE_SSE42_CRC32C_WITH_RUNTIME_CHECK + */ + +/* Define to the type of arg 1 of 'accept' */ +#define ACCEPT_TYPE_ARG1 unsigned int + +/* Define to the type of arg 2 of 'accept' */ +#define ACCEPT_TYPE_ARG2 struct sockaddr * + +/* Define to the type of arg 3 of 'accept' */ +#define ACCEPT_TYPE_ARG3 int + +/* Define to the return type of 'accept' */ +#define ACCEPT_TYPE_RETURN unsigned int PASCAL + +/* The alignment requirement of a `double'. */ +#define ALIGNOF_DOUBLE 8 + +/* The alignment requirement of a `int'. */ +#define ALIGNOF_INT 4 + +/* The alignment requirement of a `long'. */ +#define ALIGNOF_LONG 4 + +/* The alignment requirement of a `long long int'. */ +#define ALIGNOF_LONG_LONG_INT 8 + +/* The alignment requirement of a `short'. */ +#define ALIGNOF_SHORT 2 + +/* Define to the default TCP port number on which the server listens and to + which clients will try to connect. This can be overridden at run-time, but + it's convenient if your clients have the right default compiled in. + (--with-pgport=PORTNUM) */ +#define DEF_PGPORT 5432 + +/* Define to the default TCP port number as a string constant. */ +#define DEF_PGPORT_STR "5432" + +/* Define to nothing if C supports flexible array members, and to 1 if it does + not. That way, with a declaration like `struct s { int n; double + d[FLEXIBLE_ARRAY_MEMBER]; };', the struct hack can be used with pre-C99 + compilers. When computing the size of such an object, don't use 'sizeof + (struct s)' as it overestimates the size. Use 'offsetof (struct s, d)' + instead. Don't use 'offsetof (struct s, d[0])', as this doesn't work with + MSVC and with C++ compilers. */ +#define FLEXIBLE_ARRAY_MEMBER + +/* Define to 1 if you want National Language Support. (--enable-nls) */ +/* #undef ENABLE_NLS */ + +/* Define to 1 to build client libraries as thread-safe code. + (--enable-thread-safety) */ +#define ENABLE_THREAD_SAFETY 1 + +/* Define to 1 if gettimeofday() takes only 1 argument. */ +/* #undef GETTIMEOFDAY_1ARG */ + +#ifdef GETTIMEOFDAY_1ARG +# define gettimeofday(a,b) gettimeofday(a) +#endif + +/* Define to 1 if you have the `cbrt' function. */ +//#define HAVE_CBRT 1 + +/* Define to 1 if you have the `class' function. */ +/* #undef HAVE_CLASS */ + +/* Define to 1 if you have the `crypt' function. */ +/* #undef HAVE_CRYPT */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_CRYPT_H */ + +/* Define to 1 if you have the declaration of `fdatasync', and to 0 if you + don't. */ +#define HAVE_DECL_FDATASYNC 0 + +/* Define to 1 if you have the declaration of `F_FULLFSYNC', and to 0 if you + don't. */ +#define HAVE_DECL_F_FULLFSYNC 0 + +/* Define to 1 if you have the declaration of `snprintf', and to 0 if you + don't. */ +#define HAVE_DECL_SNPRINTF 1 + +/* Define to 1 if you have the declaration of `vsnprintf', and to 0 if you + don't. */ +#define HAVE_DECL_VSNPRINTF 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_DLD_H */ + +/* Define to 1 if you have the `dlopen' function. */ +/* #undef HAVE_DLOPEN */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_EDITLINE_HISTORY_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_EDITLINE_READLINE_H */ + +/* Define to 1 if you have the `fcvt' function. */ +#define HAVE_FCVT 1 + +/* Define to 1 if you have the `fdatasync' function. */ +/* #undef HAVE_FDATASYNC */ + +/* Define to 1 if you have finite(). */ +#define HAVE_FINITE 1 + +/* Define to 1 if you have the `fpclass' function. */ +/* #undef HAVE_FPCLASS */ + +/* Define to 1 if you have the `fp_class' function. */ +/* #undef HAVE_FP_CLASS */ + +/* Define to 1 if you have the `fp_class_d' function. */ +/* #undef HAVE_FP_CLASS_D */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_FP_CLASS_H */ + +/* Define to 1 if fseeko (and presumably ftello) exists and is declared. */ +#define HAVE_FSEEKO 1 + +/* Define to 1 if your compiler understands __func__. */ +//#define HAVE_FUNCNAME__FUNC 1 + +/* Define to 1 if your compiler understands __FUNCTION__. */ +#define HAVE_FUNCNAME__FUNCTION 1 + +/* Define to 1 if you have getaddrinfo(). */ +/* #undef HAVE_GETADDRINFO */ + +/* Define to 1 if you have the `gethostbyname_r' function. */ +/* #undef HAVE_GETHOSTBYNAME_R */ + +/* Define to 1 if you have the `getopt' function. */ +//#define HAVE_GETOPT 1 + +/* Define to 1 if you have the header file. */ +//#define HAVE_GETOPT_H 1 + +/* Define to 1 if you have the `getopt_long' function. */ +//#define HAVE_GETOPT_LONG 1 + +/* Define to 1 if you have the `getpeereid' function. */ +/* #undef HAVE_GETPEEREID */ + +/* Define to 1 if you have the `getpwuid_r' function. */ +/* #undef HAVE_GETPWUID_R */ + +/* Define to 1 if you have the `getrusage' function. */ +/* #undef HAVE_GETRUSAGE */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_HISTORY_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_IEEEFP_H */ + +/* Define to 1 if you have the `inet_aton' function. */ +/* #undef HAVE_INET_ATON */ + +/* Define to 1 if the system has the type `int64'. */ +/* #undef HAVE_INT64 */ + +/* Define to 1 if the system has the type `int8'. */ +/* #undef HAVE_INT8 */ + +/* Define to 1 if you have the header file. */ +//#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the global variable 'int timezone'. */ +#define HAVE_INT_TIMEZONE 1 + +/* Define to 1 if you have support for IPv6. */ +#define HAVE_IPV6 1 + +/* Define to 1 if you have isinf(). */ +#define HAVE_ISINF 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_LANGINFO_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_LDAP_H */ + +/* Define to 1 if you have the `crypto' library (-lcrypto). */ +/* #undef HAVE_LIBCRYPTO */ + +/* Define to 1 if you have the `ldap' library (-lldap). */ +/* #undef HAVE_LIBLDAP */ + +/* Define to 1 if you have the `pam' library (-lpam). */ +/* #undef HAVE_LIBPAM */ + +/* Define if you have a function readline library */ +/* #undef HAVE_LIBREADLINE */ + +/* Define to 1 if you have the `ssl' library (-lssl). */ +/* #undef HAVE_LIBSSL */ + +/* Define to 1 if you have the `wldap32' library (-lwldap32). */ +/* #undef HAVE_LIBWLDAP32 */ + +/* Define to 1 if you have the `z' library (-lz). */ +/* #undef HAVE_LIBZ */ + +/* Define to 1 if constants of type 'long long int' should have the suffix LL. + */ +#if (_MSC_VER > 1200) +#define HAVE_LL_CONSTANTS 1 +#endif + +/* Define to 1 if the system has the type `locale_t'. */ +#define HAVE_LOCALE_T 1 + +/* Define to 1 if `long int' works and is 64 bits. */ +/* #undef HAVE_LONG_INT_64 */ + +/* Define to 1 if `long long int' works and is 64 bits. */ +#if (_MSC_VER > 1200) +#define HAVE_LONG_LONG_INT_64 +#endif + +/* Define to 1 if you have the `mbstowcs_l' function. */ +#define HAVE_MBSTOWCS_L 1 + +/* Define to 1 if you have the `memmove' function. */ +#define HAVE_MEMMOVE 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if the system has the type `MINIDUMP_TYPE'. */ +#define HAVE_MINIDUMP_TYPE 1 + +/* Define to 1 if you have the `mkdtemp' function. */ +/* #undef HAVE_MKDTEMP */ + +/* Define to 1 if you have the header file. */ +#define HAVE_NETINET_IN_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_NETINET_TCP_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_PAM_PAM_APPL_H */ + +/* Define to 1 if you have the `poll' function. */ +/* #undef HAVE_POLL */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_POLL_H */ + +/* Define to 1 if you have the `pstat' function. */ +/* #undef HAVE_PSTAT */ + +/* Define to 1 if the PS_STRINGS thing exists. */ +/* #undef HAVE_PS_STRINGS */ + +/* Define to 1 if you have the header file. */ +#define HAVE_PWD_H 1 + +/* Define to 1 if you have the `random' function. */ +/* #undef HAVE_RANDOM */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_READLINE_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_READLINE_HISTORY_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_READLINE_READLINE_H */ + +/* Define to 1 if you have the `readlink' function. */ +/* #undef HAVE_READLINK */ + +/* Define to 1 if you have the `rint' function. */ +#if (_MSC_VER >= 1800) +#define HAVE_RINT 1 +#endif + + +/* Define to 1 if you have the global variable + 'rl_completion_append_character'. */ +/* #undef HAVE_RL_COMPLETION_APPEND_CHARACTER */ + +/* Define to 1 if you have the `rl_completion_matches' function. */ +/* #undef HAVE_RL_COMPLETION_MATCHES */ + +/* Define to 1 if you have the `rl_filename_completion_function' function. */ +/* #undef HAVE_RL_FILENAME_COMPLETION_FUNCTION */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SECURITY_PAM_APPL_H */ + +/* Define to 1 if you have the `setproctitle' function. */ +/* #undef HAVE_SETPROCTITLE */ + +/* Define to 1 if you have the `setsid' function. */ +/* #undef HAVE_SETSID */ + +/* Define to 1 if you have the `snprintf' function. */ +/* #undef HAVE_SNPRINTF */ + +/* Define to 1 if you have spinlocks. */ +#define HAVE_SPINLOCKS 1 + +/* Define to 1 if you have atomics. */ +#define HAVE_ATOMICS 1 + +/* Define to 1 if you have the `srandom' function. */ +/* #undef HAVE_SRANDOM */ + +/* Define to 1 if you have the `SSL_get_current_compression' function. */ +#define HAVE_SSL_GET_CURRENT_COMPRESSION 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_STDINT_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the `strerror' function. */ +#ifndef HAVE_STRERROR +#define HAVE_STRERROR 1 +#endif + +/* Define to 1 if you have the `strerror_r' function. */ +/* #undef HAVE_STRERROR_R */ + +/* Define to 1 if you have the header file. */ +/*#define HAVE_STRINGS_H 1 */ + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the `strtoll' function. */ +//#define HAVE_STRTOLL 1 + +/* Define to 1 if you have the `strtoq' function. */ +/* #undef HAVE_STRTOQ */ + +/* Define to 1 if you have the `strtoull' function. */ +//#define HAVE_STRTOULL 1 + +/* Define to 1 if you have the `strtouq' function. */ +/* #undef HAVE_STRTOUQ */ + +/* Define to 1 if the system has the type `struct addrinfo'. */ +#if (_MSC_VER > 1200) +#define HAVE_STRUCT_ADDRINFO 1 +#endif + +/* Define to 1 if the system has the type `struct cmsgcred'. */ +/* #undef HAVE_STRUCT_CMSGCRED */ + +/* Define to 1 if the system has the type `struct option'. */ +//#define HAVE_STRUCT_OPTION 1 + +/* Define to 1 if `sa_len' is member of `struct sockaddr'. */ +/* #undef HAVE_STRUCT_SOCKADDR_SA_LEN */ + +/* Define to 1 if the system has the type `struct sockaddr_storage'. */ +#if (_MSC_VER > 1200) +#define HAVE_STRUCT_SOCKADDR_STORAGE 1 +#endif + +/* Define to 1 if `ss_family' is member of `struct sockaddr_storage'. */ +#if (_MSC_VER > 1200) +#define HAVE_STRUCT_SOCKADDR_STORAGE_SS_FAMILY 1 +#endif + +/* Define to 1 if `ss_len' is member of `struct sockaddr_storage'. */ +/* #undef HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN */ + +/* Define to 1 if `__ss_family' is member of `struct sockaddr_storage'. */ +/* #undef HAVE_STRUCT_SOCKADDR_STORAGE___SS_FAMILY */ + +/* Define to 1 if `__ss_len' is member of `struct sockaddr_storage'. */ +/* #undef HAVE_STRUCT_SOCKADDR_STORAGE___SS_LEN */ + +/* Define to 1 if the system has the type `struct sockaddr_un'. */ +/* #undef HAVE_STRUCT_SOCKADDR_UN */ + +/* Define to 1 if `tm_zone' is member of `struct tm'. */ +/* #undef HAVE_STRUCT_TM_TM_ZONE */ + +/* Define to 1 if you have the `symlink' function. */ +#define HAVE_SYMLINK 1 + +/* Define to 1 if you have the `sync_file_range' function. */ +/* #undef HAVE_SYNC_FILE_RANGE */ + +/* Define to 1 if you have the `sysconf' function. */ +/* #undef HAVE_SYSCONF */ + +/* Define to 1 if you have the syslog interface. */ +/* #undef HAVE_SYSLOG */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_IPC_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_POLL_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_PSTAT_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_SELECT_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_SEM_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_SHM_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_SOCKET_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TIME_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_UCRED_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_UN_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_TERMIOS_H */ + +/* Define to 1 if your `struct tm' has `tm_zone'. Deprecated, use + `HAVE_STRUCT_TM_TM_ZONE' instead. */ +/* #undef HAVE_TM_ZONE */ + +/* Define to 1 if you have the `towlower' function. */ +#define HAVE_TOWLOWER 1 + +/* Define to 1 if you have the external array `tzname'. */ +/* #undef HAVE_TZNAME */ + +/* Define to 1 if the system has the type `uint64'. */ +/* #undef HAVE_UINT64 */ + +/* Define to 1 if the system has the type `uint8'. */ +/* #undef HAVE_UINT8 */ + +/* Define to 1 if the system has the type `union semun'. */ +/* #undef HAVE_UNION_SEMUN */ + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to 1 if you have unix sockets. */ +/* #undef HAVE_UNIX_SOCKETS */ + +/* Define to 1 if you have the `unsetenv' function. */ +/* #undef HAVE_UNSETENV */ + +/* Define to 1 if you have the `utime' function. */ +#define HAVE_UTIME 1 + +/* Define to 1 if you have the `utimes' function. */ +/* #undef HAVE_UTIMES */ + +/* Define to 1 if you have the header file. */ +#define HAVE_UTIME_H 1 + +/* Define to 1 if you have the `vsnprintf' function. */ +#define HAVE_VSNPRINTF 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_WCHAR_H 1 + +/* Define to 1 if you have the `wcstombs' function. */ +#define HAVE_WCSTOMBS 1 + +/* Define to 1 if you have the `wcstombs_l' function. */ +#define HAVE_WCSTOMBS_L 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_WCTYPE_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_WINLDAP_H */ + +/* Define to 1 if your compiler understands __builtin_bswap32. */ +/* #undef HAVE__BUILTIN_BSWAP32 */ + +/* Define to 1 if your compiler understands __builtin_bswap64. */ +/* #undef HAVE__BUILTIN_BSWAP64 */ + +/* Define to 1 if your compiler understands __builtin_constant_p. */ +/* #undef HAVE__BUILTIN_CONSTANT_P */ + +/* Define to 1 if your compiler understands __builtin_types_compatible_p. */ +/* #undef HAVE__BUILTIN_TYPES_COMPATIBLE_P */ + +/* Define to 1 if your compiler understands __builtin_unreachable. */ +/* #undef HAVE__BUILTIN_UNREACHABLE */ + +/* Define to 1 if you have __cpuid. */ +#define HAVE__CPUID 1 + +/* Define to 1 if you have __get_cpuid. */ +#undef HAVE__GET_CPUID + +/* Define to 1 if your compiler understands _Static_assert. */ +/* #undef HAVE__STATIC_ASSERT */ + +/* Define to 1 if your compiler understands __VA_ARGS__ in macros. */ +#define HAVE__VA_ARGS 1 + +/* Define to the appropriate snprintf length modifier for 64-bit ints. */ +#define INT64_MODIFIER "ll" + +/* Define to 1 if `locale_t' requires . */ +/* #undef LOCALE_T_IN_XLOCALE */ + +/* Define to the location of locale files. */ +/* #undef LOCALEDIR */ + +/* Define as the maximum alignment requirement of any C data type. */ +#define MAXIMUM_ALIGNOF 8 + +/* Define bytes to use libc memset(). */ +#define MEMSET_LOOP_LIMIT 1024 + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "pgsql-bugs@postgresql.org" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "PostgreSQL" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "PostgreSQL 9.6.5" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "9.6.5" + +/* Define to the name of a signed 128-bit integer type. */ +#undef PG_INT128_TYPE + +/* Define to the name of a signed 64-bit integer type. */ +#define PG_INT64_TYPE long long int + +/* PostgreSQL version as a string */ +#define PG_VERSION "9.6.5" + +/* PostgreSQL version as a number */ +#define PG_VERSION_NUM 90605 + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "postgresql" + +/* Define to the name of the default PostgreSQL service principal in Kerberos. + (--with-krb-srvnam=NAME) */ +#define PG_KRB_SRVNAM "postgres" + +/* A string containing the version number, platform, and C compiler */ +#define PG_VERSION_STR "Uninitialized version string (win32)" + +/* The size of `long', as computed by sizeof. */ +#define SIZEOF_LONG 4 + +/* The size of `size_t', as computed by sizeof. */ +#ifndef _WIN64 +#define SIZEOF_SIZE_T 4 +#else +#define SIZEOF_SIZE_T 8 +#endif + +/* The size of `void *', as computed by sizeof. */ +#ifndef _WIN64 +#define SIZEOF_VOID_P 4 +#else +#define SIZEOF_VOID_P 8 +#endif + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Define to 1 if strerror_r() returns a int. */ +/* #undef STRERROR_R_INT */ + +/* Define to 1 if your declares `struct tm'. */ +/* #undef TM_IN_SYS_TIME */ + +/* Define to 1 to build with assertion checks. (--enable-cassert) */ +/* #undef USE_ASSERT_CHECKING */ + +/* Define to 1 to build with Bonjour support. (--with-bonjour) */ +/* #undef USE_BONJOUR */ + +/* Define to 1 to build with BSD Authentication support. (--with-bsd-auth) */ +/* #undef USE_BSD_AUTH */ + +/* Define to 1 if you want 64-bit integer timestamp and interval support. + (--enable-integer-datetimes) */ +/* #undef USE_INTEGER_DATETIMES */ + +/* Define to 1 to build with LDAP support. (--with-ldap) */ +/* #undef USE_LDAP */ + +/* Define to select named POSIX semaphores. */ +/* #undef USE_NAMED_POSIX_SEMAPHORES */ + +/* Define to build with OpenSSL support. (--with-openssl) */ +/* #undef USE_OPENSSL */ + +/* Define to 1 to build with PAM support. (--with-pam) */ +/* #undef USE_PAM */ + +/* Use replacement snprintf() functions. */ +#define USE_REPL_SNPRINTF 1 + +/* Define to 1 to use Intel SSE 4.2 CRC instructions with a runtime check. */ +#if (_MSC_VER < 1500) +#define USE_SLICING_BY_8_CRC32C 1 +#endif + +/* Define to 1 use Intel SSE 4.2 CRC instructions. */ +/* #undef USE_SSE42_CRC32C */ + +/* Define to 1 to use Intel SSSE 4.2 CRC instructions with a runtime check. */ +#if (_MSC_VER >= 1500) +#define USE_SSE42_CRC32C_WITH_RUNTIME_CHECK +#endif + +/* Define to select SysV-style semaphores. */ +/* #undef USE_SYSV_SEMAPHORES */ + +/* Define to select SysV-style shared memory. */ +#define USE_SYSV_SHARED_MEMORY 1 + +/* Define to select unnamed POSIX semaphores. */ +/* #undef USE_UNNAMED_POSIX_SEMAPHORES */ + +/* Define to select Win32-style semaphores. */ +#define USE_WIN32_SEMAPHORES 1 + +/* Define to 1 if `wcstombs_l' requires . */ +/* #undef WCSTOMBS_L_IN_XLOCALE */ + +/* Number of bits in a file offset, on hosts where this is settable. */ +/* #undef _FILE_OFFSET_BITS */ + +/* Define to 1 to make fseeko visible on some hosts (e.g. glibc 2.2). */ +/* #undef _LARGEFILE_SOURCE */ + +/* Define for large files, on AIX-style hosts. */ +/* #undef _LARGE_FILES */ + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef __cplusplus +#define inline __inline +#endif + +/* Define to empty if the C compiler does not understand signed types. */ +/* #undef signed */ diff --git a/libpq/postgresql/pg_config_ext.h b/libpq/postgresql/pg_config_ext.h new file mode 100644 index 0000000..78a5178 --- /dev/null +++ b/libpq/postgresql/pg_config_ext.h @@ -0,0 +1,20 @@ +/* file : libpq/postgresql/pg_config_ext.h -*- C -*- + * copyright : Copyright (c) 2016-2017 Code Synthesis Ltd + * license : PostgreSQL License; see accompanying COPYRIGHT file + */ + +/* + * For the semanics of the following macros refer to the + * libpq/postgresql/pg_config_ext.h.in.orig and + * libpq/postgresql/pg_config_ext.h.win32.orig file. + */ + +/* + * Note that is invented by C99 and we can't expect that the libpq + * client is compiled according to this standard. However, when compile with + * GCC, Clang or VC, even requesting C90 standard explicitly, then the header + * and int64_t type are both available. + */ +#include + +#define PG_INT64_TYPE int64_t diff --git a/libpq/postgresql/pg_config_ext.h.in.orig b/libpq/postgresql/pg_config_ext.h.in.orig new file mode 100644 index 0000000..8acadbd --- /dev/null +++ b/libpq/postgresql/pg_config_ext.h.in.orig @@ -0,0 +1,7 @@ +/* + * src/include/pg_config_ext.h.in. This is generated manually, not by + * autoheader, since we want to limit which symbols get defined here. + */ + +/* Define to the name of a signed 64-bit integer type. */ +#undef PG_INT64_TYPE diff --git a/libpq/postgresql/pg_config_ext.h.win32.orig b/libpq/postgresql/pg_config_ext.h.win32.orig new file mode 100644 index 0000000..65bbb5d --- /dev/null +++ b/libpq/postgresql/pg_config_ext.h.win32.orig @@ -0,0 +1,7 @@ +/* + * src/include/pg_config_ext.h.win32. This is generated manually, not by + * autoheader, since we want to limit which symbols get defined here. + */ + +/* Define to the name of a signed 64-bit integer type. */ +#define PG_INT64_TYPE long long int diff --git a/libpq/postgresql/pg_config_manual.h b/libpq/postgresql/pg_config_manual.h new file mode 100644 index 0000000..96885bb --- /dev/null +++ b/libpq/postgresql/pg_config_manual.h @@ -0,0 +1,327 @@ +/*------------------------------------------------------------------------ + * PostgreSQL manual configuration settings + * + * This file contains various configuration symbols and limits. In + * all cases, changing them is only useful in very rare situations or + * for developers. If you edit any of these, be sure to do a *full* + * rebuild (and an initdb if noted). + * + * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/pg_config_manual.h + *------------------------------------------------------------------------ + */ + +/* + * Maximum length for identifiers (e.g. table names, column names, + * function names). Names actually are limited to one less byte than this, + * because the length must include a trailing zero byte. + * + * Changing this requires an initdb. + */ +#define NAMEDATALEN 64 + +/* + * Maximum number of arguments to a function. + * + * The minimum value is 8 (GIN indexes use 8-argument support functions). + * The maximum possible value is around 600 (limited by index tuple size in + * pg_proc's index; BLCKSZ larger than 8K would allow more). Values larger + * than needed will waste memory and processing time, but do not directly + * cost disk space. + * + * Changing this does not require an initdb, but it does require a full + * backend recompile (including any user-defined C functions). + */ +#define FUNC_MAX_ARGS 100 + +/* + * Maximum number of columns in an index. There is little point in making + * this anything but a multiple of 32, because the main cost is associated + * with index tuple header size (see access/itup.h). + * + * Changing this requires an initdb. + */ +#define INDEX_MAX_KEYS 32 + +/* + * Set the upper and lower bounds of sequence values. + */ +#define SEQ_MAXVALUE PG_INT64_MAX +#define SEQ_MINVALUE (-SEQ_MAXVALUE) + +/* + * When we don't have native spinlocks, we use semaphores to simulate them. + * Decreasing this value reduces consumption of OS resources; increasing it + * may improve performance, but supplying a real spinlock implementation is + * probably far better. + */ +#define NUM_SPINLOCK_SEMAPHORES 128 + +/* + * When we have neither spinlocks nor atomic operations support we're + * implementing atomic operations on top of spinlock on top of semaphores. To + * be safe against atomic operations while holding a spinlock separate + * semaphores have to be used. + */ +#define NUM_ATOMICS_SEMAPHORES 64 + +/* + * Define this if you want to allow the lo_import and lo_export SQL + * functions to be executed by ordinary users. By default these + * functions are only available to the Postgres superuser. CAUTION: + * These functions are SECURITY HOLES since they can read and write + * any file that the PostgreSQL server has permission to access. If + * you turn this on, don't say we didn't warn you. + */ +/* #define ALLOW_DANGEROUS_LO_FUNCTIONS */ + +/* + * MAXPGPATH: standard size of a pathname buffer in PostgreSQL (hence, + * maximum usable pathname length is one less). + * + * We'd use a standard system header symbol for this, if there weren't + * so many to choose from: MAXPATHLEN, MAX_PATH, PATH_MAX are all + * defined by different "standards", and often have different values + * on the same platform! So we just punt and use a reasonably + * generous setting here. + */ +#define MAXPGPATH 1024 + +/* + * PG_SOMAXCONN: maximum accept-queue length limit passed to + * listen(2). You'd think we should use SOMAXCONN from + * , but on many systems that symbol is much smaller + * than the kernel's actual limit. In any case, this symbol need be + * twiddled only if you have a kernel that refuses large limit values, + * rather than silently reducing the value to what it can handle + * (which is what most if not all Unixen do). + */ +#define PG_SOMAXCONN 10000 + +/* + * You can try changing this if you have a machine with bytes of + * another size, but no guarantee... + */ +#define BITS_PER_BYTE 8 + +/* + * Preferred alignment for disk I/O buffers. On some CPUs, copies between + * user space and kernel space are significantly faster if the user buffer + * is aligned on a larger-than-MAXALIGN boundary. Ideally this should be + * a platform-dependent value, but for now we just hard-wire it. + */ +#define ALIGNOF_BUFFER 32 + +/* + * Disable UNIX sockets for certain operating systems. + */ +#if defined(WIN32) +#undef HAVE_UNIX_SOCKETS +#endif + +/* + * Define this if your operating system supports link() + */ +#if !defined(WIN32) && !defined(__CYGWIN__) +#define HAVE_WORKING_LINK 1 +#endif + +/* + * USE_POSIX_FADVISE controls whether Postgres will attempt to use the + * posix_fadvise() kernel call. Usually the automatic configure tests are + * sufficient, but some older Linux distributions had broken versions of + * posix_fadvise(). If necessary you can remove the #define here. + */ +#if HAVE_DECL_POSIX_FADVISE && defined(HAVE_POSIX_FADVISE) +#define USE_POSIX_FADVISE +#endif + +/* + * USE_PREFETCH code should be compiled only if we have a way to implement + * prefetching. (This is decoupled from USE_POSIX_FADVISE because there + * might in future be support for alternative low-level prefetch APIs.) + */ +#ifdef USE_POSIX_FADVISE +#define USE_PREFETCH +#endif + +/* + * Default and maximum values for backend_flush_after, bgwriter_flush_after + * and checkpoint_flush_after; measured in blocks. Currently, these are + * enabled by default if sync_file_range() exists, ie, only on Linux. Perhaps + * we could also enable by default if we have mmap and msync(MS_ASYNC)? + */ +#ifdef HAVE_SYNC_FILE_RANGE +#define DEFAULT_BACKEND_FLUSH_AFTER 0 /* never enabled by default */ +#define DEFAULT_BGWRITER_FLUSH_AFTER 64 +#define DEFAULT_CHECKPOINT_FLUSH_AFTER 32 +#else +#define DEFAULT_BACKEND_FLUSH_AFTER 0 +#define DEFAULT_BGWRITER_FLUSH_AFTER 0 +#define DEFAULT_CHECKPOINT_FLUSH_AFTER 0 +#endif +/* upper limit for all three variables */ +#define WRITEBACK_MAX_PENDING_FLUSHES 256 + +/* + * USE_SSL code should be compiled only when compiling with an SSL + * implementation. (Currently, only OpenSSL is supported, but we might add + * more implementations in the future.) + */ +#ifdef USE_OPENSSL +#define USE_SSL +#endif + +/* + * This is the default directory in which AF_UNIX socket files are + * placed. Caution: changing this risks breaking your existing client + * applications, which are likely to continue to look in the old + * directory. But if you just hate the idea of sockets in /tmp, + * here's where to twiddle it. You can also override this at runtime + * with the postmaster's -k switch. + */ +#define DEFAULT_PGSOCKET_DIR "/tmp" + +/* + * This is the default event source for Windows event log. + */ +#define DEFAULT_EVENT_SOURCE "PostgreSQL" + +/* + * The random() function is expected to yield values between 0 and + * MAX_RANDOM_VALUE. Currently, all known implementations yield + * 0..2^31-1, so we just hardwire this constant. We could do a + * configure test if it proves to be necessary. CAUTION: Think not to + * replace this with RAND_MAX. RAND_MAX defines the maximum value of + * the older rand() function, which is often different from --- and + * considerably inferior to --- random(). + */ +#define MAX_RANDOM_VALUE PG_INT32_MAX + +/* + * On PPC machines, decide whether to use the mutex hint bit in LWARX + * instructions. Setting the hint bit will slightly improve spinlock + * performance on POWER6 and later machines, but does nothing before that, + * and will result in illegal-instruction failures on some pre-POWER4 + * machines. By default we use the hint bit when building for 64-bit PPC, + * which should be safe in nearly all cases. You might want to override + * this if you are building 32-bit code for a known-recent PPC machine. + */ +#ifdef HAVE_PPC_LWARX_MUTEX_HINT /* must have assembler support in any case */ +#if defined(__ppc64__) || defined(__powerpc64__) +#define USE_PPC_LWARX_MUTEX_HINT +#endif +#endif + +/* + * On PPC machines, decide whether to use LWSYNC instructions in place of + * ISYNC and SYNC. This provides slightly better performance, but will + * result in illegal-instruction failures on some pre-POWER4 machines. + * By default we use LWSYNC when building for 64-bit PPC, which should be + * safe in nearly all cases. + */ +#if defined(__ppc64__) || defined(__powerpc64__) +#define USE_PPC_LWSYNC +#endif + +/* + * Assumed cache line size. This doesn't affect correctness, but can be used + * for low-level optimizations. Currently, this is used to pad some data + * structures in xlog.c, to ensure that highly-contended fields are on + * different cache lines. Too small a value can hurt performance due to false + * sharing, while the only downside of too large a value is a few bytes of + * wasted memory. The default is 128, which should be large enough for all + * supported platforms. + */ +#define PG_CACHE_LINE_SIZE 128 + +/* + *------------------------------------------------------------------------ + * The following symbols are for enabling debugging code, not for + * controlling user-visible features or resource limits. + *------------------------------------------------------------------------ + */ + +/* + * Include Valgrind "client requests", mostly in the memory allocator, so + * Valgrind understands PostgreSQL memory contexts. This permits detecting + * memory errors that Valgrind would not detect on a vanilla build. See also + * src/tools/valgrind.supp. "make installcheck" runs 20-30x longer under + * Valgrind. Note that USE_VALGRIND slowed older versions of Valgrind by an + * additional order of magnitude; Valgrind 3.8.1 does not have this problem. + * The client requests fall in hot code paths, so USE_VALGRIND also slows + * native execution by a few percentage points. + * + * You should normally use MEMORY_CONTEXT_CHECKING with USE_VALGRIND; + * instrumentation of repalloc() is inferior without it. + */ +/* #define USE_VALGRIND */ + +/* + * Define this to cause pfree()'d memory to be cleared immediately, to + * facilitate catching bugs that refer to already-freed values. + * Right now, this gets defined automatically if --enable-cassert. + */ +#ifdef USE_ASSERT_CHECKING +#define CLOBBER_FREED_MEMORY +#endif + +/* + * Define this to check memory allocation errors (scribbling on more + * bytes than were allocated). Right now, this gets defined + * automatically if --enable-cassert or USE_VALGRIND. + */ +#if defined(USE_ASSERT_CHECKING) || defined(USE_VALGRIND) +#define MEMORY_CONTEXT_CHECKING +#endif + +/* + * Define this to cause palloc()'d memory to be filled with random data, to + * facilitate catching code that depends on the contents of uninitialized + * memory. Caution: this is horrendously expensive. + */ +/* #define RANDOMIZE_ALLOCATED_MEMORY */ + +/* + * Define this to force all parse and plan trees to be passed through + * copyObject(), to facilitate catching errors and omissions in + * copyObject(). + */ +/* #define COPY_PARSE_PLAN_TREES */ + +/* + * Define this to force all raw parse trees for DML statements to be scanned + * by raw_expression_tree_walker(), to facilitate catching errors and + * omissions in that function. + */ +/* #define RAW_EXPRESSION_COVERAGE_TEST */ + +/* + * Enable debugging print statements for lock-related operations. + */ +/* #define LOCK_DEBUG */ + +/* + * Enable debugging print statements for WAL-related operations; see + * also the wal_debug GUC var. + */ +/* #define WAL_DEBUG */ + +/* + * Enable tracing of resource consumption during sort operations; + * see also the trace_sort GUC var. For 8.1 this is enabled by default. + */ +#define TRACE_SORT 1 + +/* + * Enable tracing of syncscan operations (see also the trace_syncscan GUC var). + */ +/* #define TRACE_SYNCSCAN */ + +/* + * Other debug #defines (documentation, anyone?) + */ +/* #define HEAPDEBUGALL */ +/* #define ACLDEBUG */ diff --git a/libpq/postgresql/pg_config_paths.h b/libpq/postgresql/pg_config_paths.h new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/libpq/postgresql/pg_config_paths.h @@ -0,0 +1 @@ + diff --git a/libpq/postgresql/port.h b/libpq/postgresql/port.h new file mode 100644 index 0000000..a61e59c --- /dev/null +++ b/libpq/postgresql/port.h @@ -0,0 +1,477 @@ +/*------------------------------------------------------------------------- + * + * port.h + * Header for src/port/ compatibility functions. + * + * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/port.h + * + *------------------------------------------------------------------------- + */ +#ifndef PG_PORT_H +#define PG_PORT_H + +#include +#include +#include + +/* socket has a different definition on WIN32 */ +#ifndef WIN32 +typedef int pgsocket; + +#define PGINVALID_SOCKET (-1) +#else +typedef SOCKET pgsocket; + +#define PGINVALID_SOCKET INVALID_SOCKET +#endif + +/* non-blocking */ +extern bool pg_set_noblock(pgsocket sock); +extern bool pg_set_block(pgsocket sock); + +/* Portable path handling for Unix/Win32 (in path.c) */ + +extern bool has_drive_prefix(const char *filename); +extern char *first_dir_separator(const char *filename); +extern char *last_dir_separator(const char *filename); +extern char *first_path_var_separator(const char *pathlist); +extern void join_path_components(char *ret_path, + const char *head, const char *tail); +extern void canonicalize_path(char *path); +extern void make_native_path(char *path); +extern void cleanup_path(char *path); +extern bool path_contains_parent_reference(const char *path); +extern bool path_is_relative_and_below_cwd(const char *path); +extern bool path_is_prefix_of_path(const char *path1, const char *path2); +extern char *make_absolute_path(const char *path); +extern const char *get_progname(const char *argv0); +extern void get_share_path(const char *my_exec_path, char *ret_path); +extern void get_etc_path(const char *my_exec_path, char *ret_path); +extern void get_include_path(const char *my_exec_path, char *ret_path); +extern void get_pkginclude_path(const char *my_exec_path, char *ret_path); +extern void get_includeserver_path(const char *my_exec_path, char *ret_path); +extern void get_lib_path(const char *my_exec_path, char *ret_path); +extern void get_pkglib_path(const char *my_exec_path, char *ret_path); +extern void get_locale_path(const char *my_exec_path, char *ret_path); +extern void get_doc_path(const char *my_exec_path, char *ret_path); +extern void get_html_path(const char *my_exec_path, char *ret_path); +extern void get_man_path(const char *my_exec_path, char *ret_path); +extern bool get_home_path(char *ret_path); +extern void get_parent_directory(char *path); + +/* common/pgfnames.c */ +extern char **pgfnames(const char *path); +extern void pgfnames_cleanup(char **filenames); + +/* + * is_absolute_path + * + * By making this a macro we avoid needing to include path.c in libpq. + */ +#ifndef WIN32 +#define IS_DIR_SEP(ch) ((ch) == '/') + +#define is_absolute_path(filename) \ +( \ + IS_DIR_SEP((filename)[0]) \ +) +#else +#define IS_DIR_SEP(ch) ((ch) == '/' || (ch) == '\\') + +/* See path_is_relative_and_below_cwd() for how we handle 'E:abc'. */ +#define is_absolute_path(filename) \ +( \ + IS_DIR_SEP((filename)[0]) || \ + (isalpha((unsigned char) ((filename)[0])) && (filename)[1] == ':' && \ + IS_DIR_SEP((filename)[2])) \ +) +#endif + +/* Portable locale initialization (in exec.c) */ +extern void set_pglocale_pgservice(const char *argv0, const char *app); + +/* Portable way to find binaries (in exec.c) */ +extern int find_my_exec(const char *argv0, char *retpath); +extern int find_other_exec(const char *argv0, const char *target, + const char *versionstr, char *retpath); + +/* Windows security token manipulation (in exec.c) */ +#ifdef WIN32 +extern BOOL AddUserToTokenDacl(HANDLE hToken); +#endif + + +#if defined(WIN32) || defined(__CYGWIN__) +#define EXE ".exe" +#else +#define EXE "" +#endif + +#if defined(WIN32) && !defined(__CYGWIN__) +#define DEVNULL "nul" +#else +#define DEVNULL "/dev/null" +#endif + +/* Portable delay handling */ +extern void pg_usleep(long microsec); + +/* Portable SQL-like case-independent comparisons and conversions */ +extern int pg_strcasecmp(const char *s1, const char *s2); +extern int pg_strncasecmp(const char *s1, const char *s2, size_t n); +extern unsigned char pg_toupper(unsigned char ch); +extern unsigned char pg_tolower(unsigned char ch); +extern unsigned char pg_ascii_toupper(unsigned char ch); +extern unsigned char pg_ascii_tolower(unsigned char ch); + +#ifdef USE_REPL_SNPRINTF + +/* + * Versions of libintl >= 0.13 try to replace printf() and friends with + * macros to their own versions that understand the %$ format. We do the + * same, so disable their macros, if they exist. + */ +#ifdef vsnprintf +#undef vsnprintf +#endif +#ifdef snprintf +#undef snprintf +#endif +#ifdef sprintf +#undef sprintf +#endif +#ifdef vfprintf +#undef vfprintf +#endif +#ifdef fprintf +#undef fprintf +#endif +#ifdef printf +#undef printf +#endif + +extern int pg_vsnprintf(char *str, size_t count, const char *fmt, va_list args); +extern int pg_snprintf(char *str, size_t count, const char *fmt,...) pg_attribute_printf(3, 4); +extern int pg_sprintf(char *str, const char *fmt,...) pg_attribute_printf(2, 3); +extern int pg_vfprintf(FILE *stream, const char *fmt, va_list args); +extern int pg_fprintf(FILE *stream, const char *fmt,...) pg_attribute_printf(2, 3); +extern int pg_printf(const char *fmt,...) pg_attribute_printf(1, 2); + +/* + * The GCC-specific code below prevents the pg_attribute_printf above from + * being replaced, and this is required because gcc doesn't know anything + * about pg_printf. + */ +#ifdef __GNUC__ +#define vsnprintf(...) pg_vsnprintf(__VA_ARGS__) +#define snprintf(...) pg_snprintf(__VA_ARGS__) +#define sprintf(...) pg_sprintf(__VA_ARGS__) +#define vfprintf(...) pg_vfprintf(__VA_ARGS__) +#define fprintf(...) pg_fprintf(__VA_ARGS__) +#define printf(...) pg_printf(__VA_ARGS__) +#else +#define vsnprintf pg_vsnprintf +#define snprintf pg_snprintf +#define sprintf pg_sprintf +#define vfprintf pg_vfprintf +#define fprintf pg_fprintf +#define printf pg_printf +#endif +#endif /* USE_REPL_SNPRINTF */ + +#if defined(WIN32) +/* + * Versions of libintl >= 0.18? try to replace setlocale() with a macro + * to their own versions. Remove the macro, if it exists, because it + * ends up calling the wrong version when the backend and libintl use + * different versions of msvcrt. + */ +#if defined(setlocale) +#undef setlocale +#endif + +/* + * Define our own wrapper macro around setlocale() to work around bugs in + * Windows' native setlocale() function. + */ +extern char *pgwin32_setlocale(int category, const char *locale); + +#define setlocale(a,b) pgwin32_setlocale(a,b) +#endif /* WIN32 */ + +/* Portable prompt handling */ +extern char *simple_prompt(const char *prompt, int maxlen, bool echo); + +#ifdef WIN32 +#define PG_SIGNAL_COUNT 32 +#define kill(pid,sig) pgkill(pid,sig) +extern int pgkill(int pid, int sig); +#endif + +extern int pclose_check(FILE *stream); + +/* Global variable holding time zone information. */ +#if defined(WIN32) || defined(__CYGWIN__) +#define TIMEZONE_GLOBAL _timezone +#define TZNAME_GLOBAL _tzname +#else +#define TIMEZONE_GLOBAL timezone +#define TZNAME_GLOBAL tzname +#endif + +#if defined(WIN32) || defined(__CYGWIN__) +/* + * Win32 doesn't have reliable rename/unlink during concurrent access. + */ +extern int pgrename(const char *from, const char *to); +extern int pgunlink(const char *path); + +/* Include this first so later includes don't see these defines */ +#ifdef WIN32_ONLY_COMPILER +#include +#endif + +#define rename(from, to) pgrename(from, to) +#define unlink(path) pgunlink(path) +#endif /* defined(WIN32) || defined(__CYGWIN__) */ + +/* + * Win32 also doesn't have symlinks, but we can emulate them with + * junction points on newer Win32 versions. + * + * Cygwin has its own symlinks which work on Win95/98/ME where + * junction points don't, so use those instead. We have no way of + * knowing what type of system Cygwin binaries will be run on. + * Note: Some CYGWIN includes might #define WIN32. + */ +#if defined(WIN32) && !defined(__CYGWIN__) +extern int pgsymlink(const char *oldpath, const char *newpath); +extern int pgreadlink(const char *path, char *buf, size_t size); +extern bool pgwin32_is_junction(char *path); + +#define symlink(oldpath, newpath) pgsymlink(oldpath, newpath) +#define readlink(path, buf, size) pgreadlink(path, buf, size) +#endif + +extern bool rmtree(const char *path, bool rmtopdir); + +/* + * stat() is not guaranteed to set the st_size field on win32, so we + * redefine it to our own implementation that is. + * + * We must pull in sys/stat.h here so the system header definition + * goes in first, and we redefine that, and not the other way around. + * + * Some frontends don't need the size from stat, so if UNSAFE_STAT_OK + * is defined we don't bother with this. + */ +#if defined(WIN32) && !defined(__CYGWIN__) && !defined(UNSAFE_STAT_OK) +#include +extern int pgwin32_safestat(const char *path, struct stat * buf); + +#define stat(a,b) pgwin32_safestat(a,b) +#endif + +#if defined(WIN32) && !defined(__CYGWIN__) + +/* + * open() and fopen() replacements to allow deletion of open files and + * passing of other special options. + */ +#define O_DIRECT 0x80000000 +extern int pgwin32_open(const char *, int,...); +extern FILE *pgwin32_fopen(const char *, const char *); + +#ifndef FRONTEND +#define open(a,b,c) pgwin32_open(a,b,c) +#define fopen(a,b) pgwin32_fopen(a,b) +#endif + +/* + * Mingw-w64 headers #define popen and pclose to _popen and _pclose. We want + * to use our popen wrapper, rather than plain _popen, so override that. For + * consistency, use our version of pclose, too. + */ +#ifdef popen +#undef popen +#endif +#ifdef pclose +#undef pclose +#endif + +/* + * system() and popen() replacements to enclose the command in an extra + * pair of quotes. + */ +extern int pgwin32_system(const char *command); +extern FILE *pgwin32_popen(const char *command, const char *type); + +#define system(a) pgwin32_system(a) +#define popen(a,b) pgwin32_popen(a,b) +#define pclose(a) _pclose(a) + +/* New versions of MingW have gettimeofday, old mingw and msvc don't */ +#ifndef HAVE_GETTIMEOFDAY +/* Last parameter not used */ +extern int gettimeofday(struct timeval * tp, struct timezone * tzp); +#endif +#else /* !WIN32 */ + +/* + * Win32 requires a special close for sockets and pipes, while on Unix + * close() does them all. + */ +#define closesocket close +#endif /* WIN32 */ + +/* + * On Windows, setvbuf() does not support _IOLBF mode, and interprets that + * as _IOFBF. To add insult to injury, setvbuf(file, NULL, _IOFBF, 0) + * crashes outright if "parameter validation" is enabled. Therefore, in + * places where we'd like to select line-buffered mode, we fall back to + * unbuffered mode instead on Windows. Always use PG_IOLBF not _IOLBF + * directly in order to implement this behavior. + */ +#ifndef WIN32 +#define PG_IOLBF _IOLBF +#else +#define PG_IOLBF _IONBF +#endif + +/* + * Default "extern" declarations or macro substitutes for library routines. + * When necessary, these routines are provided by files in src/port/. + */ +#ifndef HAVE_CRYPT +extern char *crypt(const char *key, const char *setting); +#endif + +/* WIN32 handled in port/win32.h */ +#ifndef WIN32 +#define pgoff_t off_t +#ifdef __NetBSD__ +extern int fseeko(FILE *stream, off_t offset, int whence); +extern off_t ftello(FILE *stream); +#endif +#endif + +extern double pg_erand48(unsigned short xseed[3]); +extern long pg_lrand48(void); +extern void pg_srand48(long seed); + +#ifndef HAVE_FLS +extern int fls(int mask); +#endif + +#ifndef HAVE_FSEEKO +#define fseeko(a, b, c) fseek(a, b, c) +#define ftello(a) ftell(a) +#endif + +#if !defined(HAVE_GETPEEREID) && !defined(WIN32) +extern int getpeereid(int sock, uid_t *uid, gid_t *gid); +#endif + +#ifndef HAVE_ISINF +extern int isinf(double x); +#endif + +#ifndef HAVE_MKDTEMP +extern char *mkdtemp(char *path); +#endif + +#ifndef HAVE_RINT +extern double rint(double x); +#endif + +#ifndef HAVE_INET_ATON +#include +#include +extern int inet_aton(const char *cp, struct in_addr * addr); +#endif + +#if !HAVE_DECL_STRLCAT +extern size_t strlcat(char *dst, const char *src, size_t siz); +#endif + +#if !HAVE_DECL_STRLCPY +extern size_t strlcpy(char *dst, const char *src, size_t siz); +#endif + +#if !defined(HAVE_RANDOM) && !defined(__BORLANDC__) +extern long random(void); +#endif + +#ifndef HAVE_UNSETENV +extern void unsetenv(const char *name); +#endif + +#ifndef HAVE_SRANDOM +extern void srandom(unsigned int seed); +#endif + +#ifndef HAVE_SSL_GET_CURRENT_COMPRESSION +#define SSL_get_current_compression(x) 0 +#endif + +/* thread.h */ +extern char *pqStrerror(int errnum, char *strerrbuf, size_t buflen); + +#ifndef WIN32 +extern int pqGetpwuid(uid_t uid, struct passwd * resultbuf, char *buffer, + size_t buflen, struct passwd ** result); +#endif + +extern int pqGethostbyname(const char *name, + struct hostent * resultbuf, + char *buffer, size_t buflen, + struct hostent ** result, + int *herrno); + +extern void pg_qsort(void *base, size_t nel, size_t elsize, + int (*cmp) (const void *, const void *)); +extern int pg_qsort_strcmp(const void *a, const void *b); + +#define qsort(a,b,c,d) pg_qsort(a,b,c,d) + +typedef int (*qsort_arg_comparator) (const void *a, const void *b, void *arg); + +extern void qsort_arg(void *base, size_t nel, size_t elsize, + qsort_arg_comparator cmp, void *arg); + +/* port/chklocale.c */ +extern int pg_get_encoding_from_locale(const char *ctype, bool write_message); + +#if defined(WIN32) && !defined(FRONTEND) +extern int pg_codepage_to_encoding(UINT cp); +#endif + +/* port/inet_net_ntop.c */ +extern char *inet_net_ntop(int af, const void *src, int bits, + char *dst, size_t size); + +/* port/pgcheckdir.c */ +extern int pg_check_dir(const char *dir); + +/* port/pgmkdirp.c */ +extern int pg_mkdir_p(char *path, int omode); + +/* port/pqsignal.c */ +typedef void (*pqsigfunc) (int signo); +extern pqsigfunc pqsignal(int signo, pqsigfunc func); +#ifndef WIN32 +extern pqsigfunc pqsignal_no_restart(int signo, pqsigfunc func); +#else +#define pqsignal_no_restart(signo, func) pqsignal(signo, func) +#endif + +/* port/quotes.c */ +extern char *escape_single_quotes_ascii(const char *src); + +/* port/wait_error.c */ +extern char *wait_result_to_str(int exit_status); + +#endif /* PG_PORT_H */ diff --git a/libpq/postgresql/port/bsd/pg_config_os.h b/libpq/postgresql/port/bsd/pg_config_os.h new file mode 100644 index 0000000..e69de29 diff --git a/libpq/postgresql/port/darwin/pg_config_os.h b/libpq/postgresql/port/darwin/pg_config_os.h new file mode 100644 index 0000000..29c4b91 --- /dev/null +++ b/libpq/postgresql/port/darwin/pg_config_os.h @@ -0,0 +1,8 @@ +/* src/include/port/darwin.h */ + +#define __darwin__ 1 + +#if HAVE_DECL_F_FULLFSYNC /* not present before OS X 10.3 */ +#define HAVE_FSYNC_WRITETHROUGH + +#endif diff --git a/libpq/postgresql/port/linux/pg_config_os.h b/libpq/postgresql/port/linux/pg_config_os.h new file mode 100644 index 0000000..7a6e46c --- /dev/null +++ b/libpq/postgresql/port/linux/pg_config_os.h @@ -0,0 +1,22 @@ +/* src/include/port/linux.h */ + +/* + * As of July 2007, all known versions of the Linux kernel will sometimes + * return EIDRM for a shmctl() operation when EINVAL is correct (it happens + * when the low-order 15 bits of the supplied shm ID match the slot number + * assigned to a newer shmem segment). We deal with this by assuming that + * EIDRM means EINVAL in PGSharedMemoryIsInUse(). This is reasonably safe + * since in fact Linux has no excuse for ever returning EIDRM; it doesn't + * track removed segments in a way that would allow distinguishing them from + * private ones. But someday that code might get upgraded, and we'd have + * to have a kernel version test here. + */ +#define HAVE_LINUX_EIDRM_BUG + +/* + * Set the default wal_sync_method to fdatasync. With recent Linux versions, + * xlogdefs.h's normal rules will prefer open_datasync, which (a) doesn't + * perform better and (b) causes outright failures on ext4 data=journal + * filesystems, because those don't support O_DIRECT. + */ +#define PLATFORM_DEFAULT_SYNC_METHOD SYNC_METHOD_FDATASYNC diff --git a/libpq/postgresql/port/win32/arpa/inet.h b/libpq/postgresql/port/win32/arpa/inet.h new file mode 100644 index 0000000..ad18031 --- /dev/null +++ b/libpq/postgresql/port/win32/arpa/inet.h @@ -0,0 +1,3 @@ +/* src/include/port/win32/arpa/inet.h */ + +#include diff --git a/libpq/postgresql/port/win32/netdb.h b/libpq/postgresql/port/win32/netdb.h new file mode 100644 index 0000000..ad0627e --- /dev/null +++ b/libpq/postgresql/port/win32/netdb.h @@ -0,0 +1 @@ +/* src/include/port/win32/netdb.h */ diff --git a/libpq/postgresql/port/win32/netinet/in.h b/libpq/postgresql/port/win32/netinet/in.h new file mode 100644 index 0000000..a4e22f8 --- /dev/null +++ b/libpq/postgresql/port/win32/netinet/in.h @@ -0,0 +1,3 @@ +/* src/include/port/win32/netinet/in.h */ + +#include diff --git a/libpq/postgresql/port/win32/pg_config_os.h b/libpq/postgresql/port/win32/pg_config_os.h new file mode 100644 index 0000000..4453c90 --- /dev/null +++ b/libpq/postgresql/port/win32/pg_config_os.h @@ -0,0 +1,486 @@ +/* src/include/port/win32.h */ + +#if defined(_MSC_VER) || defined(__BORLANDC__) +#define WIN32_ONLY_COMPILER +#endif + +/* + * Make sure _WIN32_WINNT has the minimum required value. + * Leave a higher value in place. When building with at least Visual + * Studio 2015 the minimum requirement is Windows Vista (0x0600) to + * get support for GetLocaleInfoEx() with locales. For everything else + * the minimum version is Windows XP (0x0501). + * Also for VS2015, add a define that stops compiler complaints about + * using the old Winsock API. + */ +#if defined(_MSC_VER) && _MSC_VER >= 1900 +#define _WINSOCK_DEPRECATED_NO_WARNINGS +#define MIN_WINNT 0x0600 +#else +#define MIN_WINNT 0x0501 +#endif + +#if defined(_WIN32_WINNT) && _WIN32_WINNT < MIN_WINNT +#undef _WIN32_WINNT +#endif + +#ifndef _WIN32_WINNT +#define _WIN32_WINNT MIN_WINNT +#endif + +/* + * Always build with SSPI support. Keep it as a #define in case + * we want a switch to disable it sometime in the future. + */ +#ifndef __BORLANDC__ +#define ENABLE_SSPI 1 +#endif + +/* undefine and redefine after #include */ +#undef mkdir + +#undef ERROR + +/* + * The Mingw64 headers choke if this is already defined - they + * define it themselves. + */ +#if !defined(__MINGW64_VERSION_MAJOR) || defined(WIN32_ONLY_COMPILER) +#define _WINSOCKAPI_ +#endif +#include +#include +#include +#undef small +#include +#include +#include +#include +#ifndef __BORLANDC__ +#include /* for non-unicode version */ +#endif +#undef near + +/* Must be here to avoid conflicting with prototype in windows.h */ +#define mkdir(a,b) mkdir(a) + +#define ftruncate(a,b) chsize(a,b) + +/* Windows doesn't have fsync() as such, use _commit() */ +#define fsync(fd) _commit(fd) + +/* + * For historical reasons, we allow setting wal_sync_method to + * fsync_writethrough on Windows, even though it's really identical to fsync + * (both code paths wind up at _commit()). + */ +#define HAVE_FSYNC_WRITETHROUGH +#define FSYNC_WRITETHROUGH_IS_FSYNC + +#define USES_WINSOCK + +/* defines for dynamic linking on Win32 platform + * + * http://support.microsoft.com/kb/132044 + * http://msdn.microsoft.com/en-us/library/8fskxacy(v=vs.80).aspx + * http://msdn.microsoft.com/en-us/library/a90k134d(v=vs.80).aspx + */ + +#if defined(WIN32) || defined(__CYGWIN__) + +#ifdef BUILDING_DLL +#define PGDLLIMPORT __declspec (dllexport) +#else /* not BUILDING_DLL */ +#define PGDLLIMPORT __declspec (dllimport) +#endif + +#ifdef _MSC_VER +#define PGDLLEXPORT __declspec (dllexport) +#else +#define PGDLLEXPORT +#endif +#else /* not CYGWIN, not MSVC, not MingW */ +#define PGDLLIMPORT +#define PGDLLEXPORT +#endif + + +/* + * IPC defines + */ +#undef HAVE_UNION_SEMUN +#define HAVE_UNION_SEMUN 1 + +#define IPC_RMID 256 +#define IPC_CREAT 512 +#define IPC_EXCL 1024 +#define IPC_PRIVATE 234564 +#define IPC_NOWAIT 2048 +#define IPC_STAT 4096 + +#define EACCESS 2048 +#ifndef EIDRM +#define EIDRM 4096 +#endif + +#define SETALL 8192 +#define GETNCNT 16384 +#define GETVAL 65536 +#define SETVAL 131072 +#define GETPID 262144 + + +/* + * Signal stuff + * + * For WIN32, there is no wait() call so there are no wait() macros + * to interpret the return value of system(). Instead, system() + * return values < 0x100 are used for exit() termination, and higher + * values are used to indicated non-exit() termination, which is + * similar to a unix-style signal exit (think SIGSEGV == + * STATUS_ACCESS_VIOLATION). Return values are broken up into groups: + * + * http://msdn2.microsoft.com/en-gb/library/aa489609.aspx + * + * NT_SUCCESS 0 - 0x3FFFFFFF + * NT_INFORMATION 0x40000000 - 0x7FFFFFFF + * NT_WARNING 0x80000000 - 0xBFFFFFFF + * NT_ERROR 0xC0000000 - 0xFFFFFFFF + * + * Effectively, we don't care on the severity of the return value from + * system(), we just need to know if it was because of exit() or generated + * by the system, and it seems values >= 0x100 are system-generated. + * See this URL for a list of WIN32 STATUS_* values: + * + * Wine (URL used in our error messages) - + * http://source.winehq.org/source/include/ntstatus.h + * Descriptions - http://www.comp.nus.edu.sg/~wuyongzh/my_doc/ntstatus.txt + * MS SDK - http://www.nologs.com/ntstatus.html + * + * It seems the exception lists are in both ntstatus.h and winnt.h, but + * ntstatus.h has a more comprehensive list, and it only contains + * exception values, rather than winnt, which contains lots of other + * things: + * + * http://www.microsoft.com/msj/0197/exception/exception.aspx + * + * The ExceptionCode parameter is the number that the operating system + * assigned to the exception. You can see a list of various exception codes + * in WINNT.H by searching for #defines that start with "STATUS_". For + * example, the code for the all-too-familiar STATUS_ACCESS_VIOLATION is + * 0xC0000005. A more complete set of exception codes can be found in + * NTSTATUS.H from the Windows NT DDK. + * + * Some day we might want to print descriptions for the most common + * exceptions, rather than printing an include file name. We could use + * RtlNtStatusToDosError() and pass to FormatMessage(), which can print + * the text of error values, but MinGW does not support + * RtlNtStatusToDosError(). + */ +#define WIFEXITED(w) (((w) & 0XFFFFFF00) == 0) +#define WIFSIGNALED(w) (!WIFEXITED(w)) +#define WEXITSTATUS(w) (w) +#define WTERMSIG(w) (w) + +#define sigmask(sig) ( 1 << ((sig)-1) ) + +/* Signal function return values */ +#undef SIG_DFL +#undef SIG_ERR +#undef SIG_IGN +#define SIG_DFL ((pqsigfunc)0) +#define SIG_ERR ((pqsigfunc)-1) +#define SIG_IGN ((pqsigfunc)1) + +/* Some extra signals */ +#define SIGHUP 1 +#define SIGQUIT 3 +#define SIGTRAP 5 +#define SIGABRT 22 /* Set to match W32 value -- not UNIX value */ +#define SIGKILL 9 +#define SIGPIPE 13 +#define SIGALRM 14 +#define SIGSTOP 17 +#define SIGTSTP 18 +#define SIGCONT 19 +#define SIGCHLD 20 +#define SIGTTIN 21 +#define SIGTTOU 22 /* Same as SIGABRT -- no problem, I hope */ +#define SIGWINCH 28 +#ifndef __BORLANDC__ +#define SIGUSR1 30 +#define SIGUSR2 31 +#endif + +/* + * New versions of mingw have gettimeofday() and also declare + * struct timezone to support it. + */ +#ifndef HAVE_GETTIMEOFDAY +struct timezone +{ + int tz_minuteswest; /* Minutes west of GMT. */ + int tz_dsttime; /* Nonzero if DST is ever in effect. */ +}; +#endif + +/* for setitimer in backend/port/win32/timer.c */ +#define ITIMER_REAL 0 +struct itimerval +{ + struct timeval it_interval; + struct timeval it_value; +}; + +int setitimer(int which, const struct itimerval * value, struct itimerval * ovalue); + +/* + * WIN32 does not provide 64-bit off_t, but does provide the functions operating + * with 64-bit offsets. + */ +#define pgoff_t __int64 +#ifdef WIN32_ONLY_COMPILER +#define fseeko(stream, offset, origin) _fseeki64(stream, offset, origin) +#define ftello(stream) _ftelli64(stream) +#else +#ifndef fseeko +#define fseeko(stream, offset, origin) fseeko64(stream, offset, origin) +#endif +#ifndef ftello +#define ftello(stream) ftello64(stream) +#endif +#endif + +/* + * Supplement to . + * + * Perl already has typedefs for uid_t and gid_t. + */ +#ifndef PLPERL_HAVE_UID_GID +typedef int uid_t; +typedef int gid_t; +#endif +typedef long key_t; + +#ifdef WIN32_ONLY_COMPILER +typedef int pid_t; +#endif + +/* + * Supplement to . + */ +#define lstat(path, sb) stat((path), (sb)) + +/* + * Supplement to . + * This is the same value as _O_NOINHERIT in the MS header file. This is + * to ensure that we don't collide with a future definition. It means + * we cannot use _O_NOINHERIT ourselves. + */ +#define O_DSYNC 0x0080 + +/* + * Supplement to . + * + * We redefine network-related Berkeley error symbols as the corresponding WSA + * constants. This allows elog.c to recognize them as being in the Winsock + * error code range and pass them off to pgwin32_socket_strerror(), since + * Windows' version of plain strerror() won't cope. Note that this will break + * if these names are used for anything else besides Windows Sockets errors. + * See TranslateSocketError() when changing this list. + */ +#undef EAGAIN +#define EAGAIN WSAEWOULDBLOCK +#undef EINTR +#define EINTR WSAEINTR +#undef EMSGSIZE +#define EMSGSIZE WSAEMSGSIZE +#undef EAFNOSUPPORT +#define EAFNOSUPPORT WSAEAFNOSUPPORT +#undef EWOULDBLOCK +#define EWOULDBLOCK WSAEWOULDBLOCK +#undef ECONNABORTED +#define ECONNABORTED WSAECONNABORTED +#undef ECONNRESET +#define ECONNRESET WSAECONNRESET +#undef EINPROGRESS +#define EINPROGRESS WSAEINPROGRESS +#undef EISCONN +#define EISCONN WSAEISCONN +#undef ENOBUFS +#define ENOBUFS WSAENOBUFS +#undef EPROTONOSUPPORT +#define EPROTONOSUPPORT WSAEPROTONOSUPPORT +#undef ECONNREFUSED +#define ECONNREFUSED WSAECONNREFUSED +#undef ENOTSOCK +#define ENOTSOCK WSAENOTSOCK +#undef EOPNOTSUPP +#define EOPNOTSUPP WSAEOPNOTSUPP +#undef EADDRINUSE +#define EADDRINUSE WSAEADDRINUSE +#undef EADDRNOTAVAIL +#define EADDRNOTAVAIL WSAEADDRNOTAVAIL +#undef EHOSTUNREACH +#define EHOSTUNREACH WSAEHOSTUNREACH +#undef ENOTCONN +#define ENOTCONN WSAENOTCONN + +/* + * Extended locale functions with gratuitous underscore prefixes. + * (These APIs are nevertheless fully documented by Microsoft.) + */ +#define locale_t _locale_t +#define tolower_l _tolower_l +#define toupper_l _toupper_l +#define towlower_l _towlower_l +#define towupper_l _towupper_l +#define isdigit_l _isdigit_l +#define iswdigit_l _iswdigit_l +#define isalpha_l _isalpha_l +#define iswalpha_l _iswalpha_l +#define isalnum_l _isalnum_l +#define iswalnum_l _iswalnum_l +#define isupper_l _isupper_l +#define iswupper_l _iswupper_l +#define islower_l _islower_l +#define iswlower_l _iswlower_l +#define isgraph_l _isgraph_l +#define iswgraph_l _iswgraph_l +#define isprint_l _isprint_l +#define iswprint_l _iswprint_l +#define ispunct_l _ispunct_l +#define iswpunct_l _iswpunct_l +#define isspace_l _isspace_l +#define iswspace_l _iswspace_l +#define strcoll_l _strcoll_l +#define strxfrm_l _strxfrm_l +#define wcscoll_l _wcscoll_l +#define wcstombs_l _wcstombs_l +#define mbstowcs_l _mbstowcs_l + + +/* In backend/port/win32/signal.c */ +extern PGDLLIMPORT volatile int pg_signal_queue; +extern PGDLLIMPORT int pg_signal_mask; +extern HANDLE pgwin32_signal_event; +extern HANDLE pgwin32_initial_signal_pipe; + +#define UNBLOCKED_SIGNAL_QUEUE() (pg_signal_queue & ~pg_signal_mask) + + +void pgwin32_signal_initialize(void); +HANDLE pgwin32_create_signal_listener(pid_t pid); +void pgwin32_dispatch_queued_signals(void); +void pg_queue_signal(int signum); + +/* In backend/port/win32/socket.c */ +#ifndef FRONTEND +#define socket(af, type, protocol) pgwin32_socket(af, type, protocol) +#define bind(s, addr, addrlen) pgwin32_bind(s, addr, addrlen) +#define listen(s, backlog) pgwin32_listen(s, backlog) +#define accept(s, addr, addrlen) pgwin32_accept(s, addr, addrlen) +#define connect(s, name, namelen) pgwin32_connect(s, name, namelen) +#define select(n, r, w, e, timeout) pgwin32_select(n, r, w, e, timeout) +#define recv(s, buf, len, flags) pgwin32_recv(s, buf, len, flags) +#define send(s, buf, len, flags) pgwin32_send(s, buf, len, flags) + +SOCKET pgwin32_socket(int af, int type, int protocol); +int pgwin32_bind(SOCKET s, struct sockaddr * addr, int addrlen); +int pgwin32_listen(SOCKET s, int backlog); +SOCKET pgwin32_accept(SOCKET s, struct sockaddr * addr, int *addrlen); +int pgwin32_connect(SOCKET s, const struct sockaddr * name, int namelen); +int pgwin32_select(int nfds, fd_set *readfs, fd_set *writefds, fd_set *exceptfds, const struct timeval * timeout); +int pgwin32_recv(SOCKET s, char *buf, int len, int flags); +int pgwin32_send(SOCKET s, const void *buf, int len, int flags); + +const char *pgwin32_socket_strerror(int err); +int pgwin32_waitforsinglesocket(SOCKET s, int what, int timeout); + +extern int pgwin32_noblock; + +#endif + +/* in backend/port/win32_shmem.c */ +extern int pgwin32_ReserveSharedMemoryRegion(HANDLE); + +/* in backend/port/win32/crashdump.c */ +extern void pgwin32_install_crashdump_handler(void); + +/* in port/win32error.c */ +extern void _dosmaperr(unsigned long); + +/* in port/win32env.c */ +extern int pgwin32_putenv(const char *); +extern void pgwin32_unsetenv(const char *); + +/* in port/win32security.c */ +extern int pgwin32_is_service(void); +extern int pgwin32_is_admin(void); + +#define putenv(x) pgwin32_putenv(x) +#define unsetenv(x) pgwin32_unsetenv(x) + +/* Things that exist in MingW headers, but need to be added to MSVC & BCC */ +#ifdef WIN32_ONLY_COMPILER + +#ifndef _WIN64 +typedef long ssize_t; +#else +typedef __int64 ssize_t; +#endif + +#ifndef __BORLANDC__ +typedef unsigned short mode_t; + +#define S_IRUSR _S_IREAD +#define S_IWUSR _S_IWRITE +#define S_IXUSR _S_IEXEC +#define S_IRWXU (S_IRUSR | S_IWUSR | S_IXUSR) +/* see also S_IRGRP etc below */ +#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) +#endif /* __BORLANDC__ */ + +#define F_OK 0 +#define W_OK 2 +#define R_OK 4 + +#if (_MSC_VER < 1800) +#define isinf(x) ((_fpclass(x) == _FPCLASS_PINF) || (_fpclass(x) == _FPCLASS_NINF)) +#define isnan(x) _isnan(x) +#endif + +/* Pulled from Makefile.port in mingw */ +#define DLSUFFIX ".dll" + +#ifdef __BORLANDC__ + +/* for port/dirent.c */ +#ifndef INVALID_FILE_ATTRIBUTES +#define INVALID_FILE_ATTRIBUTES ((DWORD) -1) +#endif + +/* for port/open.c */ +#ifndef O_RANDOM +#define O_RANDOM 0x0010 /* File access is primarily random */ +#define O_SEQUENTIAL 0x0020 /* File access is primarily sequential */ +#define O_TEMPORARY 0x0040 /* Temporary file bit */ +#define O_SHORT_LIVED 0x1000 /* Temporary storage file, try not to flush */ +#define _O_SHORT_LIVED O_SHORT_LIVED +#endif /* ifndef O_RANDOM */ +#endif /* __BORLANDC__ */ +#endif /* WIN32_ONLY_COMPILER */ + +/* These aren't provided by either MingW or MSVC */ +#ifndef __BORLANDC__ +#define S_IRGRP 0 +#define S_IWGRP 0 +#define S_IXGRP 0 +#define S_IRWXG 0 +#define S_IROTH 0 +#define S_IWOTH 0 +#define S_IXOTH 0 +#define S_IRWXO 0 + +#endif /* __BORLANDC__ */ diff --git a/libpq/postgresql/port/win32/pthread-win32.h b/libpq/postgresql/port/win32/pthread-win32.h new file mode 100644 index 0000000..97ccc17 --- /dev/null +++ b/libpq/postgresql/port/win32/pthread-win32.h @@ -0,0 +1,22 @@ +/* + * src/port/pthread-win32.h + */ +#ifndef __PTHREAD_H +#define __PTHREAD_H + +typedef ULONG pthread_key_t; +typedef CRITICAL_SECTION *pthread_mutex_t; +typedef int pthread_once_t; + +DWORD pthread_self(void); + +void pthread_setspecific(pthread_key_t, void *); +void *pthread_getspecific(pthread_key_t); + +int pthread_mutex_init(pthread_mutex_t *, void *attr); +int pthread_mutex_lock(pthread_mutex_t *); + +/* blocking */ +int pthread_mutex_unlock(pthread_mutex_t *); + +#endif diff --git a/libpq/postgresql/port/win32/pwd.h b/libpq/postgresql/port/win32/pwd.h new file mode 100644 index 0000000..b8c7178 --- /dev/null +++ b/libpq/postgresql/port/win32/pwd.h @@ -0,0 +1,3 @@ +/* + * src/include/port/win32/pwd.h + */ diff --git a/libpq/postgresql/port/win32/sys/socket.h b/libpq/postgresql/port/win32/sys/socket.h new file mode 100644 index 0000000..edaee6a --- /dev/null +++ b/libpq/postgresql/port/win32/sys/socket.h @@ -0,0 +1,33 @@ +/* + * src/include/port/win32/sys/socket.h + */ +#ifndef WIN32_SYS_SOCKET_H +#define WIN32_SYS_SOCKET_H + +/* + * Unfortunately, of VC++ also defines ERROR. + * To avoid the conflict, we include here and undefine ERROR + * immediately. + * + * Note: Don't include directly. It causes compile errors. + */ +#include +#include +#include + +#undef ERROR +#undef small + +/* Restore old ERROR value */ +#ifdef PGERROR +#define ERROR PGERROR +#endif + +/* + * we can't use the windows gai_strerror{AW} functions because + * they are defined inline in the MS header files. So we'll use our + * own + */ +#undef gai_strerror + +#endif /* WIN32_SYS_SOCKET_H */ diff --git a/libpq/postgresql/port/win32/sys/wait.h b/libpq/postgresql/port/win32/sys/wait.h new file mode 100644 index 0000000..eaeb566 --- /dev/null +++ b/libpq/postgresql/port/win32/sys/wait.h @@ -0,0 +1,3 @@ +/* + * src/include/port/win32/sys/wait.h + */ diff --git a/libpq/postgresql/port/win32_msvc/sys/file.h b/libpq/postgresql/port/win32_msvc/sys/file.h new file mode 100644 index 0000000..76be3e7 --- /dev/null +++ b/libpq/postgresql/port/win32_msvc/sys/file.h @@ -0,0 +1 @@ +/* src/include/port/win32_msvc/sys/file.h */ diff --git a/libpq/postgresql/port/win32_msvc/sys/param.h b/libpq/postgresql/port/win32_msvc/sys/param.h new file mode 100644 index 0000000..160df3b --- /dev/null +++ b/libpq/postgresql/port/win32_msvc/sys/param.h @@ -0,0 +1 @@ +/* src/include/port/win32_msvc/sys/param.h */ diff --git a/libpq/postgresql/port/win32_msvc/sys/time.h b/libpq/postgresql/port/win32_msvc/sys/time.h new file mode 100644 index 0000000..9d943ec --- /dev/null +++ b/libpq/postgresql/port/win32_msvc/sys/time.h @@ -0,0 +1 @@ +/* src/include/port/win32_msvc/sys/time.h */ diff --git a/libpq/postgresql/port/win32_msvc/unistd.h b/libpq/postgresql/port/win32_msvc/unistd.h new file mode 100644 index 0000000..b63f477 --- /dev/null +++ b/libpq/postgresql/port/win32_msvc/unistd.h @@ -0,0 +1 @@ +/* src/include/port/win32_msvc/unistd.h */ diff --git a/libpq/postgresql/postgres_ext.h b/libpq/postgresql/postgres_ext.h new file mode 100644 index 0000000..ae2f087 --- /dev/null +++ b/libpq/postgresql/postgres_ext.h @@ -0,0 +1,70 @@ +/*------------------------------------------------------------------------- + * + * postgres_ext.h + * + * This file contains declarations of things that are visible everywhere + * in PostgreSQL *and* are visible to clients of frontend interface libraries. + * For example, the Oid type is part of the API of libpq and other libraries. + * + * Declarations which are specific to a particular interface should + * go in the header file for that interface (such as libpq-fe.h). This + * file is only for fundamental Postgres declarations. + * + * User-written C functions don't count as "external to Postgres." + * Those function much as local modifications to the backend itself, and + * use header files that are otherwise internal to Postgres to interface + * with the backend. + * + * src/include/postgres_ext.h + * + *------------------------------------------------------------------------- + */ + +#ifndef POSTGRES_EXT_H +#define POSTGRES_EXT_H + +#include "pg_config_ext.h" + +/* + * Object ID is a fundamental type in Postgres. + */ +typedef unsigned int Oid; + +#ifdef __cplusplus +#define InvalidOid (Oid(0)) +#else +#define InvalidOid ((Oid) 0) +#endif + +#define OID_MAX UINT_MAX +/* you will need to include to use the above #define */ + +/* Define a signed 64-bit integer type for use in client API declarations. */ +typedef PG_INT64_TYPE pg_int64; + + +/* + * Identifiers of error message fields. Kept here to keep common + * between frontend and backend, and also to export them to libpq + * applications. + */ +#define PG_DIAG_SEVERITY 'S' +#define PG_DIAG_SEVERITY_NONLOCALIZED 'V' +#define PG_DIAG_SQLSTATE 'C' +#define PG_DIAG_MESSAGE_PRIMARY 'M' +#define PG_DIAG_MESSAGE_DETAIL 'D' +#define PG_DIAG_MESSAGE_HINT 'H' +#define PG_DIAG_STATEMENT_POSITION 'P' +#define PG_DIAG_INTERNAL_POSITION 'p' +#define PG_DIAG_INTERNAL_QUERY 'q' +#define PG_DIAG_CONTEXT 'W' +#define PG_DIAG_SCHEMA_NAME 's' +#define PG_DIAG_TABLE_NAME 't' +#define PG_DIAG_COLUMN_NAME 'c' +#define PG_DIAG_DATATYPE_NAME 'd' +#define PG_DIAG_CONSTRAINT_NAME 'n' +#define PG_DIAG_SOURCE_FILE 'F' +#define PG_DIAG_SOURCE_LINE 'L' +#define PG_DIAG_SOURCE_FUNCTION 'R' + +#endif /* POSTGRES_EXT_H */ diff --git a/libpq/postgresql/postgres_fe.h b/libpq/postgresql/postgres_fe.h new file mode 100644 index 0000000..69c0ad8 --- /dev/null +++ b/libpq/postgresql/postgres_fe.h @@ -0,0 +1,29 @@ +/*------------------------------------------------------------------------- + * + * postgres_fe.h + * Primary include file for PostgreSQL client-side .c files + * + * This should be the first file included by PostgreSQL client libraries and + * application programs --- but not by backend modules, which should include + * postgres.h. + * + * + * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1995, Regents of the University of California + * + * src/include/postgres_fe.h + * + *------------------------------------------------------------------------- + */ +#ifndef POSTGRES_FE_H +#define POSTGRES_FE_H + +#ifndef FRONTEND +#define FRONTEND 1 +#endif + +#include "c.h" + +#include "common/fe_memutils.h" + +#endif /* POSTGRES_FE_H */ diff --git a/libpq/pqexpbuffer.c b/libpq/pqexpbuffer.c new file mode 100644 index 0000000..8d82d5c --- /dev/null +++ b/libpq/pqexpbuffer.c @@ -0,0 +1,430 @@ +/*------------------------------------------------------------------------- + * + * pqexpbuffer.c + * + * PQExpBuffer provides an indefinitely-extensible string data type. + * It can be used to buffer either ordinary C strings (null-terminated text) + * or arbitrary binary data. All storage is allocated with malloc(). + * + * This module is essentially the same as the backend's StringInfo data type, + * but it is intended for use in frontend libpq and client applications. + * Thus, it does not rely on palloc() nor elog(), nor psprintf.c which + * will exit() on error. + * + * It does rely on vsnprintf(); if configure finds that libc doesn't provide + * a usable vsnprintf(), then a copy of our own implementation of it will + * be linked into libpq. + * + * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/interfaces/libpq/pqexpbuffer.c + * + *------------------------------------------------------------------------- + */ + +#include "postgres_fe.h" + +#include + +#include "pqexpbuffer.h" + +#ifdef WIN32 +#include "win32.h" +#endif + + +/* All "broken" PQExpBuffers point to this string. */ +static const char oom_buffer[1] = ""; + +static bool appendPQExpBufferVA(PQExpBuffer str, const char *fmt, va_list args) pg_attribute_printf(2, 0); + + +/* + * markPQExpBufferBroken + * + * Put a PQExpBuffer in "broken" state if it isn't already. + */ +static void +markPQExpBufferBroken(PQExpBuffer str) +{ + if (str->data != oom_buffer) + free(str->data); + + /* + * Casting away const here is a bit ugly, but it seems preferable to not + * marking oom_buffer const. We want to do that to encourage the compiler + * to put oom_buffer in read-only storage, so that anyone who tries to + * scribble on a broken PQExpBuffer will get a failure. + */ + str->data = (char *) oom_buffer; + str->len = 0; + str->maxlen = 0; +} + +/* + * createPQExpBuffer + * + * Create an empty 'PQExpBufferData' & return a pointer to it. + */ +PQExpBuffer +createPQExpBuffer(void) +{ + PQExpBuffer res; + + res = (PQExpBuffer) malloc(sizeof(PQExpBufferData)); + if (res != NULL) + initPQExpBuffer(res); + + return res; +} + +/* + * initPQExpBuffer + * + * Initialize a PQExpBufferData struct (with previously undefined contents) + * to describe an empty string. + */ +void +initPQExpBuffer(PQExpBuffer str) +{ + str->data = (char *) malloc(INITIAL_EXPBUFFER_SIZE); + if (str->data == NULL) + { + str->data = (char *) oom_buffer; /* see comment above */ + str->maxlen = 0; + str->len = 0; + } + else + { + str->maxlen = INITIAL_EXPBUFFER_SIZE; + str->len = 0; + str->data[0] = '\0'; + } +} + +/* + * destroyPQExpBuffer(str); + * + * free()s both the data buffer and the PQExpBufferData. + * This is the inverse of createPQExpBuffer(). + */ +void +destroyPQExpBuffer(PQExpBuffer str) +{ + if (str) + { + termPQExpBuffer(str); + free(str); + } +} + +/* + * termPQExpBuffer(str) + * free()s the data buffer but not the PQExpBufferData itself. + * This is the inverse of initPQExpBuffer(). + */ +void +termPQExpBuffer(PQExpBuffer str) +{ + if (str->data != oom_buffer) + free(str->data); + /* just for luck, make the buffer validly empty. */ + str->data = (char *) oom_buffer; /* see comment above */ + str->maxlen = 0; + str->len = 0; +} + +/* + * resetPQExpBuffer + * Reset a PQExpBuffer to empty + * + * Note: if possible, a "broken" PQExpBuffer is returned to normal. + */ +void +resetPQExpBuffer(PQExpBuffer str) +{ + if (str) + { + if (str->data != oom_buffer) + { + str->len = 0; + str->data[0] = '\0'; + } + else + { + /* try to reinitialize to valid state */ + initPQExpBuffer(str); + } + } +} + +/* + * enlargePQExpBuffer + * Make sure there is enough space for 'needed' more bytes in the buffer + * ('needed' does not include the terminating null). + * + * Returns 1 if OK, 0 if failed to enlarge buffer. (In the latter case + * the buffer is left in "broken" state.) + */ +int +enlargePQExpBuffer(PQExpBuffer str, size_t needed) +{ + size_t newlen; + char *newdata; + + if (PQExpBufferBroken(str)) + return 0; /* already failed */ + + /* + * Guard against ridiculous "needed" values, which can occur if we're fed + * bogus data. Without this, we can get an overflow or infinite loop in + * the following. + */ + if (needed >= ((size_t) INT_MAX - str->len)) + { + markPQExpBufferBroken(str); + return 0; + } + + needed += str->len + 1; /* total space required now */ + + /* Because of the above test, we now have needed <= INT_MAX */ + + if (needed <= str->maxlen) + return 1; /* got enough space already */ + + /* + * We don't want to allocate just a little more space with each append; + * for efficiency, double the buffer size each time it overflows. + * Actually, we might need to more than double it if 'needed' is big... + */ + newlen = (str->maxlen > 0) ? (2 * str->maxlen) : 64; + while (needed > newlen) + newlen = 2 * newlen; + + /* + * Clamp to INT_MAX in case we went past it. Note we are assuming here + * that INT_MAX <= UINT_MAX/2, else the above loop could overflow. We + * will still have newlen >= needed. + */ + if (newlen > (size_t) INT_MAX) + newlen = (size_t) INT_MAX; + + newdata = (char *) realloc(str->data, newlen); + if (newdata != NULL) + { + str->data = newdata; + str->maxlen = newlen; + return 1; + } + + markPQExpBufferBroken(str); + return 0; +} + +/* + * printfPQExpBuffer + * Format text data under the control of fmt (an sprintf-like format string) + * and insert it into str. More space is allocated to str if necessary. + * This is a convenience routine that does the same thing as + * resetPQExpBuffer() followed by appendPQExpBuffer(). + */ +void +printfPQExpBuffer(PQExpBuffer str, const char *fmt,...) +{ + va_list args; + bool done; + + resetPQExpBuffer(str); + + if (PQExpBufferBroken(str)) + return; /* already failed */ + + /* Loop in case we have to retry after enlarging the buffer. */ + do + { + va_start(args, fmt); + done = appendPQExpBufferVA(str, fmt, args); + va_end(args); + } while (!done); +} + +/* + * appendPQExpBuffer + * + * Format text data under the control of fmt (an sprintf-like format string) + * and append it to whatever is already in str. More space is allocated + * to str if necessary. This is sort of like a combination of sprintf and + * strcat. + */ +void +appendPQExpBuffer(PQExpBuffer str, const char *fmt,...) +{ + va_list args; + bool done; + + if (PQExpBufferBroken(str)) + return; /* already failed */ + + /* Loop in case we have to retry after enlarging the buffer. */ + do + { + va_start(args, fmt); + done = appendPQExpBufferVA(str, fmt, args); + va_end(args); + } while (!done); +} + +/* + * appendPQExpBufferVA + * Shared guts of printfPQExpBuffer/appendPQExpBuffer. + * Attempt to format data and append it to str. Returns true if done + * (either successful or hard failure), false if need to retry. + */ +static bool +appendPQExpBufferVA(PQExpBuffer str, const char *fmt, va_list args) +{ + size_t avail; + size_t needed; + int nprinted; + + /* + * Try to format the given string into the available space; but if there's + * hardly any space, don't bother trying, just enlarge the buffer first. + */ + if (str->maxlen > str->len + 16) + { + /* + * Note: we intentionally leave one byte unused, as a guard against + * old broken versions of vsnprintf. + */ + avail = str->maxlen - str->len - 1; + + errno = 0; + + nprinted = vsnprintf(str->data + str->len, avail, fmt, args); + + /* + * If vsnprintf reports an error other than ENOMEM, fail. + */ + if (nprinted < 0 && errno != 0 && errno != ENOMEM) + { + markPQExpBufferBroken(str); + return true; + } + + /* + * Note: some versions of vsnprintf return the number of chars + * actually stored, not the total space needed as C99 specifies. And + * at least one returns -1 on failure. Be conservative about + * believing whether the print worked. + */ + if (nprinted >= 0 && (size_t) nprinted < avail - 1) + { + /* Success. Note nprinted does not include trailing null. */ + str->len += nprinted; + return true; + } + + if (nprinted >= 0 && (size_t) nprinted > avail) + { + /* + * This appears to be a C99-compliant vsnprintf, so believe its + * estimate of the required space. (If it's wrong, the logic will + * still work, but we may loop multiple times.) Note that the + * space needed should be only nprinted+1 bytes, but we'd better + * allocate one more than that so that the test above will succeed + * next time. + * + * In the corner case where the required space just barely + * overflows, fail. + */ + if (nprinted > INT_MAX - 2) + { + markPQExpBufferBroken(str); + return true; + } + needed = nprinted + 2; + } + else + { + /* + * Buffer overrun, and we don't know how much space is needed. + * Estimate twice the previous buffer size, but not more than + * INT_MAX. + */ + if (avail >= INT_MAX / 2) + needed = INT_MAX; + else + needed = avail * 2; + } + } + else + { + /* + * We have to guess at how much to enlarge, since we're skipping the + * formatting work. + */ + needed = 32; + } + + /* Increase the buffer size and try again. */ + if (!enlargePQExpBuffer(str, needed)) + return true; /* oops, out of memory */ + + return false; +} + +/* + * appendPQExpBufferStr + * Append the given string to a PQExpBuffer, allocating more space + * if necessary. + */ +void +appendPQExpBufferStr(PQExpBuffer str, const char *data) +{ + appendBinaryPQExpBuffer(str, data, strlen(data)); +} + +/* + * appendPQExpBufferChar + * Append a single byte to str. + * Like appendPQExpBuffer(str, "%c", ch) but much faster. + */ +void +appendPQExpBufferChar(PQExpBuffer str, char ch) +{ + /* Make more room if needed */ + if (!enlargePQExpBuffer(str, 1)) + return; + + /* OK, append the character */ + str->data[str->len] = ch; + str->len++; + str->data[str->len] = '\0'; +} + +/* + * appendBinaryPQExpBuffer + * + * Append arbitrary binary data to a PQExpBuffer, allocating more space + * if necessary. + */ +void +appendBinaryPQExpBuffer(PQExpBuffer str, const char *data, size_t datalen) +{ + /* Make more room if needed */ + if (!enlargePQExpBuffer(str, datalen)) + return; + + /* OK, append the data */ + memcpy(str->data + str->len, data, datalen); + str->len += datalen; + + /* + * Keep a trailing null in place, even though it's probably useless for + * binary data... + */ + str->data[str->len] = '\0'; +} diff --git a/libpq/pqexpbuffer.h b/libpq/pqexpbuffer.h new file mode 100644 index 0000000..ce87cd5 --- /dev/null +++ b/libpq/pqexpbuffer.h @@ -0,0 +1,182 @@ +/*------------------------------------------------------------------------- + * + * pqexpbuffer.h + * Declarations/definitions for "PQExpBuffer" functions. + * + * PQExpBuffer provides an indefinitely-extensible string data type. + * It can be used to buffer either ordinary C strings (null-terminated text) + * or arbitrary binary data. All storage is allocated with malloc(). + * + * This module is essentially the same as the backend's StringInfo data type, + * but it is intended for use in frontend libpq and client applications. + * Thus, it does not rely on palloc() nor elog(). + * + * It does rely on vsnprintf(); if configure finds that libc doesn't provide + * a usable vsnprintf(), then a copy of our own implementation of it will + * be linked into libpq. + * + * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/interfaces/libpq/pqexpbuffer.h + * + *------------------------------------------------------------------------- + */ +#ifndef PQEXPBUFFER_H +#define PQEXPBUFFER_H + +/*------------------------- + * PQExpBufferData holds information about an extensible string. + * data is the current buffer for the string (allocated with malloc). + * len is the current string length. There is guaranteed to be + * a terminating '\0' at data[len], although this is not very + * useful when the string holds binary data rather than text. + * maxlen is the allocated size in bytes of 'data', i.e. the maximum + * string size (including the terminating '\0' char) that we can + * currently store in 'data' without having to reallocate + * more space. We must always have maxlen > len. + * + * An exception occurs if we failed to allocate enough memory for the string + * buffer. In that case data points to a statically allocated empty string, + * and len = maxlen = 0. + *------------------------- + */ +typedef struct PQExpBufferData +{ + char *data; + size_t len; + size_t maxlen; +} PQExpBufferData; + +typedef PQExpBufferData *PQExpBuffer; + +/*------------------------ + * Test for a broken (out of memory) PQExpBuffer. + * When a buffer is "broken", all operations except resetting or deleting it + * are no-ops. + *------------------------ + */ +#define PQExpBufferBroken(str) \ + ((str) == NULL || (str)->maxlen == 0) + +/*------------------------ + * Same, but for use when using a static or local PQExpBufferData struct. + * For that, a null-pointer test is useless and may draw compiler warnings. + *------------------------ + */ +#define PQExpBufferDataBroken(buf) \ + ((buf).maxlen == 0) + +/*------------------------ + * Initial size of the data buffer in a PQExpBuffer. + * NB: this must be large enough to hold error messages that might + * be returned by PQrequestCancel(). + *------------------------ + */ +#define INITIAL_EXPBUFFER_SIZE 256 + +/*------------------------ + * There are two ways to create a PQExpBuffer object initially: + * + * PQExpBuffer stringptr = createPQExpBuffer(); + * Both the PQExpBufferData and the data buffer are malloc'd. + * + * PQExpBufferData string; + * initPQExpBuffer(&string); + * The data buffer is malloc'd but the PQExpBufferData is presupplied. + * This is appropriate if the PQExpBufferData is a field of another + * struct. + *------------------------- + */ + +/*------------------------ + * createPQExpBuffer + * Create an empty 'PQExpBufferData' & return a pointer to it. + */ +extern PQExpBuffer createPQExpBuffer(void); + +/*------------------------ + * initPQExpBuffer + * Initialize a PQExpBufferData struct (with previously undefined contents) + * to describe an empty string. + */ +extern void initPQExpBuffer(PQExpBuffer str); + +/*------------------------ + * To destroy a PQExpBuffer, use either: + * + * destroyPQExpBuffer(str); + * free()s both the data buffer and the PQExpBufferData. + * This is the inverse of createPQExpBuffer(). + * + * termPQExpBuffer(str) + * free()s the data buffer but not the PQExpBufferData itself. + * This is the inverse of initPQExpBuffer(). + * + * NOTE: some routines build up a string using PQExpBuffer, and then + * release the PQExpBufferData but return the data string itself to their + * caller. At that point the data string looks like a plain malloc'd + * string. + */ +extern void destroyPQExpBuffer(PQExpBuffer str); +extern void termPQExpBuffer(PQExpBuffer str); + +/*------------------------ + * resetPQExpBuffer + * Reset a PQExpBuffer to empty + * + * Note: if possible, a "broken" PQExpBuffer is returned to normal. + */ +extern void resetPQExpBuffer(PQExpBuffer str); + +/*------------------------ + * enlargePQExpBuffer + * Make sure there is enough space for 'needed' more bytes in the buffer + * ('needed' does not include the terminating null). + * + * Returns 1 if OK, 0 if failed to enlarge buffer. (In the latter case + * the buffer is left in "broken" state.) + */ +extern int enlargePQExpBuffer(PQExpBuffer str, size_t needed); + +/*------------------------ + * printfPQExpBuffer + * Format text data under the control of fmt (an sprintf-like format string) + * and insert it into str. More space is allocated to str if necessary. + * This is a convenience routine that does the same thing as + * resetPQExpBuffer() followed by appendPQExpBuffer(). + */ +extern void printfPQExpBuffer(PQExpBuffer str, const char *fmt,...) pg_attribute_printf(2, 3); + +/*------------------------ + * appendPQExpBuffer + * Format text data under the control of fmt (an sprintf-like format string) + * and append it to whatever is already in str. More space is allocated + * to str if necessary. This is sort of like a combination of sprintf and + * strcat. + */ +extern void appendPQExpBuffer(PQExpBuffer str, const char *fmt,...) pg_attribute_printf(2, 3); + +/*------------------------ + * appendPQExpBufferStr + * Append the given string to a PQExpBuffer, allocating more space + * if necessary. + */ +extern void appendPQExpBufferStr(PQExpBuffer str, const char *data); + +/*------------------------ + * appendPQExpBufferChar + * Append a single byte to str. + * Like appendPQExpBuffer(str, "%c", ch) but much faster. + */ +extern void appendPQExpBufferChar(PQExpBuffer str, char ch); + +/*------------------------ + * appendBinaryPQExpBuffer + * Append arbitrary binary data to a PQExpBuffer, allocating more space + * if necessary. + */ +extern void appendBinaryPQExpBuffer(PQExpBuffer str, + const char *data, size_t datalen); + +#endif /* PQEXPBUFFER_H */ diff --git a/libpq/pqsignal.c b/libpq/pqsignal.c new file mode 100644 index 0000000..07797f9 --- /dev/null +++ b/libpq/pqsignal.c @@ -0,0 +1,90 @@ +/*------------------------------------------------------------------------- + * + * pqsignal.c + * reliable BSD-style signal(2) routine stolen from RWW who stole it + * from Stevens... + * + * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/port/pqsignal.c + * + * We now assume that all Unix-oid systems have POSIX sigaction(2) + * with support for restartable signals (SA_RESTART). We used to also + * support BSD-style signal(2), but there really shouldn't be anything + * out there anymore that doesn't have the POSIX API. + * + * Windows, of course, is resolutely in a class by itself. In the backend, + * we don't use this file at all; src/backend/port/win32/signal.c provides + * pqsignal() for the backend environment. Frontend programs can use + * this version of pqsignal() if they wish, but beware that this does + * not provide restartable signals on Windows. + * + * ------------------------------------------------------------------------ + */ + +#include "c.h" + +#include + +#if !defined(WIN32) || defined(FRONTEND) + +/* + * Set up a signal handler, with SA_RESTART, for signal "signo" + * + * Returns the previous handler. + */ +pqsigfunc +pqsignal(int signo, pqsigfunc func) +{ +#ifndef WIN32 + struct sigaction act, + oact; + + act.sa_handler = func; + sigemptyset(&act.sa_mask); + act.sa_flags = SA_RESTART; +#ifdef SA_NOCLDSTOP + if (signo == SIGCHLD) + act.sa_flags |= SA_NOCLDSTOP; +#endif + if (sigaction(signo, &act, &oact) < 0) + return SIG_ERR; + return oact.sa_handler; +#else /* WIN32 */ + return signal(signo, func); +#endif +} + +/* + * Set up a signal handler, without SA_RESTART, for signal "signo" + * + * Returns the previous handler. + * + * On Windows, this would be identical to pqsignal(), so don't bother. + */ +#ifndef WIN32 + +pqsigfunc +pqsignal_no_restart(int signo, pqsigfunc func) +{ + struct sigaction act, + oact; + + act.sa_handler = func; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; +#ifdef SA_NOCLDSTOP + if (signo == SIGCHLD) + act.sa_flags |= SA_NOCLDSTOP; +#endif + if (sigaction(signo, &act, &oact) < 0) + return SIG_ERR; + return oact.sa_handler; +} + +#endif /* !WIN32 */ + +#endif /* !defined(WIN32) || defined(FRONTEND) */ diff --git a/libpq/thread.c b/libpq/thread.c new file mode 100644 index 0000000..ed908ba --- /dev/null +++ b/libpq/thread.c @@ -0,0 +1,146 @@ +/*------------------------------------------------------------------------- + * + * thread.c + * + * Prototypes and macros around system calls, used to help make + * threaded libraries reentrant and safe to use from threaded applications. + * + * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * + * src/port/thread.c + * + *------------------------------------------------------------------------- + */ + +#include "c.h" + +#include + + +/* + * Threading sometimes requires specially-named versions of functions + * that return data in static buffers, like strerror_r() instead of + * strerror(). Other operating systems use pthread_setspecific() + * and pthread_getspecific() internally to allow standard library + * functions to return static data to threaded applications. And some + * operating systems have neither. + * + * Additional confusion exists because many operating systems that + * use pthread_setspecific/pthread_getspecific() also have *_r versions + * of standard library functions for compatibility with operating systems + * that require them. However, internally, these *_r functions merely + * call the thread-safe standard library functions. + * + * For example, BSD/OS 4.3 uses Bind 8.2.3 for getpwuid(). Internally, + * getpwuid() calls pthread_setspecific/pthread_getspecific() to return + * static data to the caller in a thread-safe manner. However, BSD/OS + * also has getpwuid_r(), which merely calls getpwuid() and shifts + * around the arguments to match the getpwuid_r() function declaration. + * Therefore, while BSD/OS has getpwuid_r(), it isn't required. It also + * doesn't have strerror_r(), so we can't fall back to only using *_r + * functions for threaded programs. + * + * The current setup is to try threading in this order: + * + * use *_r function names if they exit + * (*_THREADSAFE=yes) + * use non-*_r functions if they are thread-safe + * + * One thread-safe solution for gethostbyname() might be to use getaddrinfo(). + * + * Run src/test/thread to test if your operating system has thread-safe + * non-*_r functions. + */ + + +/* + * Wrapper around strerror and strerror_r to use the former if it is + * available and also return a more useful value (the error string). + */ +char * +pqStrerror(int errnum, char *strerrbuf, size_t buflen) +{ +#if defined(FRONTEND) && defined(ENABLE_THREAD_SAFETY) && defined(HAVE_STRERROR_R) + /* reentrant strerror_r is available */ +#ifdef STRERROR_R_INT + /* SUSv3 version */ + if (strerror_r(errnum, strerrbuf, buflen) == 0) + return strerrbuf; + else + return "Unknown error"; +#else + /* GNU libc */ + return strerror_r(errnum, strerrbuf, buflen); +#endif +#else + /* no strerror_r() available, just use strerror */ + strlcpy(strerrbuf, strerror(errnum), buflen); + + return strerrbuf; +#endif +} + +/* + * Wrapper around getpwuid() or getpwuid_r() to mimic POSIX getpwuid_r() + * behaviour, if that function is not available or required. + * + * Per POSIX, the possible cases are: + * success: returns zero, *result is non-NULL + * uid not found: returns zero, *result is NULL + * error during lookup: returns an errno code, *result is NULL + * (caller should *not* assume that the errno variable is set) + */ +#ifndef WIN32 +int +pqGetpwuid(uid_t uid, struct passwd * resultbuf, char *buffer, + size_t buflen, struct passwd ** result) +{ +#if defined(FRONTEND) && defined(ENABLE_THREAD_SAFETY) && defined(HAVE_GETPWUID_R) + return getpwuid_r(uid, resultbuf, buffer, buflen, result); +#else + /* no getpwuid_r() available, just use getpwuid() */ + errno = 0; + *result = getpwuid(uid); + /* paranoia: ensure we return zero on success */ + return (*result == NULL) ? errno : 0; +#endif +} +#endif + +/* + * Wrapper around gethostbyname() or gethostbyname_r() to mimic + * POSIX gethostbyname_r() behaviour, if it is not available or required. + * This function is called _only_ by our getaddinfo() portability function. + */ +#ifndef HAVE_GETADDRINFO +int +pqGethostbyname(const char *name, + struct hostent * resultbuf, + char *buffer, size_t buflen, + struct hostent ** result, + int *herrno) +{ +#if defined(FRONTEND) && defined(ENABLE_THREAD_SAFETY) && defined(HAVE_GETHOSTBYNAME_R) + + /* + * broken (well early POSIX draft) gethostbyname_r() which returns 'struct + * hostent *' + */ + *result = gethostbyname_r(name, resultbuf, buffer, buflen, herrno); + return (*result == NULL) ? -1 : 0; +#else + + /* no gethostbyname_r(), just use gethostbyname() */ + *result = gethostbyname(name); + + if (*result != NULL) + *herrno = h_errno; + + if (*result != NULL) + return 0; + else + return -1; +#endif +} + +#endif diff --git a/libpq/version.h.in b/libpq/version.h.in new file mode 100644 index 0000000..f5d6b31 --- /dev/null +++ b/libpq/version.h.in @@ -0,0 +1,26 @@ +/* file : libpq/version.h.in -*- C -*- + * copyright : Copyright (c) 2016-2017 Code Synthesis Ltd + * license : PostgreSQL License; see accompanying COPYRIGHT file + */ + +#ifndef PG_MAJORVERSION /* Note: using the version macro itself. */ + +/* + * Note that staring PostgreSQL 10 the release version schema changed (see + * build/bootstrap.build for details). + */ +#if $libpq.version.major$ < 10 +# define PG_MAJORVERSION "$libpq.version.major$.$libpq.version.minor$" +#else +# error "change PG_MAJORVERSION macro definition" +# define PG_MAJORVERSION "$libpq.version.major$" +#endif + +/* + * Is valid for both old and new version schemes (see above). + */ +#define PG_VERSION_NUM (10000 * $libpq.version.major$ + \ + 100 * $libpq.version.minor$ + \ + $libpq.version.patch$) + +#endif /* PG_MAJORVERSION */ diff --git a/libpq/wchar.c b/libpq/wchar.c new file mode 100644 index 0000000..fd51eed --- /dev/null +++ b/libpq/wchar.c @@ -0,0 +1,2054 @@ +/* + * conversion functions between pg_wchar and multibyte streams. + * Tatsuo Ishii + * src/backend/utils/mb/wchar.c + * + */ +/* can be used in either frontend or backend */ +#ifdef FRONTEND +#include "postgres_fe.h" +#else +#include "postgres.h" +#endif + +#include "mb/pg_wchar.h" + + +/* + * conversion to pg_wchar is done by "table driven." + * to add an encoding support, define mb2wchar_with_len(), mblen(), dsplen() + * for the particular encoding. Note that if the encoding is only + * supported in the client, you don't need to define + * mb2wchar_with_len() function (SJIS is the case). + * + * These functions generally assume that their input is validly formed. + * The "verifier" functions, further down in the file, have to be more + * paranoid. We expect that mblen() does not need to examine more than + * the first byte of the character to discover the correct length. + * + * Note: for the display output of psql to work properly, the return values + * of the dsplen functions must conform to the Unicode standard. In particular + * the NUL character is zero width and control characters are generally + * width -1. It is recommended that non-ASCII encodings refer their ASCII + * subset to the ASCII routines to ensure consistency. + */ + +/* + * SQL/ASCII + */ +static int +pg_ascii2wchar_with_len(const unsigned char *from, pg_wchar *to, int len) +{ + int cnt = 0; + + while (len > 0 && *from) + { + *to++ = *from++; + len--; + cnt++; + } + *to = 0; + return cnt; +} + +static int +pg_ascii_mblen(const unsigned char *s) +{ + return 1; +} + +static int +pg_ascii_dsplen(const unsigned char *s) +{ + if (*s == '\0') + return 0; + if (*s < 0x20 || *s == 0x7f) + return -1; + + return 1; +} + +/* + * EUC + */ +static int +pg_euc2wchar_with_len(const unsigned char *from, pg_wchar *to, int len) +{ + int cnt = 0; + + while (len > 0 && *from) + { + if (*from == SS2 && len >= 2) /* JIS X 0201 (so called "1 byte + * KANA") */ + { + from++; + *to = (SS2 << 8) | *from++; + len -= 2; + } + else if (*from == SS3 && len >= 3) /* JIS X 0212 KANJI */ + { + from++; + *to = (SS3 << 16) | (*from++ << 8); + *to |= *from++; + len -= 3; + } + else if (IS_HIGHBIT_SET(*from) && len >= 2) /* JIS X 0208 KANJI */ + { + *to = *from++ << 8; + *to |= *from++; + len -= 2; + } + else /* must be ASCII */ + { + *to = *from++; + len--; + } + to++; + cnt++; + } + *to = 0; + return cnt; +} + +static inline int +pg_euc_mblen(const unsigned char *s) +{ + int len; + + if (*s == SS2) + len = 2; + else if (*s == SS3) + len = 3; + else if (IS_HIGHBIT_SET(*s)) + len = 2; + else + len = 1; + return len; +} + +static inline int +pg_euc_dsplen(const unsigned char *s) +{ + int len; + + if (*s == SS2) + len = 2; + else if (*s == SS3) + len = 2; + else if (IS_HIGHBIT_SET(*s)) + len = 2; + else + len = pg_ascii_dsplen(s); + return len; +} + +/* + * EUC_JP + */ +static int +pg_eucjp2wchar_with_len(const unsigned char *from, pg_wchar *to, int len) +{ + return pg_euc2wchar_with_len(from, to, len); +} + +static int +pg_eucjp_mblen(const unsigned char *s) +{ + return pg_euc_mblen(s); +} + +static int +pg_eucjp_dsplen(const unsigned char *s) +{ + int len; + + if (*s == SS2) + len = 1; + else if (*s == SS3) + len = 2; + else if (IS_HIGHBIT_SET(*s)) + len = 2; + else + len = pg_ascii_dsplen(s); + return len; +} + +/* + * EUC_KR + */ +static int +pg_euckr2wchar_with_len(const unsigned char *from, pg_wchar *to, int len) +{ + return pg_euc2wchar_with_len(from, to, len); +} + +static int +pg_euckr_mblen(const unsigned char *s) +{ + return pg_euc_mblen(s); +} + +static int +pg_euckr_dsplen(const unsigned char *s) +{ + return pg_euc_dsplen(s); +} + +/* + * EUC_CN + * + */ +static int +pg_euccn2wchar_with_len(const unsigned char *from, pg_wchar *to, int len) +{ + int cnt = 0; + + while (len > 0 && *from) + { + if (*from == SS2 && len >= 3) /* code set 2 (unused?) */ + { + from++; + *to = (SS2 << 16) | (*from++ << 8); + *to |= *from++; + len -= 3; + } + else if (*from == SS3 && len >= 3) /* code set 3 (unused ?) */ + { + from++; + *to = (SS3 << 16) | (*from++ << 8); + *to |= *from++; + len -= 3; + } + else if (IS_HIGHBIT_SET(*from) && len >= 2) /* code set 1 */ + { + *to = *from++ << 8; + *to |= *from++; + len -= 2; + } + else + { + *to = *from++; + len--; + } + to++; + cnt++; + } + *to = 0; + return cnt; +} + +static int +pg_euccn_mblen(const unsigned char *s) +{ + int len; + + if (IS_HIGHBIT_SET(*s)) + len = 2; + else + len = 1; + return len; +} + +static int +pg_euccn_dsplen(const unsigned char *s) +{ + int len; + + if (IS_HIGHBIT_SET(*s)) + len = 2; + else + len = pg_ascii_dsplen(s); + return len; +} + +/* + * EUC_TW + * + */ +static int +pg_euctw2wchar_with_len(const unsigned char *from, pg_wchar *to, int len) +{ + int cnt = 0; + + while (len > 0 && *from) + { + if (*from == SS2 && len >= 4) /* code set 2 */ + { + from++; + *to = (((uint32) SS2) << 24) | (*from++ << 16); + *to |= *from++ << 8; + *to |= *from++; + len -= 4; + } + else if (*from == SS3 && len >= 3) /* code set 3 (unused?) */ + { + from++; + *to = (SS3 << 16) | (*from++ << 8); + *to |= *from++; + len -= 3; + } + else if (IS_HIGHBIT_SET(*from) && len >= 2) /* code set 2 */ + { + *to = *from++ << 8; + *to |= *from++; + len -= 2; + } + else + { + *to = *from++; + len--; + } + to++; + cnt++; + } + *to = 0; + return cnt; +} + +static int +pg_euctw_mblen(const unsigned char *s) +{ + int len; + + if (*s == SS2) + len = 4; + else if (*s == SS3) + len = 3; + else if (IS_HIGHBIT_SET(*s)) + len = 2; + else + len = 1; + return len; +} + +static int +pg_euctw_dsplen(const unsigned char *s) +{ + int len; + + if (*s == SS2) + len = 2; + else if (*s == SS3) + len = 2; + else if (IS_HIGHBIT_SET(*s)) + len = 2; + else + len = pg_ascii_dsplen(s); + return len; +} + +/* + * Convert pg_wchar to EUC_* encoding. + * caller must allocate enough space for "to", including a trailing zero! + * len: length of from. + * "from" not necessarily null terminated. + */ +static int +pg_wchar2euc_with_len(const pg_wchar *from, unsigned char *to, int len) +{ + int cnt = 0; + + while (len > 0 && *from) + { + unsigned char c; + + if ((c = (*from >> 24))) + { + *to++ = c; + *to++ = (*from >> 16) & 0xff; + *to++ = (*from >> 8) & 0xff; + *to++ = *from & 0xff; + cnt += 4; + } + else if ((c = (*from >> 16))) + { + *to++ = c; + *to++ = (*from >> 8) & 0xff; + *to++ = *from & 0xff; + cnt += 3; + } + else if ((c = (*from >> 8))) + { + *to++ = c; + *to++ = *from & 0xff; + cnt += 2; + } + else + { + *to++ = *from; + cnt++; + } + from++; + len--; + } + *to = 0; + return cnt; +} + + +/* + * JOHAB + */ +static int +pg_johab_mblen(const unsigned char *s) +{ + return pg_euc_mblen(s); +} + +static int +pg_johab_dsplen(const unsigned char *s) +{ + return pg_euc_dsplen(s); +} + +/* + * convert UTF8 string to pg_wchar (UCS-4) + * caller must allocate enough space for "to", including a trailing zero! + * len: length of from. + * "from" not necessarily null terminated. + */ +static int +pg_utf2wchar_with_len(const unsigned char *from, pg_wchar *to, int len) +{ + int cnt = 0; + uint32 c1, + c2, + c3, + c4; + + while (len > 0 && *from) + { + if ((*from & 0x80) == 0) + { + *to = *from++; + len--; + } + else if ((*from & 0xe0) == 0xc0) + { + if (len < 2) + break; /* drop trailing incomplete char */ + c1 = *from++ & 0x1f; + c2 = *from++ & 0x3f; + *to = (c1 << 6) | c2; + len -= 2; + } + else if ((*from & 0xf0) == 0xe0) + { + if (len < 3) + break; /* drop trailing incomplete char */ + c1 = *from++ & 0x0f; + c2 = *from++ & 0x3f; + c3 = *from++ & 0x3f; + *to = (c1 << 12) | (c2 << 6) | c3; + len -= 3; + } + else if ((*from & 0xf8) == 0xf0) + { + if (len < 4) + break; /* drop trailing incomplete char */ + c1 = *from++ & 0x07; + c2 = *from++ & 0x3f; + c3 = *from++ & 0x3f; + c4 = *from++ & 0x3f; + *to = (c1 << 18) | (c2 << 12) | (c3 << 6) | c4; + len -= 4; + } + else + { + /* treat a bogus char as length 1; not ours to raise error */ + *to = *from++; + len--; + } + to++; + cnt++; + } + *to = 0; + return cnt; +} + + +/* + * Map a Unicode code point to UTF-8. utf8string must have 4 bytes of + * space allocated. + */ +unsigned char * +unicode_to_utf8(pg_wchar c, unsigned char *utf8string) +{ + if (c <= 0x7F) + { + utf8string[0] = c; + } + else if (c <= 0x7FF) + { + utf8string[0] = 0xC0 | ((c >> 6) & 0x1F); + utf8string[1] = 0x80 | (c & 0x3F); + } + else if (c <= 0xFFFF) + { + utf8string[0] = 0xE0 | ((c >> 12) & 0x0F); + utf8string[1] = 0x80 | ((c >> 6) & 0x3F); + utf8string[2] = 0x80 | (c & 0x3F); + } + else + { + utf8string[0] = 0xF0 | ((c >> 18) & 0x07); + utf8string[1] = 0x80 | ((c >> 12) & 0x3F); + utf8string[2] = 0x80 | ((c >> 6) & 0x3F); + utf8string[3] = 0x80 | (c & 0x3F); + } + + return utf8string; +} + +/* + * Trivial conversion from pg_wchar to UTF-8. + * caller should allocate enough space for "to" + * len: length of from. + * "from" not necessarily null terminated. + */ +static int +pg_wchar2utf_with_len(const pg_wchar *from, unsigned char *to, int len) +{ + int cnt = 0; + + while (len > 0 && *from) + { + int char_len; + + unicode_to_utf8(*from, to); + char_len = pg_utf_mblen(to); + cnt += char_len; + to += char_len; + from++; + len--; + } + *to = 0; + return cnt; +} + +/* + * Return the byte length of a UTF8 character pointed to by s + * + * Note: in the current implementation we do not support UTF8 sequences + * of more than 4 bytes; hence do NOT return a value larger than 4. + * We return "1" for any leading byte that is either flat-out illegal or + * indicates a length larger than we support. + * + * pg_utf2wchar_with_len(), utf8_to_unicode(), pg_utf8_islegal(), and perhaps + * other places would need to be fixed to change this. + */ +int +pg_utf_mblen(const unsigned char *s) +{ + int len; + + if ((*s & 0x80) == 0) + len = 1; + else if ((*s & 0xe0) == 0xc0) + len = 2; + else if ((*s & 0xf0) == 0xe0) + len = 3; + else if ((*s & 0xf8) == 0xf0) + len = 4; +#ifdef NOT_USED + else if ((*s & 0xfc) == 0xf8) + len = 5; + else if ((*s & 0xfe) == 0xfc) + len = 6; +#endif + else + len = 1; + return len; +} + +/* + * This is an implementation of wcwidth() and wcswidth() as defined in + * "The Single UNIX Specification, Version 2, The Open Group, 1997" + * + * + * Markus Kuhn -- 2001-09-08 -- public domain + * + * customised for PostgreSQL + * + * original available at : http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c + */ + +struct mbinterval +{ + unsigned short first; + unsigned short last; +}; + +/* auxiliary function for binary search in interval table */ +static int +mbbisearch(pg_wchar ucs, const struct mbinterval * table, int max) +{ + int min = 0; + int mid; + + if (ucs < table[0].first || ucs > table[max].last) + return 0; + while (max >= min) + { + mid = (min + max) / 2; + if (ucs > table[mid].last) + min = mid + 1; + else if (ucs < table[mid].first) + max = mid - 1; + else + return 1; + } + + return 0; +} + + +/* The following functions define the column width of an ISO 10646 + * character as follows: + * + * - The null character (U+0000) has a column width of 0. + * + * - Other C0/C1 control characters and DEL will lead to a return + * value of -1. + * + * - Non-spacing and enclosing combining characters (general + * category code Mn or Me in the Unicode database) have a + * column width of 0. + * + * - Other format characters (general category code Cf in the Unicode + * database) and ZERO WIDTH SPACE (U+200B) have a column width of 0. + * + * - Hangul Jamo medial vowels and final consonants (U+1160-U+11FF) + * have a column width of 0. + * + * - Spacing characters in the East Asian Wide (W) or East Asian + * FullWidth (F) category as defined in Unicode Technical + * Report #11 have a column width of 2. + * + * - All remaining characters (including all printable + * ISO 8859-1 and WGL4 characters, Unicode control characters, + * etc.) have a column width of 1. + * + * This implementation assumes that wchar_t characters are encoded + * in ISO 10646. + */ + +static int +ucs_wcwidth(pg_wchar ucs) +{ + /* sorted list of non-overlapping intervals of non-spacing characters */ + static const struct mbinterval combining[] = { + {0x0300, 0x034E}, {0x0360, 0x0362}, {0x0483, 0x0486}, + {0x0488, 0x0489}, {0x0591, 0x05A1}, {0x05A3, 0x05B9}, + {0x05BB, 0x05BD}, {0x05BF, 0x05BF}, {0x05C1, 0x05C2}, + {0x05C4, 0x05C4}, {0x064B, 0x0655}, {0x0670, 0x0670}, + {0x06D6, 0x06E4}, {0x06E7, 0x06E8}, {0x06EA, 0x06ED}, + {0x070F, 0x070F}, {0x0711, 0x0711}, {0x0730, 0x074A}, + {0x07A6, 0x07B0}, {0x0901, 0x0902}, {0x093C, 0x093C}, + {0x0941, 0x0948}, {0x094D, 0x094D}, {0x0951, 0x0954}, + {0x0962, 0x0963}, {0x0981, 0x0981}, {0x09BC, 0x09BC}, + {0x09C1, 0x09C4}, {0x09CD, 0x09CD}, {0x09E2, 0x09E3}, + {0x0A02, 0x0A02}, {0x0A3C, 0x0A3C}, {0x0A41, 0x0A42}, + {0x0A47, 0x0A48}, {0x0A4B, 0x0A4D}, {0x0A70, 0x0A71}, + {0x0A81, 0x0A82}, {0x0ABC, 0x0ABC}, {0x0AC1, 0x0AC5}, + {0x0AC7, 0x0AC8}, {0x0ACD, 0x0ACD}, {0x0B01, 0x0B01}, + {0x0B3C, 0x0B3C}, {0x0B3F, 0x0B3F}, {0x0B41, 0x0B43}, + {0x0B4D, 0x0B4D}, {0x0B56, 0x0B56}, {0x0B82, 0x0B82}, + {0x0BC0, 0x0BC0}, {0x0BCD, 0x0BCD}, {0x0C3E, 0x0C40}, + {0x0C46, 0x0C48}, {0x0C4A, 0x0C4D}, {0x0C55, 0x0C56}, + {0x0CBF, 0x0CBF}, {0x0CC6, 0x0CC6}, {0x0CCC, 0x0CCD}, + {0x0D41, 0x0D43}, {0x0D4D, 0x0D4D}, {0x0DCA, 0x0DCA}, + {0x0DD2, 0x0DD4}, {0x0DD6, 0x0DD6}, {0x0E31, 0x0E31}, + {0x0E34, 0x0E3A}, {0x0E47, 0x0E4E}, {0x0EB1, 0x0EB1}, + {0x0EB4, 0x0EB9}, {0x0EBB, 0x0EBC}, {0x0EC8, 0x0ECD}, + {0x0F18, 0x0F19}, {0x0F35, 0x0F35}, {0x0F37, 0x0F37}, + {0x0F39, 0x0F39}, {0x0F71, 0x0F7E}, {0x0F80, 0x0F84}, + {0x0F86, 0x0F87}, {0x0F90, 0x0F97}, {0x0F99, 0x0FBC}, + {0x0FC6, 0x0FC6}, {0x102D, 0x1030}, {0x1032, 0x1032}, + {0x1036, 0x1037}, {0x1039, 0x1039}, {0x1058, 0x1059}, + {0x1160, 0x11FF}, {0x17B7, 0x17BD}, {0x17C6, 0x17C6}, + {0x17C9, 0x17D3}, {0x180B, 0x180E}, {0x18A9, 0x18A9}, + {0x200B, 0x200F}, {0x202A, 0x202E}, {0x206A, 0x206F}, + {0x20D0, 0x20E3}, {0x302A, 0x302F}, {0x3099, 0x309A}, + {0xFB1E, 0xFB1E}, {0xFE20, 0xFE23}, {0xFEFF, 0xFEFF}, + {0xFFF9, 0xFFFB} + }; + + /* test for 8-bit control characters */ + if (ucs == 0) + return 0; + + if (ucs < 0x20 || (ucs >= 0x7f && ucs < 0xa0) || ucs > 0x0010ffff) + return -1; + + /* binary search in table of non-spacing characters */ + if (mbbisearch(ucs, combining, + sizeof(combining) / sizeof(struct mbinterval) - 1)) + return 0; + + /* + * if we arrive here, ucs is not a combining or C0/C1 control character + */ + + return 1 + + (ucs >= 0x1100 && + (ucs <= 0x115f || /* Hangul Jamo init. consonants */ + (ucs >= 0x2e80 && ucs <= 0xa4cf && (ucs & ~0x0011) != 0x300a && + ucs != 0x303f) || /* CJK ... Yi */ + (ucs >= 0xac00 && ucs <= 0xd7a3) || /* Hangul Syllables */ + (ucs >= 0xf900 && ucs <= 0xfaff) || /* CJK Compatibility + * Ideographs */ + (ucs >= 0xfe30 && ucs <= 0xfe6f) || /* CJK Compatibility Forms */ + (ucs >= 0xff00 && ucs <= 0xff5f) || /* Fullwidth Forms */ + (ucs >= 0xffe0 && ucs <= 0xffe6) || + (ucs >= 0x20000 && ucs <= 0x2ffff))); +} + +/* + * Convert a UTF-8 character to a Unicode code point. + * This is a one-character version of pg_utf2wchar_with_len. + * + * No error checks here, c must point to a long-enough string. + */ +pg_wchar +utf8_to_unicode(const unsigned char *c) +{ + if ((*c & 0x80) == 0) + return (pg_wchar) c[0]; + else if ((*c & 0xe0) == 0xc0) + return (pg_wchar) (((c[0] & 0x1f) << 6) | + (c[1] & 0x3f)); + else if ((*c & 0xf0) == 0xe0) + return (pg_wchar) (((c[0] & 0x0f) << 12) | + ((c[1] & 0x3f) << 6) | + (c[2] & 0x3f)); + else if ((*c & 0xf8) == 0xf0) + return (pg_wchar) (((c[0] & 0x07) << 18) | + ((c[1] & 0x3f) << 12) | + ((c[2] & 0x3f) << 6) | + (c[3] & 0x3f)); + else + /* that is an invalid code on purpose */ + return 0xffffffff; +} + +static int +pg_utf_dsplen(const unsigned char *s) +{ + return ucs_wcwidth(utf8_to_unicode(s)); +} + +/* + * convert mule internal code to pg_wchar + * caller should allocate enough space for "to" + * len: length of from. + * "from" not necessarily null terminated. + */ +static int +pg_mule2wchar_with_len(const unsigned char *from, pg_wchar *to, int len) +{ + int cnt = 0; + + while (len > 0 && *from) + { + if (IS_LC1(*from) && len >= 2) + { + *to = *from++ << 16; + *to |= *from++; + len -= 2; + } + else if (IS_LCPRV1(*from) && len >= 3) + { + from++; + *to = *from++ << 16; + *to |= *from++; + len -= 3; + } + else if (IS_LC2(*from) && len >= 3) + { + *to = *from++ << 16; + *to |= *from++ << 8; + *to |= *from++; + len -= 3; + } + else if (IS_LCPRV2(*from) && len >= 4) + { + from++; + *to = *from++ << 16; + *to |= *from++ << 8; + *to |= *from++; + len -= 4; + } + else + { /* assume ASCII */ + *to = (unsigned char) *from++; + len--; + } + to++; + cnt++; + } + *to = 0; + return cnt; +} + +/* + * convert pg_wchar to mule internal code + * caller should allocate enough space for "to" + * len: length of from. + * "from" not necessarily null terminated. + */ +static int +pg_wchar2mule_with_len(const pg_wchar *from, unsigned char *to, int len) +{ + int cnt = 0; + + while (len > 0 && *from) + { + unsigned char lb; + + lb = (*from >> 16) & 0xff; + if (IS_LC1(lb)) + { + *to++ = lb; + *to++ = *from & 0xff; + cnt += 2; + } + else if (IS_LC2(lb)) + { + *to++ = lb; + *to++ = (*from >> 8) & 0xff; + *to++ = *from & 0xff; + cnt += 3; + } + else if (IS_LCPRV1_A_RANGE(lb)) + { + *to++ = LCPRV1_A; + *to++ = lb; + *to++ = *from & 0xff; + cnt += 3; + } + else if (IS_LCPRV1_B_RANGE(lb)) + { + *to++ = LCPRV1_B; + *to++ = lb; + *to++ = *from & 0xff; + cnt += 3; + } + else if (IS_LCPRV2_A_RANGE(lb)) + { + *to++ = LCPRV2_A; + *to++ = lb; + *to++ = (*from >> 8) & 0xff; + *to++ = *from & 0xff; + cnt += 4; + } + else if (IS_LCPRV2_B_RANGE(lb)) + { + *to++ = LCPRV2_B; + *to++ = lb; + *to++ = (*from >> 8) & 0xff; + *to++ = *from & 0xff; + cnt += 4; + } + else + { + *to++ = *from & 0xff; + cnt += 1; + } + from++; + len--; + } + *to = 0; + return cnt; +} + +int +pg_mule_mblen(const unsigned char *s) +{ + int len; + + if (IS_LC1(*s)) + len = 2; + else if (IS_LCPRV1(*s)) + len = 3; + else if (IS_LC2(*s)) + len = 3; + else if (IS_LCPRV2(*s)) + len = 4; + else + len = 1; /* assume ASCII */ + return len; +} + +static int +pg_mule_dsplen(const unsigned char *s) +{ + int len; + + /* + * Note: it's not really appropriate to assume that all multibyte charsets + * are double-wide on screen. But this seems an okay approximation for + * the MULE charsets we currently support. + */ + + if (IS_LC1(*s)) + len = 1; + else if (IS_LCPRV1(*s)) + len = 1; + else if (IS_LC2(*s)) + len = 2; + else if (IS_LCPRV2(*s)) + len = 2; + else + len = 1; /* assume ASCII */ + + return len; +} + +/* + * ISO8859-1 + */ +static int +pg_latin12wchar_with_len(const unsigned char *from, pg_wchar *to, int len) +{ + int cnt = 0; + + while (len > 0 && *from) + { + *to++ = *from++; + len--; + cnt++; + } + *to = 0; + return cnt; +} + +/* + * Trivial conversion from pg_wchar to single byte encoding. Just ignores + * high bits. + * caller should allocate enough space for "to" + * len: length of from. + * "from" not necessarily null terminated. + */ +static int +pg_wchar2single_with_len(const pg_wchar *from, unsigned char *to, int len) +{ + int cnt = 0; + + while (len > 0 && *from) + { + *to++ = *from++; + len--; + cnt++; + } + *to = 0; + return cnt; +} + +static int +pg_latin1_mblen(const unsigned char *s) +{ + return 1; +} + +static int +pg_latin1_dsplen(const unsigned char *s) +{ + return pg_ascii_dsplen(s); +} + +/* + * SJIS + */ +static int +pg_sjis_mblen(const unsigned char *s) +{ + int len; + + if (*s >= 0xa1 && *s <= 0xdf) + len = 1; /* 1 byte kana? */ + else if (IS_HIGHBIT_SET(*s)) + len = 2; /* kanji? */ + else + len = 1; /* should be ASCII */ + return len; +} + +static int +pg_sjis_dsplen(const unsigned char *s) +{ + int len; + + if (*s >= 0xa1 && *s <= 0xdf) + len = 1; /* 1 byte kana? */ + else if (IS_HIGHBIT_SET(*s)) + len = 2; /* kanji? */ + else + len = pg_ascii_dsplen(s); /* should be ASCII */ + return len; +} + +/* + * Big5 + */ +static int +pg_big5_mblen(const unsigned char *s) +{ + int len; + + if (IS_HIGHBIT_SET(*s)) + len = 2; /* kanji? */ + else + len = 1; /* should be ASCII */ + return len; +} + +static int +pg_big5_dsplen(const unsigned char *s) +{ + int len; + + if (IS_HIGHBIT_SET(*s)) + len = 2; /* kanji? */ + else + len = pg_ascii_dsplen(s); /* should be ASCII */ + return len; +} + +/* + * GBK + */ +static int +pg_gbk_mblen(const unsigned char *s) +{ + int len; + + if (IS_HIGHBIT_SET(*s)) + len = 2; /* kanji? */ + else + len = 1; /* should be ASCII */ + return len; +} + +static int +pg_gbk_dsplen(const unsigned char *s) +{ + int len; + + if (IS_HIGHBIT_SET(*s)) + len = 2; /* kanji? */ + else + len = pg_ascii_dsplen(s); /* should be ASCII */ + return len; +} + +/* + * UHC + */ +static int +pg_uhc_mblen(const unsigned char *s) +{ + int len; + + if (IS_HIGHBIT_SET(*s)) + len = 2; /* 2byte? */ + else + len = 1; /* should be ASCII */ + return len; +} + +static int +pg_uhc_dsplen(const unsigned char *s) +{ + int len; + + if (IS_HIGHBIT_SET(*s)) + len = 2; /* 2byte? */ + else + len = pg_ascii_dsplen(s); /* should be ASCII */ + return len; +} + +/* + * GB18030 + * Added by Bill Huang , + */ +static int +pg_gb18030_mblen(const unsigned char *s) +{ + int len; + + if (!IS_HIGHBIT_SET(*s)) + len = 1; /* ASCII */ + else if (*(s + 1) >= 0x30 && *(s + 1) <= 0x39) + len = 4; + else + len = 2; + return len; +} + +static int +pg_gb18030_dsplen(const unsigned char *s) +{ + int len; + + if (IS_HIGHBIT_SET(*s)) + len = 2; + else + len = pg_ascii_dsplen(s); /* ASCII */ + return len; +} + +/* + *------------------------------------------------------------------- + * multibyte sequence validators + * + * These functions accept "s", a pointer to the first byte of a string, + * and "len", the remaining length of the string. If there is a validly + * encoded character beginning at *s, return its length in bytes; else + * return -1. + * + * The functions can assume that len > 0 and that *s != '\0', but they must + * test for and reject zeroes in any additional bytes of a multibyte character. + * + * Note that this definition allows the function for a single-byte + * encoding to be just "return 1". + *------------------------------------------------------------------- + */ + +static int +pg_ascii_verifier(const unsigned char *s, int len) +{ + return 1; +} + +#define IS_EUC_RANGE_VALID(c) ((c) >= 0xa1 && (c) <= 0xfe) + +static int +pg_eucjp_verifier(const unsigned char *s, int len) +{ + int l; + unsigned char c1, + c2; + + c1 = *s++; + + switch (c1) + { + case SS2: /* JIS X 0201 */ + l = 2; + if (l > len) + return -1; + c2 = *s++; + if (c2 < 0xa1 || c2 > 0xdf) + return -1; + break; + + case SS3: /* JIS X 0212 */ + l = 3; + if (l > len) + return -1; + c2 = *s++; + if (!IS_EUC_RANGE_VALID(c2)) + return -1; + c2 = *s++; + if (!IS_EUC_RANGE_VALID(c2)) + return -1; + break; + + default: + if (IS_HIGHBIT_SET(c1)) /* JIS X 0208? */ + { + l = 2; + if (l > len) + return -1; + if (!IS_EUC_RANGE_VALID(c1)) + return -1; + c2 = *s++; + if (!IS_EUC_RANGE_VALID(c2)) + return -1; + } + else + /* must be ASCII */ + { + l = 1; + } + break; + } + + return l; +} + +static int +pg_euckr_verifier(const unsigned char *s, int len) +{ + int l; + unsigned char c1, + c2; + + c1 = *s++; + + if (IS_HIGHBIT_SET(c1)) + { + l = 2; + if (l > len) + return -1; + if (!IS_EUC_RANGE_VALID(c1)) + return -1; + c2 = *s++; + if (!IS_EUC_RANGE_VALID(c2)) + return -1; + } + else + /* must be ASCII */ + { + l = 1; + } + + return l; +} + +/* EUC-CN byte sequences are exactly same as EUC-KR */ +#define pg_euccn_verifier pg_euckr_verifier + +static int +pg_euctw_verifier(const unsigned char *s, int len) +{ + int l; + unsigned char c1, + c2; + + c1 = *s++; + + switch (c1) + { + case SS2: /* CNS 11643 Plane 1-7 */ + l = 4; + if (l > len) + return -1; + c2 = *s++; + if (c2 < 0xa1 || c2 > 0xa7) + return -1; + c2 = *s++; + if (!IS_EUC_RANGE_VALID(c2)) + return -1; + c2 = *s++; + if (!IS_EUC_RANGE_VALID(c2)) + return -1; + break; + + case SS3: /* unused */ + return -1; + + default: + if (IS_HIGHBIT_SET(c1)) /* CNS 11643 Plane 1 */ + { + l = 2; + if (l > len) + return -1; + /* no further range check on c1? */ + c2 = *s++; + if (!IS_EUC_RANGE_VALID(c2)) + return -1; + } + else + /* must be ASCII */ + { + l = 1; + } + break; + } + return l; +} + +static int +pg_johab_verifier(const unsigned char *s, int len) +{ + int l, + mbl; + unsigned char c; + + l = mbl = pg_johab_mblen(s); + + if (len < l) + return -1; + + if (!IS_HIGHBIT_SET(*s)) + return mbl; + + while (--l > 0) + { + c = *++s; + if (!IS_EUC_RANGE_VALID(c)) + return -1; + } + return mbl; +} + +static int +pg_mule_verifier(const unsigned char *s, int len) +{ + int l, + mbl; + unsigned char c; + + l = mbl = pg_mule_mblen(s); + + if (len < l) + return -1; + + while (--l > 0) + { + c = *++s; + if (!IS_HIGHBIT_SET(c)) + return -1; + } + return mbl; +} + +static int +pg_latin1_verifier(const unsigned char *s, int len) +{ + return 1; +} + +static int +pg_sjis_verifier(const unsigned char *s, int len) +{ + int l, + mbl; + unsigned char c1, + c2; + + l = mbl = pg_sjis_mblen(s); + + if (len < l) + return -1; + + if (l == 1) /* pg_sjis_mblen already verified it */ + return mbl; + + c1 = *s++; + c2 = *s; + if (!ISSJISHEAD(c1) || !ISSJISTAIL(c2)) + return -1; + return mbl; +} + +static int +pg_big5_verifier(const unsigned char *s, int len) +{ + int l, + mbl; + + l = mbl = pg_big5_mblen(s); + + if (len < l) + return -1; + + while (--l > 0) + { + if (*++s == '\0') + return -1; + } + + return mbl; +} + +static int +pg_gbk_verifier(const unsigned char *s, int len) +{ + int l, + mbl; + + l = mbl = pg_gbk_mblen(s); + + if (len < l) + return -1; + + while (--l > 0) + { + if (*++s == '\0') + return -1; + } + + return mbl; +} + +static int +pg_uhc_verifier(const unsigned char *s, int len) +{ + int l, + mbl; + + l = mbl = pg_uhc_mblen(s); + + if (len < l) + return -1; + + while (--l > 0) + { + if (*++s == '\0') + return -1; + } + + return mbl; +} + +static int +pg_gb18030_verifier(const unsigned char *s, int len) +{ + int l; + + if (!IS_HIGHBIT_SET(*s)) + l = 1; /* ASCII */ + else if (len >= 4 && *(s + 1) >= 0x30 && *(s + 1) <= 0x39) + { + /* Should be 4-byte, validate remaining bytes */ + if (*s >= 0x81 && *s <= 0xfe && + *(s + 2) >= 0x81 && *(s + 2) <= 0xfe && + *(s + 3) >= 0x30 && *(s + 3) <= 0x39) + l = 4; + else + l = -1; + } + else if (len >= 2 && *s >= 0x81 && *s <= 0xfe) + { + /* Should be 2-byte, validate */ + if ((*(s + 1) >= 0x40 && *(s + 1) <= 0x7e) || + (*(s + 1) >= 0x80 && *(s + 1) <= 0xfe)) + l = 2; + else + l = -1; + } + else + l = -1; + return l; +} + +static int +pg_utf8_verifier(const unsigned char *s, int len) +{ + int l = pg_utf_mblen(s); + + if (len < l) + return -1; + + if (!pg_utf8_islegal(s, l)) + return -1; + + return l; +} + +/* + * Check for validity of a single UTF-8 encoded character + * + * This directly implements the rules in RFC3629. The bizarre-looking + * restrictions on the second byte are meant to ensure that there isn't + * more than one encoding of a given Unicode character point; that is, + * you may not use a longer-than-necessary byte sequence with high order + * zero bits to represent a character that would fit in fewer bytes. + * To do otherwise is to create security hazards (eg, create an apparent + * non-ASCII character that decodes to plain ASCII). + * + * length is assumed to have been obtained by pg_utf_mblen(), and the + * caller must have checked that that many bytes are present in the buffer. + */ +bool +pg_utf8_islegal(const unsigned char *source, int length) +{ + unsigned char a; + + switch (length) + { + default: + /* reject lengths 5 and 6 for now */ + return false; + case 4: + a = source[3]; + if (a < 0x80 || a > 0xBF) + return false; + /* FALL THRU */ + case 3: + a = source[2]; + if (a < 0x80 || a > 0xBF) + return false; + /* FALL THRU */ + case 2: + a = source[1]; + switch (*source) + { + case 0xE0: + if (a < 0xA0 || a > 0xBF) + return false; + break; + case 0xED: + if (a < 0x80 || a > 0x9F) + return false; + break; + case 0xF0: + if (a < 0x90 || a > 0xBF) + return false; + break; + case 0xF4: + if (a < 0x80 || a > 0x8F) + return false; + break; + default: + if (a < 0x80 || a > 0xBF) + return false; + break; + } + /* FALL THRU */ + case 1: + a = *source; + if (a >= 0x80 && a < 0xC2) + return false; + if (a > 0xF4) + return false; + break; + } + return true; +} + +#ifndef FRONTEND + +/* + * Generic character incrementer function. + * + * Not knowing anything about the properties of the encoding in use, we just + * keep incrementing the last byte until we get a validly-encoded result, + * or we run out of values to try. We don't bother to try incrementing + * higher-order bytes, so there's no growth in runtime for wider characters. + * (If we did try to do that, we'd need to consider the likelihood that 255 + * is not a valid final byte in the encoding.) + */ +static bool +pg_generic_charinc(unsigned char *charptr, int len) +{ + unsigned char *lastbyte = charptr + len - 1; + mbverifier mbverify; + + /* We can just invoke the character verifier directly. */ + mbverify = pg_wchar_table[GetDatabaseEncoding()].mbverify; + + while (*lastbyte < (unsigned char) 255) + { + (*lastbyte)++; + if ((*mbverify) (charptr, len) == len) + return true; + } + + return false; +} + +/* + * UTF-8 character incrementer function. + * + * For a one-byte character less than 0x7F, we just increment the byte. + * + * For a multibyte character, every byte but the first must fall between 0x80 + * and 0xBF; and the first byte must be between 0xC0 and 0xF4. We increment + * the last byte that's not already at its maximum value. If we can't find a + * byte that's less than the maximum allowable value, we simply fail. We also + * need some special-case logic to skip regions used for surrogate pair + * handling, as those should not occur in valid UTF-8. + * + * Note that we don't reset lower-order bytes back to their minimums, since + * we can't afford to make an exhaustive search (see make_greater_string). + */ +static bool +pg_utf8_increment(unsigned char *charptr, int length) +{ + unsigned char a; + unsigned char limit; + + switch (length) + { + default: + /* reject lengths 5 and 6 for now */ + return false; + case 4: + a = charptr[3]; + if (a < 0xBF) + { + charptr[3]++; + break; + } + /* FALL THRU */ + case 3: + a = charptr[2]; + if (a < 0xBF) + { + charptr[2]++; + break; + } + /* FALL THRU */ + case 2: + a = charptr[1]; + switch (*charptr) + { + case 0xED: + limit = 0x9F; + break; + case 0xF4: + limit = 0x8F; + break; + default: + limit = 0xBF; + break; + } + if (a < limit) + { + charptr[1]++; + break; + } + /* FALL THRU */ + case 1: + a = *charptr; + if (a == 0x7F || a == 0xDF || a == 0xEF || a == 0xF4) + return false; + charptr[0]++; + break; + } + + return true; +} + +/* + * EUC-JP character incrementer function. + * + * If the sequence starts with SS2 (0x8e), it must be a two-byte sequence + * representing JIS X 0201 characters with the second byte ranging between + * 0xa1 and 0xdf. We just increment the last byte if it's less than 0xdf, + * and otherwise rewrite the whole sequence to 0xa1 0xa1. + * + * If the sequence starts with SS3 (0x8f), it must be a three-byte sequence + * in which the last two bytes range between 0xa1 and 0xfe. The last byte + * is incremented if possible, otherwise the second-to-last byte. + * + * If the sequence starts with a value other than the above and its MSB + * is set, it must be a two-byte sequence representing JIS X 0208 characters + * with both bytes ranging between 0xa1 and 0xfe. The last byte is + * incremented if possible, otherwise the second-to-last byte. + * + * Otherwise, the sequence is a single-byte ASCII character. It is + * incremented up to 0x7f. + */ +static bool +pg_eucjp_increment(unsigned char *charptr, int length) +{ + unsigned char c1, + c2; + int i; + + c1 = *charptr; + + switch (c1) + { + case SS2: /* JIS X 0201 */ + if (length != 2) + return false; + + c2 = charptr[1]; + + if (c2 >= 0xdf) + charptr[0] = charptr[1] = 0xa1; + else if (c2 < 0xa1) + charptr[1] = 0xa1; + else + charptr[1]++; + break; + + case SS3: /* JIS X 0212 */ + if (length != 3) + return false; + + for (i = 2; i > 0; i--) + { + c2 = charptr[i]; + if (c2 < 0xa1) + { + charptr[i] = 0xa1; + return true; + } + else if (c2 < 0xfe) + { + charptr[i]++; + return true; + } + } + + /* Out of 3-byte code region */ + return false; + + default: + if (IS_HIGHBIT_SET(c1)) /* JIS X 0208? */ + { + if (length != 2) + return false; + + for (i = 1; i >= 0; i--) + { + c2 = charptr[i]; + if (c2 < 0xa1) + { + charptr[i] = 0xa1; + return true; + } + else if (c2 < 0xfe) + { + charptr[i]++; + return true; + } + } + + /* Out of 2 byte code region */ + return false; + } + else + { /* ASCII, single byte */ + if (c1 > 0x7e) + return false; + (*charptr)++; + } + break; + } + + return true; +} +#endif /* !FRONTEND */ + + +/* + *------------------------------------------------------------------- + * encoding info table + * XXX must be sorted by the same order as enum pg_enc (in mb/pg_wchar.h) + *------------------------------------------------------------------- + */ +const pg_wchar_tbl pg_wchar_table[] = { + {pg_ascii2wchar_with_len, pg_wchar2single_with_len, pg_ascii_mblen, pg_ascii_dsplen, pg_ascii_verifier, 1}, /* PG_SQL_ASCII */ + {pg_eucjp2wchar_with_len, pg_wchar2euc_with_len, pg_eucjp_mblen, pg_eucjp_dsplen, pg_eucjp_verifier, 3}, /* PG_EUC_JP */ + {pg_euccn2wchar_with_len, pg_wchar2euc_with_len, pg_euccn_mblen, pg_euccn_dsplen, pg_euccn_verifier, 2}, /* PG_EUC_CN */ + {pg_euckr2wchar_with_len, pg_wchar2euc_with_len, pg_euckr_mblen, pg_euckr_dsplen, pg_euckr_verifier, 3}, /* PG_EUC_KR */ + {pg_euctw2wchar_with_len, pg_wchar2euc_with_len, pg_euctw_mblen, pg_euctw_dsplen, pg_euctw_verifier, 4}, /* PG_EUC_TW */ + {pg_eucjp2wchar_with_len, pg_wchar2euc_with_len, pg_eucjp_mblen, pg_eucjp_dsplen, pg_eucjp_verifier, 3}, /* PG_EUC_JIS_2004 */ + {pg_utf2wchar_with_len, pg_wchar2utf_with_len, pg_utf_mblen, pg_utf_dsplen, pg_utf8_verifier, 4}, /* PG_UTF8 */ + {pg_mule2wchar_with_len, pg_wchar2mule_with_len, pg_mule_mblen, pg_mule_dsplen, pg_mule_verifier, 4}, /* PG_MULE_INTERNAL */ + {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_LATIN1 */ + {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_LATIN2 */ + {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_LATIN3 */ + {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_LATIN4 */ + {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_LATIN5 */ + {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_LATIN6 */ + {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_LATIN7 */ + {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_LATIN8 */ + {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_LATIN9 */ + {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_LATIN10 */ + {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_WIN1256 */ + {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_WIN1258 */ + {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_WIN866 */ + {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_WIN874 */ + {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_KOI8R */ + {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_WIN1251 */ + {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_WIN1252 */ + {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* ISO-8859-5 */ + {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* ISO-8859-6 */ + {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* ISO-8859-7 */ + {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* ISO-8859-8 */ + {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_WIN1250 */ + {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_WIN1253 */ + {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_WIN1254 */ + {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_WIN1255 */ + {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_WIN1257 */ + {pg_latin12wchar_with_len, pg_wchar2single_with_len, pg_latin1_mblen, pg_latin1_dsplen, pg_latin1_verifier, 1}, /* PG_KOI8U */ + {0, 0, pg_sjis_mblen, pg_sjis_dsplen, pg_sjis_verifier, 2}, /* PG_SJIS */ + {0, 0, pg_big5_mblen, pg_big5_dsplen, pg_big5_verifier, 2}, /* PG_BIG5 */ + {0, 0, pg_gbk_mblen, pg_gbk_dsplen, pg_gbk_verifier, 2}, /* PG_GBK */ + {0, 0, pg_uhc_mblen, pg_uhc_dsplen, pg_uhc_verifier, 2}, /* PG_UHC */ + {0, 0, pg_gb18030_mblen, pg_gb18030_dsplen, pg_gb18030_verifier, 4}, /* PG_GB18030 */ + {0, 0, pg_johab_mblen, pg_johab_dsplen, pg_johab_verifier, 3}, /* PG_JOHAB */ + {0, 0, pg_sjis_mblen, pg_sjis_dsplen, pg_sjis_verifier, 2} /* PG_SHIFT_JIS_2004 */ +}; + +/* returns the byte length of a word for mule internal code */ +int +pg_mic_mblen(const unsigned char *mbstr) +{ + return pg_mule_mblen(mbstr); +} + +/* + * Returns the byte length of a multibyte character. + */ +int +pg_encoding_mblen(int encoding, const char *mbstr) +{ + return (PG_VALID_ENCODING(encoding) ? + ((*pg_wchar_table[encoding].mblen) ((const unsigned char *) mbstr)) : + ((*pg_wchar_table[PG_SQL_ASCII].mblen) ((const unsigned char *) mbstr))); +} + +/* + * Returns the display length of a multibyte character. + */ +int +pg_encoding_dsplen(int encoding, const char *mbstr) +{ + return (PG_VALID_ENCODING(encoding) ? + ((*pg_wchar_table[encoding].dsplen) ((const unsigned char *) mbstr)) : + ((*pg_wchar_table[PG_SQL_ASCII].dsplen) ((const unsigned char *) mbstr))); +} + +/* + * Verify the first multibyte character of the given string. + * Return its byte length if good, -1 if bad. (See comments above for + * full details of the mbverify API.) + */ +int +pg_encoding_verifymb(int encoding, const char *mbstr, int len) +{ + return (PG_VALID_ENCODING(encoding) ? + ((*pg_wchar_table[encoding].mbverify) ((const unsigned char *) mbstr, len)) : + ((*pg_wchar_table[PG_SQL_ASCII].mbverify) ((const unsigned char *) mbstr, len))); +} + +/* + * fetch maximum length of a given encoding + */ +int +pg_encoding_max_length(int encoding) +{ + Assert(PG_VALID_ENCODING(encoding)); + + return pg_wchar_table[encoding].maxmblen; +} + +#ifndef FRONTEND + +/* + * fetch maximum length of the encoding for the current database + */ +int +pg_database_encoding_max_length(void) +{ + return pg_wchar_table[GetDatabaseEncoding()].maxmblen; +} + +/* + * get the character incrementer for the encoding for the current database + */ +mbcharacter_incrementer +pg_database_encoding_character_incrementer(void) +{ + /* + * Eventually it might be best to add a field to pg_wchar_table[], but for + * now we just use a switch. + */ + switch (GetDatabaseEncoding()) + { + case PG_UTF8: + return pg_utf8_increment; + + case PG_EUC_JP: + return pg_eucjp_increment; + + default: + return pg_generic_charinc; + } +} + +/* + * Verify mbstr to make sure that it is validly encoded in the current + * database encoding. Otherwise same as pg_verify_mbstr(). + */ +bool +pg_verifymbstr(const char *mbstr, int len, bool noError) +{ + return + pg_verify_mbstr_len(GetDatabaseEncoding(), mbstr, len, noError) >= 0; +} + +/* + * Verify mbstr to make sure that it is validly encoded in the specified + * encoding. + */ +bool +pg_verify_mbstr(int encoding, const char *mbstr, int len, bool noError) +{ + return pg_verify_mbstr_len(encoding, mbstr, len, noError) >= 0; +} + +/* + * Verify mbstr to make sure that it is validly encoded in the specified + * encoding. + * + * mbstr is not necessarily zero terminated; length of mbstr is + * specified by len. + * + * If OK, return length of string in the encoding. + * If a problem is found, return -1 when noError is + * true; when noError is false, ereport() a descriptive message. + */ +int +pg_verify_mbstr_len(int encoding, const char *mbstr, int len, bool noError) +{ + mbverifier mbverify; + int mb_len; + + Assert(PG_VALID_ENCODING(encoding)); + + /* + * In single-byte encodings, we need only reject nulls (\0). + */ + if (pg_encoding_max_length(encoding) <= 1) + { + const char *nullpos = memchr(mbstr, 0, len); + + if (nullpos == NULL) + return len; + if (noError) + return -1; + report_invalid_encoding(encoding, nullpos, 1); + } + + /* fetch function pointer just once */ + mbverify = pg_wchar_table[encoding].mbverify; + + mb_len = 0; + + while (len > 0) + { + int l; + + /* fast path for ASCII-subset characters */ + if (!IS_HIGHBIT_SET(*mbstr)) + { + if (*mbstr != '\0') + { + mb_len++; + mbstr++; + len--; + continue; + } + if (noError) + return -1; + report_invalid_encoding(encoding, mbstr, len); + } + + l = (*mbverify) ((const unsigned char *) mbstr, len); + + if (l < 0) + { + if (noError) + return -1; + report_invalid_encoding(encoding, mbstr, len); + } + + mbstr += l; + len -= l; + mb_len++; + } + return mb_len; +} + +/* + * check_encoding_conversion_args: check arguments of a conversion function + * + * "expected" arguments can be either an encoding ID or -1 to indicate that + * the caller will check whether it accepts the ID. + * + * Note: the errors here are not really user-facing, so elog instead of + * ereport seems sufficient. Also, we trust that the "expected" encoding + * arguments are valid encoding IDs, but we don't trust the actuals. + */ +void +check_encoding_conversion_args(int src_encoding, + int dest_encoding, + int len, + int expected_src_encoding, + int expected_dest_encoding) +{ + if (!PG_VALID_ENCODING(src_encoding)) + elog(ERROR, "invalid source encoding ID: %d", src_encoding); + if (src_encoding != expected_src_encoding && expected_src_encoding >= 0) + elog(ERROR, "expected source encoding \"%s\", but got \"%s\"", + pg_enc2name_tbl[expected_src_encoding].name, + pg_enc2name_tbl[src_encoding].name); + if (!PG_VALID_ENCODING(dest_encoding)) + elog(ERROR, "invalid destination encoding ID: %d", dest_encoding); + if (dest_encoding != expected_dest_encoding && expected_dest_encoding >= 0) + elog(ERROR, "expected destination encoding \"%s\", but got \"%s\"", + pg_enc2name_tbl[expected_dest_encoding].name, + pg_enc2name_tbl[dest_encoding].name); + if (len < 0) + elog(ERROR, "encoding conversion length must not be negative"); +} + +/* + * report_invalid_encoding: complain about invalid multibyte character + * + * note: len is remaining length of string, not length of character; + * len must be greater than zero, as we always examine the first byte. + */ +void +report_invalid_encoding(int encoding, const char *mbstr, int len) +{ + int l = pg_encoding_mblen(encoding, mbstr); + char buf[8 * 5 + 1]; + char *p = buf; + int j, + jlimit; + + jlimit = Min(l, len); + jlimit = Min(jlimit, 8); /* prevent buffer overrun */ + + for (j = 0; j < jlimit; j++) + { + p += sprintf(p, "0x%02x", (unsigned char) mbstr[j]); + if (j < jlimit - 1) + p += sprintf(p, " "); + } + + ereport(ERROR, + (errcode(ERRCODE_CHARACTER_NOT_IN_REPERTOIRE), + errmsg("invalid byte sequence for encoding \"%s\": %s", + pg_enc2name_tbl[encoding].name, + buf))); +} + +/* + * report_untranslatable_char: complain about untranslatable character + * + * note: len is remaining length of string, not length of character; + * len must be greater than zero, as we always examine the first byte. + */ +void +report_untranslatable_char(int src_encoding, int dest_encoding, + const char *mbstr, int len) +{ + int l = pg_encoding_mblen(src_encoding, mbstr); + char buf[8 * 5 + 1]; + char *p = buf; + int j, + jlimit; + + jlimit = Min(l, len); + jlimit = Min(jlimit, 8); /* prevent buffer overrun */ + + for (j = 0; j < jlimit; j++) + { + p += sprintf(p, "0x%02x", (unsigned char) mbstr[j]); + if (j < jlimit - 1) + p += sprintf(p, " "); + } + + ereport(ERROR, + (errcode(ERRCODE_UNTRANSLATABLE_CHARACTER), + errmsg("character with byte sequence %s in encoding \"%s\" has no equivalent in encoding \"%s\"", + buf, + pg_enc2name_tbl[src_encoding].name, + pg_enc2name_tbl[dest_encoding].name))); +} + +#endif /* !FRONTEND */ diff --git a/libpq/win32/crypt.c b/libpq/win32/crypt.c new file mode 100644 index 0000000..6a902ef --- /dev/null +++ b/libpq/win32/crypt.c @@ -0,0 +1,1085 @@ +/* src/port/crypt.c */ +/* $NetBSD: crypt.c,v 1.18 2001/03/01 14:37:35 wiz Exp $ */ + +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Tom Truscott. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +#if 0 +static char sccsid[] = "@(#)crypt.c 8.1.1.1 (Berkeley) 8/18/93"; +#else +__RCSID("$NetBSD: crypt.c,v 1.18 2001/03/01 14:37:35 wiz Exp $"); +#endif +#endif /* not lint */ + +#include "c.h" + +#include + +#ifndef WIN32 +#include +#endif + +static int des_setkey(const char *key); +static int des_cipher(const char *in, char *out, long salt, int num_iter); + +/* + * UNIX password, and DES, encryption. + * By Tom Truscott, trt@rti.rti.org, + * from algorithms by Robert W. Baldwin and James Gillogly. + * + * References: + * "Mathematical Cryptology for Computer Scientists and Mathematicians," + * by Wayne Patterson, 1987, ISBN 0-8476-7438-X. + * + * "Password Security: A Case History," R. Morris and Ken Thompson, + * Communications of the ACM, vol. 22, pp. 594-597, Nov. 1979. + * + * "DES will be Totally Insecure within Ten Years," M.E. Hellman, + * IEEE Spectrum, vol. 16, pp. 32-39, July 1979. + */ + +/* ===== Configuration ==================== */ + +/* + * define "MUST_ALIGN" if your compiler cannot load/store + * long integers at arbitrary (e.g. odd) memory locations. + * (Either that or never pass unaligned addresses to des_cipher!) + */ +/* #define MUST_ALIGN */ + +#ifdef CHAR_BITS +#if CHAR_BITS != 8 +#error C_block structure assumes 8 bit characters +#endif +#endif + +/* + * define "B64" to be the declaration for a 64 bit integer. + * XXX this feature is currently unused, see "endian" comment below. + */ +/* #define B64 int64 */ + +/* + * define "LARGEDATA" to get faster permutations, by using about 72 kilobytes + * of lookup tables. This speeds up des_setkey() and des_cipher(), but has + * little effect on crypt(). + */ +/* #define LARGEDATA */ + +/* compile with "-DSTATIC=void" when profiling */ +#ifndef STATIC +#define STATIC static void +#endif + +/* + * Define the "int32_t" type for integral type with a width of at least + * 32 bits. + */ +typedef int int32_t; + +/* ==================================== */ + +#define _PASSWORD_EFMT1 '_' /* extended encryption format */ + +/* + * Cipher-block representation (Bob Baldwin): + * + * DES operates on groups of 64 bits, numbered 1..64 (sigh). One + * representation is to store one bit per byte in an array of bytes. Bit N of + * the NBS spec is stored as the LSB of the Nth byte (index N-1) in the array. + * Another representation stores the 64 bits in 8 bytes, with bits 1..8 in the + * first byte, 9..16 in the second, and so on. The DES spec apparently has + * bit 1 in the MSB of the first byte, but that is particularly noxious so we + * bit-reverse each byte so that bit 1 is the LSB of the first byte, bit 8 is + * the MSB of the first byte. Specifically, the 64-bit input data and key are + * converted to LSB format, and the output 64-bit block is converted back into + * MSB format. + * + * DES operates internally on groups of 32 bits which are expanded to 48 bits + * by permutation E and shrunk back to 32 bits by the S boxes. To speed up + * the computation, the expansion is applied only once, the expanded + * representation is maintained during the encryption, and a compression + * permutation is applied only at the end. To speed up the S-box lookups, + * the 48 bits are maintained as eight 6 bit groups, one per byte, which + * directly feed the eight S-boxes. Within each byte, the 6 bits are the + * most significant ones. The low two bits of each byte are zero. (Thus, + * bit 1 of the 48 bit E expansion is stored as the "4"-valued bit of the + * first byte in the eight byte representation, bit 2 of the 48 bit value is + * the "8"-valued bit, and so on.) In fact, a combined "SPE"-box lookup is + * used, in which the output is the 64 bit result of an S-box lookup which + * has been permuted by P and expanded by E, and is ready for use in the next + * iteration. Two 32-bit wide tables, SPE[0] and SPE[1], are used for this + * lookup. Since each byte in the 48 bit path is a multiple of four, indexed + * lookup of SPE[0] and SPE[1] is simple and fast. The key schedule and + * "salt" are also converted to this 8*(6+2) format. The SPE table size is + * 8*64*8 = 4K bytes. + * + * To speed up bit-parallel operations (such as XOR), the 8 byte + * representation is "union"ed with 32 bit values "i0" and "i1", and, on + * machines which support it, a 64 bit value "b64". This data structure, + * "C_block", has two problems. First, alignment restrictions must be + * honored. Second, the byte-order (e.g. little-endian or big-endian) of + * the architecture becomes visible. + * + * The byte-order problem is unfortunate, since on the one hand it is good + * to have a machine-independent C_block representation (bits 1..8 in the + * first byte, etc.), and on the other hand it is good for the LSB of the + * first byte to be the LSB of i0. We cannot have both these things, so we + * currently use the "little-endian" representation and avoid any multi-byte + * operations that depend on byte order. This largely precludes use of the + * 64-bit datatype since the relative order of i0 and i1 are unknown. It + * also inhibits grouping the SPE table to look up 12 bits at a time. (The + * 12 bits can be stored in a 16-bit field with 3 low-order zeroes and 1 + * high-order zero, providing fast indexing into a 64-bit wide SPE.) On the + * other hand, 64-bit datatypes are currently rare, and a 12-bit SPE lookup + * requires a 128 kilobyte table, so perhaps this is not a big loss. + * + * Permutation representation (Jim Gillogly): + * + * A transformation is defined by its effect on each of the 8 bytes of the + * 64-bit input. For each byte we give a 64-bit output that has the bits in + * the input distributed appropriately. The transformation is then the OR + * of the 8 sets of 64-bits. This uses 8*256*8 = 16K bytes of storage for + * each transformation. Unless LARGEDATA is defined, however, a more compact + * table is used which looks up 16 4-bit "chunks" rather than 8 8-bit chunks. + * The smaller table uses 16*16*8 = 2K bytes for each transformation. This + * is slower but tolerable, particularly for password encryption in which + * the SPE transformation is iterated many times. The small tables total 9K + * bytes, the large tables total 72K bytes. + * + * The transformations used are: + * IE3264: MSB->LSB conversion, initial permutation, and expansion. + * This is done by collecting the 32 even-numbered bits and applying + * a 32->64 bit transformation, and then collecting the 32 odd-numbered + * bits and applying the same transformation. Since there are only + * 32 input bits, the IE3264 transformation table is half the size of + * the usual table. + * CF6464: Compression, final permutation, and LSB->MSB conversion. + * This is done by two trivial 48->32 bit compressions to obtain + * a 64-bit block (the bit numbering is given in the "CIFP" table) + * followed by a 64->64 bit "cleanup" transformation. (It would + * be possible to group the bits in the 64-bit block so that 2 + * identical 32->32 bit transformations could be used instead, + * saving a factor of 4 in space and possibly 2 in time, but + * byte-ordering and other complications rear their ugly head. + * Similar opportunities/problems arise in the key schedule + * transforms.) + * PC1ROT: MSB->LSB, PC1 permutation, rotate, and PC2 permutation. + * This admittedly baroque 64->64 bit transformation is used to + * produce the first code (in 8*(6+2) format) of the key schedule. + * PC2ROT[0]: Inverse PC2 permutation, rotate, and PC2 permutation. + * It would be possible to define 15 more transformations, each + * with a different rotation, to generate the entire key schedule. + * To save space, however, we instead permute each code into the + * next by using a transformation that "undoes" the PC2 permutation, + * rotates the code, and then applies PC2. Unfortunately, PC2 + * transforms 56 bits into 48 bits, dropping 8 bits, so PC2 is not + * invertible. We get around that problem by using a modified PC2 + * which retains the 8 otherwise-lost bits in the unused low-order + * bits of each byte. The low-order bits are cleared when the + * codes are stored into the key schedule. + * PC2ROT[1]: Same as PC2ROT[0], but with two rotations. + * This is faster than applying PC2ROT[0] twice, + * + * The Bell Labs "salt" (Bob Baldwin): + * + * The salting is a simple permutation applied to the 48-bit result of E. + * Specifically, if bit i (1 <= i <= 24) of the salt is set then bits i and + * i+24 of the result are swapped. The salt is thus a 24 bit number, with + * 16777216 possible values. (The original salt was 12 bits and could not + * swap bits 13..24 with 36..48.) + * + * It is possible, but ugly, to warp the SPE table to account for the salt + * permutation. Fortunately, the conditional bit swapping requires only + * about four machine instructions and can be done on-the-fly with about an + * 8% performance penalty. + */ + +typedef union +{ + unsigned char b[8]; + struct + { + int32_t i0; + int32_t i1; + } b32; +#if defined(B64) + B64 b64; +#endif +} C_block; + +/* + * Convert twenty-four-bit long in host-order + * to six bits (and 2 low-order zeroes) per char little-endian format. + */ +#define TO_SIX_BIT(rslt, src) { \ + C_block cvt; \ + cvt.b[0] = src; src >>= 6; \ + cvt.b[1] = src; src >>= 6; \ + cvt.b[2] = src; src >>= 6; \ + cvt.b[3] = src; \ + rslt = (cvt.b32.i0 & 0x3f3f3f3fL) << 2; \ + } + +/* + * These macros may someday permit efficient use of 64-bit integers. + */ +#define ZERO(d,d0,d1) d0 = 0, d1 = 0 +#define LOAD(d,d0,d1,bl) d0 = (bl).b32.i0, d1 = (bl).b32.i1 +#define LOADREG(d,d0,d1,s,s0,s1) d0 = s0, d1 = s1 +#define OR(d,d0,d1,bl) d0 |= (bl).b32.i0, d1 |= (bl).b32.i1 +#define STORE(s,s0,s1,bl) (bl).b32.i0 = s0, (bl).b32.i1 = s1 +#define DCL_BLOCK(d,d0,d1) int32_t d0, d1 + +#if defined(LARGEDATA) + /* Waste memory like crazy. Also, do permutations in line */ +#define LGCHUNKBITS 3 +#define CHUNKBITS (1<> 4]; + OR(D, D0, D1, *tp); + p += (1 << CHUNKBITS); + } while (--chars_in > 0); + STORE(D, D0, D1, *out); +} +#endif /* LARGEDATA */ + + +/* ===== (mostly) Standard DES Tables ==================== */ + +static const unsigned char IP[] = { /* initial permutation */ + 58, 50, 42, 34, 26, 18, 10, 2, + 60, 52, 44, 36, 28, 20, 12, 4, + 62, 54, 46, 38, 30, 22, 14, 6, + 64, 56, 48, 40, 32, 24, 16, 8, + 57, 49, 41, 33, 25, 17, 9, 1, + 59, 51, 43, 35, 27, 19, 11, 3, + 61, 53, 45, 37, 29, 21, 13, 5, + 63, 55, 47, 39, 31, 23, 15, 7, +}; + +/* The final permutation is the inverse of IP - no table is necessary */ + +static const unsigned char ExpandTr[] = { /* expansion operation */ + 32, 1, 2, 3, 4, 5, + 4, 5, 6, 7, 8, 9, + 8, 9, 10, 11, 12, 13, + 12, 13, 14, 15, 16, 17, + 16, 17, 18, 19, 20, 21, + 20, 21, 22, 23, 24, 25, + 24, 25, 26, 27, 28, 29, + 28, 29, 30, 31, 32, 1, +}; + +static const unsigned char PC1[] = { /* permuted choice table 1 */ + 57, 49, 41, 33, 25, 17, 9, + 1, 58, 50, 42, 34, 26, 18, + 10, 2, 59, 51, 43, 35, 27, + 19, 11, 3, 60, 52, 44, 36, + + 63, 55, 47, 39, 31, 23, 15, + 7, 62, 54, 46, 38, 30, 22, + 14, 6, 61, 53, 45, 37, 29, + 21, 13, 5, 28, 20, 12, 4, +}; + +static const unsigned char Rotates[] = { /* PC1 rotation schedule */ + 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1, +}; + +/* note: each "row" of PC2 is left-padded with bits that make it invertible */ +static const unsigned char PC2[] = { /* permuted choice table 2 */ + 9, 18, 14, 17, 11, 24, 1, 5, + 22, 25, 3, 28, 15, 6, 21, 10, + 35, 38, 23, 19, 12, 4, 26, 8, + 43, 54, 16, 7, 27, 20, 13, 2, + + 0, 0, 41, 52, 31, 37, 47, 55, + 0, 0, 30, 40, 51, 45, 33, 48, + 0, 0, 44, 49, 39, 56, 34, 53, + 0, 0, 46, 42, 50, 36, 29, 32, +}; + +static const unsigned char S[8][64] = { /* 48->32 bit substitution tables */ + /* S[1] */ + {14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7, + 0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8, + 4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0, + 15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13}, + /* S[2] */ + {15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10, + 3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5, + 0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15, + 13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9}, + /* S[3] */ + {10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8, + 13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1, + 13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7, + 1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12}, + /* S[4] */ + {7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15, + 13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9, + 10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4, + 3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14}, + /* S[5] */ + {2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9, + 14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6, + 4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14, + 11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3}, + /* S[6] */ + {12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11, + 10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8, + 9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6, + 4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13}, + /* S[7] */ + {4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1, + 13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6, + 1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2, + 6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12}, + /* S[8] */ + {13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7, + 1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2, + 7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8, + 2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11} +}; + +static const unsigned char P32Tr[] = { /* 32-bit permutation function */ + 16, 7, 20, 21, + 29, 12, 28, 17, + 1, 15, 23, 26, + 5, 18, 31, 10, + 2, 8, 24, 14, + 32, 27, 3, 9, + 19, 13, 30, 6, + 22, 11, 4, 25, +}; + +static const unsigned char CIFP[] = { /* compressed/interleaved permutation */ + 1, 2, 3, 4, 17, 18, 19, 20, + 5, 6, 7, 8, 21, 22, 23, 24, + 9, 10, 11, 12, 25, 26, 27, 28, + 13, 14, 15, 16, 29, 30, 31, 32, + + 33, 34, 35, 36, 49, 50, 51, 52, + 37, 38, 39, 40, 53, 54, 55, 56, + 41, 42, 43, 44, 57, 58, 59, 60, + 45, 46, 47, 48, 61, 62, 63, 64, +}; + +static const unsigned char itoa64[] = /* 0..63 => ascii-64 */ +"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + + +/* ===== Tables that are initialized at run time ==================== */ + + +static unsigned char a64toi[128]; /* ascii-64 => 0..63 */ + +/* Initial key schedule permutation */ +static C_block PC1ROT[64 / CHUNKBITS][1 << CHUNKBITS]; + +/* Subsequent key schedule rotation permutations */ +static C_block PC2ROT[2][64 / CHUNKBITS][1 << CHUNKBITS]; + +/* Initial permutation/expansion table */ +static C_block IE3264[32 / CHUNKBITS][1 << CHUNKBITS]; + +/* Table that combines the S, P, and E operations. */ +static int32_t SPE[2][8][64]; + +/* compressed/interleaved => final permutation table */ +static C_block CF6464[64 / CHUNKBITS][1 << CHUNKBITS]; + + +/* ==================================== */ + + +static C_block constdatablock; /* encryption constant */ +static char cryptresult[1 + 4 + 4 + 11 + 1]; /* encrypted result */ + +extern char *__md5crypt(const char *, const char *); /* XXX */ +extern char *__bcrypt(const char *, const char *); /* XXX */ + + +/* + * Return a pointer to static data consisting of the "setting" + * followed by an encryption produced by the "key" and "setting". + */ +char * +crypt(key, setting) +const char *key; +const char *setting; +{ + char *encp; + int32_t i; + int t; + int32_t salt; + int num_iter, + salt_size; + C_block keyblock, + rsltblock; + +#if 0 + /* Non-DES encryption schemes hook in here. */ + if (setting[0] == _PASSWORD_NONDES) + { + switch (setting[1]) + { + case '2': + return (__bcrypt(key, setting)); + case '1': + default: + return (__md5crypt(key, setting)); + } + } +#endif + + for (i = 0; i < 8; i++) + { + if ((t = 2 * (unsigned char) (*key)) != 0) + key++; + keyblock.b[i] = t; + } + if (des_setkey((char *) keyblock.b)) /* also initializes "a64toi" */ + return (NULL); + + encp = &cryptresult[0]; + switch (*setting) + { + case _PASSWORD_EFMT1: + + /* + * Involve the rest of the password 8 characters at a time. + */ + while (*key) + { + if (des_cipher((char *) (void *) &keyblock, + (char *) (void *) &keyblock, 0L, 1)) + return (NULL); + for (i = 0; i < 8; i++) + { + if ((t = 2 * (unsigned char) (*key)) != 0) + key++; + keyblock.b[i] ^= t; + } + if (des_setkey((char *) keyblock.b)) + return (NULL); + } + + *encp++ = *setting++; + + /* get iteration count */ + num_iter = 0; + for (i = 4; --i >= 0;) + { + if ((t = (unsigned char) setting[i]) == '\0') + t = '.'; + encp[i] = t; + num_iter = (num_iter << 6) | a64toi[t]; + } + setting += 4; + encp += 4; + salt_size = 4; + break; + default: + num_iter = 25; + salt_size = 2; + } + + salt = 0; + for (i = salt_size; --i >= 0;) + { + if ((t = (unsigned char) setting[i]) == '\0') + t = '.'; + encp[i] = t; + salt = (salt << 6) | a64toi[t]; + } + encp += salt_size; + if (des_cipher((char *) (void *) &constdatablock, + (char *) (void *) &rsltblock, salt, num_iter)) + return (NULL); + + /* + * Encode the 64 cipher bits as 11 ascii characters. + */ + i = ((int32_t) ((rsltblock.b[0] << 8) | rsltblock.b[1]) << 8) | + rsltblock.b[2]; + encp[3] = itoa64[i & 0x3f]; + i >>= 6; + encp[2] = itoa64[i & 0x3f]; + i >>= 6; + encp[1] = itoa64[i & 0x3f]; + i >>= 6; + encp[0] = itoa64[i]; + encp += 4; + i = ((int32_t) ((rsltblock.b[3] << 8) | rsltblock.b[4]) << 8) | + rsltblock.b[5]; + encp[3] = itoa64[i & 0x3f]; + i >>= 6; + encp[2] = itoa64[i & 0x3f]; + i >>= 6; + encp[1] = itoa64[i & 0x3f]; + i >>= 6; + encp[0] = itoa64[i]; + encp += 4; + i = ((int32_t) ((rsltblock.b[6]) << 8) | rsltblock.b[7]) << 2; + encp[2] = itoa64[i & 0x3f]; + i >>= 6; + encp[1] = itoa64[i & 0x3f]; + i >>= 6; + encp[0] = itoa64[i]; + + encp[3] = 0; + + return (cryptresult); +} + + +/* + * The Key Schedule, filled in by des_setkey() or setkey(). + */ +#define KS_SIZE 16 +static C_block KS[KS_SIZE]; + +static volatile int des_ready = 0; + +/* + * Set up the key schedule from the key. + */ +static int +des_setkey(key) +const char *key; +{ + DCL_BLOCK(K, K0, K1); + C_block *ptabp; + int i; + + if (!des_ready) + init_des(); + + PERM6464(K, K0, K1, (unsigned char *) key, (C_block *) PC1ROT); + key = (char *) &KS[0]; + STORE(K & ~0x03030303L, K0 & ~0x03030303L, K1, *(C_block *) key); + for (i = 1; i < 16; i++) + { + key += sizeof(C_block); + STORE(K, K0, K1, *(C_block *) key); + ptabp = (C_block *) PC2ROT[Rotates[i] - 1]; + PERM6464(K, K0, K1, (unsigned char *) key, ptabp); + STORE(K & ~0x03030303L, K0 & ~0x03030303L, K1, *(C_block *) key); + } + return (0); +} + +/* + * Encrypt (or decrypt if num_iter < 0) the 8 chars at "in" with abs(num_iter) + * iterations of DES, using the given 24-bit salt and the pre-computed key + * schedule, and store the resulting 8 chars at "out" (in == out is permitted). + * + * NOTE: the performance of this routine is critically dependent on your + * compiler and machine architecture. + */ +static int +des_cipher(in, out, salt, num_iter) +const char *in; +char *out; +long salt; +int num_iter; +{ + /* variables that we want in registers, most important first */ +#if defined(pdp11) + int j; +#endif + int32_t L0, + L1, + R0, + R1, + k; + C_block *kp; + int ks_inc, + loop_count; + C_block B; + + L0 = salt; + TO_SIX_BIT(salt, L0); /* convert to 4*(6+2) format */ + +#if defined(__vax__) || defined(pdp11) + salt = ~salt; /* "x &~ y" is faster than "x & y". */ +#define SALT (~salt) +#else +#define SALT salt +#endif + +#if defined(MUST_ALIGN) + B.b[0] = in[0]; + B.b[1] = in[1]; + B.b[2] = in[2]; + B.b[3] = in[3]; + B.b[4] = in[4]; + B.b[5] = in[5]; + B.b[6] = in[6]; + B.b[7] = in[7]; + LOAD(L, L0, L1, B); +#else + LOAD(L, L0, L1, *(C_block *) in); +#endif + LOADREG(R, R0, R1, L, L0, L1); + L0 &= 0x55555555L; + L1 &= 0x55555555L; + L0 = (L0 << 1) | L1; /* L0 is the even-numbered input bits */ + R0 &= 0xaaaaaaaaL; + R1 = (R1 >> 1) & 0x55555555L; + L1 = R0 | R1; /* L1 is the odd-numbered input bits */ + STORE(L, L0, L1, B); + PERM3264(L, L0, L1, B.b, (C_block *) IE3264); /* even bits */ + PERM3264(R, R0, R1, B.b + 4, (C_block *) IE3264); /* odd bits */ + + if (num_iter >= 0) + { /* encryption */ + kp = &KS[0]; + ks_inc = sizeof(*kp); + } + else + { /* decryption */ + num_iter = -num_iter; + kp = &KS[KS_SIZE - 1]; + ks_inc = -(long) sizeof(*kp); + } + + while (--num_iter >= 0) + { + loop_count = 8; + do + { + +#define SPTAB(t, i) \ + (*(int32_t *)((unsigned char *)(t) + (i)*(sizeof(int32_t)/4))) +#if defined(gould) + /* use this if B.b[i] is evaluated just once ... */ +#define DOXOR(x,y,i) x^=SPTAB(SPE[0][i],B.b[i]); y^=SPTAB(SPE[1][i],B.b[i]); +#else +#if defined(pdp11) + /* use this if your "long" int indexing is slow */ +#define DOXOR(x,y,i) j=B.b[i]; x^=SPTAB(SPE[0][i],j); y^=SPTAB(SPE[1][i],j); +#else + /* use this if "k" is allocated to a register ... */ +#define DOXOR(x,y,i) k=B.b[i]; x^=SPTAB(SPE[0][i],k); y^=SPTAB(SPE[1][i],k); +#endif +#endif + +#define CRUNCH(p0, p1, q0, q1) \ + k = ((q0) ^ (q1)) & SALT; \ + B.b32.i0 = k ^ (q0) ^ kp->b32.i0; \ + B.b32.i1 = k ^ (q1) ^ kp->b32.i1; \ + kp = (C_block *)((char *)kp+ks_inc); \ + \ + DOXOR(p0, p1, 0); \ + DOXOR(p0, p1, 1); \ + DOXOR(p0, p1, 2); \ + DOXOR(p0, p1, 3); \ + DOXOR(p0, p1, 4); \ + DOXOR(p0, p1, 5); \ + DOXOR(p0, p1, 6); \ + DOXOR(p0, p1, 7); + + CRUNCH(L0, L1, R0, R1); + CRUNCH(R0, R1, L0, L1); + } while (--loop_count != 0); + kp = (C_block *) ((char *) kp - (ks_inc * KS_SIZE)); + + + /* swap L and R */ + L0 ^= R0; + L1 ^= R1; + R0 ^= L0; + R1 ^= L1; + L0 ^= R0; + L1 ^= R1; + } + + /* store the encrypted (or decrypted) result */ + L0 = ((L0 >> 3) & 0x0f0f0f0fL) | ((L1 << 1) & 0xf0f0f0f0L); + L1 = ((R0 >> 3) & 0x0f0f0f0fL) | ((R1 << 1) & 0xf0f0f0f0L); + STORE(L, L0, L1, B); + PERM6464(L, L0, L1, B.b, (C_block *) CF6464); +#if defined(MUST_ALIGN) + STORE(L, L0, L1, B); + out[0] = B.b[0]; + out[1] = B.b[1]; + out[2] = B.b[2]; + out[3] = B.b[3]; + out[4] = B.b[4]; + out[5] = B.b[5]; + out[6] = B.b[6]; + out[7] = B.b[7]; +#else + STORE(L, L0, L1, *(C_block *) out); +#endif + return (0); +} + + +/* + * Initialize various tables. This need only be done once. It could even be + * done at compile time, if the compiler were capable of that sort of thing. + */ +STATIC +init_des() +{ + int i, + j; + int32_t k; + int tableno; + static unsigned char perm[64], + tmp32[32]; /* "static" for speed */ + +/* static volatile long init_start = 0; not used */ + + /* + * table that converts chars "./0-9A-Za-z"to integers 0-63. + */ + for (i = 0; i < 64; i++) + a64toi[itoa64[i]] = i; + + /* + * PC1ROT - bit reverse, then PC1, then Rotate, then PC2. + */ + for (i = 0; i < 64; i++) + perm[i] = 0; + for (i = 0; i < 64; i++) + { + if ((k = PC2[i]) == 0) + continue; + k += Rotates[0] - 1; + if ((k % 28) < Rotates[0]) + k -= 28; + k = PC1[k]; + if (k > 0) + { + k--; + k = (k | 07) - (k & 07); + k++; + } + perm[i] = k; + } +#ifdef DEBUG + prtab("pc1tab", perm, 8); +#endif + init_perm(PC1ROT, perm, 8, 8); + + /* + * PC2ROT - PC2 inverse, then Rotate (once or twice), then PC2. + */ + for (j = 0; j < 2; j++) + { + unsigned char pc2inv[64]; + + for (i = 0; i < 64; i++) + perm[i] = pc2inv[i] = 0; + for (i = 0; i < 64; i++) + { + if ((k = PC2[i]) == 0) + continue; + pc2inv[k - 1] = i + 1; + } + for (i = 0; i < 64; i++) + { + if ((k = PC2[i]) == 0) + continue; + k += j; + if ((k % 28) <= j) + k -= 28; + perm[i] = pc2inv[k]; + } +#ifdef DEBUG + prtab("pc2tab", perm, 8); +#endif + init_perm(PC2ROT[j], perm, 8, 8); + } + + /* + * Bit reverse, then initial permutation, then expansion. + */ + for (i = 0; i < 8; i++) + { + for (j = 0; j < 8; j++) + { + k = (j < 2) ? 0 : IP[ExpandTr[i * 6 + j - 2] - 1]; + if (k > 32) + k -= 32; + else if (k > 0) + k--; + if (k > 0) + { + k--; + k = (k | 07) - (k & 07); + k++; + } + perm[i * 8 + j] = k; + } + } +#ifdef DEBUG + prtab("ietab", perm, 8); +#endif + init_perm(IE3264, perm, 4, 8); + + /* + * Compression, then final permutation, then bit reverse. + */ + for (i = 0; i < 64; i++) + { + k = IP[CIFP[i] - 1]; + if (k > 0) + { + k--; + k = (k | 07) - (k & 07); + k++; + } + perm[k - 1] = i + 1; + } +#ifdef DEBUG + prtab("cftab", perm, 8); +#endif + init_perm(CF6464, perm, 8, 8); + + /* + * SPE table + */ + for (i = 0; i < 48; i++) + perm[i] = P32Tr[ExpandTr[i] - 1]; + for (tableno = 0; tableno < 8; tableno++) + { + for (j = 0; j < 64; j++) + { + k = (((j >> 0) & 01) << 5) | + (((j >> 1) & 01) << 3) | + (((j >> 2) & 01) << 2) | + (((j >> 3) & 01) << 1) | + (((j >> 4) & 01) << 0) | + (((j >> 5) & 01) << 4); + k = S[tableno][k]; + k = (((k >> 3) & 01) << 0) | + (((k >> 2) & 01) << 1) | + (((k >> 1) & 01) << 2) | + (((k >> 0) & 01) << 3); + for (i = 0; i < 32; i++) + tmp32[i] = 0; + for (i = 0; i < 4; i++) + tmp32[4 * tableno + i] = (k >> i) & 01; + k = 0; + for (i = 24; --i >= 0;) + k = (k << 1) | tmp32[perm[i] - 1]; + TO_SIX_BIT(SPE[0][tableno][j], k); + k = 0; + for (i = 24; --i >= 0;) + k = (k << 1) | tmp32[perm[i + 24] - 1]; + TO_SIX_BIT(SPE[1][tableno][j], k); + } + } + + des_ready = 1; +} + +/* + * Initialize "perm" to represent transformation "p", which rearranges + * (perhaps with expansion and/or contraction) one packed array of bits + * (of size "chars_in" characters) into another array (of size "chars_out" + * characters). + * + * "perm" must be all-zeroes on entry to this routine. + */ +STATIC +init_perm(perm, p, chars_in, chars_out) +C_block perm[64 / CHUNKBITS][1 << CHUNKBITS]; +unsigned char p[64]; +int chars_in, + chars_out; +{ + int i, + j, + k, + l; + + for (k = 0; k < chars_out * 8; k++) + { /* each output bit position */ + l = p[k] - 1; /* where this bit comes from */ + if (l < 0) + continue; /* output bit is always 0 */ + i = l >> LGCHUNKBITS; /* which chunk this bit comes from */ + l = 1 << (l & (CHUNKBITS - 1)); /* mask for this bit */ + for (j = 0; j < (1 << CHUNKBITS); j++) + { /* each chunk value */ + if ((j & l) != 0) + perm[i][j].b[k >> 3] |= 1 << (k & 07); + } + } +} + +/* + * "setkey" routine (for backwards compatibility) + */ +#ifdef NOT_USED +int +setkey(key) +const char *key; +{ + int i, + j, + k; + C_block keyblock; + + for (i = 0; i < 8; i++) + { + k = 0; + for (j = 0; j < 8; j++) + { + k <<= 1; + k |= (unsigned char) *key++; + } + keyblock.b[i] = k; + } + return (des_setkey((char *) keyblock.b)); +} + +/* + * "encrypt" routine (for backwards compatibility) + */ +static int +encrypt(block, flag) +char *block; +int flag; +{ + int i, + j, + k; + C_block cblock; + + for (i = 0; i < 8; i++) + { + k = 0; + for (j = 0; j < 8; j++) + { + k <<= 1; + k |= (unsigned char) *block++; + } + cblock.b[i] = k; + } + if (des_cipher((char *) &cblock, (char *) &cblock, 0L, (flag ? -1 : 1))) + return (1); + for (i = 7; i >= 0; i--) + { + k = cblock.b[i]; + for (j = 7; j >= 0; j--) + { + *--block = k & 01; + k >>= 1; + } + } + return (0); +} +#endif + +#ifdef DEBUG +STATIC +prtab(s, t, num_rows) +char *s; +unsigned char *t; +int num_rows; +{ + int i, + j; + + (void) printf("%s:\n", s); + for (i = 0; i < num_rows; i++) + { + for (j = 0; j < 8; j++) + (void) printf("%3d", t[i * 8 + j]); + (void) printf("\n"); + } + (void) printf("\n"); +} + +#endif diff --git a/libpq/win32/getaddrinfo.c b/libpq/win32/getaddrinfo.c new file mode 100644 index 0000000..90c1b87 --- /dev/null +++ b/libpq/win32/getaddrinfo.c @@ -0,0 +1,412 @@ +/*------------------------------------------------------------------------- + * + * getaddrinfo.c + * Support getaddrinfo() on platforms that don't have it. + * + * We also supply getnameinfo() here, assuming that the platform will have + * it if and only if it has getaddrinfo(). If this proves false on some + * platform, we'll need to split this file and provide a separate configure + * test for getnameinfo(). + * + * Windows may or may not have these routines, so we handle Windows specially + * by dynamically checking for their existence. If they already exist, we + * use the Windows native routines, but if not, we use our own. + * + * + * Copyright (c) 2003-2016, PostgreSQL Global Development Group + * + * IDENTIFICATION + * src/port/getaddrinfo.c + * + *------------------------------------------------------------------------- + */ + +/* This is intended to be used in both frontend and backend, so use c.h */ +#include "c.h" + +#include +#include +#include +#include + +#include "getaddrinfo.h" +#include "libpq/pqcomm.h" /* needed for struct sockaddr_storage */ + + +#ifdef WIN32 +/* + * The native routines may or may not exist on the Windows platform we are on, + * so we dynamically look up the routines, and call them via function pointers. + * Here we need to declare what the function pointers look like + */ +typedef int (__stdcall * getaddrinfo_ptr_t) (const char *nodename, + const char *servname, + const struct addrinfo * hints, + struct addrinfo ** res); + +typedef void (__stdcall * freeaddrinfo_ptr_t) (struct addrinfo * ai); + +typedef int (__stdcall * getnameinfo_ptr_t) (const struct sockaddr * sa, + int salen, + char *host, int hostlen, + char *serv, int servlen, + int flags); + +/* static pointers to the native routines, so we only do the lookup once. */ +static getaddrinfo_ptr_t getaddrinfo_ptr = NULL; +static freeaddrinfo_ptr_t freeaddrinfo_ptr = NULL; +static getnameinfo_ptr_t getnameinfo_ptr = NULL; + + +static bool +haveNativeWindowsIPv6routines(void) +{ + void *hLibrary = NULL; + static bool alreadyLookedForIpv6routines = false; + + if (alreadyLookedForIpv6routines) + return (getaddrinfo_ptr != NULL); + + /* + * For Windows XP and Windows 2003 (and longhorn/vista), the IPv6 routines + * are present in the WinSock 2 library (ws2_32.dll). Try that first + */ + + hLibrary = LoadLibraryA("ws2_32"); + + if (hLibrary == NULL || GetProcAddress(hLibrary, "getaddrinfo") == NULL) + { + /* + * Well, ws2_32 doesn't exist, or more likely doesn't have + * getaddrinfo. + */ + if (hLibrary != NULL) + FreeLibrary(hLibrary); + + /* + * In Windows 2000, there was only the IPv6 Technology Preview look in + * the IPv6 WinSock library (wship6.dll). + */ + + hLibrary = LoadLibraryA("wship6"); + } + + /* If hLibrary is null, we couldn't find a dll with functions */ + if (hLibrary != NULL) + { + /* We found a dll, so now get the addresses of the routines */ + + getaddrinfo_ptr = (getaddrinfo_ptr_t) GetProcAddress(hLibrary, + "getaddrinfo"); + freeaddrinfo_ptr = (freeaddrinfo_ptr_t) GetProcAddress(hLibrary, + "freeaddrinfo"); + getnameinfo_ptr = (getnameinfo_ptr_t) GetProcAddress(hLibrary, + "getnameinfo"); + + /* + * If any one of the routines is missing, let's play it safe and + * ignore them all + */ + if (getaddrinfo_ptr == NULL || + freeaddrinfo_ptr == NULL || + getnameinfo_ptr == NULL) + { + FreeLibrary(hLibrary); + hLibrary = NULL; + getaddrinfo_ptr = NULL; + freeaddrinfo_ptr = NULL; + getnameinfo_ptr = NULL; + } + } + + alreadyLookedForIpv6routines = true; + return (getaddrinfo_ptr != NULL); +} +#endif + + +/* + * get address info for ipv4 sockets. + * + * Bugs: - only one addrinfo is set even though hintp is NULL or + * ai_socktype is 0 + * - AI_CANONNAME is not supported. + * - servname can only be a number, not text. + */ +int +getaddrinfo(const char *node, const char *service, + const struct addrinfo * hintp, + struct addrinfo ** res) +{ + struct addrinfo *ai; + struct sockaddr_in sin, + *psin; + struct addrinfo hints; + +#ifdef WIN32 + + /* + * If Windows has native IPv6 support, use the native Windows routine. + * Otherwise, fall through and use our own code. + */ + if (haveNativeWindowsIPv6routines()) + return (*getaddrinfo_ptr) (node, service, hintp, res); +#endif + + if (hintp == NULL) + { + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + } + else + memcpy(&hints, hintp, sizeof(hints)); + + if (hints.ai_family != AF_INET && hints.ai_family != AF_UNSPEC) + return EAI_FAMILY; + + if (hints.ai_socktype == 0) + hints.ai_socktype = SOCK_STREAM; + + if (!node && !service) + return EAI_NONAME; + + memset(&sin, 0, sizeof(sin)); + + sin.sin_family = AF_INET; + + if (node) + { + if (node[0] == '\0') + sin.sin_addr.s_addr = htonl(INADDR_ANY); + else if (hints.ai_flags & AI_NUMERICHOST) + { + if (!inet_aton(node, &sin.sin_addr)) + return EAI_NONAME; + } + else + { + struct hostent *hp; + +#ifdef FRONTEND + struct hostent hpstr; + char buf[BUFSIZ]; + int herrno = 0; + + pqGethostbyname(node, &hpstr, buf, sizeof(buf), + &hp, &herrno); +#else + hp = gethostbyname(node); +#endif + if (hp == NULL) + { + switch (h_errno) + { + case HOST_NOT_FOUND: + case NO_DATA: + return EAI_NONAME; + case TRY_AGAIN: + return EAI_AGAIN; + case NO_RECOVERY: + default: + return EAI_FAIL; + } + } + if (hp->h_addrtype != AF_INET) + return EAI_FAIL; + + memcpy(&(sin.sin_addr), hp->h_addr, hp->h_length); + } + } + else + { + if (hints.ai_flags & AI_PASSIVE) + sin.sin_addr.s_addr = htonl(INADDR_ANY); + else + sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + } + + if (service) + sin.sin_port = htons((unsigned short) atoi(service)); + +#ifdef HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN + sin.sin_len = sizeof(sin); +#endif + + ai = malloc(sizeof(*ai)); + if (!ai) + return EAI_MEMORY; + + psin = malloc(sizeof(*psin)); + if (!psin) + { + free(ai); + return EAI_MEMORY; + } + + memcpy(psin, &sin, sizeof(*psin)); + + ai->ai_flags = 0; + ai->ai_family = AF_INET; + ai->ai_socktype = hints.ai_socktype; + ai->ai_protocol = hints.ai_protocol; + ai->ai_addrlen = sizeof(*psin); + ai->ai_addr = (struct sockaddr *) psin; + ai->ai_canonname = NULL; + ai->ai_next = NULL; + + *res = ai; + + return 0; +} + + +void +freeaddrinfo(struct addrinfo * res) +{ + if (res) + { +#ifdef WIN32 + + /* + * If Windows has native IPv6 support, use the native Windows routine. + * Otherwise, fall through and use our own code. + */ + if (haveNativeWindowsIPv6routines()) + { + (*freeaddrinfo_ptr) (res); + return; + } +#endif + + if (res->ai_addr) + free(res->ai_addr); + free(res); + } +} + + +const char * +gai_strerror(int errcode) +{ +#ifdef HAVE_HSTRERROR + int hcode; + + switch (errcode) + { + case EAI_NONAME: + hcode = HOST_NOT_FOUND; + break; + case EAI_AGAIN: + hcode = TRY_AGAIN; + break; + case EAI_FAIL: + default: + hcode = NO_RECOVERY; + break; + } + + return hstrerror(hcode); +#else /* !HAVE_HSTRERROR */ + + switch (errcode) + { + case EAI_NONAME: + return "Unknown host"; + case EAI_AGAIN: + return "Host name lookup failure"; + /* Errors below are probably WIN32 only */ +#ifdef EAI_BADFLAGS + case EAI_BADFLAGS: + return "Invalid argument"; +#endif +#ifdef EAI_FAMILY + case EAI_FAMILY: + return "Address family not supported"; +#endif +#ifdef EAI_MEMORY + case EAI_MEMORY: + return "Not enough memory"; +#endif +#if defined(EAI_NODATA) && EAI_NODATA != EAI_NONAME /* MSVC/WIN64 duplicate */ + case EAI_NODATA: + return "No host data of that type was found"; +#endif +#ifdef EAI_SERVICE + case EAI_SERVICE: + return "Class type not found"; +#endif +#ifdef EAI_SOCKTYPE + case EAI_SOCKTYPE: + return "Socket type not supported"; +#endif + default: + return "Unknown server error"; + } +#endif /* HAVE_HSTRERROR */ +} + +/* + * Convert an ipv4 address to a hostname. + * + * Bugs: - Only supports NI_NUMERICHOST and NI_NUMERICSERV behavior. + * It will never resolve a hostname. + * - No IPv6 support. + */ +int +getnameinfo(const struct sockaddr * sa, int salen, + char *node, int nodelen, + char *service, int servicelen, int flags) +{ +#ifdef WIN32 + + /* + * If Windows has native IPv6 support, use the native Windows routine. + * Otherwise, fall through and use our own code. + */ + if (haveNativeWindowsIPv6routines()) + return (*getnameinfo_ptr) (sa, salen, node, nodelen, + service, servicelen, flags); +#endif + + /* Invalid arguments. */ + if (sa == NULL || (node == NULL && service == NULL)) + return EAI_FAIL; + +#ifdef HAVE_IPV6 + if (sa->sa_family == AF_INET6) + return EAI_FAMILY; +#endif + + /* Unsupported flags. */ + if (flags & NI_NAMEREQD) + return EAI_AGAIN; + + if (node) + { + if (sa->sa_family == AF_INET) + { + if (inet_net_ntop(AF_INET, &((struct sockaddr_in *) sa)->sin_addr, + sa->sa_family == AF_INET ? 32 : 128, + node, nodelen) == NULL) + return EAI_MEMORY; + } + else + return EAI_MEMORY; + } + + if (service) + { + int ret = -1; + + if (sa->sa_family == AF_INET) + { + ret = snprintf(service, servicelen, "%d", + ntohs(((struct sockaddr_in *) sa)->sin_port)); + } + if (ret == -1 || ret >= servicelen) + return EAI_MEMORY; + } + + return 0; +} diff --git a/libpq/win32/inet_aton.c b/libpq/win32/inet_aton.c new file mode 100644 index 0000000..27e8aaa --- /dev/null +++ b/libpq/win32/inet_aton.c @@ -0,0 +1,147 @@ +/* src/port/inet_aton.c + * + * This inet_aton() function was taken from the GNU C library and + * incorporated into Postgres for those systems which do not have this + * routine in their standard C libraries. + * + * The function was been extracted whole from the file inet_aton.c in + * Release 5.3.12 of the Linux C library, which is derived from the + * GNU C library, by Bryan Henderson in October 1996. The copyright + * notice from that file is below. + */ + +/* + * Copyright (c) 1983, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. */ + +#include "c.h" + +#include +#include + +/* + * Check whether "cp" is a valid ascii representation + * of an Internet address and convert to a binary address. + * Returns 1 if the address is valid, 0 if not. + * This replaces inet_addr, the return value from which + * cannot distinguish between failure and a local broadcast address. + */ +int +inet_aton(const char *cp, struct in_addr * addr) +{ + unsigned int val; + int base, + n; + char c; + u_int parts[4]; + u_int *pp = parts; + + for (;;) + { + /* + * Collect number up to ``.''. Values are specified as for C: 0x=hex, + * 0=octal, other=decimal. + */ + val = 0; + base = 10; + if (*cp == '0') + { + if (*++cp == 'x' || *cp == 'X') + base = 16, cp++; + else + base = 8; + } + while ((c = *cp) != '\0') + { + if (isdigit((unsigned char) c)) + { + val = (val * base) + (c - '0'); + cp++; + continue; + } + if (base == 16 && isxdigit((unsigned char) c)) + { + val = (val << 4) + + (c + 10 - (islower((unsigned char) c) ? 'a' : 'A')); + cp++; + continue; + } + break; + } + if (*cp == '.') + { + /* + * Internet format: a.b.c.d a.b.c (with c treated as 16-bits) + * a.b (with b treated as 24 bits) + */ + if (pp >= parts + 3 || val > 0xff) + return 0; + *pp++ = val, cp++; + } + else + break; + } + + /* + * Check for trailing junk. + */ + while (*cp) + if (!isspace((unsigned char) *cp++)) + return 0; + + /* + * Concoct the address according to the number of parts specified. + */ + n = pp - parts + 1; + switch (n) + { + + case 1: /* a -- 32 bits */ + break; + + case 2: /* a.b -- 8.24 bits */ + if (val > 0xffffff) + return 0; + val |= parts[0] << 24; + break; + + case 3: /* a.b.c -- 8.8.16 bits */ + if (val > 0xffff) + return 0; + val |= (parts[0] << 24) | (parts[1] << 16); + break; + + case 4: /* a.b.c.d -- 8.8.8.8 bits */ + if (val > 0xff) + return 0; + val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8); + break; + } + if (addr) + addr->s_addr = htonl(val); + return 1; +} diff --git a/libpq/win32/libpq.rc.in b/libpq/win32/libpq.rc.in new file mode 100644 index 0000000..da97574 --- /dev/null +++ b/libpq/win32/libpq.rc.in @@ -0,0 +1,31 @@ +#include + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 9,6,5,0 + PRODUCTVERSION 9,6,5,0 + FILEFLAGSMASK 0x3fL + FILEFLAGS 0 + FILEOS VOS__WINDOWS32 + FILETYPE VFT_DLL + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904B0" + BEGIN + VALUE "CompanyName", "\0" + VALUE "FileDescription", "PostgreSQL Access Library\0" + VALUE "FileVersion", "9.6.5\0" + VALUE "InternalName", "libpq\0" + VALUE "LegalCopyright", "Copyright (C) 2016\0" + VALUE "LegalTrademarks", "\0" + VALUE "OriginalFilename", "libpq.dll\0" + VALUE "ProductName", "PostgreSQL\0" + VALUE "ProductVersion", "9.6.5\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/libpq/win32/libpqdll.def b/libpq/win32/libpqdll.def new file mode 100644 index 0000000..810d4be --- /dev/null +++ b/libpq/win32/libpqdll.def @@ -0,0 +1,174 @@ +; DEF file for win32.mak release build and for Makefile.shlib (MinGW) +; LIBRARY LIBPQ.dll +EXPORTS + PQconnectdb @ 1 + PQsetdbLogin @ 2 + PQconndefaults @ 3 + PQfinish @ 4 + PQreset @ 5 + PQrequestCancel @ 6 + PQdb @ 7 + PQuser @ 8 + PQpass @ 9 + PQhost @ 10 + PQport @ 11 + PQtty @ 12 + PQoptions @ 13 + PQstatus @ 14 + PQerrorMessage @ 15 + PQsocket @ 16 + PQbackendPID @ 17 + PQtrace @ 18 + PQuntrace @ 19 + PQsetNoticeProcessor @ 20 + PQexec @ 21 + PQnotifies @ 22 + PQsendQuery @ 23 + PQgetResult @ 24 + PQisBusy @ 25 + PQconsumeInput @ 26 + PQgetline @ 27 + PQputline @ 28 + PQgetlineAsync @ 29 + PQputnbytes @ 30 + PQendcopy @ 31 + PQfn @ 32 + PQresultStatus @ 33 + PQntuples @ 34 + PQnfields @ 35 + PQbinaryTuples @ 36 + PQfname @ 37 + PQfnumber @ 38 + PQftype @ 39 + PQfsize @ 40 + PQfmod @ 41 + PQcmdStatus @ 42 + PQoidStatus @ 43 + PQcmdTuples @ 44 + PQgetvalue @ 45 + PQgetlength @ 46 + PQgetisnull @ 47 + PQclear @ 48 + PQmakeEmptyPGresult @ 49 + PQprint @ 50 + PQdisplayTuples @ 51 + PQprintTuples @ 52 + lo_open @ 53 + lo_close @ 54 + lo_read @ 55 + lo_write @ 56 + lo_lseek @ 57 + lo_creat @ 58 + lo_tell @ 59 + lo_unlink @ 60 + lo_import @ 61 + lo_export @ 62 + pgresStatus @ 63 + PQmblen @ 64 + PQresultErrorMessage @ 65 + PQresStatus @ 66 + termPQExpBuffer @ 67 + appendPQExpBufferChar @ 68 + initPQExpBuffer @ 69 + resetPQExpBuffer @ 70 + PQoidValue @ 71 + PQclientEncoding @ 72 + PQenv2encoding @ 73 + appendBinaryPQExpBuffer @ 74 + appendPQExpBufferStr @ 75 + destroyPQExpBuffer @ 76 + createPQExpBuffer @ 77 + PQconninfoFree @ 78 + PQconnectPoll @ 79 + PQconnectStart @ 80 + PQflush @ 81 + PQisnonblocking @ 82 + PQresetPoll @ 83 + PQresetStart @ 84 + PQsetClientEncoding @ 85 + PQsetnonblocking @ 86 + PQfreeNotify @ 87 + PQescapeString @ 88 + PQescapeBytea @ 89 + printfPQExpBuffer @ 90 + appendPQExpBuffer @ 91 + pg_encoding_to_char @ 92 + pg_utf_mblen @ 93 + PQunescapeBytea @ 94 + PQfreemem @ 95 + PQtransactionStatus @ 96 + PQparameterStatus @ 97 + PQprotocolVersion @ 98 + PQsetErrorVerbosity @ 99 + PQsetNoticeReceiver @ 100 + PQexecParams @ 101 + PQsendQueryParams @ 102 + PQputCopyData @ 103 + PQputCopyEnd @ 104 + PQgetCopyData @ 105 + PQresultErrorField @ 106 + PQftable @ 107 + PQftablecol @ 108 + PQfformat @ 109 + PQexecPrepared @ 110 + PQsendQueryPrepared @ 111 + PQdsplen @ 112 + PQserverVersion @ 113 + PQgetssl @ 114 + pg_char_to_encoding @ 115 + pg_valid_server_encoding @ 116 + pqsignal @ 117 + PQprepare @ 118 + PQsendPrepare @ 119 + PQgetCancel @ 120 + PQfreeCancel @ 121 + PQcancel @ 122 + lo_create @ 123 + PQinitSSL @ 124 + PQregisterThreadLock @ 125 + PQescapeStringConn @ 126 + PQescapeByteaConn @ 127 + PQencryptPassword @ 128 + PQisthreadsafe @ 129 + enlargePQExpBuffer @ 130 + PQnparams @ 131 + PQparamtype @ 132 + PQdescribePrepared @ 133 + PQdescribePortal @ 134 + PQsendDescribePrepared @ 135 + PQsendDescribePortal @ 136 + lo_truncate @ 137 + PQconnectionUsedPassword @ 138 + pg_valid_server_encoding_id @ 139 + PQconnectionNeedsPassword @ 140 + lo_import_with_oid @ 141 + PQcopyResult @ 142 + PQsetResultAttrs @ 143 + PQsetvalue @ 144 + PQresultAlloc @ 145 + PQregisterEventProc @ 146 + PQinstanceData @ 147 + PQsetInstanceData @ 148 + PQresultInstanceData @ 149 + PQresultSetInstanceData @ 150 + PQfireResultCreateEvents @ 151 + PQconninfoParse @ 152 + PQinitOpenSSL @ 153 + PQescapeLiteral @ 154 + PQescapeIdentifier @ 155 + PQconnectdbParams @ 156 + PQconnectStartParams @ 157 + PQping @ 158 + PQpingParams @ 159 + PQlibVersion @ 160 + PQsetSingleRowMode @ 161 + lo_lseek64 @ 162 + lo_tell64 @ 163 + lo_truncate64 @ 164 + PQconninfo @ 165 + PQsslInUse @ 166 + PQsslStruct @ 167 + PQsslAttributeNames @ 168 + PQsslAttribute @ 169 + PQsetErrorContextVisibility @ 170 + PQresultVerboseErrorMessage @ 171 diff --git a/libpq/win32/libpqdll.def.orig b/libpq/win32/libpqdll.def.orig new file mode 100644 index 0000000..0c9e8e4 --- /dev/null +++ b/libpq/win32/libpqdll.def.orig @@ -0,0 +1,174 @@ +; DEF file for win32.mak release build and for Makefile.shlib (MinGW) +LIBRARY LIBPQ.dll +EXPORTS + PQconnectdb @ 1 + PQsetdbLogin @ 2 + PQconndefaults @ 3 + PQfinish @ 4 + PQreset @ 5 + PQrequestCancel @ 6 + PQdb @ 7 + PQuser @ 8 + PQpass @ 9 + PQhost @ 10 + PQport @ 11 + PQtty @ 12 + PQoptions @ 13 + PQstatus @ 14 + PQerrorMessage @ 15 + PQsocket @ 16 + PQbackendPID @ 17 + PQtrace @ 18 + PQuntrace @ 19 + PQsetNoticeProcessor @ 20 + PQexec @ 21 + PQnotifies @ 22 + PQsendQuery @ 23 + PQgetResult @ 24 + PQisBusy @ 25 + PQconsumeInput @ 26 + PQgetline @ 27 + PQputline @ 28 + PQgetlineAsync @ 29 + PQputnbytes @ 30 + PQendcopy @ 31 + PQfn @ 32 + PQresultStatus @ 33 + PQntuples @ 34 + PQnfields @ 35 + PQbinaryTuples @ 36 + PQfname @ 37 + PQfnumber @ 38 + PQftype @ 39 + PQfsize @ 40 + PQfmod @ 41 + PQcmdStatus @ 42 + PQoidStatus @ 43 + PQcmdTuples @ 44 + PQgetvalue @ 45 + PQgetlength @ 46 + PQgetisnull @ 47 + PQclear @ 48 + PQmakeEmptyPGresult @ 49 + PQprint @ 50 + PQdisplayTuples @ 51 + PQprintTuples @ 52 + lo_open @ 53 + lo_close @ 54 + lo_read @ 55 + lo_write @ 56 + lo_lseek @ 57 + lo_creat @ 58 + lo_tell @ 59 + lo_unlink @ 60 + lo_import @ 61 + lo_export @ 62 + pgresStatus @ 63 + PQmblen @ 64 + PQresultErrorMessage @ 65 + PQresStatus @ 66 + termPQExpBuffer @ 67 + appendPQExpBufferChar @ 68 + initPQExpBuffer @ 69 + resetPQExpBuffer @ 70 + PQoidValue @ 71 + PQclientEncoding @ 72 + PQenv2encoding @ 73 + appendBinaryPQExpBuffer @ 74 + appendPQExpBufferStr @ 75 + destroyPQExpBuffer @ 76 + createPQExpBuffer @ 77 + PQconninfoFree @ 78 + PQconnectPoll @ 79 + PQconnectStart @ 80 + PQflush @ 81 + PQisnonblocking @ 82 + PQresetPoll @ 83 + PQresetStart @ 84 + PQsetClientEncoding @ 85 + PQsetnonblocking @ 86 + PQfreeNotify @ 87 + PQescapeString @ 88 + PQescapeBytea @ 89 + printfPQExpBuffer @ 90 + appendPQExpBuffer @ 91 + pg_encoding_to_char @ 92 + pg_utf_mblen @ 93 + PQunescapeBytea @ 94 + PQfreemem @ 95 + PQtransactionStatus @ 96 + PQparameterStatus @ 97 + PQprotocolVersion @ 98 + PQsetErrorVerbosity @ 99 + PQsetNoticeReceiver @ 100 + PQexecParams @ 101 + PQsendQueryParams @ 102 + PQputCopyData @ 103 + PQputCopyEnd @ 104 + PQgetCopyData @ 105 + PQresultErrorField @ 106 + PQftable @ 107 + PQftablecol @ 108 + PQfformat @ 109 + PQexecPrepared @ 110 + PQsendQueryPrepared @ 111 + PQdsplen @ 112 + PQserverVersion @ 113 + PQgetssl @ 114 + pg_char_to_encoding @ 115 + pg_valid_server_encoding @ 116 + pqsignal @ 117 + PQprepare @ 118 + PQsendPrepare @ 119 + PQgetCancel @ 120 + PQfreeCancel @ 121 + PQcancel @ 122 + lo_create @ 123 + PQinitSSL @ 124 + PQregisterThreadLock @ 125 + PQescapeStringConn @ 126 + PQescapeByteaConn @ 127 + PQencryptPassword @ 128 + PQisthreadsafe @ 129 + enlargePQExpBuffer @ 130 + PQnparams @ 131 + PQparamtype @ 132 + PQdescribePrepared @ 133 + PQdescribePortal @ 134 + PQsendDescribePrepared @ 135 + PQsendDescribePortal @ 136 + lo_truncate @ 137 + PQconnectionUsedPassword @ 138 + pg_valid_server_encoding_id @ 139 + PQconnectionNeedsPassword @ 140 + lo_import_with_oid @ 141 + PQcopyResult @ 142 + PQsetResultAttrs @ 143 + PQsetvalue @ 144 + PQresultAlloc @ 145 + PQregisterEventProc @ 146 + PQinstanceData @ 147 + PQsetInstanceData @ 148 + PQresultInstanceData @ 149 + PQresultSetInstanceData @ 150 + PQfireResultCreateEvents @ 151 + PQconninfoParse @ 152 + PQinitOpenSSL @ 153 + PQescapeLiteral @ 154 + PQescapeIdentifier @ 155 + PQconnectdbParams @ 156 + PQconnectStartParams @ 157 + PQping @ 158 + PQpingParams @ 159 + PQlibVersion @ 160 + PQsetSingleRowMode @ 161 + lo_lseek64 @ 162 + lo_tell64 @ 163 + lo_truncate64 @ 164 + PQconninfo @ 165 + PQsslInUse @ 166 + PQsslStruct @ 167 + PQsslAttributeNames @ 168 + PQsslAttribute @ 169 + PQsetErrorContextVisibility @ 170 + PQresultVerboseErrorMessage @ 171 diff --git a/libpq/win32/open.c b/libpq/win32/open.c new file mode 100644 index 0000000..717375d --- /dev/null +++ b/libpq/win32/open.c @@ -0,0 +1,167 @@ +/*------------------------------------------------------------------------- + * + * open.c + * Win32 open() replacement + * + * + * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * + * src/port/open.c + * + *------------------------------------------------------------------------- + */ + +#ifdef WIN32 + +#ifndef FRONTEND +#include "postgres.h" +#else +#include "postgres_fe.h" +#endif + +#include +#include +#include + + +static int +openFlagsToCreateFileFlags(int openFlags) +{ + switch (openFlags & (O_CREAT | O_TRUNC | O_EXCL)) + { + /* O_EXCL is meaningless without O_CREAT */ + case 0: + case O_EXCL: + return OPEN_EXISTING; + + case O_CREAT: + return OPEN_ALWAYS; + + /* O_EXCL is meaningless without O_CREAT */ + case O_TRUNC: + case O_TRUNC | O_EXCL: + return TRUNCATE_EXISTING; + + case O_CREAT | O_TRUNC: + return CREATE_ALWAYS; + + /* O_TRUNC is meaningless with O_CREAT */ + case O_CREAT | O_EXCL: + case O_CREAT | O_TRUNC | O_EXCL: + return CREATE_NEW; + } + + /* will never get here */ + return 0; +} + +/* + * - file attribute setting, based on fileMode? + */ +int +pgwin32_open(const char *fileName, int fileFlags,...) +{ + int fd; + HANDLE h = INVALID_HANDLE_VALUE; + SECURITY_ATTRIBUTES sa; + int loops = 0; + + /* Check that we can handle the request */ + assert((fileFlags & ((O_RDONLY | O_WRONLY | O_RDWR) | O_APPEND | + (O_RANDOM | O_SEQUENTIAL | O_TEMPORARY) | + _O_SHORT_LIVED | O_DSYNC | O_DIRECT | + (O_CREAT | O_TRUNC | O_EXCL) | (O_TEXT | O_BINARY))) == fileFlags); + + sa.nLength = sizeof(sa); + sa.bInheritHandle = TRUE; + sa.lpSecurityDescriptor = NULL; + + while ((h = CreateFile(fileName, + /* cannot use O_RDONLY, as it == 0 */ + (fileFlags & O_RDWR) ? (GENERIC_WRITE | GENERIC_READ) : + ((fileFlags & O_WRONLY) ? GENERIC_WRITE : GENERIC_READ), + /* These flags allow concurrent rename/unlink */ + (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE), + &sa, + openFlagsToCreateFileFlags(fileFlags), + FILE_ATTRIBUTE_NORMAL | + ((fileFlags & O_RANDOM) ? FILE_FLAG_RANDOM_ACCESS : 0) | + ((fileFlags & O_SEQUENTIAL) ? FILE_FLAG_SEQUENTIAL_SCAN : 0) | + ((fileFlags & _O_SHORT_LIVED) ? FILE_ATTRIBUTE_TEMPORARY : 0) | + ((fileFlags & O_TEMPORARY) ? FILE_FLAG_DELETE_ON_CLOSE : 0) | + ((fileFlags & O_DIRECT) ? FILE_FLAG_NO_BUFFERING : 0) | + ((fileFlags & O_DSYNC) ? FILE_FLAG_WRITE_THROUGH : 0), + NULL)) == INVALID_HANDLE_VALUE) + { + /* + * Sharing violation or locking error can indicate antivirus, backup + * or similar software that's locking the file. Try again for 30 + * seconds before giving up. + */ + DWORD err = GetLastError(); + + if (err == ERROR_SHARING_VIOLATION || + err == ERROR_LOCK_VIOLATION) + { + pg_usleep(100000); + loops++; + +#ifndef FRONTEND + if (loops == 50) + ereport(LOG, + (errmsg("could not open file \"%s\": %s", fileName, + (err == ERROR_SHARING_VIOLATION) ? _("sharing violation") : _("lock violation")), + errdetail("Continuing to retry for 30 seconds."), + errhint("You might have antivirus, backup, or similar software interfering with the database system."))); +#endif + + if (loops < 300) + continue; + } + + _dosmaperr(err); + return -1; + } + + /* _open_osfhandle will, on error, set errno accordingly */ + if ((fd = _open_osfhandle((intptr_t) h, fileFlags & O_APPEND)) < 0) + CloseHandle(h); /* will not affect errno */ + else if (fileFlags & (O_TEXT | O_BINARY) && + _setmode(fd, fileFlags & (O_TEXT | O_BINARY)) < 0) + { + _close(fd); + return -1; + } + + return fd; +} + +FILE * +pgwin32_fopen(const char *fileName, const char *mode) +{ + int openmode = 0; + int fd; + + if (strstr(mode, "r+")) + openmode |= O_RDWR; + else if (strchr(mode, 'r')) + openmode |= O_RDONLY; + if (strstr(mode, "w+")) + openmode |= O_RDWR | O_CREAT | O_TRUNC; + else if (strchr(mode, 'w')) + openmode |= O_WRONLY | O_CREAT | O_TRUNC; + if (strchr(mode, 'a')) + openmode |= O_WRONLY | O_CREAT | O_APPEND; + + if (strchr(mode, 'b')) + openmode |= O_BINARY; + if (strchr(mode, 't')) + openmode |= O_TEXT; + + fd = pgwin32_open(fileName, openmode); + if (fd == -1) + return NULL; + return _fdopen(fd, mode); +} + +#endif diff --git a/libpq/win32/pgsleep.c b/libpq/win32/pgsleep.c new file mode 100644 index 0000000..ecefe04 --- /dev/null +++ b/libpq/win32/pgsleep.c @@ -0,0 +1,63 @@ +/*------------------------------------------------------------------------- + * + * pgsleep.c + * Portable delay handling. + * + * + * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * + * src/port/pgsleep.c + * + *------------------------------------------------------------------------- + */ +#include "c.h" + +#include +#include +#ifdef HAVE_SYS_SELECT_H +#include +#endif + +/* + * In a Windows backend, we don't use this implementation, but rather + * the signal-aware version in src/backend/port/win32/signal.c. + */ +#if defined(FRONTEND) || !defined(WIN32) + +/* + * pg_usleep --- delay the specified number of microseconds. + * + * NOTE: although the delay is specified in microseconds, the effective + * resolution is only 1/HZ, or 10 milliseconds, on most Unixen. Expect + * the requested delay to be rounded up to the next resolution boundary. + * + * On machines where "long" is 32 bits, the maximum delay is ~2000 seconds. + * + * CAUTION: the behavior when a signal arrives during the sleep is platform + * dependent. On most Unix-ish platforms, a signal does not terminate the + * sleep; but on some, it will (the Windows implementation also allows signals + * to terminate pg_usleep). And there are platforms where not only does a + * signal not terminate the sleep, but it actually resets the timeout counter + * so that the sleep effectively starts over! It is therefore rather hazardous + * to use this for long sleeps; a continuing stream of signal events could + * prevent the sleep from ever terminating. Better practice for long sleeps + * is to use WaitLatch() with a timeout. + */ +void +pg_usleep(long microsec) +{ + if (microsec > 0) + { +#ifndef WIN32 + struct timeval delay; + + delay.tv_sec = microsec / 1000000L; + delay.tv_usec = microsec % 1000000L; + (void) select(0, NULL, NULL, NULL, &delay); +#else + SleepEx((microsec < 500 ? 1 : (microsec + 500) / 1000), FALSE); +#endif + } +} + +#endif /* defined(FRONTEND) || !defined(WIN32) */ diff --git a/libpq/win32/pthread-win32.c b/libpq/win32/pthread-win32.c new file mode 100644 index 0000000..68dfefc --- /dev/null +++ b/libpq/win32/pthread-win32.c @@ -0,0 +1,61 @@ +/*------------------------------------------------------------------------- +* +* pthread-win32.c +* partial pthread implementation for win32 +* +* Copyright (c) 2004-2016, PostgreSQL Global Development Group +* IDENTIFICATION +* src/interfaces/libpq/pthread-win32.c +* +*------------------------------------------------------------------------- +*/ + +#include "postgres_fe.h" + +#include +#include "pthread-win32.h" + +DWORD +pthread_self(void) +{ + return GetCurrentThreadId(); +} + +void +pthread_setspecific(pthread_key_t key, void *val) +{ +} + +void * +pthread_getspecific(pthread_key_t key) +{ + return NULL; +} + +int +pthread_mutex_init(pthread_mutex_t *mp, void *attr) +{ + *mp = (CRITICAL_SECTION *) malloc(sizeof(CRITICAL_SECTION)); + if (!*mp) + return 1; + InitializeCriticalSection(*mp); + return 0; +} + +int +pthread_mutex_lock(pthread_mutex_t *mp) +{ + if (!*mp) + return 1; + EnterCriticalSection(*mp); + return 0; +} + +int +pthread_mutex_unlock(pthread_mutex_t *mp) +{ + if (!*mp) + return 1; + LeaveCriticalSection(*mp); + return 0; +} diff --git a/libpq/win32/pthread-win32.h b/libpq/win32/pthread-win32.h new file mode 100644 index 0000000..97ccc17 --- /dev/null +++ b/libpq/win32/pthread-win32.h @@ -0,0 +1,22 @@ +/* + * src/port/pthread-win32.h + */ +#ifndef __PTHREAD_H +#define __PTHREAD_H + +typedef ULONG pthread_key_t; +typedef CRITICAL_SECTION *pthread_mutex_t; +typedef int pthread_once_t; + +DWORD pthread_self(void); + +void pthread_setspecific(pthread_key_t, void *); +void *pthread_getspecific(pthread_key_t); + +int pthread_mutex_init(pthread_mutex_t *, void *attr); +int pthread_mutex_lock(pthread_mutex_t *); + +/* blocking */ +int pthread_mutex_unlock(pthread_mutex_t *); + +#endif diff --git a/libpq/win32/snprintf.c b/libpq/win32/snprintf.c new file mode 100644 index 0000000..62b23b0 --- /dev/null +++ b/libpq/win32/snprintf.c @@ -0,0 +1,1141 @@ +/* + * Copyright (c) 1983, 1995, 1996 Eric P. Allman + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * src/port/snprintf.c + */ + +#include "c.h" + +#include +#ifdef _MSC_VER +#include /* for _isnan */ +#endif +#include +#include +#ifndef WIN32 +#include +#endif +#include + +#ifndef NL_ARGMAX +#define NL_ARGMAX 16 +#endif + + +/* + * SNPRINTF, VSNPRINTF and friends + * + * These versions have been grabbed off the net. They have been + * cleaned up to compile properly and support for most of the Single Unix + * Specification has been added. Remaining unimplemented features are: + * + * 1. No locale support: the radix character is always '.' and the ' + * (single quote) format flag is ignored. + * + * 2. No support for the "%n" format specification. + * + * 3. No support for wide characters ("lc" and "ls" formats). + * + * 4. No support for "long double" ("Lf" and related formats). + * + * 5. Space and '#' flags are not implemented. + * + * + * The result values of these functions are not the same across different + * platforms. This implementation is compatible with the Single Unix Spec: + * + * 1. -1 is returned only if processing is abandoned due to an invalid + * parameter, such as incorrect format string. (Although not required by + * the spec, this happens only when no characters have yet been transmitted + * to the destination.) + * + * 2. For snprintf and sprintf, 0 is returned if str == NULL or count == 0; + * no data has been stored. + * + * 3. Otherwise, the number of bytes actually transmitted to the destination + * is returned (excluding the trailing '\0' for snprintf and sprintf). + * + * For snprintf with nonzero count, the result cannot be more than count-1 + * (a trailing '\0' is always stored); it is not possible to distinguish + * buffer overrun from exact fit. This is unlike some implementations that + * return the number of bytes that would have been needed for the complete + * result string. + */ + +/************************************************************** + * Original: + * Patrick Powell Tue Apr 11 09:48:21 PDT 1995 + * A bombproof version of doprnt (dopr) included. + * Sigh. This sort of thing is always nasty do deal with. Note that + * the version here does not include floating point. (now it does ... tgl) + **************************************************************/ + +/* Prevent recursion */ +#undef vsnprintf +#undef snprintf +#undef sprintf +#undef vfprintf +#undef fprintf +#undef printf + +/* Info about where the formatted output is going */ +typedef struct +{ + char *bufptr; /* next buffer output position */ + char *bufstart; /* first buffer element */ + char *bufend; /* last buffer element, or NULL */ + /* bufend == NULL is for sprintf, where we assume buf is big enough */ + FILE *stream; /* eventual output destination, or NULL */ + int nchars; /* # chars already sent to stream */ + bool failed; /* call is a failure; errno is set */ +} PrintfTarget; + +/* + * Info about the type and value of a formatting parameter. Note that we + * don't currently support "long double", "wint_t", or "wchar_t *" data, + * nor the '%n' formatting code; else we'd need more types. Also, at this + * level we need not worry about signed vs unsigned values. + */ +typedef enum +{ + ATYPE_NONE = 0, + ATYPE_INT, + ATYPE_LONG, + ATYPE_LONGLONG, + ATYPE_DOUBLE, + ATYPE_CHARPTR +} PrintfArgType; + +typedef union +{ + int i; + long l; + int64 ll; + double d; + char *cptr; +} PrintfArgValue; + + +static void flushbuffer(PrintfTarget *target); +static void dopr(PrintfTarget *target, const char *format, va_list args); + + +int +pg_vsnprintf(char *str, size_t count, const char *fmt, va_list args) +{ + PrintfTarget target; + + if (str == NULL || count == 0) + return 0; + target.bufstart = target.bufptr = str; + target.bufend = str + count - 1; + target.stream = NULL; + /* target.nchars is unused in this case */ + target.failed = false; + dopr(&target, fmt, args); + *(target.bufptr) = '\0'; + return target.failed ? -1 : (target.bufptr - target.bufstart); +} + +int +pg_snprintf(char *str, size_t count, const char *fmt,...) +{ + int len; + va_list args; + + va_start(args, fmt); + len = pg_vsnprintf(str, count, fmt, args); + va_end(args); + return len; +} + +static int +pg_vsprintf(char *str, const char *fmt, va_list args) +{ + PrintfTarget target; + + if (str == NULL) + return 0; + target.bufstart = target.bufptr = str; + target.bufend = NULL; + target.stream = NULL; + /* target.nchars is unused in this case */ + target.failed = false; + dopr(&target, fmt, args); + *(target.bufptr) = '\0'; + return target.failed ? -1 : (target.bufptr - target.bufstart); +} + +int +pg_sprintf(char *str, const char *fmt,...) +{ + int len; + va_list args; + + va_start(args, fmt); + len = pg_vsprintf(str, fmt, args); + va_end(args); + return len; +} + +int +pg_vfprintf(FILE *stream, const char *fmt, va_list args) +{ + PrintfTarget target; + char buffer[1024]; /* size is arbitrary */ + + if (stream == NULL) + { + errno = EINVAL; + return -1; + } + target.bufstart = target.bufptr = buffer; + target.bufend = buffer + sizeof(buffer) - 1; + target.stream = stream; + target.nchars = 0; + target.failed = false; + dopr(&target, fmt, args); + /* dump any remaining buffer contents */ + flushbuffer(&target); + return target.failed ? -1 : target.nchars; +} + +int +pg_fprintf(FILE *stream, const char *fmt,...) +{ + int len; + va_list args; + + va_start(args, fmt); + len = pg_vfprintf(stream, fmt, args); + va_end(args); + return len; +} + +int +pg_printf(const char *fmt,...) +{ + int len; + va_list args; + + va_start(args, fmt); + len = pg_vfprintf(stdout, fmt, args); + va_end(args); + return len; +} + +/* + * Attempt to write the entire buffer to target->stream; discard the entire + * buffer in any case. Call this only when target->stream is defined. + */ +static void +flushbuffer(PrintfTarget *target) +{ + size_t nc = target->bufptr - target->bufstart; + + if (!target->failed && nc > 0) + { + size_t written; + + written = fwrite(target->bufstart, 1, nc, target->stream); + target->nchars += written; + if (written != nc) + target->failed = true; + } + target->bufptr = target->bufstart; +} + + +static void fmtstr(char *value, int leftjust, int minlen, int maxwidth, + int pointflag, PrintfTarget *target); +static void fmtptr(void *value, PrintfTarget *target); +static void fmtint(int64 value, char type, int forcesign, + int leftjust, int minlen, int zpad, int precision, int pointflag, + PrintfTarget *target); +static void fmtchar(int value, int leftjust, int minlen, PrintfTarget *target); +static void fmtfloat(double value, char type, int forcesign, + int leftjust, int minlen, int zpad, int precision, int pointflag, + PrintfTarget *target); +static void dostr(const char *str, int slen, PrintfTarget *target); +static void dopr_outch(int c, PrintfTarget *target); +static int adjust_sign(int is_negative, int forcesign, int *signvalue); +static void adjust_padlen(int minlen, int vallen, int leftjust, int *padlen); +static void leading_pad(int zpad, int *signvalue, int *padlen, + PrintfTarget *target); +static void trailing_pad(int *padlen, PrintfTarget *target); + + +/* + * dopr(): poor man's version of doprintf + */ +static void +dopr(PrintfTarget *target, const char *format, va_list args) +{ + const char *format_start = format; + int ch; + bool have_dollar; + bool have_non_dollar; + bool have_star; + bool afterstar; + int accum; + int longlongflag; + int longflag; + int pointflag; + int leftjust; + int fieldwidth; + int precision; + int zpad; + int forcesign; + int last_dollar; + int fmtpos; + int cvalue; + int64 numvalue; + double fvalue; + char *strvalue; + int i; + PrintfArgType argtypes[NL_ARGMAX + 1]; + PrintfArgValue argvalues[NL_ARGMAX + 1]; + + /* + * Parse the format string to determine whether there are %n$ format + * specs, and identify the types and order of the format parameters. + */ + have_dollar = have_non_dollar = false; + last_dollar = 0; + MemSet(argtypes, 0, sizeof(argtypes)); + + while ((ch = *format++) != '\0') + { + if (ch != '%') + continue; + longflag = longlongflag = pointflag = 0; + fmtpos = accum = 0; + afterstar = false; +nextch1: + ch = *format++; + if (ch == '\0') + break; /* illegal, but we don't complain */ + switch (ch) + { + case '-': + case '+': + goto nextch1; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + accum = accum * 10 + (ch - '0'); + goto nextch1; + case '.': + pointflag = 1; + accum = 0; + goto nextch1; + case '*': + if (afterstar) + have_non_dollar = true; /* multiple stars */ + afterstar = true; + accum = 0; + goto nextch1; + case '$': + have_dollar = true; + if (accum <= 0 || accum > NL_ARGMAX) + goto bad_format; + if (afterstar) + { + if (argtypes[accum] && + argtypes[accum] != ATYPE_INT) + goto bad_format; + argtypes[accum] = ATYPE_INT; + last_dollar = Max(last_dollar, accum); + afterstar = false; + } + else + fmtpos = accum; + accum = 0; + goto nextch1; + case 'l': + if (longflag) + longlongflag = 1; + else + longflag = 1; + goto nextch1; + case 'z': +#if SIZEOF_SIZE_T == 8 +#ifdef HAVE_LONG_INT_64 + longflag = 1; +#elif defined(HAVE_LONG_LONG_INT_64) + longlongflag = 1; +#else +#error "Don't know how to print 64bit integers" +#endif +#else + /* assume size_t is same size as int */ +#endif + goto nextch1; + case 'h': + case '\'': + /* ignore these */ + goto nextch1; + case 'd': + case 'i': + case 'o': + case 'u': + case 'x': + case 'X': + if (fmtpos) + { + PrintfArgType atype; + + if (longlongflag) + atype = ATYPE_LONGLONG; + else if (longflag) + atype = ATYPE_LONG; + else + atype = ATYPE_INT; + if (argtypes[fmtpos] && + argtypes[fmtpos] != atype) + goto bad_format; + argtypes[fmtpos] = atype; + last_dollar = Max(last_dollar, fmtpos); + } + else + have_non_dollar = true; + break; + case 'c': + if (fmtpos) + { + if (argtypes[fmtpos] && + argtypes[fmtpos] != ATYPE_INT) + goto bad_format; + argtypes[fmtpos] = ATYPE_INT; + last_dollar = Max(last_dollar, fmtpos); + } + else + have_non_dollar = true; + break; + case 's': + case 'p': + if (fmtpos) + { + if (argtypes[fmtpos] && + argtypes[fmtpos] != ATYPE_CHARPTR) + goto bad_format; + argtypes[fmtpos] = ATYPE_CHARPTR; + last_dollar = Max(last_dollar, fmtpos); + } + else + have_non_dollar = true; + break; + case 'e': + case 'E': + case 'f': + case 'g': + case 'G': + if (fmtpos) + { + if (argtypes[fmtpos] && + argtypes[fmtpos] != ATYPE_DOUBLE) + goto bad_format; + argtypes[fmtpos] = ATYPE_DOUBLE; + last_dollar = Max(last_dollar, fmtpos); + } + else + have_non_dollar = true; + break; + case '%': + break; + } + + /* + * If we finish the spec with afterstar still set, there's a + * non-dollar star in there. + */ + if (afterstar) + have_non_dollar = true; + } + + /* Per spec, you use either all dollar or all not. */ + if (have_dollar && have_non_dollar) + goto bad_format; + + /* + * In dollar mode, collect the arguments in physical order. + */ + for (i = 1; i <= last_dollar; i++) + { + switch (argtypes[i]) + { + case ATYPE_NONE: + goto bad_format; + case ATYPE_INT: + argvalues[i].i = va_arg(args, int); + break; + case ATYPE_LONG: + argvalues[i].l = va_arg(args, long); + break; + case ATYPE_LONGLONG: + argvalues[i].ll = va_arg(args, int64); + break; + case ATYPE_DOUBLE: + argvalues[i].d = va_arg(args, double); + break; + case ATYPE_CHARPTR: + argvalues[i].cptr = va_arg(args, char *); + break; + } + } + + /* + * At last we can parse the format for real. + */ + format = format_start; + while ((ch = *format++) != '\0') + { + if (target->failed) + break; + + if (ch != '%') + { + dopr_outch(ch, target); + continue; + } + fieldwidth = precision = zpad = leftjust = forcesign = 0; + longflag = longlongflag = pointflag = 0; + fmtpos = accum = 0; + have_star = afterstar = false; +nextch2: + ch = *format++; + if (ch == '\0') + break; /* illegal, but we don't complain */ + switch (ch) + { + case '-': + leftjust = 1; + goto nextch2; + case '+': + forcesign = 1; + goto nextch2; + case '0': + /* set zero padding if no nonzero digits yet */ + if (accum == 0 && !pointflag) + zpad = '0'; + /* FALL THRU */ + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + accum = accum * 10 + (ch - '0'); + goto nextch2; + case '.': + if (have_star) + have_star = false; + else + fieldwidth = accum; + pointflag = 1; + accum = 0; + goto nextch2; + case '*': + if (have_dollar) + { + /* process value after reading n$ */ + afterstar = true; + } + else + { + /* fetch and process value now */ + int starval = va_arg(args, int); + + if (pointflag) + { + precision = starval; + if (precision < 0) + { + precision = 0; + pointflag = 0; + } + } + else + { + fieldwidth = starval; + if (fieldwidth < 0) + { + leftjust = 1; + fieldwidth = -fieldwidth; + } + } + } + have_star = true; + accum = 0; + goto nextch2; + case '$': + if (afterstar) + { + /* fetch and process star value */ + int starval = argvalues[accum].i; + + if (pointflag) + { + precision = starval; + if (precision < 0) + { + precision = 0; + pointflag = 0; + } + } + else + { + fieldwidth = starval; + if (fieldwidth < 0) + { + leftjust = 1; + fieldwidth = -fieldwidth; + } + } + afterstar = false; + } + else + fmtpos = accum; + accum = 0; + goto nextch2; + case 'l': + if (longflag) + longlongflag = 1; + else + longflag = 1; + goto nextch2; + case 'z': +#if SIZEOF_SIZE_T == 8 +#ifdef HAVE_LONG_INT_64 + longflag = 1; +#elif defined(HAVE_LONG_LONG_INT_64) + longlongflag = 1; +#else +#error "Don't know how to print 64bit integers" +#endif +#else + /* assume size_t is same size as int */ +#endif + goto nextch2; + case 'h': + case '\'': + /* ignore these */ + goto nextch2; + case 'd': + case 'i': + if (!have_star) + { + if (pointflag) + precision = accum; + else + fieldwidth = accum; + } + if (have_dollar) + { + if (longlongflag) + numvalue = argvalues[fmtpos].ll; + else if (longflag) + numvalue = argvalues[fmtpos].l; + else + numvalue = argvalues[fmtpos].i; + } + else + { + if (longlongflag) + numvalue = va_arg(args, int64); + else if (longflag) + numvalue = va_arg(args, long); + else + numvalue = va_arg(args, int); + } + fmtint(numvalue, ch, forcesign, leftjust, fieldwidth, zpad, + precision, pointflag, target); + break; + case 'o': + case 'u': + case 'x': + case 'X': + if (!have_star) + { + if (pointflag) + precision = accum; + else + fieldwidth = accum; + } + if (have_dollar) + { + if (longlongflag) + numvalue = (uint64) argvalues[fmtpos].ll; + else if (longflag) + numvalue = (unsigned long) argvalues[fmtpos].l; + else + numvalue = (unsigned int) argvalues[fmtpos].i; + } + else + { + if (longlongflag) + numvalue = (uint64) va_arg(args, int64); + else if (longflag) + numvalue = (unsigned long) va_arg(args, long); + else + numvalue = (unsigned int) va_arg(args, int); + } + fmtint(numvalue, ch, forcesign, leftjust, fieldwidth, zpad, + precision, pointflag, target); + break; + case 'c': + if (!have_star) + { + if (pointflag) + precision = accum; + else + fieldwidth = accum; + } + if (have_dollar) + cvalue = (unsigned char) argvalues[fmtpos].i; + else + cvalue = (unsigned char) va_arg(args, int); + fmtchar(cvalue, leftjust, fieldwidth, target); + break; + case 's': + if (!have_star) + { + if (pointflag) + precision = accum; + else + fieldwidth = accum; + } + if (have_dollar) + strvalue = argvalues[fmtpos].cptr; + else + strvalue = va_arg(args, char *); + fmtstr(strvalue, leftjust, fieldwidth, precision, pointflag, + target); + break; + case 'p': + /* fieldwidth/leftjust are ignored ... */ + if (have_dollar) + strvalue = argvalues[fmtpos].cptr; + else + strvalue = va_arg(args, char *); + fmtptr((void *) strvalue, target); + break; + case 'e': + case 'E': + case 'f': + case 'g': + case 'G': + if (!have_star) + { + if (pointflag) + precision = accum; + else + fieldwidth = accum; + } + if (have_dollar) + fvalue = argvalues[fmtpos].d; + else + fvalue = va_arg(args, double); + fmtfloat(fvalue, ch, forcesign, leftjust, + fieldwidth, zpad, + precision, pointflag, + target); + break; + case '%': + dopr_outch('%', target); + break; + } + } + + return; + +bad_format: + errno = EINVAL; + target->failed = true; +} + +static size_t +pg_strnlen(const char *str, size_t maxlen) +{ + const char *p = str; + + while (maxlen-- > 0 && *p) + p++; + return p - str; +} + +static void +fmtstr(char *value, int leftjust, int minlen, int maxwidth, + int pointflag, PrintfTarget *target) +{ + int padlen, + vallen; /* amount to pad */ + + /* + * If a maxwidth (precision) is specified, we must not fetch more bytes + * than that. + */ + if (pointflag) + vallen = pg_strnlen(value, maxwidth); + else + vallen = strlen(value); + + adjust_padlen(minlen, vallen, leftjust, &padlen); + + while (padlen > 0) + { + dopr_outch(' ', target); + --padlen; + } + + dostr(value, vallen, target); + + trailing_pad(&padlen, target); +} + +static void +fmtptr(void *value, PrintfTarget *target) +{ + int vallen; + char convert[64]; + + /* we rely on regular C library's sprintf to do the basic conversion */ + vallen = sprintf(convert, "%p", value); + if (vallen < 0) + target->failed = true; + else + dostr(convert, vallen, target); +} + +static void +fmtint(int64 value, char type, int forcesign, int leftjust, + int minlen, int zpad, int precision, int pointflag, + PrintfTarget *target) +{ + uint64 base; + int dosign; + const char *cvt = "0123456789abcdef"; + int signvalue = 0; + char convert[64]; + int vallen = 0; + int padlen = 0; /* amount to pad */ + int zeropad; /* extra leading zeroes */ + + switch (type) + { + case 'd': + case 'i': + base = 10; + dosign = 1; + break; + case 'o': + base = 8; + dosign = 0; + break; + case 'u': + base = 10; + dosign = 0; + break; + case 'x': + base = 16; + dosign = 0; + break; + case 'X': + cvt = "0123456789ABCDEF"; + base = 16; + dosign = 0; + break; + default: + return; /* keep compiler quiet */ + } + + /* Handle +/- */ + if (dosign && adjust_sign((value < 0), forcesign, &signvalue)) + value = -value; + + /* + * SUS: the result of converting 0 with an explicit precision of 0 is no + * characters + */ + if (value == 0 && pointflag && precision == 0) + vallen = 0; + else + { + /* make integer string */ + uint64 uvalue = (uint64) value; + + do + { + convert[vallen++] = cvt[uvalue % base]; + uvalue = uvalue / base; + } while (uvalue); + } + + zeropad = Max(0, precision - vallen); + + adjust_padlen(minlen, vallen + zeropad, leftjust, &padlen); + + leading_pad(zpad, &signvalue, &padlen, target); + + while (zeropad-- > 0) + dopr_outch('0', target); + + while (vallen > 0) + dopr_outch(convert[--vallen], target); + + trailing_pad(&padlen, target); +} + +static void +fmtchar(int value, int leftjust, int minlen, PrintfTarget *target) +{ + int padlen = 0; /* amount to pad */ + + adjust_padlen(minlen, 1, leftjust, &padlen); + + while (padlen > 0) + { + dopr_outch(' ', target); + --padlen; + } + + dopr_outch(value, target); + + trailing_pad(&padlen, target); +} + +static void +fmtfloat(double value, char type, int forcesign, int leftjust, + int minlen, int zpad, int precision, int pointflag, + PrintfTarget *target) +{ + int signvalue = 0; + int prec; + int vallen; + char fmt[32]; + char convert[1024]; + int zeropadlen = 0; /* amount to pad with zeroes */ + int padlen = 0; /* amount to pad with spaces */ + + /* + * We rely on the regular C library's sprintf to do the basic conversion, + * then handle padding considerations here. + * + * The dynamic range of "double" is about 1E+-308 for IEEE math, and not + * too wildly more than that with other hardware. In "f" format, sprintf + * could therefore generate at most 308 characters to the left of the + * decimal point; while we need to allow the precision to get as high as + * 308+17 to ensure that we don't truncate significant digits from very + * small values. To handle both these extremes, we use a buffer of 1024 + * bytes and limit requested precision to 350 digits; this should prevent + * buffer overrun even with non-IEEE math. If the original precision + * request was more than 350, separately pad with zeroes. + */ + if (precision < 0) /* cover possible overflow of "accum" */ + precision = 0; + prec = Min(precision, 350); + + if (pointflag) + { + if (sprintf(fmt, "%%.%d%c", prec, type) < 0) + goto fail; + zeropadlen = precision - prec; + } + else if (sprintf(fmt, "%%%c", type) < 0) + goto fail; + + if (!isnan(value) && adjust_sign((value < 0), forcesign, &signvalue)) + value = -value; + + vallen = sprintf(convert, fmt, value); + if (vallen < 0) + goto fail; + + /* If it's infinity or NaN, forget about doing any zero-padding */ + if (zeropadlen > 0 && !isdigit((unsigned char) convert[vallen - 1])) + zeropadlen = 0; + + adjust_padlen(minlen, vallen + zeropadlen, leftjust, &padlen); + + leading_pad(zpad, &signvalue, &padlen, target); + + if (zeropadlen > 0) + { + /* If 'e' or 'E' format, inject zeroes before the exponent */ + char *epos = strrchr(convert, 'e'); + + if (!epos) + epos = strrchr(convert, 'E'); + if (epos) + { + /* pad after exponent */ + dostr(convert, epos - convert, target); + while (zeropadlen-- > 0) + dopr_outch('0', target); + dostr(epos, vallen - (epos - convert), target); + } + else + { + /* no exponent, pad after the digits */ + dostr(convert, vallen, target); + while (zeropadlen-- > 0) + dopr_outch('0', target); + } + } + else + { + /* no zero padding, just emit the number as-is */ + dostr(convert, vallen, target); + } + + trailing_pad(&padlen, target); + return; + +fail: + target->failed = true; +} + +static void +dostr(const char *str, int slen, PrintfTarget *target) +{ + while (slen > 0) + { + int avail; + + if (target->bufend != NULL) + avail = target->bufend - target->bufptr; + else + avail = slen; + if (avail <= 0) + { + /* buffer full, can we dump to stream? */ + if (target->stream == NULL) + return; /* no, lose the data */ + flushbuffer(target); + continue; + } + avail = Min(avail, slen); + memmove(target->bufptr, str, avail); + target->bufptr += avail; + str += avail; + slen -= avail; + } +} + +static void +dopr_outch(int c, PrintfTarget *target) +{ + if (target->bufend != NULL && target->bufptr >= target->bufend) + { + /* buffer full, can we dump to stream? */ + if (target->stream == NULL) + return; /* no, lose the data */ + flushbuffer(target); + } + *(target->bufptr++) = c; +} + + +static int +adjust_sign(int is_negative, int forcesign, int *signvalue) +{ + if (is_negative) + { + *signvalue = '-'; + return true; + } + else if (forcesign) + *signvalue = '+'; + return false; +} + + +static void +adjust_padlen(int minlen, int vallen, int leftjust, int *padlen) +{ + *padlen = minlen - vallen; + if (*padlen < 0) + *padlen = 0; + if (leftjust) + *padlen = -(*padlen); +} + + +static void +leading_pad(int zpad, int *signvalue, int *padlen, PrintfTarget *target) +{ + if (*padlen > 0 && zpad) + { + if (*signvalue) + { + dopr_outch(*signvalue, target); + --(*padlen); + *signvalue = 0; + } + while (*padlen > 0) + { + dopr_outch(zpad, target); + --(*padlen); + } + } + while (*padlen > (*signvalue != 0)) + { + dopr_outch(' ', target); + --(*padlen); + } + if (*signvalue) + { + dopr_outch(*signvalue, target); + if (*padlen > 0) + --(*padlen); + else if (*padlen < 0) + ++(*padlen); + } +} + + +static void +trailing_pad(int *padlen, PrintfTarget *target) +{ + while (*padlen < 0) + { + dopr_outch(' ', target); + ++(*padlen); + } +} diff --git a/libpq/win32/system.c b/libpq/win32/system.c new file mode 100644 index 0000000..15b6e37 --- /dev/null +++ b/libpq/win32/system.c @@ -0,0 +1,119 @@ +/*------------------------------------------------------------------------- + * + * system.c + * Win32 system() and popen() replacements + * + * + * Win32 needs double quotes at the beginning and end of system() + * strings. If not, it gets confused with multiple quoted strings. + * It also requires double-quotes around the executable name and + * any files used for redirection. Filter other args through + * appendShellString() to quote them. + * + * Generated using Win32 "CMD /?": + * + * 1. If all of the following conditions are met, then quote characters + * on the command line are preserved: + * + * - no /S switch + * - exactly two quote characters + * - no special characters between the two quote characters, where special + * is one of: &<>()@^| + * - there are one or more whitespace characters between the two quote + * characters + * - the string between the two quote characters is the name of an + * executable file. + * + * 2. Otherwise, old behavior is to see if the first character is a quote + * character and if so, strip the leading character and remove the last + * quote character on the command line, preserving any text after the last + * quote character. + * + * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * + * src/port/system.c + * + *------------------------------------------------------------------------- + */ +int a; + +#if defined(WIN32) && !defined(__CYGWIN__) + +#ifndef FRONTEND +#include "postgres.h" +#else +#include "postgres_fe.h" +#endif + +#include +#include + +#undef system +#undef popen + +int +pgwin32_system(const char *command) +{ + size_t cmdlen = strlen(command); + char *buf; + int save_errno; + int res; + + /* + * Create a malloc'd copy of the command string, enclosed with an extra + * pair of quotes + */ + buf = malloc(cmdlen + 2 + 1); + if (buf == NULL) + { + errno = ENOMEM; + return -1; + } + buf[0] = '"'; + memcpy(&buf[1], command, cmdlen); + buf[cmdlen + 1] = '"'; + buf[cmdlen + 2] = '\0'; + + res = system(buf); + + save_errno = errno; + free(buf); + errno = save_errno; + + return res; +} + + +FILE * +pgwin32_popen(const char *command, const char *type) +{ + size_t cmdlen = strlen(command); + char *buf; + int save_errno; + FILE *res; + + /* + * Create a malloc'd copy of the command string, enclosed with an extra + * pair of quotes + */ + buf = malloc(cmdlen + 2 + 1); + if (buf == NULL) + { + errno = ENOMEM; + return NULL; + } + buf[0] = '"'; + memcpy(&buf[1], command, cmdlen); + buf[cmdlen + 1] = '"'; + buf[cmdlen + 2] = '\0'; + + res = _popen(buf, type); + + save_errno = errno; + free(buf); + errno = save_errno; + + return res; +} + +#endif diff --git a/libpq/win32/win32.c b/libpq/win32/win32.c new file mode 100644 index 0000000..1b7ad37 --- /dev/null +++ b/libpq/win32/win32.c @@ -0,0 +1,327 @@ +/* + * src/interfaces/libpq/win32.c + * + * + * FILE + * win32.c + * + * DESCRIPTION + * Win32 support functions. + * + * Contains table and functions for looking up win32 socket error + * descriptions. But will/may contain other win32 helper functions + * for libpq. + * + * The error constants are taken from the Frambak Bakfram LGSOCKET + * library guys who in turn took them from the Winsock FAQ. + * + * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + */ + +/* Make stuff compile faster by excluding not used stuff */ + +#define VC_EXTRALEAN +#ifndef __MINGW32__ +#define NOGDI +#endif +#define NOCRYPT + +#include "postgres_fe.h" + +#include "win32.h" + +/* Declared here to avoid pulling in all includes, which causes name collisions */ +#ifdef ENABLE_NLS +extern char *libpq_gettext(const char *msgid) pg_attribute_format_arg(1); +#else +#define libpq_gettext(x) (x) +#endif + + +static struct WSErrorEntry +{ + DWORD error; + const char *description; +} WSErrors[] = + +{ + { + 0, "No error" + }, + { + WSAEINTR, "Interrupted system call" + }, + { + WSAEBADF, "Bad file number" + }, + { + WSAEACCES, "Permission denied" + }, + { + WSAEFAULT, "Bad address" + }, + { + WSAEINVAL, "Invalid argument" + }, + { + WSAEMFILE, "Too many open sockets" + }, + { + WSAEWOULDBLOCK, "Operation would block" + }, + { + WSAEINPROGRESS, "Operation now in progress" + }, + { + WSAEALREADY, "Operation already in progress" + }, + { + WSAENOTSOCK, "Socket operation on non-socket" + }, + { + WSAEDESTADDRREQ, "Destination address required" + }, + { + WSAEMSGSIZE, "Message too long" + }, + { + WSAEPROTOTYPE, "Protocol wrong type for socket" + }, + { + WSAENOPROTOOPT, "Bad protocol option" + }, + { + WSAEPROTONOSUPPORT, "Protocol not supported" + }, + { + WSAESOCKTNOSUPPORT, "Socket type not supported" + }, + { + WSAEOPNOTSUPP, "Operation not supported on socket" + }, + { + WSAEPFNOSUPPORT, "Protocol family not supported" + }, + { + WSAEAFNOSUPPORT, "Address family not supported" + }, + { + WSAEADDRINUSE, "Address already in use" + }, + { + WSAEADDRNOTAVAIL, "Cannot assign requested address" + }, + { + WSAENETDOWN, "Network is down" + }, + { + WSAENETUNREACH, "Network is unreachable" + }, + { + WSAENETRESET, "Net connection reset" + }, + { + WSAECONNABORTED, "Software caused connection abort" + }, + { + WSAECONNRESET, "Connection reset by peer" + }, + { + WSAENOBUFS, "No buffer space available" + }, + { + WSAEISCONN, "Socket is already connected" + }, + { + WSAENOTCONN, "Socket is not connected" + }, + { + WSAESHUTDOWN, "Cannot send after socket shutdown" + }, + { + WSAETOOMANYREFS, "Too many references, cannot splice" + }, + { + WSAETIMEDOUT, "Connection timed out" + }, + { + WSAECONNREFUSED, "Connection refused" + }, + { + WSAELOOP, "Too many levels of symbolic links" + }, + { + WSAENAMETOOLONG, "File name too long" + }, + { + WSAEHOSTDOWN, "Host is down" + }, + { + WSAEHOSTUNREACH, "No route to host" + }, + { + WSAENOTEMPTY, "Directory not empty" + }, + { + WSAEPROCLIM, "Too many processes" + }, + { + WSAEUSERS, "Too many users" + }, + { + WSAEDQUOT, "Disc quota exceeded" + }, + { + WSAESTALE, "Stale NFS file handle" + }, + { + WSAEREMOTE, "Too many levels of remote in path" + }, + { + WSASYSNOTREADY, "Network system is unavailable" + }, + { + WSAVERNOTSUPPORTED, "Winsock version out of range" + }, + { + WSANOTINITIALISED, "WSAStartup not yet called" + }, + { + WSAEDISCON, "Graceful shutdown in progress" + }, + { + WSAHOST_NOT_FOUND, "Host not found" + }, + { + WSATRY_AGAIN, "NA Host not found / SERVFAIL" + }, + { + WSANO_RECOVERY, "Non recoverable FORMERR||REFUSED||NOTIMP" + }, + { + WSANO_DATA, "No host data of that type was found" + }, + { + 0, 0 + } /* End of table */ +}; + + +/* + * Returns 0 if not found, linear but who cares, at this moment + * we're already in pain :) + */ + +static int +LookupWSErrorMessage(DWORD err, char *dest) +{ + struct WSErrorEntry *e; + + for (e = WSErrors; e->description; e++) + { + if (e->error == err) + { + strcpy(dest, e->description); + return 1; + } + } + return 0; +} + + +struct MessageDLL +{ + const char *dll_name; + void *handle; + int loaded; /* BOOL */ +} dlls[] = + +{ + { + "netmsg.dll", 0, 0 + }, + { + "winsock.dll", 0, 0 + }, + { + "ws2_32.dll", 0, 0 + }, + { + "wsock32n.dll", 0, 0 + }, + { + "mswsock.dll", 0, 0 + }, + { + "ws2help.dll", 0, 0 + }, + { + "ws2thk.dll", 0, 0 + }, + { + 0, 0, 1 + } /* Last one, no dll, always loaded */ +}; + +#define DLLS_SIZE (sizeof(dlls)/sizeof(struct MessageDLL)) + +/* + * Returns a description of the socket error by first trying + * to find it in the lookup table, and if that fails, tries + * to load any of the winsock dlls to find that message. + * The DLL thing works from Nt4 (spX ?) up, but some special + * versions of winsock might have this as well (seen on Win98 SE + * special install) / Magnus Naeslund (mag@fbab.net) + * + */ + +const char * +winsock_strerror(int err, char *strerrbuf, size_t buflen) +{ + unsigned long flags; + int offs, + i; + int success = LookupWSErrorMessage(err, strerrbuf); + + for (i = 0; !success && i < DLLS_SIZE; i++) + { + + if (!dlls[i].loaded) + { + dlls[i].loaded = 1; /* Only load once */ + dlls[i].handle = (void *) LoadLibraryEx( + dlls[i].dll_name, + 0, + LOAD_LIBRARY_AS_DATAFILE); + } + + if (dlls[i].dll_name && !dlls[i].handle) + continue; /* Didn't load */ + + flags = FORMAT_MESSAGE_FROM_SYSTEM + | FORMAT_MESSAGE_IGNORE_INSERTS + | (dlls[i].handle ? FORMAT_MESSAGE_FROM_HMODULE : 0); + + success = 0 != FormatMessage( + flags, + dlls[i].handle, err, + MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT), + strerrbuf, buflen - 64, + 0 + ); + } + + if (!success) + sprintf(strerrbuf, libpq_gettext("unrecognized socket error: 0x%08X/%d"), err, err); + else + { + strerrbuf[buflen - 1] = '\0'; + offs = strlen(strerrbuf); + if (offs > (int) buflen - 64) + offs = buflen - 64; + sprintf(strerrbuf + offs, " (0x%08X/%d)", err, err); + } + return strerrbuf; +} diff --git a/libpq/win32/win32.h b/libpq/win32/win32.h new file mode 100644 index 0000000..be00ea7 --- /dev/null +++ b/libpq/win32/win32.h @@ -0,0 +1,40 @@ +/* + * src/interfaces/libpq/win32.h + */ +#ifndef __win32_h_included +#define __win32_h_included + +/* + * Some compatibility functions + */ +#ifdef __BORLANDC__ +#define _timeb timeb +#define _ftime(a) ftime(a) +#define _errno errno +#define popen(a,b) _popen(a,b) +#else +/* open provided elsewhere */ +#define close(a) _close(a) +#define read(a,b,c) _read(a,b,c) +#define write(a,b,c) _write(a,b,c) +#endif + +#undef EAGAIN /* doesn't apply on sockets */ +#undef EINTR +#define EINTR WSAEINTR +#ifndef EWOULDBLOCK +#define EWOULDBLOCK WSAEWOULDBLOCK +#endif +#ifndef ECONNRESET +#define ECONNRESET WSAECONNRESET +#endif +#ifndef EINPROGRESS +#define EINPROGRESS WSAEINPROGRESS +#endif + +/* + * support for handling Windows Socket errors + */ +extern const char *winsock_strerror(int err, char *strerrbuf, size_t buflen); + +#endif diff --git a/libpq/win32/win32error.c b/libpq/win32/win32error.c new file mode 100644 index 0000000..cf65225 --- /dev/null +++ b/libpq/win32/win32error.c @@ -0,0 +1,206 @@ +/*------------------------------------------------------------------------- + * + * win32error.c + * Map win32 error codes to errno values + * + * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * + * IDENTIFICATION + * src/port/win32error.c + * + *------------------------------------------------------------------------- + */ + +#ifndef FRONTEND +#include "postgres.h" +#else +#include "postgres_fe.h" +#endif + +static const struct +{ + DWORD winerr; + int doserr; +} doserrors[] = + +{ + { + ERROR_INVALID_FUNCTION, EINVAL + }, + { + ERROR_FILE_NOT_FOUND, ENOENT + }, + { + ERROR_PATH_NOT_FOUND, ENOENT + }, + { + ERROR_TOO_MANY_OPEN_FILES, EMFILE + }, + { + ERROR_ACCESS_DENIED, EACCES + }, + { + ERROR_INVALID_HANDLE, EBADF + }, + { + ERROR_ARENA_TRASHED, ENOMEM + }, + { + ERROR_NOT_ENOUGH_MEMORY, ENOMEM + }, + { + ERROR_INVALID_BLOCK, ENOMEM + }, + { + ERROR_BAD_ENVIRONMENT, E2BIG + }, + { + ERROR_BAD_FORMAT, ENOEXEC + }, + { + ERROR_INVALID_ACCESS, EINVAL + }, + { + ERROR_INVALID_DATA, EINVAL + }, + { + ERROR_INVALID_DRIVE, ENOENT + }, + { + ERROR_CURRENT_DIRECTORY, EACCES + }, + { + ERROR_NOT_SAME_DEVICE, EXDEV + }, + { + ERROR_NO_MORE_FILES, ENOENT + }, + { + ERROR_LOCK_VIOLATION, EACCES + }, + { + ERROR_SHARING_VIOLATION, EACCES + }, + { + ERROR_BAD_NETPATH, ENOENT + }, + { + ERROR_NETWORK_ACCESS_DENIED, EACCES + }, + { + ERROR_BAD_NET_NAME, ENOENT + }, + { + ERROR_FILE_EXISTS, EEXIST + }, + { + ERROR_CANNOT_MAKE, EACCES + }, + { + ERROR_FAIL_I24, EACCES + }, + { + ERROR_INVALID_PARAMETER, EINVAL + }, + { + ERROR_NO_PROC_SLOTS, EAGAIN + }, + { + ERROR_DRIVE_LOCKED, EACCES + }, + { + ERROR_BROKEN_PIPE, EPIPE + }, + { + ERROR_DISK_FULL, ENOSPC + }, + { + ERROR_INVALID_TARGET_HANDLE, EBADF + }, + { + ERROR_INVALID_HANDLE, EINVAL + }, + { + ERROR_WAIT_NO_CHILDREN, ECHILD + }, + { + ERROR_CHILD_NOT_COMPLETE, ECHILD + }, + { + ERROR_DIRECT_ACCESS_HANDLE, EBADF + }, + { + ERROR_NEGATIVE_SEEK, EINVAL + }, + { + ERROR_SEEK_ON_DEVICE, EACCES + }, + { + ERROR_DIR_NOT_EMPTY, ENOTEMPTY + }, + { + ERROR_NOT_LOCKED, EACCES + }, + { + ERROR_BAD_PATHNAME, ENOENT + }, + { + ERROR_MAX_THRDS_REACHED, EAGAIN + }, + { + ERROR_LOCK_FAILED, EACCES + }, + { + ERROR_ALREADY_EXISTS, EEXIST + }, + { + ERROR_FILENAME_EXCED_RANGE, ENOENT + }, + { + ERROR_NESTING_NOT_ALLOWED, EAGAIN + }, + { + ERROR_NOT_ENOUGH_QUOTA, ENOMEM + } +}; + +void +_dosmaperr(unsigned long e) +{ + int i; + + if (e == 0) + { + errno = 0; + return; + } + + for (i = 0; i < lengthof(doserrors); i++) + { + if (doserrors[i].winerr == e) + { + int doserr = doserrors[i].doserr; + +#ifndef FRONTEND + ereport(DEBUG5, + (errmsg_internal("mapped win32 error code %lu to %d", + e, doserr))); +#elif FRONTEND_DEBUG + fprintf(stderr, "mapped win32 error code %lu to %d", e, doserr); +#endif + errno = doserr; + return; + } + } + +#ifndef FRONTEND + ereport(LOG, + (errmsg_internal("unrecognized win32 error code: %lu", + e))); +#else + fprintf(stderr, "unrecognized win32 error code: %lu", e); +#endif + + errno = EINVAL; + return; +} diff --git a/libpq/win32/win32setlocale.c b/libpq/win32/win32setlocale.c new file mode 100644 index 0000000..4abc7aa --- /dev/null +++ b/libpq/win32/win32setlocale.c @@ -0,0 +1,189 @@ +/*------------------------------------------------------------------------- + * + * win32setlocale.c + * Wrapper to work around bugs in Windows setlocale() implementation + * + * Copyright (c) 2011-2016, PostgreSQL Global Development Group + * + * IDENTIFICATION + * src/port/win32setlocale.c + * + * + * The setlocale() function in Windows is broken in two ways. First, it + * has a problem with locale names that have a dot in the country name. For + * example: + * + * "Chinese (Traditional)_Hong Kong S.A.R..950" + * + * For some reason, setlocale() doesn't accept that as argument, even though + * setlocale(LC_ALL, NULL) returns exactly that. Fortunately, it accepts + * various alternative names for such countries, so to work around the broken + * setlocale() function, we map the troublemaking locale names to accepted + * aliases, before calling setlocale(). + * + * The second problem is that the locale name for "Norwegian (Bokmål)" + * contains a non-ASCII character. That's problematic, because it's not clear + * what encoding the locale name itself is supposed to be in, when you + * haven't yet set a locale. Also, it causes problems when the cluster + * contains databases with different encodings, as the locale name is stored + * in the pg_database system catalog. To work around that, when setlocale() + * returns that locale name, map it to a pure-ASCII alias for the same + * locale. + *------------------------------------------------------------------------- + */ + +#include "c.h" + +#undef setlocale + +struct locale_map +{ + /* + * String in locale name to replace. Can be a single string (end is NULL), + * or separate start and end strings. If two strings are given, the locale + * name must contain both of them, and everything between them is + * replaced. This is used for a poor-man's regexp search, allowing + * replacement of "start.*end". + */ + const char *locale_name_start; + const char *locale_name_end; + + const char *replacement; /* string to replace the match with */ +}; + +/* + * Mappings applied before calling setlocale(), to the argument. + */ +static const struct locale_map locale_map_argument[] = { + /* + * "HKG" is listed here: + * http://msdn.microsoft.com/en-us/library/cdax410z%28v=vs.71%29.aspx + * (Country/Region Strings). + * + * "ARE" is the ISO-3166 three-letter code for U.A.E. It is not on the + * above list, but seems to work anyway. + */ + {"Hong Kong S.A.R.", NULL, "HKG"}, + {"U.A.E.", NULL, "ARE"}, + + /* + * The ISO-3166 country code for Macau S.A.R. is MAC, but Windows doesn't + * seem to recognize that. And Macau isn't listed in the table of accepted + * abbreviations linked above. Fortunately, "ZHM" seems to be accepted as + * an alias for "Chinese (Traditional)_Macau S.A.R..950". I'm not sure + * where "ZHM" comes from, must be some legacy naming scheme. But hey, it + * works. + * + * Note that unlike HKG and ARE, ZHM is an alias for the *whole* locale + * name, not just the country part. + * + * Some versions of Windows spell it "Macau", others "Macao". + */ + {"Chinese (Traditional)_Macau S.A.R..950", NULL, "ZHM"}, + {"Chinese_Macau S.A.R..950", NULL, "ZHM"}, + {"Chinese (Traditional)_Macao S.A.R..950", NULL, "ZHM"}, + {"Chinese_Macao S.A.R..950", NULL, "ZHM"}, + {NULL, NULL, NULL} +}; + +/* + * Mappings applied after calling setlocale(), to its return value. + */ +static const struct locale_map locale_map_result[] = { + /* + * "Norwegian (Bokmål)" locale name contains the a-ring character. + * Map it to a pure-ASCII alias. + * + * It's not clear what encoding setlocale() uses when it returns the + * locale name, so to play it safe, we search for "Norwegian (Bok*l)". + */ + {"Norwegian (Bokm", "l)_Norway", "Norwegian_Norway"}, + {NULL, NULL, NULL} +}; + +#define MAX_LOCALE_NAME_LEN 100 + +static const char * +map_locale(const struct locale_map * map, const char *locale) +{ + static char aliasbuf[MAX_LOCALE_NAME_LEN]; + int i; + + /* Check if the locale name matches any of the problematic ones. */ + for (i = 0; map[i].locale_name_start != NULL; i++) + { + const char *needle_start = map[i].locale_name_start; + const char *needle_end = map[i].locale_name_end; + const char *replacement = map[i].replacement; + char *match; + char *match_start = NULL; + char *match_end = NULL; + + match = strstr(locale, needle_start); + if (match) + { + /* + * Found a match for the first part. If this was a two-part + * replacement, find the second part. + */ + match_start = match; + if (needle_end) + { + match = strstr(match_start + strlen(needle_start), needle_end); + if (match) + match_end = match + strlen(needle_end); + else + match_start = NULL; + } + else + match_end = match_start + strlen(needle_start); + } + + if (match_start) + { + /* Found a match. Replace the matched string. */ + int matchpos = match_start - locale; + int replacementlen = strlen(replacement); + char *rest = match_end; + int restlen = strlen(rest); + + /* check that the result fits in the static buffer */ + if (matchpos + replacementlen + restlen + 1 > MAX_LOCALE_NAME_LEN) + return NULL; + + memcpy(&aliasbuf[0], &locale[0], matchpos); + memcpy(&aliasbuf[matchpos], replacement, replacementlen); + /* includes null terminator */ + memcpy(&aliasbuf[matchpos + replacementlen], rest, restlen + 1); + + return aliasbuf; + } + } + + /* no match, just return the original string */ + return locale; +} + +char * +pgwin32_setlocale(int category, const char *locale) +{ + const char *argument; + char *result; + + if (locale == NULL) + argument = NULL; + else + argument = map_locale(locale_map_argument, locale); + + /* Call the real setlocale() function */ + result = setlocale(category, argument); + + /* + * setlocale() is specified to return a "char *" that the caller is + * forbidden to modify, so casting away the "const" is innocuous. + */ + if (result) + result = (char *) map_locale(locale_map_result, result); + + return result; +} -- cgit v1.1