summaryrefslogtreecommitdiffstats
path: root/src/interfaces/libpq
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:19:15 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:19:15 +0000
commit6eb9c5a5657d1fe77b55cc261450f3538d35a94d (patch)
tree657d8194422a5daccecfd42d654b8a245ef7b4c8 /src/interfaces/libpq
parentInitial commit. (diff)
downloadpostgresql-13-6eb9c5a5657d1fe77b55cc261450f3538d35a94d.tar.xz
postgresql-13-6eb9c5a5657d1fe77b55cc261450f3538d35a94d.zip
Adding upstream version 13.4.upstream/13.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/interfaces/libpq')
-rw-r--r--src/interfaces/libpq/.gitignore1
-rw-r--r--src/interfaces/libpq/Makefile133
-rw-r--r--src/interfaces/libpq/README3
-rw-r--r--src/interfaces/libpq/exports.txt181
-rw-r--r--src/interfaces/libpq/fe-auth-scram.c860
-rw-r--r--src/interfaces/libpq/fe-auth.c1280
-rw-r--r--src/interfaces/libpq/fe-auth.h36
-rw-r--r--src/interfaces/libpq/fe-connect.c7255
-rw-r--r--src/interfaces/libpq/fe-exec.c3860
-rw-r--r--src/interfaces/libpq/fe-gssapi-common.c131
-rw-r--r--src/interfaces/libpq/fe-gssapi-common.h28
-rw-r--r--src/interfaces/libpq/fe-lobj.c1102
-rw-r--r--src/interfaces/libpq/fe-misc.c1314
-rw-r--r--src/interfaces/libpq/fe-print.c782
-rw-r--r--src/interfaces/libpq/fe-protocol2.c1612
-rw-r--r--src/interfaces/libpq/fe-protocol3.c2178
-rw-r--r--src/interfaces/libpq/fe-secure-common.c211
-rw-r--r--src/interfaces/libpq/fe-secure-common.h26
-rw-r--r--src/interfaces/libpq/fe-secure-gssapi.c731
-rw-r--r--src/interfaces/libpq/fe-secure-openssl.c1790
-rw-r--r--src/interfaces/libpq/fe-secure.c555
-rw-r--r--src/interfaces/libpq/legacy-pqsignal.c57
-rw-r--r--src/interfaces/libpq/libpq-events.c209
-rw-r--r--src/interfaces/libpq/libpq-events.h94
-rw-r--r--src/interfaces/libpq/libpq-fe.h632
-rw-r--r--src/interfaces/libpq/libpq-int.h823
-rw-r--r--src/interfaces/libpq/nls.mk6
-rw-r--r--src/interfaces/libpq/pg_service.conf.sample17
-rw-r--r--src/interfaces/libpq/po/cs.po1334
-rw-r--r--src/interfaces/libpq/po/de.po1273
-rw-r--r--src/interfaces/libpq/po/es.po1290
-rw-r--r--src/interfaces/libpq/po/fr.po1399
-rw-r--r--src/interfaces/libpq/po/it.po1148
-rw-r--r--src/interfaces/libpq/po/ja.po1272
-rw-r--r--src/interfaces/libpq/po/ko.po1340
-rw-r--r--src/interfaces/libpq/po/ru.po1402
-rw-r--r--src/interfaces/libpq/po/sv.po1294
-rw-r--r--src/interfaces/libpq/po/tr.po1247
-rw-r--r--src/interfaces/libpq/po/uk.po1273
-rw-r--r--src/interfaces/libpq/po/zh_CN.po1280
-rw-r--r--src/interfaces/libpq/pqexpbuffer.c414
-rw-r--r--src/interfaces/libpq/pqexpbuffer.h182
-rw-r--r--src/interfaces/libpq/pthread-win32.c60
-rw-r--r--src/interfaces/libpq/test/.gitignore3
-rw-r--r--src/interfaces/libpq/test/Makefile22
-rw-r--r--src/interfaces/libpq/test/README7
-rw-r--r--src/interfaces/libpq/test/expected.out171
-rw-r--r--src/interfaces/libpq/test/regress.in57
-rw-r--r--src/interfaces/libpq/test/regress.pl63
-rw-r--r--src/interfaces/libpq/test/uri-regress.c84
-rw-r--r--src/interfaces/libpq/win32.c324
-rw-r--r--src/interfaces/libpq/win32.h34
52 files changed, 42880 insertions, 0 deletions
diff --git a/src/interfaces/libpq/.gitignore b/src/interfaces/libpq/.gitignore
new file mode 100644
index 0000000..a4afe7c
--- /dev/null
+++ b/src/interfaces/libpq/.gitignore
@@ -0,0 +1 @@
+/exports.list
diff --git a/src/interfaces/libpq/Makefile b/src/interfaces/libpq/Makefile
new file mode 100644
index 0000000..d491997
--- /dev/null
+++ b/src/interfaces/libpq/Makefile
@@ -0,0 +1,133 @@
+#-------------------------------------------------------------------------
+#
+# Makefile for src/interfaces/libpq library
+#
+# Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
+# Portions Copyright (c) 1994, Regents of the University of California
+#
+# src/interfaces/libpq/Makefile
+#
+#-------------------------------------------------------------------------
+
+subdir = src/interfaces/libpq
+top_builddir = ../../..
+include $(top_builddir)/src/Makefile.global
+
+
+PGFILEDESC = "PostgreSQL Access Library"
+
+# shared library parameters
+NAME= pq
+SO_MAJOR_VERSION= 5
+SO_MINOR_VERSION= $(MAJORVERSION)
+
+override CPPFLAGS := -DFRONTEND -DUNSAFE_STAT_OK -I$(srcdir) $(CPPFLAGS) -I$(top_builddir)/src/port -I$(top_srcdir)/src/port
+ifneq ($(PORTNAME), win32)
+override CFLAGS += $(PTHREAD_CFLAGS)
+endif
+
+# The MSVC build system scrapes OBJS from this file. If you change any of
+# the conditional additions of files to OBJS, update Mkvcbuild.pm to match.
+
+OBJS = \
+ $(WIN32RES) \
+ fe-auth-scram.o \
+ fe-connect.o \
+ fe-exec.o \
+ fe-lobj.o \
+ fe-misc.o \
+ fe-print.o \
+ fe-protocol2.o \
+ fe-protocol3.o \
+ fe-secure.o \
+ legacy-pqsignal.o \
+ libpq-events.o \
+ pqexpbuffer.o \
+ fe-auth.o
+
+ifeq ($(with_openssl),yes)
+OBJS += \
+ fe-secure-common.o \
+ fe-secure-openssl.o
+endif
+
+ifeq ($(with_gssapi),yes)
+OBJS += \
+ fe-gssapi-common.o \
+ fe-secure-gssapi.o
+endif
+
+ifeq ($(PORTNAME), cygwin)
+override shlib = cyg$(NAME)$(DLSUFFIX)
+endif
+
+ifeq ($(PORTNAME), win32)
+OBJS += \
+ win32.o
+
+ifeq ($(enable_thread_safety), yes)
+OBJS += pthread-win32.o
+endif
+endif
+
+
+# Add libraries that libpq depends (or might depend) on into the
+# shared library link. (The order in which you list them here doesn't
+# matter.) Note that we filter out -lpgcommon and -lpgport from LIBS and
+# instead link with -lpgcommon_shlib and -lpgport_shlib, to get object files
+# that are built correctly for use in a shlib.
+SHLIB_LINK_INTERNAL = -lpgcommon_shlib -lpgport_shlib
+ifneq ($(PORTNAME), win32)
+SHLIB_LINK += $(filter -lcrypt -ldes -lcom_err -lcrypto -lk5crypto -lkrb5 -lgssapi_krb5 -lgss -lgssapi -lssl -lsocket -lnsl -lresolv -lintl -lm, $(LIBS)) $(LDAP_LIBS_FE) $(PTHREAD_LIBS)
+else
+SHLIB_LINK += $(filter -lcrypt -ldes -lcom_err -lcrypto -lk5crypto -lkrb5 -lgssapi32 -lssl -lsocket -lnsl -lresolv -lintl -lm $(PTHREAD_LIBS), $(LIBS)) $(LDAP_LIBS_FE)
+endif
+ifeq ($(PORTNAME), win32)
+SHLIB_LINK += -lshell32 -lws2_32 -lsecur32 $(filter -leay32 -lssleay32 -lcomerr32 -lkrb5_32, $(LIBS))
+endif
+SHLIB_PREREQS = submake-libpgport
+
+SHLIB_EXPORTS = exports.txt
+
+all: all-lib
+
+# Shared library stuff
+include $(top_srcdir)/src/Makefile.shlib
+backend_src = $(top_srcdir)/src/backend
+
+
+# Make dependencies on pg_config_paths.h visible in all builds.
+fe-connect.o: fe-connect.c $(top_builddir)/src/port/pg_config_paths.h
+fe-misc.o: fe-misc.c $(top_builddir)/src/port/pg_config_paths.h
+
+$(top_builddir)/src/port/pg_config_paths.h:
+ $(MAKE) -C $(top_builddir)/src/port pg_config_paths.h
+
+install: all installdirs install-lib
+ $(INSTALL_DATA) $(srcdir)/libpq-fe.h '$(DESTDIR)$(includedir)'
+ $(INSTALL_DATA) $(srcdir)/libpq-events.h '$(DESTDIR)$(includedir)'
+ $(INSTALL_DATA) $(srcdir)/libpq-int.h '$(DESTDIR)$(includedir_internal)'
+ $(INSTALL_DATA) $(srcdir)/pqexpbuffer.h '$(DESTDIR)$(includedir_internal)'
+ $(INSTALL_DATA) $(srcdir)/pg_service.conf.sample '$(DESTDIR)$(datadir)/pg_service.conf.sample'
+
+installcheck:
+ $(MAKE) -C test $@
+
+installdirs: installdirs-lib
+ $(MKDIR_P) '$(DESTDIR)$(includedir)' '$(DESTDIR)$(includedir_internal)' '$(DESTDIR)$(datadir)'
+
+uninstall: uninstall-lib
+ rm -f '$(DESTDIR)$(includedir)/libpq-fe.h'
+ rm -f '$(DESTDIR)$(includedir)/libpq-events.h'
+ rm -f '$(DESTDIR)$(includedir_internal)/libpq-int.h'
+ rm -f '$(DESTDIR)$(includedir_internal)/pqexpbuffer.h'
+ rm -f '$(DESTDIR)$(datadir)/pg_service.conf.sample'
+
+clean distclean: clean-lib
+ $(MAKE) -C test $@
+ rm -f $(OBJS) pthread.h
+# Might be left over from a Win32 client-only build
+ rm -f pg_config_paths.h
+
+maintainer-clean: distclean
+ $(MAKE) -C test $@
diff --git a/src/interfaces/libpq/README b/src/interfaces/libpq/README
new file mode 100644
index 0000000..0dcef75
--- /dev/null
+++ b/src/interfaces/libpq/README
@@ -0,0 +1,3 @@
+src/interfaces/libpq/README
+
+This directory contains the C version of Libpq, the POSTGRES frontend library.
diff --git a/src/interfaces/libpq/exports.txt b/src/interfaces/libpq/exports.txt
new file mode 100644
index 0000000..bbc1f90
--- /dev/null
+++ b/src/interfaces/libpq/exports.txt
@@ -0,0 +1,181 @@
+# src/interfaces/libpq/exports.txt
+# Functions to be exported by libpq DLLs
+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
+PQencryptPasswordConn 172
+PQresultMemorySize 173
+PQhostaddr 174
+PQgssEncInUse 175
+PQgetgssctx 176
+PQsetSSLKeyPassHook_OpenSSL 177
+PQgetSSLKeyPassHook_OpenSSL 178
+PQdefaultSSLKeyPassHook_OpenSSL 179
diff --git a/src/interfaces/libpq/fe-auth-scram.c b/src/interfaces/libpq/fe-auth-scram.c
new file mode 100644
index 0000000..6d266e9
--- /dev/null
+++ b/src/interfaces/libpq/fe-auth-scram.c
@@ -0,0 +1,860 @@
+/*-------------------------------------------------------------------------
+ *
+ * fe-auth-scram.c
+ * The front-end (client) implementation of SCRAM authentication.
+ *
+ * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * src/interfaces/libpq/fe-auth-scram.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres_fe.h"
+
+#include "common/base64.h"
+#include "common/saslprep.h"
+#include "common/scram-common.h"
+#include "fe-auth.h"
+
+
+/*
+ * Status of exchange messages used for SCRAM authentication via the
+ * SASL protocol.
+ */
+typedef enum
+{
+ FE_SCRAM_INIT,
+ FE_SCRAM_NONCE_SENT,
+ FE_SCRAM_PROOF_SENT,
+ FE_SCRAM_FINISHED
+} fe_scram_state_enum;
+
+typedef struct
+{
+ fe_scram_state_enum state;
+
+ /* These are supplied by the user */
+ PGconn *conn;
+ char *password;
+ char *sasl_mechanism;
+
+ /* We construct these */
+ uint8 SaltedPassword[SCRAM_KEY_LEN];
+ char *client_nonce;
+ char *client_first_message_bare;
+ char *client_final_message_without_proof;
+
+ /* These come from the server-first message */
+ char *server_first_message;
+ char *salt;
+ int saltlen;
+ int iterations;
+ char *nonce;
+
+ /* These come from the server-final message */
+ char *server_final_message;
+ char ServerSignature[SCRAM_KEY_LEN];
+} fe_scram_state;
+
+static bool read_server_first_message(fe_scram_state *state, char *input);
+static bool read_server_final_message(fe_scram_state *state, char *input);
+static char *build_client_first_message(fe_scram_state *state);
+static char *build_client_final_message(fe_scram_state *state);
+static bool verify_server_signature(fe_scram_state *state);
+static void calculate_client_proof(fe_scram_state *state,
+ const char *client_final_message_without_proof,
+ uint8 *result);
+
+/*
+ * Initialize SCRAM exchange status.
+ */
+void *
+pg_fe_scram_init(PGconn *conn,
+ const char *password,
+ const char *sasl_mechanism)
+{
+ fe_scram_state *state;
+ char *prep_password;
+ pg_saslprep_rc rc;
+
+ Assert(sasl_mechanism != NULL);
+
+ state = (fe_scram_state *) malloc(sizeof(fe_scram_state));
+ if (!state)
+ return NULL;
+ memset(state, 0, sizeof(fe_scram_state));
+ state->conn = conn;
+ state->state = FE_SCRAM_INIT;
+ state->sasl_mechanism = strdup(sasl_mechanism);
+
+ if (!state->sasl_mechanism)
+ {
+ free(state);
+ return NULL;
+ }
+
+ /* Normalize the password with SASLprep, if possible */
+ rc = pg_saslprep(password, &prep_password);
+ if (rc == SASLPREP_OOM)
+ {
+ free(state->sasl_mechanism);
+ free(state);
+ return NULL;
+ }
+ if (rc != SASLPREP_SUCCESS)
+ {
+ prep_password = strdup(password);
+ if (!prep_password)
+ {
+ free(state->sasl_mechanism);
+ free(state);
+ return NULL;
+ }
+ }
+ state->password = prep_password;
+
+ return state;
+}
+
+/*
+ * Return true if channel binding was employed and the SCRAM exchange
+ * completed. This should be used after a successful exchange to determine
+ * whether the server authenticated itself to the client.
+ *
+ * Note that the caller must also ensure that the exchange was actually
+ * successful.
+ */
+bool
+pg_fe_scram_channel_bound(void *opaq)
+{
+ fe_scram_state *state = (fe_scram_state *) opaq;
+
+ /* no SCRAM exchange done */
+ if (state == NULL)
+ return false;
+
+ /* SCRAM exchange not completed */
+ if (state->state != FE_SCRAM_FINISHED)
+ return false;
+
+ /* channel binding mechanism not used */
+ if (strcmp(state->sasl_mechanism, SCRAM_SHA_256_PLUS_NAME) != 0)
+ return false;
+
+ /* all clear! */
+ return true;
+}
+
+/*
+ * Free SCRAM exchange status
+ */
+void
+pg_fe_scram_free(void *opaq)
+{
+ fe_scram_state *state = (fe_scram_state *) opaq;
+
+ if (state->password)
+ free(state->password);
+ if (state->sasl_mechanism)
+ free(state->sasl_mechanism);
+
+ /* client messages */
+ if (state->client_nonce)
+ free(state->client_nonce);
+ if (state->client_first_message_bare)
+ free(state->client_first_message_bare);
+ if (state->client_final_message_without_proof)
+ free(state->client_final_message_without_proof);
+
+ /* first message from server */
+ if (state->server_first_message)
+ free(state->server_first_message);
+ if (state->salt)
+ free(state->salt);
+ if (state->nonce)
+ free(state->nonce);
+
+ /* final message from server */
+ if (state->server_final_message)
+ free(state->server_final_message);
+
+ free(state);
+}
+
+/*
+ * Exchange a SCRAM message with backend.
+ */
+void
+pg_fe_scram_exchange(void *opaq, char *input, int inputlen,
+ char **output, int *outputlen,
+ bool *done, bool *success)
+{
+ fe_scram_state *state = (fe_scram_state *) opaq;
+ PGconn *conn = state->conn;
+
+ *done = false;
+ *success = false;
+ *output = NULL;
+ *outputlen = 0;
+
+ /*
+ * Check that the input length agrees with the string length of the input.
+ * We can ignore inputlen after this.
+ */
+ if (state->state != FE_SCRAM_INIT)
+ {
+ if (inputlen == 0)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("malformed SCRAM message (empty message)\n"));
+ goto error;
+ }
+ if (inputlen != strlen(input))
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("malformed SCRAM message (length mismatch)\n"));
+ goto error;
+ }
+ }
+
+ switch (state->state)
+ {
+ case FE_SCRAM_INIT:
+ /* Begin the SCRAM handshake, by sending client nonce */
+ *output = build_client_first_message(state);
+ if (*output == NULL)
+ goto error;
+
+ *outputlen = strlen(*output);
+ *done = false;
+ state->state = FE_SCRAM_NONCE_SENT;
+ break;
+
+ case FE_SCRAM_NONCE_SENT:
+ /* Receive salt and server nonce, send response. */
+ if (!read_server_first_message(state, input))
+ goto error;
+
+ *output = build_client_final_message(state);
+ if (*output == NULL)
+ goto error;
+
+ *outputlen = strlen(*output);
+ *done = false;
+ state->state = FE_SCRAM_PROOF_SENT;
+ break;
+
+ case FE_SCRAM_PROOF_SENT:
+ /* Receive server signature */
+ if (!read_server_final_message(state, input))
+ goto error;
+
+ /*
+ * Verify server signature, to make sure we're talking to the
+ * genuine server.
+ */
+ if (verify_server_signature(state))
+ *success = true;
+ else
+ {
+ *success = false;
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("incorrect server signature\n"));
+ }
+ *done = true;
+ state->state = FE_SCRAM_FINISHED;
+ break;
+
+ default:
+ /* shouldn't happen */
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("invalid SCRAM exchange state\n"));
+ goto error;
+ }
+ return;
+
+error:
+ *done = true;
+ *success = false;
+}
+
+/*
+ * Read value for an attribute part of a SCRAM message.
+ */
+static char *
+read_attr_value(char **input, char attr, PQExpBuffer errorMessage)
+{
+ char *begin = *input;
+ char *end;
+
+ if (*begin != attr)
+ {
+ printfPQExpBuffer(errorMessage,
+ libpq_gettext("malformed SCRAM message (attribute \"%c\" expected)\n"),
+ attr);
+ return NULL;
+ }
+ begin++;
+
+ if (*begin != '=')
+ {
+ printfPQExpBuffer(errorMessage,
+ libpq_gettext("malformed SCRAM message (expected character \"=\" for attribute \"%c\")\n"),
+ attr);
+ return NULL;
+ }
+ begin++;
+
+ end = begin;
+ while (*end && *end != ',')
+ end++;
+
+ if (*end)
+ {
+ *end = '\0';
+ *input = end + 1;
+ }
+ else
+ *input = end;
+
+ return begin;
+}
+
+/*
+ * Build the first exchange message sent by the client.
+ */
+static char *
+build_client_first_message(fe_scram_state *state)
+{
+ PGconn *conn = state->conn;
+ char raw_nonce[SCRAM_RAW_NONCE_LEN + 1];
+ char *result;
+ int channel_info_len;
+ int encoded_len;
+ PQExpBufferData buf;
+
+ /*
+ * Generate a "raw" nonce. This is converted to ASCII-printable form by
+ * base64-encoding it.
+ */
+ if (!pg_strong_random(raw_nonce, SCRAM_RAW_NONCE_LEN))
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("could not generate nonce\n"));
+ return NULL;
+ }
+
+ encoded_len = pg_b64_enc_len(SCRAM_RAW_NONCE_LEN);
+ /* don't forget the zero-terminator */
+ state->client_nonce = malloc(encoded_len + 1);
+ if (state->client_nonce == NULL)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("out of memory\n"));
+ return NULL;
+ }
+ encoded_len = pg_b64_encode(raw_nonce, SCRAM_RAW_NONCE_LEN,
+ state->client_nonce, encoded_len);
+ if (encoded_len < 0)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("could not encode nonce\n"));
+ return NULL;
+ }
+ state->client_nonce[encoded_len] = '\0';
+
+ /*
+ * Generate message. The username is left empty as the backend uses the
+ * value provided by the startup packet. Also, as this username is not
+ * prepared with SASLprep, the message parsing would fail if it includes
+ * '=' or ',' characters.
+ */
+
+ initPQExpBuffer(&buf);
+
+ /*
+ * First build the gs2-header with channel binding information.
+ */
+ if (strcmp(state->sasl_mechanism, SCRAM_SHA_256_PLUS_NAME) == 0)
+ {
+ Assert(conn->ssl_in_use);
+ appendPQExpBufferStr(&buf, "p=tls-server-end-point");
+ }
+#ifdef HAVE_PGTLS_GET_PEER_CERTIFICATE_HASH
+ else if (conn->channel_binding[0] != 'd' && /* disable */
+ conn->ssl_in_use)
+ {
+ /*
+ * Client supports channel binding, but thinks the server does not.
+ */
+ appendPQExpBufferChar(&buf, 'y');
+ }
+#endif
+ else
+ {
+ /*
+ * Client does not support channel binding, or has disabled it.
+ */
+ appendPQExpBufferChar(&buf, 'n');
+ }
+
+ if (PQExpBufferDataBroken(buf))
+ goto oom_error;
+
+ channel_info_len = buf.len;
+
+ appendPQExpBuffer(&buf, ",,n=,r=%s", state->client_nonce);
+ if (PQExpBufferDataBroken(buf))
+ goto oom_error;
+
+ /*
+ * The first message content needs to be saved without channel binding
+ * information.
+ */
+ state->client_first_message_bare = strdup(buf.data + channel_info_len + 2);
+ if (!state->client_first_message_bare)
+ goto oom_error;
+
+ result = strdup(buf.data);
+ if (result == NULL)
+ goto oom_error;
+
+ termPQExpBuffer(&buf);
+ return result;
+
+oom_error:
+ termPQExpBuffer(&buf);
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("out of memory\n"));
+ return NULL;
+}
+
+/*
+ * Build the final exchange message sent from the client.
+ */
+static char *
+build_client_final_message(fe_scram_state *state)
+{
+ PQExpBufferData buf;
+ PGconn *conn = state->conn;
+ uint8 client_proof[SCRAM_KEY_LEN];
+ char *result;
+ int encoded_len;
+
+ initPQExpBuffer(&buf);
+
+ /*
+ * Construct client-final-message-without-proof. We need to remember it
+ * for verifying the server proof in the final step of authentication.
+ *
+ * The channel binding flag handling (p/y/n) must be consistent with
+ * build_client_first_message(), because the server will check that it's
+ * the same flag both times.
+ */
+ if (strcmp(state->sasl_mechanism, SCRAM_SHA_256_PLUS_NAME) == 0)
+ {
+#ifdef HAVE_PGTLS_GET_PEER_CERTIFICATE_HASH
+ char *cbind_data = NULL;
+ size_t cbind_data_len = 0;
+ size_t cbind_header_len;
+ char *cbind_input;
+ size_t cbind_input_len;
+ int encoded_cbind_len;
+
+ /* Fetch hash data of server's SSL certificate */
+ cbind_data =
+ pgtls_get_peer_certificate_hash(state->conn,
+ &cbind_data_len);
+ if (cbind_data == NULL)
+ {
+ /* error message is already set on error */
+ termPQExpBuffer(&buf);
+ return NULL;
+ }
+
+ appendPQExpBufferStr(&buf, "c=");
+
+ /* p=type,, */
+ cbind_header_len = strlen("p=tls-server-end-point,,");
+ cbind_input_len = cbind_header_len + cbind_data_len;
+ cbind_input = malloc(cbind_input_len);
+ if (!cbind_input)
+ {
+ free(cbind_data);
+ goto oom_error;
+ }
+ memcpy(cbind_input, "p=tls-server-end-point,,", cbind_header_len);
+ memcpy(cbind_input + cbind_header_len, cbind_data, cbind_data_len);
+
+ encoded_cbind_len = pg_b64_enc_len(cbind_input_len);
+ if (!enlargePQExpBuffer(&buf, encoded_cbind_len))
+ {
+ free(cbind_data);
+ free(cbind_input);
+ goto oom_error;
+ }
+ encoded_cbind_len = pg_b64_encode(cbind_input, cbind_input_len,
+ buf.data + buf.len,
+ encoded_cbind_len);
+ if (encoded_cbind_len < 0)
+ {
+ free(cbind_data);
+ free(cbind_input);
+ termPQExpBuffer(&buf);
+ printfPQExpBuffer(&conn->errorMessage,
+ "could not encode cbind data for channel binding\n");
+ return NULL;
+ }
+ buf.len += encoded_cbind_len;
+ buf.data[buf.len] = '\0';
+
+ free(cbind_data);
+ free(cbind_input);
+#else
+ /*
+ * Chose channel binding, but the SSL library doesn't support it.
+ * Shouldn't happen.
+ */
+ termPQExpBuffer(&buf);
+ printfPQExpBuffer(&conn->errorMessage,
+ "channel binding not supported by this build\n");
+ return NULL;
+#endif /* HAVE_PGTLS_GET_PEER_CERTIFICATE_HASH */
+ }
+#ifdef HAVE_PGTLS_GET_PEER_CERTIFICATE_HASH
+ else if (conn->channel_binding[0] != 'd' && /* disable */
+ conn->ssl_in_use)
+ appendPQExpBufferStr(&buf, "c=eSws"); /* base64 of "y,," */
+#endif
+ else
+ appendPQExpBufferStr(&buf, "c=biws"); /* base64 of "n,," */
+
+ if (PQExpBufferDataBroken(buf))
+ goto oom_error;
+
+ appendPQExpBuffer(&buf, ",r=%s", state->nonce);
+ if (PQExpBufferDataBroken(buf))
+ goto oom_error;
+
+ state->client_final_message_without_proof = strdup(buf.data);
+ if (state->client_final_message_without_proof == NULL)
+ goto oom_error;
+
+ /* Append proof to it, to form client-final-message. */
+ calculate_client_proof(state,
+ state->client_final_message_without_proof,
+ client_proof);
+
+ appendPQExpBufferStr(&buf, ",p=");
+ encoded_len = pg_b64_enc_len(SCRAM_KEY_LEN);
+ if (!enlargePQExpBuffer(&buf, encoded_len))
+ goto oom_error;
+ encoded_len = pg_b64_encode((char *) client_proof,
+ SCRAM_KEY_LEN,
+ buf.data + buf.len,
+ encoded_len);
+ if (encoded_len < 0)
+ {
+ termPQExpBuffer(&buf);
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("could not encode client proof\n"));
+ return NULL;
+ }
+ buf.len += encoded_len;
+ buf.data[buf.len] = '\0';
+
+ result = strdup(buf.data);
+ if (result == NULL)
+ goto oom_error;
+
+ termPQExpBuffer(&buf);
+ return result;
+
+oom_error:
+ termPQExpBuffer(&buf);
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("out of memory\n"));
+ return NULL;
+}
+
+/*
+ * Read the first exchange message coming from the server.
+ */
+static bool
+read_server_first_message(fe_scram_state *state, char *input)
+{
+ PGconn *conn = state->conn;
+ char *iterations_str;
+ char *endptr;
+ char *encoded_salt;
+ char *nonce;
+ int decoded_salt_len;
+
+ state->server_first_message = strdup(input);
+ if (state->server_first_message == NULL)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("out of memory\n"));
+ return false;
+ }
+
+ /* parse the message */
+ nonce = read_attr_value(&input, 'r',
+ &conn->errorMessage);
+ if (nonce == NULL)
+ {
+ /* read_attr_value() has generated an error string */
+ return false;
+ }
+
+ /* Verify immediately that the server used our part of the nonce */
+ if (strlen(nonce) < strlen(state->client_nonce) ||
+ memcmp(nonce, state->client_nonce, strlen(state->client_nonce)) != 0)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("invalid SCRAM response (nonce mismatch)\n"));
+ return false;
+ }
+
+ state->nonce = strdup(nonce);
+ if (state->nonce == NULL)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("out of memory\n"));
+ return false;
+ }
+
+ encoded_salt = read_attr_value(&input, 's', &conn->errorMessage);
+ if (encoded_salt == NULL)
+ {
+ /* read_attr_value() has generated an error string */
+ return false;
+ }
+ decoded_salt_len = pg_b64_dec_len(strlen(encoded_salt));
+ state->salt = malloc(decoded_salt_len);
+ if (state->salt == NULL)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("out of memory\n"));
+ return false;
+ }
+ state->saltlen = pg_b64_decode(encoded_salt,
+ strlen(encoded_salt),
+ state->salt,
+ decoded_salt_len);
+ if (state->saltlen < 0)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("malformed SCRAM message (invalid salt)\n"));
+ return false;
+ }
+
+ iterations_str = read_attr_value(&input, 'i', &conn->errorMessage);
+ if (iterations_str == NULL)
+ {
+ /* read_attr_value() has generated an error string */
+ return false;
+ }
+ state->iterations = strtol(iterations_str, &endptr, 10);
+ if (*endptr != '\0' || state->iterations < 1)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("malformed SCRAM message (invalid iteration count)\n"));
+ return false;
+ }
+
+ if (*input != '\0')
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("malformed SCRAM message (garbage at end of server-first-message)\n"));
+
+ return true;
+}
+
+/*
+ * Read the final exchange message coming from the server.
+ */
+static bool
+read_server_final_message(fe_scram_state *state, char *input)
+{
+ PGconn *conn = state->conn;
+ char *encoded_server_signature;
+ char *decoded_server_signature;
+ int server_signature_len;
+
+ state->server_final_message = strdup(input);
+ if (!state->server_final_message)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("out of memory\n"));
+ return false;
+ }
+
+ /* Check for error result. */
+ if (*input == 'e')
+ {
+ char *errmsg = read_attr_value(&input, 'e',
+ &conn->errorMessage);
+
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("error received from server in SCRAM exchange: %s\n"),
+ errmsg);
+ return false;
+ }
+
+ /* Parse the message. */
+ encoded_server_signature = read_attr_value(&input, 'v',
+ &conn->errorMessage);
+ if (encoded_server_signature == NULL)
+ {
+ /* read_attr_value() has generated an error message */
+ return false;
+ }
+
+ if (*input != '\0')
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("malformed SCRAM message (garbage at end of server-final-message)\n"));
+
+ server_signature_len = pg_b64_dec_len(strlen(encoded_server_signature));
+ decoded_server_signature = malloc(server_signature_len);
+ if (!decoded_server_signature)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("out of memory\n"));
+ return false;
+ }
+
+ server_signature_len = pg_b64_decode(encoded_server_signature,
+ strlen(encoded_server_signature),
+ decoded_server_signature,
+ server_signature_len);
+ if (server_signature_len != SCRAM_KEY_LEN)
+ {
+ free(decoded_server_signature);
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("malformed SCRAM message (invalid server signature)\n"));
+ return false;
+ }
+ memcpy(state->ServerSignature, decoded_server_signature, SCRAM_KEY_LEN);
+ free(decoded_server_signature);
+
+ return true;
+}
+
+/*
+ * Calculate the client proof, part of the final exchange message sent
+ * by the client.
+ */
+static void
+calculate_client_proof(fe_scram_state *state,
+ const char *client_final_message_without_proof,
+ uint8 *result)
+{
+ uint8 StoredKey[SCRAM_KEY_LEN];
+ uint8 ClientKey[SCRAM_KEY_LEN];
+ uint8 ClientSignature[SCRAM_KEY_LEN];
+ int i;
+ scram_HMAC_ctx ctx;
+
+ /*
+ * Calculate SaltedPassword, and store it in 'state' so that we can reuse
+ * it later in verify_server_signature.
+ */
+ scram_SaltedPassword(state->password, state->salt, state->saltlen,
+ state->iterations, state->SaltedPassword);
+
+ scram_ClientKey(state->SaltedPassword, ClientKey);
+ scram_H(ClientKey, SCRAM_KEY_LEN, StoredKey);
+
+ scram_HMAC_init(&ctx, StoredKey, SCRAM_KEY_LEN);
+ scram_HMAC_update(&ctx,
+ state->client_first_message_bare,
+ strlen(state->client_first_message_bare));
+ scram_HMAC_update(&ctx, ",", 1);
+ scram_HMAC_update(&ctx,
+ state->server_first_message,
+ strlen(state->server_first_message));
+ scram_HMAC_update(&ctx, ",", 1);
+ scram_HMAC_update(&ctx,
+ client_final_message_without_proof,
+ strlen(client_final_message_without_proof));
+ scram_HMAC_final(ClientSignature, &ctx);
+
+ for (i = 0; i < SCRAM_KEY_LEN; i++)
+ result[i] = ClientKey[i] ^ ClientSignature[i];
+}
+
+/*
+ * Validate the server signature, received as part of the final exchange
+ * message received from the server.
+ */
+static bool
+verify_server_signature(fe_scram_state *state)
+{
+ uint8 expected_ServerSignature[SCRAM_KEY_LEN];
+ uint8 ServerKey[SCRAM_KEY_LEN];
+ scram_HMAC_ctx ctx;
+
+ scram_ServerKey(state->SaltedPassword, ServerKey);
+
+ /* calculate ServerSignature */
+ scram_HMAC_init(&ctx, ServerKey, SCRAM_KEY_LEN);
+ scram_HMAC_update(&ctx,
+ state->client_first_message_bare,
+ strlen(state->client_first_message_bare));
+ scram_HMAC_update(&ctx, ",", 1);
+ scram_HMAC_update(&ctx,
+ state->server_first_message,
+ strlen(state->server_first_message));
+ scram_HMAC_update(&ctx, ",", 1);
+ scram_HMAC_update(&ctx,
+ state->client_final_message_without_proof,
+ strlen(state->client_final_message_without_proof));
+ scram_HMAC_final(expected_ServerSignature, &ctx);
+
+ if (memcmp(expected_ServerSignature, state->ServerSignature, SCRAM_KEY_LEN) != 0)
+ return false;
+
+ return true;
+}
+
+/*
+ * Build a new SCRAM secret.
+ */
+char *
+pg_fe_scram_build_secret(const char *password)
+{
+ char *prep_password;
+ pg_saslprep_rc rc;
+ char saltbuf[SCRAM_DEFAULT_SALT_LEN];
+ char *result;
+
+ /*
+ * Normalize the password with SASLprep. If that doesn't work, because
+ * the password isn't valid UTF-8 or contains prohibited characters, just
+ * proceed with the original password. (See comments at top of file.)
+ */
+ rc = pg_saslprep(password, &prep_password);
+ if (rc == SASLPREP_OOM)
+ return NULL;
+ if (rc == SASLPREP_SUCCESS)
+ password = (const char *) prep_password;
+
+ /* Generate a random salt */
+ if (!pg_strong_random(saltbuf, SCRAM_DEFAULT_SALT_LEN))
+ {
+ if (prep_password)
+ free(prep_password);
+ return NULL;
+ }
+
+ result = scram_build_secret(saltbuf, SCRAM_DEFAULT_SALT_LEN,
+ SCRAM_DEFAULT_ITERATIONS, password);
+
+ if (prep_password)
+ free(prep_password);
+
+ return result;
+}
diff --git a/src/interfaces/libpq/fe-auth.c b/src/interfaces/libpq/fe-auth.c
new file mode 100644
index 0000000..9f5403d
--- /dev/null
+++ b/src/interfaces/libpq/fe-auth.c
@@ -0,0 +1,1280 @@
+/*-------------------------------------------------------------------------
+ *
+ * fe-auth.c
+ * The front-end (client) authorization routines
+ *
+ * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * src/interfaces/libpq/fe-auth.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+/*
+ * INTERFACE ROUTINES
+ * frontend (client) routines:
+ * pg_fe_sendauth send authentication information
+ * pg_fe_getauthname get user's name according to the client side
+ * of the authentication system
+ */
+
+#include "postgres_fe.h"
+
+#ifdef WIN32
+#include "win32.h"
+#else
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/param.h> /* for MAXHOSTNAMELEN on most */
+#include <sys/socket.h>
+#ifdef HAVE_SYS_UCRED_H
+#include <sys/ucred.h>
+#endif
+#ifndef MAXHOSTNAMELEN
+#include <netdb.h> /* for MAXHOSTNAMELEN on some */
+#endif
+#include <pwd.h>
+#endif
+
+#include "common/md5.h"
+#include "common/scram-common.h"
+#include "fe-auth.h"
+#include "libpq-fe.h"
+
+#ifdef ENABLE_GSS
+/*
+ * GSSAPI authentication system.
+ */
+
+#include "fe-gssapi-common.h"
+
+/*
+ * Continue GSS authentication with next token as needed.
+ */
+static int
+pg_GSS_continue(PGconn *conn, int payloadlen)
+{
+ OM_uint32 maj_stat,
+ min_stat,
+ lmin_s;
+ gss_buffer_desc ginbuf;
+ gss_buffer_desc goutbuf;
+
+ /*
+ * On first call, there's no input token. On subsequent calls, read the
+ * input token into a GSS buffer.
+ */
+ if (conn->gctx != GSS_C_NO_CONTEXT)
+ {
+ ginbuf.length = payloadlen;
+ ginbuf.value = malloc(payloadlen);
+ if (!ginbuf.value)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("out of memory allocating GSSAPI buffer (%d)\n"),
+ payloadlen);
+ return STATUS_ERROR;
+ }
+ if (pqGetnchar(ginbuf.value, payloadlen, conn))
+ {
+ /*
+ * Shouldn't happen, because the caller should've ensured that the
+ * whole message is already in the input buffer.
+ */
+ free(ginbuf.value);
+ return STATUS_ERROR;
+ }
+ }
+ else
+ {
+ ginbuf.length = 0;
+ ginbuf.value = NULL;
+ }
+
+ 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,
+ (ginbuf.value == NULL) ? GSS_C_NO_BUFFER : &ginbuf,
+ NULL,
+ &goutbuf,
+ NULL,
+ NULL);
+
+ if (ginbuf.value)
+ free(ginbuf.value);
+
+ if (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',
+ goutbuf.value, goutbuf.length) != STATUS_OK)
+ {
+ gss_release_buffer(&lmin_s, &goutbuf);
+ return STATUS_ERROR;
+ }
+ }
+ gss_release_buffer(&lmin_s, &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, int payloadlen)
+{
+ int ret;
+ char *host = conn->connhost[conn->whichhost].host;
+
+ if (!(host && host[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;
+ }
+
+ ret = pg_GSS_load_servicename(conn);
+ if (ret != STATUS_OK)
+ return ret;
+
+ /*
+ * Initial packet is the same as a continuation packet with no initial
+ * context.
+ */
+ conn->gctx = GSS_C_NO_CONTEXT;
+
+ return pg_GSS_continue(conn, payloadlen);
+}
+#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, int payloadlen)
+{
+ SECURITY_STATUS r;
+ CtxtHandle newContext;
+ ULONG contextAttr;
+ SecBufferDesc inbuf;
+ SecBufferDesc outbuf;
+ SecBuffer OutBuffers[1];
+ SecBuffer InBuffers[1];
+ char *inputbuf = NULL;
+
+ if (conn->sspictx != NULL)
+ {
+ /*
+ * On runs other than the first we have some data to send. Put this
+ * data in a SecBuffer type structure.
+ */
+ inputbuf = malloc(payloadlen);
+ if (!inputbuf)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("out of memory allocating SSPI buffer (%d)\n"),
+ payloadlen);
+ return STATUS_ERROR;
+ }
+ if (pqGetnchar(inputbuf, payloadlen, conn))
+ {
+ /*
+ * Shouldn't happen, because the caller should've ensured that the
+ * whole message is already in the input buffer.
+ */
+ free(inputbuf);
+ return STATUS_ERROR;
+ }
+
+ inbuf.ulVersion = SECBUFFER_VERSION;
+ inbuf.cBuffers = 1;
+ inbuf.pBuffers = InBuffers;
+ InBuffers[0].pvBuffer = inputbuf;
+ InBuffers[0].cbBuffer = payloadlen;
+ 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);
+
+ /* we don't need the input anymore */
+ if (inputbuf)
+ free(inputbuf);
+
+ 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));
+ }
+
+ /*
+ * 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, int payloadlen)
+{
+ SECURITY_STATUS r;
+ TimeStamp expire;
+ char *host = conn->connhost[conn->whichhost].host;
+
+ 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 (!(host && host[0] != '\0'))
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("host name must be specified\n"));
+ return STATUS_ERROR;
+ }
+ conn->sspitarget = malloc(strlen(conn->krbsrvname) + strlen(host) + 2);
+ if (!conn->sspitarget)
+ {
+ printfPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory\n"));
+ return STATUS_ERROR;
+ }
+ sprintf(conn->sspitarget, "%s/%s", conn->krbsrvname, host);
+
+ /*
+ * 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, payloadlen);
+}
+#endif /* ENABLE_SSPI */
+
+/*
+ * Initialize SASL authentication exchange.
+ */
+static int
+pg_SASL_init(PGconn *conn, int payloadlen)
+{
+ char *initialresponse = NULL;
+ int initialresponselen;
+ bool done;
+ bool success;
+ const char *selected_mechanism;
+ PQExpBufferData mechanism_buf;
+ char *password;
+
+ initPQExpBuffer(&mechanism_buf);
+
+ if (conn->channel_binding[0] == 'r' && /* require */
+ !conn->ssl_in_use)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("channel binding required, but SSL not in use\n"));
+ goto error;
+ }
+
+ if (conn->sasl_state)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("duplicate SASL authentication request\n"));
+ goto error;
+ }
+
+ /*
+ * Parse the list of SASL authentication mechanisms in the
+ * AuthenticationSASL message, and select the best mechanism that we
+ * support. SCRAM-SHA-256-PLUS and SCRAM-SHA-256 are the only ones
+ * supported at the moment, listed by order of decreasing importance.
+ */
+ selected_mechanism = NULL;
+ for (;;)
+ {
+ if (pqGets(&mechanism_buf, conn))
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ "fe_sendauth: invalid authentication request from server: invalid list of authentication mechanisms\n");
+ goto error;
+ }
+ if (PQExpBufferDataBroken(mechanism_buf))
+ goto oom_error;
+
+ /* An empty string indicates end of list */
+ if (mechanism_buf.data[0] == '\0')
+ break;
+
+ /*
+ * Select the mechanism to use. Pick SCRAM-SHA-256-PLUS over anything
+ * else if a channel binding type is set and if the client supports it
+ * (and did not set channel_binding=disable). Pick SCRAM-SHA-256 if
+ * nothing else has already been picked. If we add more mechanisms, a
+ * more refined priority mechanism might become necessary.
+ */
+ if (strcmp(mechanism_buf.data, SCRAM_SHA_256_PLUS_NAME) == 0)
+ {
+ if (conn->ssl_in_use)
+ {
+ /* The server has offered SCRAM-SHA-256-PLUS. */
+
+#ifdef HAVE_PGTLS_GET_PEER_CERTIFICATE_HASH
+ /*
+ * The client supports channel binding, which is chosen if
+ * channel_binding is not disabled.
+ */
+ if (conn->channel_binding[0] != 'd') /* disable */
+ selected_mechanism = SCRAM_SHA_256_PLUS_NAME;
+#else
+ /*
+ * The client does not support channel binding. If it is
+ * required, complain immediately instead of the error below
+ * which would be confusing as the server is publishing
+ * SCRAM-SHA-256-PLUS.
+ */
+ if (conn->channel_binding[0] == 'r') /* require */
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("channel binding is required, but client does not support it\n"));
+ goto error;
+ }
+#endif
+ }
+ else
+ {
+ /*
+ * The server offered SCRAM-SHA-256-PLUS, but the connection
+ * is not SSL-encrypted. That's not sane. Perhaps SSL was
+ * stripped by a proxy? There's no point in continuing,
+ * because the server will reject the connection anyway if we
+ * try authenticate without channel binding even though both
+ * the client and server supported it. The SCRAM exchange
+ * checks for that, to prevent downgrade attacks.
+ */
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("server offered SCRAM-SHA-256-PLUS authentication over a non-SSL connection\n"));
+ goto error;
+ }
+ }
+ else if (strcmp(mechanism_buf.data, SCRAM_SHA_256_NAME) == 0 &&
+ !selected_mechanism)
+ selected_mechanism = SCRAM_SHA_256_NAME;
+ }
+
+ if (!selected_mechanism)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("none of the server's SASL authentication mechanisms are supported\n"));
+ goto error;
+ }
+
+ if (conn->channel_binding[0] == 'r' && /* require */
+ strcmp(selected_mechanism, SCRAM_SHA_256_PLUS_NAME) != 0)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("channel binding is required, but server did not offer an authentication method that supports channel binding\n"));
+ goto error;
+ }
+
+ /*
+ * Now that the SASL mechanism has been chosen for the exchange,
+ * initialize its state information.
+ */
+
+ /*
+ * First, select the password to use for the exchange, complaining if
+ * there isn't one. Currently, all supported SASL mechanisms require a
+ * password, so we can just go ahead here without further distinction.
+ */
+ conn->password_needed = true;
+ password = conn->connhost[conn->whichhost].password;
+ if (password == NULL)
+ password = conn->pgpass;
+ if (password == NULL || password[0] == '\0')
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ PQnoPasswordSupplied);
+ goto error;
+ }
+
+ /*
+ * Initialize the SASL state information with all the information gathered
+ * during the initial exchange.
+ *
+ * Note: Only tls-unique is supported for the moment.
+ */
+ conn->sasl_state = pg_fe_scram_init(conn,
+ password,
+ selected_mechanism);
+ if (!conn->sasl_state)
+ goto oom_error;
+
+ /* Get the mechanism-specific Initial Client Response, if any */
+ pg_fe_scram_exchange(conn->sasl_state,
+ NULL, -1,
+ &initialresponse, &initialresponselen,
+ &done, &success);
+
+ if (done && !success)
+ goto error;
+
+ /*
+ * Build a SASLInitialResponse message, and send it.
+ */
+ if (pqPutMsgStart('p', true, conn))
+ goto error;
+ if (pqPuts(selected_mechanism, conn))
+ goto error;
+ if (initialresponse)
+ {
+ if (pqPutInt(initialresponselen, 4, conn))
+ goto error;
+ if (pqPutnchar(initialresponse, initialresponselen, conn))
+ goto error;
+ }
+ if (pqPutMsgEnd(conn))
+ goto error;
+ if (pqFlush(conn))
+ goto error;
+
+ termPQExpBuffer(&mechanism_buf);
+ if (initialresponse)
+ free(initialresponse);
+
+ return STATUS_OK;
+
+error:
+ termPQExpBuffer(&mechanism_buf);
+ if (initialresponse)
+ free(initialresponse);
+ return STATUS_ERROR;
+
+oom_error:
+ termPQExpBuffer(&mechanism_buf);
+ if (initialresponse)
+ free(initialresponse);
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("out of memory\n"));
+ return STATUS_ERROR;
+}
+
+/*
+ * Exchange a message for SASL communication protocol with the backend.
+ * This should be used after calling pg_SASL_init to set up the status of
+ * the protocol.
+ */
+static int
+pg_SASL_continue(PGconn *conn, int payloadlen, bool final)
+{
+ char *output;
+ int outputlen;
+ bool done;
+ bool success;
+ int res;
+ char *challenge;
+
+ /* Read the SASL challenge from the AuthenticationSASLContinue message. */
+ challenge = malloc(payloadlen + 1);
+ if (!challenge)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("out of memory allocating SASL buffer (%d)\n"),
+ payloadlen);
+ return STATUS_ERROR;
+ }
+
+ if (pqGetnchar(challenge, payloadlen, conn))
+ {
+ free(challenge);
+ return STATUS_ERROR;
+ }
+ /* For safety and convenience, ensure the buffer is NULL-terminated. */
+ challenge[payloadlen] = '\0';
+
+ pg_fe_scram_exchange(conn->sasl_state,
+ challenge, payloadlen,
+ &output, &outputlen,
+ &done, &success);
+ free(challenge); /* don't need the input anymore */
+
+ if (final && !done)
+ {
+ if (outputlen != 0)
+ free(output);
+
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("AuthenticationSASLFinal received from server, but SASL authentication was not completed\n"));
+ return STATUS_ERROR;
+ }
+ if (outputlen != 0)
+ {
+ /*
+ * Send the SASL response to the server.
+ */
+ res = pqPacketSend(conn, 'p', output, outputlen);
+ free(output);
+
+ if (res != STATUS_OK)
+ return STATUS_ERROR;
+ }
+
+ if (done && !success)
+ return STATUS_ERROR;
+
+ return STATUS_OK;
+}
+
+/*
+ * 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[PG_STRERROR_R_BUFLEN];
+
+ printfPQExpBuffer(&conn->errorMessage,
+ "pg_local_sendauth: sendmsg: %s\n",
+ strerror_r(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;
+ char md5Salt[4];
+
+ /* Read the salt from the AuthenticationMD5Password message. */
+ if (areq == AUTH_REQ_MD5)
+ {
+ if (pqGetnchar(md5Salt, 4, conn))
+ return STATUS_ERROR; /* shouldn't happen */
+ }
+
+ /* 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"), md5Salt,
+ 4, 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;
+}
+
+/*
+ * Verify that the authentication request is expected, given the connection
+ * parameters. This is especially important when the client wishes to
+ * authenticate the server before any sensitive information is exchanged.
+ */
+static bool
+check_expected_areq(AuthRequest areq, PGconn *conn)
+{
+ bool result = true;
+
+ /*
+ * When channel_binding=require, we must protect against two cases: (1) we
+ * must not respond to non-SASL authentication requests, which might leak
+ * information such as the client's password; and (2) even if we receive
+ * AUTH_REQ_OK, we still must ensure that channel binding has happened in
+ * order to authenticate the server.
+ */
+ if (conn->channel_binding[0] == 'r' /* require */ )
+ {
+ switch (areq)
+ {
+ case AUTH_REQ_SASL:
+ case AUTH_REQ_SASL_CONT:
+ case AUTH_REQ_SASL_FIN:
+ break;
+ case AUTH_REQ_OK:
+ if (!pg_fe_scram_channel_bound(conn->sasl_state))
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("channel binding required, but server authenticated client without channel binding\n"));
+ result = false;
+ }
+ break;
+ default:
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("channel binding required but not supported by server's authentication request\n"));
+ result = false;
+ break;
+ }
+ }
+
+ return result;
+}
+
+/*
+ * pg_fe_sendauth
+ * client demux routine for processing an authentication request
+ *
+ * The server has sent us an authentication challenge (or OK). Send an
+ * appropriate response. The caller has ensured that the whole message is
+ * now in the input buffer, and has already read the type and length of
+ * it. We are responsible for reading any remaining extra data, specific
+ * to the authentication method. 'payloadlen' is the remaining length in
+ * the message.
+ */
+int
+pg_fe_sendauth(AuthRequest areq, int payloadlen, PGconn *conn)
+{
+ if (!check_expected_areq(areq, conn))
+ return STATUS_ERROR;
+
+ 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, payloadlen);
+ else
+ r = pg_SSPI_startup(conn, 0, payloadlen);
+#elif defined(ENABLE_GSS) && !defined(ENABLE_SSPI)
+ r = pg_GSS_startup(conn, payloadlen);
+#elif !defined(ENABLE_GSS) && defined(ENABLE_SSPI)
+ r = pg_SSPI_startup(conn, 0, payloadlen);
+#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, payloadlen);
+ else
+ r = pg_GSS_continue(conn, payloadlen);
+#elif defined(ENABLE_GSS) && !defined(ENABLE_SSPI)
+ r = pg_GSS_continue(conn, payloadlen);
+#elif !defined(ENABLE_GSS) && defined(ENABLE_SSPI)
+ r = pg_SSPI_continue(conn, payloadlen);
+#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 its 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, payloadlen) != 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_GSS 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_GSS) */
+#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:
+ {
+ char *password;
+
+ conn->password_needed = true;
+ password = conn->connhost[conn->whichhost].password;
+ if (password == NULL)
+ password = conn->pgpass;
+ if (password == NULL || password[0] == '\0')
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ PQnoPasswordSupplied);
+ return STATUS_ERROR;
+ }
+ if (pg_password_sendauth(conn, password, areq) != STATUS_OK)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ "fe_sendauth: error sending password authentication\n");
+ return STATUS_ERROR;
+ }
+ break;
+ }
+
+ case AUTH_REQ_SASL:
+
+ /*
+ * The request contains the name (as assigned by IANA) of the
+ * authentication mechanism.
+ */
+ if (pg_SASL_init(conn, payloadlen) != STATUS_OK)
+ {
+ /* pg_SASL_init already set the error message */
+ return STATUS_ERROR;
+ }
+ break;
+
+ case AUTH_REQ_SASL_CONT:
+ case AUTH_REQ_SASL_FIN:
+ if (conn->sasl_state == NULL)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ "fe_sendauth: invalid authentication request from server: AUTH_REQ_SASL_CONT without AUTH_REQ_SASL\n");
+ return STATUS_ERROR;
+ }
+ if (pg_SASL_continue(conn, payloadlen,
+ (areq == AUTH_REQ_SASL_FIN)) != STATUS_OK)
+ {
+ /* Use error message, if set already */
+ if (conn->errorMessage.len == 0)
+ printfPQExpBuffer(&conn->errorMessage,
+ "fe_sendauth: error in SASL authentication\n");
+ return STATUS_ERROR;
+ }
+ break;
+
+ case AUTH_REQ_SCM_CREDS:
+ if (pg_local_sendauth(conn) != STATUS_OK)
+ return STATUS_ERROR;
+ break;
+
+ 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,
+ strerror_r(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 with MD5
+ *
+ * This function is equivalent to calling PQencryptPasswordConn with
+ * "md5" as the encryption method, except that this doesn't require
+ * a connection object. This function is deprecated, use
+ * PQencryptPasswordConn instead.
+ */
+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;
+}
+
+/*
+ * PQencryptPasswordConn -- 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 a connection object, the cleartext password, the SQL
+ * name of the user it is for, and a string indicating the algorithm to
+ * use for encrypting the password. If algorithm is NULL, this queries
+ * the server for the current 'password_encryption' value. If you wish
+ * to avoid that, e.g. to avoid blocking, you can execute
+ * 'show password_encryption' yourself before calling this function, and
+ * pass it as the algorithm.
+ *
+ * Return value is a malloc'd string. The client may assume the string
+ * doesn't contain any special characters that would require escaping.
+ * On error, an error message is stored in the connection object, and
+ * returns NULL.
+ */
+char *
+PQencryptPasswordConn(PGconn *conn, const char *passwd, const char *user,
+ const char *algorithm)
+{
+#define MAX_ALGORITHM_NAME_LEN 50
+ char algobuf[MAX_ALGORITHM_NAME_LEN + 1];
+ char *crypt_pwd = NULL;
+
+ if (!conn)
+ return NULL;
+
+ /* If no algorithm was given, ask the server. */
+ if (algorithm == NULL)
+ {
+ PGresult *res;
+ char *val;
+
+ res = PQexec(conn, "show password_encryption");
+ if (res == NULL)
+ {
+ /* PQexec() should've set conn->errorMessage already */
+ return NULL;
+ }
+ if (PQresultStatus(res) != PGRES_TUPLES_OK)
+ {
+ /* PQexec() should've set conn->errorMessage already */
+ PQclear(res);
+ return NULL;
+ }
+ if (PQntuples(res) != 1 || PQnfields(res) != 1)
+ {
+ PQclear(res);
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("unexpected shape of result set returned for SHOW\n"));
+ return NULL;
+ }
+ val = PQgetvalue(res, 0, 0);
+
+ if (strlen(val) > MAX_ALGORITHM_NAME_LEN)
+ {
+ PQclear(res);
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("password_encryption value too long\n"));
+ return NULL;
+ }
+ strcpy(algobuf, val);
+ PQclear(res);
+
+ algorithm = algobuf;
+ }
+
+ /*
+ * Also accept "on" and "off" as aliases for "md5", because
+ * password_encryption was a boolean before PostgreSQL 10. We refuse to
+ * send the password in plaintext even if it was "off".
+ */
+ if (strcmp(algorithm, "on") == 0 ||
+ strcmp(algorithm, "off") == 0)
+ algorithm = "md5";
+
+ /*
+ * Ok, now we know what algorithm to use
+ */
+ if (strcmp(algorithm, "scram-sha-256") == 0)
+ {
+ crypt_pwd = pg_fe_scram_build_secret(passwd);
+ }
+ else if (strcmp(algorithm, "md5") == 0)
+ {
+ crypt_pwd = malloc(MD5_PASSWD_LEN + 1);
+ if (crypt_pwd)
+ {
+ if (!pg_md5_encrypt(passwd, user, strlen(user), crypt_pwd))
+ {
+ free(crypt_pwd);
+ crypt_pwd = NULL;
+ }
+ }
+ }
+ else
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("unrecognized password encryption algorithm \"%s\"\n"),
+ algorithm);
+ return NULL;
+ }
+
+ if (!crypt_pwd)
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("out of memory\n"));
+
+ return crypt_pwd;
+}
diff --git a/src/interfaces/libpq/fe-auth.h b/src/interfaces/libpq/fe-auth.h
new file mode 100644
index 0000000..dc4b15d
--- /dev/null
+++ b/src/interfaces/libpq/fe-auth.h
@@ -0,0 +1,36 @@
+/*-------------------------------------------------------------------------
+ *
+ * fe-auth.h
+ *
+ * Definitions for network authentication routines
+ *
+ * Portions Copyright (c) 1996-2020, 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"
+
+
+/* Prototypes for functions in fe-auth.c */
+extern int pg_fe_sendauth(AuthRequest areq, int payloadlen, PGconn *conn);
+extern char *pg_fe_getauthname(PQExpBuffer errorMessage);
+
+/* Prototypes for functions in fe-auth-scram.c */
+extern void *pg_fe_scram_init(PGconn *conn,
+ const char *password,
+ const char *sasl_mechanism);
+extern bool pg_fe_scram_channel_bound(void *opaq);
+extern void pg_fe_scram_free(void *opaq);
+extern void pg_fe_scram_exchange(void *opaq, char *input, int inputlen,
+ char **output, int *outputlen,
+ bool *done, bool *success);
+extern char *pg_fe_scram_build_secret(const char *password);
+
+#endif /* FE_AUTH_H */
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
new file mode 100644
index 0000000..f80f4e9
--- /dev/null
+++ b/src/interfaces/libpq/fe-connect.c
@@ -0,0 +1,7255 @@
+/*-------------------------------------------------------------------------
+ *
+ * fe-connect.c
+ * functions related to setting up a connection to the backend
+ *
+ * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/interfaces/libpq/fe-connect.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres_fe.h"
+
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "common/ip.h"
+#include "common/link-canary.h"
+#include "common/scram-common.h"
+#include "common/string.h"
+#include "fe-auth.h"
+#include "libpq-fe.h"
+#include "libpq-int.h"
+#include "mb/pg_wchar.h"
+#include "pg_config_paths.h"
+#include "port/pg_bswap.h"
+
+#ifdef WIN32
+#include "win32.h"
+#ifdef _WIN32_IE
+#undef _WIN32_IE
+#endif
+#define _WIN32_IE 0x0500
+#ifdef near
+#undef near
+#endif
+#define near
+#include <shlobj.h>
+#ifdef _MSC_VER /* mstcpip.h is missing on mingw */
+#include <mstcpip.h>
+#endif
+#else
+#include <sys/socket.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#ifdef HAVE_NETINET_TCP_H
+#include <netinet/tcp.h>
+#endif
+#endif
+
+#ifdef ENABLE_THREAD_SAFETY
+#ifdef WIN32
+#include "pthread-win32.h"
+#else
+#include <pthread.h>
+#endif
+#endif
+
+#ifdef USE_LDAP
+#ifdef WIN32
+#include <winldap.h>
+#else
+/* OpenLDAP deprecates RFC 1823, but we want standard conformance */
+#define LDAP_DEPRECATED 1
+#include <ldap.h>
+typedef struct timeval LDAP_TIMEVAL;
+#endif
+static int ldapServiceLookup(const char *purl, PQconninfoOption *options,
+ PQExpBuffer errorMessage);
+#endif
+
+#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 ""
+#ifdef USE_SSL
+#define DefaultChannelBinding "prefer"
+#else
+#define DefaultChannelBinding "disable"
+#endif
+#define DefaultTargetSessionAttrs "any"
+#ifdef USE_SSL
+#define DefaultSSLMode "prefer"
+#else
+#define DefaultSSLMode "disable"
+#endif
+#ifdef ENABLE_GSS
+#include "fe-gssapi-common.h"
+#define DefaultGSSMode "prefer"
+#else
+#define DefaultGSSMode "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)},
+
+ {"passfile", "PGPASSFILE", NULL, NULL,
+ "Database-Password-File", "", 64,
+ offsetof(struct pg_conn, pgpassfile)},
+
+ {"channel_binding", "PGCHANNELBINDING", DefaultChannelBinding, NULL,
+ "Channel-Binding", "", 8, /* sizeof("require") == 8 */
+ offsetof(struct pg_conn, channel_binding)},
+
+ {"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-Options", "", 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)},
+
+ {"tcp_user_timeout", NULL, NULL, NULL,
+ "TCP-User-Timeout", "", 10, /* strlen(INT32_MAX) == 10 */
+ offsetof(struct pg_conn, pgtcp_user_timeout)},
+
+ /*
+ * 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", "0", 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)},
+
+ {"sslpassword", NULL, NULL, NULL,
+ "SSL-Client-Key-Password", "*", 20,
+ offsetof(struct pg_conn, sslpassword)},
+
+ {"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)},
+
+ {"ssl_min_protocol_version", "PGSSLMINPROTOCOLVERSION", "TLSv1.2", NULL,
+ "SSL-Minimum-Protocol-Version", "", 8, /* sizeof("TLSv1.x") == 8 */
+ offsetof(struct pg_conn, ssl_min_protocol_version)},
+
+ {"ssl_max_protocol_version", "PGSSLMAXPROTOCOLVERSION", NULL, NULL,
+ "SSL-Maximum-Protocol-Version", "", 8, /* sizeof("TLSv1.x") == 8 */
+ offsetof(struct pg_conn, ssl_max_protocol_version)},
+
+ /*
+ * As with SSL, all GSS options are exposed even in builds that don't have
+ * support.
+ */
+ {"gssencmode", "PGGSSENCMODE", DefaultGSSMode, NULL,
+ "GSSENC-Mode", "", 8, /* sizeof("disable") == 8 */
+ offsetof(struct pg_conn, gssencmode)},
+
+ /* Kerberos and GSSAPI authentication support specifying the service name */
+ {"krbsrvname", "PGKRBSRVNAME", PG_KRB_SRVNAM, NULL,
+ "Kerberos-service-name", "", 20,
+ offsetof(struct pg_conn, krbsrvname)},
+
+ {"gsslib", "PGGSSLIB", NULL, NULL,
+ "GSS-library", "", 7, /* sizeof("gssapi") == 7 */
+ offsetof(struct pg_conn, gsslib)},
+
+ {"replication", NULL, NULL, NULL,
+ "Replication", "D", 5,
+ offsetof(struct pg_conn, replication)},
+
+ {"target_session_attrs", "PGTARGETSESSIONATTRS",
+ DefaultTargetSessionAttrs, NULL,
+ "Target-Session-Attrs", "", 11, /* sizeof("read-write") = 11 */
+ offsetof(struct pg_conn, target_session_attrs)},
+
+ /* 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 void release_conn_addrinfo(PGconn *conn);
+static void sendTerminateConn(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, const char *token);
+static char *passwordFromFile(const char *hostname, const char *port, const char *dbname,
+ const char *username, const char *pgpassfile);
+static void pgpassfileWarning(PGconn *conn);
+static void default_threadlock(int acquire);
+static bool sslVerifyProtocolVersion(const char *version);
+static bool sslVerifyProtocolRange(const char *min, const char *max);
+
+
+/* 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, nor local state that might still
+ * be useful later.
+ *
+ * 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/encryption state */
+#ifdef ENABLE_GSS
+ {
+ OM_uint32 min_s;
+
+ if (conn->gcred != GSS_C_NO_CREDENTIAL)
+ {
+ gss_release_cred(&min_s, &conn->gcred);
+ conn->gcred = GSS_C_NO_CREDENTIAL;
+ }
+ 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->gss_SendBuffer)
+ {
+ free(conn->gss_SendBuffer);
+ conn->gss_SendBuffer = NULL;
+ }
+ if (conn->gss_RecvBuffer)
+ {
+ free(conn->gss_RecvBuffer);
+ conn->gss_RecvBuffer = NULL;
+ }
+ if (conn->gss_ResultBuffer)
+ {
+ free(conn->gss_ResultBuffer);
+ conn->gss_ResultBuffer = NULL;
+ }
+ conn->gssenc = false;
+ }
+#endif
+#ifdef ENABLE_SSPI
+ 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
+ if (conn->sasl_state)
+ {
+ /*
+ * XXX: if support for more authentication mechanisms is added, this
+ * needs to call the right 'free' function.
+ */
+ pg_fe_scram_free(conn->sasl_state);
+ conn->sasl_state = NULL;
+ }
+}
+
+
+/*
+ * pqDropServerData
+ *
+ * Clear all connection state data that was received from (or deduced about)
+ * the server. This is essential to do between connection attempts to
+ * different servers, else we may incorrectly hold over some data from the
+ * old server.
+ *
+ * It would be better to merge this into pqDropConnection, perhaps, but
+ * right now we cannot because that function is called immediately on
+ * detection of connection loss (cf. pqReadData, for instance). This data
+ * should be kept until we are actually starting a new connection.
+ */
+static void
+pqDropServerData(PGconn *conn)
+{
+ PGnotify *notify;
+ pgParameterStatus *pstatus;
+
+ /* Forget pending notifies */
+ notify = conn->notifyHead;
+ while (notify != NULL)
+ {
+ PGnotify *prev = notify;
+
+ notify = notify->next;
+ free(prev);
+ }
+ conn->notifyHead = conn->notifyTail = NULL;
+
+ /* Reset ParameterStatus data, as well as variables deduced from it */
+ pstatus = conn->pstatus;
+ while (pstatus != NULL)
+ {
+ pgParameterStatus *prev = pstatus;
+
+ pstatus = pstatus->next;
+ free(prev);
+ }
+ conn->pstatus = NULL;
+ conn->client_encoding = PG_SQL_ASCII;
+ conn->std_strings = false;
+ conn->sversion = 0;
+
+ /* Drop large-object lookup data */
+ if (conn->lobjfuncs)
+ free(conn->lobjfuncs);
+ conn->lobjfuncs = NULL;
+
+ /* Reset assorted other per-connection state */
+ conn->last_sqlstate[0] = '\0';
+ conn->auth_req_received = false;
+ conn->password_needed = false;
+ conn->write_failed = false;
+ if (conn->write_err_msg)
+ free(conn->write_err_msg);
+ conn->write_err_msg = NULL;
+ conn->be_pid = 0;
+ conn->be_key = 0;
+}
+
+
+/*
+ * 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;
+}
+
+/*
+ * Count the number of elements in a simple comma-separated list.
+ */
+static int
+count_comma_separated_elems(const char *input)
+{
+ int n;
+
+ n = 1;
+ for (; *input != '\0'; input++)
+ {
+ if (*input == ',')
+ n++;
+ }
+
+ return n;
+}
+
+/*
+ * Parse a simple comma-separated list.
+ *
+ * On each call, returns a malloc'd copy of the next element, and sets *more
+ * to indicate whether there are any more elements in the list after this,
+ * and updates *startptr to point to the next element, if any.
+ *
+ * On out of memory, returns NULL.
+ */
+static char *
+parse_comma_separated_list(char **startptr, bool *more)
+{
+ char *p;
+ char *s = *startptr;
+ char *e;
+ int len;
+
+ /*
+ * Search for the end of the current element; a comma or end-of-string
+ * acts as a terminator.
+ */
+ e = s;
+ while (*e != '\0' && *e != ',')
+ ++e;
+ *more = (*e == ',');
+
+ len = e - s;
+ p = (char *) malloc(sizeof(char) * (len + 1));
+ if (p)
+ {
+ memcpy(p, s, len);
+ p[len] = '\0';
+ }
+ *startptr = e + 1;
+
+ return p;
+}
+
+/*
+ * 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)
+{
+ int i;
+
+ /*
+ * Allocate memory for details about each host to which we might possibly
+ * try to connect. For that, count the number of elements in the hostaddr
+ * or host options. If neither is given, assume one host.
+ */
+ conn->whichhost = 0;
+ if (conn->pghostaddr && conn->pghostaddr[0] != '\0')
+ conn->nconnhost = count_comma_separated_elems(conn->pghostaddr);
+ else if (conn->pghost && conn->pghost[0] != '\0')
+ conn->nconnhost = count_comma_separated_elems(conn->pghost);
+ else
+ conn->nconnhost = 1;
+ conn->connhost = (pg_conn_host *)
+ calloc(conn->nconnhost, sizeof(pg_conn_host));
+ if (conn->connhost == NULL)
+ goto oom_error;
+
+ /*
+ * We now have one pg_conn_host structure per possible host. Fill in the
+ * host and hostaddr fields for each, by splitting the parameter strings.
+ */
+ if (conn->pghostaddr != NULL && conn->pghostaddr[0] != '\0')
+ {
+ char *s = conn->pghostaddr;
+ bool more = true;
+
+ for (i = 0; i < conn->nconnhost && more; i++)
+ {
+ conn->connhost[i].hostaddr = parse_comma_separated_list(&s, &more);
+ if (conn->connhost[i].hostaddr == NULL)
+ goto oom_error;
+ }
+
+ /*
+ * If hostaddr was given, the array was allocated according to the
+ * number of elements in the hostaddr list, so it really should be the
+ * right size.
+ */
+ Assert(!more);
+ Assert(i == conn->nconnhost);
+ }
+
+ if (conn->pghost != NULL && conn->pghost[0] != '\0')
+ {
+ char *s = conn->pghost;
+ bool more = true;
+
+ for (i = 0; i < conn->nconnhost && more; i++)
+ {
+ conn->connhost[i].host = parse_comma_separated_list(&s, &more);
+ if (conn->connhost[i].host == NULL)
+ goto oom_error;
+ }
+
+ /* Check for wrong number of host items. */
+ if (more || i != conn->nconnhost)
+ {
+ conn->status = CONNECTION_BAD;
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("could not match %d host names to %d hostaddr values\n"),
+ count_comma_separated_elems(conn->pghost), conn->nconnhost);
+ return false;
+ }
+ }
+
+ /*
+ * Now, for each host slot, identify the type of address spec, and fill in
+ * the default address if nothing was given.
+ */
+ for (i = 0; i < conn->nconnhost; i++)
+ {
+ pg_conn_host *ch = &conn->connhost[i];
+
+ if (ch->hostaddr != NULL && ch->hostaddr[0] != '\0')
+ ch->type = CHT_HOST_ADDRESS;
+ else if (ch->host != NULL && ch->host[0] != '\0')
+ {
+ ch->type = CHT_HOST_NAME;
+#ifdef HAVE_UNIX_SOCKETS
+ if (is_absolute_path(ch->host))
+ ch->type = CHT_UNIX_SOCKET;
+#endif
+ }
+ else
+ {
+ if (ch->host)
+ free(ch->host);
+#ifdef HAVE_UNIX_SOCKETS
+ if (DEFAULT_PGSOCKET_DIR[0])
+ {
+ ch->host = strdup(DEFAULT_PGSOCKET_DIR);
+ ch->type = CHT_UNIX_SOCKET;
+ }
+ else
+#endif
+ {
+ ch->host = strdup(DefaultHost);
+ ch->type = CHT_HOST_NAME;
+ }
+ if (ch->host == NULL)
+ goto oom_error;
+ }
+ }
+
+ /*
+ * Next, work out the port number corresponding to each host name.
+ *
+ * Note: unlike the above for host names, this could leave the port fields
+ * as null or empty strings. We will substitute DEF_PGPORT whenever we
+ * read such a port field.
+ */
+ if (conn->pgport != NULL && conn->pgport[0] != '\0')
+ {
+ char *s = conn->pgport;
+ bool more = true;
+
+ for (i = 0; i < conn->nconnhost && more; i++)
+ {
+ conn->connhost[i].port = parse_comma_separated_list(&s, &more);
+ if (conn->connhost[i].port == NULL)
+ goto oom_error;
+ }
+
+ /*
+ * If exactly one port was given, use it for every host. Otherwise,
+ * there must be exactly as many ports as there were hosts.
+ */
+ if (i == 1 && !more)
+ {
+ for (i = 1; i < conn->nconnhost; i++)
+ {
+ conn->connhost[i].port = strdup(conn->connhost[0].port);
+ if (conn->connhost[i].port == NULL)
+ goto oom_error;
+ }
+ }
+ else if (more || i != conn->nconnhost)
+ {
+ conn->status = CONNECTION_BAD;
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("could not match %d port numbers to %d hosts\n"),
+ count_comma_separated_elems(conn->pgport), conn->nconnhost);
+ return false;
+ }
+ }
+
+ /*
+ * 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;
+ }
+
+ /*
+ * If password was not given, try to look it up in password file. Note
+ * that the result might be different for each host/port pair.
+ */
+ if (conn->pgpass == NULL || conn->pgpass[0] == '\0')
+ {
+ /* If password file wasn't specified, use ~/PGPASSFILE */
+ if (conn->pgpassfile == NULL || conn->pgpassfile[0] == '\0')
+ {
+ char homedir[MAXPGPATH];
+
+ if (pqGetHomeDirectory(homedir, sizeof(homedir)))
+ {
+ if (conn->pgpassfile)
+ free(conn->pgpassfile);
+ conn->pgpassfile = malloc(MAXPGPATH);
+ if (!conn->pgpassfile)
+ goto oom_error;
+ snprintf(conn->pgpassfile, MAXPGPATH, "%s/%s",
+ homedir, PGPASSFILE);
+ }
+ }
+
+ if (conn->pgpassfile != NULL && conn->pgpassfile[0] != '\0')
+ {
+ for (i = 0; i < conn->nconnhost; i++)
+ {
+ /*
+ * Try to get a password for this host from file. We use host
+ * for the hostname search key if given, else hostaddr (at
+ * least one of them is guaranteed nonempty by now).
+ */
+ const char *pwhost = conn->connhost[i].host;
+
+ if (pwhost == NULL || pwhost[0] == '\0')
+ pwhost = conn->connhost[i].hostaddr;
+
+ conn->connhost[i].password =
+ passwordFromFile(pwhost,
+ conn->connhost[i].port,
+ conn->dbName,
+ conn->pguser,
+ conn->pgpassfile);
+ }
+ }
+ }
+
+ /*
+ * validate channel_binding option
+ */
+ if (conn->channel_binding)
+ {
+ if (strcmp(conn->channel_binding, "disable") != 0
+ && strcmp(conn->channel_binding, "prefer") != 0
+ && strcmp(conn->channel_binding, "require") != 0)
+ {
+ conn->status = CONNECTION_BAD;
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("invalid channel_binding value: \"%s\"\n"),
+ conn->channel_binding);
+ return false;
+ }
+ }
+ else
+ {
+ conn->channel_binding = strdup(DefaultChannelBinding);
+ if (!conn->channel_binding)
+ goto oom_error;
+ }
+
+ /*
+ * 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;
+ }
+
+ /*
+ * Validate TLS protocol versions for ssl_min_protocol_version and
+ * ssl_max_protocol_version.
+ */
+ if (!sslVerifyProtocolVersion(conn->ssl_min_protocol_version))
+ {
+ conn->status = CONNECTION_BAD;
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("invalid ssl_min_protocol_version value: \"%s\"\n"),
+ conn->ssl_min_protocol_version);
+ return false;
+ }
+ if (!sslVerifyProtocolVersion(conn->ssl_max_protocol_version))
+ {
+ conn->status = CONNECTION_BAD;
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("invalid ssl_max_protocol_version value: \"%s\"\n"),
+ conn->ssl_max_protocol_version);
+ return false;
+ }
+
+ /*
+ * Check if the range of SSL protocols defined is correct. This is done
+ * at this early step because this is independent of the SSL
+ * implementation used, and this avoids unnecessary cycles with an
+ * already-built SSL context when the connection is being established, as
+ * it would be doomed anyway.
+ */
+ if (!sslVerifyProtocolRange(conn->ssl_min_protocol_version,
+ conn->ssl_max_protocol_version))
+ {
+ conn->status = CONNECTION_BAD;
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("invalid SSL protocol version range\n"));
+ return false;
+ }
+
+ /*
+ * validate gssencmode option
+ */
+ if (conn->gssencmode)
+ {
+ if (strcmp(conn->gssencmode, "disable") != 0 &&
+ strcmp(conn->gssencmode, "prefer") != 0 &&
+ strcmp(conn->gssencmode, "require") != 0)
+ {
+ conn->status = CONNECTION_BAD;
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("invalid gssencmode value: \"%s\"\n"),
+ conn->gssencmode);
+ return false;
+ }
+#ifndef ENABLE_GSS
+ if (strcmp(conn->gssencmode, "require") == 0)
+ {
+ conn->status = CONNECTION_BAD;
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("gssencmode value \"%s\" invalid when GSSAPI support is not compiled in\n"),
+ conn->gssencmode);
+ return false;
+ }
+#endif
+ }
+ else
+ {
+ conn->gssencmode = strdup(DefaultGSSMode);
+ if (!conn->gssencmode)
+ 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;
+ }
+
+ /*
+ * Validate target_session_attrs option.
+ */
+ if (conn->target_session_attrs)
+ {
+ if (strcmp(conn->target_session_attrs, "any") != 0
+ && strcmp(conn->target_session_attrs, "read-write") != 0)
+ {
+ conn->status = CONNECTION_BAD;
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("invalid target_session_attrs value: \"%s\"\n"),
+ conn->target_session_attrs);
+ return false;
+ }
+ }
+
+ /*
+ * 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[PG_STRERROR_R_BUFLEN];
+
+ 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;
+}
+
+/* ----------
+ * Write currently connected IP address into host_addr (of len host_addr_len).
+ * If unable to, set it to the empty string.
+ * ----------
+ */
+static void
+getHostaddr(PGconn *conn, char *host_addr, int host_addr_len)
+{
+ struct sockaddr_storage *addr = &conn->raddr.addr;
+
+ if (addr->ss_family == AF_INET)
+ {
+ if (pg_inet_net_ntop(AF_INET,
+ &((struct sockaddr_in *) addr)->sin_addr.s_addr,
+ 32,
+ host_addr, host_addr_len) == NULL)
+ host_addr[0] = '\0';
+ }
+#ifdef HAVE_IPV6
+ else if (addr->ss_family == AF_INET6)
+ {
+ if (pg_inet_net_ntop(AF_INET6,
+ &((struct sockaddr_in6 *) addr)->sin6_addr.s6_addr,
+ 128,
+ host_addr, host_addr_len) == NULL)
+ host_addr[0] = '\0';
+ }
+#endif
+ else
+ host_addr[0] = '\0';
+}
+
+/* ----------
+ * connectFailureMessage -
+ * create a friendly error message on connection failure.
+ * ----------
+ */
+static void
+connectFailureMessage(PGconn *conn, int errorno)
+{
+ char sebuf[PG_STRERROR_R_BUFLEN];
+
+#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;
+ const char *displayed_port;
+
+ /*
+ * Optionally display the network address with the hostname. This is
+ * useful to distinguish between IPv4 and IPv6 connections.
+ */
+ getHostaddr(conn, host_addr, NI_MAXHOST);
+
+ /* To which host and port were we actually connecting? */
+ if (conn->connhost[conn->whichhost].type == CHT_HOST_ADDRESS)
+ displayed_host = conn->connhost[conn->whichhost].hostaddr;
+ else
+ displayed_host = conn->connhost[conn->whichhost].host;
+ displayed_port = conn->connhost[conn->whichhost].port;
+ if (displayed_port == NULL || displayed_port[0] == '\0')
+ displayed_port = DEF_PGPORT_STR;
+
+ /*
+ * 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->connhost[conn->whichhost].type != CHT_HOST_ADDRESS &&
+ strlen(host_addr) > 0 &&
+ strcmp(displayed_host, 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,
+ displayed_port);
+ 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,
+ displayed_port);
+ }
+}
+
+/*
+ * 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;
+}
+
+/*
+ * Parse and try to interpret "value" as an integer value, and if successful,
+ * store it in *result, complaining if there is any trailing garbage or an
+ * overflow. This allows any number of leading and trailing whitespaces.
+ */
+static bool
+parse_int_param(const char *value, int *result, PGconn *conn,
+ const char *context)
+{
+ char *end;
+ long numval;
+
+ Assert(value != NULL);
+
+ *result = 0;
+
+ /* strtol(3) skips leading whitespaces */
+ errno = 0;
+ numval = strtol(value, &end, 10);
+
+ /*
+ * If no progress was done during the parsing or an error happened, fail.
+ * This tests properly for overflows of the result.
+ */
+ if (value == end || errno != 0 || numval != (int) numval)
+ goto error;
+
+ /*
+ * Skip any trailing whitespace; if anything but whitespace remains before
+ * the terminating character, fail
+ */
+ while (*end != '\0' && isspace((unsigned char) *end))
+ end++;
+
+ if (*end != '\0')
+ goto error;
+
+ *result = numval;
+ return true;
+
+error:
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("invalid integer value \"%s\" for connection option \"%s\"\n"),
+ value, context);
+ return false;
+}
+
+#ifndef WIN32
+/*
+ * Set the keepalive idle timer.
+ */
+static int
+setKeepalivesIdle(PGconn *conn)
+{
+ int idle;
+
+ if (conn->keepalives_idle == NULL)
+ return 1;
+
+ if (!parse_int_param(conn->keepalives_idle, &idle, conn,
+ "keepalives_idle"))
+ return 0;
+ 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[PG_STRERROR_R_BUFLEN];
+
+ 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;
+
+ if (!parse_int_param(conn->keepalives_interval, &interval, conn,
+ "keepalives_interval"))
+ return 0;
+ if (interval < 0)
+ interval = 0;
+
+#ifdef TCP_KEEPINTVL
+ if (setsockopt(conn->sock, IPPROTO_TCP, TCP_KEEPINTVL,
+ (char *) &interval, sizeof(interval)) < 0)
+ {
+ char sebuf[PG_STRERROR_R_BUFLEN];
+
+ 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;
+
+ if (!parse_int_param(conn->keepalives_count, &count, conn,
+ "keepalives_count"))
+ return 0;
+ if (count < 0)
+ count = 0;
+
+#ifdef TCP_KEEPCNT
+ if (setsockopt(conn->sock, IPPROTO_TCP, TCP_KEEPCNT,
+ (char *) &count, sizeof(count)) < 0)
+ {
+ char sebuf[PG_STRERROR_R_BUFLEN];
+
+ 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 &&
+ !parse_int_param(conn->keepalives_idle, &idle, conn,
+ "keepalives_idle"))
+ return 0;
+ if (idle <= 0)
+ idle = 2 * 60 * 60; /* 2 hours = default */
+
+ if (conn->keepalives_interval &&
+ !parse_int_param(conn->keepalives_interval, &interval, conn,
+ "keepalives_interval"))
+ return 0;
+ 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 */
+
+/*
+ * Set the TCP user timeout.
+ */
+static int
+setTCPUserTimeout(PGconn *conn)
+{
+ int timeout;
+
+ if (conn->pgtcp_user_timeout == NULL)
+ return 1;
+
+ if (!parse_int_param(conn->pgtcp_user_timeout, &timeout, conn,
+ "tcp_user_timeout"))
+ return 0;
+
+ if (timeout < 0)
+ timeout = 0;
+
+#ifdef TCP_USER_TIMEOUT
+ if (setsockopt(conn->sock, IPPROTO_TCP, TCP_USER_TIMEOUT,
+ (char *) &timeout, sizeof(timeout)) < 0)
+ {
+ char sebuf[256];
+
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("setsockopt(%s) failed: %s\n"),
+ "TCP_USER_TIMEOUT",
+ SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
+ return 0;
+ }
+#endif
+
+ return 1;
+}
+
+/* ----------
+ * connectDBStart -
+ * Begin the process of making a connection to the backend.
+ *
+ * Returns 1 if successful, 0 if not.
+ * ----------
+ */
+static int
+connectDBStart(PGconn *conn)
+{
+ if (!conn)
+ return 0;
+
+ if (!conn->options_valid)
+ goto connect_errReturn;
+
+ /*
+ * Check for bad linking to backend-internal versions of src/common
+ * functions (see comments in link-canary.c for the reason we need this).
+ * Nobody but developers should see this message, so we don't bother
+ * translating it.
+ */
+ if (!pg_link_canary_is_frontend())
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ "libpq is incorrectly linked to backend functions\n");
+ goto connect_errReturn;
+ }
+
+ /* Ensure our buffers are empty */
+ conn->inStart = conn->inCursor = conn->inEnd = 0;
+ conn->outCount = 0;
+
+ /*
+ * Ensure errorMessage is empty, too. PQconnectPoll will append messages
+ * to it in the process of scanning for a working server. Thus, if we
+ * fail to connect to multiple hosts, the final error message will include
+ * details about each failure.
+ */
+ resetPQExpBuffer(&conn->errorMessage);
+
+ /*
+ * Set up to try to connect to the first host. (Setting whichhost = -1 is
+ * a bit of a cheat, but PQconnectPoll will advance it to 0 before
+ * anything else looks at it.)
+ */
+ conn->whichhost = -1;
+ conn->try_next_addr = false;
+ conn->try_next_host = 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:
+
+ /*
+ * If we managed to open a socket, close it immediately rather than
+ * waiting till PQfinish. (The application cannot have gotten the socket
+ * from PQsocket yet, so this doesn't risk breaking anything.)
+ */
+ 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);
+ int timeout = 0;
+ int last_whichhost = -2; /* certainly different from whichhost */
+ struct addrinfo *last_addr_cur = NULL;
+
+ 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)
+ {
+ if (!parse_int_param(conn->connect_timeout, &timeout, conn,
+ "connect_timeout"))
+ {
+ /* mark the connection as bad to report the parsing failure */
+ conn->status = CONNECTION_BAD;
+ return 0;
+ }
+
+ if (timeout > 0)
+ {
+ /*
+ * Rounding could cause connection to fail unexpectedly quickly;
+ * to prevent possibly waiting hardly-at-all, insist on at least
+ * two seconds.
+ */
+ if (timeout < 2)
+ timeout = 2;
+ }
+ else /* negative means 0 */
+ timeout = 0;
+ }
+
+ for (;;)
+ {
+ int ret = 0;
+
+ /*
+ * (Re)start the connect_timeout timer if it's active and we are
+ * considering a different host than we were last time through. If
+ * we've already succeeded, though, needn't recalculate.
+ */
+ if (flag != PGRES_POLLING_OK &&
+ timeout > 0 &&
+ (conn->whichhost != last_whichhost ||
+ conn->addr_cur != last_addr_cur))
+ {
+ finish_time = time(NULL) + timeout;
+ last_whichhost = conn->whichhost;
+ last_addr_cur = conn->addr_cur;
+ }
+
+ /*
+ * 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:
+ ret = pqWaitTimed(1, 0, conn, finish_time);
+ if (ret == -1)
+ {
+ /* hard failure, eg select() problem, aborts everything */
+ conn->status = CONNECTION_BAD;
+ return 0;
+ }
+ break;
+
+ case PGRES_POLLING_WRITING:
+ ret = pqWaitTimed(0, 1, conn, finish_time);
+ if (ret == -1)
+ {
+ /* hard failure, eg select() problem, aborts everything */
+ conn->status = CONNECTION_BAD;
+ return 0;
+ }
+ break;
+
+ default:
+ /* Just in case we failed to set it in PQconnectPoll */
+ conn->status = CONNECTION_BAD;
+ return 0;
+ }
+
+ if (ret == 1) /* connect_timeout elapsed */
+ {
+ /*
+ * Give up on current server/address, try the next one.
+ */
+ conn->try_next_addr = true;
+ conn->status = CONNECTION_NEEDED;
+ }
+
+ /*
+ * Now try to advance the state machine.
+ */
+ flag = PQconnectPoll(conn);
+ }
+}
+
+/*
+ * This subroutine saves conn->errorMessage, which will be restored back by
+ * restoreErrorMessage subroutine. Returns false on OOM failure.
+ */
+static bool
+saveErrorMessage(PGconn *conn, PQExpBuffer savedMessage)
+{
+ initPQExpBuffer(savedMessage);
+ appendPQExpBufferStr(savedMessage,
+ conn->errorMessage.data);
+ if (PQExpBufferBroken(savedMessage))
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("out of memory\n"));
+ return false;
+ }
+ /* Clear whatever is in errorMessage now */
+ resetPQExpBuffer(&conn->errorMessage);
+ return true;
+}
+
+/*
+ * Restores saved error messages back to conn->errorMessage, prepending them
+ * to whatever is in conn->errorMessage already. (This does the right thing
+ * if anything's been added to conn->errorMessage since saveErrorMessage.)
+ */
+static void
+restoreErrorMessage(PGconn *conn, PQExpBuffer savedMessage)
+{
+ appendPQExpBufferStr(savedMessage, conn->errorMessage.data);
+ resetPQExpBuffer(&conn->errorMessage);
+ appendPQExpBufferStr(&conn->errorMessage, savedMessage->data);
+ /* If any step above hit OOM, just report that */
+ if (PQExpBufferBroken(savedMessage) ||
+ PQExpBufferBroken(&conn->errorMessage))
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("out of memory\n"));
+ termPQExpBuffer(savedMessage);
+}
+
+/* ----------------
+ * 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)
+{
+ bool reset_connection_state_machine = false;
+ bool need_new_connection = false;
+ PGresult *res;
+ char sebuf[PG_STRERROR_R_BUFLEN];
+ int optval;
+ PQExpBufferData savedMessage;
+
+ 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:
+ case CONNECTION_CHECK_WRITABLE:
+ case CONNECTION_CONSUME:
+ case CONNECTION_GSS_STARTUP:
+ 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. */
+
+ /* Time to advance to next address, or next host if no more addresses? */
+ if (conn->try_next_addr)
+ {
+ if (conn->addr_cur && conn->addr_cur->ai_next)
+ {
+ conn->addr_cur = conn->addr_cur->ai_next;
+ reset_connection_state_machine = true;
+ }
+ else
+ conn->try_next_host = true;
+ conn->try_next_addr = false;
+ }
+
+ /* Time to advance to next connhost[] entry? */
+ if (conn->try_next_host)
+ {
+ pg_conn_host *ch;
+ struct addrinfo hint;
+ int thisport;
+ int ret;
+ char portstr[MAXPGPATH];
+
+ if (conn->whichhost + 1 >= conn->nconnhost)
+ {
+ /*
+ * Oops, no more hosts. An appropriate error message is already
+ * set up, so just set the right status.
+ */
+ goto error_return;
+ }
+ conn->whichhost++;
+
+ /* Drop any address info for previous host */
+ release_conn_addrinfo(conn);
+
+ /*
+ * Look up info for the new host. On failure, log the problem in
+ * conn->errorMessage, then loop around to try the next host. (Note
+ * we don't clear try_next_host until we've succeeded.)
+ */
+ ch = &conn->connhost[conn->whichhost];
+
+ /* Initialize hint structure */
+ MemSet(&hint, 0, sizeof(hint));
+ hint.ai_socktype = SOCK_STREAM;
+ conn->addrlist_family = hint.ai_family = AF_UNSPEC;
+
+ /* Figure out the port number we're going to use. */
+ if (ch->port == NULL || ch->port[0] == '\0')
+ thisport = DEF_PGPORT;
+ else
+ {
+ if (!parse_int_param(ch->port, &thisport, conn, "port"))
+ goto error_return;
+
+ if (thisport < 1 || thisport > 65535)
+ {
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("invalid port number: \"%s\"\n"),
+ ch->port);
+ goto keep_going;
+ }
+ }
+ snprintf(portstr, sizeof(portstr), "%d", thisport);
+
+ /* Use pg_getaddrinfo_all() to resolve the address */
+ switch (ch->type)
+ {
+ case CHT_HOST_NAME:
+ ret = pg_getaddrinfo_all(ch->host, portstr, &hint,
+ &conn->addrlist);
+ if (ret || !conn->addrlist)
+ {
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("could not translate host name \"%s\" to address: %s\n"),
+ ch->host, gai_strerror(ret));
+ goto keep_going;
+ }
+ break;
+
+ case CHT_HOST_ADDRESS:
+ hint.ai_flags = AI_NUMERICHOST;
+ ret = pg_getaddrinfo_all(ch->hostaddr, portstr, &hint,
+ &conn->addrlist);
+ if (ret || !conn->addrlist)
+ {
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("could not parse network address \"%s\": %s\n"),
+ ch->hostaddr, gai_strerror(ret));
+ goto keep_going;
+ }
+ break;
+
+ case CHT_UNIX_SOCKET:
+#ifdef HAVE_UNIX_SOCKETS
+ conn->addrlist_family = hint.ai_family = AF_UNIX;
+ UNIXSOCK_PATH(portstr, thisport, ch->host);
+ 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));
+ goto keep_going;
+ }
+
+ /*
+ * NULL hostname tells pg_getaddrinfo_all to parse the service
+ * name as a Unix-domain socket path.
+ */
+ ret = pg_getaddrinfo_all(NULL, portstr, &hint,
+ &conn->addrlist);
+ if (ret || !conn->addrlist)
+ {
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("could not translate Unix-domain socket path \"%s\" to address: %s\n"),
+ portstr, gai_strerror(ret));
+ goto keep_going;
+ }
+#else
+ Assert(false);
+#endif
+ break;
+ }
+
+ /* OK, scan this addrlist for a working server address */
+ conn->addr_cur = conn->addrlist;
+ reset_connection_state_machine = true;
+ conn->try_next_host = false;
+ }
+
+ /* Reset connection state machine? */
+ if (reset_connection_state_machine)
+ {
+ /*
+ * (Re) initialize our connection control variables for a set of
+ * connection attempts to a single server address. These variables
+ * must persist across individual connection attempts, but we must
+ * reset them when we start to consider a new server.
+ */
+ conn->pversion = PG_PROTOCOL(3, 0);
+ conn->send_appname = true;
+#ifdef USE_SSL
+ /* initialize these values based on SSL mode */
+ conn->allow_ssl_try = (conn->sslmode[0] != 'd'); /* "disable" */
+ conn->wait_ssl_try = (conn->sslmode[0] == 'a'); /* "allow" */
+#endif
+#ifdef ENABLE_GSS
+ conn->try_gss = (conn->gssencmode[0] != 'd'); /* "disable" */
+#endif
+
+ reset_connection_state_machine = false;
+ need_new_connection = true;
+ }
+
+ /* Force a new connection (perhaps to the same server as before)? */
+ if (need_new_connection)
+ {
+ /* Drop any existing connection */
+ pqDropConnection(conn, true);
+
+ /* Reset all state obtained from old server */
+ pqDropServerData(conn);
+
+ /* Drop any PGresult we might have, too */
+ conn->asyncStatus = PGASYNC_IDLE;
+ conn->xactStatus = PQTRANS_IDLE;
+ pqClearAsyncResult(conn);
+
+ /* Reset conn->status to put the state machine in the right state */
+ conn->status = CONNECTION_NEEDED;
+
+ need_new_connection = false;
+ }
+
+ /* Now try to advance the state machine for this connection */
+ 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.
+ *
+ * The extra level of braces here is historical. It's not
+ * worth reindenting this whole switch case to remove 'em.
+ */
+ {
+ struct addrinfo *addr_cur = conn->addr_cur;
+ char host_addr[NI_MAXHOST];
+
+ /*
+ * Advance to next possible host, if we've tried all of
+ * the addresses for the current host.
+ */
+ if (addr_cur == NULL)
+ {
+ conn->try_next_host = true;
+ goto keep_going;
+ }
+
+ /* 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;
+
+ /* set connip */
+ if (conn->connip != NULL)
+ {
+ free(conn->connip);
+ conn->connip = NULL;
+ }
+
+ getHostaddr(conn, host_addr, NI_MAXHOST);
+ if (strlen(host_addr) > 0)
+ conn->connip = strdup(host_addr);
+
+ /*
+ * purposely ignore strdup failure; not a big problem if
+ * it fails anyway.
+ */
+
+ conn->sock = socket(addr_cur->ai_family, SOCK_STREAM, 0);
+ if (conn->sock == PGINVALID_SOCKET)
+ {
+ /*
+ * Silently ignore socket() failure if we have more
+ * addresses to try; this reduces useless chatter in
+ * cases where the address list includes both IPv4 and
+ * IPv6 but kernel only accepts one family.
+ */
+ if (addr_cur->ai_next != NULL ||
+ conn->whichhost + 1 < conn->nconnhost)
+ {
+ conn->try_next_addr = true;
+ goto keep_going;
+ }
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("could not create socket: %s\n"),
+ SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
+ goto error_return;
+ }
+
+ /*
+ * Select socket options: no delay of outgoing data for
+ * TCP sockets, nonblock mode, close-on-exec. Try the
+ * next address if any of this fails.
+ */
+ if (!IS_AF_UNIX(addr_cur->ai_family))
+ {
+ if (!connectNoDelay(conn))
+ {
+ /* error message already created */
+ conn->try_next_addr = true;
+ goto keep_going;
+ }
+ }
+ 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)));
+ conn->try_next_addr = true;
+ goto keep_going;
+ }
+
+#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)));
+ conn->try_next_addr = true;
+ goto keep_going;
+ }
+#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 */
+ else if (!setTCPUserTimeout(conn))
+ err = 1;
+
+ if (err)
+ {
+ conn->try_next_addr = true;
+ goto keep_going;
+ }
+ }
+
+ /*----------
+ * 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. Add the error report to
+ * conn->errorMessage, then try the next address if any.
+ */
+ connectFailureMessage(conn, SOCK_ERRNO);
+ conn->try_next_addr = true;
+ goto keep_going;
+ }
+ }
+
+ 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);
+
+ /*
+ * Try the next address if any, just as in the case where
+ * connect() returned failure immediately.
+ */
+ conn->try_next_addr = true;
+ goto keep_going;
+ }
+
+ /* 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;
+
+ /*
+ * 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))
+ {
+#ifndef WIN32
+ char pwdbuf[BUFSIZ];
+ struct passwd pass_buf;
+ struct passwd *pass;
+ int passerr;
+#endif
+ 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"),
+ strerror_r(errno, sebuf, sizeof(sebuf)));
+ goto error_return;
+ }
+
+#ifndef WIN32
+ 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,
+ strerror_r(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;
+ }
+#else /* WIN32 */
+ /* should have failed with ENOSYS above */
+ Assert(false);
+#endif /* WIN32 */
+ }
+
+ if (IS_AF_UNIX(conn->raddr.addr.ss_family))
+ {
+ /* Don't request SSL or GSSAPI over Unix sockets */
+#ifdef USE_SSL
+ conn->allow_ssl_try = false;
+#endif
+#ifdef ENABLE_GSS
+ conn->try_gss = false;
+#endif
+ }
+
+#ifdef ENABLE_GSS
+
+ /*
+ * If GSSAPI encryption is enabled, then call
+ * pg_GSS_have_cred_cache() which will return true if we can
+ * acquire credentials (and give us a handle to use in
+ * conn->gcred), and then send a packet to the server asking
+ * for GSSAPI Encryption (and skip past SSL negotiation and
+ * regular startup below).
+ */
+ if (conn->try_gss && !conn->gctx)
+ conn->try_gss = pg_GSS_have_cred_cache(&conn->gcred);
+ if (conn->try_gss && !conn->gctx)
+ {
+ ProtocolVersion pv = pg_hton32(NEGOTIATE_GSS_CODE);
+
+ if (pqPacketSend(conn, 0, &pv, sizeof(pv)) != STATUS_OK)
+ {
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("could not send GSSAPI negotiation packet: %s\n"),
+ SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
+ goto error_return;
+ }
+
+ /* Ok, wait for response */
+ conn->status = CONNECTION_GSS_STARTUP;
+ return PGRES_POLLING_READING;
+ }
+ else if (!conn->gctx && conn->gssencmode[0] == 'r')
+ {
+ appendPQExpBufferStr(&conn->errorMessage,
+ libpq_gettext("GSSAPI encryption required but was impossible (possibly no credential cache, no server support, or using a local socket)\n"));
+ goto error_return;
+ }
+#endif
+
+#ifdef USE_SSL
+
+ /*
+ * If SSL is enabled and we haven't already got encryption of
+ * some sort running, request SSL instead of sending the
+ * startup message.
+ */
+ if (conn->allow_ssl_try && !conn->wait_ssl_try &&
+ !conn->ssl_in_use
+#ifdef ENABLE_GSS
+ && !conn->gssenc
+#endif
+ )
+ {
+ 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 = pg_hton32(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;
+ /* We can proceed using this connection */
+ 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;
+ need_new_connection = true;
+ goto keep_going;
+ }
+ /* Else it's a hard failure */
+ goto error_return;
+ }
+ /* Else, return POLLING_READING or POLLING_WRITING status */
+ return pollres;
+#else /* !USE_SSL */
+ /* can't get here */
+ goto error_return;
+#endif /* USE_SSL */
+ }
+
+ case CONNECTION_GSS_STARTUP:
+ {
+#ifdef ENABLE_GSS
+ PostgresPollingStatusType pollres;
+
+ /*
+ * If we haven't yet, get the postmaster's response to our
+ * negotiation packet
+ */
+ if (conn->try_gss && !conn->gctx)
+ {
+ char gss_ok;
+ int rdresult = pqReadData(conn);
+
+ if (rdresult < 0)
+ /* pqReadData fills in error message */
+ goto error_return;
+ else if (rdresult == 0)
+ /* caller failed to wait for data */
+ return PGRES_POLLING_READING;
+ if (pqGetc(&gss_ok, conn) < 0)
+ /* shouldn't happen... */
+ return PGRES_POLLING_READING;
+
+ if (gss_ok == 'E')
+ {
+ /*
+ * Server failure of some sort. Assume it's a
+ * protocol version support failure, and let's see if
+ * we can't recover (if it's not, we'll get a better
+ * error message on retry). Server gets fussy if we
+ * don't hang up the socket, though.
+ */
+ conn->try_gss = false;
+ need_new_connection = true;
+ goto keep_going;
+ }
+
+ /* mark byte consumed */
+ conn->inStart = conn->inCursor;
+
+ if (gss_ok == 'N')
+ {
+ /* Server doesn't want GSSAPI; fall back if we can */
+ if (conn->gssencmode[0] == 'r')
+ {
+ appendPQExpBufferStr(&conn->errorMessage,
+ libpq_gettext("server doesn't support GSSAPI encryption, but it was required\n"));
+ goto error_return;
+ }
+
+ conn->try_gss = false;
+ /* We can proceed using this connection */
+ conn->status = CONNECTION_MADE;
+ return PGRES_POLLING_WRITING;
+ }
+ else if (gss_ok != 'G')
+ {
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("received invalid response to GSSAPI negotiation: %c\n"),
+ gss_ok);
+ goto error_return;
+ }
+ }
+
+ /* Begin or continue GSSAPI negotiation */
+ pollres = pqsecure_open_gss(conn);
+ if (pollres == PGRES_POLLING_OK)
+ {
+ /* All set for startup packet */
+ conn->status = CONNECTION_MADE;
+ return PGRES_POLLING_WRITING;
+ }
+ else if (pollres == PGRES_POLLING_FAILED &&
+ conn->gssencmode[0] == 'p')
+ {
+ /*
+ * We failed, but we can retry on "prefer". Have to drop
+ * the current connection to do so, though.
+ */
+ conn->try_gss = false;
+ need_new_connection = true;
+ goto keep_going;
+ }
+ return pollres;
+#else /* !ENABLE_GSS */
+ /* unreachable */
+ goto error_return;
+#endif /* ENABLE_GSS */
+ }
+
+ /*
+ * Handle authentication exchange: wait for postmaster messages
+ * and respond as necessary.
+ */
+ case CONNECTION_AWAITING_RESPONSE:
+ {
+ char beresp;
+ int msgLength;
+ int avail;
+ AuthRequest areq;
+ int res;
+
+ /*
+ * 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);
+ need_new_connection = true;
+ 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;
+
+ /* Check to see if we should mention pgpassfile */
+ pgpassfileWarning(conn);
+
+#ifdef ENABLE_GSS
+
+ /*
+ * If gssencmode is "prefer" and we're using GSSAPI, retry
+ * without it.
+ */
+ if (conn->gssenc && conn->gssencmode[0] == 'p')
+ {
+ /* only retry once */
+ conn->try_gss = false;
+ need_new_connection = true;
+ goto keep_going;
+ }
+#endif
+
+#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;
+ need_new_connection = true;
+ 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->ssl_in_use
+ && conn->allow_ssl_try /* redundant? */
+ && !conn->wait_ssl_try) /* redundant? */
+ {
+ /* only retry once */
+ conn->allow_ssl_try = false;
+ need_new_connection = true;
+ 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;
+ }
+ msgLength -= 4;
+
+ /*
+ * Ensure the password salt is in the input buffer, if it's an
+ * MD5 request. All the other authentication methods that
+ * contain extra data in the authentication request are only
+ * supported in protocol version 3, in which case we already
+ * read the whole message above.
+ */
+ if (areq == AUTH_REQ_MD5 && PG_PROTOCOL_MAJOR(conn->pversion) < 3)
+ {
+ msgLength += 4;
+
+ avail = conn->inEnd - conn->inCursor;
+ if (avail < 4)
+ {
+ /*
+ * 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) 4,
+ conn))
+ goto error_return;
+ /* We'll come back when there is more data */
+ return PGRES_POLLING_READING;
+ }
+ }
+
+ /*
+ * Process the rest of the authentication request message, and
+ * respond to it if necessary.
+ *
+ * Note that conn->pghost must be non-NULL if we are going to
+ * avoid the Kerberos code doing a hostname look-up.
+ */
+ res = pg_fe_sendauth(areq, msgLength, conn);
+ conn->errorMessage.len = strlen(conn->errorMessage.data);
+
+ /* OK, we have processed the message; mark data consumed */
+ conn->inStart = conn->inCursor;
+
+ if (res != STATUS_OK)
+ goto error_return;
+
+ /*
+ * 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;
+ need_new_connection = true;
+ 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;
+ }
+
+ /* 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;
+ }
+
+ /* Almost there now ... */
+ conn->status = CONNECTION_CHECK_TARGET;
+ goto keep_going;
+ }
+
+ case CONNECTION_CHECK_TARGET:
+ {
+ /*
+ * If a read-write connection is required, see if we have one.
+ *
+ * Servers before 7.4 lack the transaction_read_only GUC, but
+ * by the same token they don't have any read-only mode, so we
+ * may just skip the test in that case.
+ */
+ if (conn->sversion >= 70400 &&
+ conn->target_session_attrs != NULL &&
+ strcmp(conn->target_session_attrs, "read-write") == 0)
+ {
+ /*
+ * Save existing error messages across the PQsendQuery
+ * attempt. This is necessary because PQsendQuery is
+ * going to reset conn->errorMessage, so we would lose
+ * error messages related to previous hosts we have tried
+ * and failed to connect to.
+ */
+ if (!saveErrorMessage(conn, &savedMessage))
+ goto error_return;
+
+ conn->status = CONNECTION_OK;
+ if (!PQsendQuery(conn,
+ "SHOW transaction_read_only"))
+ {
+ restoreErrorMessage(conn, &savedMessage);
+ goto error_return;
+ }
+ conn->status = CONNECTION_CHECK_WRITABLE;
+ restoreErrorMessage(conn, &savedMessage);
+ return PGRES_POLLING_READING;
+ }
+
+ /* We can release the address list now. */
+ release_conn_addrinfo(conn);
+
+ /* 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;
+ }
+
+ /* Almost there now ... */
+ conn->status = CONNECTION_CHECK_TARGET;
+ goto keep_going;
+ }
+
+ case CONNECTION_CONSUME:
+ {
+ conn->status = CONNECTION_OK;
+ if (!PQconsumeInput(conn))
+ goto error_return;
+
+ if (PQisBusy(conn))
+ {
+ conn->status = CONNECTION_CONSUME;
+ return PGRES_POLLING_READING;
+ }
+
+ /*
+ * Call PQgetResult() again to consume NULL result.
+ */
+ res = PQgetResult(conn);
+ if (res != NULL)
+ {
+ PQclear(res);
+ conn->status = CONNECTION_CONSUME;
+ goto keep_going;
+ }
+
+ /* We can release the address list now. */
+ release_conn_addrinfo(conn);
+
+ /* We are open for business! */
+ conn->status = CONNECTION_OK;
+ return PGRES_POLLING_OK;
+ }
+ case CONNECTION_CHECK_WRITABLE:
+ {
+ const char *displayed_host;
+ const char *displayed_port;
+
+ if (!saveErrorMessage(conn, &savedMessage))
+ goto error_return;
+
+ conn->status = CONNECTION_OK;
+ if (!PQconsumeInput(conn))
+ {
+ restoreErrorMessage(conn, &savedMessage);
+ goto error_return;
+ }
+
+ if (PQisBusy(conn))
+ {
+ conn->status = CONNECTION_CHECK_WRITABLE;
+ restoreErrorMessage(conn, &savedMessage);
+ return PGRES_POLLING_READING;
+ }
+
+ res = PQgetResult(conn);
+ if (res && (PQresultStatus(res) == PGRES_TUPLES_OK) &&
+ PQntuples(res) == 1)
+ {
+ char *val;
+
+ val = PQgetvalue(res, 0, 0);
+ if (strncmp(val, "on", 2) == 0)
+ {
+ /* Not writable; fail this connection. */
+ PQclear(res);
+ restoreErrorMessage(conn, &savedMessage);
+
+ /* Append error report to conn->errorMessage. */
+ if (conn->connhost[conn->whichhost].type == CHT_HOST_ADDRESS)
+ displayed_host = conn->connhost[conn->whichhost].hostaddr;
+ else
+ displayed_host = conn->connhost[conn->whichhost].host;
+ displayed_port = conn->connhost[conn->whichhost].port;
+ if (displayed_port == NULL || displayed_port[0] == '\0')
+ displayed_port = DEF_PGPORT_STR;
+
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("could not make a writable "
+ "connection to server "
+ "\"%s:%s\"\n"),
+ displayed_host, displayed_port);
+
+ /* Close connection politely. */
+ conn->status = CONNECTION_OK;
+ sendTerminateConn(conn);
+
+ /*
+ * Try next host if any, but we don't want to consider
+ * additional addresses for this host.
+ */
+ conn->try_next_host = true;
+ goto keep_going;
+ }
+
+ /* Session is read-write, so we're good. */
+ PQclear(res);
+ termPQExpBuffer(&savedMessage);
+
+ /*
+ * Finish reading any remaining messages before being
+ * considered as ready.
+ */
+ conn->status = CONNECTION_CONSUME;
+ goto keep_going;
+ }
+
+ /*
+ * Something went wrong with "SHOW transaction_read_only". We
+ * should try next addresses.
+ */
+ if (res)
+ PQclear(res);
+ restoreErrorMessage(conn, &savedMessage);
+
+ /* Append error report to conn->errorMessage. */
+ if (conn->connhost[conn->whichhost].type == CHT_HOST_ADDRESS)
+ displayed_host = conn->connhost[conn->whichhost].hostaddr;
+ else
+ displayed_host = conn->connhost[conn->whichhost].host;
+ displayed_port = conn->connhost[conn->whichhost].port;
+ if (displayed_port == NULL || displayed_port[0] == '\0')
+ displayed_port = DEF_PGPORT_STR;
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("test \"SHOW transaction_read_only\" failed "
+ "on server \"%s:%s\"\n"),
+ displayed_host, displayed_port);
+
+ /* Close connection politely. */
+ conn->status = CONNECTION_OK;
+ sendTerminateConn(conn);
+
+ /* Try next address */
+ conn->try_next_addr = true;
+ goto keep_going;
+ }
+
+ default:
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("invalid connection state %d, "
+ "probably indicative of memory corruption\n"),
+ conn->status);
+ goto error_return;
+ }
+
+ /* Unreachable */
+
+error_return:
+
+ /*
+ * 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 PQPING_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 in this process.
+ *
+ * Note: the Windows documentation says that we should eventually do a
+ * matching WSACleanup() call, but experience suggests that that is at
+ * least as likely to cause problems as fix them. So we don't.
+ */
+ static bool wsastartup_done = false;
+
+ if (!wsastartup_done)
+ {
+ WSADATA wsaData;
+
+ if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0)
+ return NULL;
+ wsastartup_done = true;
+ }
+
+ /* Forget any earlier error */
+ WSASetLastError(0);
+#endif /* WIN32 */
+
+ conn = (PGconn *) malloc(sizeof(PGconn));
+ if (conn == NULL)
+ 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;
+
+ /*
+ * 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);
+ }
+
+ /* clean up pg_conn_host structures */
+ if (conn->connhost != NULL)
+ {
+ for (i = 0; i < conn->nconnhost; ++i)
+ {
+ if (conn->connhost[i].host != NULL)
+ free(conn->connhost[i].host);
+ if (conn->connhost[i].hostaddr != NULL)
+ free(conn->connhost[i].hostaddr);
+ if (conn->connhost[i].port != NULL)
+ free(conn->connhost[i].port);
+ if (conn->connhost[i].password != NULL)
+ {
+ explicit_bzero(conn->connhost[i].password, strlen(conn->connhost[i].password));
+ free(conn->connhost[i].password);
+ }
+ }
+ free(conn->connhost);
+ }
+
+ 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->pgtty)
+ free(conn->pgtty);
+ if (conn->connect_timeout)
+ free(conn->connect_timeout);
+ if (conn->pgtcp_user_timeout)
+ free(conn->pgtcp_user_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)
+ {
+ explicit_bzero(conn->pgpass, strlen(conn->pgpass));
+ free(conn->pgpass);
+ }
+ if (conn->pgpassfile)
+ free(conn->pgpassfile);
+ if (conn->channel_binding)
+ free(conn->channel_binding);
+ 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->sslpassword)
+ {
+ explicit_bzero(conn->sslpassword, strlen(conn->sslpassword));
+ free(conn->sslpassword);
+ }
+ 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 (conn->ssl_min_protocol_version)
+ free(conn->ssl_min_protocol_version);
+ if (conn->ssl_max_protocol_version)
+ free(conn->ssl_max_protocol_version);
+ if (conn->gssencmode)
+ free(conn->gssencmode);
+ if (conn->krbsrvname)
+ free(conn->krbsrvname);
+ if (conn->gsslib)
+ free(conn->gsslib);
+ if (conn->connip)
+ free(conn->connip);
+ /* Note that conn->Pfdebug is not ours to close or free */
+ if (conn->last_query)
+ free(conn->last_query);
+ if (conn->write_err_msg)
+ free(conn->write_err_msg);
+ if (conn->inBuffer)
+ free(conn->inBuffer);
+ if (conn->outBuffer)
+ free(conn->outBuffer);
+ if (conn->rowBuf)
+ free(conn->rowBuf);
+ if (conn->target_session_attrs)
+ free(conn->target_session_attrs);
+ termPQExpBuffer(&conn->errorMessage);
+ termPQExpBuffer(&conn->workBuffer);
+
+ free(conn);
+}
+
+/*
+ * release_conn_addrinfo
+ * - Free any addrinfo list in the PGconn.
+ */
+static void
+release_conn_addrinfo(PGconn *conn)
+{
+ if (conn->addrlist)
+ {
+ pg_freeaddrinfo_all(conn->addrlist_family, conn->addrlist);
+ conn->addrlist = NULL;
+ conn->addr_cur = NULL; /* for safety */
+ }
+}
+
+/*
+ * sendTerminateConn
+ * - Send a terminate message to backend.
+ */
+static void
+sendTerminateConn(PGconn *conn)
+{
+ /*
+ * 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);
+ }
+}
+
+/*
+ * 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)
+{
+ /*
+ * If possible, send Terminate message to close the connection politely.
+ */
+ sendTerminateConn(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;
+ conn->xactStatus = PQTRANS_IDLE;
+ pqClearAsyncResult(conn); /* deallocate result */
+ resetPQExpBuffer(&conn->errorMessage);
+ release_conn_addrinfo(conn);
+
+ /* Reset all state obtained from server, too */
+ pqDropServerData(conn);
+}
+
+/*
+ * 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[PG_STRERROR_R_BUFLEN];
+ 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 = pg_hton32((uint32) sizeof(crp));
+ crp.cp.cancelRequestCode = (MsgType) pg_hton32(CANCEL_REQUEST_CODE);
+ crp.cp.backendPID = pg_hton32(be_pid);
+ crp.cp.cancelAuthCode = pg_hton32(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
+
+/*
+ * parseServiceInfo: if a service name has been given, look it up and absorb
+ * connection options from it into *options.
+ *
+ * Returns 0 on success, nonzero on failure. On failure, if errorMessage
+ * isn't null, also store an error message there. (Note: the only reason
+ * this function and related ones don't dump core on errorMessage == NULL
+ * is the undocumented fact that printfPQExpBuffer does nothing when passed
+ * a null PQExpBuffer pointer.)
+ */
+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 no service name given, nothing to do */
+ if (service == NULL)
+ return 0;
+
+ /*
+ * Try PGSERVICEFILE if specified, else try ~/.pg_service.conf (if that
+ * exists).
+ */
+ if ((env = getenv("PGSERVICEFILE")) != NULL)
+ strlcpy(serviceFile, env, sizeof(serviceFile));
+ else
+ {
+ char homedir[MAXPGPATH];
+
+ if (!pqGetHomeDirectory(homedir, sizeof(homedir)))
+ goto next_file;
+ snprintf(serviceFile, MAXPGPATH, "%s/%s", homedir, ".pg_service.conf");
+ if (stat(serviceFile, &stat_buf) != 0)
+ 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);
+ if (stat(serviceFile, &stat_buf) != 0)
+ 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)
+ {
+ int len;
+
+ 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 whitespace at end of line, especially the newline */
+ len = strlen(line);
+ while (len > 0 && isspace((unsigned char) line[len - 1]))
+ line[--len] = '\0';
+
+ /* ignore leading whitespace too */
+ while (*line && isspace((unsigned char) line[0]))
+ line++;
+
+ /* ignore comments and empty lines */
+ if (line[0] == '\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. As an extension, we also allow multiple
+ * netloc[:port] specifications, separated by commas:
+ *
+ * postgresql://[user[:password]@][netloc][:port][,...][/dbname][?param1=value1&...]
+ *
+ * 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 = NULL;
+ char *start;
+ char prevchar = '\0';
+ char *user = NULL;
+ char *host = NULL;
+ bool retval = false;
+ PQExpBufferData hostbuf;
+ PQExpBufferData portbuf;
+
+ initPQExpBuffer(&hostbuf);
+ initPQExpBuffer(&portbuf);
+ if (PQExpBufferDataBroken(hostbuf) || PQExpBufferDataBroken(portbuf))
+ {
+ printfPQExpBuffer(errorMessage,
+ libpq_gettext("out of memory\n"));
+ goto cleanup;
+ }
+
+ /* need a modifiable copy of the input URI */
+ buf = strdup(uri);
+ if (buf == NULL)
+ {
+ printfPQExpBuffer(errorMessage,
+ libpq_gettext("out of memory\n"));
+ goto cleanup;
+ }
+ 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;
+ }
+
+ /*
+ * There may be multiple netloc[:port] pairs, each separated from the next
+ * by a comma. When we initially enter this loop, "p" has been
+ * incremented past optional URI credential information at this point and
+ * now points at the "netloc" part of the URI. On subsequent loop
+ * iterations, "p" has been incremented past the comma separator and now
+ * points at the start of the next "netloc".
+ */
+ for (;;)
+ {
+ /*
+ * 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 or a separator comma.
+ */
+ if (*p && *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) or host separator (comma).
+ */
+ while (*p && *p != ':' && *p != '/' && *p != '?' && *p != ',')
+ ++p;
+ }
+
+ /* Save the hostname terminator before we null it */
+ prevchar = *p;
+ *p = '\0';
+
+ appendPQExpBufferStr(&hostbuf, host);
+
+ if (prevchar == ':')
+ {
+ const char *port = ++p; /* advance past host terminator */
+
+ while (*p && *p != '/' && *p != '?' && *p != ',')
+ ++p;
+
+ prevchar = *p;
+ *p = '\0';
+
+ appendPQExpBufferStr(&portbuf, port);
+ }
+
+ if (prevchar != ',')
+ break;
+ ++p; /* advance past comma separator */
+ appendPQExpBufferChar(&hostbuf, ',');
+ appendPQExpBufferChar(&portbuf, ',');
+ }
+
+ /* Save final values for host and port. */
+ if (PQExpBufferDataBroken(hostbuf) || PQExpBufferDataBroken(portbuf))
+ goto cleanup;
+ if (hostbuf.data[0] &&
+ !conninfo_storeval(options, "host", hostbuf.data,
+ errorMessage, false, true))
+ goto cleanup;
+ if (portbuf.data[0] &&
+ !conninfo_storeval(options, "port", portbuf.data,
+ 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:
+ termPQExpBuffer(&hostbuf);
+ termPQExpBuffer(&portbuf);
+ if (buf)
+ 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)
+{
+ char *password = NULL;
+
+ if (!conn)
+ return NULL;
+ if (conn->connhost != NULL)
+ password = conn->connhost[conn->whichhost].password;
+ if (password == NULL)
+ password = conn->pgpass;
+ /* Historically we've returned "" not NULL for no password specified */
+ if (password == NULL)
+ password = "";
+ return password;
+}
+
+char *
+PQhost(const PGconn *conn)
+{
+ if (!conn)
+ return NULL;
+
+ if (conn->connhost != NULL)
+ {
+ /*
+ * Return the verbatim host value provided by user, or hostaddr in its
+ * lack.
+ */
+ if (conn->connhost[conn->whichhost].host != NULL &&
+ conn->connhost[conn->whichhost].host[0] != '\0')
+ return conn->connhost[conn->whichhost].host;
+ else if (conn->connhost[conn->whichhost].hostaddr != NULL &&
+ conn->connhost[conn->whichhost].hostaddr[0] != '\0')
+ return conn->connhost[conn->whichhost].hostaddr;
+ }
+
+ return "";
+}
+
+char *
+PQhostaddr(const PGconn *conn)
+{
+ if (!conn)
+ return NULL;
+
+ /* Return the parsed IP address */
+ if (conn->connhost != NULL && conn->connip != NULL)
+ return conn->connip;
+
+ return "";
+}
+
+char *
+PQport(const PGconn *conn)
+{
+ if (!conn)
+ return NULL;
+
+ if (conn->connhost != NULL)
+ return conn->connhost[conn->whichhost].port;
+
+ return "";
+}
+
+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)
+{
+ char *password;
+
+ if (!conn)
+ return false;
+ password = PQpass(conn);
+ if (conn->password_needed &&
+ (password == NULL || password[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, const char *token)
+{
+ char *tbuf;
+ const char *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(const char *hostname, const char *port, const char *dbname,
+ const char *username, const char *pgpassfile)
+{
+ FILE *fp;
+ struct stat stat_buf;
+ PQExpBufferData buf;
+
+ if (dbname == NULL || dbname[0] == '\0')
+ return NULL;
+
+ if (username == NULL || username[0] == '\0')
+ return NULL;
+
+ /* 'localhost' matches pghost of '' or the default socket directory */
+ if (hostname == NULL || hostname[0] == '\0')
+ 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[0] == '\0')
+ port = DEF_PGPORT_STR;
+
+ /* 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;
+
+ /* Use an expansible buffer to accommodate any reasonable line length */
+ initPQExpBuffer(&buf);
+
+ while (!feof(fp) && !ferror(fp))
+ {
+ /* Make sure there's a reasonable amount of room in the buffer */
+ if (!enlargePQExpBuffer(&buf, 128))
+ break;
+
+ /* Read some data, appending it to what we already have */
+ if (fgets(buf.data + buf.len, buf.maxlen - buf.len, fp) == NULL)
+ break;
+ buf.len += strlen(buf.data + buf.len);
+
+ /* If we don't yet have a whole line, loop around to read more */
+ if (!(buf.len > 0 && buf.data[buf.len - 1] == '\n') && !feof(fp))
+ continue;
+
+ /* ignore comments */
+ if (buf.data[0] != '#')
+ {
+ char *t = buf.data;
+ int len;
+
+ /* strip trailing newline and carriage return */
+ len = pg_strip_crlf(t);
+
+ if (len > 0 &&
+ (t = pwdfMatchesString(t, hostname)) != NULL &&
+ (t = pwdfMatchesString(t, port)) != NULL &&
+ (t = pwdfMatchesString(t, dbname)) != NULL &&
+ (t = pwdfMatchesString(t, username)) != NULL)
+ {
+ /* Found a match. */
+ char *ret,
+ *p1,
+ *p2;
+
+ ret = strdup(t);
+
+ fclose(fp);
+ explicit_bzero(buf.data, buf.maxlen);
+ termPQExpBuffer(&buf);
+
+ 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;
+ }
+ }
+
+ /* No match, reset buffer to prepare for next line. */
+ buf.len = 0;
+ }
+
+ fclose(fp);
+ explicit_bzero(buf.data, buf.maxlen);
+ termPQExpBuffer(&buf);
+ return NULL;
+}
+
+
+/*
+ * If the connection failed due to bad password, we should mention
+ * if we got the password from the pgpassfile.
+ */
+static void
+pgpassfileWarning(PGconn *conn)
+{
+ /* If it was 'invalid authorization', add pgpassfile mention */
+ /* only works with >= 9.0 servers */
+ if (conn->password_needed &&
+ conn->connhost[conn->whichhost].password != NULL &&
+ conn->result)
+ {
+ const char *sqlstate = PQresultErrorField(conn->result,
+ PG_DIAG_SQLSTATE);
+
+ if (sqlstate && strcmp(sqlstate, ERRCODE_INVALID_PASSWORD) == 0)
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("password retrieved from file \"%s\"\n"),
+ conn->pgpassfile);
+ }
+}
+
+/*
+ * Check if the SSL procotol value given in input is valid or not.
+ * This is used as a sanity check routine for the connection parameters
+ * ssl_min_protocol_version and ssl_max_protocol_version.
+ */
+static bool
+sslVerifyProtocolVersion(const char *version)
+{
+ /*
+ * An empty string and a NULL value are considered valid as it is
+ * equivalent to ignoring the parameter.
+ */
+ if (!version || strlen(version) == 0)
+ return true;
+
+ if (pg_strcasecmp(version, "TLSv1") == 0 ||
+ pg_strcasecmp(version, "TLSv1.1") == 0 ||
+ pg_strcasecmp(version, "TLSv1.2") == 0 ||
+ pg_strcasecmp(version, "TLSv1.3") == 0)
+ return true;
+
+ /* anything else is wrong */
+ return false;
+}
+
+
+/*
+ * Ensure that the SSL protocol range given in input is correct. The check
+ * is performed on the input string to keep it TLS backend agnostic. Input
+ * to this function is expected verified with sslVerifyProtocolVersion().
+ */
+static bool
+sslVerifyProtocolRange(const char *min, const char *max)
+{
+ Assert(sslVerifyProtocolVersion(min) &&
+ sslVerifyProtocolVersion(max));
+
+ /* If at least one of the bounds is not set, the range is valid */
+ if (min == NULL || max == NULL || strlen(min) == 0 || strlen(max) == 0)
+ return true;
+
+ /*
+ * If the minimum version is the lowest one we accept, then all options
+ * for the maximum are valid.
+ */
+ if (pg_strcasecmp(min, "TLSv1") == 0)
+ return true;
+
+ /*
+ * The minimum bound is valid, and cannot be TLSv1, so using TLSv1 for the
+ * maximum is incorrect.
+ */
+ if (pg_strcasecmp(max, "TLSv1") == 0)
+ return false;
+
+ /*
+ * At this point we know that we have a mix of TLSv1.1 through 1.3
+ * versions.
+ */
+ if (pg_strcasecmp(min, max) > 0)
+ return false;
+
+ return true;
+}
+
+
+/*
+ * 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).
+ *
+ * Returns true on success, false on failure to obtain the directory name.
+ *
+ * CAUTION: although in most situations failure is unexpected, there are users
+ * who like to run applications in a home-directory-less environment. On
+ * failure, you almost certainly DO NOT want to report an error. Just act as
+ * though whatever file you were hoping to find in the home directory isn't
+ * there (which it isn't).
+ */
+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/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c
new file mode 100644
index 0000000..eea0237
--- /dev/null
+++ b/src/interfaces/libpq/fe-exec.c
@@ -0,0 +1,3860 @@
+/*-------------------------------------------------------------------------
+ *
+ * fe-exec.c
+ * functions related to sending a query down to the backend
+ *
+ * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/interfaces/libpq/fe-exec.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres_fe.h"
+
+#include <ctype.h>
+#include <fcntl.h>
+#include <limits.h>
+
+#ifdef WIN32
+#include "win32.h"
+#else
+#include <unistd.h>
+#endif
+
+#include "libpq-fe.h"
+#include "libpq-int.h"
+#include "mb/pg_wchar.h"
+
+/* 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, size_t *memSize);
+static bool pqAddTuple(PGresult *res, PGresAttValue *tup,
+ const char **errmsgp);
+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;
+ result->memorySize = sizeof(PGresult);
+
+ 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,
+ &result->memorySize);
+ 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,
+ &dest->memorySize);
+ 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.
+ * The total space allocated is added to *memSize.
+ */
+static PGEvent *
+dupEvents(PGEvent *events, int count, size_t *memSize)
+{
+ PGEvent *newEvents;
+ size_t msize;
+ int i;
+
+ if (!events || count <= 0)
+ return NULL;
+
+ msize = count * sizeof(PGEvent);
+ newEvents = (PGEvent *) malloc(msize);
+ 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;
+ }
+ msize += strlen(events[i].name) + 1;
+ }
+
+ *memSize += msize;
+ 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.
+ * (On failure, we report the specific problem via pqInternalNotice.)
+ */
+int
+PQsetvalue(PGresult *res, int tup_num, int field_num, char *value, int len)
+{
+ PGresAttValue *attval;
+ const char *errmsg = NULL;
+
+ /* Note that this check also protects us against null "res" */
+ if (!check_field_number(res, field_num))
+ return false;
+
+ /* Invalid tup_num, must be <= ntups */
+ if (tup_num < 0 || tup_num > res->ntups)
+ {
+ pqInternalNotice(&res->noticeHooks,
+ "row number %d is out of range 0..%d",
+ 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)
+ goto fail;
+
+ /* 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, &errmsg))
+ goto fail;
+ }
+
+ 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)
+ goto fail;
+ attval->len = len;
+ memcpy(attval->value, value, len);
+ attval->value[len] = '\0';
+ }
+
+ return true;
+
+ /*
+ * Report failure via pqInternalNotice. If preceding code didn't provide
+ * an error message, assume "out of memory" was meant.
+ */
+fail:
+ if (!errmsg)
+ errmsg = libpq_gettext("out of memory");
+ pqInternalNotice(&res->noticeHooks, "%s", errmsg);
+
+ return false;
+}
+
+/*
+ * 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)
+ {
+ size_t alloc_size = nBytes + PGRESULT_BLOCK_OVERHEAD;
+
+ block = (PGresult_data *) malloc(alloc_size);
+ if (!block)
+ return NULL;
+ res->memorySize += alloc_size;
+ 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;
+ res->memorySize += PGRESULT_DATA_BLOCKSIZE;
+ 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;
+}
+
+/*
+ * PQresultMemorySize -
+ * Returns total space allocated for the PGresult.
+ */
+size_t
+PQresultMemorySize(const PGresult *res)
+{
+ if (!res)
+ return 0;
+ return res->memorySize;
+}
+
+/*
+ * 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);
+ }
+}
+
+/*
+ * As above, and append conn->write_err_msg to whatever other error we have.
+ * This is used when we've detected a write failure and have exhausted our
+ * chances of reporting something else instead.
+ */
+static void
+pqSaveWriteError(PGconn *conn)
+{
+ /*
+ * Ensure conn->result is an error result, and add anything in
+ * conn->errorMessage to it.
+ */
+ pqSaveErrorResult(conn);
+
+ /*
+ * Now append write_err_msg to that. If it's null because of previous
+ * strdup failure, do what we can. (It's likely our machinations here are
+ * all getting OOM failures as well, but ...)
+ */
+ if (conn->write_err_msg && conn->write_err_msg[0] != '\0')
+ pqCatenateResultError(conn->result, conn->write_err_msg);
+ else
+ pqCatenateResultError(conn->result,
+ libpq_gettext("write to server failed\n"));
+}
+
+/*
+ * 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 an error prevented adding the row
+ *
+ * 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".
+ */
+static bool
+pqAddTuple(PGresult *res, PGresAttValue *tup, const char **errmsgp)
+{
+ 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;
+ PGresAttValue **newTuples;
+
+ /*
+ * Since we use integers for row numbers, we can't support more than
+ * INT_MAX rows. Make sure we allow that many, though.
+ */
+ if (res->tupArrSize <= INT_MAX / 2)
+ newSize = (res->tupArrSize > 0) ? res->tupArrSize * 2 : 128;
+ else if (res->tupArrSize < INT_MAX)
+ newSize = INT_MAX;
+ else
+ {
+ *errmsgp = libpq_gettext("PGresult cannot support more than INT_MAX tuples");
+ return false;
+ }
+
+ /*
+ * Also, on 32-bit platforms we could, in theory, overflow size_t even
+ * before newSize gets to INT_MAX. (In practice we'd doubtless hit
+ * OOM long before that, but let's check.)
+ */
+#if INT_MAX >= (SIZE_MAX / 2)
+ if (newSize > SIZE_MAX / sizeof(PGresAttValue *))
+ {
+ *errmsgp = libpq_gettext("size_t overflow");
+ return false;
+ }
+#endif
+
+ 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->memorySize +=
+ (newSize - res->tupArrSize) * sizeof(PGresAttValue *);
+ 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, errmsgp))
+ 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)
+ {
+ /* error message should be set up already */
+ 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)
+ {
+ /* error message should be set up already */
+ 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:
+ /* error message should be set up already */
+ 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:
+ /* error message should be set up already */
+ return 0;
+}
+
+/*
+ * 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, or if we
+ * had a write failure.
+ */
+ return conn->asyncStatus == PGASYNC_BUSY || conn->write_failed;
+}
+
+
+/*
+ * 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. (Note: if the connection has
+ * been lost, pqWait should return immediately because the socket
+ * should be read-ready, either with the last server data or with an
+ * EOF indication. We expect therefore that this won't result in any
+ * undue delay in reporting a previous write failure.)
+ */
+ 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);
+
+ /*
+ * If we had a write error, but nothing above obtained a query result
+ * or detected a read error, report the write error.
+ */
+ if (conn->write_failed && conn->asyncStatus == PGASYNC_BUSY)
+ {
+ pqSaveWriteError(conn);
+ conn->asyncStatus = PGASYNC_IDLE;
+ return pqPrepareAsyncResult(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:
+ /* error message should be set up already */
+ 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
+ *
+ * Note that this function does not read any new data from the socket;
+ * so usually, caller should call PQconsumeInput() first.
+ */
+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)
+ {
+ /* Oops, no way to do this in 2.0 */
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("function requires at least protocol version 3.0\n"));
+ return -1;
+ }
+ else
+ {
+ /* Send old-style end-of-data marker */
+ if (pqPutMsgStart(0, false, conn) < 0 ||
+ pqPutnchar("\\.\n", 3, conn) < 0 ||
+ pqPutMsgEnd(conn) < 0)
+ return -1;
+ }
+ }
+
+ /* Return to active duty */
+ if (conn->asyncStatus == PGASYNC_COPY_BOTH)
+ conn->asyncStatus = PGASYNC_COPY_OUT;
+ else
+ conn->asyncStatus = PGASYNC_BUSY;
+ resetPQExpBuffer(&conn->errorMessage);
+
+ /* Try to flush data */
+ if (pqFlush(conn) < 0)
+ return -1;
+
+ return 1;
+}
+
+/*
+ * PQgetCopyData - read a row of data from the backend during COPY OUT
+ * or COPY BOTH
+ *
+ * If successful, sets *buffer to point to a malloc'd row of data, and
+ * returns row length (always > 0) as result.
+ * Returns 0 if no row available yet (only possible if async is true),
+ * -1 if end of copy (consult PQgetResult), or -2 if error (consult
+ * PQerrorMessage).
+ */
+int
+PQgetCopyData(PGconn *conn, char **buffer, int async)
+{
+ *buffer = NULL; /* for all failure cases */
+ if (!conn)
+ return -2;
+ if (conn->asyncStatus != PGASYNC_COPY_OUT &&
+ conn->asyncStatus != PGASYNC_COPY_BOTH)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("no COPY in progress\n"));
+ return -2;
+ }
+ if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
+ return pqGetCopyData3(conn, buffer, async);
+ else
+ return pqGetCopyData2(conn, buffer, async);
+}
+
+/*
+ * PQgetline - gets a newline-terminated string from the backend.
+ *
+ * Chiefly here so that applications can use "COPY <rel> to stdout"
+ * and read the output string. Returns a null-terminated string in s.
+ *
+ * XXX this routine is now deprecated, because it can't handle binary data.
+ * If called during a COPY BINARY we return EOF.
+ *
+ * PQgetline reads up to maxlen-1 characters (like fgets(3)) but strips
+ * the terminating \n (like gets(3)).
+ *
+ * CAUTION: the caller is responsible for detecting the end-of-copy signal
+ * (a line containing just "\.") when using this routine.
+ *
+ * RETURNS:
+ * EOF if error (eg, invalid arguments are given)
+ * 0 if EOL is reached (i.e., \n has been read)
+ * (this is required for backward-compatibility -- this
+ * routine used to always return EOF or 0, assuming that
+ * the line ended within maxlen bytes.)
+ * 1 in other cases (i.e., the buffer was filled before \n is reached)
+ */
+int
+PQgetline(PGconn *conn, char *s, int maxlen)
+{
+ if (!s || maxlen <= 0)
+ return EOF;
+ *s = '\0';
+ /* maxlen must be at least 3 to hold the \. terminator! */
+ if (maxlen < 3)
+ return EOF;
+
+ if (!conn)
+ return EOF;
+
+ if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
+ return pqGetline3(conn, s, maxlen);
+ else
+ return pqGetline2(conn, s, maxlen);
+}
+
+/*
+ * PQgetlineAsync - gets a COPY data row without blocking.
+ *
+ * This routine is for applications that want to do "COPY <rel> to stdout"
+ * asynchronously, that is without blocking. Having issued the COPY command
+ * and gotten a PGRES_COPY_OUT response, the app should call PQconsumeInput
+ * and this routine until the end-of-data signal is detected. Unlike
+ * PQgetline, this routine takes responsibility for detecting end-of-data.
+ *
+ * On each call, PQgetlineAsync will return data if a complete data row
+ * is available in libpq's input buffer. Otherwise, no data is returned
+ * until the rest of the row arrives.
+ *
+ * If -1 is returned, the end-of-data signal has been recognized (and removed
+ * from libpq's input buffer). The caller *must* next call PQendcopy and
+ * then return to normal processing.
+ *
+ * RETURNS:
+ * -1 if the end-of-copy-data marker has been recognized
+ * 0 if no data is available
+ * >0 the number of bytes returned.
+ *
+ * The data returned will not extend beyond a data-row boundary. If possible
+ * a whole row will be returned at one time. But if the buffer offered by
+ * the caller is too small to hold a row sent by the backend, then a partial
+ * data row will be returned. In text mode this can be detected by testing
+ * whether the last returned byte is '\n' or not.
+ *
+ * The returned data is *not* null-terminated.
+ */
+
+int
+PQgetlineAsync(PGconn *conn, char *buffer, int bufsize)
+{
+ if (!conn)
+ return -1;
+
+ if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
+ return pqGetlineAsync3(conn, buffer, bufsize);
+ else
+ return pqGetlineAsync2(conn, buffer, bufsize);
+}
+
+/*
+ * PQputline -- sends a string to the backend during COPY IN.
+ * Returns 0 if OK, EOF if not.
+ *
+ * This is deprecated primarily because the return convention doesn't allow
+ * caller to tell the difference between a hard error and a nonblock-mode
+ * send failure.
+ */
+int
+PQputline(PGconn *conn, const char *s)
+{
+ return PQputnbytes(conn, s, strlen(s));
+}
+
+/*
+ * PQputnbytes -- like PQputline, but buffer need not be null-terminated.
+ * Returns 0 if OK, EOF if not.
+ */
+int
+PQputnbytes(PGconn *conn, const char *buffer, int nbytes)
+{
+ if (PQputCopyData(conn, buffer, nbytes) > 0)
+ return 0;
+ else
+ return EOF;
+}
+
+/*
+ * PQendcopy
+ * After completing the data transfer portion of a copy in/out,
+ * the application must call this routine to finish the command protocol.
+ *
+ * When using protocol 3.0 this is deprecated; it's cleaner to use PQgetResult
+ * to get the transfer status. Note however that when using 2.0 protocol,
+ * recovering from a copy failure often requires a PQreset. PQendcopy will
+ * take care of that, PQgetResult won't.
+ *
+ * RETURNS:
+ * 0 on success
+ * 1 on failure
+ */
+int
+PQendcopy(PGconn *conn)
+{
+ if (!conn)
+ return 0;
+
+ if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
+ return pqEndcopy3(conn);
+ else
+ return pqEndcopy2(conn);
+}
+
+
+/* ----------------
+ * PQfn - Send a function call to the POSTGRES backend.
+ *
+ * conn : backend connection
+ * fnid : OID of function to be called
+ * result_buf : pointer to result buffer
+ * result_len : actual length of result is returned here
+ * result_is_int : If the result is an integer, this must be 1,
+ * otherwise this should be 0
+ * args : pointer to an array of function arguments
+ * (each has length, if integer, and value/pointer)
+ * nargs : # of arguments in args array.
+ *
+ * RETURNS
+ * PGresult with status = PGRES_COMMAND_OK if successful.
+ * *result_len is > 0 if there is a return value, 0 if not.
+ * PGresult with status = PGRES_FATAL_ERROR if backend returns an error.
+ * NULL on communications failure. conn->errorMessage will be set.
+ * ----------------
+ */
+
+PGresult *
+PQfn(PGconn *conn,
+ int fnid,
+ int *result_buf,
+ int *result_len,
+ int result_is_int,
+ const PQArgBlock *args,
+ int nargs)
+{
+ *result_len = 0;
+
+ if (!conn)
+ return NULL;
+
+ /* clear the error string */
+ resetPQExpBuffer(&conn->errorMessage);
+
+ if (conn->sock == PGINVALID_SOCKET || conn->asyncStatus != PGASYNC_IDLE ||
+ conn->result != NULL)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("connection in wrong state\n"));
+ return NULL;
+ }
+
+ if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
+ return pqFunctionCall3(conn, fnid,
+ result_buf, result_len,
+ result_is_int,
+ args, nargs);
+ else
+ return pqFunctionCall2(conn, fnid,
+ result_buf, result_len,
+ result_is_int,
+ args, nargs);
+}
+
+
+/* ====== accessor funcs for PGresult ======== */
+
+ExecStatusType
+PQresultStatus(const PGresult *res)
+{
+ if (!res)
+ return PGRES_FATAL_ERROR;
+ return res->resultStatus;
+}
+
+char *
+PQresStatus(ExecStatusType status)
+{
+ if ((unsigned int) status >= sizeof pgresStatus / sizeof pgresStatus[0])
+ return libpq_gettext("invalid ExecStatusType code");
+ return pgresStatus[status];
+}
+
+char *
+PQresultErrorMessage(const PGresult *res)
+{
+ if (!res || !res->errMsg)
+ return "";
+ return res->errMsg;
+}
+
+char *
+PQresultVerboseErrorMessage(const PGresult *res,
+ PGVerbosity verbosity,
+ PGContextVisibility show_context)
+{
+ PQExpBufferData workBuf;
+
+ /*
+ * Because the caller is expected to free the result string, we must
+ * strdup any constant result. We use plain strdup and document that
+ * callers should expect NULL if out-of-memory.
+ */
+ if (!res ||
+ (res->resultStatus != PGRES_FATAL_ERROR &&
+ res->resultStatus != PGRES_NONFATAL_ERROR))
+ return strdup(libpq_gettext("PGresult is not an error result\n"));
+
+ initPQExpBuffer(&workBuf);
+
+ /*
+ * Currently, we pass this off to fe-protocol3.c in all cases; it will
+ * behave reasonably sanely with an error reported by fe-protocol2.c as
+ * well. If necessary, we could record the protocol version in PGresults
+ * so as to be able to invoke a version-specific message formatter, but
+ * for now there's no need.
+ */
+ pqBuildErrorMessage3(&workBuf, res, verbosity, show_context);
+
+ /* If insufficient memory to format the message, fail cleanly */
+ if (PQExpBufferDataBroken(workBuf))
+ {
+ termPQExpBuffer(&workBuf);
+ return strdup(libpq_gettext("out of memory\n"));
+ }
+
+ return workBuf.data;
+}
+
+char *
+PQresultErrorField(const PGresult *res, int fieldcode)
+{
+ PGMessageField *pfield;
+
+ if (!res)
+ return NULL;
+ for (pfield = res->errFields; pfield != NULL; pfield = pfield->next)
+ {
+ if (pfield->code == fieldcode)
+ return pfield->contents;
+ }
+ return NULL;
+}
+
+int
+PQntuples(const PGresult *res)
+{
+ if (!res)
+ return 0;
+ return res->ntups;
+}
+
+int
+PQnfields(const PGresult *res)
+{
+ if (!res)
+ return 0;
+ return res->numAttributes;
+}
+
+int
+PQbinaryTuples(const PGresult *res)
+{
+ if (!res)
+ return 0;
+ return res->binary;
+}
+
+/*
+ * Helper routines to range-check field numbers and tuple numbers.
+ * Return true if OK, false if not
+ */
+
+static int
+check_field_number(const PGresult *res, int field_num)
+{
+ if (!res)
+ return false; /* no way to display error message... */
+ if (field_num < 0 || field_num >= res->numAttributes)
+ {
+ pqInternalNotice(&res->noticeHooks,
+ "column number %d is out of range 0..%d",
+ field_num, res->numAttributes - 1);
+ return false;
+ }
+ return true;
+}
+
+static int
+check_tuple_field_number(const PGresult *res,
+ int tup_num, int field_num)
+{
+ if (!res)
+ return false; /* no way to display error message... */
+ if (tup_num < 0 || tup_num >= res->ntups)
+ {
+ pqInternalNotice(&res->noticeHooks,
+ "row number %d is out of range 0..%d",
+ tup_num, res->ntups - 1);
+ return false;
+ }
+ if (field_num < 0 || field_num >= res->numAttributes)
+ {
+ pqInternalNotice(&res->noticeHooks,
+ "column number %d is out of range 0..%d",
+ field_num, res->numAttributes - 1);
+ return false;
+ }
+ return true;
+}
+
+static int
+check_param_number(const PGresult *res, int param_num)
+{
+ if (!res)
+ return false; /* no way to display error message... */
+ if (param_num < 0 || param_num >= res->numParameters)
+ {
+ pqInternalNotice(&res->noticeHooks,
+ "parameter number %d is out of range 0..%d",
+ param_num, res->numParameters - 1);
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * returns NULL if the field_num is invalid
+ */
+char *
+PQfname(const PGresult *res, int field_num)
+{
+ if (!check_field_number(res, field_num))
+ return NULL;
+ if (res->attDescs)
+ return res->attDescs[field_num].name;
+ else
+ return NULL;
+}
+
+/*
+ * PQfnumber: find column number given column name
+ *
+ * The column name is parsed as if it were in a SQL statement, including
+ * case-folding and double-quote processing. But note a possible gotcha:
+ * downcasing in the frontend might follow different locale rules than
+ * downcasing in the backend...
+ *
+ * Returns -1 if no match. In the present backend it is also possible
+ * to have multiple matches, in which case the first one is found.
+ */
+int
+PQfnumber(const PGresult *res, const char *field_name)
+{
+ char *field_case;
+ bool in_quotes;
+ bool all_lower = true;
+ const char *iptr;
+ char *optr;
+ int i;
+
+ if (!res)
+ return -1;
+
+ /*
+ * Note: it is correct to reject a zero-length input string; the proper
+ * input to match a zero-length field name would be "".
+ */
+ if (field_name == NULL ||
+ field_name[0] == '\0' ||
+ res->attDescs == NULL)
+ return -1;
+
+ /*
+ * Check if we can avoid the strdup() and related work because the
+ * passed-in string wouldn't be changed before we do the check anyway.
+ */
+ for (iptr = field_name; *iptr; iptr++)
+ {
+ char c = *iptr;
+
+ if (c == '"' || c != pg_tolower((unsigned char) c))
+ {
+ all_lower = false;
+ break;
+ }
+ }
+
+ if (all_lower)
+ for (i = 0; i < res->numAttributes; i++)
+ if (strcmp(field_name, res->attDescs[i].name) == 0)
+ return i;
+
+ /* Fall through to the normal check if that didn't work out. */
+
+ /*
+ * Note: this code will not reject partially quoted strings, eg
+ * foo"BAR"foo will become fooBARfoo when it probably ought to be an error
+ * condition.
+ */
+ field_case = strdup(field_name);
+ if (field_case == NULL)
+ return -1; /* grotty */
+
+ in_quotes = false;
+ optr = field_case;
+ for (iptr = field_case; *iptr; iptr++)
+ {
+ char c = *iptr;
+
+ if (in_quotes)
+ {
+ if (c == '"')
+ {
+ if (iptr[1] == '"')
+ {
+ /* doubled quotes become a single quote */
+ *optr++ = '"';
+ iptr++;
+ }
+ else
+ in_quotes = false;
+ }
+ else
+ *optr++ = c;
+ }
+ else if (c == '"')
+ in_quotes = true;
+ else
+ {
+ c = pg_tolower((unsigned char) c);
+ *optr++ = c;
+ }
+ }
+ *optr = '\0';
+
+ for (i = 0; i < res->numAttributes; i++)
+ {
+ if (strcmp(field_case, res->attDescs[i].name) == 0)
+ {
+ free(field_case);
+ return i;
+ }
+ }
+ free(field_case);
+ return -1;
+}
+
+Oid
+PQftable(const PGresult *res, int field_num)
+{
+ if (!check_field_number(res, field_num))
+ return InvalidOid;
+ if (res->attDescs)
+ return res->attDescs[field_num].tableid;
+ else
+ return InvalidOid;
+}
+
+int
+PQftablecol(const PGresult *res, int field_num)
+{
+ if (!check_field_number(res, field_num))
+ return 0;
+ if (res->attDescs)
+ return res->attDescs[field_num].columnid;
+ else
+ return 0;
+}
+
+int
+PQfformat(const PGresult *res, int field_num)
+{
+ if (!check_field_number(res, field_num))
+ return 0;
+ if (res->attDescs)
+ return res->attDescs[field_num].format;
+ else
+ return 0;
+}
+
+Oid
+PQftype(const PGresult *res, int field_num)
+{
+ if (!check_field_number(res, field_num))
+ return InvalidOid;
+ if (res->attDescs)
+ return res->attDescs[field_num].typid;
+ else
+ return InvalidOid;
+}
+
+int
+PQfsize(const PGresult *res, int field_num)
+{
+ if (!check_field_number(res, field_num))
+ return 0;
+ if (res->attDescs)
+ return res->attDescs[field_num].typlen;
+ else
+ return 0;
+}
+
+int
+PQfmod(const PGresult *res, int field_num)
+{
+ if (!check_field_number(res, field_num))
+ return 0;
+ if (res->attDescs)
+ return res->attDescs[field_num].atttypmod;
+ else
+ return 0;
+}
+
+char *
+PQcmdStatus(PGresult *res)
+{
+ if (!res)
+ return NULL;
+ return res->cmdStatus;
+}
+
+/*
+ * PQoidStatus -
+ * if the last command was an INSERT, return the oid string
+ * if not, return ""
+ */
+char *
+PQoidStatus(const PGresult *res)
+{
+ /*
+ * This must be enough to hold the result. Don't laugh, this is better
+ * than what this function used to do.
+ */
+ static char buf[24];
+
+ size_t len;
+
+ if (!res || strncmp(res->cmdStatus, "INSERT ", 7) != 0)
+ return "";
+
+ len = strspn(res->cmdStatus + 7, "0123456789");
+ if (len > sizeof(buf) - 1)
+ len = sizeof(buf) - 1;
+ memcpy(buf, res->cmdStatus + 7, len);
+ buf[len] = '\0';
+
+ return buf;
+}
+
+/*
+ * PQoidValue -
+ * a perhaps preferable form of the above which just returns
+ * an Oid type
+ */
+Oid
+PQoidValue(const PGresult *res)
+{
+ char *endptr = NULL;
+ unsigned long result;
+
+ if (!res ||
+ strncmp(res->cmdStatus, "INSERT ", 7) != 0 ||
+ res->cmdStatus[7] < '0' ||
+ res->cmdStatus[7] > '9')
+ return InvalidOid;
+
+ result = strtoul(res->cmdStatus + 7, &endptr, 10);
+
+ if (!endptr || (*endptr != ' ' && *endptr != '\0'))
+ return InvalidOid;
+ else
+ return (Oid) result;
+}
+
+
+/*
+ * PQcmdTuples -
+ * If the last command was INSERT/UPDATE/DELETE/MOVE/FETCH/COPY, return
+ * a string containing the number of inserted/affected tuples. If not,
+ * return "".
+ *
+ * XXX: this should probably return an int
+ */
+char *
+PQcmdTuples(PGresult *res)
+{
+ char *p,
+ *c;
+
+ if (!res)
+ return "";
+
+ if (strncmp(res->cmdStatus, "INSERT ", 7) == 0)
+ {
+ p = res->cmdStatus + 7;
+ /* INSERT: skip oid and space */
+ while (*p && *p != ' ')
+ p++;
+ if (*p == 0)
+ goto interpret_error; /* no space? */
+ p++;
+ }
+ else if (strncmp(res->cmdStatus, "SELECT ", 7) == 0 ||
+ strncmp(res->cmdStatus, "DELETE ", 7) == 0 ||
+ strncmp(res->cmdStatus, "UPDATE ", 7) == 0)
+ p = res->cmdStatus + 7;
+ else if (strncmp(res->cmdStatus, "FETCH ", 6) == 0)
+ p = res->cmdStatus + 6;
+ else if (strncmp(res->cmdStatus, "MOVE ", 5) == 0 ||
+ strncmp(res->cmdStatus, "COPY ", 5) == 0)
+ p = res->cmdStatus + 5;
+ else
+ return "";
+
+ /* check that we have an integer (at least one digit, nothing else) */
+ for (c = p; *c; c++)
+ {
+ if (!isdigit((unsigned char) *c))
+ goto interpret_error;
+ }
+ if (c == p)
+ goto interpret_error;
+
+ return p;
+
+interpret_error:
+ pqInternalNotice(&res->noticeHooks,
+ "could not interpret result from server: %s",
+ res->cmdStatus);
+ return "";
+}
+
+/*
+ * PQgetvalue:
+ * return the value of field 'field_num' of row 'tup_num'
+ */
+char *
+PQgetvalue(const PGresult *res, int tup_num, int field_num)
+{
+ if (!check_tuple_field_number(res, tup_num, field_num))
+ return NULL;
+ return res->tuples[tup_num][field_num].value;
+}
+
+/* PQgetlength:
+ * returns the actual length of a field value in bytes.
+ */
+int
+PQgetlength(const PGresult *res, int tup_num, int field_num)
+{
+ if (!check_tuple_field_number(res, tup_num, field_num))
+ return 0;
+ if (res->tuples[tup_num][field_num].len != NULL_LEN)
+ return res->tuples[tup_num][field_num].len;
+ else
+ return 0;
+}
+
+/* PQgetisnull:
+ * returns the null status of a field value.
+ */
+int
+PQgetisnull(const PGresult *res, int tup_num, int field_num)
+{
+ if (!check_tuple_field_number(res, tup_num, field_num))
+ return 1; /* pretend it is null */
+ if (res->tuples[tup_num][field_num].len == NULL_LEN)
+ return 1;
+ else
+ return 0;
+}
+
+/* PQnparams:
+ * returns the number of input parameters of a prepared statement.
+ */
+int
+PQnparams(const PGresult *res)
+{
+ if (!res)
+ return 0;
+ return res->numParameters;
+}
+
+/* PQparamtype:
+ * returns type Oid of the specified statement parameter.
+ */
+Oid
+PQparamtype(const PGresult *res, int param_num)
+{
+ if (!check_param_number(res, param_num))
+ return InvalidOid;
+ if (res->paramDescs)
+ return res->paramDescs[param_num].typid;
+ else
+ return InvalidOid;
+}
+
+
+/* PQsetnonblocking:
+ * sets the PGconn's database connection non-blocking if the arg is true
+ * or makes it blocking if the arg is false, this will not protect
+ * you from PQexec(), you'll only be safe when using the non-blocking API.
+ * Needs to be called only on a connected database connection.
+ */
+int
+PQsetnonblocking(PGconn *conn, int arg)
+{
+ bool barg;
+
+ if (!conn || conn->status == CONNECTION_BAD)
+ return -1;
+
+ barg = (arg ? true : false);
+
+ /* early out if the socket is already in the state requested */
+ if (barg == conn->nonblocking)
+ return 0;
+
+ /*
+ * to guarantee constancy for flushing/query/result-polling behavior we
+ * need to flush the send queue at this point in order to guarantee proper
+ * behavior. this is ok because either they are making a transition _from_
+ * or _to_ blocking mode, either way we can block them.
+ */
+ /* if we are going from blocking to non-blocking flush here */
+ if (pqFlush(conn))
+ return -1;
+
+ conn->nonblocking = barg;
+
+ return 0;
+}
+
+/*
+ * return the blocking status of the database connection
+ * true == nonblocking, false == blocking
+ */
+int
+PQisnonblocking(const PGconn *conn)
+{
+ return pqIsnonblocking(conn);
+}
+
+/* libpq is thread-safe? */
+int
+PQisthreadsafe(void)
+{
+#ifdef ENABLE_THREAD_SAFETY
+ return true;
+#else
+ return false;
+#endif
+}
+
+
+/* try to force data out, really only useful for non-blocking users */
+int
+PQflush(PGconn *conn)
+{
+ return pqFlush(conn);
+}
+
+
+/*
+ * PQfreemem - safely frees memory allocated
+ *
+ * Needed mostly by Win32, unless multithreaded DLL (/MD in VC6)
+ * Used for freeing memory from PQescapeBytea()/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/src/interfaces/libpq/fe-gssapi-common.c b/src/interfaces/libpq/fe-gssapi-common.c
new file mode 100644
index 0000000..f87e7b2
--- /dev/null
+++ b/src/interfaces/libpq/fe-gssapi-common.c
@@ -0,0 +1,131 @@
+/*-------------------------------------------------------------------------
+ *
+ * fe-gssapi-common.c
+ * The front-end (client) GSSAPI common code
+ *
+ * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * src/interfaces/libpq/fe-gssapi-common.c
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres_fe.h"
+
+#include "fe-gssapi-common.h"
+
+#include "libpq-int.h"
+#include "pqexpbuffer.h"
+
+/*
+ * Fetch all errors of a specific type and append to "str".
+ * Each error string is preceded by a space.
+ */
+static void
+pg_GSS_error_int(PQExpBuffer str, OM_uint32 stat, int type)
+{
+ OM_uint32 lmin_s;
+ gss_buffer_desc lmsg;
+ OM_uint32 msg_ctx = 0;
+
+ do
+ {
+ if (gss_display_status(&lmin_s, stat, type, GSS_C_NO_OID,
+ &msg_ctx, &lmsg) != GSS_S_COMPLETE)
+ break;
+ appendPQExpBufferChar(str, ' ');
+ appendBinaryPQExpBuffer(str, lmsg.value, lmsg.length);
+ gss_release_buffer(&lmin_s, &lmsg);
+ } while (msg_ctx);
+}
+
+/*
+ * GSSAPI errors contain two parts; put both into conn->errorMessage.
+ */
+void
+pg_GSS_error(const char *mprefix, PGconn *conn,
+ OM_uint32 maj_stat, OM_uint32 min_stat)
+{
+ resetPQExpBuffer(&conn->errorMessage);
+ appendPQExpBuffer(&conn->errorMessage, "%s:", mprefix);
+ pg_GSS_error_int(&conn->errorMessage, maj_stat, GSS_C_GSS_CODE);
+ appendPQExpBufferChar(&conn->errorMessage, ':');
+ pg_GSS_error_int(&conn->errorMessage, min_stat, GSS_C_MECH_CODE);
+ appendPQExpBufferChar(&conn->errorMessage, '\n');
+}
+
+/*
+ * Check if we can acquire credentials at all (and yield them if so).
+ */
+bool
+pg_GSS_have_cred_cache(gss_cred_id_t *cred_out)
+{
+ OM_uint32 major,
+ minor;
+ gss_cred_id_t cred = GSS_C_NO_CREDENTIAL;
+
+ major = gss_acquire_cred(&minor, GSS_C_NO_NAME, 0, GSS_C_NO_OID_SET,
+ GSS_C_INITIATE, &cred, NULL, NULL);
+ if (major != GSS_S_COMPLETE)
+ {
+ *cred_out = NULL;
+ return false;
+ }
+ *cred_out = cred;
+ return true;
+}
+
+/*
+ * Try to load service name for a connection
+ */
+int
+pg_GSS_load_servicename(PGconn *conn)
+{
+ OM_uint32 maj_stat,
+ min_stat;
+ int maxlen;
+ gss_buffer_desc temp_gbuf;
+ char *host;
+
+ if (conn->gtarg_nam != NULL)
+ /* Already taken care of - move along */
+ return STATUS_OK;
+
+ host = PQhost(conn);
+ if (!(host && host[0] != '\0'))
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("host name must be specified\n"));
+ return STATUS_ERROR;
+ }
+
+ /*
+ * Import service principal name so the proper ticket can be acquired by
+ * the GSSAPI system.
+ */
+ maxlen = strlen(conn->krbsrvname) + strlen(host) + 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, host);
+ 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;
+ }
+ return STATUS_OK;
+}
diff --git a/src/interfaces/libpq/fe-gssapi-common.h b/src/interfaces/libpq/fe-gssapi-common.h
new file mode 100644
index 0000000..1569252
--- /dev/null
+++ b/src/interfaces/libpq/fe-gssapi-common.h
@@ -0,0 +1,28 @@
+/*-------------------------------------------------------------------------
+ *
+ * fe-gssapi-common.h
+ *
+ * Definitions for GSSAPI common routines
+ *
+ * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/interfaces/libpq/fe-gssapi-common.h
+ */
+
+#ifndef FE_GSSAPI_COMMON_H
+#define FE_GSSAPI_COMMON_H
+
+#include "libpq-fe.h"
+#include "libpq-int.h"
+
+#ifdef ENABLE_GSS
+
+void pg_GSS_error(const char *mprefix, PGconn *conn,
+ OM_uint32 maj_stat, OM_uint32 min_stat);
+bool pg_GSS_have_cred_cache(gss_cred_id_t *cred_out);
+int pg_GSS_load_servicename(PGconn *conn);
+
+#endif
+
+#endif /* FE_GSSAPI_COMMON_H */
diff --git a/src/interfaces/libpq/fe-lobj.c b/src/interfaces/libpq/fe-lobj.c
new file mode 100644
index 0000000..a94ce06
--- /dev/null
+++ b/src/interfaces/libpq/fe-lobj.c
@@ -0,0 +1,1102 @@
+/*-------------------------------------------------------------------------
+ *
+ * fe-lobj.c
+ * Front-end large object interface
+ *
+ * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/interfaces/libpq/fe-lobj.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifdef WIN32
+/*
+ * As unlink/rename are #define'd in port.h (via postgres_fe.h), io.h
+ * must be included first on MS C. Might as well do it for all WIN32's
+ * here.
+ */
+#include <io.h>
+#endif
+
+#include "postgres_fe.h"
+
+#ifdef WIN32
+#include "win32.h"
+#else
+#include <unistd.h>
+#endif
+
+#include <fcntl.h>
+#include <limits.h>
+#include <sys/stat.h>
+
+#include "libpq-fe.h"
+#include "libpq-int.h"
+#include "libpq/libpq-fs.h" /* must come after sys/stat.h */
+#include "port/pg_bswap.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 *) unconstify(char *, 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[PG_STRERROR_R_BUFLEN];
+
+ /*
+ * 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, strerror_r(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,
+ strerror_r(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[PG_STRERROR_R_BUFLEN];
+
+ /*
+ * 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,
+ strerror_r(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,
+ strerror_r(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) != 0 && result >= 0)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("could not write to file \"%s\": %s\n"),
+ filename, strerror_r(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] = pg_hton32(t);
+
+ /* Now the low order half */
+ t = (uint32) host64;
+ swap.i32[1] = pg_hton32(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) pg_ntoh32(swap.i32[0]);
+ result <<= 32;
+ result |= (uint32) pg_ntoh32(swap.i32[1]);
+
+ return result;
+}
diff --git a/src/interfaces/libpq/fe-misc.c b/src/interfaces/libpq/fe-misc.c
new file mode 100644
index 0000000..9273984
--- /dev/null
+++ b/src/interfaces/libpq/fe-misc.c
@@ -0,0 +1,1314 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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/pqformat.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-2020, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * src/interfaces/libpq/fe-misc.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres_fe.h"
+
+#include <signal.h>
+#include <time.h>
+
+#ifdef WIN32
+#include "win32.h"
+#else
+#include <unistd.h>
+#include <sys/time.h>
+#endif
+
+#ifdef HAVE_POLL_H
+#include <poll.h>
+#endif
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+
+#include "libpq-fe.h"
+#include "libpq-int.h"
+#include "mb/pg_wchar.h"
+#include "pg_config_paths.h"
+#include "port/pg_bswap.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) pg_ntoh16(tmp2);
+ break;
+ case 4:
+ if (conn->inCursor + 4 > conn->inEnd)
+ return EOF;
+ memcpy(&tmp4, conn->inBuffer + conn->inCursor, 4);
+ conn->inCursor += 4;
+ *result = (int) pg_ntoh32(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 = pg_hton16((uint16) value);
+ if (pqPutMsgBytes((const char *) &tmp2, 2, conn))
+ return EOF;
+ break;
+ case 4:
+ tmp4 = pg_hton32((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 = pg_hton32(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.
+ *
+ * Note that this is also responsible for consuming data from the socket
+ * (putting it in conn->inBuffer) in any situation where we can't send
+ * all the specified data immediately.
+ *
+ * Upon write failure, conn->write_failed is set and the error message is
+ * saved in conn->write_err_msg, but we clear the output buffer and return
+ * zero anyway; this is because callers should soldier on until it's possible
+ * to read from the server and check for an error message. write_err_msg
+ * should be reported only when we are unable to obtain a server error first.
+ * (Thus, a -1 result is returned only for an internal *read* failure.)
+ */
+static int
+pqSendSome(PGconn *conn, int len)
+{
+ char *ptr = conn->outBuffer;
+ int remaining = conn->outCount;
+ int result = 0;
+
+ /*
+ * If we already had a write failure, we will never again try to send data
+ * on that connection. Even if the kernel would let us, we've probably
+ * lost message boundary sync with the server. conn->write_failed
+ * therefore persists until the connection is reset, and we just discard
+ * all data presented to be written. However, as long as we still have a
+ * valid socket, we should continue to absorb data from the backend, so
+ * that we can collect any final error messages.
+ */
+ if (conn->write_failed)
+ {
+ /* conn->write_err_msg should be set up already */
+ conn->outCount = 0;
+ /* Absorb input data if any, and detect socket closure */
+ if (conn->sock != PGINVALID_SOCKET)
+ {
+ if (pqReadData(conn) < 0)
+ return -1;
+ }
+ return 0;
+ }
+
+ if (conn->sock == PGINVALID_SOCKET)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("connection not open\n"));
+ conn->write_failed = true;
+ /* Transfer error message to conn->write_err_msg, if possible */
+ /* (strdup failure is OK, we'll cope later) */
+ conn->write_err_msg = strdup(conn->errorMessage.data);
+ resetPQExpBuffer(&conn->errorMessage);
+ /* Discard queued data; no chance it'll ever be sent */
+ conn->outCount = 0;
+ return 0;
+ }
+
+ /* 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 */
+ conn->write_failed = true;
+
+ /*
+ * Transfer error message to conn->write_err_msg, if
+ * possible (strdup failure is OK, we'll cope later).
+ *
+ * Note: this assumes that pqsecure_write and its children
+ * will overwrite not append to conn->errorMessage. If
+ * that's ever changed, we could remember the length of
+ * conn->errorMessage at entry to this routine, and then
+ * save and delete just what was appended.
+ */
+ conn->write_err_msg = strdup(conn->errorMessage.data);
+ resetPQExpBuffer(&conn->errorMessage);
+
+ /* Discard queued data; no chance it'll ever be sent */
+ conn->outCount = 0;
+
+ /* Absorb input data if any, and detect socket closure */
+ if (conn->sock != PGINVALID_SOCKET)
+ {
+ if (pqReadData(conn) < 0)
+ return -1;
+ }
+ return 0;
+ }
+ }
+ 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.)
+ *
+ * Note that errors here don't result in write_failed becoming
+ * set.
+ */
+ 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.
+ * (See pqSendSome comments about how failure should be handled.)
+ */
+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.
+ *
+ * finish_time = ((time_t) -1) disables the wait limit.
+ *
+ * Returns -1 on failure, 0 if the socket is readable/writable, 1 if it timed out.
+ */
+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 -1; /* errorMessage is already set */
+
+ if (result == 0)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("timeout expired\n"));
+ return 1;
+ }
+
+ 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))
+ {
+ /* 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[PG_STRERROR_R_BUFLEN];
+
+ 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(void)
+{
+ 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/src/interfaces/libpq/fe-print.c b/src/interfaces/libpq/fe-print.c
new file mode 100644
index 0000000..8096934
--- /dev/null
+++ b/src/interfaces/libpq/fe-print.c
@@ -0,0 +1,782 @@
+/*-------------------------------------------------------------------------
+ *
+ * fe-print.c
+ * functions for pretty-printing query results
+ *
+ * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * These functions were formerly part of fe-exec.c, but they
+ * didn't really belong there.
+ *
+ * IDENTIFICATION
+ * src/interfaces/libpq/fe-print.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres_fe.h"
+
+#include <signal.h>
+
+#ifdef WIN32
+#include "win32.h"
+#else
+#include <unistd.h>
+#include <sys/ioctl.h>
+#endif
+
+#ifdef HAVE_TERMIOS_H
+#include <termios.h>
+#else
+#ifndef WIN32
+#include <sys/termios.h>
+#endif
+#endif
+
+#include "libpq-fe.h"
+#include "libpq-int.h"
+
+#define PQmblenBounded(s, e) strnlen(s, PQmblen(s, e))
+
+static bool 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 = NULL;
+ int fieldMaxLen = 0;
+ int numFieldName;
+ int fs_len = strlen(po->fieldSep);
+ int total_line_length = 0;
+ bool usePipe = false;
+ 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);
+ fieldNames = (const char **) calloc(nFields, sizeof(char *));
+ fieldNotNum = (unsigned char *) calloc(nFields, 1);
+ fieldMax = (int *) calloc(nFields, sizeof(int));
+ if (!fieldNames || !fieldNotNum || !fieldMax)
+ {
+ fprintf(stderr, libpq_gettext("out of memory\n"));
+ goto exit;
+ }
+ 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
+
+ /*
+ * Since this function is no longer used by psql, we don't examine
+ * PSQL_PAGER. It's possible that the hypothetical external users
+ * of the function would like that to happen, but in the name of
+ * backwards compatibility, we'll stick to just examining PAGER.
+ */
+ 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 = true;
+#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))
+ {
+ fields = (char **) calloc((size_t) nTups + 1,
+ nFields * sizeof(char *));
+ if (!fields)
+ {
+ fprintf(stderr, libpq_gettext("out of memory\n"));
+ goto exit;
+ }
+ }
+ else if (po->header && !po->html3)
+ {
+ if (po->expanded)
+ {
+ if (po->align)
+ fprintf(fout, libpq_gettext("%-*s%s Value\n"),
+ fieldMaxLen - fs_len, libpq_gettext("Field"), po->fieldSep);
+ else
+ fprintf(fout, libpq_gettext("%s%sValue\n"), libpq_gettext("Field"), po->fieldSep);
+ }
+ else
+ {
+ int len = 0;
+
+ for (j = 0; j < nFields; j++)
+ {
+ const char *s = fieldNames[j];
+
+ fputs(s, fout);
+ len += strlen(s) + fs_len;
+ if ((j + 1) < nFields)
+ fputs(po->fieldSep, fout);
+ }
+ fputc('\n', fout);
+ for (len -= fs_len; len--; fputc('-', fout));
+ fputc('\n', fout);
+ }
+ }
+ if (po->expanded && po->html3)
+ {
+ if (po->caption)
+ fprintf(fout, "<center><h2>%s</h2></center>\n", po->caption);
+ else
+ fprintf(fout,
+ "<center><h2>"
+ "Query retrieved %d rows * %d fields"
+ "</h2></center>\n",
+ nTups, nFields);
+ }
+ for (i = 0; i < nTups; i++)
+ {
+ if (po->expanded)
+ {
+ if (po->html3)
+ fprintf(fout,
+ "<table %s><caption align=\"top\">%d</caption>\n",
+ po->tableOpt ? po->tableOpt : "", i);
+ else
+ fprintf(fout, libpq_gettext("-- RECORD %d --\n"), i);
+ }
+ for (j = 0; j < nFields; j++)
+ {
+ if (!do_field(po, res, i, j, fs_len, fields, nFields,
+ fieldNames, fieldNotNum,
+ fieldMax, fieldMaxLen, fout))
+ goto exit;
+ }
+ if (po->html3 && po->expanded)
+ fputs("</table>\n", fout);
+ }
+ if (!po->expanded && (po->align || po->html3))
+ {
+ if (po->html3)
+ {
+ if (po->header)
+ {
+ if (po->caption)
+ fprintf(fout,
+ "<table %s><caption align=\"top\">%s</caption>\n",
+ po->tableOpt ? po->tableOpt : "",
+ po->caption);
+ else
+ fprintf(fout,
+ "<table %s><caption align=\"top\">"
+ "Retrieved %d rows * %d fields"
+ "</caption>\n",
+ po->tableOpt ? po->tableOpt : "", nTups, nFields);
+ }
+ else
+ fprintf(fout, "<table %s>", po->tableOpt ? po->tableOpt : "");
+ }
+ if (po->header)
+ border = do_header(fout, po, nFields, fieldMax, fieldNames,
+ fieldNotNum, fs_len, res);
+ for (i = 0; i < nTups; i++)
+ output_row(fout, po, nFields, fields,
+ fieldNotNum, fieldMax, border, i);
+ }
+ if (po->header && !po->html3)
+ fprintf(fout, "(%d row%s)\n\n", PQntuples(res),
+ (PQntuples(res) == 1) ? "" : "s");
+ if (po->html3 && !po->expanded)
+ fputs("</table>\n", fout);
+
+exit:
+ if (fieldMax)
+ free(fieldMax);
+ if (fieldNotNum)
+ free(fieldNotNum);
+ if (border)
+ free(border);
+ if (fields)
+ {
+ /* if calloc succeeded, this shouldn't overflow size_t */
+ size_t numfields = ((size_t) nTups + 1) * (size_t) nFields;
+
+ while (numfields-- > 0)
+ {
+ if (fields[numfields])
+ free(fields[numfields]);
+ }
+ free(fields);
+ }
+ if (fieldNames)
+ 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 */
+ }
+ }
+}
+
+
+static bool
+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 += PQmblenBounded(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"));
+ return false;
+ }
+ strcpy(fields[i * nFields + j], pval);
+ }
+ else
+ {
+ if (po->expanded)
+ {
+ if (po->html3)
+ fprintf(fout,
+ "<tr><td align=\"left\"><b>%s</b></td>"
+ "<td align=\"%s\">%s</td></tr>\n",
+ fieldNames[j],
+ fieldNotNum[j] ? "left" : "right",
+ pval);
+ else
+ {
+ if (po->align)
+ fprintf(fout,
+ "%-*s%s %s\n",
+ fieldMaxLen - fs_len, fieldNames[j],
+ po->fieldSep,
+ pval);
+ else
+ fprintf(fout,
+ "%s%s%s\n",
+ fieldNames[j], po->fieldSep, pval);
+ }
+ }
+ else
+ {
+ if (!po->html3)
+ {
+ fputs(pval, fout);
+ efield:
+ if ((j + 1) < nFields)
+ fputs(po->fieldSep, fout);
+ else
+ fputc('\n', fout);
+ }
+ }
+ }
+ }
+ return true;
+}
+
+
+static char *
+do_header(FILE *fout, const PQprintOpt *po, const int nFields, int *fieldMax,
+ const char **fieldNames, unsigned char *fieldNotNum,
+ const int fs_len, const PGresult *res)
+{
+ int j; /* for loop index */
+ char *border = NULL;
+
+ if (po->html3)
+ fputs("<tr>", fout);
+ else
+ {
+ int tot = 0;
+ int n = 0;
+ char *p = NULL;
+
+ for (; n < nFields; n++)
+ tot += fieldMax[n] + fs_len + (po->standard ? 2 : 0);
+ if (po->standard)
+ tot += fs_len * 2 + 2;
+ border = malloc(tot + 1);
+ if (!border)
+ {
+ fprintf(stderr, libpq_gettext("out of memory\n"));
+ return NULL;
+ }
+ p = border;
+ if (po->standard)
+ {
+ char *fs = po->fieldSep;
+
+ while (*fs++)
+ *p++ = '+';
+ }
+ for (j = 0; j < nFields; j++)
+ {
+ int len;
+
+ for (len = fieldMax[j] + (po->standard ? 2 : 0); len--; *p++ = '-');
+ if (po->standard || (j + 1) < nFields)
+ {
+ char *fs = po->fieldSep;
+
+ while (*fs++)
+ *p++ = '+';
+ }
+ }
+ *p = '\0';
+ if (po->standard)
+ fprintf(fout, "%s\n", border);
+ }
+ if (po->standard)
+ fputs(po->fieldSep, fout);
+ for (j = 0; j < nFields; j++)
+ {
+ const char *s = PQfname(res, j);
+
+ if (po->html3)
+ {
+ fprintf(fout, "<th align=\"%s\">%s</th>",
+ fieldNotNum[j] ? "left" : "right", fieldNames[j]);
+ }
+ else
+ {
+ int n = strlen(s);
+
+ if (n > fieldMax[j])
+ fieldMax[j] = n;
+ if (po->standard)
+ fprintf(fout,
+ fieldNotNum[j] ? " %-*s " : " %*s ",
+ fieldMax[j], s);
+ else
+ fprintf(fout, fieldNotNum[j] ? "%-*s" : "%*s", fieldMax[j], s);
+ if (po->standard || (j + 1) < nFields)
+ fputs(po->fieldSep, fout);
+ }
+ }
+ if (po->html3)
+ fputs("</tr>\n", fout);
+ else
+ fprintf(fout, "\n%s\n", border);
+ return border;
+}
+
+
+static void
+output_row(FILE *fout, const PQprintOpt *po, const int nFields, char **fields,
+ unsigned char *fieldNotNum, int *fieldMax, char *border,
+ const int row_index)
+{
+ int field_index; /* for loop index */
+
+ if (po->html3)
+ fputs("<tr>", fout);
+ else if (po->standard)
+ fputs(po->fieldSep, fout);
+ for (field_index = 0; field_index < nFields; field_index++)
+ {
+ char *p = fields[row_index * nFields + field_index];
+
+ if (po->html3)
+ fprintf(fout, "<td align=\"%s\">%s</td>",
+ fieldNotNum[field_index] ? "left" : "right", p ? p : "");
+ else
+ {
+ fprintf(fout,
+ fieldNotNum[field_index] ?
+ (po->standard ? " %-*s " : "%-*s") :
+ (po->standard ? " %*s " : "%*s"),
+ fieldMax[field_index],
+ p ? p : "");
+ if (po->standard || field_index + 1 < nFields)
+ fputs(po->fieldSep, fout);
+ }
+ }
+ if (po->html3)
+ fputs("</tr>", fout);
+ else if (po->standard)
+ fprintf(fout, "\n%s", border);
+ fputc('\n', fout);
+}
+
+
+
+/*
+ * really old printing routines
+ */
+
+void
+PQdisplayTuples(const PGresult *res,
+ FILE *fp, /* where to send the output */
+ int fillAlign, /* pad the fields with spaces */
+ const char *fieldSep, /* field separator */
+ int printHeader, /* display headers? */
+ int quiet
+)
+{
+#define DEFAULT_FIELD_SEP " "
+
+ int i,
+ j;
+ int nFields;
+ int nTuples;
+ int *fLength = NULL;
+
+ if (fieldSep == NULL)
+ fieldSep = DEFAULT_FIELD_SEP;
+
+ /* Get some useful info about the results */
+ nFields = PQnfields(res);
+ nTuples = PQntuples(res);
+
+ if (fp == NULL)
+ fp = stdout;
+
+ /* Figure the field lengths to align to */
+ /* will be somewhat time consuming for very large results */
+ if (fillAlign)
+ {
+ fLength = (int *) malloc(nFields * sizeof(int));
+ if (!fLength)
+ {
+ fprintf(stderr, libpq_gettext("out of memory\n"));
+ return;
+ }
+
+ 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"));
+ return;
+ }
+ 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/src/interfaces/libpq/fe-protocol2.c b/src/interfaces/libpq/fe-protocol2.c
new file mode 100644
index 0000000..9360c54
--- /dev/null
+++ b/src/interfaces/libpq/fe-protocol2.c
@@ -0,0 +1,1612 @@
+/*-------------------------------------------------------------------------
+ *
+ * fe-protocol2.c
+ * functions that are specific to frontend/backend protocol version 2
+ *
+ * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/interfaces/libpq/fe-protocol2.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres_fe.h"
+
+#include <ctype.h>
+#include <fcntl.h>
+
+#ifdef WIN32
+#include "win32.h"
+#else
+#include <unistd.h>
+#ifdef HAVE_NETINET_TCP_H
+#include <netinet/tcp.h>
+#endif
+#endif
+
+#include "libpq-fe.h"
+#include "libpq-int.h"
+#include "port/pg_bswap.h"
+
+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;
+
+ /*
+ * If this is an error message, pre-emptively clear any incomplete query
+ * result we may have. We'd just throw it away below anyway, and
+ * releasing it before collecting the error might avoid out-of-memory.
+ */
+ if (isError)
+ pqClearAsyncResult(conn);
+
+ /*
+ * 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); /* redundant, but be safe */
+ 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 */
+ {
+ /* error message should be set up already */
+ return NULL;
+ }
+
+ for (i = 0; i < nargs; ++i)
+ { /* len.int4 + contents */
+ if (pqPutInt(args[i].len, 4, conn))
+ return NULL;
+
+ if (args[i].isint)
+ {
+ if (pqPutInt(args[i].u.integer, 4, conn))
+ return NULL;
+ }
+ else
+ {
+ if (pqPutnchar((char *) args[i].u.ptr, args[i].len, conn))
+ return NULL;
+ }
+ }
+
+ if (pqPutMsgEnd(conn) < 0 ||
+ pqFlush(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 = pg_hton32(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/src/interfaces/libpq/fe-protocol3.c b/src/interfaces/libpq/fe-protocol3.c
new file mode 100644
index 0000000..04fd4ba
--- /dev/null
+++ b/src/interfaces/libpq/fe-protocol3.c
@@ -0,0 +1,2178 @@
+/*-------------------------------------------------------------------------
+ *
+ * fe-protocol3.c
+ * functions that are specific to frontend/backend protocol version 3
+ *
+ * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/interfaces/libpq/fe-protocol3.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres_fe.h"
+
+#include <ctype.h>
+#include <fcntl.h>
+
+#ifdef WIN32
+#include "win32.h"
+#else
+#include <unistd.h>
+#ifdef HAVE_NETINET_TCP_H
+#include <netinet/tcp.h>
+#endif
+#endif
+
+#include "libpq-fe.h"
+#include "libpq-int.h"
+#include "mb/pg_wchar.h"
+#include "port/pg_bswap.h"
+
+/*
+ * 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')
+
+#define PQmblenBounded(s, e) strnlen(s, PQmblen(s, e))
+
+
+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;
+ }
+ 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 PGRES_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;
+ break;
+ 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;
+ }
+ 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).
+ */
+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;
+ }
+
+ /* Success! */
+ conn->result = result;
+
+ /*
+ * 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);
+
+ /*
+ * 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);
+
+ /*
+ * Show the message as fully consumed, else pqParseInput3 will overwrite
+ * our error with a complaint about that.
+ */
+ conn->inCursor = conn->inStart + 5 + msgLength;
+
+ /*
+ * 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 processed message successfully, EOF to suspend parsing
+ * (the latter case is not actually used currently).
+ */
+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;
+ }
+
+ /* Success! */
+ conn->result = result;
+
+ return 0;
+
+not_enough_data:
+ errmsg = libpq_gettext("insufficient data in \"t\" message");
+
+advance_and_error:
+ /* Discard unsaved result, if any */
+ if (result && result != conn->result)
+ PQclear(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");
+ printfPQExpBuffer(&conn->errorMessage, "%s\n", errmsg);
+ pqSaveErrorResult(conn);
+
+ /*
+ * Show the message as fully consumed, else pqParseInput3 will overwrite
+ * our error with a complaint about that.
+ */
+ conn->inCursor = conn->inStart + 5 + msgLength;
+
+ /*
+ * 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).
+ */
+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;
+ }
+ }
+ }
+
+ /* Process the collected row */
+ errmsg = NULL;
+ if (pqRowProcessor(conn, &errmsg))
+ return 0; /* normal, successful exit */
+
+ /* pqRowProcessor failed, fall through to report it */
+
+advance_and_error:
+
+ /*
+ * 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);
+
+ /*
+ * Show the message as fully consumed, else pqParseInput3 will overwrite
+ * our error with a complaint about that.
+ */
+ conn->inCursor = conn->inStart + 5 + msgLength;
+
+ /*
+ * 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;
+
+ /*
+ * If this is an error message, pre-emptively clear any incomplete query
+ * result we may have. We'd just throw it away below anyway, and
+ * releasing it before collecting the error might avoid out-of-memory.
+ */
+ if (isError)
+ pqClearAsyncResult(conn);
+
+ /*
+ * 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); /* redundant, but be safe */
+ 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)
+ {
+ appendPQExpBufferStr(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
+ appendPQExpBufferStr(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_SQLSTATE)
+ {
+ /*
+ * If we have a SQLSTATE, print that and nothing else. If not (which
+ * shouldn't happen for server-generated errors, but might possibly
+ * happen for libpq-generated ones), fall back to TERSE format, as
+ * that seems better than printing nothing at all.
+ */
+ val = PQresultErrorField(res, PG_DIAG_SQLSTATE);
+ if (val)
+ {
+ appendPQExpBuffer(msg, "%s\n", val);
+ return;
+ }
+ verbosity = PQERRORS_TERSE;
+ }
+
+ 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 += PQmblenBounded(&wquery[qoffset], encoding);
+ }
+ 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 += PQmblenBounded(&msg->data[i], encoding))
+ {
+ 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 */
+ {
+ /* error message should be set up already */
+ return NULL;
+ }
+
+ for (i = 0; i < nargs; ++i)
+ { /* len.int4 + contents */
+ if (pqPutInt(args[i].len, 4, 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))
+ return NULL;
+ }
+ else
+ {
+ if (pqPutnchar((char *) args[i].u.ptr, args[i].len, conn))
+ return NULL;
+ }
+ }
+
+ if (pqPutInt(1, 2, conn) < 0) /* result format code: BINARY */
+ return NULL;
+
+ if (pqPutMsgEnd(conn) < 0 ||
+ pqFlush(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 = pg_hton32(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/src/interfaces/libpq/fe-secure-common.c b/src/interfaces/libpq/fe-secure-common.c
new file mode 100644
index 0000000..48512b6
--- /dev/null
+++ b/src/interfaces/libpq/fe-secure-common.c
@@ -0,0 +1,211 @@
+/*-------------------------------------------------------------------------
+ *
+ * fe-secure-common.c
+ *
+ * common implementation-independent SSL support code
+ *
+ * While fe-secure.c contains the interfaces that the rest of libpq call, this
+ * file contains support routines that are used by the library-specific
+ * implementations such as fe-secure-openssl.c.
+ *
+ * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * src/interfaces/libpq/fe-secure-common.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres_fe.h"
+
+#include "fe-secure-common.h"
+
+#include "libpq-int.h"
+#include "pqexpbuffer.h"
+
+/*
+ * Check if a wildcard certificate matches the server hostname.
+ *
+ * The rule for this is:
+ * 1. We only match the '*' character as wildcard
+ * 2. We match only wildcards at the start of the string
+ * 3. The '*' character does *not* match '.', meaning that we match only
+ * a single pathname component.
+ * 4. We don't support more than one '*' in a single pattern.
+ *
+ * This is roughly in line with RFC2818, but contrary to what most browsers
+ * appear to be implementing (point 3 being the difference)
+ *
+ * Matching is always case-insensitive, since DNS is case insensitive.
+ */
+static bool
+wildcard_certificate_match(const char *pattern, const char *string)
+{
+ int lenpat = strlen(pattern);
+ int lenstr = strlen(string);
+
+ /* If we don't start with a wildcard, it's not a match (rule 1 & 2) */
+ if (lenpat < 3 ||
+ pattern[0] != '*' ||
+ pattern[1] != '.')
+ return false;
+
+ /* If pattern is longer than the string, we can never match */
+ if (lenpat > lenstr)
+ return false;
+
+ /*
+ * If string does not end in pattern (minus the wildcard), we don't match
+ */
+ if (pg_strcasecmp(pattern + 1, string + lenstr - lenpat + 1) != 0)
+ return false;
+
+ /*
+ * If there is a dot left of where the pattern started to match, we don't
+ * match (rule 3)
+ */
+ if (strchr(string, '.') < string + lenstr - lenpat)
+ return false;
+
+ /* String ended with pattern, and didn't have a dot before, so we match */
+ return true;
+}
+
+/*
+ * Check if a name from a server's certificate matches the peer's hostname.
+ *
+ * Returns 1 if the name matches, and 0 if it does not. On error, returns
+ * -1, and sets the libpq error message.
+ *
+ * The name extracted from the certificate is returned in *store_name. The
+ * caller is responsible for freeing it.
+ */
+int
+pq_verify_peer_name_matches_certificate_name(PGconn *conn,
+ const char *namedata, size_t namelen,
+ char **store_name)
+{
+ char *name;
+ int result;
+ char *host = conn->connhost[conn->whichhost].host;
+
+ *store_name = NULL;
+
+ if (!(host && host[0] != '\0'))
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("host name must be specified\n"));
+ return -1;
+ }
+
+ /*
+ * There is no guarantee the string returned from the certificate is
+ * NULL-terminated, so make a copy that is.
+ */
+ name = malloc(namelen + 1);
+ if (name == NULL)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("out of memory\n"));
+ return -1;
+ }
+ memcpy(name, namedata, namelen);
+ name[namelen] = '\0';
+
+ /*
+ * Reject embedded NULLs in certificate common or alternative name to
+ * prevent attacks like CVE-2009-4034.
+ */
+ if (namelen != strlen(name))
+ {
+ free(name);
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("SSL certificate's name contains embedded null\n"));
+ return -1;
+ }
+
+ if (pg_strcasecmp(name, host) == 0)
+ {
+ /* Exact name match */
+ result = 1;
+ }
+ else if (wildcard_certificate_match(name, host))
+ {
+ /* Matched wildcard name */
+ result = 1;
+ }
+ else
+ {
+ result = 0;
+ }
+
+ *store_name = name;
+ return result;
+}
+
+/*
+ * Verify that the server certificate matches the hostname we connected to.
+ *
+ * The certificate's Common Name and Subject Alternative Names are considered.
+ */
+bool
+pq_verify_peer_name_matches_certificate(PGconn *conn)
+{
+ char *host = conn->connhost[conn->whichhost].host;
+ int rc;
+ int names_examined = 0;
+ char *first_name = NULL;
+
+ /*
+ * If told not to verify the peer name, don't do it. Return true
+ * indicating that the verification was successful.
+ */
+ if (strcmp(conn->sslmode, "verify-full") != 0)
+ return true;
+
+ /* Check that we have a hostname to compare with. */
+ if (!(host && host[0] != '\0'))
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("host name must be specified for a verified SSL connection\n"));
+ return false;
+ }
+
+ rc = pgtls_verify_peer_name_matches_certificate_guts(conn, &names_examined, &first_name);
+
+ if (rc == 0)
+ {
+ /*
+ * No match. Include the name from the server certificate in the error
+ * message, to aid debugging broken configurations. If there are
+ * multiple names, only print the first one to avoid an overly long
+ * error message.
+ */
+ if (names_examined > 1)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_ngettext("server certificate for \"%s\" (and %d other name) does not match host name \"%s\"\n",
+ "server certificate for \"%s\" (and %d other names) does not match host name \"%s\"\n",
+ names_examined - 1),
+ first_name, names_examined - 1, host);
+ }
+ else if (names_examined == 1)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("server certificate for \"%s\" does not match host name \"%s\"\n"),
+ first_name, host);
+ }
+ else
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("could not get server's host name from server certificate\n"));
+ }
+ }
+
+ /* clean up */
+ if (first_name)
+ free(first_name);
+
+ return (rc == 1);
+}
diff --git a/src/interfaces/libpq/fe-secure-common.h b/src/interfaces/libpq/fe-secure-common.h
new file mode 100644
index 0000000..8e483f6
--- /dev/null
+++ b/src/interfaces/libpq/fe-secure-common.h
@@ -0,0 +1,26 @@
+/*-------------------------------------------------------------------------
+ *
+ * fe-secure-common.h
+ *
+ * common implementation-independent SSL support code
+ *
+ * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * src/interfaces/libpq/fe-secure-common.h
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef FE_SECURE_COMMON_H
+#define FE_SECURE_COMMON_H
+
+#include "libpq-fe.h"
+
+extern int pq_verify_peer_name_matches_certificate_name(PGconn *conn,
+ const char *namedata, size_t namelen,
+ char **store_name);
+extern bool pq_verify_peer_name_matches_certificate(PGconn *conn);
+
+#endif /* FE_SECURE_COMMON_H */
diff --git a/src/interfaces/libpq/fe-secure-gssapi.c b/src/interfaces/libpq/fe-secure-gssapi.c
new file mode 100644
index 0000000..d70189c
--- /dev/null
+++ b/src/interfaces/libpq/fe-secure-gssapi.c
@@ -0,0 +1,731 @@
+/*-------------------------------------------------------------------------
+ *
+ * fe-secure-gssapi.c
+ * The front-end (client) encryption support for GSSAPI
+ *
+ * Portions Copyright (c) 2016-2020, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ * src/interfaces/libpq/fe-secure-gssapi.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres_fe.h"
+
+#include "fe-gssapi-common.h"
+#include "libpq-fe.h"
+#include "libpq-int.h"
+#include "port/pg_bswap.h"
+
+
+/*
+ * Require encryption support, as well as mutual authentication and
+ * tamperproofing measures.
+ */
+#define GSS_REQUIRED_FLAGS GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | \
+ GSS_C_SEQUENCE_FLAG | GSS_C_CONF_FLAG | GSS_C_INTEG_FLAG
+
+/*
+ * Handle the encryption/decryption of data using GSSAPI.
+ *
+ * In the encrypted data stream on the wire, we break up the data
+ * into packets where each packet starts with a uint32-size length
+ * word (in network byte order), then encrypted data of that length
+ * immediately following. Decryption yields the same data stream
+ * that would appear when not using encryption.
+ *
+ * Encrypted data typically ends up being larger than the same data
+ * unencrypted, so we use fixed-size buffers for handling the
+ * encryption/decryption which are larger than PQComm's buffer will
+ * typically be to minimize the times where we have to make multiple
+ * packets (and therefore multiple recv/send calls for a single
+ * read/write call to us).
+ *
+ * NOTE: The client and server have to agree on the max packet size,
+ * because we have to pass an entire packet to GSSAPI at a time and we
+ * don't want the other side to send arbitrarily huge packets as we
+ * would have to allocate memory for them to then pass them to GSSAPI.
+ *
+ * Therefore, these two #define's are effectively part of the protocol
+ * spec and can't ever be changed.
+ */
+#define PQ_GSS_SEND_BUFFER_SIZE 16384
+#define PQ_GSS_RECV_BUFFER_SIZE 16384
+
+/*
+ * We need these state variables per-connection. To allow the functions
+ * in this file to look mostly like those in be-secure-gssapi.c, set up
+ * these macros.
+ */
+#define PqGSSSendBuffer (conn->gss_SendBuffer)
+#define PqGSSSendLength (conn->gss_SendLength)
+#define PqGSSSendNext (conn->gss_SendNext)
+#define PqGSSSendConsumed (conn->gss_SendConsumed)
+#define PqGSSRecvBuffer (conn->gss_RecvBuffer)
+#define PqGSSRecvLength (conn->gss_RecvLength)
+#define PqGSSResultBuffer (conn->gss_ResultBuffer)
+#define PqGSSResultLength (conn->gss_ResultLength)
+#define PqGSSResultNext (conn->gss_ResultNext)
+#define PqGSSMaxPktSize (conn->gss_MaxPktSize)
+
+
+/*
+ * Attempt to write len bytes of data from ptr to a GSSAPI-encrypted connection.
+ *
+ * The connection must be already set up for GSSAPI encryption (i.e., GSSAPI
+ * transport negotiation is complete).
+ *
+ * On success, returns the number of data bytes consumed (possibly less than
+ * len). On failure, returns -1 with errno set appropriately. If the errno
+ * indicates a non-retryable error, a message is put into conn->errorMessage.
+ * For retryable errors, caller should call again (passing the same data)
+ * once the socket is ready.
+ */
+ssize_t
+pg_GSS_write(PGconn *conn, const void *ptr, size_t len)
+{
+ OM_uint32 major,
+ minor;
+ gss_buffer_desc input,
+ output = GSS_C_EMPTY_BUFFER;
+ ssize_t ret = -1;
+ size_t bytes_sent = 0;
+ size_t bytes_to_encrypt;
+ size_t bytes_encrypted;
+ gss_ctx_id_t gctx = conn->gctx;
+
+ /*
+ * When we get a failure, we must not tell the caller we have successfully
+ * transmitted everything, else it won't retry. Hence a "success"
+ * (positive) return value must only count source bytes corresponding to
+ * fully-transmitted encrypted packets. The amount of source data
+ * corresponding to the current partly-transmitted packet is remembered in
+ * PqGSSSendConsumed. On a retry, the caller *must* be sending that data
+ * again, so if it offers a len less than that, something is wrong.
+ */
+ if (len < PqGSSSendConsumed)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ "GSSAPI caller failed to retransmit all data needing to be retried\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* Discount whatever source data we already encrypted. */
+ bytes_to_encrypt = len - PqGSSSendConsumed;
+ bytes_encrypted = PqGSSSendConsumed;
+
+ /*
+ * Loop through encrypting data and sending it out until it's all done or
+ * pqsecure_raw_write() complains (which would likely mean that the socket
+ * is non-blocking and the requested send() would block, or there was some
+ * kind of actual error).
+ */
+ while (bytes_to_encrypt || PqGSSSendLength)
+ {
+ int conf_state = 0;
+ uint32 netlen;
+
+ /*
+ * Check if we have data in the encrypted output buffer that needs to
+ * be sent (possibly left over from a previous call), and if so, try
+ * to send it. If we aren't able to, return that fact back up to the
+ * caller.
+ */
+ if (PqGSSSendLength)
+ {
+ ssize_t ret;
+ ssize_t amount = PqGSSSendLength - PqGSSSendNext;
+
+ ret = pqsecure_raw_write(conn, PqGSSSendBuffer + PqGSSSendNext, amount);
+ if (ret <= 0)
+ {
+ /*
+ * Report any previously-sent data; if there was none, reflect
+ * the pqsecure_raw_write result up to our caller. When there
+ * was some, we're effectively assuming that any interesting
+ * failure condition will recur on the next try.
+ */
+ if (bytes_sent)
+ return bytes_sent;
+ return ret;
+ }
+
+ /*
+ * Check if this was a partial write, and if so, move forward that
+ * far in our buffer and try again.
+ */
+ if (ret != amount)
+ {
+ PqGSSSendNext += ret;
+ continue;
+ }
+
+ /* We've successfully sent whatever data was in that packet. */
+ bytes_sent += PqGSSSendConsumed;
+
+ /* All encrypted data was sent, our buffer is empty now. */
+ PqGSSSendLength = PqGSSSendNext = PqGSSSendConsumed = 0;
+ }
+
+ /*
+ * Check if there are any bytes left to encrypt. If not, we're done.
+ */
+ if (!bytes_to_encrypt)
+ break;
+
+ /*
+ * Check how much we are being asked to send, if it's too much, then
+ * we will have to loop and possibly be called multiple times to get
+ * through all the data.
+ */
+ if (bytes_to_encrypt > PqGSSMaxPktSize)
+ input.length = PqGSSMaxPktSize;
+ else
+ input.length = bytes_to_encrypt;
+
+ input.value = (char *) ptr + bytes_encrypted;
+
+ output.value = NULL;
+ output.length = 0;
+
+ /*
+ * Create the next encrypted packet. Any failure here is considered a
+ * hard failure, so we return -1 even if bytes_sent > 0.
+ */
+ major = gss_wrap(&minor, gctx, 1, GSS_C_QOP_DEFAULT,
+ &input, &conf_state, &output);
+ if (major != GSS_S_COMPLETE)
+ {
+ pg_GSS_error(libpq_gettext("GSSAPI wrap error"), conn, major, minor);
+ errno = EIO; /* for lack of a better idea */
+ goto cleanup;
+ }
+
+ if (conf_state == 0)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("outgoing GSSAPI message would not use confidentiality\n"));
+ errno = EIO; /* for lack of a better idea */
+ goto cleanup;
+ }
+
+ if (output.length > PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32))
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("client tried to send oversize GSSAPI packet (%zu > %zu)\n"),
+ (size_t) output.length,
+ PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32));
+ errno = EIO; /* for lack of a better idea */
+ goto cleanup;
+ }
+
+ bytes_encrypted += input.length;
+ bytes_to_encrypt -= input.length;
+ PqGSSSendConsumed += input.length;
+
+ /* 4 network-order bytes of length, then payload */
+ netlen = htonl(output.length);
+ memcpy(PqGSSSendBuffer + PqGSSSendLength, &netlen, sizeof(uint32));
+ PqGSSSendLength += sizeof(uint32);
+
+ memcpy(PqGSSSendBuffer + PqGSSSendLength, output.value, output.length);
+ PqGSSSendLength += output.length;
+
+ /* Release buffer storage allocated by GSSAPI */
+ gss_release_buffer(&minor, &output);
+ }
+
+ /* If we get here, our counters should all match up. */
+ Assert(bytes_sent == len);
+ Assert(bytes_sent == bytes_encrypted);
+
+ ret = bytes_sent;
+
+cleanup:
+ /* Release GSSAPI buffer storage, if we didn't already */
+ if (output.value != NULL)
+ gss_release_buffer(&minor, &output);
+ return ret;
+}
+
+/*
+ * Read up to len bytes of data into ptr from a GSSAPI-encrypted connection.
+ *
+ * The connection must be already set up for GSSAPI encryption (i.e., GSSAPI
+ * transport negotiation is complete).
+ *
+ * Returns the number of data bytes read, or on failure, returns -1
+ * with errno set appropriately. If the errno indicates a non-retryable
+ * error, a message is put into conn->errorMessage. For retryable errors,
+ * caller should call again once the socket is ready.
+ */
+ssize_t
+pg_GSS_read(PGconn *conn, void *ptr, size_t len)
+{
+ OM_uint32 major,
+ minor;
+ gss_buffer_desc input = GSS_C_EMPTY_BUFFER,
+ output = GSS_C_EMPTY_BUFFER;
+ ssize_t ret;
+ size_t bytes_returned = 0;
+ gss_ctx_id_t gctx = conn->gctx;
+
+ /*
+ * The plan here is to read one incoming encrypted packet into
+ * PqGSSRecvBuffer, decrypt it into PqGSSResultBuffer, and then dole out
+ * data from there to the caller. When we exhaust the current input
+ * packet, read another.
+ */
+ while (bytes_returned < len)
+ {
+ int conf_state = 0;
+
+ /* Check if we have data in our buffer that we can return immediately */
+ if (PqGSSResultNext < PqGSSResultLength)
+ {
+ size_t bytes_in_buffer = PqGSSResultLength - PqGSSResultNext;
+ size_t bytes_to_copy = Min(bytes_in_buffer, len - bytes_returned);
+
+ /*
+ * Copy the data from our result buffer into the caller's buffer,
+ * at the point where we last left off filling their buffer.
+ */
+ memcpy((char *) ptr + bytes_returned, PqGSSResultBuffer + PqGSSResultNext, bytes_to_copy);
+ PqGSSResultNext += bytes_to_copy;
+ bytes_returned += bytes_to_copy;
+
+ /*
+ * At this point, we've either filled the caller's buffer or
+ * emptied our result buffer. Either way, return to caller. In
+ * the second case, we could try to read another encrypted packet,
+ * but the odds are good that there isn't one available. (If this
+ * isn't true, we chose too small a max packet size.) In any
+ * case, there's no harm letting the caller process the data we've
+ * already returned.
+ */
+ break;
+ }
+
+ /* Result buffer is empty, so reset buffer pointers */
+ PqGSSResultLength = PqGSSResultNext = 0;
+
+ /*
+ * Because we chose above to return immediately as soon as we emit
+ * some data, bytes_returned must be zero at this point. Therefore
+ * the failure exits below can just return -1 without worrying about
+ * whether we already emitted some data.
+ */
+ Assert(bytes_returned == 0);
+
+ /*
+ * At this point, our result buffer is empty with more bytes being
+ * requested to be read. We are now ready to load the next packet and
+ * decrypt it (entirely) into our result buffer.
+ */
+
+ /* Collect the length if we haven't already */
+ if (PqGSSRecvLength < sizeof(uint32))
+ {
+ ret = pqsecure_raw_read(conn, PqGSSRecvBuffer + PqGSSRecvLength,
+ sizeof(uint32) - PqGSSRecvLength);
+
+ /* If ret <= 0, pqsecure_raw_read already set the correct errno */
+ if (ret <= 0)
+ return ret;
+
+ PqGSSRecvLength += ret;
+
+ /* If we still haven't got the length, return to the caller */
+ if (PqGSSRecvLength < sizeof(uint32))
+ {
+ errno = EWOULDBLOCK;
+ return -1;
+ }
+ }
+
+ /* Decode the packet length and check for overlength packet */
+ input.length = ntohl(*(uint32 *) PqGSSRecvBuffer);
+
+ if (input.length > PQ_GSS_RECV_BUFFER_SIZE - sizeof(uint32))
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("oversize GSSAPI packet sent by the server (%zu > %zu)\n"),
+ (size_t) input.length,
+ PQ_GSS_RECV_BUFFER_SIZE - sizeof(uint32));
+ errno = EIO; /* for lack of a better idea */
+ return -1;
+ }
+
+ /*
+ * Read as much of the packet as we are able to on this call into
+ * wherever we left off from the last time we were called.
+ */
+ ret = pqsecure_raw_read(conn, PqGSSRecvBuffer + PqGSSRecvLength,
+ input.length - (PqGSSRecvLength - sizeof(uint32)));
+ /* If ret <= 0, pqsecure_raw_read already set the correct errno */
+ if (ret <= 0)
+ return ret;
+
+ PqGSSRecvLength += ret;
+
+ /* If we don't yet have the whole packet, return to the caller */
+ if (PqGSSRecvLength - sizeof(uint32) < input.length)
+ {
+ errno = EWOULDBLOCK;
+ return -1;
+ }
+
+ /*
+ * We now have the full packet and we can perform the decryption and
+ * refill our result buffer, then loop back up to pass data back to
+ * the caller. Note that error exits below here must take care of
+ * releasing the gss output buffer.
+ */
+ output.value = NULL;
+ output.length = 0;
+ input.value = PqGSSRecvBuffer + sizeof(uint32);
+
+ major = gss_unwrap(&minor, gctx, &input, &output, &conf_state, NULL);
+ if (major != GSS_S_COMPLETE)
+ {
+ pg_GSS_error(libpq_gettext("GSSAPI unwrap error"), conn,
+ major, minor);
+ ret = -1;
+ errno = EIO; /* for lack of a better idea */
+ goto cleanup;
+ }
+
+ if (conf_state == 0)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("incoming GSSAPI message did not use confidentiality\n"));
+ ret = -1;
+ errno = EIO; /* for lack of a better idea */
+ goto cleanup;
+ }
+
+ memcpy(PqGSSResultBuffer, output.value, output.length);
+ PqGSSResultLength = output.length;
+
+ /* Our receive buffer is now empty, reset it */
+ PqGSSRecvLength = 0;
+
+ /* Release buffer storage allocated by GSSAPI */
+ gss_release_buffer(&minor, &output);
+ }
+
+ ret = bytes_returned;
+
+cleanup:
+ /* Release GSSAPI buffer storage, if we didn't already */
+ if (output.value != NULL)
+ gss_release_buffer(&minor, &output);
+ return ret;
+}
+
+/*
+ * Simple wrapper for reading from pqsecure_raw_read.
+ *
+ * This takes the same arguments as pqsecure_raw_read, plus an output parameter
+ * to return the number of bytes read. This handles if blocking would occur and
+ * if we detect EOF on the connection.
+ */
+static PostgresPollingStatusType
+gss_read(PGconn *conn, void *recv_buffer, size_t length, ssize_t *ret)
+{
+ *ret = pqsecure_raw_read(conn, recv_buffer, length);
+ if (*ret < 0)
+ {
+ if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
+ return PGRES_POLLING_READING;
+ else
+ return PGRES_POLLING_FAILED;
+ }
+
+ /* Check for EOF */
+ if (*ret == 0)
+ {
+ int result = pqReadReady(conn);
+
+ if (result < 0)
+ return PGRES_POLLING_FAILED;
+
+ if (!result)
+ return PGRES_POLLING_READING;
+
+ *ret = pqsecure_raw_read(conn, recv_buffer, length);
+ if (*ret < 0)
+ {
+ if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
+ return PGRES_POLLING_READING;
+ else
+ return PGRES_POLLING_FAILED;
+ }
+ if (*ret == 0)
+ return PGRES_POLLING_FAILED;
+ }
+
+ return PGRES_POLLING_OK;
+}
+
+/*
+ * Negotiate GSSAPI transport for a connection. When complete, returns
+ * PGRES_POLLING_OK. Will return PGRES_POLLING_READING or
+ * PGRES_POLLING_WRITING as appropriate whenever it would block, and
+ * PGRES_POLLING_FAILED if transport could not be negotiated.
+ */
+PostgresPollingStatusType
+pqsecure_open_gss(PGconn *conn)
+{
+ ssize_t ret;
+ OM_uint32 major,
+ minor;
+ uint32 netlen;
+ PostgresPollingStatusType result;
+ gss_buffer_desc input = GSS_C_EMPTY_BUFFER,
+ output = GSS_C_EMPTY_BUFFER;
+
+ /*
+ * If first time through for this connection, allocate buffers and
+ * initialize state variables. By malloc'ing the buffers separately, we
+ * ensure that they are sufficiently aligned for the length-word accesses
+ * that we do in some places in this file.
+ */
+ if (PqGSSSendBuffer == NULL)
+ {
+ PqGSSSendBuffer = malloc(PQ_GSS_SEND_BUFFER_SIZE);
+ PqGSSRecvBuffer = malloc(PQ_GSS_RECV_BUFFER_SIZE);
+ PqGSSResultBuffer = malloc(PQ_GSS_RECV_BUFFER_SIZE);
+ if (!PqGSSSendBuffer || !PqGSSRecvBuffer || !PqGSSResultBuffer)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("out of memory\n"));
+ return PGRES_POLLING_FAILED;
+ }
+ PqGSSSendLength = PqGSSSendNext = PqGSSSendConsumed = 0;
+ PqGSSRecvLength = PqGSSResultLength = PqGSSResultNext = 0;
+ }
+
+ /*
+ * Check if we have anything to send from a prior call and if so, send it.
+ */
+ if (PqGSSSendLength)
+ {
+ ssize_t amount = PqGSSSendLength - PqGSSSendNext;
+
+ ret = pqsecure_raw_write(conn, PqGSSSendBuffer + PqGSSSendNext, amount);
+ if (ret < 0)
+ {
+ if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
+ return PGRES_POLLING_WRITING;
+ else
+ return PGRES_POLLING_FAILED;
+ }
+
+ if (ret < amount)
+ {
+ PqGSSSendNext += ret;
+ return PGRES_POLLING_WRITING;
+ }
+
+ PqGSSSendLength = PqGSSSendNext = 0;
+ }
+
+ /*
+ * Client sends first, and sending creates a context, therefore this will
+ * be false the first time through, and then when we get called again we
+ * will check for incoming data.
+ */
+ if (conn->gctx)
+ {
+ /* Process any incoming data we might have */
+
+ /* See if we are still trying to get the length */
+ if (PqGSSRecvLength < sizeof(uint32))
+ {
+ /* Attempt to get the length first */
+ result = gss_read(conn, PqGSSRecvBuffer + PqGSSRecvLength, sizeof(uint32) - PqGSSRecvLength, &ret);
+ if (result != PGRES_POLLING_OK)
+ return result;
+
+ PqGSSRecvLength += ret;
+
+ if (PqGSSRecvLength < sizeof(uint32))
+ return PGRES_POLLING_READING;
+ }
+
+ /*
+ * Check if we got an error packet
+ *
+ * This is safe to do because we shouldn't ever get a packet over 8192
+ * and therefore the actual length bytes, being that they are in
+ * network byte order, for any real packet will start with two zero
+ * bytes.
+ */
+ if (PqGSSRecvBuffer[0] == 'E')
+ {
+ /*
+ * For an error packet during startup, we don't get a length, so
+ * simply read as much as we can fit into our buffer (as a string,
+ * so leave a spot at the end for a NULL byte too) and report that
+ * back to the caller.
+ */
+ result = gss_read(conn, PqGSSRecvBuffer + PqGSSRecvLength, PQ_GSS_RECV_BUFFER_SIZE - PqGSSRecvLength - 1, &ret);
+ if (result != PGRES_POLLING_OK)
+ return result;
+
+ PqGSSRecvLength += ret;
+
+ printfPQExpBuffer(&conn->errorMessage, "%s\n", PqGSSRecvBuffer + 1);
+
+ return PGRES_POLLING_FAILED;
+ }
+
+ /*
+ * We should have the whole length at this point, so pull it out and
+ * then read whatever we have left of the packet
+ */
+
+ /* Get the length and check for over-length packet */
+ input.length = ntohl(*(uint32 *) PqGSSRecvBuffer);
+ if (input.length > PQ_GSS_RECV_BUFFER_SIZE - sizeof(uint32))
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("oversize GSSAPI packet sent by the server (%zu > %zu)\n"),
+ (size_t) input.length,
+ PQ_GSS_RECV_BUFFER_SIZE - sizeof(uint32));
+ return PGRES_POLLING_FAILED;
+ }
+
+ /*
+ * Read as much of the packet as we are able to on this call into
+ * wherever we left off from the last time we were called.
+ */
+ result = gss_read(conn, PqGSSRecvBuffer + PqGSSRecvLength,
+ input.length - (PqGSSRecvLength - sizeof(uint32)), &ret);
+ if (result != PGRES_POLLING_OK)
+ return result;
+
+ PqGSSRecvLength += ret;
+
+ /*
+ * If we got less than the rest of the packet then we need to return
+ * and be called again.
+ */
+ if (PqGSSRecvLength - sizeof(uint32) < input.length)
+ return PGRES_POLLING_READING;
+
+ input.value = PqGSSRecvBuffer + sizeof(uint32);
+ }
+
+ /* Load the service name (no-op if already done */
+ ret = pg_GSS_load_servicename(conn);
+ if (ret != STATUS_OK)
+ return PGRES_POLLING_FAILED;
+
+ /*
+ * Call GSS init context, either with an empty input, or with a complete
+ * packet from the server.
+ */
+ major = gss_init_sec_context(&minor, conn->gcred, &conn->gctx,
+ conn->gtarg_nam, GSS_C_NO_OID,
+ GSS_REQUIRED_FLAGS, 0, 0, &input, NULL,
+ &output, NULL, NULL);
+
+ /* GSS Init Sec Context uses the whole packet, so clear it */
+ PqGSSRecvLength = 0;
+
+ if (GSS_ERROR(major))
+ {
+ pg_GSS_error(libpq_gettext("could not initiate GSSAPI security context"),
+ conn, major, minor);
+ return PGRES_POLLING_FAILED;
+ }
+
+ if (output.length == 0)
+ {
+ /*
+ * We're done - hooray! Set flag to tell the low-level I/O routines
+ * to do GSS wrapping/unwrapping.
+ */
+ conn->gssenc = true;
+
+ /* Clean up */
+ gss_release_cred(&minor, &conn->gcred);
+ conn->gcred = GSS_C_NO_CREDENTIAL;
+ gss_release_buffer(&minor, &output);
+
+ /*
+ * Determine the max packet size which will fit in our buffer, after
+ * accounting for the length. pg_GSS_write will need this.
+ */
+ major = gss_wrap_size_limit(&minor, conn->gctx, 1, GSS_C_QOP_DEFAULT,
+ PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32),
+ &PqGSSMaxPktSize);
+
+ if (GSS_ERROR(major))
+ {
+ pg_GSS_error(libpq_gettext("GSSAPI size check error"), conn,
+ major, minor);
+ return PGRES_POLLING_FAILED;
+ }
+
+ return PGRES_POLLING_OK;
+ }
+
+ /* Must have output.length > 0 */
+ if (output.length > PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32))
+ {
+ pg_GSS_error(libpq_gettext("GSSAPI context establishment error"),
+ conn, major, minor);
+ gss_release_buffer(&minor, &output);
+ return PGRES_POLLING_FAILED;
+ }
+
+ /* Queue the token for writing */
+ netlen = htonl(output.length);
+
+ memcpy(PqGSSSendBuffer, (char *) &netlen, sizeof(uint32));
+ PqGSSSendLength += sizeof(uint32);
+
+ memcpy(PqGSSSendBuffer + PqGSSSendLength, output.value, output.length);
+ PqGSSSendLength += output.length;
+
+ /* We don't bother with PqGSSSendConsumed here */
+
+ /* Release buffer storage allocated by GSSAPI */
+ gss_release_buffer(&minor, &output);
+
+ /* Ask to be called again to write data */
+ return PGRES_POLLING_WRITING;
+}
+
+/*
+ * GSSAPI Information functions.
+ */
+
+/*
+ * Return the GSSAPI Context itself.
+ */
+void *
+PQgetgssctx(PGconn *conn)
+{
+ if (!conn)
+ return NULL;
+
+ return conn->gctx;
+}
+
+/*
+ * Return true if GSSAPI encryption is in use.
+ */
+int
+PQgssEncInUse(PGconn *conn)
+{
+ if (!conn || !conn->gctx)
+ return 0;
+
+ return conn->gssenc;
+}
diff --git a/src/interfaces/libpq/fe-secure-openssl.c b/src/interfaces/libpq/fe-secure-openssl.c
new file mode 100644
index 0000000..d609a38
--- /dev/null
+++ b/src/interfaces/libpq/fe-secure-openssl.c
@@ -0,0 +1,1790 @@
+/*-------------------------------------------------------------------------
+ *
+ * fe-secure-openssl.c
+ * OpenSSL support
+ *
+ *
+ * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/interfaces/libpq/fe-secure-openssl.c
+ *
+ * NOTES
+ *
+ * We don't provide informational callbacks here (like
+ * info_cb() in be-secure.c), since there's no good mechanism to
+ * display such information to the user.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres_fe.h"
+
+#include <signal.h>
+#include <fcntl.h>
+#include <ctype.h>
+
+#include "libpq-fe.h"
+#include "fe-auth.h"
+#include "fe-secure-common.h"
+#include "libpq-int.h"
+#include "common/openssl.h"
+
+#ifdef WIN32
+#include "win32.h"
+#else
+#include <sys/socket.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#ifdef HAVE_NETINET_TCP_H
+#include <netinet/tcp.h>
+#endif
+#include <arpa/inet.h>
+#endif
+
+#include <sys/stat.h>
+
+#ifdef ENABLE_THREAD_SAFETY
+#ifdef WIN32
+#include "pthread-win32.h"
+#else
+#include <pthread.h>
+#endif
+#endif
+
+#include <openssl/ssl.h>
+#include <openssl/conf.h>
+#ifdef USE_SSL_ENGINE
+#include <openssl/engine.h>
+#endif
+#include <openssl/x509v3.h>
+
+static int verify_cb(int ok, X509_STORE_CTX *ctx);
+static int openssl_verify_peer_name_matches_certificate_name(PGconn *conn,
+ ASN1_STRING *name,
+ char **store_name);
+static void destroy_ssl_system(void);
+static int initialize_SSL(PGconn *conn);
+static PostgresPollingStatusType open_client_SSL(PGconn *);
+static char *SSLerrmessage(unsigned long ecode);
+static void SSLerrfree(char *buf);
+static int PQssl_passwd_cb(char *buf, int size, int rwflag, void *userdata);
+
+static int my_sock_read(BIO *h, char *buf, int size);
+static int my_sock_write(BIO *h, const char *buf, int size);
+static BIO_METHOD *my_BIO_s_socket(void);
+static int my_SSL_set_fd(PGconn *conn, int fd);
+
+
+static bool pq_init_ssl_lib = true;
+static bool pq_init_crypto_lib = true;
+
+static bool ssl_lib_initialized = false;
+
+#ifdef ENABLE_THREAD_SAFETY
+static long ssl_open_connections = 0;
+
+#ifndef WIN32
+static pthread_mutex_t ssl_config_mutex = PTHREAD_MUTEX_INITIALIZER;
+#else
+static pthread_mutex_t ssl_config_mutex = NULL;
+static long win32_ssl_create_mutex = 0;
+#endif
+#endif /* ENABLE_THREAD_SAFETY */
+
+static PQsslKeyPassHook_OpenSSL_type PQsslKeyPassHook = NULL;
+static int ssl_protocol_version_to_openssl(const char *protocol);
+
+/* ------------------------------------------------------------ */
+/* Procedures common to all secure sessions */
+/* ------------------------------------------------------------ */
+
+void
+pgtls_init_library(bool do_ssl, int do_crypto)
+{
+#ifdef ENABLE_THREAD_SAFETY
+
+ /*
+ * Disallow changing the flags while we have open connections, else we'd
+ * get completely confused.
+ */
+ if (ssl_open_connections != 0)
+ return;
+#endif
+
+ pq_init_ssl_lib = do_ssl;
+ pq_init_crypto_lib = do_crypto;
+}
+
+PostgresPollingStatusType
+pgtls_open_client(PGconn *conn)
+{
+ /* First time through? */
+ if (conn->ssl == NULL)
+ {
+ /*
+ * Create a connection-specific SSL object, and load client
+ * certificate, private key, and trusted CA certs.
+ */
+ if (initialize_SSL(conn) != 0)
+ {
+ /* initialize_SSL already put a message in conn->errorMessage */
+ pgtls_close(conn);
+ return PGRES_POLLING_FAILED;
+ }
+ }
+
+ /* Begin or continue the actual handshake */
+ return open_client_SSL(conn);
+}
+
+ssize_t
+pgtls_read(PGconn *conn, void *ptr, size_t len)
+{
+ ssize_t n;
+ int result_errno = 0;
+ char sebuf[PG_STRERROR_R_BUFLEN];
+ int err;
+ unsigned long ecode;
+
+rloop:
+
+ /*
+ * Prepare to call SSL_get_error() by clearing thread's OpenSSL error
+ * queue. In general, the current thread's error queue must be empty
+ * before the TLS/SSL I/O operation is attempted, or SSL_get_error() will
+ * not work reliably. Since the possibility exists that other OpenSSL
+ * clients running in the same thread but not under our control will fail
+ * to call ERR_get_error() themselves (after their own I/O operations),
+ * pro-actively clear the per-thread error queue now.
+ */
+ SOCK_ERRNO_SET(0);
+ ERR_clear_error();
+ n = SSL_read(conn->ssl, ptr, len);
+ err = SSL_get_error(conn->ssl, n);
+
+ /*
+ * Other clients of OpenSSL may fail to call ERR_get_error(), but we
+ * always do, so as to not cause problems for OpenSSL clients that don't
+ * call ERR_clear_error() defensively. Be sure that this happens by
+ * calling now. SSL_get_error() relies on the OpenSSL per-thread error
+ * queue being intact, so this is the earliest possible point
+ * ERR_get_error() may be called.
+ */
+ ecode = (err != SSL_ERROR_NONE || n < 0) ? ERR_get_error() : 0;
+ switch (err)
+ {
+ case SSL_ERROR_NONE:
+ if (n < 0)
+ {
+ /* Not supposed to happen, so we don't translate the msg */
+ printfPQExpBuffer(&conn->errorMessage,
+ "SSL_read failed but did not provide error information\n");
+ /* assume the connection is broken */
+ result_errno = ECONNRESET;
+ }
+ break;
+ case SSL_ERROR_WANT_READ:
+ n = 0;
+ break;
+ case SSL_ERROR_WANT_WRITE:
+
+ /*
+ * Returning 0 here would cause caller to wait for read-ready,
+ * which is not correct since what SSL wants is wait for
+ * write-ready. The former could get us stuck in an infinite
+ * wait, so don't risk it; busy-loop instead.
+ */
+ goto rloop;
+ case SSL_ERROR_SYSCALL:
+ if (n < 0)
+ {
+ result_errno = SOCK_ERRNO;
+ if (result_errno == EPIPE ||
+ result_errno == 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"));
+ else
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("SSL SYSCALL error: %s\n"),
+ SOCK_STRERROR(result_errno,
+ sebuf, sizeof(sebuf)));
+ }
+ else
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("SSL SYSCALL error: EOF detected\n"));
+ /* assume the connection is broken */
+ result_errno = ECONNRESET;
+ n = -1;
+ }
+ break;
+ case SSL_ERROR_SSL:
+ {
+ char *errm = SSLerrmessage(ecode);
+
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("SSL error: %s\n"), errm);
+ SSLerrfree(errm);
+ /* assume the connection is broken */
+ result_errno = ECONNRESET;
+ n = -1;
+ break;
+ }
+ case SSL_ERROR_ZERO_RETURN:
+
+ /*
+ * Per OpenSSL documentation, this error code is only returned for
+ * a clean connection closure, so we should not report it as a
+ * server crash.
+ */
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("SSL connection has been closed unexpectedly\n"));
+ result_errno = ECONNRESET;
+ n = -1;
+ break;
+ default:
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("unrecognized SSL error code: %d\n"),
+ err);
+ /* assume the connection is broken */
+ result_errno = ECONNRESET;
+ n = -1;
+ break;
+ }
+
+ /* ensure we return the intended errno to caller */
+ SOCK_ERRNO_SET(result_errno);
+
+ return n;
+}
+
+bool
+pgtls_read_pending(PGconn *conn)
+{
+ return SSL_pending(conn->ssl) > 0;
+}
+
+ssize_t
+pgtls_write(PGconn *conn, const void *ptr, size_t len)
+{
+ ssize_t n;
+ int result_errno = 0;
+ char sebuf[PG_STRERROR_R_BUFLEN];
+ int err;
+ unsigned long ecode;
+
+ SOCK_ERRNO_SET(0);
+ ERR_clear_error();
+ n = SSL_write(conn->ssl, ptr, len);
+ err = SSL_get_error(conn->ssl, n);
+ ecode = (err != SSL_ERROR_NONE || n < 0) ? ERR_get_error() : 0;
+ switch (err)
+ {
+ case SSL_ERROR_NONE:
+ if (n < 0)
+ {
+ /* Not supposed to happen, so we don't translate the msg */
+ printfPQExpBuffer(&conn->errorMessage,
+ "SSL_write failed but did not provide error information\n");
+ /* assume the connection is broken */
+ result_errno = ECONNRESET;
+ }
+ break;
+ case SSL_ERROR_WANT_READ:
+
+ /*
+ * Returning 0 here causes caller to wait for write-ready, which
+ * is not really the right thing, but it's the best we can do.
+ */
+ n = 0;
+ break;
+ case SSL_ERROR_WANT_WRITE:
+ n = 0;
+ break;
+ case SSL_ERROR_SYSCALL:
+ if (n < 0)
+ {
+ result_errno = SOCK_ERRNO;
+ if (result_errno == EPIPE || result_errno == 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"));
+ else
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("SSL SYSCALL error: %s\n"),
+ SOCK_STRERROR(result_errno,
+ sebuf, sizeof(sebuf)));
+ }
+ else
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("SSL SYSCALL error: EOF detected\n"));
+ /* assume the connection is broken */
+ result_errno = ECONNRESET;
+ n = -1;
+ }
+ break;
+ case SSL_ERROR_SSL:
+ {
+ char *errm = SSLerrmessage(ecode);
+
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("SSL error: %s\n"), errm);
+ SSLerrfree(errm);
+ /* assume the connection is broken */
+ result_errno = ECONNRESET;
+ n = -1;
+ break;
+ }
+ case SSL_ERROR_ZERO_RETURN:
+
+ /*
+ * Per OpenSSL documentation, this error code is only returned for
+ * a clean connection closure, so we should not report it as a
+ * server crash.
+ */
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("SSL connection has been closed unexpectedly\n"));
+ result_errno = ECONNRESET;
+ n = -1;
+ break;
+ default:
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("unrecognized SSL error code: %d\n"),
+ err);
+ /* assume the connection is broken */
+ result_errno = ECONNRESET;
+ n = -1;
+ break;
+ }
+
+ /* ensure we return the intended errno to caller */
+ SOCK_ERRNO_SET(result_errno);
+
+ return n;
+}
+
+#ifdef HAVE_X509_GET_SIGNATURE_NID
+char *
+pgtls_get_peer_certificate_hash(PGconn *conn, size_t *len)
+{
+ X509 *peer_cert;
+ const EVP_MD *algo_type;
+ unsigned char hash[EVP_MAX_MD_SIZE]; /* size for SHA-512 */
+ unsigned int hash_size;
+ int algo_nid;
+ char *cert_hash;
+
+ *len = 0;
+
+ if (!conn->peer)
+ return NULL;
+
+ peer_cert = conn->peer;
+
+ /*
+ * Get the signature algorithm of the certificate to determine the hash
+ * algorithm to use for the result.
+ */
+ if (!OBJ_find_sigid_algs(X509_get_signature_nid(peer_cert),
+ &algo_nid, NULL))
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("could not determine server certificate signature algorithm\n"));
+ return NULL;
+ }
+
+ /*
+ * The TLS server's certificate bytes need to be hashed with SHA-256 if
+ * its signature algorithm is MD5 or SHA-1 as per RFC 5929
+ * (https://tools.ietf.org/html/rfc5929#section-4.1). If something else
+ * is used, the same hash as the signature algorithm is used.
+ */
+ switch (algo_nid)
+ {
+ case NID_md5:
+ case NID_sha1:
+ algo_type = EVP_sha256();
+ break;
+ default:
+ algo_type = EVP_get_digestbynid(algo_nid);
+ if (algo_type == NULL)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("could not find digest for NID %s\n"),
+ OBJ_nid2sn(algo_nid));
+ return NULL;
+ }
+ break;
+ }
+
+ if (!X509_digest(peer_cert, algo_type, hash, &hash_size))
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("could not generate peer certificate hash\n"));
+ return NULL;
+ }
+
+ /* save result */
+ cert_hash = malloc(hash_size);
+ if (cert_hash == NULL)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("out of memory\n"));
+ return NULL;
+ }
+ memcpy(cert_hash, hash, hash_size);
+ *len = hash_size;
+
+ return cert_hash;
+}
+#endif /* HAVE_X509_GET_SIGNATURE_NID */
+
+/* ------------------------------------------------------------ */
+/* OpenSSL specific code */
+/* ------------------------------------------------------------ */
+
+/*
+ * Certificate verification callback
+ *
+ * This callback allows us to log intermediate problems during
+ * verification, but there doesn't seem to be a clean way to get
+ * our PGconn * structure. So we can't log anything!
+ *
+ * This callback also allows us to override the default acceptance
+ * criteria (e.g., accepting self-signed or expired certs), but
+ * for now we accept the default checks.
+ */
+static int
+verify_cb(int ok, X509_STORE_CTX *ctx)
+{
+ return ok;
+}
+
+
+/*
+ * OpenSSL-specific wrapper around
+ * pq_verify_peer_name_matches_certificate_name(), converting the ASN1_STRING
+ * into a plain C string.
+ */
+static int
+openssl_verify_peer_name_matches_certificate_name(PGconn *conn, ASN1_STRING *name_entry,
+ char **store_name)
+{
+ int len;
+ const unsigned char *namedata;
+
+ /* Should not happen... */
+ if (name_entry == NULL)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("SSL certificate's name entry is missing\n"));
+ return -1;
+ }
+
+ /*
+ * GEN_DNS can be only IA5String, equivalent to US ASCII.
+ */
+#ifdef HAVE_ASN1_STRING_GET0_DATA
+ namedata = ASN1_STRING_get0_data(name_entry);
+#else
+ namedata = ASN1_STRING_data(name_entry);
+#endif
+ len = ASN1_STRING_length(name_entry);
+
+ /* OK to cast from unsigned to plain char, since it's all ASCII. */
+ return pq_verify_peer_name_matches_certificate_name(conn, (const char *) namedata, len, store_name);
+}
+
+/*
+ * Verify that the server certificate matches the hostname we connected to.
+ *
+ * The certificate's Common Name and Subject Alternative Names are considered.
+ */
+int
+pgtls_verify_peer_name_matches_certificate_guts(PGconn *conn,
+ int *names_examined,
+ char **first_name)
+{
+ STACK_OF(GENERAL_NAME) * peer_san;
+ int i;
+ int rc = 0;
+
+ /*
+ * First, get the Subject Alternative Names (SANs) from the certificate,
+ * and compare them against the originally given hostname.
+ */
+ peer_san = (STACK_OF(GENERAL_NAME) *)
+ X509_get_ext_d2i(conn->peer, NID_subject_alt_name, NULL, NULL);
+
+ if (peer_san)
+ {
+ int san_len = sk_GENERAL_NAME_num(peer_san);
+
+ for (i = 0; i < san_len; i++)
+ {
+ const GENERAL_NAME *name = sk_GENERAL_NAME_value(peer_san, i);
+
+ if (name->type == GEN_DNS)
+ {
+ char *alt_name;
+
+ (*names_examined)++;
+ rc = openssl_verify_peer_name_matches_certificate_name(conn,
+ name->d.dNSName,
+ &alt_name);
+
+ if (alt_name)
+ {
+ if (!*first_name)
+ *first_name = alt_name;
+ else
+ free(alt_name);
+ }
+ }
+ if (rc != 0)
+ break;
+ }
+ sk_GENERAL_NAME_pop_free(peer_san, GENERAL_NAME_free);
+ }
+
+ /*
+ * If there is no subjectAltName extension of type dNSName, check the
+ * Common Name.
+ *
+ * (Per RFC 2818 and RFC 6125, if the subjectAltName extension of type
+ * dNSName is present, the CN must be ignored.)
+ */
+ if (*names_examined == 0)
+ {
+ X509_NAME *subject_name;
+
+ subject_name = X509_get_subject_name(conn->peer);
+ if (subject_name != NULL)
+ {
+ int cn_index;
+
+ cn_index = X509_NAME_get_index_by_NID(subject_name,
+ NID_commonName, -1);
+ if (cn_index >= 0)
+ {
+ (*names_examined)++;
+ rc = openssl_verify_peer_name_matches_certificate_name(conn,
+ X509_NAME_ENTRY_get_data(X509_NAME_get_entry(subject_name, cn_index)),
+ first_name);
+ }
+ }
+ }
+
+ return rc;
+}
+
+#if defined(ENABLE_THREAD_SAFETY) && defined(HAVE_CRYPTO_LOCK)
+/*
+ * Callback functions for OpenSSL internal locking. (OpenSSL 1.1.0
+ * does its own locking, and doesn't need these anymore. The
+ * CRYPTO_lock() function was removed in 1.1.0, when the callbacks
+ * were made obsolete, so we assume that if CRYPTO_lock() exists,
+ * the callbacks are still required.)
+ */
+
+static unsigned long
+pq_threadidcallback(void)
+{
+ /*
+ * This is not standards-compliant. pthread_self() returns pthread_t, and
+ * shouldn't be cast to unsigned long, but CRYPTO_set_id_callback requires
+ * it, so we have to do it.
+ */
+ return (unsigned long) pthread_self();
+}
+
+static pthread_mutex_t *pq_lockarray;
+
+static void
+pq_lockingcallback(int mode, int n, const char *file, int line)
+{
+ if (mode & CRYPTO_LOCK)
+ {
+ if (pthread_mutex_lock(&pq_lockarray[n]))
+ PGTHREAD_ERROR("failed to lock mutex");
+ }
+ else
+ {
+ if (pthread_mutex_unlock(&pq_lockarray[n]))
+ PGTHREAD_ERROR("failed to unlock mutex");
+ }
+}
+#endif /* ENABLE_THREAD_SAFETY && HAVE_CRYPTO_LOCK */
+
+/*
+ * Initialize SSL library.
+ *
+ * In threadsafe mode, this includes setting up libcrypto callback functions
+ * to do thread locking.
+ *
+ * If the caller has told us (through PQinitOpenSSL) that he's taking care
+ * of libcrypto, we expect that callbacks are already set, and won't try to
+ * override it.
+ */
+int
+pgtls_init(PGconn *conn)
+{
+#ifdef ENABLE_THREAD_SAFETY
+#ifdef WIN32
+ /* Also see similar code in fe-connect.c, default_threadlock() */
+ if (ssl_config_mutex == NULL)
+ {
+ while (InterlockedExchange(&win32_ssl_create_mutex, 1) == 1)
+ /* loop, another thread own the lock */ ;
+ if (ssl_config_mutex == NULL)
+ {
+ if (pthread_mutex_init(&ssl_config_mutex, NULL))
+ return -1;
+ }
+ InterlockedExchange(&win32_ssl_create_mutex, 0);
+ }
+#endif
+ if (pthread_mutex_lock(&ssl_config_mutex))
+ return -1;
+
+#ifdef HAVE_CRYPTO_LOCK
+ if (pq_init_crypto_lib)
+ {
+ /*
+ * If necessary, set up an array to hold locks for libcrypto.
+ * libcrypto will tell us how big to make this array.
+ */
+ if (pq_lockarray == NULL)
+ {
+ int i;
+
+ pq_lockarray = malloc(sizeof(pthread_mutex_t) * CRYPTO_num_locks());
+ if (!pq_lockarray)
+ {
+ pthread_mutex_unlock(&ssl_config_mutex);
+ return -1;
+ }
+ for (i = 0; i < CRYPTO_num_locks(); i++)
+ {
+ if (pthread_mutex_init(&pq_lockarray[i], NULL))
+ {
+ free(pq_lockarray);
+ pq_lockarray = NULL;
+ pthread_mutex_unlock(&ssl_config_mutex);
+ return -1;
+ }
+ }
+ }
+
+ if (ssl_open_connections++ == 0)
+ {
+ /*
+ * These are only required for threaded libcrypto applications,
+ * but make sure we don't stomp on them if they're already set.
+ */
+ if (CRYPTO_get_id_callback() == NULL)
+ CRYPTO_set_id_callback(pq_threadidcallback);
+ if (CRYPTO_get_locking_callback() == NULL)
+ CRYPTO_set_locking_callback(pq_lockingcallback);
+ }
+ }
+#endif /* HAVE_CRYPTO_LOCK */
+#endif /* ENABLE_THREAD_SAFETY */
+
+ if (!ssl_lib_initialized)
+ {
+ if (pq_init_ssl_lib)
+ {
+#ifdef HAVE_OPENSSL_INIT_SSL
+ OPENSSL_init_ssl(OPENSSL_INIT_LOAD_CONFIG, NULL);
+#else
+ OPENSSL_config(NULL);
+ SSL_library_init();
+ SSL_load_error_strings();
+#endif
+ }
+ ssl_lib_initialized = true;
+ }
+
+#ifdef ENABLE_THREAD_SAFETY
+ pthread_mutex_unlock(&ssl_config_mutex);
+#endif
+ return 0;
+}
+
+/*
+ * This function is needed because if the libpq library is unloaded
+ * from the application, the callback functions will no longer exist when
+ * libcrypto is used by other parts of the system. For this reason,
+ * we unregister the callback functions when the last libpq
+ * connection is closed. (The same would apply for OpenSSL callbacks
+ * if we had any.)
+ *
+ * Callbacks are only set when we're compiled in threadsafe mode, so
+ * we only need to remove them in this case. They are also not needed
+ * with OpenSSL 1.1.0 anymore.
+ */
+static void
+destroy_ssl_system(void)
+{
+#if defined(ENABLE_THREAD_SAFETY) && defined(HAVE_CRYPTO_LOCK)
+ /* Mutex is created in pgtls_init() */
+ if (pthread_mutex_lock(&ssl_config_mutex))
+ return;
+
+ if (pq_init_crypto_lib && ssl_open_connections > 0)
+ --ssl_open_connections;
+
+ if (pq_init_crypto_lib && ssl_open_connections == 0)
+ {
+ /*
+ * No connections left, unregister libcrypto callbacks, if no one
+ * registered different ones in the meantime.
+ */
+ if (CRYPTO_get_locking_callback() == pq_lockingcallback)
+ CRYPTO_set_locking_callback(NULL);
+ if (CRYPTO_get_id_callback() == pq_threadidcallback)
+ CRYPTO_set_id_callback(NULL);
+
+ /*
+ * We don't free the lock array. If we get another connection in this
+ * process, we will just re-use them with the existing mutexes.
+ *
+ * This means we leak a little memory on repeated load/unload of the
+ * library.
+ */
+ }
+
+ pthread_mutex_unlock(&ssl_config_mutex);
+#endif
+}
+
+/*
+ * Create per-connection SSL object, and load the client certificate,
+ * private key, and trusted CA certs.
+ *
+ * Returns 0 if OK, -1 on failure (with a message in conn->errorMessage).
+ */
+static int
+initialize_SSL(PGconn *conn)
+{
+ SSL_CTX *SSL_context;
+ struct stat buf;
+ char homedir[MAXPGPATH];
+ char fnbuf[MAXPGPATH];
+ char sebuf[PG_STRERROR_R_BUFLEN];
+ bool have_homedir;
+ bool have_cert;
+ bool have_rootcert;
+ EVP_PKEY *pkey = NULL;
+
+ /*
+ * We'll need the home directory if any of the relevant parameters are
+ * defaulted. If pqGetHomeDirectory fails, act as though none of the
+ * files could be found.
+ */
+ if (!(conn->sslcert && strlen(conn->sslcert) > 0) ||
+ !(conn->sslkey && strlen(conn->sslkey) > 0) ||
+ !(conn->sslrootcert && strlen(conn->sslrootcert) > 0) ||
+ !(conn->sslcrl && strlen(conn->sslcrl) > 0))
+ have_homedir = pqGetHomeDirectory(homedir, sizeof(homedir));
+ else /* won't need it */
+ have_homedir = false;
+
+ /*
+ * Create a new SSL_CTX object.
+ *
+ * We used to share a single SSL_CTX between all connections, but it was
+ * complicated if connections used different certificates. So now we
+ * create a separate context for each connection, and accept the overhead.
+ */
+ SSL_context = SSL_CTX_new(SSLv23_method());
+ if (!SSL_context)
+ {
+ char *err = SSLerrmessage(ERR_get_error());
+
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("could not create SSL context: %s\n"),
+ err);
+ SSLerrfree(err);
+ return -1;
+ }
+
+ /*
+ * Delegate the client cert password prompt to the libpq wrapper callback
+ * if any is defined.
+ *
+ * If the application hasn't installed its own and the sslpassword
+ * parameter is non-null, we install ours now to make sure we supply
+ * PGconn->sslpassword to OpenSSL instead of letting it prompt on stdin.
+ *
+ * This will replace OpenSSL's default PEM_def_callback (which prompts on
+ * stdin), but we're only setting it for this SSL context so it's
+ * harmless.
+ */
+ if (PQsslKeyPassHook
+ || (conn->sslpassword && strlen(conn->sslpassword) > 0))
+ {
+ SSL_CTX_set_default_passwd_cb(SSL_context, PQssl_passwd_cb);
+ SSL_CTX_set_default_passwd_cb_userdata(SSL_context, conn);
+ }
+
+ /* Disable old protocol versions */
+ SSL_CTX_set_options(SSL_context, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
+
+ /* Set the minimum and maximum protocol versions if necessary */
+ if (conn->ssl_min_protocol_version &&
+ strlen(conn->ssl_min_protocol_version) != 0)
+ {
+ int ssl_min_ver;
+
+ ssl_min_ver = ssl_protocol_version_to_openssl(conn->ssl_min_protocol_version);
+
+ if (ssl_min_ver == -1)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("invalid value \"%s\" for minimum SSL protocol version\n"),
+ conn->ssl_min_protocol_version);
+ SSL_CTX_free(SSL_context);
+ return -1;
+ }
+
+ if (!SSL_CTX_set_min_proto_version(SSL_context, ssl_min_ver))
+ {
+ char *err = SSLerrmessage(ERR_get_error());
+
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("could not set minimum SSL protocol version: %s\n"),
+ err);
+ SSLerrfree(err);
+ SSL_CTX_free(SSL_context);
+ return -1;
+ }
+ }
+
+ if (conn->ssl_max_protocol_version &&
+ strlen(conn->ssl_max_protocol_version) != 0)
+ {
+ int ssl_max_ver;
+
+ ssl_max_ver = ssl_protocol_version_to_openssl(conn->ssl_max_protocol_version);
+
+ if (ssl_max_ver == -1)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("invalid value \"%s\" for maximum SSL protocol version\n"),
+ conn->ssl_max_protocol_version);
+ SSL_CTX_free(SSL_context);
+ return -1;
+ }
+
+ if (!SSL_CTX_set_max_proto_version(SSL_context, ssl_max_ver))
+ {
+ char *err = SSLerrmessage(ERR_get_error());
+
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("could not set maximum SSL protocol version: %s\n"),
+ err);
+ SSLerrfree(err);
+ SSL_CTX_free(SSL_context);
+ return -1;
+ }
+ }
+
+ /*
+ * Disable OpenSSL's moving-write-buffer sanity check, because it causes
+ * unnecessary failures in nonblocking send cases.
+ */
+ SSL_CTX_set_mode(SSL_context, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
+
+ /*
+ * If the root cert file exists, load it so we can perform certificate
+ * verification. If sslmode is "verify-full" we will also do further
+ * verification after the connection has been completed.
+ */
+ if (conn->sslrootcert && strlen(conn->sslrootcert) > 0)
+ strlcpy(fnbuf, conn->sslrootcert, sizeof(fnbuf));
+ else if (have_homedir)
+ snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, ROOT_CERT_FILE);
+ else
+ fnbuf[0] = '\0';
+
+ if (fnbuf[0] != '\0' &&
+ stat(fnbuf, &buf) == 0)
+ {
+ X509_STORE *cvstore;
+
+ if (SSL_CTX_load_verify_locations(SSL_context, fnbuf, NULL) != 1)
+ {
+ char *err = SSLerrmessage(ERR_get_error());
+
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("could not read root certificate file \"%s\": %s\n"),
+ fnbuf, err);
+ SSLerrfree(err);
+ SSL_CTX_free(SSL_context);
+ return -1;
+ }
+
+ if ((cvstore = SSL_CTX_get_cert_store(SSL_context)) != NULL)
+ {
+ if (conn->sslcrl && strlen(conn->sslcrl) > 0)
+ strlcpy(fnbuf, conn->sslcrl, sizeof(fnbuf));
+ else if (have_homedir)
+ snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, ROOT_CRL_FILE);
+ else
+ fnbuf[0] = '\0';
+
+ /* Set the flags to check against the complete CRL chain */
+ if (fnbuf[0] != '\0' &&
+ X509_STORE_load_locations(cvstore, fnbuf, NULL) == 1)
+ {
+ X509_STORE_set_flags(cvstore,
+ X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL);
+ }
+ /* if not found, silently ignore; we do not require CRL */
+ ERR_clear_error();
+ }
+ have_rootcert = true;
+ }
+ else
+ {
+ /*
+ * stat() failed; assume root file doesn't exist. If sslmode is
+ * verify-ca or verify-full, this is an error. Otherwise, continue
+ * without performing any server cert verification.
+ */
+ if (conn->sslmode[0] == 'v') /* "verify-ca" or "verify-full" */
+ {
+ /*
+ * The only way to reach here with an empty filename is if
+ * pqGetHomeDirectory failed. That's a sufficiently unusual case
+ * that it seems worth having a specialized error message for it.
+ */
+ if (fnbuf[0] == '\0')
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("could not get home directory to locate root certificate file\n"
+ "Either provide the file or change sslmode to disable server certificate verification.\n"));
+ else
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("root certificate file \"%s\" does not exist\n"
+ "Either provide the file or change sslmode to disable server certificate verification.\n"), fnbuf);
+ SSL_CTX_free(SSL_context);
+ return -1;
+ }
+ have_rootcert = false;
+ }
+
+ /* Read the client certificate file */
+ if (conn->sslcert && strlen(conn->sslcert) > 0)
+ strlcpy(fnbuf, conn->sslcert, sizeof(fnbuf));
+ else if (have_homedir)
+ snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, USER_CERT_FILE);
+ else
+ fnbuf[0] = '\0';
+
+ if (fnbuf[0] == '\0')
+ {
+ /* no home directory, proceed without a client cert */
+ have_cert = false;
+ }
+ else if (stat(fnbuf, &buf) != 0)
+ {
+ /*
+ * If file is not present, just go on without a client cert; server
+ * might or might not accept the connection. Any other error,
+ * however, is grounds for complaint.
+ */
+ if (errno != ENOENT && errno != ENOTDIR)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("could not open certificate file \"%s\": %s\n"),
+ fnbuf, strerror_r(errno, sebuf, sizeof(sebuf)));
+ SSL_CTX_free(SSL_context);
+ return -1;
+ }
+ have_cert = false;
+ }
+ else
+ {
+ /*
+ * Cert file exists, so load it. Since OpenSSL doesn't provide the
+ * equivalent of "SSL_use_certificate_chain_file", we have to load it
+ * into the SSL context, rather than the SSL object.
+ */
+ if (SSL_CTX_use_certificate_chain_file(SSL_context, fnbuf) != 1)
+ {
+ char *err = SSLerrmessage(ERR_get_error());
+
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("could not read certificate file \"%s\": %s\n"),
+ fnbuf, err);
+ SSLerrfree(err);
+ SSL_CTX_free(SSL_context);
+ return -1;
+ }
+
+ /* need to load the associated private key, too */
+ have_cert = true;
+ }
+
+ /*
+ * The SSL context is now loaded with the correct root and client
+ * certificates. Create a connection-specific SSL object. The private key
+ * is loaded directly into the SSL object. (We could load the private key
+ * into the context, too, but we have done it this way historically, and
+ * it doesn't really matter.)
+ */
+ if (!(conn->ssl = SSL_new(SSL_context)) ||
+ !SSL_set_app_data(conn->ssl, conn) ||
+ !my_SSL_set_fd(conn, conn->sock))
+ {
+ char *err = SSLerrmessage(ERR_get_error());
+
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("could not establish SSL connection: %s\n"),
+ err);
+ SSLerrfree(err);
+ SSL_CTX_free(SSL_context);
+ return -1;
+ }
+ conn->ssl_in_use = true;
+
+ /*
+ * SSL contexts are reference counted by OpenSSL. We can free it as soon
+ * as we have created the SSL object, and it will stick around for as long
+ * as it's actually needed.
+ */
+ SSL_CTX_free(SSL_context);
+ SSL_context = NULL;
+
+ /*
+ * Read the SSL key. If a key is specified, treat it as an engine:key
+ * combination if there is colon present - we don't support files with
+ * colon in the name. The exception is if the second character is a colon,
+ * in which case it can be a Windows filename with drive specification.
+ */
+ if (have_cert && conn->sslkey && strlen(conn->sslkey) > 0)
+ {
+#ifdef USE_SSL_ENGINE
+ if (strchr(conn->sslkey, ':')
+#ifdef WIN32
+ && conn->sslkey[1] != ':'
+#endif
+ )
+ {
+ /* Colon, but not in second character, treat as engine:key */
+ char *engine_str = strdup(conn->sslkey);
+ char *engine_colon;
+
+ if (engine_str == NULL)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("out of memory\n"));
+ return -1;
+ }
+
+ /* cannot return NULL because we already checked before strdup */
+ engine_colon = strchr(engine_str, ':');
+
+ *engine_colon = '\0'; /* engine_str now has engine name */
+ engine_colon++; /* engine_colon now has key name */
+
+ conn->engine = ENGINE_by_id(engine_str);
+ if (conn->engine == NULL)
+ {
+ char *err = SSLerrmessage(ERR_get_error());
+
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("could not load SSL engine \"%s\": %s\n"),
+ engine_str, err);
+ SSLerrfree(err);
+ free(engine_str);
+ return -1;
+ }
+
+ if (ENGINE_init(conn->engine) == 0)
+ {
+ char *err = SSLerrmessage(ERR_get_error());
+
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("could not initialize SSL engine \"%s\": %s\n"),
+ engine_str, err);
+ SSLerrfree(err);
+ ENGINE_free(conn->engine);
+ conn->engine = NULL;
+ free(engine_str);
+ return -1;
+ }
+
+ pkey = ENGINE_load_private_key(conn->engine, engine_colon,
+ NULL, NULL);
+ if (pkey == NULL)
+ {
+ char *err = SSLerrmessage(ERR_get_error());
+
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("could not read private SSL key \"%s\" from engine \"%s\": %s\n"),
+ engine_colon, engine_str, err);
+ SSLerrfree(err);
+ ENGINE_finish(conn->engine);
+ ENGINE_free(conn->engine);
+ conn->engine = NULL;
+ free(engine_str);
+ return -1;
+ }
+ if (SSL_use_PrivateKey(conn->ssl, pkey) != 1)
+ {
+ char *err = SSLerrmessage(ERR_get_error());
+
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("could not load private SSL key \"%s\" from engine \"%s\": %s\n"),
+ engine_colon, engine_str, err);
+ SSLerrfree(err);
+ ENGINE_finish(conn->engine);
+ ENGINE_free(conn->engine);
+ conn->engine = NULL;
+ free(engine_str);
+ return -1;
+ }
+
+ free(engine_str);
+
+ fnbuf[0] = '\0'; /* indicate we're not going to load from a
+ * file */
+ }
+ else
+#endif /* USE_SSL_ENGINE */
+ {
+ /* PGSSLKEY is not an engine, treat it as a filename */
+ strlcpy(fnbuf, conn->sslkey, sizeof(fnbuf));
+ }
+ }
+ else if (have_homedir)
+ {
+ /* No PGSSLKEY specified, load default file */
+ snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, USER_KEY_FILE);
+ }
+ else
+ fnbuf[0] = '\0';
+
+ if (have_cert && fnbuf[0] != '\0')
+ {
+ /* read the client key from file */
+
+ if (stat(fnbuf, &buf) != 0)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("certificate present, but not private key file \"%s\"\n"),
+ fnbuf);
+ return -1;
+ }
+#ifndef WIN32
+ if (!S_ISREG(buf.st_mode) || buf.st_mode & (S_IRWXG | S_IRWXO))
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("private key file \"%s\" has group or world access; permissions should be u=rw (0600) or less\n"),
+ fnbuf);
+ return -1;
+ }
+#endif
+
+ if (SSL_use_PrivateKey_file(conn->ssl, fnbuf, SSL_FILETYPE_PEM) != 1)
+ {
+ char *err = SSLerrmessage(ERR_get_error());
+
+ /*
+ * We'll try to load the file in DER (binary ASN.1) format, and if
+ * that fails too, report the original error. This could mask
+ * issues where there's something wrong with a DER-format cert,
+ * but we'd have to duplicate openssl's format detection to be
+ * smarter than this. We can't just probe for a leading -----BEGIN
+ * because PEM can have leading non-matching lines and blanks.
+ * OpenSSL doesn't expose its get_name(...) and its PEM routines
+ * don't differentiate between failure modes in enough detail to
+ * let us tell the difference between "not PEM, try DER" and
+ * "wrong password".
+ */
+ if (SSL_use_PrivateKey_file(conn->ssl, fnbuf, SSL_FILETYPE_ASN1) != 1)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("could not load private key file \"%s\": %s\n"),
+ fnbuf, err);
+ SSLerrfree(err);
+ return -1;
+ }
+
+ SSLerrfree(err);
+
+ }
+ }
+
+ /* verify that the cert and key go together */
+ if (have_cert &&
+ SSL_check_private_key(conn->ssl) != 1)
+ {
+ char *err = SSLerrmessage(ERR_get_error());
+
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("certificate does not match private key file \"%s\": %s\n"),
+ fnbuf, err);
+ SSLerrfree(err);
+ return -1;
+ }
+
+ /*
+ * If a root cert was loaded, also set our certificate verification
+ * callback.
+ */
+ if (have_rootcert)
+ SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, verify_cb);
+
+ /*
+ * Set compression option if necessary.
+ */
+ if (conn->sslcompression && conn->sslcompression[0] == '0')
+ SSL_set_options(conn->ssl, SSL_OP_NO_COMPRESSION);
+ else
+ SSL_clear_options(conn->ssl, SSL_OP_NO_COMPRESSION);
+
+ return 0;
+}
+
+/*
+ * Attempt to negotiate SSL connection.
+ */
+static PostgresPollingStatusType
+open_client_SSL(PGconn *conn)
+{
+ int r;
+
+ ERR_clear_error();
+ r = SSL_connect(conn->ssl);
+ if (r <= 0)
+ {
+ int err = SSL_get_error(conn->ssl, r);
+ unsigned long ecode;
+
+ ecode = ERR_get_error();
+ switch (err)
+ {
+ case SSL_ERROR_WANT_READ:
+ return PGRES_POLLING_READING;
+
+ case SSL_ERROR_WANT_WRITE:
+ return PGRES_POLLING_WRITING;
+
+ case SSL_ERROR_SYSCALL:
+ {
+ char sebuf[PG_STRERROR_R_BUFLEN];
+
+ if (r == -1)
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("SSL SYSCALL error: %s\n"),
+ SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
+ else
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("SSL SYSCALL error: EOF detected\n"));
+ pgtls_close(conn);
+ return PGRES_POLLING_FAILED;
+ }
+ case SSL_ERROR_SSL:
+ {
+ char *err = SSLerrmessage(ecode);
+
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("SSL error: %s\n"),
+ err);
+ SSLerrfree(err);
+ switch (ERR_GET_REASON(ecode))
+ {
+ /*
+ * UNSUPPORTED_PROTOCOL, WRONG_VERSION_NUMBER, and
+ * TLSV1_ALERT_PROTOCOL_VERSION have been observed
+ * when trying to communicate with an old OpenSSL
+ * library, or when the client and server specify
+ * disjoint protocol ranges.
+ * NO_PROTOCOLS_AVAILABLE occurs if there's a
+ * local misconfiguration (which can happen
+ * despite our checks, if openssl.cnf injects a
+ * limit we didn't account for). It's not very
+ * clear what would make OpenSSL return the other
+ * codes listed here, but a hint about protocol
+ * versions seems like it's appropriate for all.
+ */
+ case SSL_R_NO_PROTOCOLS_AVAILABLE:
+ case SSL_R_UNSUPPORTED_PROTOCOL:
+ case SSL_R_BAD_PROTOCOL_VERSION_NUMBER:
+ case SSL_R_UNKNOWN_PROTOCOL:
+ case SSL_R_UNKNOWN_SSL_VERSION:
+ case SSL_R_UNSUPPORTED_SSL_VERSION:
+ case SSL_R_WRONG_SSL_VERSION:
+ case SSL_R_WRONG_VERSION_NUMBER:
+ case SSL_R_TLSV1_ALERT_PROTOCOL_VERSION:
+#ifdef SSL_R_VERSION_TOO_HIGH
+ case SSL_R_VERSION_TOO_HIGH:
+ case SSL_R_VERSION_TOO_LOW:
+#endif
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("This may indicate that the server does not support any SSL protocol version between %s and %s.\n"),
+ conn->ssl_min_protocol_version ?
+ conn->ssl_min_protocol_version :
+ MIN_OPENSSL_TLS_VERSION,
+ conn->ssl_max_protocol_version ?
+ conn->ssl_max_protocol_version :
+ MAX_OPENSSL_TLS_VERSION);
+ break;
+ default:
+ break;
+ }
+ pgtls_close(conn);
+ return PGRES_POLLING_FAILED;
+ }
+
+ default:
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("unrecognized SSL error code: %d\n"),
+ err);
+ pgtls_close(conn);
+ return PGRES_POLLING_FAILED;
+ }
+ }
+
+ /*
+ * We already checked the server certificate in initialize_SSL() using
+ * SSL_CTX_set_verify(), if root.crt exists.
+ */
+
+ /* get server certificate */
+ conn->peer = SSL_get_peer_certificate(conn->ssl);
+ if (conn->peer == NULL)
+ {
+ char *err = SSLerrmessage(ERR_get_error());
+
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("certificate could not be obtained: %s\n"),
+ err);
+ SSLerrfree(err);
+ pgtls_close(conn);
+ return PGRES_POLLING_FAILED;
+ }
+
+ if (!pq_verify_peer_name_matches_certificate(conn))
+ {
+ pgtls_close(conn);
+ return PGRES_POLLING_FAILED;
+ }
+
+ /* SSL handshake is complete */
+ return PGRES_POLLING_OK;
+}
+
+void
+pgtls_close(PGconn *conn)
+{
+ bool destroy_needed = false;
+
+ if (conn->ssl)
+ {
+ /*
+ * We can't destroy everything SSL-related here due to the possible
+ * later calls to OpenSSL routines which may need our thread
+ * callbacks, so set a flag here and check at the end.
+ */
+ destroy_needed = true;
+
+ SSL_shutdown(conn->ssl);
+ SSL_free(conn->ssl);
+ conn->ssl = NULL;
+ conn->ssl_in_use = false;
+ }
+
+ if (conn->peer)
+ {
+ X509_free(conn->peer);
+ conn->peer = NULL;
+ }
+
+#ifdef USE_SSL_ENGINE
+ if (conn->engine)
+ {
+ ENGINE_finish(conn->engine);
+ ENGINE_free(conn->engine);
+ conn->engine = NULL;
+ }
+#endif
+
+ /*
+ * This will remove our SSL locking hooks, if this is the last SSL
+ * connection, which means we must wait to call it until after all SSL
+ * calls have been made, otherwise we can end up with a race condition and
+ * possible deadlocks.
+ *
+ * See comments above destroy_ssl_system().
+ */
+ if (destroy_needed)
+ destroy_ssl_system();
+}
+
+
+/*
+ * Obtain reason string for passed SSL errcode
+ *
+ * ERR_get_error() is used by caller to get errcode to pass here.
+ *
+ * Some caution is needed here since ERR_reason_error_string will
+ * return NULL if it doesn't recognize the error code. We don't
+ * want to return NULL ever.
+ */
+static char ssl_nomem[] = "out of memory allocating error description";
+
+#define SSL_ERR_LEN 128
+
+static char *
+SSLerrmessage(unsigned long ecode)
+{
+ const char *errreason;
+ char *errbuf;
+
+ errbuf = malloc(SSL_ERR_LEN);
+ if (!errbuf)
+ return ssl_nomem;
+ if (ecode == 0)
+ {
+ snprintf(errbuf, SSL_ERR_LEN, libpq_gettext("no SSL error reported"));
+ return errbuf;
+ }
+ errreason = ERR_reason_error_string(ecode);
+ if (errreason != NULL)
+ {
+ strlcpy(errbuf, errreason, SSL_ERR_LEN);
+ return errbuf;
+ }
+ snprintf(errbuf, SSL_ERR_LEN, libpq_gettext("SSL error code %lu"), ecode);
+ return errbuf;
+}
+
+static void
+SSLerrfree(char *buf)
+{
+ if (buf != ssl_nomem)
+ free(buf);
+}
+
+/* ------------------------------------------------------------ */
+/* SSL information functions */
+/* ------------------------------------------------------------ */
+
+/*
+ * Return pointer to OpenSSL object.
+ */
+void *
+PQgetssl(PGconn *conn)
+{
+ if (!conn)
+ return NULL;
+ return conn->ssl;
+}
+
+void *
+PQsslStruct(PGconn *conn, const char *struct_name)
+{
+ if (!conn)
+ return NULL;
+ if (strcmp(struct_name, "OpenSSL") == 0)
+ return conn->ssl;
+ return NULL;
+}
+
+const char *const *
+PQsslAttributeNames(PGconn *conn)
+{
+ static const char *const result[] = {
+ "library",
+ "key_bits",
+ "cipher",
+ "compression",
+ "protocol",
+ NULL
+ };
+
+ return result;
+}
+
+const char *
+PQsslAttribute(PGconn *conn, const char *attribute_name)
+{
+ if (!conn)
+ return NULL;
+ if (conn->ssl == NULL)
+ return NULL;
+
+ if (strcmp(attribute_name, "library") == 0)
+ return "OpenSSL";
+
+ if (strcmp(attribute_name, "key_bits") == 0)
+ {
+ static char sslbits_str[12];
+ int sslbits;
+
+ SSL_get_cipher_bits(conn->ssl, &sslbits);
+ snprintf(sslbits_str, sizeof(sslbits_str), "%d", sslbits);
+ return sslbits_str;
+ }
+
+ if (strcmp(attribute_name, "cipher") == 0)
+ return SSL_get_cipher(conn->ssl);
+
+ if (strcmp(attribute_name, "compression") == 0)
+ return SSL_get_current_compression(conn->ssl) ? "on" : "off";
+
+ if (strcmp(attribute_name, "protocol") == 0)
+ return SSL_get_version(conn->ssl);
+
+ return NULL; /* unknown attribute */
+}
+
+/*
+ * Private substitute BIO: this does the sending and receiving using
+ * pqsecure_raw_write() and pqsecure_raw_read() instead, to allow those
+ * functions to disable SIGPIPE and give better error messages on I/O errors.
+ *
+ * These functions are closely modelled on the standard socket BIO in OpenSSL;
+ * see sock_read() and sock_write() in OpenSSL's crypto/bio/bss_sock.c.
+ * XXX OpenSSL 1.0.1e considers many more errcodes than just EINTR as reasons
+ * to retry; do we need to adopt their logic for that?
+ */
+
+#ifndef HAVE_BIO_GET_DATA
+#define BIO_get_data(bio) (bio->ptr)
+#define BIO_set_data(bio, data) (bio->ptr = data)
+#endif
+
+static BIO_METHOD *my_bio_methods;
+
+static int
+my_sock_read(BIO *h, char *buf, int size)
+{
+ int res;
+
+ res = pqsecure_raw_read((PGconn *) BIO_get_data(h), buf, size);
+ BIO_clear_retry_flags(h);
+ if (res < 0)
+ {
+ /* If we were interrupted, tell caller to retry */
+ switch (SOCK_ERRNO)
+ {
+#ifdef EAGAIN
+ case EAGAIN:
+#endif
+#if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
+ case EWOULDBLOCK:
+#endif
+ case EINTR:
+ BIO_set_retry_read(h);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ return res;
+}
+
+static int
+my_sock_write(BIO *h, const char *buf, int size)
+{
+ int res;
+
+ res = pqsecure_raw_write((PGconn *) BIO_get_data(h), buf, size);
+ BIO_clear_retry_flags(h);
+ if (res <= 0)
+ {
+ /* If we were interrupted, tell caller to retry */
+ switch (SOCK_ERRNO)
+ {
+#ifdef EAGAIN
+ case EAGAIN:
+#endif
+#if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
+ case EWOULDBLOCK:
+#endif
+ case EINTR:
+ BIO_set_retry_write(h);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ return res;
+}
+
+static BIO_METHOD *
+my_BIO_s_socket(void)
+{
+ if (!my_bio_methods)
+ {
+ BIO_METHOD *biom = (BIO_METHOD *) BIO_s_socket();
+#ifdef HAVE_BIO_METH_NEW
+ int my_bio_index;
+
+ my_bio_index = BIO_get_new_index();
+ if (my_bio_index == -1)
+ return NULL;
+ my_bio_methods = BIO_meth_new(my_bio_index, "libpq socket");
+ if (!my_bio_methods)
+ return NULL;
+
+ /*
+ * As of this writing, these functions never fail. But check anyway,
+ * like OpenSSL's own examples do.
+ */
+ if (!BIO_meth_set_write(my_bio_methods, my_sock_write) ||
+ !BIO_meth_set_read(my_bio_methods, my_sock_read) ||
+ !BIO_meth_set_gets(my_bio_methods, BIO_meth_get_gets(biom)) ||
+ !BIO_meth_set_puts(my_bio_methods, BIO_meth_get_puts(biom)) ||
+ !BIO_meth_set_ctrl(my_bio_methods, BIO_meth_get_ctrl(biom)) ||
+ !BIO_meth_set_create(my_bio_methods, BIO_meth_get_create(biom)) ||
+ !BIO_meth_set_destroy(my_bio_methods, BIO_meth_get_destroy(biom)) ||
+ !BIO_meth_set_callback_ctrl(my_bio_methods, BIO_meth_get_callback_ctrl(biom)))
+ {
+ BIO_meth_free(my_bio_methods);
+ my_bio_methods = NULL;
+ return NULL;
+ }
+#else
+ my_bio_methods = malloc(sizeof(BIO_METHOD));
+ if (!my_bio_methods)
+ return NULL;
+ memcpy(my_bio_methods, biom, sizeof(BIO_METHOD));
+ my_bio_methods->bread = my_sock_read;
+ my_bio_methods->bwrite = my_sock_write;
+#endif
+ }
+ return my_bio_methods;
+}
+
+/* This should exactly match OpenSSL's SSL_set_fd except for using my BIO */
+static int
+my_SSL_set_fd(PGconn *conn, int fd)
+{
+ int ret = 0;
+ BIO *bio;
+ BIO_METHOD *bio_method;
+
+ bio_method = my_BIO_s_socket();
+ if (bio_method == NULL)
+ {
+ SSLerr(SSL_F_SSL_SET_FD, ERR_R_BUF_LIB);
+ goto err;
+ }
+ bio = BIO_new(bio_method);
+ if (bio == NULL)
+ {
+ SSLerr(SSL_F_SSL_SET_FD, ERR_R_BUF_LIB);
+ goto err;
+ }
+ BIO_set_data(bio, conn);
+
+ SSL_set_bio(conn->ssl, bio, bio);
+ BIO_set_fd(bio, fd, BIO_NOCLOSE);
+ ret = 1;
+err:
+ return ret;
+}
+
+/*
+ * This is the default handler to return a client cert password from
+ * conn->sslpassword. Apps may install it explicitly if they want to
+ * prevent openssl from ever prompting on stdin.
+ */
+int
+PQdefaultSSLKeyPassHook_OpenSSL(char *buf, int size, PGconn *conn)
+{
+ if (conn->sslpassword)
+ {
+ if (strlen(conn->sslpassword) + 1 > size)
+ fprintf(stderr, libpq_gettext("WARNING: sslpassword truncated\n"));
+ strncpy(buf, conn->sslpassword, size);
+ buf[size - 1] = '\0';
+ return strlen(buf);
+ }
+ else
+ {
+ buf[0] = '\0';
+ return 0;
+ }
+}
+
+PQsslKeyPassHook_OpenSSL_type
+PQgetSSLKeyPassHook_OpenSSL(void)
+{
+ return PQsslKeyPassHook;
+}
+
+void
+PQsetSSLKeyPassHook_OpenSSL(PQsslKeyPassHook_OpenSSL_type hook)
+{
+ PQsslKeyPassHook = hook;
+}
+
+/*
+ * Supply a password to decrypt a client certificate.
+ *
+ * This must match OpenSSL type pem_password_cb.
+ */
+static int
+PQssl_passwd_cb(char *buf, int size, int rwflag, void *userdata)
+{
+ PGconn *conn = userdata;
+
+ if (PQsslKeyPassHook)
+ return PQsslKeyPassHook(buf, size, conn);
+ else
+ return PQdefaultSSLKeyPassHook_OpenSSL(buf, size, conn);
+}
+
+/*
+ * Convert TLS protocol version string to OpenSSL values
+ *
+ * If a version is passed that is not supported by the current OpenSSL version,
+ * then we return -1. If a non-negative value is returned, subsequent code can
+ * assume it is working with a supported version.
+ *
+ * Note: this is rather similar to the backend routine in be-secure-openssl.c,
+ * so make sure to update both routines if changing this one.
+ */
+static int
+ssl_protocol_version_to_openssl(const char *protocol)
+{
+ if (pg_strcasecmp("TLSv1", protocol) == 0)
+ return TLS1_VERSION;
+
+#ifdef TLS1_1_VERSION
+ if (pg_strcasecmp("TLSv1.1", protocol) == 0)
+ return TLS1_1_VERSION;
+#endif
+
+#ifdef TLS1_2_VERSION
+ if (pg_strcasecmp("TLSv1.2", protocol) == 0)
+ return TLS1_2_VERSION;
+#endif
+
+#ifdef TLS1_3_VERSION
+ if (pg_strcasecmp("TLSv1.3", protocol) == 0)
+ return TLS1_3_VERSION;
+#endif
+
+ return -1;
+}
diff --git a/src/interfaces/libpq/fe-secure.c b/src/interfaces/libpq/fe-secure.c
new file mode 100644
index 0000000..3311fd7
--- /dev/null
+++ b/src/interfaces/libpq/fe-secure.c
@@ -0,0 +1,555 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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-2020, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/interfaces/libpq/fe-secure.c
+ *
+ * NOTES
+ *
+ * We don't provide informational callbacks here (like
+ * info_cb() in be-secure.c), since there's no good mechanism to
+ * display such information to the user.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres_fe.h"
+
+#include <signal.h>
+#include <fcntl.h>
+#include <ctype.h>
+
+#ifdef WIN32
+#include "win32.h"
+#else
+#include <sys/socket.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#ifdef HAVE_NETINET_TCP_H
+#include <netinet/tcp.h>
+#endif
+#include <arpa/inet.h>
+#endif
+
+#include <sys/stat.h>
+
+#ifdef ENABLE_THREAD_SAFETY
+#ifdef WIN32
+#include "pthread-win32.h"
+#else
+#include <pthread.h>
+#endif
+#endif
+
+#include "fe-auth.h"
+#include "libpq-fe.h"
+#include "libpq-int.h"
+
+/*
+ * 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 */
+/* ------------------------------------------------------------ */
+
+
+int
+PQsslInUse(PGconn *conn)
+{
+ if (!conn)
+ return 0;
+ return conn->ssl_in_use;
+}
+
+/*
+ * 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
+#ifdef ENABLE_GSS
+ if (conn->gssenc)
+ {
+ n = pg_GSS_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[PG_STRERROR_R_BUFLEN];
+
+ 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
+#ifdef ENABLE_GSS
+ if (conn->gssenc)
+ {
+ n = pg_GSS_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[PG_STRERROR_R_BUFLEN];
+
+ 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);
+
+#ifdef ECONNRESET
+ /* FALL THRU */
+
+ 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
+
+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;
+}
+
+PQsslKeyPassHook_OpenSSL_type
+PQgetSSLKeyPassHook_OpenSSL(void)
+{
+ return NULL;
+}
+
+void
+PQsetSSLKeyPassHook_OpenSSL(PQsslKeyPassHook_OpenSSL_type hook)
+{
+ return;
+}
+
+int
+PQdefaultSSLKeyPassHook_OpenSSL(char *buf, int size, PGconn *conn)
+{
+ return 0;
+}
+#endif /* USE_SSL */
+
+/* Dummy version of GSSAPI information functions, when built without GSS support */
+#ifndef ENABLE_GSS
+
+void *
+PQgetgssctx(PGconn *conn)
+{
+ return NULL;
+}
+
+int
+PQgssEncInUse(PGconn *conn)
+{
+ return 0;
+}
+
+#endif /* ENABLE_GSS */
+
+
+#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/src/interfaces/libpq/legacy-pqsignal.c b/src/interfaces/libpq/legacy-pqsignal.c
new file mode 100644
index 0000000..4703adb
--- /dev/null
+++ b/src/interfaces/libpq/legacy-pqsignal.c
@@ -0,0 +1,57 @@
+/*-------------------------------------------------------------------------
+ *
+ * legacy-pqsignal.c
+ * reliable BSD-style signal(2) routine stolen from RWW who stole it
+ * from Stevens...
+ *
+ * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/interfaces/libpq/legacy-pqsignal.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres_fe.h"
+
+#include <signal.h>
+
+
+/*
+ * This version of pqsignal() exists only because pre-9.3 releases
+ * of libpq exported pqsignal(), and some old client programs still
+ * depend on that. (Since 9.3, clients are supposed to get it from
+ * libpgport instead.)
+ *
+ * Because it is only intended for backwards compatibility, we freeze it
+ * with the semantics it had in 9.2; in particular, this has different
+ * behavior for SIGALRM than the version in src/port/pqsignal.c.
+ *
+ * libpq itself uses this only for SIGPIPE (and even then, only in
+ * non-ENABLE_THREAD_SAFETY builds), so the incompatibility isn't
+ * troublesome for internal references.
+ */
+pqsigfunc
+pqsignal(int signo, pqsigfunc func)
+{
+#ifndef WIN32
+ struct sigaction act,
+ oact;
+
+ act.sa_handler = func;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = 0;
+ if (signo != SIGALRM)
+ 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
+}
diff --git a/src/interfaces/libpq/libpq-events.c b/src/interfaces/libpq/libpq-events.c
new file mode 100644
index 0000000..d050d7f
--- /dev/null
+++ b/src/interfaces/libpq/libpq-events.c
@@ -0,0 +1,209 @@
+/*-------------------------------------------------------------------------
+ *
+ * libpq-events.c
+ * functions for supporting the libpq "events" API
+ *
+ * Portions Copyright (c) 1996-2020, 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, &regevt, 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/src/interfaces/libpq/libpq-events.h b/src/interfaces/libpq/libpq-events.h
new file mode 100644
index 0000000..5108a55
--- /dev/null
+++ b/src/interfaces/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-2020, 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/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-fe.h
new file mode 100644
index 0000000..3b6a9fb
--- /dev/null
+++ b/src/interfaces/libpq/libpq-fe.h
@@ -0,0 +1,632 @@
+/*-------------------------------------------------------------------------
+ *
+ * libpq-fe.h
+ * This file contains definitions for structures and
+ * externs for functions used by frontend postgres applications.
+ *
+ * Portions Copyright (c) 1996-2020, 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 <stdio.h>
+
+/*
+ * 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 */
+ CONNECTION_CHECK_WRITABLE, /* Check if we could make a writable
+ * connection. */
+ CONNECTION_CONSUME, /* Wait for any pending message and consume
+ * them. */
+ CONNECTION_GSS_STARTUP, /* Negotiating GSSAPI. */
+ CONNECTION_CHECK_TARGET /* Check if we have a proper target connection */
+} 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 */
+ PQERRORS_SQLSTATE /* only error severity and SQLSTATE code */
+} 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 <table ...> */
+ char *caption; /* HTML <caption> */
+ 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 reestablish 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 *PQhostaddr(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);
+
+/* Return true if GSSAPI encryption is in use */
+extern int PQgssEncInUse(PGconn *conn);
+
+/* Returns GSSAPI context if GSSAPI is in use */
+extern void *PQgetgssctx(PGconn *conn);
+
+/* 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 size_t PQresultMemorySize(const PGresult *res);
+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 PrintAttNames, /* print attribute names */
+ int TerseOutput, /* delimiter bars */
+ int colWidth); /* 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);
+extern char *PQencryptPasswordConn(PGconn *conn, const char *passwd, const char *user, const char *algorithm);
+
+/* === 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);
+
+/* === in fe-secure-openssl.c === */
+
+/* Support for overriding sslpassword handling with a callback. */
+typedef int (*PQsslKeyPassHook_OpenSSL_type) (char *buf, int size, PGconn *conn);
+extern PQsslKeyPassHook_OpenSSL_type PQgetSSLKeyPassHook_OpenSSL(void);
+extern void PQsetSSLKeyPassHook_OpenSSL(PQsslKeyPassHook_OpenSSL_type hook);
+extern int PQdefaultSSLKeyPassHook_OpenSSL(char *buf, int size, PGconn *conn);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LIBPQ_FE_H */
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
new file mode 100644
index 0000000..1de91ae
--- /dev/null
+++ b/src/interfaces/libpq/libpq-int.h
@@ -0,0 +1,823 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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-2020, 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 "libpq-events.h"
+
+#include <time.h>
+#ifndef WIN32
+#include <sys/time.h>
+#endif
+
+#ifdef ENABLE_THREAD_SAFETY
+#ifdef WIN32
+#include "pthread-win32.h"
+#else
+#include <pthread.h>
+#endif
+#include <signal.h>
+#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 <gssapi.h>
+#else
+#include <gssapi/gssapi.h>
+#endif
+#endif
+
+#ifdef ENABLE_SSPI
+#define SECURITY_WIN32
+#if defined(WIN32) && !defined(_MSC_VER)
+#include <ntsecapi.h>
+#endif
+#include <security.h>
+#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 <openssl/ssl.h>
+#include <openssl/err.h>
+
+#ifndef 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 PGresult tuple 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 */
+
+ size_t memorySize; /* total space allocated for this PGresult */
+};
+
+/* 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;
+
+/* Host address type enum for struct pg_conn_host */
+typedef enum pg_conn_host_type
+{
+ CHT_HOST_NAME,
+ CHT_HOST_ADDRESS,
+ CHT_UNIX_SOCKET
+} pg_conn_host_type;
+
+/*
+ * pg_conn_host stores all information about each of possibly several hosts
+ * mentioned in the connection string. Most fields are derived by splitting
+ * the relevant connection parameter (e.g., pghost) at commas.
+ */
+typedef struct pg_conn_host
+{
+ pg_conn_host_type type; /* type of host address */
+ char *host; /* host name or socket path */
+ char *hostaddr; /* host numeric IP address */
+ char *port; /* port number (always provided) */
+ char *password; /* password for this host, read from the
+ * password file; NULL if not sought or not
+ * found in password file. */
+} pg_conn_host;
+
+/*
+ * 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,
+ * or a path to a UNIX-domain socket, or a
+ * comma-separated list of machines and/or
+ * paths; if NULL, use DEFAULT_PGSOCKET_DIR */
+ char *pghostaddr; /* the numeric IP address of the machine on
+ * which the server is running, or a
+ * comma-separated list of same. Takes
+ * precedence over pghost. */
+ char *pgport; /* the server's communication port number, or
+ * a comma-separated list of ports */
+ char *pgtty; /* tty on which the backend messages is
+ * displayed (OBSOLETE, NOT USED) */
+ char *connect_timeout; /* connection timeout (numeric string) */
+ char *pgtcp_user_timeout; /* tcp user 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 *pgpassfile; /* path to a file containing password(s) */
+ char *channel_binding; /* channel binding mode
+ * (require,prefer,disable) */
+ 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 *sslpassword; /* client key file password */
+ char *sslrootcert; /* root certificate filename */
+ char *sslcrl; /* certificate revocation list filename */
+ char *requirepeer; /* required peer credentials for local sockets */
+ char *gssencmode; /* GSS mode (require,prefer,disable) */
+ char *krbsrvname; /* Kerberos service name */
+ char *gsslib; /* What GSS library to use ("gssapi" or
+ * "sspi") */
+ char *ssl_min_protocol_version; /* minimum TLS protocol version */
+ char *ssl_max_protocol_version; /* maximum TLS protocol version */
+
+ /* Type of connection to make. Possible values: any, read-write. */
+ char *target_session_attrs;
+
+ /* 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 */
+
+ /* Support for multiple hosts in connection string */
+ int nconnhost; /* # of hosts named in conn string */
+ int whichhost; /* host we're currently trying/connected to */
+ pg_conn_host *connhost; /* details about each named host */
+ char *connip; /* IP address for current network connection */
+
+ /* Connection data */
+ 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 sigpipe_so; /* have we masked SIGPIPE via SO_NOSIGPIPE? */
+ bool sigpipe_flag; /* can we mask SIGPIPE via MSG_NOSIGNAL? */
+ bool write_failed; /* have we had a write failure on sock? */
+ char *write_err_msg; /* write error message, or NULL if OOM */
+
+ /* Transient state needed while establishing connection */
+ bool try_next_addr; /* time to advance to next address/host? */
+ bool try_next_host; /* time to advance to next connhost[]? */
+ struct addrinfo *addrlist; /* list of addresses for current connhost */
+ 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 */
+ 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 SASL, SSL, GSS, etc */
+ void *sasl_state;
+
+ /* SSL structures */
+ bool ssl_in_use;
+
+#ifdef USE_SSL
+ bool allow_ssl_try; /* Allowed to try SSL negotiation */
+ bool wait_ssl_try; /* Delay SSL negotiation until after
+ * attempting normal connection */
+#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 */
+
+ /* The following are encryption-only */
+ bool try_gss; /* GSS attempting permitted */
+ bool gssenc; /* GSS encryption is usable */
+ gss_cred_id_t gcred; /* GSS credential temp storage. */
+
+ /* GSS encryption I/O state --- see fe-secure-gssapi.c */
+ char *gss_SendBuffer; /* Encrypted data waiting to be sent */
+ int gss_SendLength; /* End of data available in gss_SendBuffer */
+ int gss_SendNext; /* Next index to send a byte from
+ * gss_SendBuffer */
+ int gss_SendConsumed; /* Number of *unencrypted* bytes consumed
+ * for current contents of gss_SendBuffer */
+ char *gss_RecvBuffer; /* Received, encrypted data */
+ int gss_RecvLength; /* End of data available in gss_RecvBuffer */
+ char *gss_ResultBuffer; /* Decryption of data in gss_RecvBuffer */
+ int gss_ResultLength; /* End of data available in
+ * gss_ResultBuffer */
+ int gss_ResultNext; /* Next index to read a byte from
+ * gss_ResultBuffer */
+ uint32 gss_MaxPktSize; /* Maximum size we can encrypt and fit the
+ * results into our output buffer */
+#endif
+
+#ifdef ENABLE_SSPI
+ 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);
+
+/* === 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 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
+
+/* === SSL === */
+
+/*
+ * The SSL implementation provides these functions.
+ */
+
+/*
+ * Implementation of PQinitSSL().
+ */
+extern void pgtls_init_library(bool do_ssl, int do_crypto);
+
+/*
+ * Initialize SSL library.
+ *
+ * The conn parameter is only used to be able to pass back an error
+ * message - no connection-local setup is made here.
+ *
+ * Returns 0 if OK, -1 on failure (with a message in conn->errorMessage).
+ */
+extern int pgtls_init(PGconn *conn);
+
+/*
+ * Begin or continue negotiating a secure session.
+ */
+extern PostgresPollingStatusType pgtls_open_client(PGconn *conn);
+
+/*
+ * Close SSL connection.
+ */
+extern void pgtls_close(PGconn *conn);
+
+/*
+ * 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.
+ */
+extern ssize_t pgtls_read(PGconn *conn, void *ptr, size_t len);
+
+/*
+ * Is there unread data waiting in the SSL read buffer?
+ */
+extern bool pgtls_read_pending(PGconn *conn);
+
+/*
+ * 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.
+ */
+extern ssize_t pgtls_write(PGconn *conn, const void *ptr, size_t len);
+
+/*
+ * Get the hash of the server certificate, for SCRAM channel binding type
+ * tls-server-end-point.
+ *
+ * NULL is sent back to the caller in the event of an error, with an
+ * error message for the caller to consume.
+ *
+ * This is not supported with old versions of OpenSSL that don't have
+ * the X509_get_signature_nid() function.
+ */
+#if defined(USE_OPENSSL) && defined(HAVE_X509_GET_SIGNATURE_NID)
+#define HAVE_PGTLS_GET_PEER_CERTIFICATE_HASH
+extern char *pgtls_get_peer_certificate_hash(PGconn *conn, size_t *len);
+#endif
+
+/*
+ * Verify that the server certificate matches the host name we connected to.
+ *
+ * The certificate's Common Name and Subject Alternative Names are considered.
+ *
+ * Returns 1 if the name matches, and 0 if it does not. On error, returns
+ * -1, and sets the libpq error message.
+ *
+ */
+extern int pgtls_verify_peer_name_matches_certificate_guts(PGconn *conn,
+ int *names_examined,
+ char **first_name);
+
+/* === GSSAPI === */
+
+#ifdef ENABLE_GSS
+
+/*
+ * Establish a GSSAPI-encrypted connection.
+ */
+extern PostgresPollingStatusType pqsecure_open_gss(PGconn *conn);
+
+/*
+ * Read and write functions for GSSAPI-encrypted connections, with internal
+ * buffering to handle nonblocking sockets.
+ */
+extern ssize_t pg_GSS_write(PGconn *conn, const void *ptr, size_t len);
+extern ssize_t pg_GSS_read(PGconn *conn, void *ptr, size_t len);
+#endif
+
+/* === miscellaneous macros === */
+
+/*
+ * 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 strerror_r
+#define SOCK_ERRNO_SET(e) (errno = (e))
+#endif
+
+#endif /* LIBPQ_INT_H */
diff --git a/src/interfaces/libpq/nls.mk b/src/interfaces/libpq/nls.mk
new file mode 100644
index 0000000..d9df6f9
--- /dev/null
+++ b/src/interfaces/libpq/nls.mk
@@ -0,0 +1,6 @@
+# src/interfaces/libpq/nls.mk
+CATALOG_NAME = libpq
+AVAIL_LANGUAGES = cs de es fr it ja ko ru sv tr uk zh_CN
+GETTEXT_FILES = fe-auth.c fe-auth-scram.c fe-connect.c fe-exec.c fe-gssapi-common.c fe-lobj.c fe-misc.c fe-protocol2.c fe-protocol3.c fe-secure.c fe-secure-common.c fe-secure-gssapi.c fe-secure-openssl.c win32.c
+GETTEXT_TRIGGERS = libpq_gettext pqInternalNotice:2
+GETTEXT_FLAGS = libpq_gettext:1:pass-c-format pqInternalNotice:2:c-format
diff --git a/src/interfaces/libpq/pg_service.conf.sample b/src/interfaces/libpq/pg_service.conf.sample
new file mode 100644
index 0000000..5a1c083
--- /dev/null
+++ b/src/interfaces/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/src/interfaces/libpq/po/cs.po b/src/interfaces/libpq/po/cs.po
new file mode 100644
index 0000000..ff590c7
--- /dev/null
+++ b/src/interfaces/libpq/po/cs.po
@@ -0,0 +1,1334 @@
+# Czech translation of libpq messages
+#
+# pgtranslation Id: libpq.po,v 1.6 2011/09/08 18:23:05 petere Exp $
+#
+# Karel Žák, 2001-2003, 2004.
+# Zdeněk Kotala, 2009, 2011, 2012, 2013.
+# Tomáš Vondra <tv@fuzzy.cz>, 2012, 2013.
+msgid ""
+msgstr ""
+"Project-Id-Version: libpq-cs (PostgreSQL 9.3)\n"
+"Report-Msgid-Bugs-To: pgsql-bugs@lists.postgresql.org\n"
+"POT-Creation-Date: 2020-10-31 16:09+0000\n"
+"PO-Revision-Date: 2020-10-31 21:45+0100\n"
+"Last-Translator: Tomas Vondra <tv@fuzzy.cz>\n"
+"Language-Team: Czech <info@cspug.cx>\n"
+"Language: cs\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
+"X-Generator: Poedit 2.4.1\n"
+
+#: fe-auth-scram.c:212
+msgid "malformed SCRAM message (empty message)\n"
+msgstr "poškozená SCRAM zpráva (prázdná zpráva)\n"
+
+#: fe-auth-scram.c:218
+msgid "malformed SCRAM message (length mismatch)\n"
+msgstr "poškozená SCRAM zpráva (délka neodpovídá)\n"
+
+#: fe-auth-scram.c:265
+msgid "incorrect server signature\n"
+msgstr "chybná signatura serveru\n"
+
+#: fe-auth-scram.c:274
+msgid "invalid SCRAM exchange state\n"
+msgstr "chybný stav SCRAM výměny\n"
+
+#: fe-auth-scram.c:296
+#, c-format
+msgid "malformed SCRAM message (attribute \"%c\" expected)\n"
+msgstr "poškozená SCRAM zpráva (očekáván atribut \"%c\")\n"
+
+#: fe-auth-scram.c:305
+#, c-format
+msgid "malformed SCRAM message (expected character \"=\" for attribute \"%c\")\n"
+msgstr "poškozená SCRAM zpráva (očekáván znak \"=\" pro atribut \"%c\")\n"
+
+#: fe-auth-scram.c:346
+msgid "could not generate nonce\n"
+msgstr "nelze vygenerovat nonce\n"
+
+#: fe-auth-scram.c:356 fe-auth-scram.c:431 fe-auth-scram.c:579
+#: fe-auth-scram.c:600 fe-auth-scram.c:626 fe-auth-scram.c:641
+#: fe-auth-scram.c:691 fe-auth-scram.c:725 fe-auth.c:289 fe-auth.c:359
+#: fe-auth.c:394 fe-auth.c:611 fe-auth.c:770 fe-auth.c:1129 fe-auth.c:1277
+#: fe-connect.c:892 fe-connect.c:1419 fe-connect.c:1595 fe-connect.c:2200
+#: fe-connect.c:2223 fe-connect.c:2952 fe-connect.c:4601 fe-connect.c:4857
+#: fe-connect.c:4976 fe-connect.c:5229 fe-connect.c:5309 fe-connect.c:5408
+#: fe-connect.c:5664 fe-connect.c:5693 fe-connect.c:5765 fe-connect.c:5789
+#: fe-connect.c:5807 fe-connect.c:5908 fe-connect.c:5917 fe-connect.c:6273
+#: fe-connect.c:6423 fe-exec.c:2747 fe-exec.c:3494 fe-exec.c:3659
+#: fe-gssapi-common.c:111 fe-lobj.c:895 fe-protocol2.c:1207 fe-protocol3.c:995
+#: fe-protocol3.c:1699 fe-secure-common.c:110 fe-secure-gssapi.c:504
+#: fe-secure-openssl.c:440 fe-secure-openssl.c:1091
+msgid "out of memory\n"
+msgstr "nedostatek paměti\n"
+
+#: fe-auth-scram.c:364
+msgid "could not encode nonce\n"
+msgstr "nelze zakódovat nonce\n"
+
+#: fe-auth-scram.c:563
+msgid "could not encode client proof\n"
+msgstr "nelze zakódovat client proof\n"
+
+#: fe-auth-scram.c:618
+msgid "invalid SCRAM response (nonce mismatch)\n"
+msgstr "chybná SCRAM odpověď (nonce neodpovídá)\n"
+
+#: fe-auth-scram.c:651
+msgid "malformed SCRAM message (invalid salt)\n"
+msgstr "poškozená SCRAM zpráva (chybná salt hodnota)\n"
+
+#: fe-auth-scram.c:665
+msgid "malformed SCRAM message (invalid iteration count)\n"
+msgstr "poškozená SCRAM zpráva (chybný počet iterací)\n"
+
+#: fe-auth-scram.c:671
+msgid "malformed SCRAM message (garbage at end of server-first-message)\n"
+msgstr "poškozená SCRAM zpráva (smetí na konci server-first-message)\n"
+
+#: fe-auth-scram.c:702
+#, c-format
+msgid "error received from server in SCRAM exchange: %s\n"
+msgstr "server zaslal chybu v rámci SCRAM výměny: %s\n"
+
+#: fe-auth-scram.c:718
+msgid "malformed SCRAM message (garbage at end of server-final-message)\n"
+msgstr "poškozená SCRAM zpráva (smetí na konci server-final-message)\n"
+
+#: fe-auth-scram.c:737
+msgid "malformed SCRAM message (invalid server signature)\n"
+msgstr "poškozená SCRAM zpráva (chybná signatura serveru)\n"
+
+#: fe-auth.c:76
+#, c-format
+msgid "out of memory allocating GSSAPI buffer (%d)\n"
+msgstr "při alokaci GSSAPI bufferu došla paměť (%d)\n"
+
+#: fe-auth.c:131
+msgid "GSSAPI continuation error"
+msgstr "Přetrvávající chyba GSSAPI"
+
+#: fe-auth.c:158 fe-auth.c:388 fe-gssapi-common.c:98 fe-secure-common.c:98
+msgid "host name must be specified\n"
+msgstr "host musí být specifikován\n"
+
+#: fe-auth.c:165
+msgid "duplicate GSS authentication request\n"
+msgstr "duplikátní GSS autentizační požadavek\n"
+
+#: fe-auth.c:230
+#, c-format
+msgid "out of memory allocating SSPI buffer (%d)\n"
+msgstr "při alokaci SSPI bufferu došla paměť (%d)\n"
+
+#: fe-auth.c:278
+msgid "SSPI continuation error"
+msgstr "Přetrvávající chyba SSPI"
+
+#: fe-auth.c:349
+msgid "duplicate SSPI authentication request\n"
+msgstr "duplicitní SSPI autentizační požadavek\n"
+
+#: fe-auth.c:374
+msgid "could not acquire SSPI credentials"
+msgstr "nelze získat SSPI credentials"
+
+#: fe-auth.c:429
+msgid "channel binding required, but SSL not in use\n"
+msgstr "channel binding vyžadováno, ale SSL není zapnuto\n"
+
+#: fe-auth.c:436
+msgid "duplicate SASL authentication request\n"
+msgstr "duplicitní SASL autentizační požadavek\n"
+
+#: fe-auth.c:492
+msgid "channel binding is required, but client does not support it\n"
+msgstr "channel binding je vyžadován, ale klient ho nepodporuje\n"
+
+#: fe-auth.c:509
+msgid "server offered SCRAM-SHA-256-PLUS authentication over a non-SSL connection\n"
+msgstr "server nabídl SCRAM-SHA-256-PLUS authentizaci přes ne-SSL spojení\n"
+
+#: fe-auth.c:521
+msgid "none of the server's SASL authentication mechanisms are supported\n"
+msgstr "žádný ze SASL authentizačních mechanismů serveru není podporován\n"
+
+#: fe-auth.c:529
+msgid "channel binding is required, but server did not offer an authentication method that supports channel binding\n"
+msgstr "channel binding je vyžadováno, ale server nenabídl methodu autentizace které channel binding podporuje\n"
+
+#: fe-auth.c:635
+#, c-format
+msgid "out of memory allocating SASL buffer (%d)\n"
+msgstr "při alokaci SASL bufferu došla paměť (%d)\n"
+
+#: fe-auth.c:660
+msgid "AuthenticationSASLFinal received from server, but SASL authentication was not completed\n"
+msgstr "AuthenticationSASLFinal obdržena od serveru, ale SASL authentizace nebyla dokončena\n"
+
+#: fe-auth.c:737
+msgid "SCM_CRED authentication method not supported\n"
+msgstr "SCM_CRED metoda autentizace není podporována\n"
+
+#: fe-auth.c:836
+msgid "channel binding required, but server authenticated client without channel binding\n"
+msgstr "channel binding vyžadováno, ale server authentizoval klienta bez channel binding\n"
+
+#: fe-auth.c:842
+msgid "channel binding required but not supported by server's authentication request\n"
+msgstr "channel binding je vyžadován ale není podporován autentizační metodou serveru\n"
+
+#: fe-auth.c:875
+msgid "Kerberos 4 authentication not supported\n"
+msgstr "Kerberos 4 autentizace není podporována\n"
+
+#: fe-auth.c:880
+msgid "Kerberos 5 authentication not supported\n"
+msgstr "Kerberos 5 autentizace není podporována\n"
+
+#: fe-auth.c:951
+msgid "GSSAPI authentication not supported\n"
+msgstr "GSSAPI autentizace není podporována\n"
+
+#: fe-auth.c:983
+msgid "SSPI authentication not supported\n"
+msgstr "SSPI autentizace není podporována\n"
+
+#: fe-auth.c:991
+msgid "Crypt authentication not supported\n"
+msgstr "Crypt autentizace není podporována\n"
+
+#: fe-auth.c:1057
+#, c-format
+msgid "authentication method %u not supported\n"
+msgstr "autentizační metoda %u není podporována\n"
+
+#: fe-auth.c:1104
+#, c-format
+msgid "user name lookup failure: error code %lu\n"
+msgstr "vyhledání uživatele selhalo: chybový kód %lu\n"
+
+#: fe-auth.c:1114 fe-connect.c:2834
+#, c-format
+msgid "could not look up local user ID %d: %s\n"
+msgstr "nelze vyhledat lokálního uživatele ID %d: %s\n"
+
+#: fe-auth.c:1119 fe-connect.c:2839
+#, c-format
+msgid "local user with ID %d does not exist\n"
+msgstr "lokální uživatel s ID %d neexistuje\n"
+
+#: fe-auth.c:1221
+msgid "unexpected shape of result set returned for SHOW\n"
+msgstr "neočekávaná podoba výsledku pro SHOW\n"
+
+#: fe-auth.c:1230
+msgid "password_encryption value too long\n"
+msgstr "hodnota password_encryption je příliš dlouhá\n"
+
+#: fe-auth.c:1270
+#, c-format
+msgid "unrecognized password encryption algorithm \"%s\"\n"
+msgstr "neznámý algoritmus pro šifrování hesla \"%s\"\n"
+
+#: fe-connect.c:1075
+#, c-format
+msgid "could not match %d host names to %d hostaddr values\n"
+msgstr "nelze napárovat %d jmen hostů na %d hostaddr hodnot\n"
+
+#: fe-connect.c:1156
+#, c-format
+msgid "could not match %d port numbers to %d hosts\n"
+msgstr "nelze napárovat %d čísel portů na %d hostů\n"
+
+#: fe-connect.c:1249
+#, c-format
+#| msgid "invalid channel binding type\n"
+msgid "invalid channel_binding value: \"%s\"\n"
+msgstr "neplatná hodnota channel_binding: \"%s\"\n"
+
+#: fe-connect.c:1275
+#, c-format
+msgid "invalid sslmode value: \"%s\"\n"
+msgstr "neplatná hodnota sslmode: \"%s\"\n"
+
+#: fe-connect.c:1296
+#, c-format
+msgid "sslmode value \"%s\" invalid when SSL support is not compiled in\n"
+msgstr "hodnota sslmode \"%s\" je neplatná pokud není zakompilována podpora SSL\n"
+
+#: fe-connect.c:1317
+#, c-format
+msgid "invalid ssl_min_protocol_version value: \"%s\"\n"
+msgstr "neplatná hodnodta ssl_min_protocol_version: \"%s\"\n"
+
+#: fe-connect.c:1325
+#, c-format
+#| msgid "invalid sslmode value: \"%s\"\n"
+msgid "invalid ssl_max_protocol_version value: \"%s\"\n"
+msgstr "neplatná hodnota ssl_max_protocol_version: \"%s\"\n"
+
+#: fe-connect.c:1342
+msgid "invalid SSL protocol version range\n"
+msgstr "neplatný rozsah verzí SSL protokolu\n"
+
+#: fe-connect.c:1357
+#, c-format
+msgid "invalid gssencmode value: \"%s\"\n"
+msgstr "invalid gssencmode gssencmode: \"%s\"\n"
+
+#: fe-connect.c:1366
+#, c-format
+msgid "gssencmode value \"%s\" invalid when GSSAPI support is not compiled in\n"
+msgstr "gssencmode hodnota \"%s\" je neplatná při nezakompilované GSSAPI podpoře\n"
+
+#: fe-connect.c:1401
+#, c-format
+msgid "invalid target_session_attrs value: \"%s\"\n"
+msgstr "neplatná hodnota target_session_attrs: \"%s\"\n"
+
+#: fe-connect.c:1619
+#, c-format
+msgid "could not set socket to TCP no delay mode: %s\n"
+msgstr "nelze nastavit \"no delay\" mód TCP soketu: %s\n"
+
+#: fe-connect.c:1680
+#, c-format
+msgid ""
+"could not connect to server: %s\n"
+"\tIs the server running locally and accepting\n"
+"\tconnections on Unix domain socket \"%s\"?\n"
+msgstr ""
+"nelze navázat spojení se serverem: %s\n"
+"\tJe spuštěn server lokálně a akceptuje\n"
+"\tspojení pomocí Unix soketu \"%s\"?\n"
+
+#: fe-connect.c:1717
+#, c-format
+msgid ""
+"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"
+msgstr ""
+"nelze navázat spojení se serverem: %s\n"
+"\tJe server na \"%s\" (%s) spuštěn a akceptuje\n"
+"\tTCP/IP spojení na portu %s?\n"
+
+#: fe-connect.c:1725
+#, c-format
+msgid ""
+"could not connect to server: %s\n"
+"\tIs the server running on host \"%s\" and accepting\n"
+"\tTCP/IP connections on port %s?\n"
+msgstr ""
+"nelze navázat spojení se serverem: %s\n"
+"\tJe server na \"%s\" spuštěn a akceptuje\n"
+"\tTCP/IP spojení na portu %s?\n"
+
+#: fe-connect.c:1795
+#, c-format
+msgid "invalid integer value \"%s\" for connection option \"%s\"\n"
+msgstr "chybná integer hodnota \"%s\" pro volbu spojení \"%s\"\n"
+
+#: fe-connect.c:1825 fe-connect.c:1859 fe-connect.c:1894 fe-connect.c:1981
+#: fe-connect.c:2623
+#, c-format
+msgid "setsockopt(%s) failed: %s\n"
+msgstr "setsockopt(%s) selhalo: %s\n"
+
+#: fe-connect.c:1947
+#, c-format
+msgid "WSAIoctl(SIO_KEEPALIVE_VALS) failed: %ui\n"
+msgstr "WSAIoctl(SIO_KEEPALIVE_VALS) selhalo: %ui\n"
+
+#: fe-connect.c:2313
+msgid "invalid connection state, probably indicative of memory corruption\n"
+msgstr "neplatný stav spojení, pravděpodobně způsobený poškozením paměti\n"
+
+#: fe-connect.c:2379
+#, c-format
+msgid "invalid port number: \"%s\"\n"
+msgstr "neplatné číslo portu: \"%s\"\n"
+
+#: fe-connect.c:2395
+#, c-format
+msgid "could not translate host name \"%s\" to address: %s\n"
+msgstr "nemohu přeložit jméno hostitele \"%s\" na adresu: %s\n"
+
+#: fe-connect.c:2408
+#, c-format
+msgid "could not parse network address \"%s\": %s\n"
+msgstr "nelze naparsovat síťovou adresu \"%s\": %s\n"
+
+#: fe-connect.c:2421
+#, c-format
+msgid "Unix-domain socket path \"%s\" is too long (maximum %d bytes)\n"
+msgstr "Cesta k unixovému \"%s\" je příliš dlouhá (maximum %d bytů)\n"
+
+#: fe-connect.c:2436
+#, c-format
+msgid "could not translate Unix-domain socket path \"%s\" to address: %s\n"
+msgstr "nemohu přeložit cestu Unix-domain soketu \"%s\" na adresu: %s\n"
+
+#: fe-connect.c:2560
+#, c-format
+msgid "could not create socket: %s\n"
+msgstr "nelze vytvořit soket: %s\n"
+
+#: fe-connect.c:2582
+#, c-format
+msgid "could not set socket to nonblocking mode: %s\n"
+msgstr "soket nelze nastavit do neblokujícího módu: %s\n"
+
+#: fe-connect.c:2592
+#, c-format
+msgid "could not set socket to close-on-exec mode: %s\n"
+msgstr "nelze nastavit soket do close-on-exec módu: %s\n"
+
+#: fe-connect.c:2610
+msgid "keepalives parameter must be an integer\n"
+msgstr "parametr keepalives musí být celé číslo\n"
+
+#: fe-connect.c:2750
+#, c-format
+msgid "could not get socket error status: %s\n"
+msgstr "nelze obdržet chybový status soketu: %s\n"
+
+#: fe-connect.c:2778
+#, c-format
+msgid "could not get client address from socket: %s\n"
+msgstr "nelze získat adresu klienta ze soketu: %s\n"
+
+#: fe-connect.c:2820
+msgid "requirepeer parameter is not supported on this platform\n"
+msgstr "parametr requirepeer není na této platformě podporován\n"
+
+#: fe-connect.c:2823
+#, c-format
+msgid "could not get peer credentials: %s\n"
+msgstr "nelze získat informace (credentials) protistrany: %s\n"
+
+#: fe-connect.c:2847
+#, c-format
+msgid "requirepeer specifies \"%s\", but actual peer user name is \"%s\"\n"
+msgstr "requirepeer obsahuje \"%s\", ale skutečné jméno peera je \"%s\"\n"
+
+#: fe-connect.c:2887
+#, c-format
+msgid "could not send GSSAPI negotiation packet: %s\n"
+msgstr "nelze zaslat GSSAPI negociační packet: %s\n"
+
+#: fe-connect.c:2899
+msgid "GSSAPI encryption required but was impossible (possibly no credential cache, no server support, or using a local socket)\n"
+msgstr "GSSAPI šifrování je vyžadováno ale bylo nemožné (možná kvůli chybějící credential cache, podpoře na serveru, nebo používání lokálního socketu)\n"
+
+#: fe-connect.c:2926
+#, c-format
+msgid "could not send SSL negotiation packet: %s\n"
+msgstr "nelze poslat SSL \"negotiation paket\": %s\n"
+
+#: fe-connect.c:2965
+#, c-format
+msgid "could not send startup packet: %s\n"
+msgstr "nelze poslat počáteční paket: %s\n"
+
+#: fe-connect.c:3035
+msgid "server does not support SSL, but SSL was required\n"
+msgstr "server nepodporuje SSL, leč SSL je vyžadováno\n"
+
+#: fe-connect.c:3061
+#, c-format
+msgid "received invalid response to SSL negotiation: %c\n"
+msgstr "přijata neplatná odpověď na SSL negotiation: %c\n"
+
+#: fe-connect.c:3151
+msgid "server doesn't support GSSAPI encryption, but it was required\n"
+msgstr "server nepodporuje GSSAPI šifrování, to ale bylo vyžadováno\n"
+
+#: fe-connect.c:3162
+#, c-format
+msgid "received invalid response to GSSAPI negotiation: %c\n"
+msgstr "přijata neplatná odpověď na GSSAPI negotiation: %c\n"
+
+#: fe-connect.c:3229 fe-connect.c:3260
+#, c-format
+msgid "expected authentication request from server, but received %c\n"
+msgstr "očekáván byl autentizační dotaz ze serveru, ale přijat byl %c\n"
+
+#: fe-connect.c:3502
+msgid "unexpected message from server during startup\n"
+msgstr "neočekávaná zpráva ze serveru během startu\n"
+
+#: fe-connect.c:3707
+#, c-format
+msgid "could not make a writable connection to server \"%s:%s\"\n"
+msgstr "nelze otevřít zapisovatelné spojení na server \"%s:%s\"\n"
+
+#: fe-connect.c:3753
+#, c-format
+msgid "test \"SHOW transaction_read_only\" failed on server \"%s:%s\"\n"
+msgstr "test \"SHOW transaction_read_only\" selhal na serveru \"%s:%s\"\n"
+
+#: fe-connect.c:3768
+#, c-format
+msgid "invalid connection state %d, probably indicative of memory corruption\n"
+msgstr "neplatný stav spojení %d, pravděpodobně způsobený poškozením paměti\n"
+
+#: fe-connect.c:4207 fe-connect.c:4267
+#, c-format
+msgid "PGEventProc \"%s\" failed during PGEVT_CONNRESET event\n"
+msgstr "PGEventProc \"%s\" selhalo během události PGEVT_CONNRESET\n"
+
+#: fe-connect.c:4614
+#, c-format
+msgid "invalid LDAP URL \"%s\": scheme must be ldap://\n"
+msgstr "naplatné LDAP URL \"%s\": schéma musí být ldap://\n"
+
+#: fe-connect.c:4629
+#, c-format
+msgid "invalid LDAP URL \"%s\": missing distinguished name\n"
+msgstr "neplatné LDAP URL \"%s\": chybí rozlišující jméno\n"
+
+#: fe-connect.c:4641 fe-connect.c:4696
+#, c-format
+msgid "invalid LDAP URL \"%s\": must have exactly one attribute\n"
+msgstr "neplatné LDAP URL \"%s\": musí mít právě jeden atribut\n"
+
+#: fe-connect.c:4652 fe-connect.c:4711
+#, c-format
+msgid "invalid LDAP URL \"%s\": must have search scope (base/one/sub)\n"
+msgstr "naplatné LDAP URL \"%s\": musí mít vyhledávací rozsah (base/one/sub)\n"
+
+#: fe-connect.c:4663
+#, c-format
+msgid "invalid LDAP URL \"%s\": no filter\n"
+msgstr "naplatné LDAP URL \"%s\": není filter\n"
+
+#: fe-connect.c:4684
+#, c-format
+msgid "invalid LDAP URL \"%s\": invalid port number\n"
+msgstr "naplatné LDAP URL \"%s\": neplatný číslo portu\n"
+
+#: fe-connect.c:4720
+msgid "could not create LDAP structure\n"
+msgstr "nelze vytvořit LDAP strukturu\n"
+
+#: fe-connect.c:4796
+#, c-format
+msgid "lookup on LDAP server failed: %s\n"
+msgstr "vyhledávání na LDAP serveru selhalo: %s\n"
+
+#: fe-connect.c:4807
+msgid "more than one entry found on LDAP lookup\n"
+msgstr "nalezen více jak jeden záznam při LDAP vyhledávání\n"
+
+#: fe-connect.c:4808 fe-connect.c:4820
+msgid "no entry found on LDAP lookup\n"
+msgstr "nebyl nalezen žádný záznam při LDAP vyhledávání\n"
+
+#: fe-connect.c:4831 fe-connect.c:4844
+msgid "attribute has no values on LDAP lookup\n"
+msgstr "atribut nemá žádnou hodnotu při LDAP vyhledávání\n"
+
+#: fe-connect.c:4896 fe-connect.c:4915 fe-connect.c:5447
+#, c-format
+msgid "missing \"=\" after \"%s\" in connection info string\n"
+msgstr "chybné \"=\" po \"%s\" v informačním řetězci spojení\n"
+
+#: fe-connect.c:4988 fe-connect.c:5632 fe-connect.c:6406
+#, c-format
+msgid "invalid connection option \"%s\"\n"
+msgstr "neplatný parametr spojení \"%s\"\n"
+
+#: fe-connect.c:5004 fe-connect.c:5496
+msgid "unterminated quoted string in connection info string\n"
+msgstr "neukončený řetězec v uvozovkách v informačním řetězci spojení\n"
+
+#: fe-connect.c:5087
+#, c-format
+msgid "definition of service \"%s\" not found\n"
+msgstr "definice služby \"%s\" nenalezena\n"
+
+#: fe-connect.c:5110
+#, c-format
+msgid "service file \"%s\" not found\n"
+msgstr "soubor se seznamem služeb \"%s\" nebyl nalezen\n"
+
+#: fe-connect.c:5125
+#, c-format
+msgid "line %d too long in service file \"%s\"\n"
+msgstr "řádek %d v souboru se seznamem služeb \"%s\" je příliš dlouhý\n"
+
+#: fe-connect.c:5197 fe-connect.c:5241
+#, c-format
+msgid "syntax error in service file \"%s\", line %d\n"
+msgstr "syntaktická chyba v souboru se seznamu služeb \"%s\", řádek %d\n"
+
+#: fe-connect.c:5208
+#, c-format
+msgid "nested service specifications not supported in service file \"%s\", line %d\n"
+msgstr "vnořené specifikace služeb nejsou podporovány v service souboru \"%s\", řádek %d\n"
+
+#: fe-connect.c:5928
+#, c-format
+msgid "invalid URI propagated to internal parser routine: \"%s\"\n"
+msgstr "neplatné URI propagované do interní procedury parseru: \"%s\"\n"
+
+#: fe-connect.c:6005
+#, c-format
+msgid "end of string reached when looking for matching \"]\" in IPv6 host address in URI: \"%s\"\n"
+msgstr "při hledání odpovídajícího znaku \"]\" v IPv6 adrese hostitele byl dosažen konec řetězce URI: \"%s\"\n"
+
+#: fe-connect.c:6012
+#, c-format
+msgid "IPv6 host address may not be empty in URI: \"%s\"\n"
+msgstr "IPv6 adresa hostitele v URI nesmí být prázdná: \"%s\"\n"
+
+#: fe-connect.c:6027
+#, c-format
+msgid "unexpected character \"%c\" at position %d in URI (expected \":\" or \"/\"): \"%s\"\n"
+msgstr "neočekávaný znak \"%c\" na pozici %d v URI (očekáváno \":\" nebo \"/\"): \"%s\"\n"
+
+#: fe-connect.c:6156
+#, c-format
+msgid "extra key/value separator \"=\" in URI query parameter: \"%s\"\n"
+msgstr "přebytečný oddělovač klíče/hodnoty \"=\" v URI parametru dotazu: \"%s\"\n"
+
+#: fe-connect.c:6176
+#, c-format
+msgid "missing key/value separator \"=\" in URI query parameter: \"%s\"\n"
+msgstr "chybějící oddělovač klíče/hodnoty \"=\" v URI parametru dotazu: \"%s\"\n"
+
+#: fe-connect.c:6227
+#, c-format
+msgid "invalid URI query parameter: \"%s\"\n"
+msgstr "neplatný parametr v URI dotazu: \"%s\"\n"
+
+#: fe-connect.c:6301
+#, c-format
+msgid "invalid percent-encoded token: \"%s\"\n"
+msgstr "neplatný procenty-kódovaný token \"%s\"\n"
+
+#: fe-connect.c:6311
+#, c-format
+msgid "forbidden value %%00 in percent-encoded value: \"%s\"\n"
+msgstr "zakázaná hodnota %%00 v procenty-k´odované hodnotě: \"%s\"\n"
+
+#: fe-connect.c:6674
+msgid "connection pointer is NULL\n"
+msgstr "pointer spojení je NULL\n"
+
+#: fe-connect.c:6970
+#, c-format
+msgid "WARNING: password file \"%s\" is not a plain file\n"
+msgstr "VAROVÁNÍ: soubor s hesly \"%s\" není prostý (plain) soubor\n"
+
+#: fe-connect.c:6979
+#, c-format
+msgid "WARNING: password file \"%s\" has group or world access; permissions should be u=rw (0600) or less\n"
+msgstr "UPOZORNĚNÍ: Soubor s hesly \"%s\" má přístupová práva pro čtení pro skupinu nebo všechny uživatele; práva by měla být u=rw (0600)\n"
+
+#: fe-connect.c:7087
+#, c-format
+msgid "password retrieved from file \"%s\"\n"
+msgstr "heslo načteno ze souboru \"%s\"\n"
+
+#: fe-exec.c:444 fe-exec.c:2821
+#, c-format
+msgid "row number %d is out of range 0..%d"
+msgstr "číslo řádky %d je mimo rozsah 0..%d"
+
+#: fe-exec.c:505 fe-protocol2.c:497 fe-protocol2.c:532 fe-protocol2.c:1050
+#: fe-protocol3.c:206 fe-protocol3.c:233 fe-protocol3.c:250 fe-protocol3.c:330
+#: fe-protocol3.c:723 fe-protocol3.c:954
+msgid "out of memory"
+msgstr "nedostatek paměti"
+
+#: fe-exec.c:506 fe-protocol2.c:1396 fe-protocol3.c:1907
+#, c-format
+msgid "%s"
+msgstr "%s"
+
+#: fe-exec.c:815
+msgid "write to server failed\n"
+msgstr "zápis na server selhal\n"
+
+#: fe-exec.c:896
+msgid "NOTICE"
+msgstr "POZNÁMKA"
+
+#: fe-exec.c:954
+msgid "PGresult cannot support more than INT_MAX tuples"
+msgstr "PGresult nemůže podporovat více než INT_MAX řádek"
+
+#: fe-exec.c:966
+msgid "size_t overflow"
+msgstr "size_t přetečení"
+
+#: fe-exec.c:1243 fe-exec.c:1301 fe-exec.c:1347
+msgid "command string is a null pointer\n"
+msgstr "řetězec příkazu je prázdný ukazatel\n"
+
+#: fe-exec.c:1307 fe-exec.c:1353 fe-exec.c:1448
+msgid "number of parameters must be between 0 and 65535\n"
+msgstr "počet parametrů musí být mezi 0 a 65535\n"
+
+#: fe-exec.c:1341 fe-exec.c:1442
+msgid "statement name is a null pointer\n"
+msgstr "název výrazu je prázdný ukazatel\n"
+
+#: fe-exec.c:1361 fe-exec.c:1524 fe-exec.c:2233 fe-exec.c:2435
+msgid "function requires at least protocol version 3.0\n"
+msgstr "funkce vyžaduje protokol alespoň 3.0 a vyšší\n"
+
+#: fe-exec.c:1479
+msgid "no connection to the server\n"
+msgstr "není spojení se serverem\n"
+
+#: fe-exec.c:1486
+msgid "another command is already in progress\n"
+msgstr "zpracovává se již jiný příkaz\n"
+
+#: fe-exec.c:1600
+msgid "length must be given for binary parameter\n"
+msgstr "délka musí být specifikována pro binarní parametr\n"
+
+#: fe-exec.c:1863
+#, c-format
+msgid "unexpected asyncStatus: %d\n"
+msgstr "neočekávaný asyncStatus: %d\n"
+
+#: fe-exec.c:1883
+#, c-format
+msgid "PGEventProc \"%s\" failed during PGEVT_RESULTCREATE event\n"
+msgstr "PGEventProc \"%s\" selhala během události PGEVT_RESULTCREATE\n"
+
+#: fe-exec.c:2043
+msgid "COPY terminated by new PQexec"
+msgstr "COPY bylo ukončeno novým PQexec"
+
+#: fe-exec.c:2051
+msgid "COPY IN state must be terminated first\n"
+msgstr "COPY IN status musí být nejdříve ukončen\n"
+
+#: fe-exec.c:2071
+msgid "COPY OUT state must be terminated first\n"
+msgstr "COPY OUT status musí být nejdříve ukončen\n"
+
+#: fe-exec.c:2079
+msgid "PQexec not allowed during COPY BOTH\n"
+msgstr "PQexec není povoleno během COPY BOTH\n"
+
+#: fe-exec.c:2325 fe-exec.c:2392 fe-exec.c:2482 fe-protocol2.c:1353
+#: fe-protocol3.c:1838
+msgid "no COPY in progress\n"
+msgstr "COPY se neprovádí\n"
+
+#: fe-exec.c:2672
+msgid "connection in wrong state\n"
+msgstr "spojení je ve špatném stavu\n"
+
+#: fe-exec.c:2703
+msgid "invalid ExecStatusType code"
+msgstr "neplatný ExecStatusType kód"
+
+#: fe-exec.c:2730
+msgid "PGresult is not an error result\n"
+msgstr "PGresult není chybový výsledek\n"
+
+#: fe-exec.c:2805 fe-exec.c:2828
+#, c-format
+msgid "column number %d is out of range 0..%d"
+msgstr "číslo sloupce %d je mimo rozsah 0..%d"
+
+#: fe-exec.c:2843
+#, c-format
+msgid "parameter number %d is out of range 0..%d"
+msgstr "číslo parametru %d je mimo rozsah 0..%d"
+
+#: fe-exec.c:3153
+#, c-format
+msgid "could not interpret result from server: %s"
+msgstr "nelze interpretovat výsledek ze serveru: %s"
+
+#: fe-exec.c:3392 fe-exec.c:3476
+msgid "incomplete multibyte character\n"
+msgstr "nekompletní multibyte znak\n"
+
+#: fe-gssapi-common.c:124
+msgid "GSSAPI name import error"
+msgstr "chyba importu GSSAPI jména"
+
+#: fe-lobj.c:154
+msgid "cannot determine OID of function lo_truncate\n"
+msgstr "nelze určit OID funkce lo_truncare\n"
+
+#: fe-lobj.c:170
+msgid "argument of lo_truncate exceeds integer range\n"
+msgstr "argument pro lo_truncate přesahuje rozsah typu integer\n"
+
+#: fe-lobj.c:221
+msgid "cannot determine OID of function lo_truncate64\n"
+msgstr "nelze určit OID funkce lo_truncare64\n"
+
+#: fe-lobj.c:279
+msgid "argument of lo_read exceeds integer range\n"
+msgstr "agrument pro lo_read přesahuje rozsah typu integer\n"
+
+#: fe-lobj.c:334
+msgid "argument of lo_write exceeds integer range\n"
+msgstr "agrument pro lo_write přesahuje rozsah typu integer\n"
+
+#: fe-lobj.c:425
+msgid "cannot determine OID of function lo_lseek64\n"
+msgstr "nelze určit OID funkce lo_lseek64\n"
+
+#: fe-lobj.c:521
+msgid "cannot determine OID of function lo_create\n"
+msgstr "nelze určit OID funkce lo_create\n"
+
+#: fe-lobj.c:600
+msgid "cannot determine OID of function lo_tell64\n"
+msgstr "nelze určit OID funkce lo_tell64\n"
+
+#: fe-lobj.c:706 fe-lobj.c:815
+#, c-format
+msgid "could not open file \"%s\": %s\n"
+msgstr "nelze otevřít soubor \"%s\": %s\n"
+
+#: fe-lobj.c:761
+#, c-format
+msgid "could not read from file \"%s\": %s\n"
+msgstr "nelze číst ze souboru \"%s\": %s\n"
+
+#: fe-lobj.c:835 fe-lobj.c:859
+#, c-format
+msgid "could not write to file \"%s\": %s\n"
+msgstr "nelze zapsat do souboru \"%s\": %s\n"
+
+#: fe-lobj.c:946
+msgid "query to initialize large object functions did not return data\n"
+msgstr "dotaz inicializující \"large object\" funkce nevrátil data\n"
+
+#: fe-lobj.c:995
+msgid "cannot determine OID of function lo_open\n"
+msgstr "nelze určit OID funkce lo_open\n"
+
+#: fe-lobj.c:1002
+msgid "cannot determine OID of function lo_close\n"
+msgstr "nelze určit OID funkce lo_close\n"
+
+#: fe-lobj.c:1009
+msgid "cannot determine OID of function lo_creat\n"
+msgstr "nelze určit OID funkce lo_create\n"
+
+#: fe-lobj.c:1016
+msgid "cannot determine OID of function lo_unlink\n"
+msgstr "nelze určit OID funkce lo_unlink\n"
+
+#: fe-lobj.c:1023
+msgid "cannot determine OID of function lo_lseek\n"
+msgstr "nelze určit OID funkce lo_lseek\n"
+
+#: fe-lobj.c:1030
+msgid "cannot determine OID of function lo_tell\n"
+msgstr "nelze určit OID funkce lo_tell\n"
+
+#: fe-lobj.c:1037
+msgid "cannot determine OID of function loread\n"
+msgstr "nelze určit OID funkce loread\n"
+
+#: fe-lobj.c:1044
+msgid "cannot determine OID of function lowrite\n"
+msgstr "nelze určit OID funkce lowrite\n"
+
+#: fe-misc.c:289
+#, c-format
+msgid "integer of size %lu not supported by pqGetInt"
+msgstr "pqGetInt nepodporuje integer velikosti %lu"
+
+#: fe-misc.c:325
+#, c-format
+msgid "integer of size %lu not supported by pqPutInt"
+msgstr "pqPutInt nepodporuje integer velikosti %lu"
+
+#: fe-misc.c:636 fe-misc.c:869
+msgid "connection not open\n"
+msgstr "spojení není otevřeno\n"
+
+#: fe-misc.c:805 fe-secure-openssl.c:209 fe-secure-openssl.c:316
+#: fe-secure.c:267 fe-secure.c:383
+msgid ""
+"server closed the connection unexpectedly\n"
+"\tThis probably means the server terminated abnormally\n"
+"\tbefore or while processing the request.\n"
+msgstr ""
+"server neočekávaně ukončil spojení\n"
+"\tToto pravděpodobně znamená, že byl ukončen nestandardně\n"
+"\tpřed nebo během vykonávání požadavku.\n"
+
+#: fe-misc.c:1063
+msgid "timeout expired\n"
+msgstr "časový limit (timeout) uběhl\n"
+
+#: fe-misc.c:1108
+msgid "invalid socket\n"
+msgstr "chybný socket\n"
+
+#: fe-misc.c:1131
+#, c-format
+msgid "select() failed: %s\n"
+msgstr "select() selhal: %s\n"
+
+#: fe-protocol2.c:87
+#, c-format
+msgid "invalid setenv state %c, probably indicative of memory corruption\n"
+msgstr "neplatný status spojení %c, pravděpodobně způsobený poškozením paměti\n"
+
+#: fe-protocol2.c:384
+#, c-format
+msgid "invalid state %c, probably indicative of memory corruption\n"
+msgstr "neplatný status %c, pravděpodobně způsobený poškozením paměti\n"
+
+#: fe-protocol2.c:473 fe-protocol3.c:183
+#, c-format
+msgid "message type 0x%02x arrived from server while idle"
+msgstr "zpráva typu 0x%02x přišla ze serveru během nečinnosti"
+
+#: fe-protocol2.c:523
+#, c-format
+msgid "unexpected character %c following empty query response (\"I\" message)"
+msgstr "neočekávaný znak %c následuje prázdnou odezvu dotazu(\"I\" zpráva)"
+
+#: fe-protocol2.c:589
+#, c-format
+msgid "server sent data (\"D\" message) without prior row description (\"T\" message)"
+msgstr "server odeslal data (\"D\" zpráva) bez předcházejícího popisu řádky (\"T\" zpráva)"
+
+#: fe-protocol2.c:607
+#, c-format
+msgid "server sent binary data (\"B\" message) without prior row description (\"T\" message)"
+msgstr "server odeslal binární data (\"B\" zpráva) bez předchozího popisu řádky (\"T\" zpráva)"
+
+#: fe-protocol2.c:626 fe-protocol3.c:408
+#, c-format
+msgid "unexpected response from server; first received character was \"%c\"\n"
+msgstr "neočekávaná odpověď serveru; předchozí znak byl \"%c\"\n"
+
+#: fe-protocol2.c:755 fe-protocol2.c:930 fe-protocol3.c:622 fe-protocol3.c:849
+msgid "out of memory for query result"
+msgstr "nedostatek paměti pro výsledek dotazu"
+
+#: fe-protocol2.c:1408
+#, c-format
+msgid "lost synchronization with server, resetting connection"
+msgstr "ztráta synchronizace se serverem, resetuji spojení"
+
+#: fe-protocol2.c:1530 fe-protocol2.c:1562 fe-protocol3.c:2095
+#, c-format
+msgid "protocol error: id=0x%x\n"
+msgstr "chyba protokolu: id=0x%x\n"
+
+#: fe-protocol3.c:365
+msgid "server sent data (\"D\" message) without prior row description (\"T\" message)\n"
+msgstr "server odeslal data (\"D\" zpráva) bez předchozího popisu řádky (\"T\" zpráva)\n"
+
+#: fe-protocol3.c:429
+#, c-format
+msgid "message contents do not agree with length in message type \"%c\"\n"
+msgstr "obsah zprávy nesouhlasí s délkou v typu zprávy \"%c\"\n"
+
+#: fe-protocol3.c:449
+#, c-format
+msgid "lost synchronization with server: got message type \"%c\", length %d\n"
+msgstr "ztracena synchronizace se serverem: obdržena zpráva typu \"%c\", délky %d\n"
+
+#: fe-protocol3.c:500 fe-protocol3.c:540
+msgid "insufficient data in \"T\" message"
+msgstr "nedostatek dat v \"T\" zprávě"
+
+#: fe-protocol3.c:573
+msgid "extraneous data in \"T\" message"
+msgstr "přebytečná data v \"T\" zprávě"
+
+#: fe-protocol3.c:686
+msgid "extraneous data in \"t\" message"
+msgstr "přebytečná data v \"t\" zprávě"
+
+#: fe-protocol3.c:757 fe-protocol3.c:789 fe-protocol3.c:807
+msgid "insufficient data in \"D\" message"
+msgstr "nedostatek dat v \"D\" zprávě"
+
+#: fe-protocol3.c:763
+msgid "unexpected field count in \"D\" message"
+msgstr "neočekávaný počet položek v \"D\" zprávě"
+
+#: fe-protocol3.c:816
+msgid "extraneous data in \"D\" message"
+msgstr "přebytečná data v \"D\" zprávě"
+
+#: fe-protocol3.c:1008
+msgid "no error message available\n"
+msgstr "chybová zpráva není k dispozici\n"
+
+#. translator: %s represents a digit string
+#: fe-protocol3.c:1056 fe-protocol3.c:1075
+#, c-format
+msgid " at character %s"
+msgstr " na znaku %s"
+
+#: fe-protocol3.c:1088
+#, c-format
+msgid "DETAIL: %s\n"
+msgstr "DETAIL: %s\n"
+
+#: fe-protocol3.c:1091
+#, c-format
+msgid "HINT: %s\n"
+msgstr "DOPORUČENÍ: %s\n"
+
+#: fe-protocol3.c:1094
+#, c-format
+msgid "QUERY: %s\n"
+msgstr "DOTAZ: %s\n"
+
+#: fe-protocol3.c:1101
+#, c-format
+msgid "CONTEXT: %s\n"
+msgstr "KONTEXT: %s\n"
+
+#: fe-protocol3.c:1110
+#, c-format
+msgid "SCHEMA NAME: %s\n"
+msgstr "NÁZEV SCHÉMATU: %s\n"
+
+#: fe-protocol3.c:1114
+#, c-format
+msgid "TABLE NAME: %s\n"
+msgstr "NÁZEV TABULKY: %s\n"
+
+#: fe-protocol3.c:1118
+#, c-format
+msgid "COLUMN NAME: %s\n"
+msgstr "NÁZEV SLOUPCE: %s\n"
+
+#: fe-protocol3.c:1122
+#, c-format
+msgid "DATATYPE NAME: %s\n"
+msgstr "NÁZEV DATOVÉHO TYPU: %s\n"
+
+#: fe-protocol3.c:1126
+#, c-format
+msgid "CONSTRAINT NAME: %s\n"
+msgstr "NÁZEV OMEZENÍ: %s\n"
+
+#: fe-protocol3.c:1138
+msgid "LOCATION: "
+msgstr "UMÍSTĚNÍ: "
+
+#: fe-protocol3.c:1140
+#, c-format
+msgid "%s, "
+msgstr "%s, "
+
+#: fe-protocol3.c:1142
+#, c-format
+msgid "%s:%s"
+msgstr "%s:%s"
+
+#: fe-protocol3.c:1337
+#, c-format
+msgid "LINE %d: "
+msgstr "ŘÁDKA %d: "
+
+#: fe-protocol3.c:1732
+msgid "PQgetline: not doing text COPY OUT\n"
+msgstr "PQgetline: not doing text COPY OUT\n"
+
+#: fe-secure-common.c:124
+msgid "SSL certificate's name contains embedded null\n"
+msgstr "jméno SSL certifikátu obsahuje vloženou null hodnotu\n"
+
+#: fe-secure-common.c:171
+msgid "host name must be specified for a verified SSL connection\n"
+msgstr "host musí být specifikován pro ověřené SSL spojení\n"
+
+#: fe-secure-common.c:196
+#, c-format
+msgid "server certificate for \"%s\" does not match host name \"%s\"\n"
+msgstr "serverový certifikát pro \"%s\" nesouhlasí s jménem serveru (host name) \"%s\"\n"
+
+#: fe-secure-common.c:202
+msgid "could not get server's host name from server certificate\n"
+msgstr "ze serverového certifikátu nelze získat host name serveru\n"
+
+#: fe-secure-gssapi.c:201
+msgid "GSSAPI wrap error"
+msgstr "GSSAPI wrap error"
+
+#: fe-secure-gssapi.c:209
+msgid "outgoing GSSAPI message would not use confidentiality\n"
+msgstr "odchozí GSSAPI zpráva by nepoužívala důvěrnost (confidentiality)\n"
+
+#: fe-secure-gssapi.c:217
+#, c-format
+msgid "client tried to send oversize GSSAPI packet (%zu > %zu)\n"
+msgstr "klient se pokusil zaslat příliš velký GSSAPI packet (%zu > %zu)\n"
+
+#: fe-secure-gssapi.c:354 fe-secure-gssapi.c:596
+#, c-format
+msgid "oversize GSSAPI packet sent by the server (%zu > %zu)\n"
+msgstr "příliš velký GSSAPI packet zaslán serverem (%zu > %zu)\n"
+
+#: fe-secure-gssapi.c:393
+msgid "GSSAPI unwrap error"
+msgstr "GSSAPI unwrap error"
+
+#: fe-secure-gssapi.c:403
+msgid "incoming GSSAPI message did not use confidentiality\n"
+msgstr "příchozí GSSAPI zpráva nepoužívala důvěrnost (confidentiality)\n"
+
+#: fe-secure-gssapi.c:642
+msgid "could not initiate GSSAPI security context"
+msgstr "nelze inicializovat GSSAPI bezpečnostní kontext"
+
+#: fe-secure-gssapi.c:673
+msgid "GSSAPI size check error"
+msgstr "GSSAPI size check error"
+
+#: fe-secure-gssapi.c:684
+msgid "GSSAPI context establishment error"
+msgstr "GSSAPI context establishment error"
+
+#: fe-secure-openssl.c:214 fe-secure-openssl.c:321 fe-secure-openssl.c:1291
+#, c-format
+msgid "SSL SYSCALL error: %s\n"
+msgstr "SSL SYSCALL chyba: %s\n"
+
+#: fe-secure-openssl.c:221 fe-secure-openssl.c:328 fe-secure-openssl.c:1295
+msgid "SSL SYSCALL error: EOF detected\n"
+msgstr "SSL SYSCALL chyba: detekován EOF\n"
+
+#: fe-secure-openssl.c:232 fe-secure-openssl.c:339 fe-secure-openssl.c:1304
+#, c-format
+msgid "SSL error: %s\n"
+msgstr "SSL chyba: %s\n"
+
+#: fe-secure-openssl.c:247 fe-secure-openssl.c:354
+msgid "SSL connection has been closed unexpectedly\n"
+msgstr "SSL spojení bylo neočekávaně ukončeno\n"
+
+#: fe-secure-openssl.c:253 fe-secure-openssl.c:360 fe-secure-openssl.c:1354
+#, c-format
+msgid "unrecognized SSL error code: %d\n"
+msgstr "neznámý chybový kód SSL: %d\n"
+
+#: fe-secure-openssl.c:400
+msgid "could not determine server certificate signature algorithm\n"
+msgstr "nelze určit podepisovací algoritmus serverového certifikátu\n"
+
+#: fe-secure-openssl.c:421
+#, c-format
+msgid "could not find digest for NID %s\n"
+msgstr "nelze nalézt digest pro NID %s\n"
+
+#: fe-secure-openssl.c:431
+msgid "could not generate peer certificate hash\n"
+msgstr "nelze vygenerovat hash peer cerfitikátu\n"
+
+#: fe-secure-openssl.c:488
+msgid "SSL certificate's name entry is missing\n"
+msgstr "SSL certifikátu chybí položka name\n"
+
+#: fe-secure-openssl.c:815
+#, c-format
+msgid "could not create SSL context: %s\n"
+msgstr "nelze vytvořit SSL kontext: %s\n"
+
+#: fe-secure-openssl.c:854
+#, c-format
+#| msgid "invalid integer value \"%s\" for connection option \"%s\"\n"
+msgid "invalid value \"%s\" for minimum SSL protocol version\n"
+msgstr "neplatná hodnota \"%s\" pro minimální verzi SSL protokolu\n"
+
+#: fe-secure-openssl.c:865
+#, c-format
+#| msgid "could not establish SSL connection: %s\n"
+msgid "could not set minimum SSL protocol version: %s\n"
+msgstr "nelze nastavit minimální verzi SSL protokolu: %s\n"
+
+#: fe-secure-openssl.c:883
+#, c-format
+msgid "invalid value \"%s\" for maximum SSL protocol version\n"
+msgstr "neplatná hodnota \"%s\" pro maximální verzi SSL protokolu\n"
+
+#: fe-secure-openssl.c:894
+#, c-format
+#| msgid "could not establish SSL connection: %s\n"
+msgid "could not set maximum SSL protocol version: %s\n"
+msgstr "nelze nastavit maximální verzi SSL protokolu: %s\n"
+
+#: fe-secure-openssl.c:930
+#, c-format
+msgid "could not read root certificate file \"%s\": %s\n"
+msgstr "nelze číst soubor s kořenovým certifikátem \"%s\": %s\n"
+
+#: fe-secure-openssl.c:974
+msgid ""
+"could not get home directory to locate root certificate file\n"
+"Either provide the file or change sslmode to disable server certificate verification.\n"
+msgstr ""
+"nelze určit domácí adresář pro nalezení souboru s kořenovým certifikátem\n"
+"Buď poskytněte soubor nebo změňte ssl mód tak, aby neověřoval certifkát serveru.\n"
+
+#: fe-secure-openssl.c:978
+#, c-format
+msgid ""
+"root certificate file \"%s\" does not exist\n"
+"Either provide the file or change sslmode to disable server certificate verification.\n"
+msgstr ""
+"soubor s kořenovým certifikátem \"%s\" neexistuje\n"
+"poskytněnte soubor nebo změntě ssl mód tak, aby neověřoval certifkát serveru.\n"
+
+#: fe-secure-openssl.c:1009
+#, c-format
+msgid "could not open certificate file \"%s\": %s\n"
+msgstr "nelze otevřít soubor s certifikátem \"%s\": %s\n"
+
+#: fe-secure-openssl.c:1028
+#, c-format
+msgid "could not read certificate file \"%s\": %s\n"
+msgstr "nelze číst soubor s certifikátem \"%s\": %s\n"
+
+#: fe-secure-openssl.c:1053
+#, c-format
+msgid "could not establish SSL connection: %s\n"
+msgstr "nelze vytvořit SSL spojení: %s\n"
+
+#: fe-secure-openssl.c:1107
+#, c-format
+msgid "could not load SSL engine \"%s\": %s\n"
+msgstr "nelze nahrát SSL engine \"%s\": %s\n"
+
+#: fe-secure-openssl.c:1119
+#, c-format
+msgid "could not initialize SSL engine \"%s\": %s\n"
+msgstr "nelze inicializovat SSL engine \"%s\": %s\n"
+
+#: fe-secure-openssl.c:1135
+#, c-format
+msgid "could not read private SSL key \"%s\" from engine \"%s\": %s\n"
+msgstr "nelze číst soubor privátního klíče \"%s\" z enginu \"%s\": %s\n"
+
+#: fe-secure-openssl.c:1149
+#, c-format
+msgid "could not load private SSL key \"%s\" from engine \"%s\": %s\n"
+msgstr "nelze načíst soubor privátního klíče \"%s\" z enginu \"%s\": %s\n"
+
+#: fe-secure-openssl.c:1186
+#, c-format
+msgid "certificate present, but not private key file \"%s\"\n"
+msgstr "certifikát je přítomen, ale soubor privátního klíče ne \"%s\"\n"
+
+#: fe-secure-openssl.c:1194
+#, c-format
+msgid "private key file \"%s\" has group or world access; permissions should be u=rw (0600) or less\n"
+msgstr "soubor s privátním klíčem \"%s\" má povolená přístupová práva pro skupinu nebo všechny uživatele; práva by měla být u=rw (0600) nebo přísnější\n"
+
+#: fe-secure-openssl.c:1219
+#, c-format
+msgid "could not load private key file \"%s\": %s\n"
+msgstr "nelze načíst soubor privátního klíče \"%s\": %s\n"
+
+#: fe-secure-openssl.c:1237
+#, c-format
+msgid "certificate does not match private key file \"%s\": %s\n"
+msgstr "certifikát nesouhlasí se souborem privátního klíče \"%s\": %s\n"
+
+#: fe-secure-openssl.c:1337
+#, c-format
+msgid "This may indicate that the server does not support any SSL protocol version between %s and %s.\n"
+msgstr "Toto může znamenat že server nepodporuje verzi SSL protokolu mezi %s a %s.\n"
+
+#: fe-secure-openssl.c:1373
+#, c-format
+msgid "certificate could not be obtained: %s\n"
+msgstr "certifikát nelze získat: %s\n"
+
+#: fe-secure-openssl.c:1462
+#, c-format
+msgid "no SSL error reported"
+msgstr "žádný chybový kód SSL nebyl hlášený"
+
+#: fe-secure-openssl.c:1471
+#, c-format
+msgid "SSL error code %lu"
+msgstr "SSL chybový kód %lu"
+
+#: fe-secure-openssl.c:1718
+#, c-format
+msgid "WARNING: sslpassword truncated\n"
+msgstr "WARNING: hodnota sslpassword oříznuta\n"
+
+#: fe-secure.c:275
+#, c-format
+msgid "could not receive data from server: %s\n"
+msgstr "nelze přijmout data ze serveru: %s\n"
+
+#: fe-secure.c:390
+#, c-format
+msgid "could not send data to server: %s\n"
+msgstr "nelze poslat data na server: %s\n"
+
+#: win32.c:314
+#, c-format
+msgid "unrecognized socket error: 0x%08X/%d"
+msgstr "neznámá chyba socketu: 0x%08X/%d"
+
+#~ msgid "empty channel binding data for channel binding type \"%s\"\n"
+#~ msgstr ""
+#~ "prázdná \"channel binding data\" pro channel binding typu \"%s\"\n"
+#~ "\n"
+
+#~ msgid "could not set socket to blocking mode: %s\n"
+#~ msgstr "nelze nastavit soket do blokujícího módu: %s\n"
+
+#~ msgid "Kerberos 5 authentication rejected: %*s\n"
+#~ msgstr "Kerberos 5 autentizace odmítnuta: %*s\n"
+
+#~ msgid "setsockopt(TCP_KEEPIDLE) failed: %s\n"
+#~ msgstr "setsockopt(TCP_KEEPIDLE) selhalo: %s\n"
+
+#~ msgid "setsockopt(TCP_KEEPALIVE) failed: %s\n"
+#~ msgstr "setsockopt(TCP_KEEPALIVE) selhalo: %s\n"
+
+#~ msgid "setsockopt(TCP_KEEPINTVL) failed: %s\n"
+#~ msgstr "setsockopt(TCP_KEEPINTVL) selhalo: %s\n"
+
+#~ msgid "setsockopt(SO_KEEPALIVE) failed: %s\n"
+#~ msgstr "setsockopt(SO_KEEPALIVE) selhalo: %s\n"
+
+#~ msgid "could not get home directory to locate service definition file"
+#~ msgstr "nelze získat domovský adresář pro nalezení kořenového certifikátu"
+
+#~ msgid "socket not open\n"
+#~ msgstr "soket není otevřen\n"
+
+#~ msgid "unrecognized return value from row processor"
+#~ msgstr "nerozpoznaná návratová hodnota z processoru řádek"
+
+#~ msgid "private key file \"%s\" changed during execution\n"
+#~ msgstr "soubor privátního klíče \"%s\" byl za chodu změněn\n"
+
+#~ msgid "could not open private key file \"%s\": %s\n"
+#~ msgstr "nelze otevřít soubor s privátním klíčem \"%s\": %s\n"
+
+#~ msgid "could not get home directory to locate client certificate files"
+#~ msgstr "nelze získat domovský adresář pro nalezení klientského certifikátu"
+
+#~ msgid "SSL library does not support CRL certificates (file \"%s\")\n"
+#~ msgstr "knihovna SSL nepodporuje CRL certifikáty (soubor \"%s\")\n"
diff --git a/src/interfaces/libpq/po/de.po b/src/interfaces/libpq/po/de.po
new file mode 100644
index 0000000..31985f4
--- /dev/null
+++ b/src/interfaces/libpq/po/de.po
@@ -0,0 +1,1273 @@
+# German message translation file for libpq
+# Peter Eisentraut <peter@eisentraut.org>, 2001 - 2021.
+#
+# Use these quotes: »%s«
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: PostgreSQL 13\n"
+"Report-Msgid-Bugs-To: pgsql-bugs@lists.postgresql.org\n"
+"POT-Creation-Date: 2021-04-04 22:09+0000\n"
+"PO-Revision-Date: 2021-04-05 10:43+0200\n"
+"Last-Translator: Peter Eisentraut <peter@eisentraut.org>\n"
+"Language-Team: German <pgsql-translators@postgresql.org>\n"
+"Language: de\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: fe-auth-scram.c:212
+msgid "malformed SCRAM message (empty message)\n"
+msgstr "fehlerhafte SCRAM-Nachricht (leere Nachricht)\n"
+
+#: fe-auth-scram.c:218
+msgid "malformed SCRAM message (length mismatch)\n"
+msgstr "fehlerhafte SCRAM-Nachricht (Länge stimmt nicht überein)\n"
+
+#: fe-auth-scram.c:265
+msgid "incorrect server signature\n"
+msgstr "falsche Serversignatur\n"
+
+#: fe-auth-scram.c:274
+msgid "invalid SCRAM exchange state\n"
+msgstr "ungültiger Zustand des SCRAM-Austauschs\n"
+
+#: fe-auth-scram.c:296
+#, c-format
+msgid "malformed SCRAM message (attribute \"%c\" expected)\n"
+msgstr "fehlerhafte SCRAM-Nachricht (Attribut »%c« erwartet)\n"
+
+#: fe-auth-scram.c:305
+#, c-format
+msgid "malformed SCRAM message (expected character \"=\" for attribute \"%c\")\n"
+msgstr "fehlerhafte SCRAM-Nachricht (Zeichen »=« für Attribut »%c« erwartet)\n"
+
+#: fe-auth-scram.c:346
+msgid "could not generate nonce\n"
+msgstr "konnte Nonce nicht erzeugen\n"
+
+#: fe-auth-scram.c:356 fe-auth-scram.c:431 fe-auth-scram.c:579
+#: fe-auth-scram.c:600 fe-auth-scram.c:626 fe-auth-scram.c:641
+#: fe-auth-scram.c:691 fe-auth-scram.c:725 fe-auth.c:289 fe-auth.c:359
+#: fe-auth.c:394 fe-auth.c:611 fe-auth.c:770 fe-auth.c:1129 fe-auth.c:1277
+#: fe-connect.c:892 fe-connect.c:1419 fe-connect.c:1595 fe-connect.c:2200
+#: fe-connect.c:2223 fe-connect.c:2957 fe-connect.c:4605 fe-connect.c:4861
+#: fe-connect.c:4980 fe-connect.c:5233 fe-connect.c:5313 fe-connect.c:5412
+#: fe-connect.c:5668 fe-connect.c:5697 fe-connect.c:5769 fe-connect.c:5793
+#: fe-connect.c:5811 fe-connect.c:5912 fe-connect.c:5921 fe-connect.c:6277
+#: fe-connect.c:6427 fe-exec.c:2747 fe-exec.c:3494 fe-exec.c:3659
+#: fe-gssapi-common.c:111 fe-lobj.c:895 fe-protocol2.c:1207 fe-protocol3.c:961
+#: fe-protocol3.c:1665 fe-secure-common.c:110 fe-secure-gssapi.c:504
+#: fe-secure-openssl.c:440 fe-secure-openssl.c:1091
+msgid "out of memory\n"
+msgstr "Speicher aufgebraucht\n"
+
+#: fe-auth-scram.c:364
+msgid "could not encode nonce\n"
+msgstr "konnte Nonce nicht kodieren\n"
+
+#: fe-auth-scram.c:563
+msgid "could not encode client proof\n"
+msgstr "konnte Client-Proof nicht kodieren\n"
+
+#: fe-auth-scram.c:618
+msgid "invalid SCRAM response (nonce mismatch)\n"
+msgstr "ungültige SCRAM-Antwort (Nonce stimmt nicht überein)\n"
+
+#: fe-auth-scram.c:651
+msgid "malformed SCRAM message (invalid salt)\n"
+msgstr "fehlerhafte SCRAM-Nachricht (ungültiges Salt)\n"
+
+#: fe-auth-scram.c:665
+msgid "malformed SCRAM message (invalid iteration count)\n"
+msgstr "fehlerhafte SCRAM-Nachricht (ungültige Iterationszahl)\n"
+
+#: fe-auth-scram.c:671
+msgid "malformed SCRAM message (garbage at end of server-first-message)\n"
+msgstr "fehlerhafte SCRAM-Nachricht (Müll am Ende der »server-first-message«)\n"
+
+#: fe-auth-scram.c:702
+#, c-format
+msgid "error received from server in SCRAM exchange: %s\n"
+msgstr "Fehler vom Server empfangen im SCRAM-Austausch: %s\n"
+
+#: fe-auth-scram.c:718
+msgid "malformed SCRAM message (garbage at end of server-final-message)\n"
+msgstr "fehlerhafte SCRAM-Nachricht (Müll am Ende der »server-final-message«)\n"
+
+#: fe-auth-scram.c:737
+msgid "malformed SCRAM message (invalid server signature)\n"
+msgstr "fehlerhafte SCRAM-Nachricht (ungültige Serversignatur)\n"
+
+#: fe-auth.c:76
+#, c-format
+msgid "out of memory allocating GSSAPI buffer (%d)\n"
+msgstr "Speicher aufgebraucht beim Anlegen des GSSAPI-Puffers (%d)\n"
+
+#: fe-auth.c:131
+msgid "GSSAPI continuation error"
+msgstr "GSSAPI-Fortsetzungsfehler"
+
+#: fe-auth.c:158 fe-auth.c:388 fe-gssapi-common.c:98 fe-secure-common.c:98
+msgid "host name must be specified\n"
+msgstr "Hostname muss angegeben werden\n"
+
+#: fe-auth.c:165
+msgid "duplicate GSS authentication request\n"
+msgstr "doppelte GSSAPI-Authentifizierungsanfrage\n"
+
+#: fe-auth.c:230
+#, c-format
+msgid "out of memory allocating SSPI buffer (%d)\n"
+msgstr "Speicher aufgebraucht beim Anlegen des SSPI-Puffers (%d)\n"
+
+#: fe-auth.c:278
+msgid "SSPI continuation error"
+msgstr "SSPI-Fortsetzungsfehler"
+
+#: fe-auth.c:349
+msgid "duplicate SSPI authentication request\n"
+msgstr "doppelte SSPI-Authentifizierungsanfrage\n"
+
+#: fe-auth.c:374
+msgid "could not acquire SSPI credentials"
+msgstr "konnte SSPI-Credentials nicht erhalten"
+
+#: fe-auth.c:429
+msgid "channel binding required, but SSL not in use\n"
+msgstr "Channel-Binding wurde verlangt, aber SSL wird nicht verwendet\n"
+
+#: fe-auth.c:436
+msgid "duplicate SASL authentication request\n"
+msgstr "doppelte SASL-Authentifizierungsanfrage\n"
+
+#: fe-auth.c:492
+msgid "channel binding is required, but client does not support it\n"
+msgstr "Channel-Binding wurde verlangt, aber der Client unterstützt es nicht\n"
+
+#: fe-auth.c:509
+msgid "server offered SCRAM-SHA-256-PLUS authentication over a non-SSL connection\n"
+msgstr "Server hat Authentifizierung mit SCRAM-SHA-256-PLUS über eine Verbindung ohne SSL angeboten\n"
+
+#: fe-auth.c:521
+msgid "none of the server's SASL authentication mechanisms are supported\n"
+msgstr "keine der SASL-Authentifizierungsmechanismen des Servers werden unterstützt\n"
+
+#: fe-auth.c:529
+msgid "channel binding is required, but server did not offer an authentication method that supports channel binding\n"
+msgstr "Channel-Binding wurde verlangt, aber der Server hat keine Authentifizierungsmethode mit Channel-Binding angeboten\n"
+
+#: fe-auth.c:635
+#, c-format
+msgid "out of memory allocating SASL buffer (%d)\n"
+msgstr "Speicher aufgebraucht beim Anlegen des SASL-Puffers (%d)\n"
+
+#: fe-auth.c:660
+msgid "AuthenticationSASLFinal received from server, but SASL authentication was not completed\n"
+msgstr "AuthenticationSASLFinal vom Server empfangen, aber SASL-Authentifizierung war noch nicht abgeschlossen\n"
+
+#: fe-auth.c:737
+msgid "SCM_CRED authentication method not supported\n"
+msgstr "SCM_CRED-Authentifizierungsmethode nicht unterstützt\n"
+
+#: fe-auth.c:836
+msgid "channel binding required, but server authenticated client without channel binding\n"
+msgstr "Channel-Binding wurde verlangt, aber der Server hat den Client ohne Channel-Binding authentifiziert\n"
+
+#: fe-auth.c:842
+msgid "channel binding required but not supported by server's authentication request\n"
+msgstr "Channel-Binding wurde verlangt aber von der Authentifizierungsanfrage des Servers nicht unterstützt\n"
+
+#: fe-auth.c:875
+msgid "Kerberos 4 authentication not supported\n"
+msgstr "Authentifizierung mit Kerberos 4 nicht unterstützt\n"
+
+#: fe-auth.c:880
+msgid "Kerberos 5 authentication not supported\n"
+msgstr "Authentifizierung mit Kerberos 5 nicht unterstützt\n"
+
+#: fe-auth.c:951
+msgid "GSSAPI authentication not supported\n"
+msgstr "Authentifizierung mit GSSAPI nicht unterstützt\n"
+
+#: fe-auth.c:983
+msgid "SSPI authentication not supported\n"
+msgstr "Authentifizierung mit SSPI nicht unterstützt\n"
+
+#: fe-auth.c:991
+msgid "Crypt authentication not supported\n"
+msgstr "Authentifizierung mit Crypt nicht unterstützt\n"
+
+#: fe-auth.c:1057
+#, c-format
+msgid "authentication method %u not supported\n"
+msgstr "Authentifizierungsmethode %u nicht unterstützt\n"
+
+#: fe-auth.c:1104
+#, c-format
+msgid "user name lookup failure: error code %lu\n"
+msgstr "Fehler beim Nachschlagen des Benutzernamens: Fehlercode %lu\n"
+
+#: fe-auth.c:1114 fe-connect.c:2834
+#, c-format
+msgid "could not look up local user ID %d: %s\n"
+msgstr "konnte lokale Benutzer-ID %d nicht nachschlagen: %s\n"
+
+#: fe-auth.c:1119 fe-connect.c:2839
+#, c-format
+msgid "local user with ID %d does not exist\n"
+msgstr "lokaler Benutzer mit ID %d existiert nicht\n"
+
+#: fe-auth.c:1221
+msgid "unexpected shape of result set returned for SHOW\n"
+msgstr "unerwartete Form der Ergebnismenge von SHOW\n"
+
+#: fe-auth.c:1230
+msgid "password_encryption value too long\n"
+msgstr "Wert von password_encryption ist zu lang\n"
+
+#: fe-auth.c:1270
+#, c-format
+msgid "unrecognized password encryption algorithm \"%s\"\n"
+msgstr "unbekannter Passwortverschlüsselungsalgorithmus »%s«\n"
+
+#: fe-connect.c:1075
+#, c-format
+msgid "could not match %d host names to %d hostaddr values\n"
+msgstr "fehlerhafte Angabe: %d Hostnamen und %d hostaddr-Angaben\n"
+
+#: fe-connect.c:1156
+#, c-format
+msgid "could not match %d port numbers to %d hosts\n"
+msgstr "fehlerhafte Angabe: %d Portnummern und %d Hosts\n"
+
+#: fe-connect.c:1249
+#, c-format
+msgid "invalid channel_binding value: \"%s\"\n"
+msgstr "ungültiger channel_binding-Wert: »%s«\n"
+
+#: fe-connect.c:1275
+#, c-format
+msgid "invalid sslmode value: \"%s\"\n"
+msgstr "ungültiger sslmode-Wert: »%s«\n"
+
+#: fe-connect.c:1296
+#, c-format
+msgid "sslmode value \"%s\" invalid when SSL support is not compiled in\n"
+msgstr "sslmode-Wert »%s« ist ungültig, wenn SSL-Unterstützung nicht einkompiliert worden ist\n"
+
+#: fe-connect.c:1317
+#, c-format
+msgid "invalid ssl_min_protocol_version value: \"%s\"\n"
+msgstr "ungültiger ssl_min_protocol_version-Wert: »%s«\n"
+
+#: fe-connect.c:1325
+#, c-format
+msgid "invalid ssl_max_protocol_version value: \"%s\"\n"
+msgstr "ungültiger ssl_max_protocol_version-Wert: »%s«\n"
+
+#: fe-connect.c:1342
+msgid "invalid SSL protocol version range\n"
+msgstr "ungültiges SSL-Protokollsintervall\n"
+
+#: fe-connect.c:1357
+#, c-format
+msgid "invalid gssencmode value: \"%s\"\n"
+msgstr "ungültiger gssencmode-Wert: »%s«\n"
+
+#: fe-connect.c:1366
+#, c-format
+msgid "gssencmode value \"%s\" invalid when GSSAPI support is not compiled in\n"
+msgstr "gssencmode-Wert »%s« ist ungültig, wenn GSSAPI-Unterstützung nicht einkompiliert worden ist\n"
+
+#: fe-connect.c:1401
+#, c-format
+msgid "invalid target_session_attrs value: \"%s\"\n"
+msgstr "ungültiger target_session_attrs-Wert: »%s«\n"
+
+#: fe-connect.c:1619
+#, c-format
+msgid "could not set socket to TCP no delay mode: %s\n"
+msgstr "konnte Socket nicht auf TCP »No Delay«-Modus umstellen: %s\n"
+
+#: fe-connect.c:1680
+#, c-format
+msgid ""
+"could not connect to server: %s\n"
+"\tIs the server running locally and accepting\n"
+"\tconnections on Unix domain socket \"%s\"?\n"
+msgstr ""
+"konnte nicht mit dem Server verbinden: %s\n"
+"\tLäuft der Server lokal und akzeptiert er Verbindungen\n"
+"\tauf dem Unix-Domain-Socket »%s«?\n"
+
+#: fe-connect.c:1717
+#, c-format
+msgid ""
+"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"
+msgstr ""
+"konnte nicht mit dem Server verbinden: %s\n"
+"\tLäuft der Server auf dem Host »%s« (%s) und akzeptiert er\n"
+"\tTCP/IP-Verbindungen auf Port %s?\n"
+
+#: fe-connect.c:1725
+#, c-format
+msgid ""
+"could not connect to server: %s\n"
+"\tIs the server running on host \"%s\" and accepting\n"
+"\tTCP/IP connections on port %s?\n"
+msgstr ""
+"konnte nicht mit dem Server verbinden: %s\n"
+"\tLäuft der Server auf dem Host »%s« und akzeptiert er\n"
+"\tTCP/IP-Verbindungen auf Port %s?\n"
+
+#: fe-connect.c:1795
+#, c-format
+msgid "invalid integer value \"%s\" for connection option \"%s\"\n"
+msgstr "ungültiger Zahlenwert »%s« für Verbindungsoption »%s«\n"
+
+#: fe-connect.c:1825 fe-connect.c:1859 fe-connect.c:1894 fe-connect.c:1981
+#: fe-connect.c:2623
+#, c-format
+msgid "setsockopt(%s) failed: %s\n"
+msgstr "setsockopt(%s) fehlgeschlagen: %s\n"
+
+#: fe-connect.c:1947
+#, c-format
+msgid "WSAIoctl(SIO_KEEPALIVE_VALS) failed: %ui\n"
+msgstr "WSAIoctl(SIO_KEEPALIVE_VALS) fehlgeschlagen: %ui\n"
+
+#: fe-connect.c:2313
+msgid "invalid connection state, probably indicative of memory corruption\n"
+msgstr "ungültiger Verbindungszustand, möglicherweise ein Speicherproblem\n"
+
+#: fe-connect.c:2379
+#, c-format
+msgid "invalid port number: \"%s\"\n"
+msgstr "ungültige Portnummer: »%s«\n"
+
+#: fe-connect.c:2395
+#, c-format
+msgid "could not translate host name \"%s\" to address: %s\n"
+msgstr "konnte Hostnamen »%s« nicht in Adresse übersetzen: %s\n"
+
+#: fe-connect.c:2408
+#, c-format
+msgid "could not parse network address \"%s\": %s\n"
+msgstr "konnte Netzwerkadresse »%s« nicht interpretieren: %s\n"
+
+#: fe-connect.c:2421
+#, c-format
+msgid "Unix-domain socket path \"%s\" is too long (maximum %d bytes)\n"
+msgstr "Unix-Domain-Socket-Pfad »%s« ist zu lang (maximal %d Bytes)\n"
+
+#: fe-connect.c:2436
+#, c-format
+msgid "could not translate Unix-domain socket path \"%s\" to address: %s\n"
+msgstr "konnte Unix-Domain-Socket-Pfad »%s« nicht in Adresse übersetzen: %s\n"
+
+#: fe-connect.c:2560
+#, c-format
+msgid "could not create socket: %s\n"
+msgstr "konnte Socket nicht erzeugen: %s\n"
+
+#: fe-connect.c:2582
+#, c-format
+msgid "could not set socket to nonblocking mode: %s\n"
+msgstr "konnte Socket nicht auf nicht-blockierenden Modus umstellen: %s\n"
+
+#: fe-connect.c:2592
+#, c-format
+msgid "could not set socket to close-on-exec mode: %s\n"
+msgstr "konnte Socket nicht auf »Close on exec«-Modus umstellen: %s\n"
+
+#: fe-connect.c:2610
+msgid "keepalives parameter must be an integer\n"
+msgstr "Parameter »keepalives« muss eine ganze Zahl sein\n"
+
+#: fe-connect.c:2750
+#, c-format
+msgid "could not get socket error status: %s\n"
+msgstr "konnte Socket-Fehlerstatus nicht ermitteln: %s\n"
+
+#: fe-connect.c:2778
+#, c-format
+msgid "could not get client address from socket: %s\n"
+msgstr "konnte Client-Adresse vom Socket nicht ermitteln: %s\n"
+
+#: fe-connect.c:2820
+msgid "requirepeer parameter is not supported on this platform\n"
+msgstr "Parameter »requirepeer« wird auf dieser Plattform nicht unterstützt\n"
+
+#: fe-connect.c:2823
+#, c-format
+msgid "could not get peer credentials: %s\n"
+msgstr "konnte Credentials von Gegenstelle nicht ermitteln: %s\n"
+
+#: fe-connect.c:2847
+#, c-format
+msgid "requirepeer specifies \"%s\", but actual peer user name is \"%s\"\n"
+msgstr "requirepeer gibt »%s« an, aber tatsächlicher Benutzername der Gegenstelle ist »%s«\n"
+
+#: fe-connect.c:2887
+#, c-format
+msgid "could not send GSSAPI negotiation packet: %s\n"
+msgstr "konnte Paket zur GSSAPI-Verhandlung nicht senden: %s\n"
+
+#: fe-connect.c:2899
+msgid "GSSAPI encryption required but was impossible (possibly no credential cache, no server support, or using a local socket)\n"
+msgstr "GSSAPI-Verschlüsselung war gefordert aber war nicht möglich (möglicherweise kein Credential-Cache, keine Serverunterstützung oder lokales Socket wird verwendet)\n"
+
+#: fe-connect.c:2931
+#, c-format
+msgid "could not send SSL negotiation packet: %s\n"
+msgstr "konnte Paket zur SSL-Verhandlung nicht senden: %s\n"
+
+#: fe-connect.c:2970
+#, c-format
+msgid "could not send startup packet: %s\n"
+msgstr "konnte Startpaket nicht senden: %s\n"
+
+#: fe-connect.c:3040
+msgid "server does not support SSL, but SSL was required\n"
+msgstr "Server unterstützt kein SSL, aber SSL wurde verlangt\n"
+
+#: fe-connect.c:3067
+#, c-format
+msgid "received invalid response to SSL negotiation: %c\n"
+msgstr "ungültige Antwort auf SSL-Verhandlungspaket empfangen: %c\n"
+
+#: fe-connect.c:3156
+msgid "server doesn't support GSSAPI encryption, but it was required\n"
+msgstr "Server unterstützt keine GSSAPI-Verschlüsselung, sie wurde aber verlangt\n"
+
+#: fe-connect.c:3168
+#, c-format
+msgid "received invalid response to GSSAPI negotiation: %c\n"
+msgstr "ungültige Antwort auf GSSAPI-Verhandlungspaket empfangen: %c\n"
+
+#: fe-connect.c:3234 fe-connect.c:3265
+#, c-format
+msgid "expected authentication request from server, but received %c\n"
+msgstr "Authentifizierungsanfrage wurde vom Server erwartet, aber %c wurde empfangen\n"
+
+#: fe-connect.c:3506
+msgid "unexpected message from server during startup\n"
+msgstr "unerwartete Nachricht vom Server beim Start\n"
+
+#: fe-connect.c:3711
+#, c-format
+msgid "could not make a writable connection to server \"%s:%s\"\n"
+msgstr "konnte keine schreibbare Verbindung zum Server »%s:%s« aufbauen\n"
+
+#: fe-connect.c:3757
+#, c-format
+msgid "test \"SHOW transaction_read_only\" failed on server \"%s:%s\"\n"
+msgstr "Test »SHOW transaction_read_only« fehlgeschlagen auf Server »%s:%s«\n"
+
+#: fe-connect.c:3772
+#, c-format
+msgid "invalid connection state %d, probably indicative of memory corruption\n"
+msgstr "ungültiger Verbindungszustand %d, möglicherweise ein Speicherproblem\n"
+
+#: fe-connect.c:4211 fe-connect.c:4271
+#, c-format
+msgid "PGEventProc \"%s\" failed during PGEVT_CONNRESET event\n"
+msgstr "PGEventProc »%s« während PGEVT_CONNRESET-Ereignis fehlgeschlagen\n"
+
+#: fe-connect.c:4618
+#, c-format
+msgid "invalid LDAP URL \"%s\": scheme must be ldap://\n"
+msgstr "ungültige LDAP-URL »%s«: Schema muss ldap:// sein\n"
+
+#: fe-connect.c:4633
+#, c-format
+msgid "invalid LDAP URL \"%s\": missing distinguished name\n"
+msgstr "ungültige LDAP-URL »%s«: Distinguished Name fehlt\n"
+
+#: fe-connect.c:4645 fe-connect.c:4700
+#, c-format
+msgid "invalid LDAP URL \"%s\": must have exactly one attribute\n"
+msgstr "ungültige LDAP-URL »%s«: muss genau ein Attribut haben\n"
+
+#: fe-connect.c:4656 fe-connect.c:4715
+#, c-format
+msgid "invalid LDAP URL \"%s\": must have search scope (base/one/sub)\n"
+msgstr "ungültige LDAP-URL »%s«: Suchbereich fehlt (base/one/sub)\n"
+
+#: fe-connect.c:4667
+#, c-format
+msgid "invalid LDAP URL \"%s\": no filter\n"
+msgstr "ungültige LDAP-URL »%s«: kein Filter\n"
+
+#: fe-connect.c:4688
+#, c-format
+msgid "invalid LDAP URL \"%s\": invalid port number\n"
+msgstr "ungültige LDAP-URL »%s«: ungültige Portnummer\n"
+
+#: fe-connect.c:4724
+msgid "could not create LDAP structure\n"
+msgstr "konnte LDAP-Struktur nicht erzeugen\n"
+
+#: fe-connect.c:4800
+#, c-format
+msgid "lookup on LDAP server failed: %s\n"
+msgstr "Suche auf LDAP-Server fehlgeschlagen: %s\n"
+
+#: fe-connect.c:4811
+msgid "more than one entry found on LDAP lookup\n"
+msgstr "LDAP-Suche ergab mehr als einen Eintrag\n"
+
+#: fe-connect.c:4812 fe-connect.c:4824
+msgid "no entry found on LDAP lookup\n"
+msgstr "kein Eintrag gefunden bei LDAP-Suche\n"
+
+#: fe-connect.c:4835 fe-connect.c:4848
+msgid "attribute has no values on LDAP lookup\n"
+msgstr "Attribut hat keine Werte bei LDAP-Suche\n"
+
+#: fe-connect.c:4900 fe-connect.c:4919 fe-connect.c:5451
+#, c-format
+msgid "missing \"=\" after \"%s\" in connection info string\n"
+msgstr "fehlendes »=« nach »%s« in der Zeichenkette der Verbindungsdaten\n"
+
+#: fe-connect.c:4992 fe-connect.c:5636 fe-connect.c:6410
+#, c-format
+msgid "invalid connection option \"%s\"\n"
+msgstr "ungültige Verbindungsoption »%s«\n"
+
+#: fe-connect.c:5008 fe-connect.c:5500
+msgid "unterminated quoted string in connection info string\n"
+msgstr "fehlendes schließendes Anführungszeichen (\") in der Zeichenkette der Verbindungsdaten\n"
+
+#: fe-connect.c:5091
+#, c-format
+msgid "definition of service \"%s\" not found\n"
+msgstr "Definition von Service »%s« nicht gefunden\n"
+
+#: fe-connect.c:5114
+#, c-format
+msgid "service file \"%s\" not found\n"
+msgstr "Servicedatei »%s« nicht gefunden\n"
+
+#: fe-connect.c:5129
+#, c-format
+msgid "line %d too long in service file \"%s\"\n"
+msgstr "Zeile %d zu lang in Servicedatei »%s«\n"
+
+#: fe-connect.c:5201 fe-connect.c:5245
+#, c-format
+msgid "syntax error in service file \"%s\", line %d\n"
+msgstr "Syntaxfehler in Servicedatei »%s«, Zeile %d\n"
+
+#: fe-connect.c:5212
+#, c-format
+msgid "nested service specifications not supported in service file \"%s\", line %d\n"
+msgstr "geschachtelte »service«-Definitionen werden nicht unterstützt in Servicedatei »%s«, Zeile %d\n"
+
+#: fe-connect.c:5932
+#, c-format
+msgid "invalid URI propagated to internal parser routine: \"%s\"\n"
+msgstr "ungültige URI an interne Parserroutine weitergeleitet: »%s«\n"
+
+#: fe-connect.c:6009
+#, c-format
+msgid "end of string reached when looking for matching \"]\" in IPv6 host address in URI: \"%s\"\n"
+msgstr "Ende der Eingabezeichenkette gefunden beim Suchen nach passendem »]« in IPv6-Hostadresse in URI: »%s«\n"
+
+#: fe-connect.c:6016
+#, c-format
+msgid "IPv6 host address may not be empty in URI: \"%s\"\n"
+msgstr "IPv6-Hostadresse darf nicht leer sein in URI: »%s«\n"
+
+#: fe-connect.c:6031
+#, c-format
+msgid "unexpected character \"%c\" at position %d in URI (expected \":\" or \"/\"): \"%s\"\n"
+msgstr "unerwartetes Zeichen »%c« an Position %d in URI (»:« oder »/« erwartet): »%s«\n"
+
+#: fe-connect.c:6160
+#, c-format
+msgid "extra key/value separator \"=\" in URI query parameter: \"%s\"\n"
+msgstr "zusätzliches Schlüssel/Wert-Trennzeichen »=« in URI-Query-Parameter: »%s«\n"
+
+#: fe-connect.c:6180
+#, c-format
+msgid "missing key/value separator \"=\" in URI query parameter: \"%s\"\n"
+msgstr "fehlendes Schlüssel/Wert-Trennzeichen »=« in URI-Query-Parameter: »%s«\n"
+
+#: fe-connect.c:6231
+#, c-format
+msgid "invalid URI query parameter: \"%s\"\n"
+msgstr "ungültiger URI-Query-Parameter: »%s«\n"
+
+#: fe-connect.c:6305
+#, c-format
+msgid "invalid percent-encoded token: \"%s\"\n"
+msgstr "ungültiges Prozent-kodiertes Token: »%s«\n"
+
+#: fe-connect.c:6315
+#, c-format
+msgid "forbidden value %%00 in percent-encoded value: \"%s\"\n"
+msgstr "verbotener Wert %%00 in Prozent-kodiertem Wert: »%s«\n"
+
+#: fe-connect.c:6678
+msgid "connection pointer is NULL\n"
+msgstr "Verbindung ist ein NULL-Zeiger\n"
+
+#: fe-connect.c:6974
+#, c-format
+msgid "WARNING: password file \"%s\" is not a plain file\n"
+msgstr "WARNUNG: Passwortdatei »%s« ist keine normale Datei\n"
+
+#: fe-connect.c:6983
+#, c-format
+msgid "WARNING: password file \"%s\" has group or world access; permissions should be u=rw (0600) or less\n"
+msgstr "WARNUNG: Passwortdatei »%s« erlaubt Lesezugriff für Gruppe oder Andere; Rechte sollten u=rw (0600) oder weniger sein\n"
+
+#: fe-connect.c:7091
+#, c-format
+msgid "password retrieved from file \"%s\"\n"
+msgstr "Passwort wurde aus Datei »%s« gelesen\n"
+
+#: fe-exec.c:444 fe-exec.c:2821
+#, c-format
+msgid "row number %d is out of range 0..%d"
+msgstr "Zeilennummer %d ist außerhalb des zulässigen Bereichs 0..%d"
+
+#: fe-exec.c:505 fe-protocol2.c:497 fe-protocol2.c:532 fe-protocol2.c:1050
+#: fe-protocol3.c:206 fe-protocol3.c:233 fe-protocol3.c:250 fe-protocol3.c:328
+#: fe-protocol3.c:692 fe-protocol3.c:920
+msgid "out of memory"
+msgstr "Speicher aufgebraucht"
+
+#: fe-exec.c:506 fe-protocol2.c:1396 fe-protocol3.c:1873
+#, c-format
+msgid "%s"
+msgstr "%s"
+
+#: fe-exec.c:815
+msgid "write to server failed\n"
+msgstr "Schreiben zum Server fehlgeschlagen\n"
+
+#: fe-exec.c:896
+msgid "NOTICE"
+msgstr "HINWEIS"
+
+#: fe-exec.c:954
+msgid "PGresult cannot support more than INT_MAX tuples"
+msgstr "PGresult kann nicht mehr als INT_MAX Tupel enthalten"
+
+#: fe-exec.c:966
+msgid "size_t overflow"
+msgstr "Überlauf von size_t"
+
+#: fe-exec.c:1243 fe-exec.c:1301 fe-exec.c:1347
+msgid "command string is a null pointer\n"
+msgstr "Befehlszeichenkette ist ein NULL-Zeiger\n"
+
+#: fe-exec.c:1307 fe-exec.c:1353 fe-exec.c:1448
+msgid "number of parameters must be between 0 and 65535\n"
+msgstr "Anzahl der Parameter muss zwischen 0 und 65535 sein\n"
+
+#: fe-exec.c:1341 fe-exec.c:1442
+msgid "statement name is a null pointer\n"
+msgstr "Anweisungsname ist ein NULL-Zeiger\n"
+
+#: fe-exec.c:1361 fe-exec.c:1524 fe-exec.c:2233 fe-exec.c:2435
+msgid "function requires at least protocol version 3.0\n"
+msgstr "Funktion erfordert mindestens Protokollversion 3.0\n"
+
+#: fe-exec.c:1479
+msgid "no connection to the server\n"
+msgstr "keine Verbindung mit dem Server\n"
+
+#: fe-exec.c:1486
+msgid "another command is already in progress\n"
+msgstr "ein anderer Befehl ist bereits in Ausführung\n"
+
+#: fe-exec.c:1600
+msgid "length must be given for binary parameter\n"
+msgstr "für binäre Parameter muss eine Länge angegeben werden\n"
+
+#: fe-exec.c:1863
+#, c-format
+msgid "unexpected asyncStatus: %d\n"
+msgstr "unerwarteter asyncStatus: %d\n"
+
+#: fe-exec.c:1883
+#, c-format
+msgid "PGEventProc \"%s\" failed during PGEVT_RESULTCREATE event\n"
+msgstr "PGEventProc »%s« während PGEVT_RESULTCREATE-Ereignis fehlgeschlagen\n"
+
+#: fe-exec.c:2043
+msgid "COPY terminated by new PQexec"
+msgstr "COPY von neuem PQexec beendet"
+
+#: fe-exec.c:2051
+msgid "COPY IN state must be terminated first\n"
+msgstr "COPY-IN-Zustand muss erst beendet werden\n"
+
+#: fe-exec.c:2071
+msgid "COPY OUT state must be terminated first\n"
+msgstr "COPY-OUT-Zustand muss erst beendet werden\n"
+
+#: fe-exec.c:2079
+msgid "PQexec not allowed during COPY BOTH\n"
+msgstr "PQexec ist während COPY BOTH nicht erlaubt\n"
+
+#: fe-exec.c:2325 fe-exec.c:2392 fe-exec.c:2482 fe-protocol2.c:1353
+#: fe-protocol3.c:1804
+msgid "no COPY in progress\n"
+msgstr "keine COPY in Ausführung\n"
+
+#: fe-exec.c:2672
+msgid "connection in wrong state\n"
+msgstr "Verbindung im falschen Zustand\n"
+
+#: fe-exec.c:2703
+msgid "invalid ExecStatusType code"
+msgstr "ungültiger ExecStatusType-Kode"
+
+#: fe-exec.c:2730
+msgid "PGresult is not an error result\n"
+msgstr "PGresult ist kein Fehlerresultat\n"
+
+#: fe-exec.c:2805 fe-exec.c:2828
+#, c-format
+msgid "column number %d is out of range 0..%d"
+msgstr "Spaltennummer %d ist außerhalb des zulässigen Bereichs 0..%d"
+
+#: fe-exec.c:2843
+#, c-format
+msgid "parameter number %d is out of range 0..%d"
+msgstr "Parameternummer %d ist außerhalb des zulässigen Bereichs 0..%d"
+
+#: fe-exec.c:3153
+#, c-format
+msgid "could not interpret result from server: %s"
+msgstr "konnte Ergebnis vom Server nicht interpretieren: %s"
+
+#: fe-exec.c:3392 fe-exec.c:3476
+msgid "incomplete multibyte character\n"
+msgstr "unvollständiges Mehrbyte-Zeichen\n"
+
+#: fe-gssapi-common.c:124
+msgid "GSSAPI name import error"
+msgstr "GSSAPI-Namensimportfehler"
+
+#: fe-lobj.c:154
+msgid "cannot determine OID of function lo_truncate\n"
+msgstr "kann OID der Funktion lo_truncate nicht ermitteln\n"
+
+#: fe-lobj.c:170
+msgid "argument of lo_truncate exceeds integer range\n"
+msgstr "Argument von lo_truncate überschreitet Bereich für ganze Zahlen\n"
+
+#: fe-lobj.c:221
+msgid "cannot determine OID of function lo_truncate64\n"
+msgstr "kann OID der Funktion lo_truncate64 nicht ermitteln\n"
+
+#: fe-lobj.c:279
+msgid "argument of lo_read exceeds integer range\n"
+msgstr "Argument von lo_read überschreitet Bereich für ganze Zahlen\n"
+
+#: fe-lobj.c:334
+msgid "argument of lo_write exceeds integer range\n"
+msgstr "Argument von lo_write überschreitet Bereich für ganze Zahlen\n"
+
+#: fe-lobj.c:425
+msgid "cannot determine OID of function lo_lseek64\n"
+msgstr "kann OID der Funktion lo_lseek64 nicht ermitteln\n"
+
+#: fe-lobj.c:521
+msgid "cannot determine OID of function lo_create\n"
+msgstr "kann OID der Funktion lo_create nicht ermitteln\n"
+
+#: fe-lobj.c:600
+msgid "cannot determine OID of function lo_tell64\n"
+msgstr "kann OID der Funktion lo_tell64 nicht ermitteln\n"
+
+#: fe-lobj.c:706 fe-lobj.c:815
+#, c-format
+msgid "could not open file \"%s\": %s\n"
+msgstr "konnte Datei »%s« nicht öffnen: %s\n"
+
+#: fe-lobj.c:761
+#, c-format
+msgid "could not read from file \"%s\": %s\n"
+msgstr "konnte nicht aus Datei »%s« lesen: %s\n"
+
+#: fe-lobj.c:835 fe-lobj.c:859
+#, c-format
+msgid "could not write to file \"%s\": %s\n"
+msgstr "konnte nicht in Datei »%s« schreiben: %s\n"
+
+#: fe-lobj.c:946
+msgid "query to initialize large object functions did not return data\n"
+msgstr "Abfrage zur Initialisierung der Large-Object-Funktionen ergab keine Daten\n"
+
+#: fe-lobj.c:995
+msgid "cannot determine OID of function lo_open\n"
+msgstr "kann OID der Funktion lo_open nicht ermitteln\n"
+
+#: fe-lobj.c:1002
+msgid "cannot determine OID of function lo_close\n"
+msgstr "kann OID der Funktion lo_close nicht ermitteln\n"
+
+#: fe-lobj.c:1009
+msgid "cannot determine OID of function lo_creat\n"
+msgstr "kann OID der Funktion lo_creat nicht ermitteln\n"
+
+#: fe-lobj.c:1016
+msgid "cannot determine OID of function lo_unlink\n"
+msgstr "kann OID der Funktion lo_unlink nicht ermitteln\n"
+
+#: fe-lobj.c:1023
+msgid "cannot determine OID of function lo_lseek\n"
+msgstr "kann OID der Funktion lo_lseek nicht ermitteln\n"
+
+#: fe-lobj.c:1030
+msgid "cannot determine OID of function lo_tell\n"
+msgstr "kann OID der Funktion lo_tell nicht ermitteln\n"
+
+#: fe-lobj.c:1037
+msgid "cannot determine OID of function loread\n"
+msgstr "kann OID der Funktion loread nicht ermitteln\n"
+
+#: fe-lobj.c:1044
+msgid "cannot determine OID of function lowrite\n"
+msgstr "kann OID der Funktion lowrite nicht ermitteln\n"
+
+#: fe-misc.c:289
+#, c-format
+msgid "integer of size %lu not supported by pqGetInt"
+msgstr "Integer der Größe %lu wird von pqGetInt nicht unterstützt"
+
+#: fe-misc.c:325
+#, c-format
+msgid "integer of size %lu not supported by pqPutInt"
+msgstr "Integer der Größe %lu wird von pqPutInt nicht unterstützt"
+
+#: fe-misc.c:636 fe-misc.c:869
+msgid "connection not open\n"
+msgstr "Verbindung nicht offen\n"
+
+#: fe-misc.c:805 fe-secure-openssl.c:209 fe-secure-openssl.c:316
+#: fe-secure.c:267 fe-secure.c:383
+msgid ""
+"server closed the connection unexpectedly\n"
+"\tThis probably means the server terminated abnormally\n"
+"\tbefore or while processing the request.\n"
+msgstr ""
+"Server beendete die Verbindung unerwartet\n"
+"\tDas heißt wahrscheinlich, dass der Server abnormal beendete\n"
+"\tbevor oder während die Anweisung bearbeitet wurde.\n"
+
+#: fe-misc.c:1063
+msgid "timeout expired\n"
+msgstr "Timeout abgelaufen\n"
+
+#: fe-misc.c:1108
+msgid "invalid socket\n"
+msgstr "ungültiges Socket\n"
+
+#: fe-misc.c:1131
+#, c-format
+msgid "select() failed: %s\n"
+msgstr "select() fehlgeschlagen: %s\n"
+
+#: fe-protocol2.c:87
+#, c-format
+msgid "invalid setenv state %c, probably indicative of memory corruption\n"
+msgstr "ungültiger Setenv-Zustand %c, möglicherweise ein Speicherproblem\n"
+
+#: fe-protocol2.c:384
+#, c-format
+msgid "invalid state %c, probably indicative of memory corruption\n"
+msgstr "ungültiger Zustand %c, möglicherweise ein Speicherproblem\n"
+
+#: fe-protocol2.c:473 fe-protocol3.c:183
+#, c-format
+msgid "message type 0x%02x arrived from server while idle"
+msgstr "Nachricht vom Typ 0x%02x kam vom Server im Ruhezustand"
+
+#: fe-protocol2.c:523
+#, c-format
+msgid "unexpected character %c following empty query response (\"I\" message)"
+msgstr "unerwartetes Zeichen %c kam nach Antwort auf leere Anfrage (»I«-Nachricht)"
+
+#: fe-protocol2.c:589
+#, c-format
+msgid "server sent data (\"D\" message) without prior row description (\"T\" message)"
+msgstr "Server sendete Daten (»D«-Nachricht) ohne vorherige Zeilenbeschreibung (»T«-Nachricht)"
+
+#: fe-protocol2.c:607
+#, c-format
+msgid "server sent binary data (\"B\" message) without prior row description (\"T\" message)"
+msgstr "Server sendete binäre Daten (»B«-Nachricht) ohne vorherige Zeilenbeschreibung (»T«-Nachricht)"
+
+#: fe-protocol2.c:626 fe-protocol3.c:403
+#, c-format
+msgid "unexpected response from server; first received character was \"%c\"\n"
+msgstr "unerwartete Antwort vom Server; erstes empfangenes Zeichen war »%c«\n"
+
+#: fe-protocol2.c:755 fe-protocol2.c:930 fe-protocol3.c:603 fe-protocol3.c:809
+msgid "out of memory for query result"
+msgstr "Speicher für Anfrageergebnis aufgebraucht"
+
+#: fe-protocol2.c:1408
+#, c-format
+msgid "lost synchronization with server, resetting connection"
+msgstr "Synchronisation mit Server verloren, Verbindung wird zurückgesetzt"
+
+#: fe-protocol2.c:1530 fe-protocol2.c:1562 fe-protocol3.c:2061
+#, c-format
+msgid "protocol error: id=0x%x\n"
+msgstr "Protokollfehler: id=0x%x\n"
+
+#: fe-protocol3.c:360
+msgid "server sent data (\"D\" message) without prior row description (\"T\" message)\n"
+msgstr "Server sendete Daten (»D«-Nachricht) ohne vorherige Zeilenbeschreibung (»T«-Nachricht)\n"
+
+#: fe-protocol3.c:424
+#, c-format
+msgid "message contents do not agree with length in message type \"%c\"\n"
+msgstr "Nachrichteninhalt stimmt nicht mit Länge in Nachrichtentyp »%c« überein\n"
+
+#: fe-protocol3.c:444
+#, c-format
+msgid "lost synchronization with server: got message type \"%c\", length %d\n"
+msgstr "Synchronisation mit Server verloren: Nachrichtentyp »%c« empfangen, Länge %d\n"
+
+#: fe-protocol3.c:494 fe-protocol3.c:534
+msgid "insufficient data in \"T\" message"
+msgstr "nicht genug Daten in »T«-Nachricht"
+
+#: fe-protocol3.c:672
+msgid "insufficient data in \"t\" message"
+msgstr "nicht genug Daten in »t«-Nachricht"
+
+#: fe-protocol3.c:731 fe-protocol3.c:763 fe-protocol3.c:781
+msgid "insufficient data in \"D\" message"
+msgstr "nicht genug Daten in »D«-Nachricht"
+
+#: fe-protocol3.c:737
+msgid "unexpected field count in \"D\" message"
+msgstr "unerwartete Feldzahl in »D«-Nachricht"
+
+#: fe-protocol3.c:974
+msgid "no error message available\n"
+msgstr "keine Fehlermeldung verfügbar\n"
+
+#. translator: %s represents a digit string
+#: fe-protocol3.c:1022 fe-protocol3.c:1041
+#, c-format
+msgid " at character %s"
+msgstr " bei Zeichen %s"
+
+#: fe-protocol3.c:1054
+#, c-format
+msgid "DETAIL: %s\n"
+msgstr "DETAIL: %s\n"
+
+#: fe-protocol3.c:1057
+#, c-format
+msgid "HINT: %s\n"
+msgstr "TIP: %s\n"
+
+#: fe-protocol3.c:1060
+#, c-format
+msgid "QUERY: %s\n"
+msgstr "ANFRAGE: %s\n"
+
+#: fe-protocol3.c:1067
+#, c-format
+msgid "CONTEXT: %s\n"
+msgstr "KONTEXT: %s\n"
+
+#: fe-protocol3.c:1076
+#, c-format
+msgid "SCHEMA NAME: %s\n"
+msgstr "SCHEMANAME: %s\n"
+
+#: fe-protocol3.c:1080
+#, c-format
+msgid "TABLE NAME: %s\n"
+msgstr "TABELLENNAME: %s\n"
+
+#: fe-protocol3.c:1084
+#, c-format
+msgid "COLUMN NAME: %s\n"
+msgstr "SPALTENNAME: %s\n"
+
+#: fe-protocol3.c:1088
+#, c-format
+msgid "DATATYPE NAME: %s\n"
+msgstr "DATENTYPNAME: %s\n"
+
+#: fe-protocol3.c:1092
+#, c-format
+msgid "CONSTRAINT NAME: %s\n"
+msgstr "CONSTRAINT-NAME: %s\n"
+
+#: fe-protocol3.c:1104
+msgid "LOCATION: "
+msgstr "ORT: "
+
+#: fe-protocol3.c:1106
+#, c-format
+msgid "%s, "
+msgstr "%s, "
+
+#: fe-protocol3.c:1108
+#, c-format
+msgid "%s:%s"
+msgstr "%s:%s"
+
+#: fe-protocol3.c:1303
+#, c-format
+msgid "LINE %d: "
+msgstr "ZEILE %d: "
+
+#: fe-protocol3.c:1698
+msgid "PQgetline: not doing text COPY OUT\n"
+msgstr "PQgetline: Text COPY OUT nicht ausgeführt\n"
+
+#: fe-secure-common.c:124
+msgid "SSL certificate's name contains embedded null\n"
+msgstr "Name im SSL-Zertifikat enthält Null-Byte\n"
+
+#: fe-secure-common.c:171
+msgid "host name must be specified for a verified SSL connection\n"
+msgstr "Hostname muss angegeben werden für eine verifizierte SSL-Verbindung\n"
+
+#: fe-secure-common.c:196
+#, c-format
+msgid "server certificate for \"%s\" does not match host name \"%s\"\n"
+msgstr "Server-Zertifikat für »%s« stimmt nicht mit dem Hostnamen »%s« überein\n"
+
+#: fe-secure-common.c:202
+msgid "could not get server's host name from server certificate\n"
+msgstr "konnte Hostnamen des Servers nicht aus dem Serverzertifikat ermitteln\n"
+
+#: fe-secure-gssapi.c:201
+msgid "GSSAPI wrap error"
+msgstr "GSSAPI-Wrap-Fehler"
+
+#: fe-secure-gssapi.c:209
+msgid "outgoing GSSAPI message would not use confidentiality\n"
+msgstr "ausgehende GSSAPI-Nachricht würde keine Vertraulichkeit verwenden\n"
+
+#: fe-secure-gssapi.c:217
+#, c-format
+msgid "client tried to send oversize GSSAPI packet (%zu > %zu)\n"
+msgstr "Client versuchte übergroßes GSSAPI-Paket zu senden (%zu > %zu)\n"
+
+#: fe-secure-gssapi.c:354 fe-secure-gssapi.c:596
+#, c-format
+msgid "oversize GSSAPI packet sent by the server (%zu > %zu)\n"
+msgstr "übergroßes GSSAPI-Paket vom Server gesendet (%zu > %zu)\n"
+
+#: fe-secure-gssapi.c:393
+msgid "GSSAPI unwrap error"
+msgstr "GSSAPI-Unwrap-Fehler"
+
+#: fe-secure-gssapi.c:403
+msgid "incoming GSSAPI message did not use confidentiality\n"
+msgstr "eingehende GSSAPI-Nachricht verwendete keine Vertraulichkeit\n"
+
+#: fe-secure-gssapi.c:642
+msgid "could not initiate GSSAPI security context"
+msgstr "konnte GSSAPI-Sicherheitskontext nicht initiieren"
+
+#: fe-secure-gssapi.c:670
+msgid "GSSAPI size check error"
+msgstr "GSSAPI-Fehler bei der Größenprüfung"
+
+#: fe-secure-gssapi.c:681
+msgid "GSSAPI context establishment error"
+msgstr "GSSAPI-Fehler beim Einrichten des Kontexts"
+
+#: fe-secure-openssl.c:214 fe-secure-openssl.c:321 fe-secure-openssl.c:1291
+#, c-format
+msgid "SSL SYSCALL error: %s\n"
+msgstr "SSL-SYSCALL-Fehler: %s\n"
+
+#: fe-secure-openssl.c:221 fe-secure-openssl.c:328 fe-secure-openssl.c:1295
+msgid "SSL SYSCALL error: EOF detected\n"
+msgstr "SSL-SYSCALL-Fehler: Dateiende entdeckt\n"
+
+#: fe-secure-openssl.c:232 fe-secure-openssl.c:339 fe-secure-openssl.c:1304
+#, c-format
+msgid "SSL error: %s\n"
+msgstr "SSL-Fehler: %s\n"
+
+#: fe-secure-openssl.c:247 fe-secure-openssl.c:354
+msgid "SSL connection has been closed unexpectedly\n"
+msgstr "SSL-Verbindung wurde unerwartet geschlossen\n"
+
+#: fe-secure-openssl.c:253 fe-secure-openssl.c:360 fe-secure-openssl.c:1354
+#, c-format
+msgid "unrecognized SSL error code: %d\n"
+msgstr "unbekannter SSL-Fehlercode: %d\n"
+
+#: fe-secure-openssl.c:400
+msgid "could not determine server certificate signature algorithm\n"
+msgstr "konnte Signaturalgorithmus des Serverzertifikats nicht ermitteln\n"
+
+#: fe-secure-openssl.c:421
+#, c-format
+msgid "could not find digest for NID %s\n"
+msgstr "konnte Digest für NID %s nicht finden\n"
+
+#: fe-secure-openssl.c:431
+msgid "could not generate peer certificate hash\n"
+msgstr "konnte Hash des Zertifikats der Gegenstelle nicht erzeugen\n"
+
+#: fe-secure-openssl.c:488
+msgid "SSL certificate's name entry is missing\n"
+msgstr "Namenseintrag fehlt im SSL-Zertifikat\n"
+
+#: fe-secure-openssl.c:815
+#, c-format
+msgid "could not create SSL context: %s\n"
+msgstr "konnte SSL-Kontext nicht erzeugen: %s\n"
+
+#: fe-secure-openssl.c:854
+#, c-format
+msgid "invalid value \"%s\" for minimum SSL protocol version\n"
+msgstr "ungültiger Wert »%s« für minimale SSL-Protokollversion\n"
+
+#: fe-secure-openssl.c:865
+#, c-format
+msgid "could not set minimum SSL protocol version: %s\n"
+msgstr "konnte minimale SSL-Protokollversion nicht setzen: %s\n"
+
+#: fe-secure-openssl.c:883
+#, c-format
+msgid "invalid value \"%s\" for maximum SSL protocol version\n"
+msgstr "ungültiger Wert »%s« für maximale SSL-Protokollversion\n"
+
+#: fe-secure-openssl.c:894
+#, c-format
+msgid "could not set maximum SSL protocol version: %s\n"
+msgstr "konnte maximale SSL-Protokollversion nicht setzen: %s\n"
+
+#: fe-secure-openssl.c:930
+#, c-format
+msgid "could not read root certificate file \"%s\": %s\n"
+msgstr "konnte Root-Zertifikat-Datei »%s« nicht lesen: %s\n"
+
+#: fe-secure-openssl.c:974
+msgid ""
+"could not get home directory to locate root certificate file\n"
+"Either provide the file or change sslmode to disable server certificate verification.\n"
+msgstr ""
+"konnte Home-Verzeichnis nicht ermitteln, um Root-Zertifikat-Datei zu finden\n"
+"Legen Sie entweder die Datei an oder ändern Sie sslmode, um die Überprüfung der Serverzertifikate abzuschalten.\n"
+
+#: fe-secure-openssl.c:978
+#, c-format
+msgid ""
+"root certificate file \"%s\" does not exist\n"
+"Either provide the file or change sslmode to disable server certificate verification.\n"
+msgstr ""
+"Root-Zertifikat-Datei »%s« existiert nicht\n"
+"Legen Sie entweder die Datei an oder ändern Sie sslmode, um die Überprüfung der Serverzertifikate abzuschalten.\n"
+
+#: fe-secure-openssl.c:1009
+#, c-format
+msgid "could not open certificate file \"%s\": %s\n"
+msgstr "konnte Zertifikatdatei »%s« nicht öffnen: %s\n"
+
+#: fe-secure-openssl.c:1028
+#, c-format
+msgid "could not read certificate file \"%s\": %s\n"
+msgstr "konnte Zertifikatdatei »%s« nicht lesen: %s\n"
+
+#: fe-secure-openssl.c:1053
+#, c-format
+msgid "could not establish SSL connection: %s\n"
+msgstr "konnte SSL-Verbindung nicht aufbauen: %s\n"
+
+#: fe-secure-openssl.c:1107
+#, c-format
+msgid "could not load SSL engine \"%s\": %s\n"
+msgstr "konnte SSL-Engine »%s« nicht laden: %s\n"
+
+#: fe-secure-openssl.c:1119
+#, c-format
+msgid "could not initialize SSL engine \"%s\": %s\n"
+msgstr "konnte SSL-Engine »%s« nicht initialisieren: %s\n"
+
+#: fe-secure-openssl.c:1135
+#, c-format
+msgid "could not read private SSL key \"%s\" from engine \"%s\": %s\n"
+msgstr "konnte privaten SSL-Schlüssel »%s« nicht von Engine »%s« lesen: %s\n"
+
+#: fe-secure-openssl.c:1149
+#, c-format
+msgid "could not load private SSL key \"%s\" from engine \"%s\": %s\n"
+msgstr "konnte privaten SSL-Schlüssel »%s« nicht von Engine »%s« laden: %s\n"
+
+#: fe-secure-openssl.c:1186
+#, c-format
+msgid "certificate present, but not private key file \"%s\"\n"
+msgstr "Zertifikat vorhanden, aber keine private Schlüsseldatei »%s«\n"
+
+#: fe-secure-openssl.c:1194
+#, c-format
+msgid "private key file \"%s\" has group or world access; permissions should be u=rw (0600) or less\n"
+msgstr "WARNUNG: private Schlüsseldatei »%s« erlaubt Lesezugriff für Gruppe oder Andere; Rechte sollten u=rw (0600) oder weniger sein\n"
+
+#: fe-secure-openssl.c:1219
+#, c-format
+msgid "could not load private key file \"%s\": %s\n"
+msgstr "konnte private Schlüsseldatei »%s« nicht laden: %s\n"
+
+#: fe-secure-openssl.c:1237
+#, c-format
+msgid "certificate does not match private key file \"%s\": %s\n"
+msgstr "Zertifikat passt nicht zur privaten Schlüsseldatei »%s«: %s\n"
+
+#: fe-secure-openssl.c:1337
+#, c-format
+msgid "This may indicate that the server does not support any SSL protocol version between %s and %s.\n"
+msgstr "Das zeigt möglicherweise an, dass der Server keine SSL-Protokollversion zwischen %s und %s unterstützt.\n"
+
+#: fe-secure-openssl.c:1373
+#, c-format
+msgid "certificate could not be obtained: %s\n"
+msgstr "Zertifikat konnte nicht ermittelt werden: %s\n"
+
+#: fe-secure-openssl.c:1462
+#, c-format
+msgid "no SSL error reported"
+msgstr "kein SSL-Fehler berichtet"
+
+#: fe-secure-openssl.c:1471
+#, c-format
+msgid "SSL error code %lu"
+msgstr "SSL-Fehlercode %lu"
+
+#: fe-secure-openssl.c:1718
+#, c-format
+msgid "WARNING: sslpassword truncated\n"
+msgstr "WARNUNG: sslpassword abgeschnitten\n"
+
+#: fe-secure.c:275
+#, c-format
+msgid "could not receive data from server: %s\n"
+msgstr "konnte keine Daten vom Server empfangen: %s\n"
+
+#: fe-secure.c:390
+#, c-format
+msgid "could not send data to server: %s\n"
+msgstr "konnte keine Daten an den Server senden: %s\n"
+
+#: win32.c:314
+#, c-format
+msgid "unrecognized socket error: 0x%08X/%d"
+msgstr "unbekannter Socket-Fehler: 0x%08X/%d"
diff --git a/src/interfaces/libpq/po/es.po b/src/interfaces/libpq/po/es.po
new file mode 100644
index 0000000..c0aad7a
--- /dev/null
+++ b/src/interfaces/libpq/po/es.po
@@ -0,0 +1,1290 @@
+# Spanish message translation file for libpq 2013-08-30 12:42-0400\n"
+#
+# Copyright (c) 2002-2019, PostgreSQL Global Development Group
+# This file is distributed under the same license as the PostgreSQL package.
+#
+# Karim <karim@mribti.com>, 2002.
+# Alvaro Herrera <alvherre@alvh.no-ip.org>, 2003-2013
+# Mario González <gonzalemario@gmail.com>, 2005
+# Carlos Chapi <carloswaldo@babelruins.org>, 2017, 2018
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: libpq (PostgreSQL) 12\n"
+"Report-Msgid-Bugs-To: pgsql-bugs@lists.postgresql.org\n"
+"POT-Creation-Date: 2021-05-13 13:53+0000\n"
+"PO-Revision-Date: 2020-09-12 22:47-0300\n"
+"Last-Translator: Carlos Chapi <carloswaldo@babelruins.org>\n"
+"Language-Team: PgSQL-es-Ayuda <pgsql-es-ayuda@lists.postgresql.org>\n"
+"Language: es\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Poedit 2.3\n"
+
+#: fe-auth-scram.c:212
+msgid "malformed SCRAM message (empty message)\n"
+msgstr "mensaje SCRAM mal formado (mensaje vacío)\n"
+
+#: fe-auth-scram.c:218
+msgid "malformed SCRAM message (length mismatch)\n"
+msgstr "mensaje SCRAM mal formado (longitud no coincide)\n"
+
+#: fe-auth-scram.c:265
+msgid "incorrect server signature\n"
+msgstr "signatura de servidor incorrecta\n"
+
+#: fe-auth-scram.c:274
+msgid "invalid SCRAM exchange state\n"
+msgstr "estado de intercambio SCRAM no es válido\n"
+
+#: fe-auth-scram.c:296
+#, c-format
+msgid "malformed SCRAM message (attribute \"%c\" expected)\n"
+msgstr "mensaje SCRAM mal formado (se esperaba atributo «%c»)\n"
+
+#: fe-auth-scram.c:305
+#, c-format
+msgid "malformed SCRAM message (expected character \"=\" for attribute \"%c\")\n"
+msgstr "mensaje SCRAM mal formado (se esperaba el carácter «=» para el atributo «%c»)\n"
+
+#: fe-auth-scram.c:346
+msgid "could not generate nonce\n"
+msgstr "no se pude generar nonce\n"
+
+#: fe-auth-scram.c:356 fe-auth-scram.c:431 fe-auth-scram.c:579
+#: fe-auth-scram.c:600 fe-auth-scram.c:626 fe-auth-scram.c:641
+#: fe-auth-scram.c:691 fe-auth-scram.c:725 fe-auth.c:289 fe-auth.c:359
+#: fe-auth.c:394 fe-auth.c:611 fe-auth.c:770 fe-auth.c:1129 fe-auth.c:1277
+#: fe-connect.c:892 fe-connect.c:1419 fe-connect.c:1595 fe-connect.c:2200
+#: fe-connect.c:2223 fe-connect.c:2957 fe-connect.c:4605 fe-connect.c:4861
+#: fe-connect.c:4980 fe-connect.c:5233 fe-connect.c:5313 fe-connect.c:5412
+#: fe-connect.c:5668 fe-connect.c:5697 fe-connect.c:5769 fe-connect.c:5793
+#: fe-connect.c:5811 fe-connect.c:5912 fe-connect.c:5921 fe-connect.c:6277
+#: fe-connect.c:6427 fe-exec.c:2747 fe-exec.c:3494 fe-exec.c:3659
+#: fe-gssapi-common.c:111 fe-lobj.c:895 fe-protocol2.c:1207 fe-protocol3.c:961
+#: fe-protocol3.c:1665 fe-secure-common.c:110 fe-secure-gssapi.c:504
+#: fe-secure-openssl.c:440 fe-secure-openssl.c:1091
+msgid "out of memory\n"
+msgstr "memoria agotada\n"
+
+#: fe-auth-scram.c:364
+msgid "could not encode nonce\n"
+msgstr "no se pude generar nonce\n"
+
+#: fe-auth-scram.c:563
+msgid "could not encode client proof\n"
+msgstr "no se pudo codificar la prueba del cliente\n"
+
+#: fe-auth-scram.c:618
+msgid "invalid SCRAM response (nonce mismatch)\n"
+msgstr "respuesta SCRAM no es válida (nonce no coincide)\n"
+
+#: fe-auth-scram.c:651
+msgid "malformed SCRAM message (invalid salt)\n"
+msgstr "mensaje SCRAM mal formado (sal no válida)\n"
+
+#: fe-auth-scram.c:665
+msgid "malformed SCRAM message (invalid iteration count)\n"
+msgstr "mensaje SCRAM mal formado (el conteo de iteración no es válido)\n"
+
+#: fe-auth-scram.c:671
+msgid "malformed SCRAM message (garbage at end of server-first-message)\n"
+msgstr "mensaje SCRAM mal formado (se encontró basura al final de server-first-message)\n"
+
+#: fe-auth-scram.c:702
+#, c-format
+msgid "error received from server in SCRAM exchange: %s\n"
+msgstr "se recibió un error desde el servidor durante el intercambio SCRAM: %s\n"
+
+#: fe-auth-scram.c:718
+msgid "malformed SCRAM message (garbage at end of server-final-message)\n"
+msgstr "mensaje SCRAM mal formado (se encontró basura al final de server-final-message)\n"
+
+#: fe-auth-scram.c:737
+msgid "malformed SCRAM message (invalid server signature)\n"
+msgstr "mensaje SCRAM mal formado (la signatura del servidor no es válida)\n"
+
+#: fe-auth.c:76
+#, c-format
+msgid "out of memory allocating GSSAPI buffer (%d)\n"
+msgstr "memoria agotada creando el búfer GSSAPI (%d)\n"
+
+#: fe-auth.c:131
+msgid "GSSAPI continuation error"
+msgstr "error en continuación de GSSAPI"
+
+#: fe-auth.c:158 fe-auth.c:388 fe-gssapi-common.c:98 fe-secure-common.c:98
+msgid "host name must be specified\n"
+msgstr "el nombre de servidor debe ser especificado\n"
+
+#: fe-auth.c:165
+msgid "duplicate GSS authentication request\n"
+msgstr "petición de autentificación GSS duplicada\n"
+
+#: fe-auth.c:230
+#, c-format
+msgid "out of memory allocating SSPI buffer (%d)\n"
+msgstr "memoria agotada creando el búfer SSPI (%d)\n"
+
+#: fe-auth.c:278
+msgid "SSPI continuation error"
+msgstr "error en continuación de SSPI"
+
+#: fe-auth.c:349
+msgid "duplicate SSPI authentication request\n"
+msgstr "petición de autentificación SSPI duplicada\n"
+
+#: fe-auth.c:374
+msgid "could not acquire SSPI credentials"
+msgstr "no se pudo obtener las credenciales SSPI"
+
+#: fe-auth.c:429
+msgid "channel binding required, but SSL not in use\n"
+msgstr "se requiere enlazado de canal (channel binding), pero no se está usando SSL\n"
+
+#: fe-auth.c:436
+msgid "duplicate SASL authentication request\n"
+msgstr "petición de autentificación SASL duplicada\n"
+
+#: fe-auth.c:492
+msgid "channel binding is required, but client does not support it\n"
+msgstr "se requiere enlazado de canal (channel binding), pero no está soportado en el cliente\n"
+
+#: fe-auth.c:509
+msgid "server offered SCRAM-SHA-256-PLUS authentication over a non-SSL connection\n"
+msgstr "el servidor ofreció autenticación SCRAM-SHA-256-PLUS sobre una conexión no-SSL\n"
+
+#: fe-auth.c:521
+msgid "none of the server's SASL authentication mechanisms are supported\n"
+msgstr "ningún método de autentificación SASL del servidor está soportado\n"
+
+#: fe-auth.c:529
+msgid "channel binding is required, but server did not offer an authentication method that supports channel binding\n"
+msgstr "se requiere enlazado de canal (channel binding), pero el servidor no ofrece un método de autenticación que lo soporte\n"
+
+#: fe-auth.c:635
+#, c-format
+msgid "out of memory allocating SASL buffer (%d)\n"
+msgstr "memoria agotada creando el búfer SASL (%d)\n"
+
+#: fe-auth.c:660
+msgid "AuthenticationSASLFinal received from server, but SASL authentication was not completed\n"
+msgstr "Se recibió AuthenticationSASLFinal desde el servidor, pero la autentificación SASL no se completó\n"
+
+#: fe-auth.c:737
+msgid "SCM_CRED authentication method not supported\n"
+msgstr "el método de autentificación SCM_CRED no está soportado\n"
+
+#: fe-auth.c:836
+msgid "channel binding required, but server authenticated client without channel binding\n"
+msgstr "se requiere enlazado de canal (channel binding), pero el servidor autenticó al cliente sin enlazado de canal\n"
+
+#: fe-auth.c:842
+msgid "channel binding required but not supported by server's authentication request\n"
+msgstr "se requiere enlazado de canal (channel binding), pero no es compatible con la solicitud de autenticación del servidor\n"
+
+#: fe-auth.c:875
+msgid "Kerberos 4 authentication not supported\n"
+msgstr "el método de autentificación Kerberos 4 no está soportado\n"
+
+#: fe-auth.c:880
+msgid "Kerberos 5 authentication not supported\n"
+msgstr "el método de autentificación Kerberos 5 no está soportado\n"
+
+#: fe-auth.c:951
+msgid "GSSAPI authentication not supported\n"
+msgstr "el método de autentificación GSSAPI no está soportado\n"
+
+#: fe-auth.c:983
+msgid "SSPI authentication not supported\n"
+msgstr "el método de autentificación SSPI no está soportado\n"
+
+#: fe-auth.c:991
+msgid "Crypt authentication not supported\n"
+msgstr "el método de autentificación Crypt no está soportado\n"
+
+#: fe-auth.c:1057
+#, c-format
+msgid "authentication method %u not supported\n"
+msgstr "el método de autentificación %u no está soportado\n"
+
+#: fe-auth.c:1104
+#, c-format
+msgid "user name lookup failure: error code %lu\n"
+msgstr "fallo en la búsqueda del nombre de usuario: código de error %lu\n"
+
+#: fe-auth.c:1114 fe-connect.c:2834
+#, c-format
+msgid "could not look up local user ID %d: %s\n"
+msgstr "no se pudo buscar el usuario local de ID %d: %s\n"
+
+#: fe-auth.c:1119 fe-connect.c:2839
+#, c-format
+msgid "local user with ID %d does not exist\n"
+msgstr "no existe un usuario local con ID %d\n"
+
+#: fe-auth.c:1221
+msgid "unexpected shape of result set returned for SHOW\n"
+msgstr "SHOW retornó un conjunto de resultados con estructura inesperada\n"
+
+#: fe-auth.c:1230
+msgid "password_encryption value too long\n"
+msgstr "el valor para password_encryption es demasiado largo\n"
+
+#: fe-auth.c:1270
+#, c-format
+msgid "unrecognized password encryption algorithm \"%s\"\n"
+msgstr "algoritmo para cifrado de contraseña «%s» desconocido\n"
+
+#: fe-connect.c:1075
+#, c-format
+msgid "could not match %d host names to %d hostaddr values\n"
+msgstr "no se pudo emparejar %d nombres de host a %d direcciones de host\n"
+
+#: fe-connect.c:1156
+#, c-format
+msgid "could not match %d port numbers to %d hosts\n"
+msgstr "no se pudo emparejar %d números de puertos a %d hosts\n"
+
+#: fe-connect.c:1249
+#, c-format
+msgid "invalid channel_binding value: \"%s\"\n"
+msgstr "valor cidr no válido: «%s»\n"
+
+#: fe-connect.c:1275
+#, c-format
+msgid "invalid sslmode value: \"%s\"\n"
+msgstr "valor sslmode no válido: «%s»\n"
+
+#: fe-connect.c:1296
+#, c-format
+msgid "sslmode value \"%s\" invalid when SSL support is not compiled in\n"
+msgstr "el valor sslmode «%s» no es válido cuando no se ha compilado con soporte SSL\n"
+
+#: fe-connect.c:1317
+#, c-format
+msgid "invalid ssl_min_protocol_version value: \"%s\"\n"
+msgstr "valor sslmode no válido: «%s»\n"
+
+#: fe-connect.c:1325
+#, c-format
+msgid "invalid ssl_max_protocol_version value: \"%s\"\n"
+msgstr "valor sslmode no válido: «%s»\n"
+
+#: fe-connect.c:1342
+msgid "invalid SSL protocol version range\n"
+msgstr "rango de protocolo SSL no válido \n"
+
+#: fe-connect.c:1357
+#, c-format
+msgid "invalid gssencmode value: \"%s\"\n"
+msgstr "valor gssencmode no válido: «%s»\n"
+
+#: fe-connect.c:1366
+#, c-format
+msgid "gssencmode value \"%s\" invalid when GSSAPI support is not compiled in\n"
+msgstr "el valor gssencmode «%s» no es válido cuando no se ha compilado con soporte GSSAPI\n"
+
+#: fe-connect.c:1401
+#, c-format
+msgid "invalid target_session_attrs value: \"%s\"\n"
+msgstr "valor para target_session_attrs no válido: «%s»\n"
+
+#: fe-connect.c:1619
+#, c-format
+msgid "could not set socket to TCP no delay mode: %s\n"
+msgstr "no se pudo establecer el socket en modo TCP sin retardo: %s\n"
+
+#: fe-connect.c:1680
+#, c-format
+msgid ""
+"could not connect to server: %s\n"
+"\tIs the server running locally and accepting\n"
+"\tconnections on Unix domain socket \"%s\"?\n"
+msgstr ""
+"no se pudo conectar con el servidor: %s\n"
+"\t¿Está el servidor en ejecución localmente y aceptando\n"
+"\tconexiones en el socket de dominio Unix «%s»?\n"
+
+#: fe-connect.c:1717
+#, c-format
+msgid ""
+"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"
+msgstr ""
+"no se pudo conectar con el servidor: %s\n"
+"\t¿Está el servidor en ejecución en el servidor «%s» (%s) y aceptando\n"
+"\tconexiones TCP/IP en el puerto %s?\n"
+
+#: fe-connect.c:1725
+#, c-format
+msgid ""
+"could not connect to server: %s\n"
+"\tIs the server running on host \"%s\" and accepting\n"
+"\tTCP/IP connections on port %s?\n"
+msgstr ""
+"no se pudo conectar con el servidor: %s\n"
+"\t¿Está el servidor en ejecución en el servidor «%s» y aceptando\n"
+"\tconexiones TCP/IP en el puerto %s?\n"
+
+#: fe-connect.c:1795
+#, c-format
+msgid "invalid integer value \"%s\" for connection option \"%s\"\n"
+msgstr "valor entero «%s» no válido para la opción de conexión «%s»\n"
+
+#: fe-connect.c:1825 fe-connect.c:1859 fe-connect.c:1894 fe-connect.c:1981
+#: fe-connect.c:2623
+#, c-format
+msgid "setsockopt(%s) failed: %s\n"
+msgstr "setsockopt(%s) falló: %s\n"
+
+#: fe-connect.c:1947
+#, c-format
+msgid "WSAIoctl(SIO_KEEPALIVE_VALS) failed: %ui\n"
+msgstr "WSAIoctl(SIO_KEEPALIVE_VALS) failed: %ui\n"
+
+#: fe-connect.c:2313
+msgid "invalid connection state, probably indicative of memory corruption\n"
+msgstr "el estado de conexión no es válido, probablemente por corrupción de memoria\n"
+
+#: fe-connect.c:2379
+#, c-format
+msgid "invalid port number: \"%s\"\n"
+msgstr "número de puerto no válido: «%s»\n"
+
+#: fe-connect.c:2395
+#, c-format
+msgid "could not translate host name \"%s\" to address: %s\n"
+msgstr "no se pudo traducir el nombre «%s» a una dirección: %s\n"
+
+#: fe-connect.c:2408
+#, c-format
+msgid "could not parse network address \"%s\": %s\n"
+msgstr "no se pudo interpretar la dirección de red «%s»: %s\n"
+
+#: fe-connect.c:2421
+#, c-format
+msgid "Unix-domain socket path \"%s\" is too long (maximum %d bytes)\n"
+msgstr "la ruta del socket de dominio Unix «%s» es demasiado larga (máximo %d bytes)\n"
+
+#: fe-connect.c:2436
+#, c-format
+msgid "could not translate Unix-domain socket path \"%s\" to address: %s\n"
+msgstr "no se pudo traducir la ruta del socket Unix «%s» a una dirección: %s\n"
+
+#: fe-connect.c:2560
+#, c-format
+msgid "could not create socket: %s\n"
+msgstr "no se pudo crear el socket: %s\n"
+
+#: fe-connect.c:2582
+#, c-format
+msgid "could not set socket to nonblocking mode: %s\n"
+msgstr "no se pudo establecer el socket en modo no bloqueante: %s\n"
+
+#: fe-connect.c:2592
+#, c-format
+msgid "could not set socket to close-on-exec mode: %s\n"
+msgstr "no se pudo poner el socket en modo close-on-exec: %s\n"
+
+#: fe-connect.c:2610
+msgid "keepalives parameter must be an integer\n"
+msgstr "el parámetro de keepalives debe ser un entero\n"
+
+#: fe-connect.c:2750
+#, c-format
+msgid "could not get socket error status: %s\n"
+msgstr "no se pudo determinar el estado de error del socket: %s\n"
+
+#: fe-connect.c:2778
+#, c-format
+msgid "could not get client address from socket: %s\n"
+msgstr "no se pudo obtener la dirección del cliente desde el socket: %s\n"
+
+#: fe-connect.c:2820
+msgid "requirepeer parameter is not supported on this platform\n"
+msgstr "el parámetro requirepeer no está soportado en esta plataforma\n"
+
+#: fe-connect.c:2823
+#, c-format
+msgid "could not get peer credentials: %s\n"
+msgstr "no se pudo obtener credenciales de la contraparte: %s\n"
+
+#: fe-connect.c:2847
+#, c-format
+msgid "requirepeer specifies \"%s\", but actual peer user name is \"%s\"\n"
+msgstr "requirepeer especifica «%s», pero el nombre de usuario de la contraparte es «%s»\n"
+
+#: fe-connect.c:2887
+#, c-format
+msgid "could not send GSSAPI negotiation packet: %s\n"
+msgstr "no se pudo enviar el paquete de negociación GSSAPI: %s\n"
+
+#: fe-connect.c:2899
+msgid "GSSAPI encryption required but was impossible (possibly no credential cache, no server support, or using a local socket)\n"
+msgstr "cifrado GSSAPI requerido, pero fue imposible (posiblemente no hay cache de credenciales, no hay soporte de servidor, o se está usando un socket local)\n"
+
+#: fe-connect.c:2931
+#, c-format
+msgid "could not send SSL negotiation packet: %s\n"
+msgstr "no se pudo enviar el paquete de negociación SSL: %s\n"
+
+#: fe-connect.c:2970
+#, c-format
+msgid "could not send startup packet: %s\n"
+msgstr "no se pudo enviar el paquete de inicio: %s\n"
+
+#: fe-connect.c:3040
+msgid "server does not support SSL, but SSL was required\n"
+msgstr "el servidor no soporta SSL, pero SSL es requerida\n"
+
+#: fe-connect.c:3067
+#, c-format
+msgid "received invalid response to SSL negotiation: %c\n"
+msgstr "se ha recibido una respuesta no válida en la negociación SSL: %c\n"
+
+#: fe-connect.c:3156
+msgid "server doesn't support GSSAPI encryption, but it was required\n"
+msgstr "el servidor no soporta cifrado GSSAPI, pero es requerida\n"
+
+#: fe-connect.c:3168
+#, c-format
+msgid "received invalid response to GSSAPI negotiation: %c\n"
+msgstr "se ha recibido una respuesta no válida en la negociación GSSAPI: %c\n"
+
+#: fe-connect.c:3234 fe-connect.c:3265
+#, c-format
+msgid "expected authentication request from server, but received %c\n"
+msgstr "se esperaba una petición de autentificación desde el servidor, pero se ha recibido %c\n"
+
+#: fe-connect.c:3506
+msgid "unexpected message from server during startup\n"
+msgstr "se ha recibido un mensaje inesperado del servidor durante el inicio\n"
+
+#: fe-connect.c:3711
+#, c-format
+msgid "could not make a writable connection to server \"%s:%s\"\n"
+msgstr "no se pudo establecer una conexión de escritura al servidor: «%s:%s»\n"
+
+#: fe-connect.c:3757
+#, c-format
+msgid "test \"SHOW transaction_read_only\" failed on server \"%s:%s\"\n"
+msgstr "la prueba «SHOW transaction_read_only» falló en el servidor «%s:%s»\n"
+
+#: fe-connect.c:3772
+#, c-format
+msgid "invalid connection state %d, probably indicative of memory corruption\n"
+msgstr "estado de conexión no válido %d, probablemente por corrupción de memoria\n"
+
+#: fe-connect.c:4211 fe-connect.c:4271
+#, c-format
+msgid "PGEventProc \"%s\" failed during PGEVT_CONNRESET event\n"
+msgstr "PGEventProc «%s» falló durante el evento PGEVT_CONNRESET\n"
+
+#: fe-connect.c:4618
+#, c-format
+msgid "invalid LDAP URL \"%s\": scheme must be ldap://\n"
+msgstr "URL LDAP no válida «%s»: el esquema debe ser ldap://\n"
+
+#: fe-connect.c:4633
+#, c-format
+msgid "invalid LDAP URL \"%s\": missing distinguished name\n"
+msgstr "URL LDAP no válida «%s»: distinguished name faltante\n"
+
+#: fe-connect.c:4645 fe-connect.c:4700
+#, c-format
+msgid "invalid LDAP URL \"%s\": must have exactly one attribute\n"
+msgstr "URL LDAP no válida «%s»: debe tener exactamente un atributo\n"
+
+#: fe-connect.c:4656 fe-connect.c:4715
+#, c-format
+msgid "invalid LDAP URL \"%s\": must have search scope (base/one/sub)\n"
+msgstr "URL LDAP no válida «%s»: debe tener ámbito de búsqueda (base/one/sub)\n"
+
+#: fe-connect.c:4667
+#, c-format
+msgid "invalid LDAP URL \"%s\": no filter\n"
+msgstr "URL LDAP no válida «%s»: no tiene filtro\n"
+
+#: fe-connect.c:4688
+#, c-format
+msgid "invalid LDAP URL \"%s\": invalid port number\n"
+msgstr "URL LDAP no válida «%s»: número de puerto no válido\n"
+
+#: fe-connect.c:4724
+msgid "could not create LDAP structure\n"
+msgstr "no se pudo crear estructura LDAP\n"
+
+#: fe-connect.c:4800
+#, c-format
+msgid "lookup on LDAP server failed: %s\n"
+msgstr "búsqueda en servidor LDAP falló: %s\n"
+
+#: fe-connect.c:4811
+msgid "more than one entry found on LDAP lookup\n"
+msgstr "se encontro más de una entrada en búsqueda LDAP\n"
+
+#: fe-connect.c:4812 fe-connect.c:4824
+msgid "no entry found on LDAP lookup\n"
+msgstr "no se encontró ninguna entrada en búsqueda LDAP\n"
+
+#: fe-connect.c:4835 fe-connect.c:4848
+msgid "attribute has no values on LDAP lookup\n"
+msgstr "la búsqueda LDAP entregó atributo sin valores\n"
+
+#: fe-connect.c:4900 fe-connect.c:4919 fe-connect.c:5451
+#, c-format
+msgid "missing \"=\" after \"%s\" in connection info string\n"
+msgstr "falta «=» después de «%s» en la cadena de información de la conexión\n"
+
+#: fe-connect.c:4992 fe-connect.c:5636 fe-connect.c:6410
+#, c-format
+msgid "invalid connection option \"%s\"\n"
+msgstr "opción de conexión no válida «%s»\n"
+
+#: fe-connect.c:5008 fe-connect.c:5500
+msgid "unterminated quoted string in connection info string\n"
+msgstr "cadena de caracteres entre comillas sin terminar en la cadena de información de conexión\n"
+
+#: fe-connect.c:5091
+#, c-format
+msgid "definition of service \"%s\" not found\n"
+msgstr "la definición de servicio «%s» no fue encontrada\n"
+
+#: fe-connect.c:5114
+#, c-format
+msgid "service file \"%s\" not found\n"
+msgstr "el archivo de servicio «%s» no fue encontrado\n"
+
+#: fe-connect.c:5129
+#, c-format
+msgid "line %d too long in service file \"%s\"\n"
+msgstr "la línea %d es demasiado larga en archivo de servicio «%s»\n"
+
+#: fe-connect.c:5201 fe-connect.c:5245
+#, c-format
+msgid "syntax error in service file \"%s\", line %d\n"
+msgstr "error de sintaxis en archivo de servicio «%s», línea %d\n"
+
+#: fe-connect.c:5212
+#, c-format
+msgid "nested service specifications not supported in service file \"%s\", line %d\n"
+msgstr "especificaciones de servicio anidadas no soportadas en archivo de servicio «%s», línea %d\n"
+
+#: fe-connect.c:5932
+#, c-format
+msgid "invalid URI propagated to internal parser routine: \"%s\"\n"
+msgstr "URI no válida propagada a rutina interna de procesamiento: «%s»\n"
+
+#: fe-connect.c:6009
+#, c-format
+msgid "end of string reached when looking for matching \"]\" in IPv6 host address in URI: \"%s\"\n"
+msgstr "se encontró el fin de la cadena mientras se buscaba el «]» correspondiente en dirección IPv6 en URI: «%s»\n"
+
+#: fe-connect.c:6016
+#, c-format
+msgid "IPv6 host address may not be empty in URI: \"%s\"\n"
+msgstr "la dirección IPv6 no puede ser vacía en la URI: «%s»\n"
+
+#: fe-connect.c:6031
+#, c-format
+msgid "unexpected character \"%c\" at position %d in URI (expected \":\" or \"/\"): \"%s\"\n"
+msgstr "carácter «%c» inesperado en la posición %d en URI (se esperaba «:» o «/»): «%s»\n"
+
+#: fe-connect.c:6160
+#, c-format
+msgid "extra key/value separator \"=\" in URI query parameter: \"%s\"\n"
+msgstr "separador llave/valor «=» extra en parámetro de la URI: «%s»\n"
+
+#: fe-connect.c:6180
+#, c-format
+msgid "missing key/value separator \"=\" in URI query parameter: \"%s\"\n"
+msgstr "separador llave/valor «=» faltante en parámetro de la URI: «%s»\n"
+
+#: fe-connect.c:6231
+#, c-format
+msgid "invalid URI query parameter: \"%s\"\n"
+msgstr "parámetro de URI no válido: «%s»\n"
+
+#: fe-connect.c:6305
+#, c-format
+msgid "invalid percent-encoded token: \"%s\"\n"
+msgstr "elemento escapado con %% no válido: «%s»\n"
+
+#: fe-connect.c:6315
+#, c-format
+msgid "forbidden value %%00 in percent-encoded value: \"%s\"\n"
+msgstr "valor no permitido %%00 en valor escapado con %%: «%s»\n"
+
+#: fe-connect.c:6678
+msgid "connection pointer is NULL\n"
+msgstr "el puntero de conexión es NULL\n"
+
+#: fe-connect.c:6974
+#, c-format
+msgid "WARNING: password file \"%s\" is not a plain file\n"
+msgstr "ADVERTENCIA: El archivo de claves «%s» no es un archivo plano\n"
+
+#: fe-connect.c:6983
+#, c-format
+msgid "WARNING: password file \"%s\" has group or world access; permissions should be u=rw (0600) or less\n"
+msgstr "ADVERTENCIA: El archivo de claves «%s» tiene permiso de lectura para el grupo u otros; los permisos deberían ser u=rw (0600) o menos\n"
+
+#: fe-connect.c:7091
+#, c-format
+msgid "password retrieved from file \"%s\"\n"
+msgstr "contraseña obtenida desde el archivo «%s»\n"
+
+#: fe-exec.c:444 fe-exec.c:2821
+#, c-format
+msgid "row number %d is out of range 0..%d"
+msgstr "el número de fila %d está fuera del rango 0..%d"
+
+#: fe-exec.c:505 fe-protocol2.c:497 fe-protocol2.c:532 fe-protocol2.c:1050
+#: fe-protocol3.c:206 fe-protocol3.c:233 fe-protocol3.c:250 fe-protocol3.c:328
+#: fe-protocol3.c:692 fe-protocol3.c:920
+msgid "out of memory"
+msgstr "memoria agotada"
+
+#: fe-exec.c:506 fe-protocol2.c:1396 fe-protocol3.c:1873
+#, c-format
+msgid "%s"
+msgstr "%s"
+
+#: fe-exec.c:815
+msgid "write to server failed\n"
+msgstr "falló escritura al servidor\n"
+
+#: fe-exec.c:896
+msgid "NOTICE"
+msgstr "AVISO"
+
+#: fe-exec.c:954
+msgid "PGresult cannot support more than INT_MAX tuples"
+msgstr "PGresult no puede soportar un número de tuplas mayor que INT_MAX"
+
+#: fe-exec.c:966
+msgid "size_t overflow"
+msgstr "desbordamiento de size_t"
+
+#: fe-exec.c:1243 fe-exec.c:1301 fe-exec.c:1347
+msgid "command string is a null pointer\n"
+msgstr "la cadena de orden es un puntero nulo\n"
+
+#: fe-exec.c:1307 fe-exec.c:1353 fe-exec.c:1448
+msgid "number of parameters must be between 0 and 65535\n"
+msgstr "el número de parámetros debe estar entre 0 y 65535\n"
+
+#: fe-exec.c:1341 fe-exec.c:1442
+msgid "statement name is a null pointer\n"
+msgstr "el nombre de sentencia es un puntero nulo\n"
+
+#: fe-exec.c:1361 fe-exec.c:1524 fe-exec.c:2233 fe-exec.c:2435
+msgid "function requires at least protocol version 3.0\n"
+msgstr "la función requiere protocolo 3.0 o superior\n"
+
+#: fe-exec.c:1479
+msgid "no connection to the server\n"
+msgstr "no hay conexión con el servidor\n"
+
+#: fe-exec.c:1486
+msgid "another command is already in progress\n"
+msgstr "hay otra orden en ejecución\n"
+
+#: fe-exec.c:1600
+msgid "length must be given for binary parameter\n"
+msgstr "el largo debe ser especificado para un parámetro binario\n"
+
+#: fe-exec.c:1863
+#, c-format
+msgid "unexpected asyncStatus: %d\n"
+msgstr "asyncStatus no esperado: %d\n"
+
+#: fe-exec.c:1883
+#, c-format
+msgid "PGEventProc \"%s\" failed during PGEVT_RESULTCREATE event\n"
+msgstr "PGEventProc «%s» falló durante el evento PGEVT_RESULTCREATE\n"
+
+#: fe-exec.c:2043
+msgid "COPY terminated by new PQexec"
+msgstr "COPY terminado por un nuevo PQexec"
+
+#: fe-exec.c:2051
+msgid "COPY IN state must be terminated first\n"
+msgstr "el estado COPY IN debe ser terminado primero\n"
+
+#: fe-exec.c:2071
+msgid "COPY OUT state must be terminated first\n"
+msgstr "el estado COPY OUT debe ser terminado primero\n"
+
+#: fe-exec.c:2079
+msgid "PQexec not allowed during COPY BOTH\n"
+msgstr "PQexec no está permitido durante COPY BOTH\n"
+
+#: fe-exec.c:2325 fe-exec.c:2392 fe-exec.c:2482 fe-protocol2.c:1353
+#: fe-protocol3.c:1804
+msgid "no COPY in progress\n"
+msgstr "no hay COPY alguno en ejecución\n"
+
+#: fe-exec.c:2672
+msgid "connection in wrong state\n"
+msgstr "la conexión está en un estado incorrecto\n"
+
+#: fe-exec.c:2703
+msgid "invalid ExecStatusType code"
+msgstr "el código de ExecStatusType no es válido"
+
+#: fe-exec.c:2730
+msgid "PGresult is not an error result\n"
+msgstr "PGresult no es un resultado de error\n"
+
+#: fe-exec.c:2805 fe-exec.c:2828
+#, c-format
+msgid "column number %d is out of range 0..%d"
+msgstr "el número de columna %d está fuera del rango 0..%d"
+
+#: fe-exec.c:2843
+#, c-format
+msgid "parameter number %d is out of range 0..%d"
+msgstr "el número de parámetro %d está fuera del rango 0..%d"
+
+#: fe-exec.c:3153
+#, c-format
+msgid "could not interpret result from server: %s"
+msgstr "no se pudo interpretar el resultado del servidor: %s"
+
+#: fe-exec.c:3392 fe-exec.c:3476
+msgid "incomplete multibyte character\n"
+msgstr "carácter multibyte incompleto\n"
+
+#: fe-gssapi-common.c:124
+msgid "GSSAPI name import error"
+msgstr "error de importación de nombre de GSSAPI"
+
+#: fe-lobj.c:154
+msgid "cannot determine OID of function lo_truncate\n"
+msgstr "no se puede determinar el OID de la función lo_truncate\n"
+
+#: fe-lobj.c:170
+msgid "argument of lo_truncate exceeds integer range\n"
+msgstr "el argumento de lo_truncate excede el rango de enteros\n"
+
+#: fe-lobj.c:221
+msgid "cannot determine OID of function lo_truncate64\n"
+msgstr "no se puede determinar el OID de la función lo_truncate64\n"
+
+#: fe-lobj.c:279
+msgid "argument of lo_read exceeds integer range\n"
+msgstr "el argumento de lo_read excede el rango de enteros\n"
+
+#: fe-lobj.c:334
+msgid "argument of lo_write exceeds integer range\n"
+msgstr "el argumento de lo_write excede el rango de enteros\n"
+
+#: fe-lobj.c:425
+msgid "cannot determine OID of function lo_lseek64\n"
+msgstr "no se puede determinar el OID de la función lo_lseek64\n"
+
+#: fe-lobj.c:521
+msgid "cannot determine OID of function lo_create\n"
+msgstr "no se puede determinar el OID de la función lo_create\n"
+
+#: fe-lobj.c:600
+msgid "cannot determine OID of function lo_tell64\n"
+msgstr "no se puede determinar el OID de la función lo_tell64\n"
+
+#: fe-lobj.c:706 fe-lobj.c:815
+#, c-format
+msgid "could not open file \"%s\": %s\n"
+msgstr "no se pudo abrir el archivo «%s»: %s\n"
+
+#: fe-lobj.c:761
+#, c-format
+msgid "could not read from file \"%s\": %s\n"
+msgstr "no se pudo leer el archivo «%s»: %s\n"
+
+#: fe-lobj.c:835 fe-lobj.c:859
+#, c-format
+msgid "could not write to file \"%s\": %s\n"
+msgstr "no se pudo escribir a archivo «%s»: %s\n"
+
+#: fe-lobj.c:946
+msgid "query to initialize large object functions did not return data\n"
+msgstr "la consulta para inicializar las funciones de objetos grandes no devuelve datos\n"
+
+#: fe-lobj.c:995
+msgid "cannot determine OID of function lo_open\n"
+msgstr "no se puede determinar el OID de la función lo_open\n"
+
+#: fe-lobj.c:1002
+msgid "cannot determine OID of function lo_close\n"
+msgstr "no se puede determinar el OID de la función lo_close\n"
+
+#: fe-lobj.c:1009
+msgid "cannot determine OID of function lo_creat\n"
+msgstr "no se puede determinar el OID de la función lo_creat\n"
+
+#: fe-lobj.c:1016
+msgid "cannot determine OID of function lo_unlink\n"
+msgstr "no se puede determinar el OID de la función lo_unlink\n"
+
+#: fe-lobj.c:1023
+msgid "cannot determine OID of function lo_lseek\n"
+msgstr "no se puede determinar el OID de la función lo_lseek\n"
+
+#: fe-lobj.c:1030
+msgid "cannot determine OID of function lo_tell\n"
+msgstr "no se puede determinar el OID de la función lo_tell\n"
+
+#: fe-lobj.c:1037
+msgid "cannot determine OID of function loread\n"
+msgstr "no se puede determinar el OID de la función loread\n"
+
+#: fe-lobj.c:1044
+msgid "cannot determine OID of function lowrite\n"
+msgstr "no se puede determinar el OID de la función lowrite\n"
+
+#: fe-misc.c:289
+#, c-format
+msgid "integer of size %lu not supported by pqGetInt"
+msgstr "el entero de tamaño %lu no está soportado por pqGetInt"
+
+#: fe-misc.c:325
+#, c-format
+msgid "integer of size %lu not supported by pqPutInt"
+msgstr "el entero de tamaño %lu no está soportado por pqPutInt"
+
+#: fe-misc.c:636 fe-misc.c:869
+msgid "connection not open\n"
+msgstr "la conexión no está abierta\n"
+
+#: fe-misc.c:805 fe-secure-openssl.c:209 fe-secure-openssl.c:316
+#: fe-secure.c:267 fe-secure.c:383
+msgid ""
+"server closed the connection unexpectedly\n"
+"\tThis probably means the server terminated abnormally\n"
+"\tbefore or while processing the request.\n"
+msgstr ""
+"el servidor ha cerrado la conexión inesperadamente\n"
+"\tProbablemente se debe a que el servidor terminó de manera anormal\n"
+"\tantes o durante el procesamiento de la petición.\n"
+
+#: fe-misc.c:1063
+msgid "timeout expired\n"
+msgstr "tiempo de espera agotado\n"
+
+#: fe-misc.c:1108
+msgid "invalid socket\n"
+msgstr "socket no válido\n"
+
+#: fe-misc.c:1131
+#, c-format
+msgid "select() failed: %s\n"
+msgstr "select() fallida: %s\n"
+
+#: fe-protocol2.c:87
+#, c-format
+msgid "invalid setenv state %c, probably indicative of memory corruption\n"
+msgstr "el estado de setenv %c no es válido, probablemente por corrupción de memoria\n"
+
+#: fe-protocol2.c:384
+#, c-format
+msgid "invalid state %c, probably indicative of memory corruption\n"
+msgstr "el estado %c no es válido, probablemente por corrupción de memoria\n"
+
+#: fe-protocol2.c:473 fe-protocol3.c:183
+#, c-format
+msgid "message type 0x%02x arrived from server while idle"
+msgstr "un mensaje de tipo 0x%02x llegó del servidor estando inactivo"
+
+#: fe-protocol2.c:523
+#, c-format
+msgid "unexpected character %c following empty query response (\"I\" message)"
+msgstr "carácter %c no esperado, siguiendo una respuesta de consulta vacía (mensaje «I»)"
+
+#: fe-protocol2.c:589
+#, c-format
+msgid "server sent data (\"D\" message) without prior row description (\"T\" message)"
+msgstr "el servidor envió datos (mensaje «D») sin precederlos con una descripción de fila (mensaje «T»)"
+
+#: fe-protocol2.c:607
+#, c-format
+msgid "server sent binary data (\"B\" message) without prior row description (\"T\" message)"
+msgstr "el servidor envió datos binarios (mensaje «B») sin precederlos con una description de fila (mensaje «T»)"
+
+#: fe-protocol2.c:626 fe-protocol3.c:403
+#, c-format
+msgid "unexpected response from server; first received character was \"%c\"\n"
+msgstr "se ha recibido una respuesta inesperada del servidor; el primer carácter recibido fue «%c»\n"
+
+#: fe-protocol2.c:755 fe-protocol2.c:930 fe-protocol3.c:603 fe-protocol3.c:809
+msgid "out of memory for query result"
+msgstr "no hay suficiente memoria para el resultado de la consulta"
+
+#: fe-protocol2.c:1408
+#, c-format
+msgid "lost synchronization with server, resetting connection"
+msgstr "se perdió la sincronía con el servidor, reseteando la conexión"
+
+#: fe-protocol2.c:1530 fe-protocol2.c:1562 fe-protocol3.c:2061
+#, c-format
+msgid "protocol error: id=0x%x\n"
+msgstr "error de protocolo: id=0x%x\n"
+
+#: fe-protocol3.c:360
+msgid "server sent data (\"D\" message) without prior row description (\"T\" message)\n"
+msgstr "el servidor envió datos (mensaje «D») sin precederlos con una descripción de fila (mensaje «T»)\n"
+
+#: fe-protocol3.c:424
+#, c-format
+msgid "message contents do not agree with length in message type \"%c\"\n"
+msgstr "el contenido del mensaje no concuerda con el largo, en el mensaje tipo «%c»\n"
+
+#: fe-protocol3.c:444
+#, c-format
+msgid "lost synchronization with server: got message type \"%c\", length %d\n"
+msgstr "se perdió la sincronía con el servidor: se recibió un mensaje de tipo «%c», largo %d\n"
+
+#: fe-protocol3.c:494 fe-protocol3.c:534
+msgid "insufficient data in \"T\" message"
+msgstr "datos insuficientes en el mensaje «T»"
+
+#: fe-protocol3.c:672
+#, fuzzy
+#| msgid "insufficient data in \"T\" message"
+msgid "insufficient data in \"t\" message"
+msgstr "datos insuficientes en el mensaje «T»"
+
+#: fe-protocol3.c:731 fe-protocol3.c:763 fe-protocol3.c:781
+msgid "insufficient data in \"D\" message"
+msgstr "datos insuficientes en el mensaje «D»"
+
+#: fe-protocol3.c:737
+msgid "unexpected field count in \"D\" message"
+msgstr "cantidad de campos inesperada en mensaje «D»"
+
+#: fe-protocol3.c:974
+msgid "no error message available\n"
+msgstr "no hay mensaje de error disponible\n"
+
+#. translator: %s represents a digit string
+#: fe-protocol3.c:1022 fe-protocol3.c:1041
+#, c-format
+msgid " at character %s"
+msgstr " en el carácter %s"
+
+#: fe-protocol3.c:1054
+#, c-format
+msgid "DETAIL: %s\n"
+msgstr "DETALLE: %s\n"
+
+#: fe-protocol3.c:1057
+#, c-format
+msgid "HINT: %s\n"
+msgstr "SUGERENCIA: %s\n"
+
+#: fe-protocol3.c:1060
+#, c-format
+msgid "QUERY: %s\n"
+msgstr "CONSULTA: %s\n"
+
+#: fe-protocol3.c:1067
+#, c-format
+msgid "CONTEXT: %s\n"
+msgstr "CONTEXTO: %s\n"
+
+#: fe-protocol3.c:1076
+#, c-format
+msgid "SCHEMA NAME: %s\n"
+msgstr "NOMBRE DE ESQUEMA: %s\n"
+
+#: fe-protocol3.c:1080
+#, c-format
+msgid "TABLE NAME: %s\n"
+msgstr "NOMBRE DE TABLA: %s\n"
+
+#: fe-protocol3.c:1084
+#, c-format
+msgid "COLUMN NAME: %s\n"
+msgstr "NOMBRE DE COLUMNA: %s\n"
+
+#: fe-protocol3.c:1088
+#, c-format
+msgid "DATATYPE NAME: %s\n"
+msgstr "NOMBRE TIPO DE DATO: %s\n"
+
+#: fe-protocol3.c:1092
+#, c-format
+msgid "CONSTRAINT NAME: %s\n"
+msgstr "NOMBRE DE RESTRICCIÓN: %s\n"
+
+#: fe-protocol3.c:1104
+msgid "LOCATION: "
+msgstr "UBICACIÓN: "
+
+#: fe-protocol3.c:1106
+#, c-format
+msgid "%s, "
+msgstr "%s, "
+
+#: fe-protocol3.c:1108
+#, c-format
+msgid "%s:%s"
+msgstr "%s:%s"
+
+#: fe-protocol3.c:1303
+#, c-format
+msgid "LINE %d: "
+msgstr "LÍNEA %d: "
+
+#: fe-protocol3.c:1698
+msgid "PQgetline: not doing text COPY OUT\n"
+msgstr "PQgetline: no se está haciendo COPY OUT de texto\n"
+
+#: fe-secure-common.c:124
+msgid "SSL certificate's name contains embedded null\n"
+msgstr "el elemento de nombre en el certificado SSL contiene un carácter null\n"
+
+#: fe-secure-common.c:171
+msgid "host name must be specified for a verified SSL connection\n"
+msgstr "el nombre de servidor debe ser especificado para una conexión SSL verificada\n"
+
+#: fe-secure-common.c:196
+#, c-format
+msgid "server certificate for \"%s\" does not match host name \"%s\"\n"
+msgstr "el certificado de servidor para «%s» no coincide con el nombre de servidor «%s»\n"
+
+#: fe-secure-common.c:202
+msgid "could not get server's host name from server certificate\n"
+msgstr "no se pudo obtener el nombre de servidor desde el certificado del servidor\n"
+
+#: fe-secure-gssapi.c:201
+msgid "GSSAPI wrap error"
+msgstr "error de «wrap» de GSSAPI"
+
+#: fe-secure-gssapi.c:209
+msgid "outgoing GSSAPI message would not use confidentiality\n"
+msgstr "mensaje GSSAPI de saluda no proveería confidencialidad\n"
+
+#: fe-secure-gssapi.c:217
+#, c-format
+msgid "client tried to send oversize GSSAPI packet (%zu > %zu)\n"
+msgstr "el cliente intentó enviar un paquete GSSAPI demasiado grande (%zu > %zu)\n"
+
+#: fe-secure-gssapi.c:354 fe-secure-gssapi.c:596
+#, c-format
+msgid "oversize GSSAPI packet sent by the server (%zu > %zu)\n"
+msgstr "paquete GSSAPI demasiado grande enviado por el servidor (%zu > %zu)\n"
+
+#: fe-secure-gssapi.c:393
+msgid "GSSAPI unwrap error"
+msgstr "error de «unwrap» de GSSAPI"
+
+#: fe-secure-gssapi.c:403
+msgid "incoming GSSAPI message did not use confidentiality\n"
+msgstr "mensaje GSSAPI entrante no usó confidencialidad\n"
+
+#: fe-secure-gssapi.c:642
+msgid "could not initiate GSSAPI security context"
+msgstr "no se pudo iniciar un contexto de seguridad GSSAPI"
+
+#: fe-secure-gssapi.c:670
+msgid "GSSAPI size check error"
+msgstr "error de verificación de tamaño GSSAPI"
+
+#: fe-secure-gssapi.c:681
+msgid "GSSAPI context establishment error"
+msgstr "error de establecimiento de contexto de GSSAPI"
+
+#: fe-secure-openssl.c:214 fe-secure-openssl.c:321 fe-secure-openssl.c:1291
+#, c-format
+msgid "SSL SYSCALL error: %s\n"
+msgstr "ERROR en llamada SSL: %s\n"
+
+#: fe-secure-openssl.c:221 fe-secure-openssl.c:328 fe-secure-openssl.c:1295
+msgid "SSL SYSCALL error: EOF detected\n"
+msgstr "ERROR en llamada SSL: detectado fin de archivo\n"
+
+#: fe-secure-openssl.c:232 fe-secure-openssl.c:339 fe-secure-openssl.c:1304
+#, c-format
+msgid "SSL error: %s\n"
+msgstr "error de SSL: %s\n"
+
+#: fe-secure-openssl.c:247 fe-secure-openssl.c:354
+msgid "SSL connection has been closed unexpectedly\n"
+msgstr "la conexión SSL se ha cerrado inesperadamente\n"
+
+#: fe-secure-openssl.c:253 fe-secure-openssl.c:360 fe-secure-openssl.c:1354
+#, c-format
+msgid "unrecognized SSL error code: %d\n"
+msgstr "código de error SSL no reconocido: %d\n"
+
+#: fe-secure-openssl.c:400
+msgid "could not determine server certificate signature algorithm\n"
+msgstr "no se pudo determinar el algoritmo de firma del certificado del servidor\n"
+
+#: fe-secure-openssl.c:421
+#, c-format
+msgid "could not find digest for NID %s\n"
+msgstr "no se pudo encontrar «digest» para el NID %s\n"
+
+#: fe-secure-openssl.c:431
+msgid "could not generate peer certificate hash\n"
+msgstr "no se pudo generar hash de certificado de la contraparte\n"
+
+#: fe-secure-openssl.c:488
+msgid "SSL certificate's name entry is missing\n"
+msgstr "falta el elemento de nombre en el certificado SSL\n"
+
+#: fe-secure-openssl.c:815
+#, c-format
+msgid "could not create SSL context: %s\n"
+msgstr "no se pudo crear un contexto SSL: %s\n"
+
+#: fe-secure-openssl.c:854
+#, c-format
+msgid "invalid value \"%s\" for minimum SSL protocol version\n"
+msgstr "valor entero «%s» no válido para la versión mínima del protocolo SSL\n"
+
+#: fe-secure-openssl.c:865
+#, c-format
+msgid "could not set minimum SSL protocol version: %s\n"
+msgstr "no se pudo definir la versión mínima de protocolo SSL: %s\n"
+
+#: fe-secure-openssl.c:883
+#, c-format
+msgid "invalid value \"%s\" for maximum SSL protocol version\n"
+msgstr "valor entero «%s» no válido para la versión máxima del protocolo SSL\n"
+
+#: fe-secure-openssl.c:894
+#, c-format
+msgid "could not set maximum SSL protocol version: %s\n"
+msgstr "no se pudo definir la versión máxima de protocolo SSL: %s\n"
+
+#: fe-secure-openssl.c:930
+#, c-format
+msgid "could not read root certificate file \"%s\": %s\n"
+msgstr "no se pudo leer la lista de certificado raíz «%s»: %s\n"
+
+#: fe-secure-openssl.c:974
+msgid ""
+"could not get home directory to locate root certificate file\n"
+"Either provide the file or change sslmode to disable server certificate verification.\n"
+msgstr ""
+"no se pudo obtener el directorio «home» para ubicar el archivo del certificado raíz\n"
+"Debe ya sea entregar este archivo, o bien cambiar sslmode para deshabilitar la verificación de certificados del servidor.\n"
+
+#: fe-secure-openssl.c:978
+#, c-format
+msgid ""
+"root certificate file \"%s\" does not exist\n"
+"Either provide the file or change sslmode to disable server certificate verification.\n"
+msgstr ""
+"el archivo de certificado raíz «%s» no existe\n"
+"Debe ya sea entregar este archivo, o bien cambiar sslmode para deshabilitar la verificación de certificados del servidor.\n"
+
+#: fe-secure-openssl.c:1009
+#, c-format
+msgid "could not open certificate file \"%s\": %s\n"
+msgstr "no se pudo abrir el archivo de certificado «%s»: %s\n"
+
+#: fe-secure-openssl.c:1028
+#, c-format
+msgid "could not read certificate file \"%s\": %s\n"
+msgstr "no se pudo leer el archivo de certificado «%s»: %s\n"
+
+#: fe-secure-openssl.c:1053
+#, c-format
+msgid "could not establish SSL connection: %s\n"
+msgstr "no se pudo establecer conexión SSL: %s\n"
+
+#: fe-secure-openssl.c:1107
+#, c-format
+msgid "could not load SSL engine \"%s\": %s\n"
+msgstr "no se pudo cargar el motor SSL «%s»: %s\n"
+
+#: fe-secure-openssl.c:1119
+#, c-format
+msgid "could not initialize SSL engine \"%s\": %s\n"
+msgstr "no se pudo inicializar el motor SSL «%s»: %s\n"
+
+#: fe-secure-openssl.c:1135
+#, c-format
+msgid "could not read private SSL key \"%s\" from engine \"%s\": %s\n"
+msgstr "no se pudo leer el archivo de la llave privada SSL «%s» desde el motor «%s»: %s\n"
+
+#: fe-secure-openssl.c:1149
+#, c-format
+msgid "could not load private SSL key \"%s\" from engine \"%s\": %s\n"
+msgstr "no se pudo leer la llave privada SSL «%s» desde el motor «%s»: %s\n"
+
+#: fe-secure-openssl.c:1186
+#, c-format
+msgid "certificate present, but not private key file \"%s\"\n"
+msgstr "el certificado está presente, pero no la llave privada «%s»\n"
+
+#: fe-secure-openssl.c:1194
+#, c-format
+msgid "private key file \"%s\" has group or world access; permissions should be u=rw (0600) or less\n"
+msgstr "el archivo de la llave privada «%s» tiene permiso de lectura para el grupo u otros; los permisos deberían ser u=rw (0600) o menos\n"
+
+#: fe-secure-openssl.c:1219
+#, c-format
+msgid "could not load private key file \"%s\": %s\n"
+msgstr "no se pudo cargar el archivo de la llave privada «%s»: %s\n"
+
+#: fe-secure-openssl.c:1237
+#, c-format
+msgid "certificate does not match private key file \"%s\": %s\n"
+msgstr "el certificado no coincide con la llave privada «%s»: %s\n"
+
+#: fe-secure-openssl.c:1337
+#, c-format
+msgid "This may indicate that the server does not support any SSL protocol version between %s and %s.\n"
+msgstr "Esto puede indicar que el servidor no soporta ninguna versión del protocolo SSL entre %s and %s.\n"
+
+#: fe-secure-openssl.c:1373
+#, c-format
+msgid "certificate could not be obtained: %s\n"
+msgstr "el certificado no pudo ser obtenido: %s\n"
+
+#: fe-secure-openssl.c:1462
+#, c-format
+msgid "no SSL error reported"
+msgstr "código de error SSL no reportado"
+
+#: fe-secure-openssl.c:1471
+#, c-format
+msgid "SSL error code %lu"
+msgstr "código de error SSL %lu"
+
+#: fe-secure-openssl.c:1718
+#, c-format
+msgid "WARNING: sslpassword truncated\n"
+msgstr "ADVERTENCIA: sslpassword truncada\n"
+
+#: fe-secure.c:275
+#, c-format
+msgid "could not receive data from server: %s\n"
+msgstr "no se pudo recibir datos del servidor: %s\n"
+
+#: fe-secure.c:390
+#, c-format
+msgid "could not send data to server: %s\n"
+msgstr "no se pudo enviar datos al servidor: %s\n"
+
+#: win32.c:314
+#, c-format
+msgid "unrecognized socket error: 0x%08X/%d"
+msgstr "código de error de socket no reconocido: 0x%08X/%d"
+
+#~ msgid "extraneous data in \"D\" message"
+#~ msgstr "datos ininteligibles en mensaje «D»"
+
+#~ msgid "extraneous data in \"t\" message"
+#~ msgstr "datos ininteligibles en mensaje «t»"
+
+#~ msgid "extraneous data in \"T\" message"
+#~ msgstr "datos ininteligibles en mensaje «T»"
diff --git a/src/interfaces/libpq/po/fr.po b/src/interfaces/libpq/po/fr.po
new file mode 100644
index 0000000..094361a
--- /dev/null
+++ b/src/interfaces/libpq/po/fr.po
@@ -0,0 +1,1399 @@
+# translation of libpq.po to fr_fr
+# french message translation file for libpq
+#
+# Use these quotes: « %s »
+#
+# Guillaume Lelarge <guillaume@lelarge.info>, 2004-2009.
+# Stéphane Schildknecht <stephane.schildknecht@dalibo.com>, 2009.
+msgid ""
+msgstr ""
+"Project-Id-Version: PostgreSQL 12\n"
+"Report-Msgid-Bugs-To: pgsql-bugs@lists.postgresql.org\n"
+"POT-Creation-Date: 2021-04-09 11:09+0000\n"
+"PO-Revision-Date: 2021-04-11 09:47+0200\n"
+"Last-Translator: Guillaume Lelarge <guillaume@lelarge.info>\n"
+"Language-Team: PostgreSQLfr <pgsql-fr-generale@postgresql.org>\n"
+"Language: fr\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Poedit 2.4.2\n"
+
+#: fe-auth-scram.c:212
+msgid "malformed SCRAM message (empty message)\n"
+msgstr "message SCRAM malformé (message vide)\n"
+
+#: fe-auth-scram.c:218
+msgid "malformed SCRAM message (length mismatch)\n"
+msgstr "message SCRAM malformé (pas de correspondance sur la longueur)\n"
+
+#: fe-auth-scram.c:265
+msgid "incorrect server signature\n"
+msgstr "signature invalide du serveur\n"
+
+#: fe-auth-scram.c:274
+msgid "invalid SCRAM exchange state\n"
+msgstr "état d'échange SCRAM invalide\n"
+
+#: fe-auth-scram.c:296
+#, c-format
+msgid "malformed SCRAM message (attribute \"%c\" expected)\n"
+msgstr "message SCRAM malformé (attribut « %c » attendu)\n"
+
+#: fe-auth-scram.c:305
+#, c-format
+msgid "malformed SCRAM message (expected character \"=\" for attribute \"%c\")\n"
+msgstr "message SCRAM malformé (caractère « = » attendu pour l'attribut « %c »)\n"
+
+#: fe-auth-scram.c:346
+msgid "could not generate nonce\n"
+msgstr "n'a pas pu générer le nonce\n"
+
+#: fe-auth-scram.c:356 fe-auth-scram.c:431 fe-auth-scram.c:579
+#: fe-auth-scram.c:600 fe-auth-scram.c:626 fe-auth-scram.c:641
+#: fe-auth-scram.c:691 fe-auth-scram.c:725 fe-auth.c:289 fe-auth.c:359
+#: fe-auth.c:394 fe-auth.c:611 fe-auth.c:770 fe-auth.c:1129 fe-auth.c:1277
+#: fe-connect.c:892 fe-connect.c:1419 fe-connect.c:1595 fe-connect.c:2200
+#: fe-connect.c:2223 fe-connect.c:2957 fe-connect.c:4605 fe-connect.c:4861
+#: fe-connect.c:4980 fe-connect.c:5233 fe-connect.c:5313 fe-connect.c:5412
+#: fe-connect.c:5668 fe-connect.c:5697 fe-connect.c:5769 fe-connect.c:5793
+#: fe-connect.c:5811 fe-connect.c:5912 fe-connect.c:5921 fe-connect.c:6277
+#: fe-connect.c:6427 fe-exec.c:2747 fe-exec.c:3494 fe-exec.c:3659
+#: fe-gssapi-common.c:111 fe-lobj.c:895 fe-protocol2.c:1207 fe-protocol3.c:961
+#: fe-protocol3.c:1665 fe-secure-common.c:110 fe-secure-gssapi.c:504
+#: fe-secure-openssl.c:440 fe-secure-openssl.c:1091
+msgid "out of memory\n"
+msgstr "mémoire épuisée\n"
+
+#: fe-auth-scram.c:364
+msgid "could not encode nonce\n"
+msgstr "n'a pas pu encoder le nonce\n"
+
+#: fe-auth-scram.c:563
+msgid "could not encode client proof\n"
+msgstr "n'a pas pu encoder la preuve du client\n"
+
+#: fe-auth-scram.c:618
+msgid "invalid SCRAM response (nonce mismatch)\n"
+msgstr "réponse SCRAM invalide (pas de correspondance sur nonce)\n"
+
+#: fe-auth-scram.c:651
+msgid "malformed SCRAM message (invalid salt)\n"
+msgstr "message SCRAM malformé (sel invalide)\n"
+
+#: fe-auth-scram.c:665
+msgid "malformed SCRAM message (invalid iteration count)\n"
+msgstr "message SCRAM malformé (décompte d'itération invalide)\n"
+
+#: fe-auth-scram.c:671
+msgid "malformed SCRAM message (garbage at end of server-first-message)\n"
+msgstr "message SCRAM malformé (problème à la fin du server-first-message)\n"
+
+#: fe-auth-scram.c:702
+#, c-format
+msgid "error received from server in SCRAM exchange: %s\n"
+msgstr "réception d'une erreur du serveur dans l'échange SCRAM : %s\n"
+
+#: fe-auth-scram.c:718
+msgid "malformed SCRAM message (garbage at end of server-final-message)\n"
+msgstr "message SCRAM malformé (problème à la fin du server-final-message)\n"
+
+#: fe-auth-scram.c:737
+msgid "malformed SCRAM message (invalid server signature)\n"
+msgstr "message SCRAM malformé (signature serveur invalide)\n"
+
+#: fe-auth.c:76
+#, c-format
+msgid "out of memory allocating GSSAPI buffer (%d)\n"
+msgstr "mémoire épuisée lors de l'allocation du tampon GSSAPI (%d)\n"
+
+#: fe-auth.c:131
+msgid "GSSAPI continuation error"
+msgstr "erreur de suite GSSAPI"
+
+#: fe-auth.c:158 fe-auth.c:388 fe-gssapi-common.c:98 fe-secure-common.c:98
+msgid "host name must be specified\n"
+msgstr "le nom d'hôte doit être précisé\n"
+
+#: fe-auth.c:165
+msgid "duplicate GSS authentication request\n"
+msgstr "requête d'authentification GSS dupliquée\n"
+
+#: fe-auth.c:230
+#, c-format
+msgid "out of memory allocating SSPI buffer (%d)\n"
+msgstr "mémoire épuisée lors de l'allocation du tampon SSPI (%d)\n"
+
+#: fe-auth.c:278
+msgid "SSPI continuation error"
+msgstr "erreur de suite SSPI"
+
+#: fe-auth.c:349
+msgid "duplicate SSPI authentication request\n"
+msgstr "requête d'authentification SSPI dupliquée\n"
+
+#: fe-auth.c:374
+msgid "could not acquire SSPI credentials"
+msgstr "n'a pas pu récupérer les pièces d'identité SSPI"
+
+#: fe-auth.c:429
+msgid "channel binding required, but SSL not in use\n"
+msgstr "lien de canal requis, mais SSL non utilisé\n"
+
+#: fe-auth.c:436
+msgid "duplicate SASL authentication request\n"
+msgstr "requête d'authentification SASL dupliquée\n"
+
+#: fe-auth.c:492
+msgid "channel binding is required, but client does not support it\n"
+msgstr "le lien de canal SCRAM est requis mais le client ne supporte par cette option\n"
+
+#: fe-auth.c:509
+msgid "server offered SCRAM-SHA-256-PLUS authentication over a non-SSL connection\n"
+msgstr "le serveur a proposé une authentification SCRAM-SHA-256-PLUS sur une connexion non SSL\n"
+
+#: fe-auth.c:521
+msgid "none of the server's SASL authentication mechanisms are supported\n"
+msgstr ""
+"authentification Kerberos 4 non supportée\n"
+"aucun des mécanismes d'authentification SASL du serveur n'est supporté\n"
+
+#: fe-auth.c:529
+msgid "channel binding is required, but server did not offer an authentication method that supports channel binding\n"
+msgstr "Lien de canal requis, mais le serveur ne propose pas de méthode d'authentification le supportant\n"
+
+#: fe-auth.c:635
+#, c-format
+msgid "out of memory allocating SASL buffer (%d)\n"
+msgstr "mémoire épuisée lors de l'allocation du tampon SASL (%d)\n"
+
+#: fe-auth.c:660
+msgid "AuthenticationSASLFinal received from server, but SASL authentication was not completed\n"
+msgstr "AuthenticationSASLFinal reçu du serveur mais l'authentification SASL n'a pas été terminée\n"
+
+#: fe-auth.c:737
+msgid "SCM_CRED authentication method not supported\n"
+msgstr "authentification SCM_CRED non supportée\n"
+
+#: fe-auth.c:836
+msgid "channel binding required, but server authenticated client without channel binding\n"
+msgstr "lien de canal requis, mais le serveur a authentifié le client sans lien de canal\n"
+
+#: fe-auth.c:842
+msgid "channel binding required but not supported by server's authentication request\n"
+msgstr "lien de canal requis, mais non supporté par la requête d'authentification du serveur\n"
+
+#: fe-auth.c:875
+msgid "Kerberos 4 authentication not supported\n"
+msgstr "authentification Kerberos 4 non supportée\n"
+
+#: fe-auth.c:880
+msgid "Kerberos 5 authentication not supported\n"
+msgstr "authentification Kerberos 5 non supportée\n"
+
+#: fe-auth.c:951
+msgid "GSSAPI authentication not supported\n"
+msgstr "authentification GSSAPI non supportée\n"
+
+#: fe-auth.c:983
+msgid "SSPI authentication not supported\n"
+msgstr "authentification SSPI non supportée\n"
+
+#: fe-auth.c:991
+msgid "Crypt authentication not supported\n"
+msgstr "authentification crypt non supportée\n"
+
+#: fe-auth.c:1057
+#, c-format
+msgid "authentication method %u not supported\n"
+msgstr "méthode d'authentification %u non supportée\n"
+
+#: fe-auth.c:1104
+#, c-format
+msgid "user name lookup failure: error code %lu\n"
+msgstr "échec de la recherche du nom d'utilisateur : code erreur %lu\n"
+
+#: fe-auth.c:1114 fe-connect.c:2834
+#, c-format
+msgid "could not look up local user ID %d: %s\n"
+msgstr "n'a pas pu rechercher l'identifiant de l'utilisateur local %d : %s\n"
+
+#: fe-auth.c:1119 fe-connect.c:2839
+#, c-format
+msgid "local user with ID %d does not exist\n"
+msgstr "l'utilisateur local dont l'identifiant est %d n'existe pas\n"
+
+#: fe-auth.c:1221
+msgid "unexpected shape of result set returned for SHOW\n"
+msgstr "forme du résultat inattendu pour SHOW\n"
+
+#: fe-auth.c:1230
+msgid "password_encryption value too long\n"
+msgstr "la valeur de password_encryption est trop longue\n"
+
+#: fe-auth.c:1270
+#, c-format
+msgid "unrecognized password encryption algorithm \"%s\"\n"
+msgstr "algorithme de chiffrement du mot de passe « %s » non reconnu\n"
+
+#: fe-connect.c:1075
+#, c-format
+msgid "could not match %d host names to %d hostaddr values\n"
+msgstr "n'a pas pu faire correspondre les %d noms d'hôte aux %d valeurs hostaddr\n"
+
+#: fe-connect.c:1156
+#, c-format
+msgid "could not match %d port numbers to %d hosts\n"
+msgstr "n'a pas pu faire correspondre les %d numéros de port aux %d hôtes\n"
+
+#: fe-connect.c:1249
+#, c-format
+msgid "invalid channel_binding value: \"%s\"\n"
+msgstr "valeur de channel_binding invalide : « %s »\n"
+
+#: fe-connect.c:1275
+#, c-format
+msgid "invalid sslmode value: \"%s\"\n"
+msgstr "valeur sslmode invalide : « %s »\n"
+
+#: fe-connect.c:1296
+#, c-format
+msgid "sslmode value \"%s\" invalid when SSL support is not compiled in\n"
+msgstr "valeur sslmode « %s » invalide si le support SSL n'est pas compilé initialement\n"
+
+#: fe-connect.c:1317
+#, c-format
+msgid "invalid ssl_min_protocol_version value: \"%s\"\n"
+msgstr "valeur ssl_min_protocol_version invalide : « %s »\n"
+
+#: fe-connect.c:1325
+#, c-format
+msgid "invalid ssl_max_protocol_version value: \"%s\"\n"
+msgstr "valeur ssl_max_protocol_version invalide : « %s »\n"
+
+#: fe-connect.c:1342
+msgid "invalid SSL protocol version range\n"
+msgstr "intervalle de version invalide pour le protocole SSL\n"
+
+#: fe-connect.c:1357
+#, c-format
+msgid "invalid gssencmode value: \"%s\"\n"
+msgstr "valeur gssencmode invalide : « %s »\n"
+
+#: fe-connect.c:1366
+#, c-format
+msgid "gssencmode value \"%s\" invalid when GSSAPI support is not compiled in\n"
+msgstr "valeur gssencmode « %s » invalide si le support GSSAPI n'est pas compilé\n"
+
+#: fe-connect.c:1401
+#, c-format
+msgid "invalid target_session_attrs value: \"%s\"\n"
+msgstr "valeur target_session_attrs invalide : « %s »\n"
+
+#: fe-connect.c:1619
+#, c-format
+msgid "could not set socket to TCP no delay mode: %s\n"
+msgstr "n'a pas pu activer le mode TCP sans délai pour la socket : %s\n"
+
+#: fe-connect.c:1680
+#, c-format
+msgid ""
+"could not connect to server: %s\n"
+"\tIs the server running locally and accepting\n"
+"\tconnections on Unix domain socket \"%s\"?\n"
+msgstr ""
+"n'a pas pu se connecter au serveur : %s\n"
+"\tLe serveur est-il actif localement et accepte-t-il les connexions sur la\n"
+" \tsocket Unix « %s » ?\n"
+
+#: fe-connect.c:1717
+#, c-format
+msgid ""
+"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"
+msgstr ""
+"n'a pas pu se connecter au serveur : %s\n"
+"\tLe serveur est-il actif sur l'hôte « %s » (%s)\n"
+"\tet accepte-t-il les connexionsTCP/IP sur le port %s ?\n"
+
+#: fe-connect.c:1725
+#, c-format
+msgid ""
+"could not connect to server: %s\n"
+"\tIs the server running on host \"%s\" and accepting\n"
+"\tTCP/IP connections on port %s?\n"
+msgstr ""
+"n'a pas pu se connecter au serveur : %s\n"
+"\tLe serveur est-il actif sur l'hôte « %s » et accepte-t-il les connexions\n"
+"\tTCP/IP sur le port %s ?\n"
+
+#: fe-connect.c:1795
+#, c-format
+msgid "invalid integer value \"%s\" for connection option \"%s\"\n"
+msgstr "valeur entière « %s » invalide pour l'option de connexion « %s »\n"
+
+#: fe-connect.c:1825 fe-connect.c:1859 fe-connect.c:1894 fe-connect.c:1981
+#: fe-connect.c:2623
+#, c-format
+msgid "setsockopt(%s) failed: %s\n"
+msgstr "setsockopt(%s) a échoué : %s\n"
+
+#: fe-connect.c:1947
+#, c-format
+msgid "WSAIoctl(SIO_KEEPALIVE_VALS) failed: %ui\n"
+msgstr "WSAIoctl(SIO_KEEPALIVE_VALS) a échoué : %ui\n"
+
+#: fe-connect.c:2313
+msgid "invalid connection state, probably indicative of memory corruption\n"
+msgstr "état de connexion invalide, indique probablement une corruption de mémoire\n"
+
+#: fe-connect.c:2379
+#, c-format
+msgid "invalid port number: \"%s\"\n"
+msgstr "numéro de port invalide : « %s »\n"
+
+#: fe-connect.c:2395
+#, c-format
+msgid "could not translate host name \"%s\" to address: %s\n"
+msgstr "n'a pas pu traduire le nom d'hôte « %s » en adresse : %s\n"
+
+#: fe-connect.c:2408
+#, c-format
+msgid "could not parse network address \"%s\": %s\n"
+msgstr "n'a pas pu analyser l'adresse réseau « %s » : %s\n"
+
+#: fe-connect.c:2421
+#, c-format
+msgid "Unix-domain socket path \"%s\" is too long (maximum %d bytes)\n"
+msgstr "Le chemin du socket de domaine Unix, « %s », est trop (maximum %d octets)\n"
+
+#: fe-connect.c:2436
+#, c-format
+msgid "could not translate Unix-domain socket path \"%s\" to address: %s\n"
+msgstr ""
+"n'a pas pu traduire le chemin de la socket du domaine Unix « %s » en adresse :\n"
+"%s\n"
+
+#: fe-connect.c:2560
+#, c-format
+msgid "could not create socket: %s\n"
+msgstr "n'a pas pu créer la socket : %s\n"
+
+#: fe-connect.c:2582
+#, c-format
+msgid "could not set socket to nonblocking mode: %s\n"
+msgstr "n'a pas pu activer le mode non-bloquant pour la socket : %s\n"
+
+#: fe-connect.c:2592
+#, c-format
+msgid "could not set socket to close-on-exec mode: %s\n"
+msgstr "n'a pas pu paramétrer la socket en mode close-on-exec : %s\n"
+
+#: fe-connect.c:2610
+msgid "keepalives parameter must be an integer\n"
+msgstr "le paramètre keepalives doit être un entier\n"
+
+#: fe-connect.c:2750
+#, c-format
+msgid "could not get socket error status: %s\n"
+msgstr "n'a pas pu déterminer le statut d'erreur de la socket : %s\n"
+
+#: fe-connect.c:2778
+#, c-format
+msgid "could not get client address from socket: %s\n"
+msgstr "n'a pas pu obtenir l'adresse du client depuis la socket : %s\n"
+
+#: fe-connect.c:2820
+msgid "requirepeer parameter is not supported on this platform\n"
+msgstr "le paramètre requirepeer n'est pas supporté sur cette plateforme\n"
+
+#: fe-connect.c:2823
+#, c-format
+msgid "could not get peer credentials: %s\n"
+msgstr "n'a pas pu obtenir l'authentification de l'autre : %s\n"
+
+#: fe-connect.c:2847
+#, c-format
+msgid "requirepeer specifies \"%s\", but actual peer user name is \"%s\"\n"
+msgstr "requirepeer indique « %s » mais le nom de l'utilisateur réel est « %s »\n"
+
+#: fe-connect.c:2887
+#, c-format
+msgid "could not send GSSAPI negotiation packet: %s\n"
+msgstr "n'a pas pu transmettre le paquet de négociation GSSAPI : %s\n"
+
+#: fe-connect.c:2899
+msgid "GSSAPI encryption required but was impossible (possibly no credential cache, no server support, or using a local socket)\n"
+msgstr "le chiffrage avec GSSAPI était requis, mais impossible (potentiellement pas de cache, de support serveur ou de socket local)\n"
+
+#: fe-connect.c:2931
+#, c-format
+msgid "could not send SSL negotiation packet: %s\n"
+msgstr "n'a pas pu transmettre le paquet de négociation SSL : %s\n"
+
+#: fe-connect.c:2970
+#, c-format
+msgid "could not send startup packet: %s\n"
+msgstr "n'a pas pu transmettre le paquet de démarrage : %s\n"
+
+#: fe-connect.c:3040
+msgid "server does not support SSL, but SSL was required\n"
+msgstr "le serveur ne supporte pas SSL alors que SSL était réclamé\n"
+
+#: fe-connect.c:3067
+#, c-format
+msgid "received invalid response to SSL negotiation: %c\n"
+msgstr "a reçu une réponse invalide à la négociation SSL : %c\n"
+
+#: fe-connect.c:3156
+msgid "server doesn't support GSSAPI encryption, but it was required\n"
+msgstr "le serveur ne supporte pas le chiffrage GSSAPI alors qu'il était réclamé\n"
+
+#: fe-connect.c:3168
+#, c-format
+msgid "received invalid response to GSSAPI negotiation: %c\n"
+msgstr "a reçu une réponse invalide à la négociation GSSAPI : %c\n"
+
+#: fe-connect.c:3234 fe-connect.c:3265
+#, c-format
+msgid "expected authentication request from server, but received %c\n"
+msgstr ""
+"attendait une requête d'authentification en provenance du serveur, mais a\n"
+" reçu %c\n"
+
+#: fe-connect.c:3506
+msgid "unexpected message from server during startup\n"
+msgstr "message inattendu du serveur lors du démarrage\n"
+
+#: fe-connect.c:3711
+#, c-format
+msgid "could not make a writable connection to server \"%s:%s\"\n"
+msgstr "n'a pas pu réaliser une connexion en écriture au serveur « %s » : %s\n"
+
+#: fe-connect.c:3757
+#, c-format
+msgid "test \"SHOW transaction_read_only\" failed on server \"%s:%s\"\n"
+msgstr "le test \"SHOW transaction_read_only\" a échoué sur le serveur \"%s:%s\"\n"
+
+#: fe-connect.c:3772
+#, c-format
+msgid "invalid connection state %d, probably indicative of memory corruption\n"
+msgstr ""
+"état de connexion invalide (%d), indiquant probablement une corruption de\n"
+" mémoire\n"
+
+#: fe-connect.c:4211 fe-connect.c:4271
+#, c-format
+msgid "PGEventProc \"%s\" failed during PGEVT_CONNRESET event\n"
+msgstr "échec de PGEventProc « %s » lors de l'événement PGEVT_CONNRESET\n"
+
+#: fe-connect.c:4618
+#, c-format
+msgid "invalid LDAP URL \"%s\": scheme must be ldap://\n"
+msgstr "URL LDAP « %s » invalide : le schéma doit être ldap://\n"
+
+#: fe-connect.c:4633
+#, c-format
+msgid "invalid LDAP URL \"%s\": missing distinguished name\n"
+msgstr "URL LDAP « %s » invalide : le « distinguished name » manque\n"
+
+#: fe-connect.c:4645 fe-connect.c:4700
+#, c-format
+msgid "invalid LDAP URL \"%s\": must have exactly one attribute\n"
+msgstr "URL LDAP « %s » invalide : doit avoir exactement un attribut\n"
+
+#: fe-connect.c:4656 fe-connect.c:4715
+#, c-format
+msgid "invalid LDAP URL \"%s\": must have search scope (base/one/sub)\n"
+msgstr "URL LDAP « %s » invalide : doit avoir une échelle de recherche (base/un/sous)\n"
+
+#: fe-connect.c:4667
+#, c-format
+msgid "invalid LDAP URL \"%s\": no filter\n"
+msgstr "URL LDAP « %s » invalide : aucun filtre\n"
+
+#: fe-connect.c:4688
+#, c-format
+msgid "invalid LDAP URL \"%s\": invalid port number\n"
+msgstr "URL LDAP « %s » invalide : numéro de port invalide\n"
+
+#: fe-connect.c:4724
+msgid "could not create LDAP structure\n"
+msgstr "n'a pas pu créer la structure LDAP\n"
+
+#: fe-connect.c:4800
+#, c-format
+msgid "lookup on LDAP server failed: %s\n"
+msgstr "échec de la recherche sur le serveur LDAP : %s\n"
+
+#: fe-connect.c:4811
+msgid "more than one entry found on LDAP lookup\n"
+msgstr "plusieurs entrées trouvées pendant la recherche LDAP\n"
+
+#: fe-connect.c:4812 fe-connect.c:4824
+msgid "no entry found on LDAP lookup\n"
+msgstr "aucune entrée trouvée pendant la recherche LDAP\n"
+
+#: fe-connect.c:4835 fe-connect.c:4848
+msgid "attribute has no values on LDAP lookup\n"
+msgstr "l'attribut n'a pas de valeur après la recherche LDAP\n"
+
+#: fe-connect.c:4900 fe-connect.c:4919 fe-connect.c:5451
+#, c-format
+msgid "missing \"=\" after \"%s\" in connection info string\n"
+msgstr "« = » manquant après « %s » dans la chaîne des paramètres de connexion\n"
+
+#: fe-connect.c:4992 fe-connect.c:5636 fe-connect.c:6410
+#, c-format
+msgid "invalid connection option \"%s\"\n"
+msgstr "option de connexion « %s » invalide\n"
+
+#: fe-connect.c:5008 fe-connect.c:5500
+msgid "unterminated quoted string in connection info string\n"
+msgstr "guillemets non refermés dans la chaîne des paramètres de connexion\n"
+
+#: fe-connect.c:5091
+#, c-format
+msgid "definition of service \"%s\" not found\n"
+msgstr "définition du service « %s » introuvable\n"
+
+#: fe-connect.c:5114
+#, c-format
+msgid "service file \"%s\" not found\n"
+msgstr "fichier de service « %s » introuvable\n"
+
+#: fe-connect.c:5129
+#, c-format
+msgid "line %d too long in service file \"%s\"\n"
+msgstr "ligne %d trop longue dans le fichier service « %s »\n"
+
+#: fe-connect.c:5201 fe-connect.c:5245
+#, c-format
+msgid "syntax error in service file \"%s\", line %d\n"
+msgstr "erreur de syntaxe dans le fichier service « %s », ligne %d\n"
+
+#: fe-connect.c:5212
+#, c-format
+msgid "nested service specifications not supported in service file \"%s\", line %d\n"
+msgstr "spécifications imbriquées de service non supportées dans le fichier service « %s », ligne %d\n"
+
+#: fe-connect.c:5932
+#, c-format
+msgid "invalid URI propagated to internal parser routine: \"%s\"\n"
+msgstr "URI invalide propagée à la routine d'analyse interne : « %s »\n"
+
+#: fe-connect.c:6009
+#, c-format
+msgid "end of string reached when looking for matching \"]\" in IPv6 host address in URI: \"%s\"\n"
+msgstr ""
+"fin de chaîne atteinte lors de la recherche du « ] » correspondant dans\n"
+"l'adresse IPv6 de l'hôte indiquée dans l'URI : « %s »\n"
+
+#: fe-connect.c:6016
+#, c-format
+msgid "IPv6 host address may not be empty in URI: \"%s\"\n"
+msgstr "l'adresse IPv6 de l'hôte ne peut pas être vide dans l'URI : « %s »\n"
+
+#: fe-connect.c:6031
+#, c-format
+msgid "unexpected character \"%c\" at position %d in URI (expected \":\" or \"/\"): \"%s\"\n"
+msgstr ""
+"caractère « %c » inattendu à la position %d de l'URI (caractère « : » ou\n"
+"« / » attendu) : « %s »\n"
+
+#: fe-connect.c:6160
+#, c-format
+msgid "extra key/value separator \"=\" in URI query parameter: \"%s\"\n"
+msgstr "séparateur « = » de clé/valeur en trop dans le paramètre de requête URI : « %s »\n"
+
+#: fe-connect.c:6180
+#, c-format
+msgid "missing key/value separator \"=\" in URI query parameter: \"%s\"\n"
+msgstr "séparateur « = » de clé/valeur manquant dans le paramètre de requête URI : « %s »\n"
+
+#: fe-connect.c:6231
+#, c-format
+msgid "invalid URI query parameter: \"%s\"\n"
+msgstr "paramètre de la requête URI invalide : « %s »\n"
+
+#: fe-connect.c:6305
+#, c-format
+msgid "invalid percent-encoded token: \"%s\"\n"
+msgstr "jeton encodé en pourcentage invalide : « %s »\n"
+
+#: fe-connect.c:6315
+#, c-format
+msgid "forbidden value %%00 in percent-encoded value: \"%s\"\n"
+msgstr "valeur %%00 interdite dans la valeur codée en pourcentage : « %s »\n"
+
+#: fe-connect.c:6678
+msgid "connection pointer is NULL\n"
+msgstr "le pointeur de connexion est NULL\n"
+
+#: fe-connect.c:6974
+#, c-format
+msgid "WARNING: password file \"%s\" is not a plain file\n"
+msgstr "ATTENTION : le fichier de mots de passe « %s » n'est pas un fichier texte\n"
+
+#: fe-connect.c:6983
+#, c-format
+msgid "WARNING: password file \"%s\" has group or world access; permissions should be u=rw (0600) or less\n"
+msgstr ""
+"ATTENTION : le fichier de mots de passe « %s » a des droits d'accès en\n"
+"lecture pour le groupe ou universel ; les droits devraient être u=rw (0600)\n"
+"ou inférieur\n"
+
+#: fe-connect.c:7091
+#, c-format
+msgid "password retrieved from file \"%s\"\n"
+msgstr "mot de passe récupéré dans le fichier « %s »\n"
+
+#: fe-exec.c:444 fe-exec.c:2821
+#, c-format
+msgid "row number %d is out of range 0..%d"
+msgstr "le numéro de ligne %d est en dehors des limites 0..%d"
+
+#: fe-exec.c:505 fe-protocol2.c:497 fe-protocol2.c:532 fe-protocol2.c:1050
+#: fe-protocol3.c:206 fe-protocol3.c:233 fe-protocol3.c:250 fe-protocol3.c:328
+#: fe-protocol3.c:692 fe-protocol3.c:920
+msgid "out of memory"
+msgstr "mémoire épuisée"
+
+#: fe-exec.c:506 fe-protocol2.c:1396 fe-protocol3.c:1873
+#, c-format
+msgid "%s"
+msgstr "%s"
+
+#: fe-exec.c:815
+msgid "write to server failed\n"
+msgstr "échec en écriture vers le serveur\n"
+
+#: fe-exec.c:896
+msgid "NOTICE"
+msgstr "NOTICE"
+
+#: fe-exec.c:954
+msgid "PGresult cannot support more than INT_MAX tuples"
+msgstr "PGresult ne supporte pas plus de INT_MAX lignes"
+
+#: fe-exec.c:966
+msgid "size_t overflow"
+msgstr "saturation de size_t"
+
+#: fe-exec.c:1243 fe-exec.c:1301 fe-exec.c:1347
+msgid "command string is a null pointer\n"
+msgstr "la chaîne de commande est un pointeur nul\n"
+
+#: fe-exec.c:1307 fe-exec.c:1353 fe-exec.c:1448
+msgid "number of parameters must be between 0 and 65535\n"
+msgstr "le nombre de paramètres doit être compris entre 0 et 65535\n"
+
+#: fe-exec.c:1341 fe-exec.c:1442
+msgid "statement name is a null pointer\n"
+msgstr "le nom de l'instruction est un pointeur nul\n"
+
+#: fe-exec.c:1361 fe-exec.c:1524 fe-exec.c:2233 fe-exec.c:2435
+msgid "function requires at least protocol version 3.0\n"
+msgstr "la fonction nécessite au minimum le protocole 3.0\n"
+
+#: fe-exec.c:1479
+msgid "no connection to the server\n"
+msgstr "aucune connexion au serveur\n"
+
+#: fe-exec.c:1486
+msgid "another command is already in progress\n"
+msgstr "une autre commande est déjà en cours\n"
+
+#: fe-exec.c:1600
+msgid "length must be given for binary parameter\n"
+msgstr "la longueur doit être indiquée pour les paramètres binaires\n"
+
+#: fe-exec.c:1863
+#, c-format
+msgid "unexpected asyncStatus: %d\n"
+msgstr "asyncStatus inattendu : %d\n"
+
+#: fe-exec.c:1883
+#, c-format
+msgid "PGEventProc \"%s\" failed during PGEVT_RESULTCREATE event\n"
+msgstr "échec de PGEventProc « %s » lors de l'événement PGEVT_RESULTCREATE\n"
+
+#: fe-exec.c:2043
+msgid "COPY terminated by new PQexec"
+msgstr "COPY terminé par un nouveau PQexec"
+
+#: fe-exec.c:2051
+msgid "COPY IN state must be terminated first\n"
+msgstr "l'état COPY IN doit d'abord être terminé\n"
+
+#: fe-exec.c:2071
+msgid "COPY OUT state must be terminated first\n"
+msgstr "l'état COPY OUT doit d'abord être terminé\n"
+
+#: fe-exec.c:2079
+msgid "PQexec not allowed during COPY BOTH\n"
+msgstr "PQexec non autorisé pendant COPY BOTH\n"
+
+#: fe-exec.c:2325 fe-exec.c:2392 fe-exec.c:2482 fe-protocol2.c:1353
+#: fe-protocol3.c:1804
+msgid "no COPY in progress\n"
+msgstr "aucun COPY en cours\n"
+
+#: fe-exec.c:2672
+msgid "connection in wrong state\n"
+msgstr "connexion dans un état erroné\n"
+
+#: fe-exec.c:2703
+msgid "invalid ExecStatusType code"
+msgstr "code ExecStatusType invalide"
+
+#: fe-exec.c:2730
+msgid "PGresult is not an error result\n"
+msgstr "PGresult n'est pas un résultat d'erreur\n"
+
+#: fe-exec.c:2805 fe-exec.c:2828
+#, c-format
+msgid "column number %d is out of range 0..%d"
+msgstr "le numéro de colonne %d est en dehors des limites 0..%d"
+
+#: fe-exec.c:2843
+#, c-format
+msgid "parameter number %d is out of range 0..%d"
+msgstr "le numéro de paramètre %d est en dehors des limites 0..%d"
+
+#: fe-exec.c:3153
+#, c-format
+msgid "could not interpret result from server: %s"
+msgstr "n'a pas pu interpréter la réponse du serveur : %s"
+
+#: fe-exec.c:3392 fe-exec.c:3476
+msgid "incomplete multibyte character\n"
+msgstr "caractère multi-octet incomplet\n"
+
+#: fe-gssapi-common.c:124
+msgid "GSSAPI name import error"
+msgstr "erreur d'import du nom GSSAPI"
+
+#: fe-lobj.c:154
+msgid "cannot determine OID of function lo_truncate\n"
+msgstr "ne peut pas déterminer l'OID de la fonction lo_truncate\n"
+
+#: fe-lobj.c:170
+msgid "argument of lo_truncate exceeds integer range\n"
+msgstr "l'argument de lo_truncate dépasse l'échelle des entiers\n"
+
+#: fe-lobj.c:221
+msgid "cannot determine OID of function lo_truncate64\n"
+msgstr "ne peut pas déterminer l'OID de la fonction lo_truncate64\n"
+
+#: fe-lobj.c:279
+msgid "argument of lo_read exceeds integer range\n"
+msgstr "l'argument de lo_read dépasse l'échelle des entiers\n"
+
+#: fe-lobj.c:334
+msgid "argument of lo_write exceeds integer range\n"
+msgstr "l'argument de lo_write dépasse l'échelle des entiers\n"
+
+#: fe-lobj.c:425
+msgid "cannot determine OID of function lo_lseek64\n"
+msgstr "ne peut pas déterminer l'OID de la fonction lo_lseek64\n"
+
+#: fe-lobj.c:521
+msgid "cannot determine OID of function lo_create\n"
+msgstr "ne peut pas déterminer l'OID de la fonction lo_create\n"
+
+#: fe-lobj.c:600
+msgid "cannot determine OID of function lo_tell64\n"
+msgstr "ne peut pas déterminer l'OID de la fonction lo_tell64\n"
+
+#: fe-lobj.c:706 fe-lobj.c:815
+#, c-format
+msgid "could not open file \"%s\": %s\n"
+msgstr "n'a pas pu ouvrir le fichier « %s » : %s\n"
+
+#: fe-lobj.c:761
+#, c-format
+msgid "could not read from file \"%s\": %s\n"
+msgstr "n'a pas pu lire le fichier « %s » : %s\n"
+
+#: fe-lobj.c:835 fe-lobj.c:859
+#, c-format
+msgid "could not write to file \"%s\": %s\n"
+msgstr "n'a pas pu écrire dans le fichier « %s » : %s\n"
+
+#: fe-lobj.c:946
+msgid "query to initialize large object functions did not return data\n"
+msgstr ""
+"la requête d'initialisation des fonctions pour « Larges Objects » ne renvoie\n"
+"pas de données\n"
+
+#: fe-lobj.c:995
+msgid "cannot determine OID of function lo_open\n"
+msgstr "ne peut pas déterminer l'OID de la fonction lo_open\n"
+
+#: fe-lobj.c:1002
+msgid "cannot determine OID of function lo_close\n"
+msgstr "ne peut pas déterminer l'OID de la fonction lo_close\n"
+
+#: fe-lobj.c:1009
+msgid "cannot determine OID of function lo_creat\n"
+msgstr "ne peut pas déterminer l'OID de la fonction lo_creat\n"
+
+#: fe-lobj.c:1016
+msgid "cannot determine OID of function lo_unlink\n"
+msgstr "ne peut pas déterminer l'OID de la fonction lo_unlink\n"
+
+#: fe-lobj.c:1023
+msgid "cannot determine OID of function lo_lseek\n"
+msgstr "ne peut pas déterminer l'OID de la fonction lo_lseek\n"
+
+#: fe-lobj.c:1030
+msgid "cannot determine OID of function lo_tell\n"
+msgstr "ne peut pas déterminer l'OID de la fonction lo_tell\n"
+
+#: fe-lobj.c:1037
+msgid "cannot determine OID of function loread\n"
+msgstr "ne peut pas déterminer l'OID de la fonction loread\n"
+
+#: fe-lobj.c:1044
+msgid "cannot determine OID of function lowrite\n"
+msgstr "ne peut pas déterminer l'OID de la fonction lowrite\n"
+
+#: fe-misc.c:289
+#, c-format
+msgid "integer of size %lu not supported by pqGetInt"
+msgstr "entier de taille %lu non supporté par pqGetInt"
+
+#: fe-misc.c:325
+#, c-format
+msgid "integer of size %lu not supported by pqPutInt"
+msgstr "entier de taille %lu non supporté par pqPutInt"
+
+#: fe-misc.c:636 fe-misc.c:869
+msgid "connection not open\n"
+msgstr "la connexion n'est pas active\n"
+
+#: fe-misc.c:805 fe-secure-openssl.c:209 fe-secure-openssl.c:316
+#: fe-secure.c:267 fe-secure.c:383
+msgid ""
+"server closed the connection unexpectedly\n"
+"\tThis probably means the server terminated abnormally\n"
+"\tbefore or while processing the request.\n"
+msgstr ""
+"la connexion au serveur a été coupée de façon inattendue\n"
+"\tLe serveur s'est peut-être arrêté anormalement avant ou durant le\n"
+"\ttraitement de la requête.\n"
+
+#: fe-misc.c:1063
+msgid "timeout expired\n"
+msgstr "le délai est dépassé\n"
+
+#: fe-misc.c:1108
+msgid "invalid socket\n"
+msgstr "socket invalide\n"
+
+#: fe-misc.c:1131
+#, c-format
+msgid "select() failed: %s\n"
+msgstr "échec de select() : %s\n"
+
+#: fe-protocol2.c:87
+#, c-format
+msgid "invalid setenv state %c, probably indicative of memory corruption\n"
+msgstr "état setenv %c invalide, indiquant probablement une corruption de la mémoire\n"
+
+#: fe-protocol2.c:384
+#, c-format
+msgid "invalid state %c, probably indicative of memory corruption\n"
+msgstr "état %c invalide, indiquant probablement une corruption de la mémoire\n"
+
+#: fe-protocol2.c:473 fe-protocol3.c:183
+#, c-format
+msgid "message type 0x%02x arrived from server while idle"
+msgstr "le message de type 0x%02x est arrivé alors que le serveur était en attente"
+
+#: fe-protocol2.c:523
+#, c-format
+msgid "unexpected character %c following empty query response (\"I\" message)"
+msgstr ""
+"caractère %c inattendu à la suite d'une réponse de requête vide (message\n"
+"« I »)"
+
+#: fe-protocol2.c:589
+#, c-format
+msgid "server sent data (\"D\" message) without prior row description (\"T\" message)"
+msgstr ""
+"le serveur a envoyé des données (message « D ») sans description préalable\n"
+"de la ligne (message « T »)"
+
+#: fe-protocol2.c:607
+#, c-format
+msgid "server sent binary data (\"B\" message) without prior row description (\"T\" message)"
+msgstr ""
+"le serveur a envoyé des données binaires (message « B ») sans description\n"
+"préalable de la ligne (message « T »)"
+
+#: fe-protocol2.c:626 fe-protocol3.c:403
+#, c-format
+msgid "unexpected response from server; first received character was \"%c\"\n"
+msgstr "réponse inattendue du serveur, le premier caractère reçu étant « %c »\n"
+
+#: fe-protocol2.c:755 fe-protocol2.c:930 fe-protocol3.c:603 fe-protocol3.c:809
+msgid "out of memory for query result"
+msgstr "mémoire épuisée pour le résultat de la requête"
+
+#: fe-protocol2.c:1408
+#, c-format
+msgid "lost synchronization with server, resetting connection"
+msgstr "synchronisation perdue avec le serveur, réinitialisation de la connexion"
+
+#: fe-protocol2.c:1530 fe-protocol2.c:1562 fe-protocol3.c:2061
+#, c-format
+msgid "protocol error: id=0x%x\n"
+msgstr "erreur de protocole : id=0x%x\n"
+
+#: fe-protocol3.c:360
+msgid "server sent data (\"D\" message) without prior row description (\"T\" message)\n"
+msgstr ""
+"le serveur a envoyé des données (message « D ») sans description préalable\n"
+"de la ligne (message « T »)\n"
+
+#: fe-protocol3.c:424
+#, c-format
+msgid "message contents do not agree with length in message type \"%c\"\n"
+msgstr ""
+"le contenu du message ne correspond pas avec la longueur du type de message\n"
+"« %c »\n"
+
+#: fe-protocol3.c:444
+#, c-format
+msgid "lost synchronization with server: got message type \"%c\", length %d\n"
+msgstr ""
+"synchronisation perdue avec le serveur : a reçu le type de message « %c »,\n"
+"longueur %d\n"
+
+#: fe-protocol3.c:494 fe-protocol3.c:534
+msgid "insufficient data in \"T\" message"
+msgstr "données insuffisantes dans le message « T »"
+
+#: fe-protocol3.c:672
+msgid "insufficient data in \"t\" message"
+msgstr "données insuffisantes dans le message « t »"
+
+#: fe-protocol3.c:731 fe-protocol3.c:763 fe-protocol3.c:781
+msgid "insufficient data in \"D\" message"
+msgstr "données insuffisantes dans le message « D »"
+
+#: fe-protocol3.c:737
+msgid "unexpected field count in \"D\" message"
+msgstr "nombre de champs inattendu dans le message « D »"
+
+#: fe-protocol3.c:974
+msgid "no error message available\n"
+msgstr "aucun message d'erreur disponible\n"
+
+#. translator: %s represents a digit string
+#: fe-protocol3.c:1022 fe-protocol3.c:1041
+#, c-format
+msgid " at character %s"
+msgstr " au caractère %s"
+
+#: fe-protocol3.c:1054
+#, c-format
+msgid "DETAIL: %s\n"
+msgstr "DÉTAIL : %s\n"
+
+#: fe-protocol3.c:1057
+#, c-format
+msgid "HINT: %s\n"
+msgstr "ASTUCE : %s\n"
+
+#: fe-protocol3.c:1060
+#, c-format
+msgid "QUERY: %s\n"
+msgstr "REQUÊTE : %s\n"
+
+#: fe-protocol3.c:1067
+#, c-format
+msgid "CONTEXT: %s\n"
+msgstr "CONTEXTE : %s\n"
+
+#: fe-protocol3.c:1076
+#, c-format
+msgid "SCHEMA NAME: %s\n"
+msgstr "NOM DE SCHÉMA : %s\n"
+
+#: fe-protocol3.c:1080
+#, c-format
+msgid "TABLE NAME: %s\n"
+msgstr "NOM DE TABLE : %s\n"
+
+#: fe-protocol3.c:1084
+#, c-format
+msgid "COLUMN NAME: %s\n"
+msgstr "NOM DE COLONNE : %s\n"
+
+#: fe-protocol3.c:1088
+#, c-format
+msgid "DATATYPE NAME: %s\n"
+msgstr "NOM DU TYPE DE DONNÉES : %s\n"
+
+#: fe-protocol3.c:1092
+#, c-format
+msgid "CONSTRAINT NAME: %s\n"
+msgstr "NOM DE CONTRAINTE : %s\n"
+
+#: fe-protocol3.c:1104
+msgid "LOCATION: "
+msgstr "EMPLACEMENT : "
+
+#: fe-protocol3.c:1106
+#, c-format
+msgid "%s, "
+msgstr "%s, "
+
+#: fe-protocol3.c:1108
+#, c-format
+msgid "%s:%s"
+msgstr "%s : %s"
+
+#: fe-protocol3.c:1303
+#, c-format
+msgid "LINE %d: "
+msgstr "LIGNE %d : "
+
+#: fe-protocol3.c:1698
+msgid "PQgetline: not doing text COPY OUT\n"
+msgstr "PQgetline : ne va pas réaliser un COPY OUT au format texte\n"
+
+#: fe-secure-common.c:124
+msgid "SSL certificate's name contains embedded null\n"
+msgstr "le nom du certificat SSL contient des NULL\n"
+
+#: fe-secure-common.c:171
+msgid "host name must be specified for a verified SSL connection\n"
+msgstr "le nom d'hôte doit être précisé pour une connexion SSL vérifiée\n"
+
+#: fe-secure-common.c:196
+#, c-format
+msgid "server certificate for \"%s\" does not match host name \"%s\"\n"
+msgstr "le certificat serveur pour « %s » ne correspond pas au nom d'hôte « %s »\n"
+
+#: fe-secure-common.c:202
+msgid "could not get server's host name from server certificate\n"
+msgstr "n'a pas pu récupérer le nom d'hôte du serveur à partir du certificat serveur\n"
+
+#: fe-secure-gssapi.c:201
+msgid "GSSAPI wrap error"
+msgstr "erreur wrap GSSAPI"
+
+#: fe-secure-gssapi.c:209
+msgid "outgoing GSSAPI message would not use confidentiality\n"
+msgstr "le message sortant GSSAPI n'utilisera pas la confidentialité\n"
+
+#: fe-secure-gssapi.c:217
+#, c-format
+msgid "client tried to send oversize GSSAPI packet (%zu > %zu)\n"
+msgstr "le client a essayé d'envoyer un paquet GSSAPI trop gros (%zu > %zu)\n"
+
+#: fe-secure-gssapi.c:354 fe-secure-gssapi.c:596
+#, c-format
+msgid "oversize GSSAPI packet sent by the server (%zu > %zu)\n"
+msgstr "paquet GSSAPI trop gros envoyé par le serveur (%zu > %zu)\n"
+
+#: fe-secure-gssapi.c:393
+msgid "GSSAPI unwrap error"
+msgstr "erreur unwrap GSSAPI"
+
+#: fe-secure-gssapi.c:403
+msgid "incoming GSSAPI message did not use confidentiality\n"
+msgstr "le message entrant GSSAPI n'utilisait pas la confidentialité\n"
+
+#: fe-secure-gssapi.c:642
+msgid "could not initiate GSSAPI security context"
+msgstr "n'a pas pu initier le contexte de sécurité GSSAPI"
+
+#: fe-secure-gssapi.c:670
+msgid "GSSAPI size check error"
+msgstr "erreur de vérification de la taille GSSAPI"
+
+#: fe-secure-gssapi.c:681
+msgid "GSSAPI context establishment error"
+msgstr "erreur d'établissement du contexte GSSAPI"
+
+#: fe-secure-openssl.c:214 fe-secure-openssl.c:321 fe-secure-openssl.c:1291
+#, c-format
+msgid "SSL SYSCALL error: %s\n"
+msgstr "erreur SYSCALL SSL : %s\n"
+
+#: fe-secure-openssl.c:221 fe-secure-openssl.c:328 fe-secure-openssl.c:1295
+msgid "SSL SYSCALL error: EOF detected\n"
+msgstr "erreur SYSCALL SSL : EOF détecté\n"
+
+#: fe-secure-openssl.c:232 fe-secure-openssl.c:339 fe-secure-openssl.c:1304
+#, c-format
+msgid "SSL error: %s\n"
+msgstr "erreur SSL : %s\n"
+
+#: fe-secure-openssl.c:247 fe-secure-openssl.c:354
+msgid "SSL connection has been closed unexpectedly\n"
+msgstr "la connexion SSL a été fermée de façon inattendu\n"
+
+#: fe-secure-openssl.c:253 fe-secure-openssl.c:360 fe-secure-openssl.c:1354
+#, c-format
+msgid "unrecognized SSL error code: %d\n"
+msgstr "code d'erreur SSL inconnu : %d\n"
+
+#: fe-secure-openssl.c:400
+msgid "could not determine server certificate signature algorithm\n"
+msgstr "n'a pas pu déterminer l'algorithme de signature du certificat serveur\n"
+
+#: fe-secure-openssl.c:421
+#, c-format
+msgid "could not find digest for NID %s\n"
+msgstr "n'a pas pu trouver l'entrée pour le NID %s\n"
+
+#: fe-secure-openssl.c:431
+msgid "could not generate peer certificate hash\n"
+msgstr "n'a pas pu générer le hachage du certificat peer\n"
+
+#: fe-secure-openssl.c:488
+msgid "SSL certificate's name entry is missing\n"
+msgstr "l'entrée du nom du certificat SSL est manquante\n"
+
+#: fe-secure-openssl.c:815
+#, c-format
+msgid "could not create SSL context: %s\n"
+msgstr "n'a pas pu créer le contexte SSL : %s\n"
+
+#: fe-secure-openssl.c:854
+#, c-format
+msgid "invalid value \"%s\" for minimum SSL protocol version\n"
+msgstr "valeur « %s » invalide pour la version minimale du protocole SSL\n"
+
+#: fe-secure-openssl.c:865
+#, c-format
+msgid "could not set minimum SSL protocol version: %s\n"
+msgstr "n'a pas pu configurer la version minimale de protocole SSL : %s\n"
+
+#: fe-secure-openssl.c:883
+#, c-format
+msgid "invalid value \"%s\" for maximum SSL protocol version\n"
+msgstr "valeur « %s » invalide pour la version maximale du protocole SSL\n"
+
+#: fe-secure-openssl.c:894
+#, c-format
+msgid "could not set maximum SSL protocol version: %s\n"
+msgstr "n'a pas pu configurer la version maximale de protocole SSL : %s\n"
+
+#: fe-secure-openssl.c:930
+#, c-format
+msgid "could not read root certificate file \"%s\": %s\n"
+msgstr "n'a pas pu lire le certificat racine « %s » : %s\n"
+
+#: fe-secure-openssl.c:974
+msgid ""
+"could not get home directory to locate root certificate file\n"
+"Either provide the file or change sslmode to disable server certificate verification.\n"
+msgstr ""
+"n'a pas pu obtenir le répertoire personnel pour situer le fichier de certificat racine.\n"
+"Fournissez le fichier ou modifiez sslmode pour désactiver la vérification du\n"
+"certificat par le serveur.\n"
+
+#: fe-secure-openssl.c:978
+#, c-format
+msgid ""
+"root certificate file \"%s\" does not exist\n"
+"Either provide the file or change sslmode to disable server certificate verification.\n"
+msgstr ""
+"le fichier de certificat racine « %s » n'existe pas.\n"
+"Fournissez le fichier ou modifiez sslmode pour désactiver la vérification du\n"
+"certificat par le serveur.\n"
+
+#: fe-secure-openssl.c:1009
+#, c-format
+msgid "could not open certificate file \"%s\": %s\n"
+msgstr "n'a pas pu ouvrir le certificat « %s » : %s\n"
+
+#: fe-secure-openssl.c:1028
+#, c-format
+msgid "could not read certificate file \"%s\": %s\n"
+msgstr "n'a pas pu lire le certificat « %s » : %s\n"
+
+#: fe-secure-openssl.c:1053
+#, c-format
+msgid "could not establish SSL connection: %s\n"
+msgstr "n'a pas pu établir la connexion SSL : %s\n"
+
+#: fe-secure-openssl.c:1107
+#, c-format
+msgid "could not load SSL engine \"%s\": %s\n"
+msgstr "n'a pas pu charger le moteur SSL « %s » : %s\n"
+
+#: fe-secure-openssl.c:1119
+#, c-format
+msgid "could not initialize SSL engine \"%s\": %s\n"
+msgstr "n'a pas pu initialiser le moteur SSL « %s » : %s\n"
+
+#: fe-secure-openssl.c:1135
+#, c-format
+msgid "could not read private SSL key \"%s\" from engine \"%s\": %s\n"
+msgstr "n'a pas pu lire la clé privée SSL « %s » à partir du moteur « %s » : %s\n"
+
+#: fe-secure-openssl.c:1149
+#, c-format
+msgid "could not load private SSL key \"%s\" from engine \"%s\": %s\n"
+msgstr "n'a pas pu charger la clé privée SSL « %s » à partir du moteur « %s » : %s\n"
+
+#: fe-secure-openssl.c:1186
+#, c-format
+msgid "certificate present, but not private key file \"%s\"\n"
+msgstr "le certificat est présent, mais la clé privée « %s » est absente\n"
+
+#: fe-secure-openssl.c:1194
+#, c-format
+msgid "private key file \"%s\" has group or world access; permissions should be u=rw (0600) or less\n"
+msgstr ""
+"le fichier de la clé privée « %s » a des droits d'accès en lecture\n"
+"pour le groupe ou universel ; les droits devraient être u=rw (0600)\n"
+"ou inférieur\n"
+
+#: fe-secure-openssl.c:1219
+#, c-format
+msgid "could not load private key file \"%s\": %s\n"
+msgstr "n'a pas pu charger le fichier de clé privée « %s » : %s\n"
+
+#: fe-secure-openssl.c:1237
+#, c-format
+msgid "certificate does not match private key file \"%s\": %s\n"
+msgstr "le certificat ne correspond pas à la clé privée « %s » : %s\n"
+
+#: fe-secure-openssl.c:1337
+#, c-format
+msgid "This may indicate that the server does not support any SSL protocol version between %s and %s.\n"
+msgstr "Ceci pourrait indiquer que le serveur ne supporte aucune des versions du protocole SSL entre %s et %s.\n"
+
+#: fe-secure-openssl.c:1373
+#, c-format
+msgid "certificate could not be obtained: %s\n"
+msgstr "le certificat n'a pas pu être obtenu : %s\n"
+
+#: fe-secure-openssl.c:1462
+#, c-format
+msgid "no SSL error reported"
+msgstr "aucune erreur SSL reportée"
+
+#: fe-secure-openssl.c:1471
+#, c-format
+msgid "SSL error code %lu"
+msgstr "erreur SSL %lu"
+
+#: fe-secure-openssl.c:1718
+#, c-format
+msgid "WARNING: sslpassword truncated\n"
+msgstr "ATTENTION : sslpassword tronqué\n"
+
+#: fe-secure.c:275
+#, c-format
+msgid "could not receive data from server: %s\n"
+msgstr "n'a pas pu recevoir des données depuis le serveur : %s\n"
+
+#: fe-secure.c:390
+#, c-format
+msgid "could not send data to server: %s\n"
+msgstr "n'a pas pu transmettre les données au serveur : %s\n"
+
+#: win32.c:314
+#, c-format
+msgid "unrecognized socket error: 0x%08X/%d"
+msgstr "erreur de socket non reconnue : 0x%08X/%d"
+
+#~ msgid "WARNING: line %d too long in password file \"%s\"\n"
+#~ msgstr "ATTENTION : ligne %d trop longue dans le fichier de mots de passe « %s »\n"
+
+#~ msgid "could not set minimum version of SSL protocol: %s\n"
+#~ msgstr "n'a pas pu mettre en place la version minimale du protocole SSL : %s\n"
+
+#~ msgid "could not set maximum version of SSL protocol: %s\n"
+#~ msgstr "n'a pas pu mettre en place la version maximale du protocole SSL : %s\n"
+
+#~ msgid "SSL library does not support CRL certificates (file \"%s\")\n"
+#~ msgstr "la bibliothèque SSL ne supporte pas les certificats CRL (fichier « %s »)\n"
+
+#~ msgid "could not get home directory to locate password file\n"
+#~ msgstr ""
+#~ "n'a pas pu obtenir le répertoire personnel pour trouver le fichier de\n"
+#~ "mot de passe\n"
+
+#~ msgid "could not get home directory to locate service definition file"
+#~ msgstr ""
+#~ "n'a pas pu obtenir le répertoire personnel pour trouver le certificat de\n"
+#~ "définition du service"
+
+#~ msgid "setsockopt(TCP_KEEPIDLE) failed: %s\n"
+#~ msgstr "setsockopt(TCP_KEEPIDLE) a échoué : %s\n"
+
+#~ msgid "setsockopt(TCP_KEEPALIVE) failed: %s\n"
+#~ msgstr "setsockopt(TCP_KEEPALIVE) a échoué : %s\n"
+
+#~ msgid "setsockopt(TCP_KEEPINTVL) failed: %s\n"
+#~ msgstr "setsockopt(TCP_KEEPINTVL) a échoué : %s\n"
+
+#~ msgid "setsockopt(SO_KEEPALIVE) failed: %s\n"
+#~ msgstr "setsockopt(SO_KEEPALIVE) a échoué : %s\n"
+
+#~ msgid "could not acquire mutex: %s\n"
+#~ msgstr "n'a pas pu acquérir le mutex : %s\n"
+
+#~ msgid "unrecognized return value from row processor"
+#~ msgstr "valeur de retour du traitement de la ligne non reconnue"
+
+#~ msgid "invalid appname state %d, probably indicative of memory corruption\n"
+#~ msgstr "état appname %d invalide, indiquant probablement une corruption de la mémoire\n"
+
+#~ msgid "could not read private key file \"%s\": %s\n"
+#~ msgstr "n'a pas pu lire la clé privée « %s » : %s\n"
+
+#~ msgid "private key file \"%s\" changed during execution\n"
+#~ msgstr "la clé privée « %s » a été modifiée durant l'exécution\n"
+
+#~ msgid "could not open private key file \"%s\": %s\n"
+#~ msgstr "n'a pas pu ouvrir le fichier de clé privée « %s » : %s\n"
+
+#~ msgid "verified SSL connections are only supported when connecting to a host name\n"
+#~ msgstr ""
+#~ "les connexions SSL vérifiées ne sont supportées que lors de la connexion\n"
+#~ "à un alias hôte\n"
+
+#~ msgid "could not get home directory to locate client certificate files\n"
+#~ msgstr ""
+#~ "n'a pas pu récupérer le répertoire personnel pour trouver les certificats\n"
+#~ "du client\n"
+
+#~ msgid "could not restore nonblocking mode on socket: %s\n"
+#~ msgstr "n'a pas pu rétablir le mode non-bloquant pour la socket : %s\n"
+
+#~ msgid "Kerberos 5 authentication rejected: %*s\n"
+#~ msgstr "authentification Kerberos 5 rejetée : %*s\n"
+
+#~ msgid "could not set socket to blocking mode: %s\n"
+#~ msgstr "n'a pas pu activer le mode bloquant pour la socket : %s\n"
+
+#~ msgid "socket not open\n"
+#~ msgstr "socket non ouvert\n"
+
+#~ msgid "failed to generate nonce\n"
+#~ msgstr "échec pour la génération de nonce\n"
+
+#~ msgid "no GSSAPI support; cannot require GSSAPI\n"
+#~ msgstr "pas de support de GSSAPI : ne peut pas nécessiter GSSAPI\n"
+
+#~ msgid "extraneous data in \"D\" message"
+#~ msgstr "données supplémentaires dans le message « D »"
+
+#~ msgid "extraneous data in \"t\" message"
+#~ msgstr "données supplémentaires dans le message « t »"
+
+#~ msgid "extraneous data in \"T\" message"
+#~ msgstr "données supplémentaires dans le message « T »"
diff --git a/src/interfaces/libpq/po/it.po b/src/interfaces/libpq/po/it.po
new file mode 100644
index 0000000..737f35b
--- /dev/null
+++ b/src/interfaces/libpq/po/it.po
@@ -0,0 +1,1148 @@
+#
+# libpq.po
+# Italian message translation file for libpq
+#
+# For development and bug report please use:
+# https://github.com/dvarrazzo/postgresql-it
+#
+# Copyright (C) 2012-2017 PostgreSQL Global Development Group
+# Copyright (C) 2010, Associazione Culturale ITPUG
+#
+# Daniele Varrazzo <daniele.varrazzo@gmail.com>, 2012-2017
+# Maurizio Totti <maurizio.totti@gmail.com>, 2010
+# Fabrizio Mazzoni <veramente@libero.it>, 2003.
+# Gaetano Mendola <mendola@bigfoot.com>, 2003.
+#
+# This file is distributed under the same license as the PostgreSQL package.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: libpq (PostgreSQL) 11\n"
+"Report-Msgid-Bugs-To: pgsql-bugs@postgresql.org\n"
+"POT-Creation-Date: 2018-10-08 14:08+0000\n"
+"PO-Revision-Date: 2018-10-08 21:59+0100\n"
+"Last-Translator: Daniele Varrazzo <daniele.varrazzo@gmail.com>\n"
+"Language-Team: https://github.com/dvarrazzo/postgresql-it\n"
+"Language: it\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
+"X-Poedit-SourceCharset: UTF-8\n"
+"X-Generator: Poedit 2.0.6\n"
+
+#: fe-auth-scram.c:189
+msgid "malformed SCRAM message (empty message)\n"
+msgstr "messaggio SCRAM malformato (messaggio vuoto)\n"
+
+#: fe-auth-scram.c:195
+msgid "malformed SCRAM message (length mismatch)\n"
+msgstr "messaggio SCRAM malformato (lunghezza errata)\n"
+
+#: fe-auth-scram.c:244
+msgid "incorrect server signature\n"
+msgstr "firma del server non corretta\n"
+
+#: fe-auth-scram.c:253
+msgid "invalid SCRAM exchange state\n"
+msgstr "stato di scambio SCRAM non valido\n"
+
+#: fe-auth-scram.c:276
+#, c-format
+msgid "malformed SCRAM message (attribute \"%c\" expected)\n"
+msgstr "messaggio SCRAM malformato (atteso attributo \"%c\")\n"
+
+#: fe-auth-scram.c:285
+#, c-format
+msgid "malformed SCRAM message (expected character \"=\" for attribute \"%c\")\n"
+msgstr "messaggio SCRAM malformato (atteso carattere \"=\" per l'attributo \"%c\")\n"
+
+#: fe-auth-scram.c:326
+msgid "could not generate nonce\n"
+msgstr "generazione del nonce fallita\n"
+
+#: fe-auth-scram.c:334 fe-auth-scram.c:401 fe-auth-scram.c:523
+#: fe-auth-scram.c:543 fe-auth-scram.c:569 fe-auth-scram.c:583
+#: fe-auth-scram.c:625 fe-auth.c:227 fe-auth.c:362 fe-auth.c:432 fe-auth.c:467
+#: fe-auth.c:643 fe-auth.c:802 fe-auth.c:1114 fe-auth.c:1262 fe-connect.c:835
+#: fe-connect.c:1264 fe-connect.c:1440 fe-connect.c:1922 fe-connect.c:1945
+#: fe-connect.c:2606 fe-connect.c:4152 fe-connect.c:4404 fe-connect.c:4523
+#: fe-connect.c:4773 fe-connect.c:4853 fe-connect.c:4952 fe-connect.c:5208
+#: fe-connect.c:5237 fe-connect.c:5309 fe-connect.c:5333 fe-connect.c:5351
+#: fe-connect.c:5452 fe-connect.c:5461 fe-connect.c:5817 fe-connect.c:5967
+#: fe-exec.c:2702 fe-exec.c:3449 fe-exec.c:3614 fe-lobj.c:895
+#: fe-protocol2.c:1213 fe-protocol3.c:999 fe-protocol3.c:1685
+#: fe-secure-common.c:110 fe-secure-openssl.c:438 fe-secure-openssl.c:1025
+msgid "out of memory\n"
+msgstr "memoria esaurita\n"
+
+#: fe-auth-scram.c:561
+msgid "invalid SCRAM response (nonce mismatch)\n"
+msgstr "risposta SCRAM non valida (il nonce non combacia)\n"
+
+#: fe-auth-scram.c:600
+msgid "malformed SCRAM message (invalid iteration count)\n"
+msgstr "messaggio SCRAM malformato (numero di iterazione non valido)\n"
+
+#: fe-auth-scram.c:606
+msgid "malformed SCRAM message (garbage at end of server-first-message)\n"
+msgstr "messaggio SCRAM malformato (dati non riconosciuti dopo il primo messaggio del server)\n"
+
+#: fe-auth-scram.c:636
+#, c-format
+msgid "error received from server in SCRAM exchange: %s\n"
+msgstr "errore ricevuto dal server durante lo scambio SCRAM: %s\n"
+
+#: fe-auth-scram.c:652
+msgid "malformed SCRAM message (garbage at end of server-final-message)\n"
+msgstr "messaggio SCRAM malformato (dati non riconosciuti dopo il messaggio finale del server)\n"
+
+#: fe-auth-scram.c:660
+msgid "malformed SCRAM message (invalid server signature)\n"
+msgstr "messaggio SCRAM malformato (firma del server non valida)\n"
+
+#: fe-auth.c:122
+#, c-format
+msgid "out of memory allocating GSSAPI buffer (%d)\n"
+msgstr "memoria esaurita nell'allocazione del buffer GSSAPI (%d)\n"
+
+# DV: non ne sono convinto
+#: fe-auth.c:177
+msgid "GSSAPI continuation error"
+msgstr "GSSAPI errore di continuazione"
+
+#: fe-auth.c:207 fe-auth.c:461 fe-secure-common.c:98
+msgid "host name must be specified\n"
+msgstr "il nome dell'host deve essere specificato\n"
+
+#: fe-auth.c:214
+msgid "duplicate GSS authentication request\n"
+msgstr "richiesta di autenticazione GSS duplicata\n"
+
+# non è che mi torni tanto così
+#: fe-auth.c:240
+msgid "GSSAPI name import error"
+msgstr "errore di importazione del nome GSSAPI"
+
+#: fe-auth.c:303
+#, c-format
+msgid "out of memory allocating SSPI buffer (%d)\n"
+msgstr "memoria esaurita nell'allocazione del buffer SSPI (%d)\n"
+
+#: fe-auth.c:351
+msgid "SSPI continuation error"
+msgstr "SSPI errore di continuazione"
+
+#: fe-auth.c:422
+msgid "duplicate SSPI authentication request\n"
+msgstr "richiesta di autenticazione SSPI duplicata\n"
+
+#: fe-auth.c:447
+msgid "could not acquire SSPI credentials"
+msgstr "non è stato possibile ottenere le credenziali SSPI"
+
+#: fe-auth.c:501
+msgid "duplicate SASL authentication request\n"
+msgstr "doppia richiesta di autenticazione SASL\n"
+
+#: fe-auth.c:549
+msgid "server offered SCRAM-SHA-256-PLUS authentication over a non-SSL connection\n"
+msgstr "il server ha offerto autenticazione SCRAM-SHA-256-PLUS su una connessione non SSL\n"
+
+#: fe-auth.c:561
+msgid "none of the server's SASL authentication mechanisms are supported\n"
+msgstr "nessuno dei meccanismi di autenticazione SASL del server è supportato\n"
+
+#: fe-auth.c:667
+#, c-format
+msgid "out of memory allocating SASL buffer (%d)\n"
+msgstr "memoria esaurita nell'allocazione del buffer SASL (%d)\n"
+
+#: fe-auth.c:692
+msgid "AuthenticationSASLFinal received from server, but SASL authentication was not completed\n"
+msgstr "ricevuto AuthenticationSASLFinal dal server, ma l'autenticazione SASL non è stata completata\n"
+
+#: fe-auth.c:769
+msgid "SCM_CRED authentication method not supported\n"
+msgstr "il metodo di autenticazione SCM_CRED non è supportato\n"
+
+#: fe-auth.c:860
+msgid "Kerberos 4 authentication not supported\n"
+msgstr "l'autenticazione Kerberos 4 non è supportata\n"
+
+#: fe-auth.c:865
+msgid "Kerberos 5 authentication not supported\n"
+msgstr "l'autenticazione Kerberos 5 non è supportata\n"
+
+#: fe-auth.c:936
+msgid "GSSAPI authentication not supported\n"
+msgstr "l'autenticazione GSSAPI non è supportata\n"
+
+#: fe-auth.c:968
+msgid "SSPI authentication not supported\n"
+msgstr "l'autenticazione SSPI non è supportata\n"
+
+#: fe-auth.c:976
+msgid "Crypt authentication not supported\n"
+msgstr "l'autenticazione Crypt non è supportata\n"
+
+#: fe-auth.c:1042
+#, c-format
+msgid "authentication method %u not supported\n"
+msgstr "l'autenticazione %u non è supportata\n"
+
+#: fe-auth.c:1089
+#, c-format
+msgid "user name lookup failure: error code %lu\n"
+msgstr "ricerca del nome utente fallita: codice di errore %lu\n"
+
+#: fe-auth.c:1099 fe-connect.c:2533
+#, c-format
+msgid "could not look up local user ID %d: %s\n"
+msgstr "ricerca dell'ID utente locale %d non riuscita: %s\n"
+
+#: fe-auth.c:1104 fe-connect.c:2538
+#, c-format
+msgid "local user with ID %d does not exist\n"
+msgstr "l'utente locale con ID %d non esiste\n"
+
+#: fe-auth.c:1206
+msgid "unexpected shape of result set returned for SHOW\n"
+msgstr "il risultato restituito da SHOW ha una forma imprevista\n"
+
+#: fe-auth.c:1215
+msgid "password_encryption value too long\n"
+msgstr "valore di password_encryption troppo lungo\n"
+
+#: fe-auth.c:1255
+#, c-format
+msgid "unrecognized password encryption algorithm \"%s\"\n"
+msgstr "algoritmo di criptaggio della password \"%s\" sconosciuto\n"
+
+#: fe-connect.c:1018
+#, c-format
+msgid "could not match %d host names to %d hostaddr values\n"
+msgstr "non è possibile far combaciare %d nomi host con %d valori hostaddr\n"
+
+#: fe-connect.c:1094
+#, c-format
+msgid "could not match %d port numbers to %d hosts\n"
+msgstr "non è possibile far combaciare %d numeri di porta con %d host\n"
+
+#: fe-connect.c:1190
+#, c-format
+msgid "invalid sslmode value: \"%s\"\n"
+msgstr "valore sslmode errato: \"%s\"\n"
+
+#: fe-connect.c:1211
+#, c-format
+msgid "sslmode value \"%s\" invalid when SSL support is not compiled in\n"
+msgstr "valore sslmode \"%s\" non valido quando il supporto SSL non è compilato\n"
+
+#: fe-connect.c:1246
+#, c-format
+msgid "invalid target_session_attrs value: \"%s\"\n"
+msgstr "valore per target_session_attrs non valido: \"%s\"\n"
+
+#: fe-connect.c:1464
+#, c-format
+msgid "could not set socket to TCP no delay mode: %s\n"
+msgstr "impostazione del socket in modalità TCP no delay fallita: %s\n"
+
+#: fe-connect.c:1494
+#, c-format
+msgid ""
+"could not connect to server: %s\n"
+"\tIs the server running locally and accepting\n"
+"\tconnections on Unix domain socket \"%s\"?\n"
+msgstr ""
+"connessione al server fallita: %s\n"
+"\tVerifica che il server locale sia in funzione e che\n"
+"\taccetti connessioni sul socket di dominio Unix \"%s\"\n"
+
+#: fe-connect.c:1552
+#, c-format
+msgid ""
+"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"
+msgstr ""
+"connessione al server fallita: %s\n"
+"\tVerifica che il server all'indirizzo \"%s\" (%s) sia in funzione\n"
+"\te che accetti connessioni TCP/IP sulla porta %s\n"
+
+#: fe-connect.c:1561
+#, c-format
+msgid ""
+"could not connect to server: %s\n"
+"\tIs the server running on host \"%s\" and accepting\n"
+"\tTCP/IP connections on port %s?\n"
+msgstr ""
+"connessione al server fallita: %s\n"
+"\tVerifica che il server all'indirizzo \"%s\" sia in funzione\n"
+"\te che accetti connessioni TCP/IP sulla porta %s\n"
+
+#: fe-connect.c:1612 fe-connect.c:1644 fe-connect.c:1677 fe-connect.c:2325
+#, c-format
+msgid "setsockopt(%s) failed: %s\n"
+msgstr "setsockopt(%s) fallita: %s\n"
+
+#: fe-connect.c:1726
+#, c-format
+msgid "WSAIoctl(SIO_KEEPALIVE_VALS) failed: %ui\n"
+msgstr "chiamata WSAIoctl(SIO_KEEPALIVE_VALS) fallito: %ui\n"
+
+#: fe-connect.c:2035
+msgid "invalid connection state, probably indicative of memory corruption\n"
+msgstr "stato della connessione non valido, probabilmente indica una corruzione della memoria\n"
+
+#: fe-connect.c:2101
+#, c-format
+msgid "invalid port number: \"%s\"\n"
+msgstr "numero di porta non valido: \"%s\"\n"
+
+#: fe-connect.c:2117
+#, c-format
+msgid "could not translate host name \"%s\" to address: %s\n"
+msgstr "conversione del nome host \"%s\" in indirizzo fallita: %s\n"
+
+#: fe-connect.c:2130
+#, c-format
+msgid "could not parse network address \"%s\": %s\n"
+msgstr "interpretazione dell'indirizzo di rete \"%s\" fallita: %s\n"
+
+#: fe-connect.c:2143
+#, c-format
+msgid "Unix-domain socket path \"%s\" is too long (maximum %d bytes)\n"
+msgstr "Il percorso del socket di dominio unix \"%s\" è troppo lungo (massimo %d byte)\n"
+
+#: fe-connect.c:2158
+#, c-format
+msgid "could not translate Unix-domain socket path \"%s\" to address: %s\n"
+msgstr "conversione del percorso del socket di dominio Unix \"%s\" in indirizzo fallita: %s\n"
+
+#: fe-connect.c:2262
+#, c-format
+msgid "could not create socket: %s\n"
+msgstr "creazione del socket fallita: %s\n"
+
+#: fe-connect.c:2284
+#, c-format
+msgid "could not set socket to nonblocking mode: %s\n"
+msgstr "impostazione del socket in modalità non bloccante fallita: %s\n"
+
+#: fe-connect.c:2294
+#, c-format
+msgid "could not set socket to close-on-exec mode: %s\n"
+msgstr "impostazione del socket in modalità close-on-exec fallita: %s\n"
+
+#: fe-connect.c:2312
+msgid "keepalives parameter must be an integer\n"
+msgstr "il parametro keepalives dev'essere un intero\n"
+
+#: fe-connect.c:2450
+#, c-format
+msgid "could not get socket error status: %s\n"
+msgstr "lettura dello stato di errore del socket fallita: %s\n"
+
+#: fe-connect.c:2478
+#, c-format
+msgid "could not get client address from socket: %s\n"
+msgstr "non è stato possibile ottenere l'indirizzo del client dal socket: %s\n"
+
+#: fe-connect.c:2520
+msgid "requirepeer parameter is not supported on this platform\n"
+msgstr "il parametro requirepeer non è supportato su questa piattaforma\n"
+
+#: fe-connect.c:2523
+#, c-format
+msgid "could not get peer credentials: %s\n"
+msgstr "non è stato possibile ottenere le credenziali del peer: %s\n"
+
+#: fe-connect.c:2546
+#, c-format
+msgid "requirepeer specifies \"%s\", but actual peer user name is \"%s\"\n"
+msgstr "requirepeer specifica \"%s\", ma il vero nome utente del peer è \"%s\"\n"
+
+#: fe-connect.c:2580
+#, c-format
+msgid "could not send SSL negotiation packet: %s\n"
+msgstr "invio del pacchetto di negoziazione SSL fallito: %s\n"
+
+#: fe-connect.c:2619
+#, c-format
+msgid "could not send startup packet: %s\n"
+msgstr "invio del pacchetto di avvio fallito: %s\n"
+
+#: fe-connect.c:2689
+msgid "server does not support SSL, but SSL was required\n"
+msgstr "il server non supporta SSL, ma SSL è stato richiesto\n"
+
+#: fe-connect.c:2715
+#, c-format
+msgid "received invalid response to SSL negotiation: %c\n"
+msgstr "ricevuta risposta errata alla negoziazione SSL: %c\n"
+
+#: fe-connect.c:2792 fe-connect.c:2825
+#, c-format
+msgid "expected authentication request from server, but received %c\n"
+msgstr "prevista richiesta di autenticazione dal server, ma è stato ricevuto %c\n"
+
+#: fe-connect.c:3052
+msgid "unexpected message from server during startup\n"
+msgstr "messaggio imprevisto dal server durante l'avvio\n"
+
+#: fe-connect.c:3282
+#, c-format
+msgid "could not make a writable connection to server \"%s:%s\"\n"
+msgstr "errore nello stabilire una connessione scrivibile col server \"%s:%s\"\n"
+
+#: fe-connect.c:3328
+#, c-format
+msgid "test \"SHOW transaction_read_only\" failed on server \"%s:%s\"\n"
+msgstr "test \"SHOW transaction_read_only\" fallito sul server \"%s:%s\"\n"
+
+#: fe-connect.c:3343
+#, c-format
+msgid "invalid connection state %d, probably indicative of memory corruption\n"
+msgstr "stato connessione errato %d, probabilmente indica una corruzione di memoria\n"
+
+#: fe-connect.c:3758 fe-connect.c:3818
+#, c-format
+msgid "PGEventProc \"%s\" failed during PGEVT_CONNRESET event\n"
+msgstr "PGEventProc \"%s\" fallito durante l'evento PGEVT_CONNRESET\n"
+
+#: fe-connect.c:4165
+#, c-format
+msgid "invalid LDAP URL \"%s\": scheme must be ldap://\n"
+msgstr "URL LDAP \"%s\" non corretta: lo schema deve essere ldap://\n"
+
+#: fe-connect.c:4180
+#, c-format
+msgid "invalid LDAP URL \"%s\": missing distinguished name\n"
+msgstr "URL LDAP \"%s\" non corretta: distinguished name non trovato\n"
+
+#: fe-connect.c:4191 fe-connect.c:4244
+#, c-format
+msgid "invalid LDAP URL \"%s\": must have exactly one attribute\n"
+msgstr "URL LDAP \"%s\" non corretta: deve avere esattamente un attributo\n"
+
+#: fe-connect.c:4201 fe-connect.c:4258
+#, c-format
+msgid "invalid LDAP URL \"%s\": must have search scope (base/one/sub)\n"
+msgstr "URL LDAP \"%s\" non corretta: deve essere specificato la portata della ricerca (base/one/sub)\n"
+
+#: fe-connect.c:4212
+#, c-format
+msgid "invalid LDAP URL \"%s\": no filter\n"
+msgstr "URL LDAP \"%s\" non corretta: filtro non specificato\n"
+
+#: fe-connect.c:4233
+#, c-format
+msgid "invalid LDAP URL \"%s\": invalid port number\n"
+msgstr "URL LDAP \"%s\" non corretta: numero di porta non valido\n"
+
+#: fe-connect.c:4267
+msgid "could not create LDAP structure\n"
+msgstr "creazione della struttura dati LDAP fallita\n"
+
+#: fe-connect.c:4343
+#, c-format
+msgid "lookup on LDAP server failed: %s\n"
+msgstr "ricerca del server LDAP fallita: %s\n"
+
+#: fe-connect.c:4354
+msgid "more than one entry found on LDAP lookup\n"
+msgstr "trovata più di una voce nella ricerca LDAP\n"
+
+#: fe-connect.c:4355 fe-connect.c:4367
+msgid "no entry found on LDAP lookup\n"
+msgstr "nessun elemento trovato per la ricerca LDAP\n"
+
+#: fe-connect.c:4378 fe-connect.c:4391
+msgid "attribute has no values on LDAP lookup\n"
+msgstr "l'attributo non ha valori nella ricerca LDAP\n"
+
+#: fe-connect.c:4443 fe-connect.c:4462 fe-connect.c:4991
+#, c-format
+msgid "missing \"=\" after \"%s\" in connection info string\n"
+msgstr "manca \"=\" dopo \"%s\" nella stringa di connessione\n"
+
+#: fe-connect.c:4535 fe-connect.c:5176 fe-connect.c:5950
+#, c-format
+msgid "invalid connection option \"%s\"\n"
+msgstr "opzione di connessione errata \"%s\"\n"
+
+#: fe-connect.c:4551 fe-connect.c:5040
+msgid "unterminated quoted string in connection info string\n"
+msgstr "stringa tra virgolette non terminata nella stringa di connessione\n"
+
+#: fe-connect.c:4634
+#, c-format
+msgid "definition of service \"%s\" not found\n"
+msgstr "il file di definizione di servizio \"%s\" non è stato trovato\n"
+
+#: fe-connect.c:4657
+#, c-format
+msgid "service file \"%s\" not found\n"
+msgstr "il file di servizio \"%s\" non è stato trovato\n"
+
+#: fe-connect.c:4670
+#, c-format
+msgid "line %d too long in service file \"%s\"\n"
+msgstr "la riga %d nel file di servizio \"%s\" è troppo lunga\n"
+
+#: fe-connect.c:4741 fe-connect.c:4785
+#, c-format
+msgid "syntax error in service file \"%s\", line %d\n"
+msgstr "errore di sintassi del file di servizio \"%s\", alla riga %d\n"
+
+#: fe-connect.c:4752
+#, c-format
+msgid "nested service specifications not supported in service file \"%s\", line %d\n"
+msgstr "specifiche di servizio annidate non supportate nel file di servizio \"%s\", linea %d\n"
+
+#: fe-connect.c:5472
+#, c-format
+msgid "invalid URI propagated to internal parser routine: \"%s\"\n"
+msgstr "URI invalida propagata alla routine di parsing interna: \"%s\"\n"
+
+#: fe-connect.c:5549
+#, c-format
+msgid "end of string reached when looking for matching \"]\" in IPv6 host address in URI: \"%s\"\n"
+msgstr "fine stringa raggiunta cercando un \"]\" corrispondente nell'indirizzo host IPv6 nella URI: \"%s\"\n"
+
+#: fe-connect.c:5556
+#, c-format
+msgid "IPv6 host address may not be empty in URI: \"%s\"\n"
+msgstr "l'indirizzo host IPv6 non dev'essere assente nella URI: \"%s\"\n"
+
+#: fe-connect.c:5571
+#, c-format
+msgid "unexpected character \"%c\" at position %d in URI (expected \":\" or \"/\"): \"%s\"\n"
+msgstr "carattere inatteso \"%c\" in posizione %d nella uri URI (atteso \":\" oppure \"/\"): \"%s\"\n"
+
+#: fe-connect.c:5700
+#, c-format
+msgid "extra key/value separator \"=\" in URI query parameter: \"%s\"\n"
+msgstr "separatore chiave/valore \"=\" in eccesso nei parametri della URI: \"%s\"\n"
+
+#: fe-connect.c:5720
+#, c-format
+msgid "missing key/value separator \"=\" in URI query parameter: \"%s\"\n"
+msgstr "separatore chiave/valore \"=\" mancante nei parametri della URI: \"%s\"\n"
+
+#: fe-connect.c:5771
+#, c-format
+msgid "invalid URI query parameter: \"%s\"\n"
+msgstr "parametro URI non valido: \"%s\"\n"
+
+#: fe-connect.c:5845
+#, c-format
+msgid "invalid percent-encoded token: \"%s\"\n"
+msgstr "simbolo percent-encoded non valido \"%s\"\n"
+
+#: fe-connect.c:5855
+#, c-format
+msgid "forbidden value %%00 in percent-encoded value: \"%s\"\n"
+msgstr "valore non ammesso %%00 nel valore percent-encoded: \"%s\"\n"
+
+#: fe-connect.c:6201
+msgid "connection pointer is NULL\n"
+msgstr "il puntatore della connessione è NULL\n"
+
+#: fe-connect.c:6499
+#, c-format
+msgid "WARNING: password file \"%s\" is not a plain file\n"
+msgstr "ATTENZIONE: il file delle password \"%s\" non è un file regolare\n"
+
+#: fe-connect.c:6508
+#, c-format
+msgid "WARNING: password file \"%s\" has group or world access; permissions should be u=rw (0600) or less\n"
+msgstr ""
+"ATTENZIONE: Il file delle password %s ha privilegi di accesso in lettura e scrittura per tutti;\n"
+"i permessi dovrebbero essere u=rw (0600) o inferiori\n"
+
+#: fe-connect.c:6602
+#, c-format
+msgid "password retrieved from file \"%s\"\n"
+msgstr "password ottenuta dal file \"%s\"\n"
+
+#: fe-exec.c:437 fe-exec.c:2776
+#, c-format
+msgid "row number %d is out of range 0..%d"
+msgstr "la riga numero %d non è compreso tra 0 e %d"
+
+#: fe-exec.c:498 fe-protocol2.c:502 fe-protocol2.c:537 fe-protocol2.c:1056
+#: fe-protocol3.c:208 fe-protocol3.c:235 fe-protocol3.c:252 fe-protocol3.c:332
+#: fe-protocol3.c:727 fe-protocol3.c:958
+msgid "out of memory"
+msgstr "memoria esaurita"
+
+#: fe-exec.c:499 fe-protocol2.c:1402 fe-protocol3.c:1893
+#, c-format
+msgid "%s"
+msgstr "%s"
+
+#: fe-exec.c:847
+msgid "NOTICE"
+msgstr "NOTIFICA"
+
+#: fe-exec.c:905
+msgid "PGresult cannot support more than INT_MAX tuples"
+msgstr "PGresult non può supportare più di INT_MAX tuple"
+
+#: fe-exec.c:917
+msgid "size_t overflow"
+msgstr "overflow size_t"
+
+#: fe-exec.c:1192 fe-exec.c:1250 fe-exec.c:1296
+msgid "command string is a null pointer\n"
+msgstr "il testo del comando è un puntatore nullo\n"
+
+#: fe-exec.c:1256 fe-exec.c:1302 fe-exec.c:1397
+msgid "number of parameters must be between 0 and 65535\n"
+msgstr "il numero di parametri deve essere tra 0 e 65535\n"
+
+#: fe-exec.c:1290 fe-exec.c:1391
+msgid "statement name is a null pointer\n"
+msgstr "il nome dell'istruzione è un puntatore nullo\n"
+
+#: fe-exec.c:1310 fe-exec.c:1473 fe-exec.c:2191 fe-exec.c:2390
+msgid "function requires at least protocol version 3.0\n"
+msgstr "la funzione richiede almeno il protocollo versione 3.0\n"
+
+#: fe-exec.c:1428
+msgid "no connection to the server\n"
+msgstr "nessuna connessione al server\n"
+
+#: fe-exec.c:1435
+msgid "another command is already in progress\n"
+msgstr "un altro comando è in esecuzione\n"
+
+#: fe-exec.c:1549
+msgid "length must be given for binary parameter\n"
+msgstr "la lunghezza deve essere fornita per i parametri binari\n"
+
+#: fe-exec.c:1821
+#, c-format
+msgid "unexpected asyncStatus: %d\n"
+msgstr "asyncStatus imprevisto: %d\n"
+
+#: fe-exec.c:1841
+#, c-format
+msgid "PGEventProc \"%s\" failed during PGEVT_RESULTCREATE event\n"
+msgstr "PGEventProc \"%s\" fallito durante l'evento PGEVT_RESULTCREATE\n"
+
+#: fe-exec.c:2001
+msgid "COPY terminated by new PQexec"
+msgstr "COPY terminato da una nuova PQexec"
+
+#: fe-exec.c:2009
+msgid "COPY IN state must be terminated first\n"
+msgstr "lo stato COPY IN deve prima essere terminato\n"
+
+#: fe-exec.c:2029
+msgid "COPY OUT state must be terminated first\n"
+msgstr "lo stato COPY OUT deve prima essere terminato\n"
+
+# NON SONO ASSOLUTAMENTE CONVINTO!
+#: fe-exec.c:2037
+msgid "PQexec not allowed during COPY BOTH\n"
+msgstr "PQexec not consentito durante COPY BOTH\n"
+
+#: fe-exec.c:2280 fe-exec.c:2347 fe-exec.c:2437 fe-protocol2.c:1359
+#: fe-protocol3.c:1824
+msgid "no COPY in progress\n"
+msgstr "nessun comando COPY in corso\n"
+
+#: fe-exec.c:2627
+msgid "connection in wrong state\n"
+msgstr "la connessione è in uno stato errato\n"
+
+#: fe-exec.c:2658
+msgid "invalid ExecStatusType code"
+msgstr "codice ExecStatusType errato"
+
+#: fe-exec.c:2685
+msgid "PGresult is not an error result\n"
+msgstr "PGresult non è un risultato di errore\n"
+
+#: fe-exec.c:2760 fe-exec.c:2783
+#, c-format
+msgid "column number %d is out of range 0..%d"
+msgstr "la colonna numero %d non è compreso tra 0 e %d"
+
+#: fe-exec.c:2798
+#, c-format
+msgid "parameter number %d is out of range 0..%d"
+msgstr "il parametro numero %d non è compreso tra 0 e %d"
+
+#: fe-exec.c:3108
+#, c-format
+msgid "could not interpret result from server: %s"
+msgstr "errore nell'interpretazione del risultato dal server: %s"
+
+#: fe-exec.c:3347 fe-exec.c:3431
+msgid "incomplete multibyte character\n"
+msgstr "carattere multibyte incompleto\n"
+
+#: fe-lobj.c:154
+msgid "cannot determine OID of function lo_truncate\n"
+msgstr "non è possibile determinare l'OID della funzione lo_truncate\n"
+
+#: fe-lobj.c:170
+msgid "argument of lo_truncate exceeds integer range\n"
+msgstr "l'argomento di lo_truncate supera l'intervallo degli interi\n"
+
+#: fe-lobj.c:221
+msgid "cannot determine OID of function lo_truncate64\n"
+msgstr "non è possibile determinare l'OID della funzione lo_truncate64\n"
+
+#: fe-lobj.c:279
+msgid "argument of lo_read exceeds integer range\n"
+msgstr "l'argomento di lo_read supera l'intervallo degli interi\n"
+
+#: fe-lobj.c:334
+msgid "argument of lo_write exceeds integer range\n"
+msgstr "l'argomento di lo_write supera l'intervallo degli interi\n"
+
+#: fe-lobj.c:425
+msgid "cannot determine OID of function lo_lseek64\n"
+msgstr "non è possibile determinare l'OID della funzione lo_seek64\n"
+
+#: fe-lobj.c:521
+msgid "cannot determine OID of function lo_create\n"
+msgstr "non è possibile determinare l'OID della funzione lo_create\n"
+
+#: fe-lobj.c:600
+msgid "cannot determine OID of function lo_tell64\n"
+msgstr "non è possibile determinare l'OID della funzione lo_tell64\n"
+
+#: fe-lobj.c:706 fe-lobj.c:815
+#, c-format
+msgid "could not open file \"%s\": %s\n"
+msgstr "apertura del file \"%s\" fallita: %s\n"
+
+#: fe-lobj.c:761
+#, c-format
+msgid "could not read from file \"%s\": %s\n"
+msgstr "lettura dal file \"%s\" fallita: %s\n"
+
+#: fe-lobj.c:835 fe-lobj.c:859
+#, c-format
+msgid "could not write to file \"%s\": %s\n"
+msgstr "scrittura nel file \"%s\" fallita: %s\n"
+
+#: fe-lobj.c:946
+msgid "query to initialize large object functions did not return data\n"
+msgstr "la query per inizializzare le funzioni large object non hanno restituito dati\n"
+
+#: fe-lobj.c:995
+msgid "cannot determine OID of function lo_open\n"
+msgstr "non è possibile determinare l'OID della funzione lo_open\n"
+
+#: fe-lobj.c:1002
+msgid "cannot determine OID of function lo_close\n"
+msgstr "non è possibile determinare l'OID della funzione lo_close\n"
+
+#: fe-lobj.c:1009
+msgid "cannot determine OID of function lo_creat\n"
+msgstr "non è possibile determinare l'OID della funzione lo_create\n"
+
+#: fe-lobj.c:1016
+msgid "cannot determine OID of function lo_unlink\n"
+msgstr "non è possibile determinare l'OID della funzione lo_unlink\n"
+
+#: fe-lobj.c:1023
+msgid "cannot determine OID of function lo_lseek\n"
+msgstr "non è possibile determinare l'OID della funzione lo_seek\n"
+
+#: fe-lobj.c:1030
+msgid "cannot determine OID of function lo_tell\n"
+msgstr "non è possibile determinare l'OID della funzione lo_tell\n"
+
+#: fe-lobj.c:1037
+msgid "cannot determine OID of function loread\n"
+msgstr "non è possibile determinare l'OID della funzione loread\n"
+
+#: fe-lobj.c:1044
+msgid "cannot determine OID of function lowrite\n"
+msgstr "non è possibile determinare l'OID della funzione lowrite\n"
+
+#: fe-misc.c:290
+#, c-format
+msgid "integer of size %lu not supported by pqGetInt"
+msgstr "intero di dimensione %lu non supportato da pqGetInt"
+
+#: fe-misc.c:326
+#, c-format
+msgid "integer of size %lu not supported by pqPutInt"
+msgstr "intero di dimensione %lu non supportato da pqPutInt"
+
+#: fe-misc.c:637 fe-misc.c:838
+msgid "connection not open\n"
+msgstr "connessione non aperta\n"
+
+#: fe-misc.c:807 fe-secure-openssl.c:206 fe-secure-openssl.c:314
+#: fe-secure.c:261 fe-secure.c:371
+msgid ""
+"server closed the connection unexpectedly\n"
+"\tThis probably means the server terminated abnormally\n"
+"\tbefore or while processing the request.\n"
+msgstr ""
+"il server ha chiuso la connessione inaspettatamente\n"
+"\tQuesto probabilmente indica che il server ha terminato in modo anormale\n"
+"\tprima o durante l'elaborazione della richiesta.\n"
+
+#: fe-misc.c:1009
+msgid "timeout expired\n"
+msgstr "timeout scaduto\n"
+
+#: fe-misc.c:1054
+msgid "invalid socket\n"
+msgstr "socket non valido\n"
+
+#: fe-misc.c:1077
+#, c-format
+msgid "select() failed: %s\n"
+msgstr "select() fallita: %s\n"
+
+#: fe-protocol2.c:90
+#, c-format
+msgid "invalid setenv state %c, probably indicative of memory corruption\n"
+msgstr "stato %c di setenv non valido, probabilmente indica una corruzione di memoria\n"
+
+#: fe-protocol2.c:389
+#, c-format
+msgid "invalid state %c, probably indicative of memory corruption\n"
+msgstr "stato %c non valido, probabilmente indica una corruzione di memoria\n"
+
+#: fe-protocol2.c:478 fe-protocol3.c:185
+#, c-format
+msgid "message type 0x%02x arrived from server while idle"
+msgstr "messaggio tipo 0x%02x arrivato dal server mentre era inattivo"
+
+#: fe-protocol2.c:528
+#, c-format
+msgid "unexpected character %c following empty query response (\"I\" message)"
+msgstr "carattere %c non previsto a seguito di una risposta vuota ad una query (messaggio \"I\")"
+
+#: fe-protocol2.c:594
+#, c-format
+msgid "server sent data (\"D\" message) without prior row description (\"T\" message)"
+msgstr "il server ha spedito dati (messaggio di tipo \"D\") senza prima il descrittore di riga (messaggio di tipo \"T\")"
+
+#: fe-protocol2.c:612
+#, c-format
+msgid "server sent binary data (\"B\" message) without prior row description (\"T\" message)"
+msgstr "il server ha spedito dati binari (messaggio di tipo \"B\") senza prima il descrittore di riga (messaggio di tipo \"T\")"
+
+#: fe-protocol2.c:632 fe-protocol3.c:411
+#, c-format
+msgid "unexpected response from server; first received character was \"%c\"\n"
+msgstr "risposta inattesa dal server; il primo carattere ricevuto era \"%c\"\n"
+
+#: fe-protocol2.c:761 fe-protocol2.c:936 fe-protocol3.c:626 fe-protocol3.c:853
+msgid "out of memory for query result"
+msgstr "memoria esaurita per il risultato della query"
+
+#: fe-protocol2.c:1414
+#, c-format
+msgid "lost synchronization with server, resetting connection"
+msgstr "persa la sincronizzazione con il server, sto resettando la connessione"
+
+#: fe-protocol2.c:1548 fe-protocol2.c:1580 fe-protocol3.c:2096
+#, c-format
+msgid "protocol error: id=0x%x\n"
+msgstr "errore di protocollo: id=0x%x\n"
+
+#: fe-protocol3.c:367
+msgid "server sent data (\"D\" message) without prior row description (\"T\" message)\n"
+msgstr "il server ha spedito dati (messaggio di tipo \"D\") senza prima il descrittore di riga (messaggio di tipo \"T\")\n"
+
+#: fe-protocol3.c:432
+#, c-format
+msgid "message contents do not agree with length in message type \"%c\"\n"
+msgstr "i contenuti del messaggio non sono in accordo con la lunghezza del tipo di messaggio \"%c\"\n"
+
+#: fe-protocol3.c:453
+#, c-format
+msgid "lost synchronization with server: got message type \"%c\", length %d\n"
+msgstr "persa la sincronizzazione con il server: ricevuto il tipo di messaggio \"%c\" di lunghezza %d\n"
+
+#: fe-protocol3.c:504 fe-protocol3.c:544
+msgid "insufficient data in \"T\" message"
+msgstr "dati insufficienti nel messaggio di tipo \"T\""
+
+#: fe-protocol3.c:577
+msgid "extraneous data in \"T\" message"
+msgstr "dati estranei nel messaggio di tipo \"T\""
+
+#: fe-protocol3.c:690
+msgid "extraneous data in \"t\" message"
+msgstr "dati estranei nel messaggio di tipo \"t\""
+
+#: fe-protocol3.c:761 fe-protocol3.c:793 fe-protocol3.c:811
+msgid "insufficient data in \"D\" message"
+msgstr "dati insufficienti nel messaggio di tipo \"D\""
+
+#: fe-protocol3.c:767
+msgid "unexpected field count in \"D\" message"
+msgstr "numero dei campi non previsto nel messaggio di tipo \"D\""
+
+#: fe-protocol3.c:820
+msgid "extraneous data in \"D\" message"
+msgstr "dati estranei nel messaggio di tipo \"D\""
+
+#: fe-protocol3.c:1012
+msgid "no error message available\n"
+msgstr "nessun messaggio di errore disponibile\n"
+
+#. translator: %s represents a digit string
+#: fe-protocol3.c:1042 fe-protocol3.c:1061
+#, c-format
+msgid " at character %s"
+msgstr " al carattere %s"
+
+#: fe-protocol3.c:1074
+#, c-format
+msgid "DETAIL: %s\n"
+msgstr "DETTAGLI: %s\n"
+
+#: fe-protocol3.c:1077
+#, c-format
+msgid "HINT: %s\n"
+msgstr "NOTA: %s\n"
+
+#: fe-protocol3.c:1080
+#, c-format
+msgid "QUERY: %s\n"
+msgstr "QUERY: %s\n"
+
+#: fe-protocol3.c:1087
+#, c-format
+msgid "CONTEXT: %s\n"
+msgstr "CONTESTO: %s\n"
+
+#: fe-protocol3.c:1096
+#, c-format
+msgid "SCHEMA NAME: %s\n"
+msgstr "NOME SCHEMA: %s\n"
+
+#: fe-protocol3.c:1100
+#, c-format
+msgid "TABLE NAME: %s\n"
+msgstr "NOME TABELLA: %s\n"
+
+#: fe-protocol3.c:1104
+#, c-format
+msgid "COLUMN NAME: %s\n"
+msgstr "NOME COLONNA: %s\n"
+
+#: fe-protocol3.c:1108
+#, c-format
+msgid "DATATYPE NAME: %s\n"
+msgstr "NOME TIPO DATI: %s\n"
+
+#: fe-protocol3.c:1112
+#, c-format
+msgid "CONSTRAINT NAME: %s\n"
+msgstr "NOME VINCOLO: %s\n"
+
+#: fe-protocol3.c:1124
+msgid "LOCATION: "
+msgstr "POSIZIONE: "
+
+#: fe-protocol3.c:1126
+#, c-format
+msgid "%s, "
+msgstr "%s, "
+
+#: fe-protocol3.c:1128
+#, c-format
+msgid "%s:%s"
+msgstr "%s:%s"
+
+#: fe-protocol3.c:1323
+#, c-format
+msgid "LINE %d: "
+msgstr "RIGA %d: "
+
+#: fe-protocol3.c:1718
+msgid "PQgetline: not doing text COPY OUT\n"
+msgstr "PQgetline: COPY OUT testuale ignorato\n"
+
+#: fe-secure-common.c:124
+msgid "SSL certificate's name contains embedded null\n"
+msgstr "Il nome del certificato SSL contiene null\n"
+
+#: fe-secure-common.c:171
+msgid "host name must be specified for a verified SSL connection\n"
+msgstr "il nome dell'host dev'essere specificato per una connessione SSL verificata\n"
+
+#: fe-secure-common.c:196
+#, c-format
+msgid "server certificate for \"%s\" does not match host name \"%s\"\n"
+msgstr "il certificato per il server \"%s\" non combacia col nome host \"%s\"\n"
+
+#: fe-secure-common.c:202
+msgid "could not get server's host name from server certificate\n"
+msgstr "impossibile ottenere il nome dell'host del server dal certificato del server\n"
+
+#: fe-secure-openssl.c:211 fe-secure-openssl.c:319 fe-secure-openssl.c:1219
+#, c-format
+msgid "SSL SYSCALL error: %s\n"
+msgstr "errore SSL SYSCALL: %s\n"
+
+#: fe-secure-openssl.c:218 fe-secure-openssl.c:326 fe-secure-openssl.c:1223
+msgid "SSL SYSCALL error: EOF detected\n"
+msgstr "errore SSL SYSCALL: rilevato EOF\n"
+
+#: fe-secure-openssl.c:229 fe-secure-openssl.c:337 fe-secure-openssl.c:1232
+#, c-format
+msgid "SSL error: %s\n"
+msgstr "errore SSL: %s\n"
+
+#: fe-secure-openssl.c:244 fe-secure-openssl.c:352
+msgid "SSL connection has been closed unexpectedly\n"
+msgstr "la connessione SSL è stata chiusa inaspettatamente\n"
+
+#: fe-secure-openssl.c:250 fe-secure-openssl.c:358 fe-secure-openssl.c:1241
+#, c-format
+msgid "unrecognized SSL error code: %d\n"
+msgstr "codice di errore SSL sconosciuto: %d\n"
+
+#: fe-secure-openssl.c:398
+msgid "could not determine server certificate signature algorithm\n"
+msgstr "impossibile determinare l'algoritmo di firma del certificato del server\n"
+
+#: fe-secure-openssl.c:419
+#, c-format
+msgid "could not find digest for NID %s\n"
+msgstr "impossibile trovare il digest per il NID %s\n"
+
+#: fe-secure-openssl.c:429
+msgid "could not generate peer certificate hash\n"
+msgstr "impossibile generare l'hash del certificato del peer\n"
+
+#: fe-secure-openssl.c:486
+msgid "SSL certificate's name entry is missing\n"
+msgstr "manca il nome del certificato SSL\n"
+
+#: fe-secure-openssl.c:815
+#, c-format
+msgid "could not create SSL context: %s\n"
+msgstr "creazione del contesto SSL fallita: %s\n"
+
+#: fe-secure-openssl.c:852
+#, c-format
+msgid "could not read root certificate file \"%s\": %s\n"
+msgstr "lettura del file di certificato radice \"%s\" fallita: %s\n"
+
+#: fe-secure-openssl.c:880
+#, c-format
+msgid "SSL library does not support CRL certificates (file \"%s\")\n"
+msgstr "la libreria SSL non supporta i certificati di tipo CRL (file \"%s\")\n"
+
+#: fe-secure-openssl.c:908
+msgid ""
+"could not get home directory to locate root certificate file\n"
+"Either provide the file or change sslmode to disable server certificate verification.\n"
+msgstr ""
+"directory utente non trovata per la locazione del file di certificato radice\n"
+"Per favore fornisci il file oppure cambia sslmode per disabilitare la verifica del certificato del server.\n"
+
+#: fe-secure-openssl.c:912
+#, c-format
+msgid ""
+"root certificate file \"%s\" does not exist\n"
+"Either provide the file or change sslmode to disable server certificate verification.\n"
+msgstr ""
+"il file \"%s\" del certificato radice non esiste\n"
+"Per favore fornisci il file oppure cambia sslmode per disabilitare la verifica del certificato del server.\n"
+
+#: fe-secure-openssl.c:943
+#, c-format
+msgid "could not open certificate file \"%s\": %s\n"
+msgstr "apertura del file di certificato \"%s\" fallita: %s\n"
+
+#: fe-secure-openssl.c:962
+#, c-format
+msgid "could not read certificate file \"%s\": %s\n"
+msgstr "lettura del file di certificato \"%s\" fallita: %s\n"
+
+#: fe-secure-openssl.c:987
+#, c-format
+msgid "could not establish SSL connection: %s\n"
+msgstr "non è stato possibile stabilire una connessione SSL: %s\n"
+
+#: fe-secure-openssl.c:1041
+#, c-format
+msgid "could not load SSL engine \"%s\": %s\n"
+msgstr "caricamento del motore SSL \"%s\" fallito: %s\n"
+
+#: fe-secure-openssl.c:1053
+#, c-format
+msgid "could not initialize SSL engine \"%s\": %s\n"
+msgstr "inizializzazione del motore SSL \"%s\" fallita: %s\n"
+
+#: fe-secure-openssl.c:1069
+#, c-format
+msgid "could not read private SSL key \"%s\" from engine \"%s\": %s\n"
+msgstr "lettura del file della chiave privata SSL \"%s\" dal motore \"%s\" fallita: %s\n"
+
+#: fe-secure-openssl.c:1083
+#, c-format
+msgid "could not load private SSL key \"%s\" from engine \"%s\": %s\n"
+msgstr "caricamento della chiave privata SSL \"%s\" dal motore \"%s\" fallito: %s\n"
+
+#: fe-secure-openssl.c:1120
+#, c-format
+msgid "certificate present, but not private key file \"%s\"\n"
+msgstr "certificato trovato, ma non la chiave privata \"%s\"\n"
+
+#: fe-secure-openssl.c:1128
+#, c-format
+msgid "private key file \"%s\" has group or world access; permissions should be u=rw (0600) or less\n"
+msgstr "Il file della chiave privata \"%s\" ha privilegi di accesso in lettura e scrittura per tutti; i permessi dovrebbero essere u=rw (0600) o inferiori\n"
+
+#: fe-secure-openssl.c:1139
+#, c-format
+msgid "could not load private key file \"%s\": %s\n"
+msgstr "caricamento del file della chiave privata \"%s\" fallito: %s\n"
+
+#: fe-secure-openssl.c:1153
+#, c-format
+msgid "certificate does not match private key file \"%s\": %s\n"
+msgstr "il certificato non corrisponde con il file della chiave privata \"%s\": %s\n"
+
+#: fe-secure-openssl.c:1262
+#, c-format
+msgid "certificate could not be obtained: %s\n"
+msgstr "non è stato possibile possibile ottenere il certificato: %s\n"
+
+#: fe-secure-openssl.c:1351
+#, c-format
+msgid "no SSL error reported"
+msgstr "nessun errore SSL riportato"
+
+#: fe-secure-openssl.c:1360
+#, c-format
+msgid "SSL error code %lu"
+msgstr "codice di errore SSL: %lu"
+
+#: fe-secure.c:269
+#, c-format
+msgid "could not receive data from server: %s\n"
+msgstr "ricezione dati dal server fallita: %s\n"
+
+#: fe-secure.c:378
+#, c-format
+msgid "could not send data to server: %s\n"
+msgstr "invio dati al server fallito: %s\n"
+
+#: win32.c:317
+#, c-format
+msgid "unrecognized socket error: 0x%08X/%d"
+msgstr "errore socket sconosciuto: 0x%08X/%d"
diff --git a/src/interfaces/libpq/po/ja.po b/src/interfaces/libpq/po/ja.po
new file mode 100644
index 0000000..3e08d46
--- /dev/null
+++ b/src/interfaces/libpq/po/ja.po
@@ -0,0 +1,1272 @@
+# Japanese message translation file for libpq
+# Copyright (C) 2019 PostgreSQL Global Development Group
+# This file is distributed under the same license as the pg_archivecleanup (PostgreSQL) package.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: libpq (PostgreSQL 13)\n"
+"Report-Msgid-Bugs-To: pgsql-bugs@lists.postgresql.org\n"
+"POT-Creation-Date: 2020-08-21 23:39+0900\n"
+"PO-Revision-Date: 2020-08-22 00:02+0900\n"
+"Last-Translator: Kyotaro Horiguchi <horikyota.ntt@gmail.com>\n"
+"Language-Team: Japan PostgreSQL Users Group <jpug-doc@ml.postgresql.jp>\n"
+"Language: ja\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+"X-Generator: Poedit 1.8.13\n"
+
+#: fe-auth-scram.c:212
+msgid "malformed SCRAM message (empty message)\n"
+msgstr "SCRAMメッセージのフォーマット異常 (空のメッセージ)\n"
+
+#: fe-auth-scram.c:218
+msgid "malformed SCRAM message (length mismatch)\n"
+msgstr "SCRAMメッセージのフォーマット異常 (長さの不整合)\n"
+
+#: fe-auth-scram.c:265
+msgid "incorrect server signature\n"
+msgstr "正しくないサーバ署名\n"
+
+#: fe-auth-scram.c:274
+msgid "invalid SCRAM exchange state\n"
+msgstr "不正なSCRAM交換状態\n"
+
+#: fe-auth-scram.c:296
+#, c-format
+msgid "malformed SCRAM message (attribute \"%c\" expected)\n"
+msgstr "SCRAMメッセージのフォーマット異常 (属性 \"%c\" が必要)\n"
+
+#: fe-auth-scram.c:305
+#, c-format
+msgid "malformed SCRAM message (expected character \"=\" for attribute \"%c\")\n"
+msgstr "SCRAMメッセージのフォーマット異常 (属性 \"%c\" に文字 \"=\" が必要)\n"
+
+#: fe-auth-scram.c:346
+msgid "could not generate nonce\n"
+msgstr "nonce を生成できませんでした\n"
+
+#: fe-auth-scram.c:356 fe-auth-scram.c:431 fe-auth-scram.c:579
+#: fe-auth-scram.c:600 fe-auth-scram.c:626 fe-auth-scram.c:641
+#: fe-auth-scram.c:691 fe-auth-scram.c:725 fe-auth.c:289 fe-auth.c:359
+#: fe-auth.c:394 fe-auth.c:611 fe-auth.c:770 fe-auth.c:1129 fe-auth.c:1277
+#: fe-connect.c:892 fe-connect.c:1423 fe-connect.c:1599 fe-connect.c:2204
+#: fe-connect.c:2227 fe-connect.c:2956 fe-connect.c:4602 fe-connect.c:4858
+#: fe-connect.c:4977 fe-connect.c:5230 fe-connect.c:5310 fe-connect.c:5409
+#: fe-connect.c:5665 fe-connect.c:5694 fe-connect.c:5766 fe-connect.c:5790
+#: fe-connect.c:5808 fe-connect.c:5909 fe-connect.c:5918 fe-connect.c:6274
+#: fe-connect.c:6424 fe-exec.c:2747 fe-exec.c:3494 fe-exec.c:3659
+#: fe-gssapi-common.c:111 fe-lobj.c:895 fe-protocol2.c:1207 fe-protocol3.c:995
+#: fe-protocol3.c:1699 fe-secure-common.c:110 fe-secure-gssapi.c:504
+#: fe-secure-openssl.c:440 fe-secure-openssl.c:1091
+msgid "out of memory\n"
+msgstr "メモリ不足\n"
+
+#: fe-auth-scram.c:364
+msgid "could not encode nonce\n"
+msgstr "nonceをエンコードに失敗しました\n"
+
+#: fe-auth-scram.c:563
+msgid "could not encode client proof\n"
+msgstr "クライアントの証明のエンコードに失敗しました\n"
+
+#: fe-auth-scram.c:618
+msgid "invalid SCRAM response (nonce mismatch)\n"
+msgstr "不正なSCRAM応答 (nonce の不一致)\n"
+
+#: fe-auth-scram.c:651
+msgid "malformed SCRAM message (invalid salt)\n"
+msgstr "SCRAMメッセージのフォーマット異常 (不正なサーバソルト)\n"
+
+#: fe-auth-scram.c:665
+msgid "malformed SCRAM message (invalid iteration count)\n"
+msgstr "SCRAMメッセージのフォーマット異常 (不正な繰り返し回数)\n"
+
+#: fe-auth-scram.c:671
+msgid "malformed SCRAM message (garbage at end of server-first-message)\n"
+msgstr "SCRAMメッセージのフォーマット異常 (server-first-message 終端の余分なデータ)\n"
+
+#: fe-auth-scram.c:702
+#, c-format
+msgid "error received from server in SCRAM exchange: %s\n"
+msgstr "SCRAM交換中にサーバからのエラーを受信しました: %s\n"
+
+#: fe-auth-scram.c:718
+msgid "malformed SCRAM message (garbage at end of server-final-message)\n"
+msgstr "SCRAMメッセージのフォーマット異常 (server-final-message 終端の余分なデータ)\n"
+
+#: fe-auth-scram.c:737
+msgid "malformed SCRAM message (invalid server signature)\n"
+msgstr "SCRAMメッセージのフォーマット異常 (不正なサーバ署名)\n"
+
+#: fe-auth.c:76
+#, c-format
+msgid "out of memory allocating GSSAPI buffer (%d)\n"
+msgstr "GSSAPIバッファの割り当ての際のメモリ不足(%d)\n"
+
+#: fe-auth.c:131
+msgid "GSSAPI continuation error"
+msgstr "GSSAI続行エラー"
+
+#: fe-auth.c:158 fe-auth.c:388 fe-gssapi-common.c:98 fe-secure-common.c:98
+msgid "host name must be specified\n"
+msgstr "ホスト名を指定しなければなりません\n"
+
+#: fe-auth.c:165
+msgid "duplicate GSS authentication request\n"
+msgstr "重複するGSS認証要求\n"
+
+#: fe-auth.c:230
+#, c-format
+msgid "out of memory allocating SSPI buffer (%d)\n"
+msgstr "SSPIバッファの割り当ての際のメモリ不足(%d)\n"
+
+#: fe-auth.c:278
+msgid "SSPI continuation error"
+msgstr "SSPI続行エラー"
+
+#: fe-auth.c:349
+msgid "duplicate SSPI authentication request\n"
+msgstr "重複するSSPI認証要求\n"
+
+#: fe-auth.c:374
+msgid "could not acquire SSPI credentials"
+msgstr "SSPI資格を入手できませんでした"
+
+#: fe-auth.c:429
+msgid "channel binding required, but SSL not in use\n"
+msgstr "チャネルバインディングが要求されていますが、SSLが使用されていません\n"
+
+#: fe-auth.c:436
+msgid "duplicate SASL authentication request\n"
+msgstr "重複するSASL認証要求\n"
+
+#: fe-auth.c:492
+msgid "channel binding is required, but client does not support it\n"
+msgstr "チャネルバインディングが要求されていますが、クライアントがサポートしていません\n"
+
+#: fe-auth.c:509
+msgid "server offered SCRAM-SHA-256-PLUS authentication over a non-SSL connection\n"
+msgstr "サーバが非SSL接続上で SCRAM-SHA-256-PLUS 認証を提示してきました\n"
+
+#: fe-auth.c:521
+msgid "none of the server's SASL authentication mechanisms are supported\n"
+msgstr "サーバ側のいずれのSASL認証機構もサポートされていません\n"
+
+#: fe-auth.c:529
+msgid "channel binding is required, but server did not offer an authentication method that supports channel binding\n"
+msgstr "チャネルバインディングが要求されていますが、サーバがチャネルバインディングをサポートする認証方式を提供しませんでした\n"
+
+#: fe-auth.c:635
+#, c-format
+msgid "out of memory allocating SASL buffer (%d)\n"
+msgstr "SASLバッファの割り当ての際のメモリ不足(%d)\n"
+
+#: fe-auth.c:660
+msgid "AuthenticationSASLFinal received from server, but SASL authentication was not completed\n"
+msgstr "サーバからAuthenticationSASLFinalを受信しました、しかしSASL認証は完了していません\n"
+
+#: fe-auth.c:737
+msgid "SCM_CRED authentication method not supported\n"
+msgstr "SCM_CRED認証方式はサポートされていません\n"
+
+#: fe-auth.c:836
+msgid "channel binding required, but server authenticated client without channel binding\n"
+msgstr "チャネルバインディングが要求されていますが、サーバはチャネルバインディングを使用せずに認証を行いました\n"
+
+#: fe-auth.c:842
+msgid "channel binding required but not supported by server's authentication request\n"
+msgstr "チャネルバインディングが要求されていますが、サーバの認証要求ではサポートされていません\n"
+
+#: fe-auth.c:875
+msgid "Kerberos 4 authentication not supported\n"
+msgstr "Kerberos 4認証はサポートされていません\n"
+
+#: fe-auth.c:880
+msgid "Kerberos 5 authentication not supported\n"
+msgstr "Kerberos 5認証はサポートされていません\n"
+
+#: fe-auth.c:951
+msgid "GSSAPI authentication not supported\n"
+msgstr "GSSAPI認証はサポートされていません\n"
+
+#: fe-auth.c:983
+msgid "SSPI authentication not supported\n"
+msgstr "SSPI認証はサポートされていません\n"
+
+#: fe-auth.c:991
+msgid "Crypt authentication not supported\n"
+msgstr "Crypt認証はサポートされていません\n"
+
+#: fe-auth.c:1057
+#, c-format
+msgid "authentication method %u not supported\n"
+msgstr "認証方式%uはサポートされていません\n"
+
+#: fe-auth.c:1104
+#, c-format
+msgid "user name lookup failure: error code %lu\n"
+msgstr "ユーザー名の検索に失敗: エラー コード %lu\n"
+
+#: fe-auth.c:1114 fe-connect.c:2838
+#, c-format
+msgid "could not look up local user ID %d: %s\n"
+msgstr "ローカルユーザID%dが見つかりませんでした: %s\n"
+
+#: fe-auth.c:1119 fe-connect.c:2843
+#, c-format
+msgid "local user with ID %d does not exist\n"
+msgstr "ID %d を持つローカルユーザは存在しません\n"
+
+#: fe-auth.c:1221
+msgid "unexpected shape of result set returned for SHOW\n"
+msgstr "SHOW に対する予期しない形のリザルトセット\n"
+
+#: fe-auth.c:1230
+msgid "password_encryption value too long\n"
+msgstr "password_encryptionの値が長すぎます\n"
+
+#: fe-auth.c:1270
+#, c-format
+msgid "unrecognized password encryption algorithm \"%s\"\n"
+msgstr "認識できないパスワード暗号化アルゴリズム \"%s\"\n"
+
+#: fe-connect.c:1075
+#, c-format
+msgid "could not match %d host names to %d hostaddr values\n"
+msgstr "%d個のホスト名と%d個のhostaddrの値との突き合せはできません\n"
+
+#: fe-connect.c:1156
+#, c-format
+msgid "could not match %d port numbers to %d hosts\n"
+msgstr "%d個のポート番号と%d個のホストとの突き合せはできません\n"
+
+#: fe-connect.c:1249 fe-connect.c:1275 fe-connect.c:1317 fe-connect.c:1326
+#: fe-connect.c:1359 fe-connect.c:1404
+#, c-format
+msgid "invalid %s value: \"%s\"\n"
+msgstr "%s の値が不正: \"%s\"\n"
+
+#: fe-connect.c:1296
+#, c-format
+msgid "sslmode value \"%s\" invalid when SSL support is not compiled in\n"
+msgstr "SSLサポートが組み込まれていない場合sslmodeの値\"%s\"は不正です\n"
+
+#: fe-connect.c:1344
+msgid "invalid SSL protocol version range\n"
+msgstr "不正なSSLプロトコルバージョン範囲\n"
+
+#: fe-connect.c:1369
+#, c-format
+msgid "gssencmode value \"%s\" invalid when GSSAPI support is not compiled in\n"
+msgstr "gssencmodeの値\"%s\"はGSSAPIサポートがコンパイルされていない場合は不正\n"
+
+#: fe-connect.c:1623
+#, c-format
+msgid "could not set socket to TCP no delay mode: %s\n"
+msgstr "TCPソケットを非遅延モードに設定できませんでした: %s\n"
+
+#: fe-connect.c:1684
+#, c-format
+msgid ""
+"could not connect to server: %s\n"
+"\tIs the server running locally and accepting\n"
+"\tconnections on Unix domain socket \"%s\"?\n"
+msgstr ""
+"サーバに接続できませんでした: %s\n"
+" ローカルにサーバが稼動していますか?\n"
+" Unixドメインソケット\"%s\"で通信を受け付けていますか?\n"
+
+#: fe-connect.c:1721
+#, c-format
+msgid ""
+"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"
+msgstr ""
+"サーバに接続できませんでした: %s\n"
+"\tサーバはホスト \"%s\" (%s) で稼動しており、\n"
+"\tまた、ポート %s で TCP/IP 接続を受け付けていますか?\n"
+
+#: fe-connect.c:1729
+#, c-format
+msgid ""
+"could not connect to server: %s\n"
+"\tIs the server running on host \"%s\" and accepting\n"
+"\tTCP/IP connections on port %s?\n"
+msgstr ""
+"サーバに接続できませんでした: %s\n"
+"\tサーバはホスト\"%s\"で稼動していますか?\n"
+"\tまた、ポート%sでTCP/IP接続を受け付けていますか?\n"
+
+#: fe-connect.c:1799
+#, c-format
+msgid "invalid integer value \"%s\" for connection option \"%s\"\n"
+msgstr "接続オプション\"%2$s\"に対する不正な整数値\"%1$s\"\n"
+
+#: fe-connect.c:1829 fe-connect.c:1863 fe-connect.c:1898 fe-connect.c:1985
+#: fe-connect.c:2627
+#, c-format
+msgid "setsockopt(%s) failed: %s\n"
+msgstr "setsockopt(%s)が失敗しました: %s\n"
+
+#: fe-connect.c:1951
+#, c-format
+msgid "WSAIoctl(SIO_KEEPALIVE_VALS) failed: %ui\n"
+msgstr "WSAIoctl(SIO_KEEPALIVE_VALS)に失敗しました:%ui\n"
+
+#: fe-connect.c:2317
+msgid "invalid connection state, probably indicative of memory corruption\n"
+msgstr "接続状態が不正です。メモリ障害の可能性があります\n"
+
+#: fe-connect.c:2383
+#, c-format
+msgid "invalid port number: \"%s\"\n"
+msgstr "不正なポート番号です: \"%s\"\n"
+
+#: fe-connect.c:2399
+#, c-format
+msgid "could not translate host name \"%s\" to address: %s\n"
+msgstr "ホスト名\"%s\"をアドレスに変換できませんでした: %s\n"
+
+#: fe-connect.c:2412
+#, c-format
+msgid "could not parse network address \"%s\": %s\n"
+msgstr "ネットワークアドレス\"%s\"をパースできませんでした: %s\n"
+
+#: fe-connect.c:2425
+#, c-format
+msgid "Unix-domain socket path \"%s\" is too long (maximum %d bytes)\n"
+msgstr "Unixドメインソケットのパス\"%s\"が長すぎます(最大 %d バイト)\n"
+
+#: fe-connect.c:2440
+#, c-format
+msgid "could not translate Unix-domain socket path \"%s\" to address: %s\n"
+msgstr "Unixドメインソケットのパス\"%s\"をアドレスに変換できませんでした: %s\n"
+
+#: fe-connect.c:2564
+#, c-format
+msgid "could not create socket: %s\n"
+msgstr "ソケットを作成できませんでした: %s\n"
+
+#: fe-connect.c:2586
+#, c-format
+msgid "could not set socket to nonblocking mode: %s\n"
+msgstr "ソケットを非ブロッキングモードに設定できませんでした: %s\n"
+
+#: fe-connect.c:2596
+#, c-format
+msgid "could not set socket to close-on-exec mode: %s\n"
+msgstr "ソケットをclose-on-execモードに設定できませんでした: %s\n"
+
+#: fe-connect.c:2614
+msgid "keepalives parameter must be an integer\n"
+msgstr "keepaliveのパラメータは整数でなければなりません\n"
+
+#: fe-connect.c:2754
+#, c-format
+msgid "could not get socket error status: %s\n"
+msgstr "ソケットのエラー状態を入手できませんでした: %s\n"
+
+#: fe-connect.c:2782
+#, c-format
+msgid "could not get client address from socket: %s\n"
+msgstr "ソケットからクライアントアドレスを入手できませんでした: %s\n"
+
+#: fe-connect.c:2824
+msgid "requirepeer parameter is not supported on this platform\n"
+msgstr "このプラットフォームでは requirepeer パラメータはサポートされていません\n"
+
+#: fe-connect.c:2827
+#, c-format
+msgid "could not get peer credentials: %s\n"
+msgstr "ピアの資格証明を入手できませんでした: %s\n"
+
+#: fe-connect.c:2851
+#, c-format
+msgid "requirepeer specifies \"%s\", but actual peer user name is \"%s\"\n"
+msgstr "requirepeerは\"%s\"を指定していますが、実際のピア名は\"%s\"です\n"
+
+#: fe-connect.c:2891
+#, c-format
+msgid "could not send GSSAPI negotiation packet: %s\n"
+msgstr "GSSAPIネゴシエーションパケットを送信できませんでした: %s\n"
+
+#: fe-connect.c:2903
+msgid "GSSAPI encryption required but was impossible (possibly no credential cache, no server support, or using a local socket)\n"
+msgstr "GSSAPI暗号化が要求されていますが、実行できませんでした(おそらく資格キャッシュがない、サーバがサポートしていないあるいはローカルソケットで接続しています)\n"
+
+#: fe-connect.c:2930
+#, c-format
+msgid "could not send SSL negotiation packet: %s\n"
+msgstr "SSLネゴシエーションパケットを送信できませんでした: %s\n"
+
+#: fe-connect.c:2969
+#, c-format
+msgid "could not send startup packet: %s\n"
+msgstr "開始パケットを送信できませんでした: %s\n"
+
+#: fe-connect.c:3039
+msgid "server does not support SSL, but SSL was required\n"
+msgstr "サーバはSSLをサポートしていませんが、SSLが要求されました\n"
+
+#: fe-connect.c:3065
+#, c-format
+msgid "received invalid response to SSL negotiation: %c\n"
+msgstr "SSLネゴシエーションに対して不正な応答を受信しました: %c\n"
+
+#: fe-connect.c:3155
+msgid "server doesn't support GSSAPI encryption, but it was required\n"
+msgstr "サーバはGSSAPI暗号化をサポートしていませんが、要求されました\n"
+
+#: fe-connect.c:3166
+#, c-format
+msgid "received invalid response to GSSAPI negotiation: %c\n"
+msgstr "GSSAPIネゴシエーションに対して不正な応答を受信しました: %c\n"
+
+#: fe-connect.c:3233 fe-connect.c:3264
+#, c-format
+msgid "expected authentication request from server, but received %c\n"
+msgstr "サーバからの認証要求を想定していましたが、%cを受信しました\n"
+
+#: fe-connect.c:3506
+msgid "unexpected message from server during startup\n"
+msgstr "起動時にサーバから想定外のメッセージがありました\n"
+
+#: fe-connect.c:3711
+#, c-format
+msgid "could not make a writable connection to server \"%s:%s\"\n"
+msgstr "サーバ\"%s:%s\"への書き込み可能な接続を確立できませんでした\n"
+
+#: fe-connect.c:3757
+#, c-format
+msgid "test \"SHOW transaction_read_only\" failed on server \"%s:%s\"\n"
+msgstr "サーバ\"%s:%s\"で\"SHOW transaction_read_only\"のテストに失敗しました\n"
+
+#: fe-connect.c:3772
+#, c-format
+msgid "invalid connection state %d, probably indicative of memory corruption\n"
+msgstr "接続状態%dが不正です。メモリ障害の可能性があります\n"
+
+#: fe-connect.c:4208 fe-connect.c:4268
+#, c-format
+msgid "PGEventProc \"%s\" failed during PGEVT_CONNRESET event\n"
+msgstr "PGEVT_CONNRESETイベント中にPGEventProc \"%s\"に失敗しました\n"
+
+#: fe-connect.c:4615
+#, c-format
+msgid "invalid LDAP URL \"%s\": scheme must be ldap://\n"
+msgstr "不正なLDAP URL\"%s\":スキーマはldap://でなければなりません\n"
+
+#: fe-connect.c:4630
+#, c-format
+msgid "invalid LDAP URL \"%s\": missing distinguished name\n"
+msgstr "不正なLDAP URL \"%s\": 区別名がありません\n"
+
+#: fe-connect.c:4642 fe-connect.c:4697
+#, c-format
+msgid "invalid LDAP URL \"%s\": must have exactly one attribute\n"
+msgstr "不正なLDAP URL \"%s\": 正確に1つの属性を持たなければなりません\n"
+
+#: fe-connect.c:4653 fe-connect.c:4712
+#, c-format
+msgid "invalid LDAP URL \"%s\": must have search scope (base/one/sub)\n"
+msgstr "不正なLDAP URL \"%s\": 検索スコープ(base/one/sub)を持たなければなりません\n"
+
+#: fe-connect.c:4664
+#, c-format
+msgid "invalid LDAP URL \"%s\": no filter\n"
+msgstr "不正なLDAP URL \"%s\": フィルタがありません\n"
+
+#: fe-connect.c:4685
+#, c-format
+msgid "invalid LDAP URL \"%s\": invalid port number\n"
+msgstr "不正なLDAP URL \"%s\": ポート番号が不正です\n"
+
+#: fe-connect.c:4721
+msgid "could not create LDAP structure\n"
+msgstr "LDAP構造体を作成できませんでした\n"
+
+#: fe-connect.c:4797
+#, c-format
+msgid "lookup on LDAP server failed: %s\n"
+msgstr "LDAPサーバで検索に失敗しました: %s\n"
+
+#: fe-connect.c:4808
+msgid "more than one entry found on LDAP lookup\n"
+msgstr "LDAP検索結果が複数ありました\n"
+
+#: fe-connect.c:4809 fe-connect.c:4821
+msgid "no entry found on LDAP lookup\n"
+msgstr "LDAP検索結果が空でした\n"
+
+#: fe-connect.c:4832 fe-connect.c:4845
+msgid "attribute has no values on LDAP lookup\n"
+msgstr "LDAP検索で属性に値がありませんでした\n"
+
+#: fe-connect.c:4897 fe-connect.c:4916 fe-connect.c:5448
+#, c-format
+msgid "missing \"=\" after \"%s\" in connection info string\n"
+msgstr "接続情報文字列において\"%s\"の後に\"=\"がありませんでした\n"
+
+#: fe-connect.c:4989 fe-connect.c:5633 fe-connect.c:6407
+#, c-format
+msgid "invalid connection option \"%s\"\n"
+msgstr "接続オプション\"%s\"は不正です\n"
+
+#: fe-connect.c:5005 fe-connect.c:5497
+msgid "unterminated quoted string in connection info string\n"
+msgstr "接続情報文字列において閉じていない引用符がありました\n"
+
+#: fe-connect.c:5088
+#, c-format
+msgid "definition of service \"%s\" not found\n"
+msgstr "サービス定義\"%s\"がみつかりません\n"
+
+#: fe-connect.c:5111
+#, c-format
+msgid "service file \"%s\" not found\n"
+msgstr "サービスファイル\"%s\"がみつかりません\n"
+
+#: fe-connect.c:5126
+#, c-format
+msgid "line %d too long in service file \"%s\"\n"
+msgstr "サービスファイル\"%2$s\"の行%1$dが長すぎます。\n"
+
+#: fe-connect.c:5198 fe-connect.c:5242
+#, c-format
+msgid "syntax error in service file \"%s\", line %d\n"
+msgstr "サービスファイル\"%s\"の行%dに構文エラーがあります\n"
+
+#: fe-connect.c:5209
+#, c-format
+msgid "nested service specifications not supported in service file \"%s\", line %d\n"
+msgstr "サービスファイル\"%s\"ではネストしたサービス指定はサポートされていません、行%d\n"
+
+#: fe-connect.c:5929
+#, c-format
+msgid "invalid URI propagated to internal parser routine: \"%s\"\n"
+msgstr "内部パーサ処理へ伝わった不正なURI: \"%s\"\n"
+
+#: fe-connect.c:6006
+#, c-format
+msgid "end of string reached when looking for matching \"]\" in IPv6 host address in URI: \"%s\"\n"
+msgstr "URI \"%s\"内のIPv6ホストアドレスにおいて対応する\"]\"を探している間に文字列が終わりました\n"
+
+#: fe-connect.c:6013
+#, c-format
+msgid "IPv6 host address may not be empty in URI: \"%s\"\n"
+msgstr "URI \"%s\"内のIPv6ホストアドレスが空である可能性があります\n"
+
+#: fe-connect.c:6028
+#, c-format
+msgid "unexpected character \"%c\" at position %d in URI (expected \":\" or \"/\"): \"%s\"\n"
+msgstr "URI(\":\"と\"/\"を除く)内の位置%2$dに想定外の\"%1$c\"文字があります: \"%3$s\"\n"
+
+#: fe-connect.c:6157
+#, c-format
+msgid "extra key/value separator \"=\" in URI query parameter: \"%s\"\n"
+msgstr "URI問い合わせパラメータ内に余分なキーと値を分ける\"=\"があります: \"%s\"\n"
+
+#: fe-connect.c:6177
+#, c-format
+msgid "missing key/value separator \"=\" in URI query parameter: \"%s\"\n"
+msgstr "URI問い合わせパラメータ内にキーと値を分ける\\\"=\\\"がありません: \"%s\"\n"
+
+#: fe-connect.c:6228
+#, c-format
+msgid "invalid URI query parameter: \"%s\"\n"
+msgstr "不正なURI問い合わせパラメータ:\"%s\"\n"
+
+#: fe-connect.c:6302
+#, c-format
+msgid "invalid percent-encoded token: \"%s\"\n"
+msgstr "不正なパーセント符号化トークン: \"%s\"\n"
+
+#: fe-connect.c:6312
+#, c-format
+msgid "forbidden value %%00 in percent-encoded value: \"%s\"\n"
+msgstr "パーセント符号化された値では%%00値は許されません: \"%s\"\n"
+
+#: fe-connect.c:6675
+msgid "connection pointer is NULL\n"
+msgstr "接続ポインタはNULLです\n"
+
+#: fe-connect.c:6974
+#, c-format
+msgid "WARNING: password file \"%s\" is not a plain file\n"
+msgstr "WARNING: パスワードファイル\"%s\"がテキストファイルではありません\n"
+
+#: fe-connect.c:6983
+#, c-format
+msgid "WARNING: password file \"%s\" has group or world access; permissions should be u=rw (0600) or less\n"
+msgstr "警告: パスワードファイル \"%s\" がグループメンバもしくは他のユーザから読める状態になっています。この権限はu=rw (0600)以下にすべきです\n"
+
+#: fe-connect.c:7024
+#, c-format
+msgid "WARNING: line %d too long in password file \"%s\"\n"
+msgstr "警告: パスワードファイル\"%2$s\"中の行%1$dが長すぎます\n"
+
+#: fe-connect.c:7103
+#, c-format
+msgid "password retrieved from file \"%s\"\n"
+msgstr "ファイル\"%s\"からパスワードを読み込みました\n"
+
+#: fe-exec.c:444 fe-exec.c:2821
+#, c-format
+msgid "row number %d is out of range 0..%d"
+msgstr "行番号%dは0..%dの範囲を超えています"
+
+#: fe-exec.c:505 fe-protocol2.c:497 fe-protocol2.c:532 fe-protocol2.c:1050
+#: fe-protocol3.c:206 fe-protocol3.c:233 fe-protocol3.c:250 fe-protocol3.c:330
+#: fe-protocol3.c:723 fe-protocol3.c:954
+msgid "out of memory"
+msgstr "メモリ不足です"
+
+#: fe-exec.c:506 fe-protocol2.c:1396 fe-protocol3.c:1907
+#, c-format
+msgid "%s"
+msgstr "%s"
+
+#: fe-exec.c:815
+msgid "write to server failed\n"
+msgstr "サーバへの書き込みに失敗\n"
+
+#: fe-exec.c:896
+msgid "NOTICE"
+msgstr "注意"
+
+#: fe-exec.c:954
+msgid "PGresult cannot support more than INT_MAX tuples"
+msgstr "PGresultはINT_MAX個以上のタプルを扱えません"
+
+#: fe-exec.c:966
+msgid "size_t overflow"
+msgstr "size_t オーバーフロー"
+
+#: fe-exec.c:1243 fe-exec.c:1301 fe-exec.c:1347
+msgid "command string is a null pointer\n"
+msgstr "コマンド文字列がヌルポインタです\n"
+
+#: fe-exec.c:1307 fe-exec.c:1353 fe-exec.c:1448
+msgid "number of parameters must be between 0 and 65535\n"
+msgstr "パラメータ数は0から65535まででなければなりません\n"
+
+#: fe-exec.c:1341 fe-exec.c:1442
+msgid "statement name is a null pointer\n"
+msgstr "文の名前がヌルポインタです\n"
+
+#: fe-exec.c:1361 fe-exec.c:1524 fe-exec.c:2233 fe-exec.c:2435
+msgid "function requires at least protocol version 3.0\n"
+msgstr "関数は少なくともプロトコルバージョン3.0が必要です\n"
+
+#: fe-exec.c:1479
+msgid "no connection to the server\n"
+msgstr "サーバへの接続がありません\n"
+
+#: fe-exec.c:1486
+msgid "another command is already in progress\n"
+msgstr "他のコマンドを処理しています\n"
+
+#: fe-exec.c:1600
+msgid "length must be given for binary parameter\n"
+msgstr "バイナリパラメータには長さを指定しなければなりません\n"
+
+#: fe-exec.c:1863
+#, c-format
+msgid "unexpected asyncStatus: %d\n"
+msgstr "想定外のasyncStatus: %d\n"
+
+#: fe-exec.c:1883
+#, c-format
+msgid "PGEventProc \"%s\" failed during PGEVT_RESULTCREATE event\n"
+msgstr "PGEVT_RESULTCREATEイベント中にPGEventProc \"%s\"に失敗しました\n"
+
+#: fe-exec.c:2043
+msgid "COPY terminated by new PQexec"
+msgstr "新たなPQexec\"によりCOPYが終了しました"
+
+#: fe-exec.c:2051
+msgid "COPY IN state must be terminated first\n"
+msgstr "まずCOPY IN状態を終了させなければなりません\n"
+
+#: fe-exec.c:2071
+msgid "COPY OUT state must be terminated first\n"
+msgstr "まずCOPY OUT状態を終了させなければなりません\n"
+
+#: fe-exec.c:2079
+msgid "PQexec not allowed during COPY BOTH\n"
+msgstr "COPY BOTH 実行中の PQexec は許可されていません\n"
+
+#: fe-exec.c:2325 fe-exec.c:2392 fe-exec.c:2482 fe-protocol2.c:1353
+#: fe-protocol3.c:1838
+msgid "no COPY in progress\n"
+msgstr "実行中のCOPYはありません\n"
+
+#: fe-exec.c:2672
+msgid "connection in wrong state\n"
+msgstr "接続状態が異常です\n"
+
+#: fe-exec.c:2703
+msgid "invalid ExecStatusType code"
+msgstr "ExecStatusTypeコードが不正です"
+
+#: fe-exec.c:2730
+msgid "PGresult is not an error result\n"
+msgstr "PGresutがエラー結果ではありません\n"
+
+#: fe-exec.c:2805 fe-exec.c:2828
+#, c-format
+msgid "column number %d is out of range 0..%d"
+msgstr "列番号%dは0..%dの範囲を超えています"
+
+#: fe-exec.c:2843
+#, c-format
+msgid "parameter number %d is out of range 0..%d"
+msgstr "パラメータ%dは0..%dの範囲を超えています"
+
+#: fe-exec.c:3153
+#, c-format
+msgid "could not interpret result from server: %s"
+msgstr "サーバからの結果を解釈できませんでした: %s"
+
+#: fe-exec.c:3392 fe-exec.c:3476
+msgid "incomplete multibyte character\n"
+msgstr "不完全なマルチバイト文字\n"
+
+#: fe-gssapi-common.c:124
+msgid "GSSAPI name import error"
+msgstr "GSSAPI名のインポートエラー"
+
+#: fe-lobj.c:154
+msgid "cannot determine OID of function lo_truncate\n"
+msgstr "lo_truncate関数のOIDを決定できません\n"
+
+#: fe-lobj.c:170
+msgid "argument of lo_truncate exceeds integer range\n"
+msgstr "lo_truncateへの引数が整数範囲を超えています。\n"
+
+#: fe-lobj.c:221
+msgid "cannot determine OID of function lo_truncate64\n"
+msgstr "lo_truncate64関数のOIDを決定できません\n"
+
+#: fe-lobj.c:279
+msgid "argument of lo_read exceeds integer range\n"
+msgstr "lo_readへの引数が整数範囲を超えています。\n"
+
+#: fe-lobj.c:334
+msgid "argument of lo_write exceeds integer range\n"
+msgstr "lo_writeへの引数が整数範囲を超えています。\n"
+
+#: fe-lobj.c:425
+msgid "cannot determine OID of function lo_lseek64\n"
+msgstr "lo_lseek64関数のOIDを決定できません\n"
+
+#: fe-lobj.c:521
+msgid "cannot determine OID of function lo_create\n"
+msgstr "lo_create関数のOIDを決定できません\n"
+
+#: fe-lobj.c:600
+msgid "cannot determine OID of function lo_tell64\n"
+msgstr "lo_tell64関数のOIDを決定できません\n"
+
+#: fe-lobj.c:706 fe-lobj.c:815
+#, c-format
+msgid "could not open file \"%s\": %s\n"
+msgstr "ファイル\"%s\"をオープンできませんでした: %s\n"
+
+#: fe-lobj.c:761
+#, c-format
+msgid "could not read from file \"%s\": %s\n"
+msgstr "ファイル\"%s\"を読み込めませんでした: %s\n"
+
+#: fe-lobj.c:835 fe-lobj.c:859
+#, c-format
+msgid "could not write to file \"%s\": %s\n"
+msgstr "ファイル\"%s\"に書き込めませんでした: %s\n"
+
+#: fe-lobj.c:946
+msgid "query to initialize large object functions did not return data\n"
+msgstr "ラージオブジェクト機能を初期化する問い合わせがデータを返しませんでした\n"
+
+#: fe-lobj.c:995
+msgid "cannot determine OID of function lo_open\n"
+msgstr "lo_open関数のOIDを決定できません\n"
+
+#: fe-lobj.c:1002
+msgid "cannot determine OID of function lo_close\n"
+msgstr "lo_close関数のOIDを決定できません\n"
+
+#: fe-lobj.c:1009
+msgid "cannot determine OID of function lo_creat\n"
+msgstr "lo_creat関数のOIDを決定できません\n"
+
+#: fe-lobj.c:1016
+msgid "cannot determine OID of function lo_unlink\n"
+msgstr "lo_unlink関数のOIDを決定できません\n"
+
+#: fe-lobj.c:1023
+msgid "cannot determine OID of function lo_lseek\n"
+msgstr "lo_lseek関数のOIDを決定できません\n"
+
+#: fe-lobj.c:1030
+msgid "cannot determine OID of function lo_tell\n"
+msgstr "lo_tell関数のOIDを決定できません\n"
+
+#: fe-lobj.c:1037
+msgid "cannot determine OID of function loread\n"
+msgstr "loread関数のOIDを決定できません\n"
+
+#: fe-lobj.c:1044
+msgid "cannot determine OID of function lowrite\n"
+msgstr "lowrite関数のOIDを決定できません\n"
+
+#: fe-misc.c:276
+#, c-format
+msgid "integer of size %lu not supported by pqGetInt"
+msgstr "サイズ%luの整数はpqGetIntでサポートされていません"
+
+#: fe-misc.c:312
+#, c-format
+msgid "integer of size %lu not supported by pqPutInt"
+msgstr "サイズ%luの整数はpqPutIntでサポートされていません"
+
+#: fe-misc.c:623 fe-misc.c:856
+msgid "connection not open\n"
+msgstr "接続はオープンされていません\n"
+
+#: fe-misc.c:792 fe-secure-openssl.c:209 fe-secure-openssl.c:316
+#: fe-secure.c:267 fe-secure.c:383
+msgid ""
+"server closed the connection unexpectedly\n"
+"\tThis probably means the server terminated abnormally\n"
+"\tbefore or while processing the request.\n"
+msgstr ""
+"サーバとの接続が想定外にクローズされました\n"
+" おそらく要求の処理前または処理中にサーバが異常終了\n"
+" したことを意味しています。\n"
+
+#: fe-misc.c:1050
+msgid "timeout expired\n"
+msgstr "タイムアウト期間が過ぎました\n"
+
+#: fe-misc.c:1095
+msgid "invalid socket\n"
+msgstr "不正なソケットです\n"
+
+#: fe-misc.c:1118
+#, c-format
+msgid "select() failed: %s\n"
+msgstr "select()が失敗しました: %s\n"
+
+#: fe-protocol2.c:87
+#, c-format
+msgid "invalid setenv state %c, probably indicative of memory corruption\n"
+msgstr "setenv状態%cは不正です。メモリ障害の可能性があります\n"
+
+#: fe-protocol2.c:384
+#, c-format
+msgid "invalid state %c, probably indicative of memory corruption\n"
+msgstr "状態%cは不正です。メモリ障害の可能性があります\n"
+
+#: fe-protocol2.c:473 fe-protocol3.c:183
+#, c-format
+msgid "message type 0x%02x arrived from server while idle"
+msgstr "待機中にサーバからメッセージ種類0x%02xが届きました"
+
+#: fe-protocol2.c:523
+#, c-format
+msgid "unexpected character %c following empty query response (\"I\" message)"
+msgstr "空の問い合わせ応答(\"I\"メッセージ)の後に想定外の文字%cがありました"
+
+#: fe-protocol2.c:589
+#, c-format
+msgid "server sent data (\"D\" message) without prior row description (\"T\" message)"
+msgstr "サーバが事前の行記述(\"T\"メッセージ)なしにデータ(\"D\"メッセージ)を送信しました"
+
+#: fe-protocol2.c:607
+#, c-format
+msgid "server sent binary data (\"B\" message) without prior row description (\"T\" message)"
+msgstr "サーバが事前の行記述(\"T\"メッセージ)なしにバイナリデータ(\"B\"メッセージ)を送信しました"
+
+#: fe-protocol2.c:626 fe-protocol3.c:408
+#, c-format
+msgid "unexpected response from server; first received character was \"%c\"\n"
+msgstr "サーバから想定外の応答がありました。受け付けた先頭文字は\"%c\"です\n"
+
+#: fe-protocol2.c:755 fe-protocol2.c:930 fe-protocol3.c:622 fe-protocol3.c:849
+msgid "out of memory for query result"
+msgstr "問い合わせ結果用のメモリが不足しています"
+
+#: fe-protocol2.c:1408
+#, c-format
+msgid "lost synchronization with server, resetting connection"
+msgstr "サーバとの動機が失われました。接続をリセットしています"
+
+#: fe-protocol2.c:1530 fe-protocol2.c:1562 fe-protocol3.c:2095
+#, c-format
+msgid "protocol error: id=0x%x\n"
+msgstr "プロトコルエラー: id=0x%x\n"
+
+#: fe-protocol3.c:365
+msgid "server sent data (\"D\" message) without prior row description (\"T\" message)\n"
+msgstr "サーバが事前の行記述(\"T\"メッセージ)なしにデータ(\"D\"メッセージ)を送信しました\"\n"
+
+#: fe-protocol3.c:429
+#, c-format
+msgid "message contents do not agree with length in message type \"%c\"\n"
+msgstr "メッセージの内容がメッセージ種類\"%c\"の長さに合いません\n"
+
+#: fe-protocol3.c:449
+#, c-format
+msgid "lost synchronization with server: got message type \"%c\", length %d\n"
+msgstr "サーバとの同期が失われました。受信したメッセージ種類は\"%c\"、長さは%d\n"
+
+#: fe-protocol3.c:500 fe-protocol3.c:540
+msgid "insufficient data in \"T\" message"
+msgstr "\"T\"メッセージ内のデータが不十分です"
+
+#: fe-protocol3.c:573
+msgid "extraneous data in \"T\" message"
+msgstr "\"T\"メッセージ内に無関係なデータがあります"
+
+#: fe-protocol3.c:686
+msgid "extraneous data in \"t\" message"
+msgstr "\"t\"メッセージ内に無関係なデータがあります"
+
+#: fe-protocol3.c:757 fe-protocol3.c:789 fe-protocol3.c:807
+msgid "insufficient data in \"D\" message"
+msgstr "\"D\"\"メッセージ内のデータが不十分です"
+
+#: fe-protocol3.c:763
+msgid "unexpected field count in \"D\" message"
+msgstr "\"D\"メッセージ内のフィールド数が想定外です。"
+
+#: fe-protocol3.c:816
+msgid "extraneous data in \"D\" message"
+msgstr "”D\"メッセージ内に無関係なデータがあります"
+
+#: fe-protocol3.c:1008
+msgid "no error message available\n"
+msgstr "エラーメッセージがありません\n"
+
+#. translator: %s represents a digit string
+#: fe-protocol3.c:1056 fe-protocol3.c:1075
+#, c-format
+msgid " at character %s"
+msgstr "(文字位置: %s)"
+
+#: fe-protocol3.c:1088
+#, c-format
+msgid "DETAIL: %s\n"
+msgstr "DETAIL: %s\n"
+
+#: fe-protocol3.c:1091
+#, c-format
+msgid "HINT: %s\n"
+msgstr "HINT: %s\n"
+
+#: fe-protocol3.c:1094
+#, c-format
+msgid "QUERY: %s\n"
+msgstr "QUERY: %s\n"
+
+#: fe-protocol3.c:1101
+#, c-format
+msgid "CONTEXT: %s\n"
+msgstr "CONTEXT: %s\n"
+
+#: fe-protocol3.c:1110
+#, c-format
+msgid "SCHEMA NAME: %s\n"
+msgstr "SCHEMA NAME: %s\n"
+
+#: fe-protocol3.c:1114
+#, c-format
+msgid "TABLE NAME: %s\n"
+msgstr "TABLE NAME: %s\n"
+
+#: fe-protocol3.c:1118
+#, c-format
+msgid "COLUMN NAME: %s\n"
+msgstr "COLUMN NAME: %s\n"
+
+#: fe-protocol3.c:1122
+#, c-format
+msgid "DATATYPE NAME: %s\n"
+msgstr "DATATYPE NAME: %s\n"
+
+#: fe-protocol3.c:1126
+#, c-format
+msgid "CONSTRAINT NAME: %s\n"
+msgstr "CONSTRAINT NAME: %s\n"
+
+#: fe-protocol3.c:1138
+msgid "LOCATION: "
+msgstr "LOCATION: "
+
+#: fe-protocol3.c:1140
+#, c-format
+msgid "%s, "
+msgstr "%s, "
+
+#: fe-protocol3.c:1142
+#, c-format
+msgid "%s:%s"
+msgstr "%s:%s"
+
+#: fe-protocol3.c:1337
+#, c-format
+msgid "LINE %d: "
+msgstr "行 %d: "
+
+#: fe-protocol3.c:1732
+msgid "PQgetline: not doing text COPY OUT\n"
+msgstr "PQgetline: テキストのCOPY OUTを行っていません\n"
+
+#: fe-secure-common.c:124
+msgid "SSL certificate's name contains embedded null\n"
+msgstr "SSL証明書の名前の途中にnullが含まれています\n"
+
+#: fe-secure-common.c:171
+msgid "host name must be specified for a verified SSL connection\n"
+msgstr "SSL 接続を検証するためにホスト名を指定しなければなりません\n"
+
+#: fe-secure-common.c:196
+#, c-format
+msgid "server certificate for \"%s\" does not match host name \"%s\"\n"
+msgstr "\"%s\"のサーバ証明書がホスト名\"%s\"とマッチしません\n"
+
+#: fe-secure-common.c:202
+msgid "could not get server's host name from server certificate\n"
+msgstr "サーバ証明書からサーバのホスト名を取り出すことができませんでした。\n"
+
+#: fe-secure-gssapi.c:201
+msgid "GSSAPI wrap error"
+msgstr "GSSAPI名ラップエラー"
+
+#: fe-secure-gssapi.c:209
+msgid "outgoing GSSAPI message would not use confidentiality\n"
+msgstr "送出されるGSSAPIメッセージは機密性を使用しません\n"
+
+#: fe-secure-gssapi.c:217
+#, c-format
+msgid "client tried to send oversize GSSAPI packet (%zu > %zu)\n"
+msgstr "クライアントは過大なGSSAPIパケットを送信しようとしました: (%zu > %zu)\n"
+
+#: fe-secure-gssapi.c:354 fe-secure-gssapi.c:596
+#, c-format
+msgid "oversize GSSAPI packet sent by the server (%zu > %zu)\n"
+msgstr "過大なGSSAPIパケットがサーバから送出されました: (%zu > %zu)\n"
+
+#: fe-secure-gssapi.c:393
+msgid "GSSAPI unwrap error"
+msgstr "GSSAPIアンラップエラー"
+
+#: fe-secure-gssapi.c:403
+msgid "incoming GSSAPI message did not use confidentiality\n"
+msgstr "受信したGSSAPIパケットは機密性を使用していませんでした\n"
+
+#: fe-secure-gssapi.c:642
+msgid "could not initiate GSSAPI security context"
+msgstr "GSSAPIセキュリティコンテキストを初期化できませんでした"
+
+#: fe-secure-gssapi.c:673
+msgid "GSSAPI size check error"
+msgstr "GSSAPIサイズチェックエラー"
+
+#: fe-secure-gssapi.c:684
+msgid "GSSAPI context establishment error"
+msgstr "GSSAPIコンテクスト確立エラー"
+
+#: fe-secure-openssl.c:214 fe-secure-openssl.c:321 fe-secure-openssl.c:1291
+#, c-format
+msgid "SSL SYSCALL error: %s\n"
+msgstr "SSL SYSCALLエラー: %s\n"
+
+#: fe-secure-openssl.c:221 fe-secure-openssl.c:328 fe-secure-openssl.c:1295
+msgid "SSL SYSCALL error: EOF detected\n"
+msgstr "SSL SYSCALLエラー: EOFを検知\n"
+
+#: fe-secure-openssl.c:232 fe-secure-openssl.c:339 fe-secure-openssl.c:1304
+#, c-format
+msgid "SSL error: %s\n"
+msgstr "SSLエラー: %s\n"
+
+#: fe-secure-openssl.c:247 fe-secure-openssl.c:354
+msgid "SSL connection has been closed unexpectedly\n"
+msgstr "SSL接続が意図せずにクローズされました\n"
+
+#: fe-secure-openssl.c:253 fe-secure-openssl.c:360 fe-secure-openssl.c:1354
+#, c-format
+msgid "unrecognized SSL error code: %d\n"
+msgstr "不明のSSLエラーコード: %d\n"
+
+#: fe-secure-openssl.c:400
+msgid "could not determine server certificate signature algorithm\n"
+msgstr "サーバ証明書の署名アルゴリズムを決定できませんでした\n"
+
+#: fe-secure-openssl.c:421
+#, c-format
+msgid "could not find digest for NID %s\n"
+msgstr "NID %sのダイジェストが見つかりませんでした\n"
+
+#: fe-secure-openssl.c:431
+msgid "could not generate peer certificate hash\n"
+msgstr "ピアの証明書ハッシュの生成に失敗しました\n"
+
+#: fe-secure-openssl.c:488
+msgid "SSL certificate's name entry is missing\n"
+msgstr "SSL証明書に名前の項目がありません\n"
+
+#: fe-secure-openssl.c:815
+#, c-format
+msgid "could not create SSL context: %s\n"
+msgstr "SSLコンテキストを作成できませんでした: %s\n"
+
+#: fe-secure-openssl.c:854
+#, c-format
+msgid "invalid value \"%s\" for minimum SSL protocol version\n"
+msgstr "不正なSSLプロトコル最小バージョンの値\"%s\"\n"
+
+#: fe-secure-openssl.c:865
+#, c-format
+msgid "could not set minimum SSL protocol version: %s\n"
+msgstr "SSLプロトコル最小バージョンを設定できませんでした: %s\n"
+
+#: fe-secure-openssl.c:883
+#, c-format
+msgid "invalid value \"%s\" for maximum SSL protocol version\n"
+msgstr "不正なSSLプロトコル最大バージョンの値\"%s\"\n"
+
+#: fe-secure-openssl.c:894
+#, c-format
+msgid "could not set maximum SSL protocol version: %s\n"
+msgstr "SSLプロトコル最大バージョンを設定できませんでした: %s\n"
+
+#: fe-secure-openssl.c:930
+#, c-format
+msgid "could not read root certificate file \"%s\": %s\n"
+msgstr "ルート証明書\"%s\"を読み取れませんでした: %s\n"
+
+#: fe-secure-openssl.c:974
+msgid ""
+"could not get home directory to locate root certificate file\n"
+"Either provide the file or change sslmode to disable server certificate verification.\n"
+msgstr ""
+"ルート証明書ファイルを置くためのホームディレクトリが存在しません。\n"
+"ファイルを用意するか、サーバ証明書の検証を無効にするように sslmode を変更してください\n"
+
+#: fe-secure-openssl.c:978
+#, c-format
+msgid ""
+"root certificate file \"%s\" does not exist\n"
+"Either provide the file or change sslmode to disable server certificate verification.\n"
+msgstr ""
+"ルート証明書ファイル\"%s\"が存在しません。\n"
+"ファイルを用意するかサーバ証明書の検証を無効にするようにsslmodeを変更してください\n"
+
+#: fe-secure-openssl.c:1009
+#, c-format
+msgid "could not open certificate file \"%s\": %s\n"
+msgstr "証明書ファイル\"%s\"をオープンできませんでした: %s\n"
+
+#: fe-secure-openssl.c:1028
+#, c-format
+msgid "could not read certificate file \"%s\": %s\n"
+msgstr "証明書ファイル\"%s\"を読み込めませんでした: %s\n"
+
+#: fe-secure-openssl.c:1053
+#, c-format
+msgid "could not establish SSL connection: %s\n"
+msgstr "SSL接続を確立できませんでした: %s\n"
+
+#: fe-secure-openssl.c:1107
+#, c-format
+msgid "could not load SSL engine \"%s\": %s\n"
+msgstr "SSLエンジン\"%s\"を読み込みできませんでした: %s\n"
+
+#: fe-secure-openssl.c:1119
+#, c-format
+msgid "could not initialize SSL engine \"%s\": %s\n"
+msgstr "SSLエンジン\"%s\"を初期化できませんでした: %s\n"
+
+#: fe-secure-openssl.c:1135
+#, c-format
+msgid "could not read private SSL key \"%s\" from engine \"%s\": %s\n"
+msgstr "SSL秘密キーファイル\"%s\"をエンジン\"%s\"から読み取れませんでした: %s\n"
+
+#: fe-secure-openssl.c:1149
+#, c-format
+msgid "could not load private SSL key \"%s\" from engine \"%s\": %s\n"
+msgstr "SSL秘密キー\"%s\"をエンジン\"%s\"から読み取れませんでした: %s\n"
+
+#: fe-secure-openssl.c:1186
+#, c-format
+msgid "certificate present, but not private key file \"%s\"\n"
+msgstr "証明書はありましたが、秘密キーファイル\"%s\"はありませんでした\n"
+
+#: fe-secure-openssl.c:1194
+#, c-format
+msgid "private key file \"%s\" has group or world access; permissions should be u=rw (0600) or less\n"
+msgstr "警告:秘密キーファイル \"%s\" がグループメンバや第三者から読める状態になっています。この権限はu=rw (0600)またはそれ以下とすべきです\n"
+
+#: fe-secure-openssl.c:1219
+#, c-format
+msgid "could not load private key file \"%s\": %s\n"
+msgstr "秘密キーファイル\"%s\"をロードできませんでした: %s\n"
+
+#: fe-secure-openssl.c:1237
+#, c-format
+msgid "certificate does not match private key file \"%s\": %s\n"
+msgstr "証明書と秘密キーファイル\"%s\"が一致しません: %s\n"
+
+#: fe-secure-openssl.c:1337
+#, c-format
+msgid "This may indicate that the server does not support any SSL protocol version between %s and %s.\n"
+msgstr "このことは、クライアントがSSLプロトコルのバージョン%sから%sの間のいずれもサポートしていないことを示唆しているかもしれません。\n"
+
+#: fe-secure-openssl.c:1373
+#, c-format
+msgid "certificate could not be obtained: %s\n"
+msgstr "証明書を入手できませんでした: %s\n"
+
+#: fe-secure-openssl.c:1462
+#, c-format
+msgid "no SSL error reported"
+msgstr "SSLエラーはありませんでした"
+
+#: fe-secure-openssl.c:1471
+#, c-format
+msgid "SSL error code %lu"
+msgstr "SSLエラーコード: %lu"
+
+#: fe-secure-openssl.c:1718
+#, c-format
+msgid "WARNING: sslpassword truncated\n"
+msgstr "警告: sslpasswordが切り詰められました\n"
+
+#: fe-secure.c:275
+#, c-format
+msgid "could not receive data from server: %s\n"
+msgstr "サーバからデータを受信できませんでした: %s\n"
+
+#: fe-secure.c:390
+#, c-format
+msgid "could not send data to server: %s\n"
+msgstr "サーバにデータを送信できませんでした: %s\n"
+
+#: win32.c:314
+#, c-format
+msgid "unrecognized socket error: 0x%08X/%d"
+msgstr "不明なソケットエラー 0x%08X/%d"
+
+#~ msgid "SSL library does not support CRL certificates (file \"%s\")\n"
+#~ msgstr "SSLライブラリがCRL証明書(\"%s\")をオープンできませんでした\n"
+
+#~ msgid "invalid target_session_attrs value: \"%s\"\n"
+#~ msgstr "target_session_attrsの値が不正です: \"%s\"\n"
+
+#~ msgid "invalid gssencmode value: \"%s\"\n"
+#~ msgstr "gssencmodeの値が不正です: \"%s\"\n"
diff --git a/src/interfaces/libpq/po/ko.po b/src/interfaces/libpq/po/ko.po
new file mode 100644
index 0000000..602069a
--- /dev/null
+++ b/src/interfaces/libpq/po/ko.po
@@ -0,0 +1,1340 @@
+# Korean message translation file for libpq
+# Ioseph Kim. <ioseph@uri.sarang.net>, 2004.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: libpq (PostgreSQL) 13\n"
+"Report-Msgid-Bugs-To: pgsql-bugs@lists.postgresql.org\n"
+"POT-Creation-Date: 2020-10-05 01:09+0000\n"
+"PO-Revision-Date: 2020-10-05 17:53+0900\n"
+"Last-Translator: Ioseph Kim <ioseph@uri.sarang.net>\n"
+"Language-Team: Korean <pgsql-kr@postgresql.kr>\n"
+"Language: ko\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+
+#: fe-auth-scram.c:212
+msgid "malformed SCRAM message (empty message)\n"
+msgstr "SCRAM 메시지가 형식에 안맞음 (메시지 비었음)\n"
+
+#: fe-auth-scram.c:218
+msgid "malformed SCRAM message (length mismatch)\n"
+msgstr "SCRAM 메시지가 형식에 안맞음 (길이 불일치)\n"
+
+#: fe-auth-scram.c:265
+msgid "incorrect server signature\n"
+msgstr "잘못된 서버 서명\n"
+
+#: fe-auth-scram.c:274
+msgid "invalid SCRAM exchange state\n"
+msgstr "SCRAM 교화 상태가 바르지 않음\n"
+
+#: fe-auth-scram.c:296
+#, c-format
+msgid "malformed SCRAM message (attribute \"%c\" expected)\n"
+msgstr "SCRAM 메시지가 형식에 안맞음 (\"%c\" 속성이 예상됨)\n"
+
+#: fe-auth-scram.c:305
+#, c-format
+msgid ""
+"malformed SCRAM message (expected character \"=\" for attribute \"%c\")\n"
+msgstr "SCRAM 메시지가 형식에 안맞음 (\"%c\" 속성 예상값은 \"=\")\n"
+
+#: fe-auth-scram.c:346
+msgid "could not generate nonce\n"
+msgstr "암호화 토큰(nonce)을 만들 수 없음\n"
+
+#: fe-auth-scram.c:356 fe-auth-scram.c:431 fe-auth-scram.c:579
+#: fe-auth-scram.c:600 fe-auth-scram.c:626 fe-auth-scram.c:641
+#: fe-auth-scram.c:691 fe-auth-scram.c:725 fe-auth.c:289 fe-auth.c:359
+#: fe-auth.c:394 fe-auth.c:611 fe-auth.c:770 fe-auth.c:1129 fe-auth.c:1277
+#: fe-connect.c:892 fe-connect.c:1419 fe-connect.c:1595 fe-connect.c:2200
+#: fe-connect.c:2223 fe-connect.c:2952 fe-connect.c:4598 fe-connect.c:4854
+#: fe-connect.c:4973 fe-connect.c:5226 fe-connect.c:5306 fe-connect.c:5405
+#: fe-connect.c:5661 fe-connect.c:5690 fe-connect.c:5762 fe-connect.c:5786
+#: fe-connect.c:5804 fe-connect.c:5905 fe-connect.c:5914 fe-connect.c:6270
+#: fe-connect.c:6420 fe-exec.c:2747 fe-exec.c:3494 fe-exec.c:3659
+#: fe-gssapi-common.c:111 fe-lobj.c:895 fe-protocol2.c:1207 fe-protocol3.c:995
+#: fe-protocol3.c:1699 fe-secure-common.c:110 fe-secure-gssapi.c:504
+#: fe-secure-openssl.c:440 fe-secure-openssl.c:1091
+msgid "out of memory\n"
+msgstr "메모리 부족\n"
+
+#: fe-auth-scram.c:364
+msgid "could not encode nonce\n"
+msgstr "암호화 토큰(nonce)을 인코딩할 수 없음\n"
+
+#: fe-auth-scram.c:563
+msgid "could not encode client proof\n"
+msgstr "클라이언트 프루프(proof)를 인코딩할 수 없음\n"
+
+#: fe-auth-scram.c:618
+msgid "invalid SCRAM response (nonce mismatch)\n"
+msgstr "잘못된 SCRAM 응답 (토큰 불일치)\n"
+
+#: fe-auth-scram.c:651
+msgid "malformed SCRAM message (invalid salt)\n"
+msgstr "형식에 맞지 않은 SCRAM 메시지 (잘못된 소금)\n"
+
+#: fe-auth-scram.c:665
+msgid "malformed SCRAM message (invalid iteration count)\n"
+msgstr "형식에 맞지 않은 SCRAM 메시지 (나열 숫자가 이상함)\n"
+
+#: fe-auth-scram.c:671
+msgid "malformed SCRAM message (garbage at end of server-first-message)\n"
+msgstr ""
+"형식에 맞지 않은 SCRAM 메시지 (서버 첫 메시지 끝에 쓸모 없는 값이 있음)\n"
+
+#: fe-auth-scram.c:702
+#, c-format
+msgid "error received from server in SCRAM exchange: %s\n"
+msgstr "SCRAM 교환작업에서 서버로부터 데이터를 받지 못했음: %s\n"
+
+#: fe-auth-scram.c:718
+msgid "malformed SCRAM message (garbage at end of server-final-message)\n"
+msgstr ""
+"형식에 맞지 않은 SCRAM 메시지 (서버 끝 메시지 뒤에 쓸모 없는 값이 있음)\n"
+
+#: fe-auth-scram.c:737
+msgid "malformed SCRAM message (invalid server signature)\n"
+msgstr "형식에 맞지 않은 SCRAM 메시지 (서버 사인이 이상함)\n"
+
+#: fe-auth.c:76
+#, c-format
+msgid "out of memory allocating GSSAPI buffer (%d)\n"
+msgstr "GSSAPI 버퍼(%d)에 할당할 메모리 부족\n"
+
+#: fe-auth.c:131
+msgid "GSSAPI continuation error"
+msgstr "GSSAPI 연속 오류"
+
+#: fe-auth.c:158 fe-auth.c:388 fe-gssapi-common.c:98 fe-secure-common.c:98
+msgid "host name must be specified\n"
+msgstr "호스트 이름을 지정해야 함\n"
+
+#: fe-auth.c:165
+msgid "duplicate GSS authentication request\n"
+msgstr "중복된 GSS 인증 요청\n"
+
+#: fe-auth.c:230
+#, c-format
+msgid "out of memory allocating SSPI buffer (%d)\n"
+msgstr "SSPI 버퍼(%d)에 할당할 메모리 부족\n"
+
+#: fe-auth.c:278
+msgid "SSPI continuation error"
+msgstr "SSPI 연속 오류"
+
+#: fe-auth.c:349
+msgid "duplicate SSPI authentication request\n"
+msgstr "중복된 SSPI 인증 요청\n"
+
+#: fe-auth.c:374
+msgid "could not acquire SSPI credentials"
+msgstr "SSPI 자격 증명을 가져올 수 없음"
+
+#: fe-auth.c:429
+msgid "channel binding required, but SSL not in use\n"
+msgstr "채널 바인딩이 필요한데, SSL 기능이 꺼져있음\n"
+
+#: fe-auth.c:436
+msgid "duplicate SASL authentication request\n"
+msgstr "중복된 SASL 인증 요청\n"
+
+#: fe-auth.c:492
+msgid "channel binding is required, but client does not support it\n"
+msgstr "채널 바인딩이 필요한데, 클라이언트에서 지원하지 않음\n"
+
+#: fe-auth.c:509
+msgid ""
+"server offered SCRAM-SHA-256-PLUS authentication over a non-SSL connection\n"
+msgstr "서버는 non-SSL 접속으로 SCRAM-SHA-256-PLUS 인증을 제공함\n"
+
+#: fe-auth.c:521
+msgid "none of the server's SASL authentication mechanisms are supported\n"
+msgstr "SASL 인증 메커니즘을 지원하는 서버가 없습니다.\n"
+
+#: fe-auth.c:529
+msgid ""
+"channel binding is required, but server did not offer an authentication "
+"method that supports channel binding\n"
+msgstr ""
+"채널 바인딩 기능을 사용하도록 지정했지만, 서버가 이 기능을 지원하지 않음\n"
+
+#: fe-auth.c:635
+#, c-format
+msgid "out of memory allocating SASL buffer (%d)\n"
+msgstr "SASL 버퍼(%d)에 할당할 메모리 부족\n"
+
+#: fe-auth.c:660
+msgid ""
+"AuthenticationSASLFinal received from server, but SASL authentication was "
+"not completed\n"
+msgstr ""
+"서버에서 AuthenticationSASLFinal 응답을 받았지만, SASL 인증이 끝나지 않았음\n"
+
+#: fe-auth.c:737
+msgid "SCM_CRED authentication method not supported\n"
+msgstr "SCM_CRED 인증 방법이 지원되지 않음\n"
+
+#: fe-auth.c:836
+msgid ""
+"channel binding required, but server authenticated client without channel "
+"binding\n"
+msgstr ""
+"채널 바인딩이 필요한데, 서버가 체널 바인딩 없이 클라이언트를 인증함\n"
+
+#: fe-auth.c:842
+msgid ""
+"channel binding required but not supported by server's authentication "
+"request\n"
+msgstr ""
+"채널 바인딩이 필요한데, 서버 인증 요청에서 지원하지 않음\n"
+
+#: fe-auth.c:875
+msgid "Kerberos 4 authentication not supported\n"
+msgstr "Kerberos 4 인증 방법이 지원되지 않음\n"
+
+#: fe-auth.c:880
+msgid "Kerberos 5 authentication not supported\n"
+msgstr "Kerberos 5 인증 방법이 지원되지 않음\n"
+
+#: fe-auth.c:951
+msgid "GSSAPI authentication not supported\n"
+msgstr "GSSAPI 인증은 지원되지 않음\n"
+
+#: fe-auth.c:983
+msgid "SSPI authentication not supported\n"
+msgstr "SSPI 인증은 지원되지 않음\n"
+
+#: fe-auth.c:991
+msgid "Crypt authentication not supported\n"
+msgstr "암호화 인증은 지원되지 않음\n"
+
+#: fe-auth.c:1057
+#, c-format
+msgid "authentication method %u not supported\n"
+msgstr "%u 인증 방법이 지원되지 않음\n"
+
+#: fe-auth.c:1104
+#, c-format
+msgid "user name lookup failure: error code %lu\n"
+msgstr "사용자 이름 찾기 실패: 오류 코드 %lu\n"
+
+#: fe-auth.c:1114 fe-connect.c:2834
+#, c-format
+msgid "could not look up local user ID %d: %s\n"
+msgstr "UID %d 해당하는 사용자를 찾을 수 없음: %s\n"
+
+#: fe-auth.c:1119 fe-connect.c:2839
+#, c-format
+msgid "local user with ID %d does not exist\n"
+msgstr "ID %d 로컬 사용자 없음\n"
+
+#: fe-auth.c:1221
+msgid "unexpected shape of result set returned for SHOW\n"
+msgstr "SHOW 명령의 결과 자료가 비정상임\n"
+
+#: fe-auth.c:1230
+msgid "password_encryption value too long\n"
+msgstr "password_encryption 너무 긺\n"
+
+#: fe-auth.c:1270
+#, c-format
+msgid "unrecognized password encryption algorithm \"%s\"\n"
+msgstr "알 수 없는 비밀번호 암호화 알고리즘: \"%s\"\n"
+
+#: fe-connect.c:1075
+#, c-format
+msgid "could not match %d host names to %d hostaddr values\n"
+msgstr "호스트 이름은 %d개인데, 호스트 주소는 %d개임\n"
+
+#: fe-connect.c:1156
+#, c-format
+msgid "could not match %d port numbers to %d hosts\n"
+msgstr "포트 번호는 %d개인데, 호스트는 %d개입니다.\n"
+
+#: fe-connect.c:1249
+#, c-format
+msgid "invalid channel_binding value: \"%s\"\n"
+msgstr "잘못된 channel_binding 값: \"%s\"\n"
+
+#: fe-connect.c:1275
+#, c-format
+msgid "invalid sslmode value: \"%s\"\n"
+msgstr "잘못된 sslmode 값: \"%s\"\n"
+
+#: fe-connect.c:1296
+#, c-format
+msgid "sslmode value \"%s\" invalid when SSL support is not compiled in\n"
+msgstr ""
+"SSL 연결 기능을 지원하지 않고 컴파일 된 경우는 sslmode 값으로 \"%s\" 값은 타"
+"당치 않습니다\n"
+
+#: fe-connect.c:1317
+#, c-format
+msgid "invalid ssl_min_protocol_version value: \"%s\"\n"
+msgstr "잘못된 ssl_min_protocol_version 값: \"%s\"\n"
+
+#: fe-connect.c:1325
+#, c-format
+msgid "invalid ssl_max_protocol_version value: \"%s\"\n"
+msgstr "잘못된 ssl_max_protocol_version 값: \"%s\"\n"
+
+#: fe-connect.c:1342
+msgid "invalid SSL protocol version range\n"
+msgstr "잘못된 SSL 프로토콜 버전 범위\n"
+
+#: fe-connect.c:1357
+#, c-format
+msgid "invalid gssencmode value: \"%s\"\n"
+msgstr "잘못된 gssencmode 값: \"%s\"\n"
+
+#: fe-connect.c:1366
+#, c-format
+msgid ""
+"gssencmode value \"%s\" invalid when GSSAPI support is not compiled in\n"
+msgstr ""
+"GSSAPI 접속을 지원하지 않는 서버에서는 gssencmode 값(\"%s\")이 적당하지 않"
+"음\n"
+
+#: fe-connect.c:1401
+#, c-format
+msgid "invalid target_session_attrs value: \"%s\"\n"
+msgstr "잘못된 target_session_attrs 값: \"%s\"\n"
+
+#: fe-connect.c:1619
+#, c-format
+msgid "could not set socket to TCP no delay mode: %s\n"
+msgstr "소켓을 TCP에 no delay 모드로 지정할 수 없음: %s\n"
+
+#: fe-connect.c:1680
+#, c-format
+msgid ""
+"could not connect to server: %s\n"
+"\tIs the server running locally and accepting\n"
+"\tconnections on Unix domain socket \"%s\"?\n"
+msgstr ""
+"서버에 연결할 수 없음: %s\n"
+"\t로컬호스트에 서버가 가동 중인지,\n"
+"\t\"%s\" 유닉스 도메인 소켓 접근이 가능한지 살펴보십시오.\n"
+
+#: fe-connect.c:1717
+#, c-format
+msgid ""
+"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"
+msgstr ""
+"서버에 연결할 수 없음: %s\n"
+"\t\"%s\" (%s) 호스트에 서버가 가동 중인지,\n"
+"\t%s 포트로 TCP/IP 연결이 가능한지 살펴보십시오.\n"
+
+#: fe-connect.c:1725
+#, c-format
+msgid ""
+"could not connect to server: %s\n"
+"\tIs the server running on host \"%s\" and accepting\n"
+"\tTCP/IP connections on port %s?\n"
+msgstr ""
+"서버에 연결할 수 없음: %s\n"
+"\t\"%s\" 호스트에 서버가 가동 중인지,\n"
+"\t%s 포트로 TCP/IP 연결이 가능한지 살펴보십시오.\n"
+
+#: fe-connect.c:1795
+#, c-format
+msgid "invalid integer value \"%s\" for connection option \"%s\"\n"
+msgstr "잘못된 정수값: \"%s\", 해당 연결 옵션: \"%s\"\n"
+
+#: fe-connect.c:1825 fe-connect.c:1859 fe-connect.c:1894 fe-connect.c:1981
+#: fe-connect.c:2623
+#, c-format
+msgid "setsockopt(%s) failed: %s\n"
+msgstr "setsockopt(%s) 실패: %s\n"
+
+#: fe-connect.c:1947
+#, c-format
+msgid "WSAIoctl(SIO_KEEPALIVE_VALS) failed: %ui\n"
+msgstr "WSAIoctl(SIO_KEEPALIVE_VALS) 실패: %ui\n"
+
+#: fe-connect.c:2313
+msgid "invalid connection state, probably indicative of memory corruption\n"
+msgstr "잘못된 연결 상태, 메모리 손상일 가능성이 큼\n"
+
+#: fe-connect.c:2379
+#, c-format
+msgid "invalid port number: \"%s\"\n"
+msgstr "잘못된 포트 번호: \"%s\"\n"
+
+#: fe-connect.c:2395
+#, c-format
+msgid "could not translate host name \"%s\" to address: %s\n"
+msgstr "\"%s\" 호스트 이름을 전송할 수 없습니다: 대상 주소: %s\n"
+
+#: fe-connect.c:2408
+#, c-format
+msgid "could not parse network address \"%s\": %s\n"
+msgstr "\"%s\" 네트워크 주소를 해석할 수 없음: %s\n"
+
+#: fe-connect.c:2421
+#, c-format
+msgid "Unix-domain socket path \"%s\" is too long (maximum %d bytes)\n"
+msgstr "\"%s\" 유닉스 도메인 소켓 경로가 너무 깁니다 (최대 %d 바이트)\n"
+
+#: fe-connect.c:2436
+#, c-format
+msgid "could not translate Unix-domain socket path \"%s\" to address: %s\n"
+msgstr "\"%s\" 유닉스 도메인 소켓 경로를 전송할 수 없습니다: 대상 주소: %s\n"
+
+#: fe-connect.c:2560
+#, c-format
+msgid "could not create socket: %s\n"
+msgstr "소켓을 만들 수 없음: %s\n"
+
+#: fe-connect.c:2582
+#, c-format
+msgid "could not set socket to nonblocking mode: %s\n"
+msgstr "소켓을 nonblocking 모드로 지정할 수 없음: %s\n"
+
+#: fe-connect.c:2592
+#, c-format
+msgid "could not set socket to close-on-exec mode: %s\n"
+msgstr "소켓을 close-on-exec 모드로 지정할 수 없음: %s\n"
+
+#: fe-connect.c:2610
+msgid "keepalives parameter must be an integer\n"
+msgstr "keepalives 매개변수값은 정수여야 합니다.\n"
+
+#: fe-connect.c:2750
+#, c-format
+msgid "could not get socket error status: %s\n"
+msgstr "소켓 오류 상태를 구할 수 없음: %s\n"
+
+#: fe-connect.c:2778
+#, c-format
+msgid "could not get client address from socket: %s\n"
+msgstr "소켓에서 클라이언트 주소를 구할 수 없음: %s\n"
+
+#: fe-connect.c:2820
+msgid "requirepeer parameter is not supported on this platform\n"
+msgstr "requirepeer 매개변수는 이 운영체제에서 지원하지 않음\n"
+
+#: fe-connect.c:2823
+#, c-format
+msgid "could not get peer credentials: %s\n"
+msgstr "신뢰성 피어를 얻을 수 없습니다: %s\n"
+
+#: fe-connect.c:2847
+#, c-format
+msgid "requirepeer specifies \"%s\", but actual peer user name is \"%s\"\n"
+msgstr ""
+"\"%s\" 이름으로 requirepeer를 지정했지만, 실재 사용자 이름은 \"%s\" 입니다\n"
+
+#: fe-connect.c:2887
+#, c-format
+msgid "could not send GSSAPI negotiation packet: %s\n"
+msgstr "GSSAPI 교섭 패킷을 보낼 수 없음: %s\n"
+
+#: fe-connect.c:2899
+msgid ""
+"GSSAPI encryption required but was impossible (possibly no credential cache, "
+"no server support, or using a local socket)\n"
+msgstr ""
+"GSSAPI 암호화가 필요하지만 사용할 수 없음 (자격 증명 캐시가 없거나, 서버가 지"
+"원하지 않거나, 로컬 소켓을 사용하고 있는 듯합니다.)\n"
+
+#: fe-connect.c:2926
+#, c-format
+msgid "could not send SSL negotiation packet: %s\n"
+msgstr "SSL 교섭 패킷을 보낼 수 없음: %s\n"
+
+#: fe-connect.c:2965
+#, c-format
+msgid "could not send startup packet: %s\n"
+msgstr "시작 패킷을 보낼 수 없음: %s\n"
+
+#: fe-connect.c:3035
+msgid "server does not support SSL, but SSL was required\n"
+msgstr "서버가 SSL 기능을 지원하지 않는데, SSL 기능을 요구했음\n"
+
+#: fe-connect.c:3061
+#, c-format
+msgid "received invalid response to SSL negotiation: %c\n"
+msgstr "SSL 교섭에 대한 잘못된 응답을 감지했음: %c\n"
+
+#: fe-connect.c:3151
+msgid "server doesn't support GSSAPI encryption, but it was required\n"
+msgstr "서버가 GSSAPI 암호화 기능을 지원하지 않는데, 이것이 필요함\n"
+
+#: fe-connect.c:3162
+#, c-format
+msgid "received invalid response to GSSAPI negotiation: %c\n"
+msgstr "GSSAPI 교섭에 대한 잘못된 응답을 감지했음: %c\n"
+
+#: fe-connect.c:3229 fe-connect.c:3260
+#, c-format
+msgid "expected authentication request from server, but received %c\n"
+msgstr "서버가 인증을 요구했지만, %c 받았음\n"
+
+#: fe-connect.c:3502
+msgid "unexpected message from server during startup\n"
+msgstr "시작하는 동안 서버로부터 기대되지 않는 메시지\n"
+
+#: fe-connect.c:3707
+#, c-format
+msgid "could not make a writable connection to server \"%s:%s\"\n"
+msgstr "\"%s:%s\" 서버에 쓰기 가능한 연결을 맺을 수 없음\n"
+
+#: fe-connect.c:3753
+#, c-format
+msgid "test \"SHOW transaction_read_only\" failed on server \"%s:%s\"\n"
+msgstr "\"%s:%s\" 서버에서 \"SHOW transaction_read_only\" 검사가 실패함\n"
+
+#: fe-connect.c:3768
+#, c-format
+msgid "invalid connection state %d, probably indicative of memory corruption\n"
+msgstr "잘못된 연결 상태 %d, 메모리 손상일 가능성이 큼\n"
+
+#: fe-connect.c:4204 fe-connect.c:4264
+#, c-format
+msgid "PGEventProc \"%s\" failed during PGEVT_CONNRESET event\n"
+msgstr "PGEVT_CONNRESET 이벤트 동안 PGEventProc \"%s\"이(가) 실패함\n"
+
+#: fe-connect.c:4611
+#, c-format
+msgid "invalid LDAP URL \"%s\": scheme must be ldap://\n"
+msgstr "잘못된 LDAP URL \"%s\": 스키마는 ldap:// 여야함\n"
+
+#: fe-connect.c:4626
+#, c-format
+msgid "invalid LDAP URL \"%s\": missing distinguished name\n"
+msgstr "잘못된 LDAP URL \"%s\": 식별자 이름이 빠졌음\n"
+
+#: fe-connect.c:4638 fe-connect.c:4693
+#, c-format
+msgid "invalid LDAP URL \"%s\": must have exactly one attribute\n"
+msgstr "잘못된 LDAP URL \"%s\": 단 하나의 속성만 가져야함\n"
+
+#: fe-connect.c:4649 fe-connect.c:4708
+#, c-format
+msgid "invalid LDAP URL \"%s\": must have search scope (base/one/sub)\n"
+msgstr "잘못된 LDAP URL \"%s\": 검색범위(base/one/sub)를 지정해야함\n"
+
+#: fe-connect.c:4660
+#, c-format
+msgid "invalid LDAP URL \"%s\": no filter\n"
+msgstr "잘못된 LDAP URL \"%s\": 필터 없음\n"
+
+#: fe-connect.c:4681
+#, c-format
+msgid "invalid LDAP URL \"%s\": invalid port number\n"
+msgstr "잘못된 LDAP URL \"%s\": 포트번호가 잘못됨\n"
+
+#: fe-connect.c:4717
+msgid "could not create LDAP structure\n"
+msgstr "LDAP 구조를 만들 수 없음\n"
+
+#: fe-connect.c:4793
+#, c-format
+msgid "lookup on LDAP server failed: %s\n"
+msgstr "LDAP 서버를 찾을 수 없음: %s\n"
+
+#: fe-connect.c:4804
+msgid "more than one entry found on LDAP lookup\n"
+msgstr "LDAP 검색에서 하나 이상의 엔트리가 발견되었음\n"
+
+#: fe-connect.c:4805 fe-connect.c:4817
+msgid "no entry found on LDAP lookup\n"
+msgstr "LDAP 검색에서 해당 항목 없음\n"
+
+#: fe-connect.c:4828 fe-connect.c:4841
+msgid "attribute has no values on LDAP lookup\n"
+msgstr "LDAP 검색에서 속성의 값이 없음\n"
+
+#: fe-connect.c:4893 fe-connect.c:4912 fe-connect.c:5444
+#, c-format
+msgid "missing \"=\" after \"%s\" in connection info string\n"
+msgstr "연결문자열에서 \"%s\" 다음에 \"=\" 문자 빠졌음\n"
+
+#: fe-connect.c:4985 fe-connect.c:5629 fe-connect.c:6403
+#, c-format
+msgid "invalid connection option \"%s\"\n"
+msgstr "잘못된 연결 옵션 \"%s\"\n"
+
+#: fe-connect.c:5001 fe-connect.c:5493
+msgid "unterminated quoted string in connection info string\n"
+msgstr "연결문자열에서 완성되지 못한 따옴표문자열이 있음\n"
+
+#: fe-connect.c:5084
+#, c-format
+msgid "definition of service \"%s\" not found\n"
+msgstr "\"%s\" 서비스 정의를 찾을 수 없음\n"
+
+#: fe-connect.c:5107
+#, c-format
+msgid "service file \"%s\" not found\n"
+msgstr "\"%s\" 서비스 파일을 찾을 수 없음\n"
+
+#: fe-connect.c:5122
+#, c-format
+msgid "line %d too long in service file \"%s\"\n"
+msgstr "%d번째 줄이 \"%s\" 서비스 파일에서 너무 깁니다\n"
+
+#: fe-connect.c:5194 fe-connect.c:5238
+#, c-format
+msgid "syntax error in service file \"%s\", line %d\n"
+msgstr "\"%s\" 서비스 파일의 %d번째 줄에 구문 오류 있음\n"
+
+#: fe-connect.c:5205
+#, c-format
+msgid ""
+"nested service specifications not supported in service file \"%s\", line %d\n"
+msgstr "\"%s\" 서비스 파일의 %d번째 줄에 설정을 지원하지 않음\n"
+
+#: fe-connect.c:5925
+#, c-format
+msgid "invalid URI propagated to internal parser routine: \"%s\"\n"
+msgstr "URI 구문 분석을 할 수 없음: \"%s\"\n"
+
+#: fe-connect.c:6002
+#, c-format
+msgid ""
+"end of string reached when looking for matching \"]\" in IPv6 host address "
+"in URI: \"%s\"\n"
+msgstr ""
+"URI의 IPv6 호스트 주소에서 \"]\" 매칭 검색을 실패했습니다, 해당 URI: \"%s\"\n"
+
+#: fe-connect.c:6009
+#, c-format
+msgid "IPv6 host address may not be empty in URI: \"%s\"\n"
+msgstr "IPv6 호스트 주소가 없습니다, 해당 URI: \"%s\"\n"
+
+#: fe-connect.c:6024
+#, c-format
+msgid ""
+"unexpected character \"%c\" at position %d in URI (expected \":\" or \"/\"): "
+"\"%s\"\n"
+msgstr ""
+"잘못된 \"%c\" 문자가 URI 문자열 가운데 %d 번째 있습니다(\":\" 또는 \"/\" 문자"
+"가 있어야 함): \"%s\"\n"
+
+#: fe-connect.c:6153
+#, c-format
+msgid "extra key/value separator \"=\" in URI query parameter: \"%s\"\n"
+msgstr "키/밸류 구분자 \"=\" 문자가 필요함, 해당 URI 쿼리 매개변수: \"%s\"\n"
+
+#: fe-connect.c:6173
+#, c-format
+msgid "missing key/value separator \"=\" in URI query parameter: \"%s\"\n"
+msgstr "키/밸류 구분자 \"=\" 문자가 필요함, 해당 URI 쿼리 매개변수: \"%s\"\n"
+
+#: fe-connect.c:6224
+#, c-format
+msgid "invalid URI query parameter: \"%s\"\n"
+msgstr "잘못된 URL 쿼리 매개변수값: \"%s\"\n"
+
+#: fe-connect.c:6298
+#, c-format
+msgid "invalid percent-encoded token: \"%s\"\n"
+msgstr "잘못된 퍼센트 인코드 토큰: \"%s\"\n"
+
+#: fe-connect.c:6308
+#, c-format
+msgid "forbidden value %%00 in percent-encoded value: \"%s\"\n"
+msgstr "퍼센트 인코드 값에 %%00 숨김 값이 있음: \"%s\"\n"
+
+#: fe-connect.c:6671
+msgid "connection pointer is NULL\n"
+msgstr "연결 포인터가 NULL\n"
+
+#: fe-connect.c:6967
+#, c-format
+msgid "WARNING: password file \"%s\" is not a plain file\n"
+msgstr "경고: \"%s\" 패스워드 파일이 plain 파일이 아님\n"
+
+#: fe-connect.c:6976
+#, c-format
+msgid ""
+"WARNING: password file \"%s\" has group or world access; permissions should "
+"be u=rw (0600) or less\n"
+msgstr ""
+"경고: 패스워드 파일 \"%s\"에 그룹 또는 범용 액세스 권한이 있습니다. 권한은 "
+"u=rw(0600) 이하여야 합니다.\n"
+
+#: fe-connect.c:7084
+#, c-format
+msgid "password retrieved from file \"%s\"\n"
+msgstr "\"%s\" 파일에서 암호를 찾을 수 없음\n"
+
+#: fe-exec.c:444 fe-exec.c:2821
+#, c-format
+msgid "row number %d is out of range 0..%d"
+msgstr "%d 번째 행(row)은 0..%d 범위를 벗어났음"
+
+#: fe-exec.c:505 fe-protocol2.c:497 fe-protocol2.c:532 fe-protocol2.c:1050
+#: fe-protocol3.c:206 fe-protocol3.c:233 fe-protocol3.c:250 fe-protocol3.c:330
+#: fe-protocol3.c:723 fe-protocol3.c:954
+msgid "out of memory"
+msgstr "메모리 부족"
+
+#: fe-exec.c:506 fe-protocol2.c:1396 fe-protocol3.c:1907
+#, c-format
+msgid "%s"
+msgstr "%s"
+
+#: fe-exec.c:815
+msgid "write to server failed\n"
+msgstr "서버에 쓰기 실패\n"
+
+#: fe-exec.c:896
+msgid "NOTICE"
+msgstr "알림"
+
+#: fe-exec.c:954
+msgid "PGresult cannot support more than INT_MAX tuples"
+msgstr "PGresult 함수는 INT_MAX 튜플보다 많은 경우를 지원하지 않음"
+
+#: fe-exec.c:966
+msgid "size_t overflow"
+msgstr "size_t 초과"
+
+#: fe-exec.c:1243 fe-exec.c:1301 fe-exec.c:1347
+msgid "command string is a null pointer\n"
+msgstr "명령 문자열이 null 포인터\n"
+
+#: fe-exec.c:1307 fe-exec.c:1353 fe-exec.c:1448
+msgid "number of parameters must be between 0 and 65535\n"
+msgstr "매개변수값으로 숫자는 0에서 65535까지만 쓸 수 있음\n"
+
+#: fe-exec.c:1341 fe-exec.c:1442
+msgid "statement name is a null pointer\n"
+msgstr "실행 구문 이름이 null 포인트(값이 없음)입니다\n"
+
+#: fe-exec.c:1361 fe-exec.c:1524 fe-exec.c:2233 fe-exec.c:2435
+msgid "function requires at least protocol version 3.0\n"
+msgstr "함수는 적어도 버전 3의 프로토콜을 요구하고 있습니다\n"
+
+#: fe-exec.c:1479
+msgid "no connection to the server\n"
+msgstr "서버에 대한 연결이 없음\n"
+
+#: fe-exec.c:1486
+msgid "another command is already in progress\n"
+msgstr "처리 중에 이미 다른 명령이 존재함\n"
+
+#: fe-exec.c:1600
+msgid "length must be given for binary parameter\n"
+msgstr "바이너리 자료 매개 변수를 사용할 때는 그 길이를 지정해야 함\n"
+
+#: fe-exec.c:1863
+#, c-format
+msgid "unexpected asyncStatus: %d\n"
+msgstr "기대되지 않은 동기화상태: %d\n"
+
+#: fe-exec.c:1883
+#, c-format
+msgid "PGEventProc \"%s\" failed during PGEVT_RESULTCREATE event\n"
+msgstr "PGEVT_RESULTCREATE 이벤트 동안 PGEventProc \"%s\" 실패함\n"
+
+#: fe-exec.c:2043
+msgid "COPY terminated by new PQexec"
+msgstr "새 PQexec 호출로 COPY 작업이 중지 되었습니다"
+
+#: fe-exec.c:2051
+msgid "COPY IN state must be terminated first\n"
+msgstr "COPY IN 상태가 먼저 끝나야함\n"
+
+#: fe-exec.c:2071
+msgid "COPY OUT state must be terminated first\n"
+msgstr "COPY OUT 상태가 먼저 끝나야함\n"
+
+#: fe-exec.c:2079
+msgid "PQexec not allowed during COPY BOTH\n"
+msgstr "COPY BOTH 작업 중에는 PQexec 사용할 수 없음\n"
+
+#: fe-exec.c:2325 fe-exec.c:2392 fe-exec.c:2482 fe-protocol2.c:1353
+#: fe-protocol3.c:1838
+msgid "no COPY in progress\n"
+msgstr "처리 가운데 COPY가 없음\n"
+
+#: fe-exec.c:2672
+msgid "connection in wrong state\n"
+msgstr "잘못된 상태의 연결\n"
+
+#: fe-exec.c:2703
+msgid "invalid ExecStatusType code"
+msgstr "잘못된 ExecStatusType 코드"
+
+#: fe-exec.c:2730
+msgid "PGresult is not an error result\n"
+msgstr "PGresult가 오류 결과가 아님\n"
+
+#: fe-exec.c:2805 fe-exec.c:2828
+#, c-format
+msgid "column number %d is out of range 0..%d"
+msgstr "%d 번째 열은 0..%d 범위를 벗어났음"
+
+#: fe-exec.c:2843
+#, c-format
+msgid "parameter number %d is out of range 0..%d"
+msgstr "%d개의 매개 변수는 0..%d 범위를 벗어났음"
+
+#: fe-exec.c:3153
+#, c-format
+msgid "could not interpret result from server: %s"
+msgstr "서버로부터 결과처리를 중지 시킬 수 없음: %s"
+
+#: fe-exec.c:3392 fe-exec.c:3476
+msgid "incomplete multibyte character\n"
+msgstr "완성되지 않은 멀티바이트 문자\n"
+
+#: fe-gssapi-common.c:124
+msgid "GSSAPI name import error"
+msgstr "GSSAPI 이름 가져오기 오류"
+
+#: fe-lobj.c:154
+msgid "cannot determine OID of function lo_truncate\n"
+msgstr "lo_truncate 함수의 OID를 결정할 수 없음\n"
+
+#: fe-lobj.c:170
+msgid "argument of lo_truncate exceeds integer range\n"
+msgstr "lo_truncate 함수의 인자값이 정수 범위가 아님\n"
+
+#: fe-lobj.c:221
+msgid "cannot determine OID of function lo_truncate64\n"
+msgstr "lo_truncate64 함수의 OID를 알 수 없음\n"
+
+#: fe-lobj.c:279
+msgid "argument of lo_read exceeds integer range\n"
+msgstr "lo_read 함수의 인자값이 정수 범위가 아님\n"
+
+#: fe-lobj.c:334
+msgid "argument of lo_write exceeds integer range\n"
+msgstr "lo_write 함수의 인자값이 정수 범위가 아님\n"
+
+#: fe-lobj.c:425
+msgid "cannot determine OID of function lo_lseek64\n"
+msgstr "lo_lseek64 함수의 OID를 알 수 없음\n"
+
+#: fe-lobj.c:521
+msgid "cannot determine OID of function lo_create\n"
+msgstr "lo_create 함수의 OID 조사를 할 수 없음\n"
+
+#: fe-lobj.c:600
+msgid "cannot determine OID of function lo_tell64\n"
+msgstr "lo_tell64 함수의 OID를 알 수 없음\n"
+
+#: fe-lobj.c:706 fe-lobj.c:815
+#, c-format
+msgid "could not open file \"%s\": %s\n"
+msgstr "\"%s\" 파일을 열 수 없음: %s\n"
+
+#: fe-lobj.c:761
+#, c-format
+msgid "could not read from file \"%s\": %s\n"
+msgstr "\"%s\" 파일을 읽을 수 없음: %s\n"
+
+#: fe-lobj.c:835 fe-lobj.c:859
+#, c-format
+msgid "could not write to file \"%s\": %s\n"
+msgstr "\"%s\" 파일을 쓸 수 없음: %s\n"
+
+#: fe-lobj.c:946
+msgid "query to initialize large object functions did not return data\n"
+msgstr "large object function을 초기화 하는 쿼리가 데이터를 리턴하지 않았음\n"
+
+#: fe-lobj.c:995
+msgid "cannot determine OID of function lo_open\n"
+msgstr "lo_open 함수의 OID 조사를 할 수 없음\n"
+
+#: fe-lobj.c:1002
+msgid "cannot determine OID of function lo_close\n"
+msgstr "lo_close 함수의 OID 조사를 할 수 없음\n"
+
+#: fe-lobj.c:1009
+msgid "cannot determine OID of function lo_creat\n"
+msgstr "lo_create 함수의 OID 조사를 할 수 없음\n"
+
+#: fe-lobj.c:1016
+msgid "cannot determine OID of function lo_unlink\n"
+msgstr "lo_unlink 함수의 OID 조사를 할 수 없음\n"
+
+#: fe-lobj.c:1023
+msgid "cannot determine OID of function lo_lseek\n"
+msgstr "lo_lseek 함수의 OID 조사를 할 수 없음\n"
+
+#: fe-lobj.c:1030
+msgid "cannot determine OID of function lo_tell\n"
+msgstr "lo_tell 함수의 OID 조사를 할 수 없음\n"
+
+#: fe-lobj.c:1037
+msgid "cannot determine OID of function loread\n"
+msgstr "loread 함수의 OID 조사를 할 수 없음\n"
+
+#: fe-lobj.c:1044
+msgid "cannot determine OID of function lowrite\n"
+msgstr "lowrite 함수의 OID 조사를 할 수 없음\n"
+
+#: fe-misc.c:289
+#, c-format
+msgid "integer of size %lu not supported by pqGetInt"
+msgstr "%lu 정수형 크기는 pqGetInt 함수에서 지원하지 않음"
+
+#: fe-misc.c:325
+#, c-format
+msgid "integer of size %lu not supported by pqPutInt"
+msgstr "%lu 정수형 크기는 pqPutInt 함수에서 지원하지 않음"
+
+#: fe-misc.c:636 fe-misc.c:869
+msgid "connection not open\n"
+msgstr "연결 열기 실패\n"
+
+#: fe-misc.c:805 fe-secure-openssl.c:209 fe-secure-openssl.c:316
+#: fe-secure.c:267 fe-secure.c:383
+msgid ""
+"server closed the connection unexpectedly\n"
+"\tThis probably means the server terminated abnormally\n"
+"\tbefore or while processing the request.\n"
+msgstr ""
+"서버가 갑자기 연결을 닫았음\n"
+"\t이런 처리는 클라이언트의 요구를 처리하는 동안이나\n"
+"\t처리하기 전에 서버가 갑자기 종료되었음을 의미함\n"
+
+#: fe-misc.c:1063
+msgid "timeout expired\n"
+msgstr "시간 초과\n"
+
+#: fe-misc.c:1108
+msgid "invalid socket\n"
+msgstr "잘못된 소켓\n"
+
+#: fe-misc.c:1131
+#, c-format
+msgid "select() failed: %s\n"
+msgstr "select() 실패: %s\n"
+
+#: fe-protocol2.c:87
+#, c-format
+msgid "invalid setenv state %c, probably indicative of memory corruption\n"
+msgstr "잘못된 환경변수 상태 %c, 메모리 손상일 가능성이 큼\n"
+
+#: fe-protocol2.c:384
+#, c-format
+msgid "invalid state %c, probably indicative of memory corruption\n"
+msgstr "잘못된 상태 %c, 메모리 손상일 가능성이 큼\n"
+
+#: fe-protocol2.c:473 fe-protocol3.c:183
+#, c-format
+msgid "message type 0x%02x arrived from server while idle"
+msgstr "휴지(idle)동안 서버로 부터 0x%02x 형태 메시지를 받았음"
+
+#: fe-protocol2.c:523
+#, c-format
+msgid "unexpected character %c following empty query response (\"I\" message)"
+msgstr "비어있는 쿼리 응답(\"I\" 메시지)에 뒤이어 %c의 잘못된 문자가 있음"
+
+#: fe-protocol2.c:589
+#, c-format
+msgid ""
+"server sent data (\"D\" message) without prior row description (\"T\" "
+"message)"
+msgstr ""
+"서버에서 먼저 행(row) 설명(\"T\" 메시지) 없이 자료(\"D\" 메시지)를 보냈음"
+
+#: fe-protocol2.c:607
+#, c-format
+msgid ""
+"server sent binary data (\"B\" message) without prior row description (\"T\" "
+"message)"
+msgstr ""
+"서버에서 먼저 행(row) 설명(\"T\" 메시지) 없이 바이너리 자료(\"B\" 메시지)를 "
+"보냈음"
+
+#: fe-protocol2.c:626 fe-protocol3.c:408
+#, c-format
+msgid "unexpected response from server; first received character was \"%c\"\n"
+msgstr "서버로부터 예상치 못한 응답을 받았음; \"%c\" 문자를 첫문자로 받았음\n"
+
+#: fe-protocol2.c:755 fe-protocol2.c:930 fe-protocol3.c:622 fe-protocol3.c:849
+msgid "out of memory for query result"
+msgstr "쿼리 결과 처리를 위한 메모리 부족"
+
+#: fe-protocol2.c:1408
+#, c-format
+msgid "lost synchronization with server, resetting connection"
+msgstr "서버와의 동기화가 끊김, 연결을 재 시도함"
+
+#: fe-protocol2.c:1530 fe-protocol2.c:1562 fe-protocol3.c:2095
+#, c-format
+msgid "protocol error: id=0x%x\n"
+msgstr "프로토콜 오류: id=0x%x\n"
+
+#: fe-protocol3.c:365
+msgid ""
+"server sent data (\"D\" message) without prior row description (\"T\" "
+"message)\n"
+msgstr ""
+"서버에서 먼저 행(row) 설명(\"T\" 메시지) 없이 자료(\"D\" 메시지)를 보냈음\n"
+
+#: fe-protocol3.c:429
+#, c-format
+msgid "message contents do not agree with length in message type \"%c\"\n"
+msgstr "메시지 내용이 \"%c\" 메시지 형태의 길이를 허락하지 않음\n"
+
+#: fe-protocol3.c:449
+#, c-format
+msgid "lost synchronization with server: got message type \"%c\", length %d\n"
+msgstr "서버와의 동기화가 끊김: \"%c\" 형태 길이 %d 메시지 받음\n"
+
+#: fe-protocol3.c:500 fe-protocol3.c:540
+msgid "insufficient data in \"T\" message"
+msgstr "\"T\" 메시지 안에 부족자 데이터"
+
+#: fe-protocol3.c:573
+msgid "extraneous data in \"T\" message"
+msgstr "\"T\" 메시지 안에 잘못된 데이터"
+
+#: fe-protocol3.c:686
+msgid "extraneous data in \"t\" message"
+msgstr "\"t\" 메시지 안에 잘못된 데이터"
+
+#: fe-protocol3.c:757 fe-protocol3.c:789 fe-protocol3.c:807
+msgid "insufficient data in \"D\" message"
+msgstr "\"D\" 메시지 안에 불충분한 데이터"
+
+#: fe-protocol3.c:763
+msgid "unexpected field count in \"D\" message"
+msgstr "\"D\" 메시지 안에 예상치 못한 필드 수"
+
+#: fe-protocol3.c:816
+msgid "extraneous data in \"D\" message"
+msgstr "\"D\" 메시지 안에 잘못된 데이터"
+
+#: fe-protocol3.c:1008
+msgid "no error message available\n"
+msgstr "보여줄 오류 메시지가 없음\n"
+
+#. translator: %s represents a digit string
+#: fe-protocol3.c:1056 fe-protocol3.c:1075
+#, c-format
+msgid " at character %s"
+msgstr " 위치: %s"
+
+#: fe-protocol3.c:1088
+#, c-format
+msgid "DETAIL: %s\n"
+msgstr "상세정보: %s\n"
+
+#: fe-protocol3.c:1091
+#, c-format
+msgid "HINT: %s\n"
+msgstr "힌트: %s\n"
+
+#: fe-protocol3.c:1094
+#, c-format
+msgid "QUERY: %s\n"
+msgstr "쿼리: %s\n"
+
+#: fe-protocol3.c:1101
+#, c-format
+msgid "CONTEXT: %s\n"
+msgstr "구문: %s\n"
+
+#: fe-protocol3.c:1110
+#, c-format
+msgid "SCHEMA NAME: %s\n"
+msgstr "스키마 이름: %s\n"
+
+#: fe-protocol3.c:1114
+#, c-format
+msgid "TABLE NAME: %s\n"
+msgstr "테이블 이름: %s\n"
+
+#: fe-protocol3.c:1118
+#, c-format
+msgid "COLUMN NAME: %s\n"
+msgstr "칼럼 이름: %s\n"
+
+#: fe-protocol3.c:1122
+#, c-format
+msgid "DATATYPE NAME: %s\n"
+msgstr "자료형 이름: %s\n"
+
+#: fe-protocol3.c:1126
+#, c-format
+msgid "CONSTRAINT NAME: %s\n"
+msgstr "제약조건 이름: %s\n"
+
+#: fe-protocol3.c:1138
+msgid "LOCATION: "
+msgstr "위치: "
+
+#: fe-protocol3.c:1140
+#, c-format
+msgid "%s, "
+msgstr "%s, "
+
+#: fe-protocol3.c:1142
+#, c-format
+msgid "%s:%s"
+msgstr "%s:%s"
+
+#: fe-protocol3.c:1337
+#, c-format
+msgid "LINE %d: "
+msgstr "줄 %d: "
+
+#: fe-protocol3.c:1732
+msgid "PQgetline: not doing text COPY OUT\n"
+msgstr "PQgetline: text COPY OUT 작업을 할 수 없음\n"
+
+#: fe-secure-common.c:124
+msgid "SSL certificate's name contains embedded null\n"
+msgstr "SSL 인증서의 이름에 null 문자가 있음\n"
+
+#: fe-secure-common.c:171
+msgid "host name must be specified for a verified SSL connection\n"
+msgstr "인증된 SSL 접속을 위해서는 호스트 이름을 지정해야 함\n"
+
+#: fe-secure-common.c:196
+#, c-format
+msgid "server certificate for \"%s\" does not match host name \"%s\"\n"
+msgstr ""
+"서버 인증서의 이름 \"%s\"이(가) 호스트 이름 \"%s\"과(와) 일치하지 않음\n"
+
+#: fe-secure-common.c:202
+msgid "could not get server's host name from server certificate\n"
+msgstr "서버 인증서에서 서버 호스트 이름을 찾을 수 없음\n"
+
+#: fe-secure-gssapi.c:201
+msgid "GSSAPI wrap error"
+msgstr "GSSAPI 감싸기 오류"
+
+#: fe-secure-gssapi.c:209
+msgid "outgoing GSSAPI message would not use confidentiality\n"
+msgstr "GSSAPI 송출 메시지는 기밀성을 사용하지 말아야함\n"
+
+#: fe-secure-gssapi.c:217
+#, c-format
+msgid "client tried to send oversize GSSAPI packet (%zu > %zu)\n"
+msgstr "클라이언트의 GSSAPI 패킷이 너무 큼 (%zu > %zu)\n"
+
+#: fe-secure-gssapi.c:354 fe-secure-gssapi.c:596
+#, c-format
+msgid "oversize GSSAPI packet sent by the server (%zu > %zu)\n"
+msgstr "서버의 GSSAPI 패킷이 너무 큼 (%zu > %zu)\n"
+
+#: fe-secure-gssapi.c:393
+msgid "GSSAPI unwrap error"
+msgstr "GSSAPI 벗기기 오류"
+
+#: fe-secure-gssapi.c:403
+msgid "incoming GSSAPI message did not use confidentiality\n"
+msgstr "GSSAPI 수신 메시지는 기밀성을 사용하지 말아야 함\n"
+
+#: fe-secure-gssapi.c:642
+msgid "could not initiate GSSAPI security context"
+msgstr "GSSAPI 보안 context 초기화 실패"
+
+#: fe-secure-gssapi.c:673
+msgid "GSSAPI size check error"
+msgstr "GSSAPI 크기 검사 오류"
+
+#: fe-secure-gssapi.c:684
+msgid "GSSAPI context establishment error"
+msgstr "GSSAPI context 설정 오류"
+
+#: fe-secure-openssl.c:214 fe-secure-openssl.c:321 fe-secure-openssl.c:1291
+#, c-format
+msgid "SSL SYSCALL error: %s\n"
+msgstr "SSL SYSCALL 오류: %s\n"
+
+#: fe-secure-openssl.c:221 fe-secure-openssl.c:328 fe-secure-openssl.c:1295
+msgid "SSL SYSCALL error: EOF detected\n"
+msgstr "SSL SYSCALL 오류: EOF 감지됨\n"
+
+#: fe-secure-openssl.c:232 fe-secure-openssl.c:339 fe-secure-openssl.c:1304
+#, c-format
+msgid "SSL error: %s\n"
+msgstr "SSL 오류: %s\n"
+
+#: fe-secure-openssl.c:247 fe-secure-openssl.c:354
+msgid "SSL connection has been closed unexpectedly\n"
+msgstr "SSL 연결이 예상치 못하게 끊김\n"
+
+#: fe-secure-openssl.c:253 fe-secure-openssl.c:360 fe-secure-openssl.c:1354
+#, c-format
+msgid "unrecognized SSL error code: %d\n"
+msgstr "알 수 없는 SSL 오류 코드: %d\n"
+
+#: fe-secure-openssl.c:400
+msgid "could not determine server certificate signature algorithm\n"
+msgstr "서버 인증서 서명 알고리즘을 알 수 없음\n"
+
+#: fe-secure-openssl.c:421
+#, c-format
+msgid "could not find digest for NID %s\n"
+msgstr "%s NID용 다이제스트를 찾을 수 없음\n"
+
+#: fe-secure-openssl.c:431
+msgid "could not generate peer certificate hash\n"
+msgstr "피어 인증 해시 값을 만들 수 없음\n"
+
+#: fe-secure-openssl.c:488
+msgid "SSL certificate's name entry is missing\n"
+msgstr "SSL 인증서의 이름 항목이 잘못됨\n"
+
+#: fe-secure-openssl.c:815
+#, c-format
+msgid "could not create SSL context: %s\n"
+msgstr "SSL context를 만들 수 없음: %s\n"
+
+#: fe-secure-openssl.c:854
+#, c-format
+msgid "invalid value \"%s\" for minimum SSL protocol version\n"
+msgstr "잘못된 값: \"%s\", 대상: 최소 SSL 프로토콜 버전\n"
+
+#: fe-secure-openssl.c:865
+#, c-format
+msgid "could not set minimum SSL protocol version: %s\n"
+msgstr "최소 SSL 프로토콜 버전을 지정할 수 없음: %s\n"
+
+#: fe-secure-openssl.c:883
+#, c-format
+msgid "invalid value \"%s\" for maximum SSL protocol version\n"
+msgstr "잘못된 값: \"%s\", 대상: 최대 SSL 프로토콜 버전\n"
+
+#: fe-secure-openssl.c:894
+#, c-format
+msgid "could not set maximum SSL protocol version: %s\n"
+msgstr "최대 SSL 프로토콜 버전을 지정할 수 없음: %s\n"
+
+#: fe-secure-openssl.c:930
+#, c-format
+msgid "could not read root certificate file \"%s\": %s\n"
+msgstr "\"%s\" 루트 인증서 파일을 읽을 수 없음: %s\n"
+
+#: fe-secure-openssl.c:974
+msgid ""
+"could not get home directory to locate root certificate file\n"
+"Either provide the file or change sslmode to disable server certificate "
+"verification.\n"
+msgstr ""
+"루트 인증서 파일이 있는 홈 디렉터리를 찾을 수 없음\n"
+"해당 파일을 제공하거나 서버 인증서 확인을 사용하지 않도록 sslmode를 변경하십"
+"시오.\n"
+
+#: fe-secure-openssl.c:978
+#, c-format
+msgid ""
+"root certificate file \"%s\" does not exist\n"
+"Either provide the file or change sslmode to disable server certificate "
+"verification.\n"
+msgstr ""
+"루트 인증서 파일 \"%s\"이(가) 없습니다.\n"
+"해당 파일을 제공하거나 서버 인증서 확인을 사용하지 않도록 sslmode를 변경하십"
+"시오.\n"
+
+#: fe-secure-openssl.c:1009
+#, c-format
+msgid "could not open certificate file \"%s\": %s\n"
+msgstr "\"%s\" 인증서 파일을 열수 없음: %s\n"
+
+#: fe-secure-openssl.c:1028
+#, c-format
+msgid "could not read certificate file \"%s\": %s\n"
+msgstr "\"%s\" 인증서 파일을 읽을 수 없음: %s\n"
+
+#: fe-secure-openssl.c:1053
+#, c-format
+msgid "could not establish SSL connection: %s\n"
+msgstr "SSL 연결을 확립할 수 없음: %s\n"
+
+#: fe-secure-openssl.c:1107
+#, c-format
+msgid "could not load SSL engine \"%s\": %s\n"
+msgstr "SSL 엔진 \"%s\"을(를) 로드할 수 없음: %s\n"
+
+#: fe-secure-openssl.c:1119
+#, c-format
+msgid "could not initialize SSL engine \"%s\": %s\n"
+msgstr "SSL 엔진 \"%s\"을(를) 초기화할 수 없음: %s\n"
+
+#: fe-secure-openssl.c:1135
+#, c-format
+msgid "could not read private SSL key \"%s\" from engine \"%s\": %s\n"
+msgstr "개인 SSL 키 \"%s\"을(를) \"%s\" 엔진에서 읽을 수 없음: %s\n"
+
+#: fe-secure-openssl.c:1149
+#, c-format
+msgid "could not load private SSL key \"%s\" from engine \"%s\": %s\n"
+msgstr "개인 SSL 키 \"%s\"을(를) \"%s\" 엔진에서 읽을 수 없음: %s\n"
+
+#: fe-secure-openssl.c:1186
+#, c-format
+msgid "certificate present, but not private key file \"%s\"\n"
+msgstr "인증서가 있지만, \"%s\" 개인키가 아닙니다.\n"
+
+#: fe-secure-openssl.c:1194
+#, c-format
+msgid ""
+"private key file \"%s\" has group or world access; permissions should be "
+"u=rw (0600) or less\n"
+msgstr ""
+"개인 키 파일 \"%s\"에 그룹 또는 범용 액세스 권한이 있습니다. 권한은 "
+"u=rw(0600) 이하여야 합니다.\n"
+
+#: fe-secure-openssl.c:1219
+#, c-format
+msgid "could not load private key file \"%s\": %s\n"
+msgstr "\"%s\" 개인키 파일을 불러들일 수 없습니다: %s\n"
+
+#: fe-secure-openssl.c:1237
+#, c-format
+msgid "certificate does not match private key file \"%s\": %s\n"
+msgstr "인증서가 \"%s\" 개인키 파일과 맞지 않습니다: %s\n"
+
+#: fe-secure-openssl.c:1337
+#, c-format
+msgid ""
+"This may indicate that the server does not support any SSL protocol version "
+"between %s and %s.\n"
+msgstr ""
+"해당 서버는 SSL 프로토콜 버전 %s - %s 사이를 지원하지 않습니다.\n"
+
+#: fe-secure-openssl.c:1373
+#, c-format
+msgid "certificate could not be obtained: %s\n"
+msgstr "인증서를 구하질 못했습니다: %s\n"
+
+#: fe-secure-openssl.c:1462
+#, c-format
+msgid "no SSL error reported"
+msgstr "SSL 오류 없음이 보고됨"
+
+#: fe-secure-openssl.c:1471
+#, c-format
+msgid "SSL error code %lu"
+msgstr "SSL 오류 번호 %lu"
+
+#: fe-secure-openssl.c:1718
+#, c-format
+msgid "WARNING: sslpassword truncated\n"
+msgstr "경고: sslpassword 삭제됨\n"
+
+#: fe-secure.c:275
+#, c-format
+msgid "could not receive data from server: %s\n"
+msgstr "서버로부터 데이터를 받지 못했음: %s\n"
+
+#: fe-secure.c:390
+#, c-format
+msgid "could not send data to server: %s\n"
+msgstr "서버에 데이터를 보낼 수 없음: %s\n"
+
+#: win32.c:314
+#, c-format
+msgid "unrecognized socket error: 0x%08X/%d"
+msgstr "알 수 없는 소켓오류: 0x%08X/%d"
diff --git a/src/interfaces/libpq/po/ru.po b/src/interfaces/libpq/po/ru.po
new file mode 100644
index 0000000..ac21a95
--- /dev/null
+++ b/src/interfaces/libpq/po/ru.po
@@ -0,0 +1,1402 @@
+# Russian message translation file for libpq
+# Copyright (C) 2001-2016 PostgreSQL Global Development Group
+# This file is distributed under the same license as the PostgreSQL package.
+# Serguei A. Mokhov <mokhov@cs.concordia.ca>, 2001-2004.
+# Oleg Bartunov <oleg@sai.msu.su>, 2005.
+# Andrey Sudnik <sudnikand@yandex.ru>, 2010.
+# Alexander Lakhin <exclusion@gmail.com>, 2012-2017, 2018, 2019, 2020.
+msgid ""
+msgstr ""
+"Project-Id-Version: libpq (PostgreSQL current)\n"
+"Report-Msgid-Bugs-To: pgsql-bugs@lists.postgresql.org\n"
+"POT-Creation-Date: 2021-01-26 17:52+0300\n"
+"PO-Revision-Date: 2020-09-07 15:43+0300\n"
+"Last-Translator: Alexander Lakhin <exclusion@gmail.com>\n"
+"Language-Team: Russian <pgsql-ru-general@postgresql.org>\n"
+"Language: ru\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
+"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
+
+#: fe-auth-scram.c:212
+msgid "malformed SCRAM message (empty message)\n"
+msgstr "неправильное сообщение SCRAM (пустое содержимое)\n"
+
+#: fe-auth-scram.c:218
+msgid "malformed SCRAM message (length mismatch)\n"
+msgstr "неправильное сообщение SCRAM (некорректная длина)\n"
+
+#: fe-auth-scram.c:265
+msgid "incorrect server signature\n"
+msgstr "некорректная сигнатура сервера\n"
+
+#: fe-auth-scram.c:274
+msgid "invalid SCRAM exchange state\n"
+msgstr "ошибочное состояние обмена SCRAM\n"
+
+#: fe-auth-scram.c:296
+#, c-format
+msgid "malformed SCRAM message (attribute \"%c\" expected)\n"
+msgstr "неправильное сообщение SCRAM (ожидался атрибут \"%c\")\n"
+
+#: fe-auth-scram.c:305
+#, c-format
+msgid ""
+"malformed SCRAM message (expected character \"=\" for attribute \"%c\")\n"
+msgstr ""
+"неправильное сообщение SCRAM (для атрибута \"%c\" ожидался символ \"=\")\n"
+
+#: fe-auth-scram.c:346
+msgid "could not generate nonce\n"
+msgstr "не удалось сгенерировать разовый код\n"
+
+#: fe-auth-scram.c:356 fe-auth-scram.c:431 fe-auth-scram.c:579
+#: fe-auth-scram.c:600 fe-auth-scram.c:626 fe-auth-scram.c:641
+#: fe-auth-scram.c:691 fe-auth-scram.c:725 fe-auth.c:289 fe-auth.c:359
+#: fe-auth.c:394 fe-auth.c:611 fe-auth.c:770 fe-auth.c:1129 fe-auth.c:1277
+#: fe-connect.c:892 fe-connect.c:1419 fe-connect.c:1595 fe-connect.c:2200
+#: fe-connect.c:2223 fe-connect.c:2957 fe-connect.c:4605 fe-connect.c:4861
+#: fe-connect.c:4980 fe-connect.c:5233 fe-connect.c:5313 fe-connect.c:5412
+#: fe-connect.c:5668 fe-connect.c:5697 fe-connect.c:5769 fe-connect.c:5793
+#: fe-connect.c:5811 fe-connect.c:5912 fe-connect.c:5921 fe-connect.c:6277
+#: fe-connect.c:6427 fe-exec.c:2747 fe-exec.c:3494 fe-exec.c:3659
+#: fe-gssapi-common.c:111 fe-lobj.c:895 fe-protocol2.c:1207 fe-protocol3.c:995
+#: fe-protocol3.c:1699 fe-secure-common.c:110 fe-secure-gssapi.c:504
+#: fe-secure-openssl.c:440 fe-secure-openssl.c:1091
+msgid "out of memory\n"
+msgstr "нехватка памяти\n"
+
+#: fe-auth-scram.c:364
+msgid "could not encode nonce\n"
+msgstr "не удалось оформить разовый код\n"
+
+#: fe-auth-scram.c:563
+msgid "could not encode client proof\n"
+msgstr "не удалось закодировать подтверждение клиента\n"
+
+#: fe-auth-scram.c:618
+msgid "invalid SCRAM response (nonce mismatch)\n"
+msgstr "неверный ответ SCRAM (несовпадение проверочного кода)\n"
+
+#: fe-auth-scram.c:651
+msgid "malformed SCRAM message (invalid salt)\n"
+msgstr "неправильное сообщение SCRAM (некорректная соль)\n"
+
+#: fe-auth-scram.c:665
+msgid "malformed SCRAM message (invalid iteration count)\n"
+msgstr "неправильное сообщение SCRAM (некорректное число итераций)\n"
+
+#: fe-auth-scram.c:671
+msgid "malformed SCRAM message (garbage at end of server-first-message)\n"
+msgstr ""
+"неправильное сообщение SCRAM (мусор в конце первого сообщения сервера)\n"
+
+#: fe-auth-scram.c:702
+#, c-format
+msgid "error received from server in SCRAM exchange: %s\n"
+msgstr "в ходе обмена SCRAM от сервера получена ошибка: %s\n"
+
+#: fe-auth-scram.c:718
+msgid "malformed SCRAM message (garbage at end of server-final-message)\n"
+msgstr ""
+"неправильное сообщение SCRAM (мусор в конце последнего сообщения сервера)\n"
+
+#: fe-auth-scram.c:737
+msgid "malformed SCRAM message (invalid server signature)\n"
+msgstr "неправильное сообщение SCRAM (неверная сигнатура сервера)\n"
+
+#: fe-auth.c:76
+#, c-format
+msgid "out of memory allocating GSSAPI buffer (%d)\n"
+msgstr "недостаточно памяти для буфера GSSAPI (%d)\n"
+
+#: fe-auth.c:131
+msgid "GSSAPI continuation error"
+msgstr "ошибка продолжения в GSSAPI"
+
+#: fe-auth.c:158 fe-auth.c:388 fe-gssapi-common.c:98 fe-secure-common.c:98
+msgid "host name must be specified\n"
+msgstr "требуется указать имя сервера\n"
+
+#: fe-auth.c:165
+msgid "duplicate GSS authentication request\n"
+msgstr "повторный запрос аутентификации GSS\n"
+
+#: fe-auth.c:230
+#, c-format
+msgid "out of memory allocating SSPI buffer (%d)\n"
+msgstr "недостаточно памяти для буфера SSPI (%d)\n"
+
+#: fe-auth.c:278
+msgid "SSPI continuation error"
+msgstr "ошибка продолжения в SSPI"
+
+#: fe-auth.c:349
+msgid "duplicate SSPI authentication request\n"
+msgstr "повторный запрос аутентификации SSPI\n"
+
+#: fe-auth.c:374
+msgid "could not acquire SSPI credentials"
+msgstr "не удалось получить удостоверение SSPI"
+
+#: fe-auth.c:429
+msgid "channel binding required, but SSL not in use\n"
+msgstr "требуется привязка каналов, но SSL не используется\n"
+
+#: fe-auth.c:436
+msgid "duplicate SASL authentication request\n"
+msgstr "повторный запрос аутентификации SASL\n"
+
+#: fe-auth.c:492
+msgid "channel binding is required, but client does not support it\n"
+msgstr "требуется привязка каналов, но клиент её не поддерживает\n"
+
+#: fe-auth.c:509
+msgid ""
+"server offered SCRAM-SHA-256-PLUS authentication over a non-SSL connection\n"
+msgstr ""
+"сервер предложил аутентификацию SCRAM-SHA-256-PLUS для соединения, не "
+"защищённого SSL\n"
+
+#: fe-auth.c:521
+msgid "none of the server's SASL authentication mechanisms are supported\n"
+msgstr ""
+"ни один из серверных механизмов аутентификации SASL не поддерживается\n"
+
+#: fe-auth.c:529
+msgid ""
+"channel binding is required, but server did not offer an authentication "
+"method that supports channel binding\n"
+msgstr ""
+"требуется привязка каналов, но сервер не предложил поддерживающий её метод "
+"аутентификации\n"
+
+#: fe-auth.c:635
+#, c-format
+msgid "out of memory allocating SASL buffer (%d)\n"
+msgstr "недостаточно памяти для буфера SASL (%d)\n"
+
+#: fe-auth.c:660
+msgid ""
+"AuthenticationSASLFinal received from server, but SASL authentication was "
+"not completed\n"
+msgstr ""
+"c сервера получено сообщение AuthenticationSASLFinal, но аутентификация SASL "
+"ещё не завершена\n"
+
+#: fe-auth.c:737
+msgid "SCM_CRED authentication method not supported\n"
+msgstr "аутентификация SCM_CRED не поддерживается\n"
+
+#: fe-auth.c:836
+msgid ""
+"channel binding required, but server authenticated client without channel "
+"binding\n"
+msgstr ""
+"требуется привязка каналов, но сервер аутентифицировал клиента без привязки\n"
+
+#: fe-auth.c:842
+msgid ""
+"channel binding required but not supported by server's authentication "
+"request\n"
+msgstr ""
+"требуется привязка каналов, но она не поддерживается при том запросе "
+"аутентификации, который передал сервер\n"
+
+#: fe-auth.c:875
+msgid "Kerberos 4 authentication not supported\n"
+msgstr "аутентификация Kerberos 4 не поддерживается\n"
+
+#: fe-auth.c:880
+msgid "Kerberos 5 authentication not supported\n"
+msgstr "аутентификация Kerberos 5 не поддерживается\n"
+
+#: fe-auth.c:951
+msgid "GSSAPI authentication not supported\n"
+msgstr "аутентификация через GSSAPI не поддерживается\n"
+
+#: fe-auth.c:983
+msgid "SSPI authentication not supported\n"
+msgstr "аутентификация через SSPI не поддерживается\n"
+
+#: fe-auth.c:991
+msgid "Crypt authentication not supported\n"
+msgstr "аутентификация Crypt не поддерживается\n"
+
+#: fe-auth.c:1057
+#, c-format
+msgid "authentication method %u not supported\n"
+msgstr "метод аутентификации %u не поддерживается\n"
+
+#: fe-auth.c:1104
+#, c-format
+msgid "user name lookup failure: error code %lu\n"
+msgstr "распознать имя пользователя не удалось (код ошибки: %lu)\n"
+
+#: fe-auth.c:1114 fe-connect.c:2834
+#, c-format
+msgid "could not look up local user ID %d: %s\n"
+msgstr "найти локального пользователя по идентификатору (%d) не удалось: %s\n"
+
+#: fe-auth.c:1119 fe-connect.c:2839
+#, c-format
+msgid "local user with ID %d does not exist\n"
+msgstr "локальный пользователь с ID %d не существует\n"
+
+#: fe-auth.c:1221
+msgid "unexpected shape of result set returned for SHOW\n"
+msgstr "неожиданная форма набора результатов, возвращённого для SHOW\n"
+
+#: fe-auth.c:1230
+msgid "password_encryption value too long\n"
+msgstr "слишком длинное значение password_encryption\n"
+
+#: fe-auth.c:1270
+#, c-format
+msgid "unrecognized password encryption algorithm \"%s\"\n"
+msgstr "нераспознанный алгоритм шифрования пароля \"%s\"\n"
+
+#: fe-connect.c:1075
+#, c-format
+msgid "could not match %d host names to %d hostaddr values\n"
+msgstr "не удалось сопоставить имена узлов (%d) со значениями hostaddr (%d)\n"
+
+#: fe-connect.c:1156
+#, c-format
+msgid "could not match %d port numbers to %d hosts\n"
+msgstr "не удалось сопоставить номера портов (%d) с узлами (%d)\n"
+
+#: fe-connect.c:1249
+#, c-format
+msgid "invalid channel_binding value: \"%s\"\n"
+msgstr "неверное значение channel_binding: \"%s\"\n"
+
+#: fe-connect.c:1275
+#, c-format
+msgid "invalid sslmode value: \"%s\"\n"
+msgstr "неверное значение sslmode: \"%s\"\n"
+
+#: fe-connect.c:1296
+#, c-format
+msgid "sslmode value \"%s\" invalid when SSL support is not compiled in\n"
+msgstr "значение sslmode \"%s\" недопустимо для сборки без поддержки SSL\n"
+
+#: fe-connect.c:1317
+#, c-format
+msgid "invalid ssl_min_protocol_version value: \"%s\"\n"
+msgstr "неверное значение ssl_min_protocol_version: \"%s\"\n"
+
+#: fe-connect.c:1325
+#, c-format
+msgid "invalid ssl_max_protocol_version value: \"%s\"\n"
+msgstr "неверное значение ssl_max_protocol_version: \"%s\"\n"
+
+#: fe-connect.c:1342
+msgid "invalid SSL protocol version range\n"
+msgstr "неверный диапазон версий протокола SSL\n"
+
+#: fe-connect.c:1357
+#, c-format
+msgid "invalid gssencmode value: \"%s\"\n"
+msgstr "неверное значение gssencmode: \"%s\"\n"
+
+#: fe-connect.c:1366
+#, c-format
+msgid ""
+"gssencmode value \"%s\" invalid when GSSAPI support is not compiled in\n"
+msgstr ""
+"значение gssencmode \"%s\" недопустимо для сборки без поддержки GSSAPI\n"
+
+#: fe-connect.c:1401
+#, c-format
+msgid "invalid target_session_attrs value: \"%s\"\n"
+msgstr "неверное значение target_session_attrs: \"%s\"\n"
+
+#: fe-connect.c:1619
+#, c-format
+msgid "could not set socket to TCP no delay mode: %s\n"
+msgstr "не удалось перевести сокет в режим TCP-передачи без задержки: %s\n"
+
+#: fe-connect.c:1680
+#, c-format
+msgid ""
+"could not connect to server: %s\n"
+"\tIs the server running locally and accepting\n"
+"\tconnections on Unix domain socket \"%s\"?\n"
+msgstr ""
+"не удалось подключиться к серверу: %s\n"
+"\tОн действительно работает локально и принимает\n"
+"\tсоединения через Unix-сокет \"%s\"?\n"
+
+#: fe-connect.c:1717
+#, c-format
+msgid ""
+"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"
+msgstr ""
+"не удалось подключиться к серверу: %s\n"
+"\tОн действительно работает по адресу \"%s\" (%s)\n"
+"\t и принимает TCP-соединения (порт %s)?\n"
+
+#: fe-connect.c:1725
+#, c-format
+msgid ""
+"could not connect to server: %s\n"
+"\tIs the server running on host \"%s\" and accepting\n"
+"\tTCP/IP connections on port %s?\n"
+msgstr ""
+"не удалось подключиться к серверу: %s\n"
+"\tОн действительно работает по адресу \"%s\"\n"
+"\t и принимает TCP-соединения (порт %s)?\n"
+
+#: fe-connect.c:1795
+#, c-format
+msgid "invalid integer value \"%s\" for connection option \"%s\"\n"
+msgstr ""
+"неверное целочисленное значение \"%s\" для параметра соединения \"%s\"\n"
+
+#: fe-connect.c:1825 fe-connect.c:1859 fe-connect.c:1894 fe-connect.c:1981
+#: fe-connect.c:2623
+#, c-format
+msgid "setsockopt(%s) failed: %s\n"
+msgstr "ошибка в setsockopt(%s): %s\n"
+
+#: fe-connect.c:1947
+#, c-format
+msgid "WSAIoctl(SIO_KEEPALIVE_VALS) failed: %ui\n"
+msgstr "ошибка в WSAIoctl(SIO_KEEPALIVE_VALS): %ui\n"
+
+#: fe-connect.c:2313
+msgid "invalid connection state, probably indicative of memory corruption\n"
+msgstr "неверное состояние соединения - возможно разрушение памяти\n"
+
+#: fe-connect.c:2379
+#, c-format
+msgid "invalid port number: \"%s\"\n"
+msgstr "неверный номер порта: \"%s\"\n"
+
+#: fe-connect.c:2395
+#, c-format
+msgid "could not translate host name \"%s\" to address: %s\n"
+msgstr "преобразовать имя \"%s\" в адрес не удалось: %s\n"
+
+#: fe-connect.c:2408
+#, c-format
+msgid "could not parse network address \"%s\": %s\n"
+msgstr "не удалось разобрать сетевой адрес \"%s\": %s\n"
+
+#: fe-connect.c:2421
+#, c-format
+msgid "Unix-domain socket path \"%s\" is too long (maximum %d bytes)\n"
+msgstr "длина пути Unix-сокета \"%s\" превышает предел (%d байт)\n"
+
+#: fe-connect.c:2436
+#, c-format
+msgid "could not translate Unix-domain socket path \"%s\" to address: %s\n"
+msgstr "преобразовать путь Unix-сокета \"%s\" в адрес не удалось: %s\n"
+
+#: fe-connect.c:2560
+#, c-format
+msgid "could not create socket: %s\n"
+msgstr "не удалось создать сокет: %s\n"
+
+#: fe-connect.c:2582
+#, c-format
+msgid "could not set socket to nonblocking mode: %s\n"
+msgstr "не удалось перевести сокет в неблокирующий режим: %s\n"
+
+#: fe-connect.c:2592
+#, c-format
+msgid "could not set socket to close-on-exec mode: %s\n"
+msgstr ""
+"не удалось перевести сокет в режим закрытия при выполнении (close-on-exec): "
+"%s\n"
+
+#: fe-connect.c:2610
+msgid "keepalives parameter must be an integer\n"
+msgstr "параметр keepalives должен быть целым числом\n"
+
+#: fe-connect.c:2750
+#, c-format
+msgid "could not get socket error status: %s\n"
+msgstr "не удалось получить статус ошибки сокета: %s\n"
+
+#: fe-connect.c:2778
+#, c-format
+msgid "could not get client address from socket: %s\n"
+msgstr "не удалось получить адрес клиента из сокета: %s\n"
+
+#: fe-connect.c:2820
+msgid "requirepeer parameter is not supported on this platform\n"
+msgstr "параметр requirepeer не поддерживается в этой ОС\n"
+
+#: fe-connect.c:2823
+#, c-format
+msgid "could not get peer credentials: %s\n"
+msgstr "не удалось получить учётные данные сервера: %s\n"
+
+#: fe-connect.c:2847
+#, c-format
+msgid "requirepeer specifies \"%s\", but actual peer user name is \"%s\"\n"
+msgstr ""
+"requirepeer допускает подключение только к \"%s\", но сервер работает под "
+"именем \"%s\"\n"
+
+#: fe-connect.c:2887
+#, c-format
+msgid "could not send GSSAPI negotiation packet: %s\n"
+msgstr "не удалось отправить пакет согласования GSSAPI: %s\n"
+
+#: fe-connect.c:2899
+msgid ""
+"GSSAPI encryption required but was impossible (possibly no credential cache, "
+"no server support, or using a local socket)\n"
+msgstr ""
+"затребовано шифрование GSSAPI, но это требование невыполнимо (возможно, "
+"отсутствует кеш учётных данных, нет поддержки на сервере или используется "
+"локальный сокет)\n"
+
+#: fe-connect.c:2931
+#, c-format
+msgid "could not send SSL negotiation packet: %s\n"
+msgstr "не удалось отправить пакет согласования SSL: %s\n"
+
+#: fe-connect.c:2970
+#, c-format
+msgid "could not send startup packet: %s\n"
+msgstr "не удалось отправить стартовый пакет: %s\n"
+
+#: fe-connect.c:3040
+msgid "server does not support SSL, but SSL was required\n"
+msgstr "затребовано подключение через SSL, но сервер не поддерживает SSL\n"
+
+#: fe-connect.c:3067
+#, c-format
+msgid "received invalid response to SSL negotiation: %c\n"
+msgstr "получен неверный ответ при согласовании SSL: %c\n"
+
+#: fe-connect.c:3156
+msgid "server doesn't support GSSAPI encryption, but it was required\n"
+msgstr "затребовано шифрование GSSAPI, но сервер его не поддерживает\n"
+
+#: fe-connect.c:3168
+#, c-format
+msgid "received invalid response to GSSAPI negotiation: %c\n"
+msgstr "получен неверный ответ при согласовании GSSAPI: %c\n"
+
+#: fe-connect.c:3234 fe-connect.c:3265
+#, c-format
+msgid "expected authentication request from server, but received %c\n"
+msgstr "ожидался запрос аутентификации от сервера, но получено: %c\n"
+
+#: fe-connect.c:3506
+msgid "unexpected message from server during startup\n"
+msgstr "неожиданное сообщение от сервера в начале работы\n"
+
+#: fe-connect.c:3711
+#, c-format
+msgid "could not make a writable connection to server \"%s:%s\"\n"
+msgstr ""
+"не удалось установить подключение для чтения/записи к серверу \"%s:%s\"\n"
+
+#: fe-connect.c:3757
+#, c-format
+msgid "test \"SHOW transaction_read_only\" failed on server \"%s:%s\"\n"
+msgstr ""
+"проверка \"SHOW transaction_read_only\" не пройдена на сервере \"%s:%s\"\n"
+
+#: fe-connect.c:3772
+#, c-format
+msgid "invalid connection state %d, probably indicative of memory corruption\n"
+msgstr "неверное состояние соединения %d - возможно разрушение памяти\n"
+
+#: fe-connect.c:4211 fe-connect.c:4271
+#, c-format
+msgid "PGEventProc \"%s\" failed during PGEVT_CONNRESET event\n"
+msgstr "ошибка в PGEventProc \"%s\" при обработке события PGEVT_CONNRESET\n"
+
+#: fe-connect.c:4618
+#, c-format
+msgid "invalid LDAP URL \"%s\": scheme must be ldap://\n"
+msgstr "некорректный адрес LDAP \"%s\": схема должна быть ldap://\n"
+
+#: fe-connect.c:4633
+#, c-format
+msgid "invalid LDAP URL \"%s\": missing distinguished name\n"
+msgstr "некорректный адрес LDAP \"%s\": отсутствует уникальное имя\n"
+
+#: fe-connect.c:4645 fe-connect.c:4700
+#, c-format
+msgid "invalid LDAP URL \"%s\": must have exactly one attribute\n"
+msgstr "некорректный адрес LDAP \"%s\": должен быть только один атрибут\n"
+
+#: fe-connect.c:4656 fe-connect.c:4715
+#, c-format
+msgid "invalid LDAP URL \"%s\": must have search scope (base/one/sub)\n"
+msgstr ""
+"некорректный адрес LDAP \"%s\": не указана область поиска (base/one/sub)\n"
+
+#: fe-connect.c:4667
+#, c-format
+msgid "invalid LDAP URL \"%s\": no filter\n"
+msgstr "некорректный адрес LDAP \"%s\": нет фильтра\n"
+
+#: fe-connect.c:4688
+#, c-format
+msgid "invalid LDAP URL \"%s\": invalid port number\n"
+msgstr "некорректный адрес LDAP \"%s\": неверный номер порта\n"
+
+#: fe-connect.c:4724
+msgid "could not create LDAP structure\n"
+msgstr "не удалось создать структуру LDAP\n"
+
+#: fe-connect.c:4800
+#, c-format
+msgid "lookup on LDAP server failed: %s\n"
+msgstr "ошибка поиска на сервере LDAP: %s\n"
+
+#: fe-connect.c:4811
+msgid "more than one entry found on LDAP lookup\n"
+msgstr "при поиске LDAP найдено более одного вхождения\n"
+
+#: fe-connect.c:4812 fe-connect.c:4824
+msgid "no entry found on LDAP lookup\n"
+msgstr "при поиске LDAP ничего не найдено\n"
+
+#: fe-connect.c:4835 fe-connect.c:4848
+msgid "attribute has no values on LDAP lookup\n"
+msgstr "атрибут не содержит значений при поиске LDAP\n"
+
+#: fe-connect.c:4900 fe-connect.c:4919 fe-connect.c:5451
+#, c-format
+msgid "missing \"=\" after \"%s\" in connection info string\n"
+msgstr "в строке соединения нет \"=\" после \"%s\"\n"
+
+#: fe-connect.c:4992 fe-connect.c:5636 fe-connect.c:6410
+#, c-format
+msgid "invalid connection option \"%s\"\n"
+msgstr "неверный параметр соединения \"%s\"\n"
+
+#: fe-connect.c:5008 fe-connect.c:5500
+msgid "unterminated quoted string in connection info string\n"
+msgstr "в строке соединения не хватает закрывающей кавычки\n"
+
+#: fe-connect.c:5091
+#, c-format
+msgid "definition of service \"%s\" not found\n"
+msgstr "определение службы \"%s\" не найдено\n"
+
+#: fe-connect.c:5114
+#, c-format
+msgid "service file \"%s\" not found\n"
+msgstr "файл определений служб \"%s\" не найден\n"
+
+#: fe-connect.c:5129
+#, c-format
+msgid "line %d too long in service file \"%s\"\n"
+msgstr "слишком длинная строка (%d) в файле определений служб \"%s\"\n"
+
+#: fe-connect.c:5201 fe-connect.c:5245
+#, c-format
+msgid "syntax error in service file \"%s\", line %d\n"
+msgstr "синтаксическая ошибка в файле определения служб \"%s\" (строка %d)\n"
+
+#: fe-connect.c:5212
+#, c-format
+msgid ""
+"nested service specifications not supported in service file \"%s\", line %d\n"
+msgstr ""
+"рекурсивные определения служб не поддерживаются (файл определения служб \"%s"
+"\", строка %d)\n"
+
+#: fe-connect.c:5932
+#, c-format
+msgid "invalid URI propagated to internal parser routine: \"%s\"\n"
+msgstr "во внутреннюю процедуру разбора строки передан ошибочный URI: \"%s\"\n"
+
+#: fe-connect.c:6009
+#, c-format
+msgid ""
+"end of string reached when looking for matching \"]\" in IPv6 host address "
+"in URI: \"%s\"\n"
+msgstr "URI не содержит символ \"]\" после адреса IPv6: \"%s\"\n"
+
+#: fe-connect.c:6016
+#, c-format
+msgid "IPv6 host address may not be empty in URI: \"%s\"\n"
+msgstr "IPv6, содержащийся в URI, не может быть пустым: \"%s\"\n"
+
+#: fe-connect.c:6031
+#, c-format
+msgid ""
+"unexpected character \"%c\" at position %d in URI (expected \":\" or \"/\"): "
+"\"%s\"\n"
+msgstr ""
+"неожиданный символ \"%c\" в позиции %d в URI (ожидалось \":\" или \"/\"): "
+"\"%s\"\n"
+
+#: fe-connect.c:6160
+#, c-format
+msgid "extra key/value separator \"=\" in URI query parameter: \"%s\"\n"
+msgstr "лишний разделитель ключа/значения \"=\" в параметрах URI: \"%s\"\n"
+
+#: fe-connect.c:6180
+#, c-format
+msgid "missing key/value separator \"=\" in URI query parameter: \"%s\"\n"
+msgstr "в параметрах URI не хватает разделителя ключа/значения \"=\": \"%s\"\n"
+
+#: fe-connect.c:6231
+#, c-format
+msgid "invalid URI query parameter: \"%s\"\n"
+msgstr "неверный параметр в URI: \"%s\"\n"
+
+#: fe-connect.c:6305
+#, c-format
+msgid "invalid percent-encoded token: \"%s\"\n"
+msgstr "неверный символ, закодированный с %%: \"%s\"\n"
+
+#: fe-connect.c:6315
+#, c-format
+msgid "forbidden value %%00 in percent-encoded value: \"%s\"\n"
+msgstr "недопустимое значение %%00 для символа, закодированного с %%: \"%s\"\n"
+
+#: fe-connect.c:6678
+msgid "connection pointer is NULL\n"
+msgstr "нулевой указатель соединения\n"
+
+#: fe-connect.c:6974
+#, c-format
+msgid "WARNING: password file \"%s\" is not a plain file\n"
+msgstr "ПРЕДУПРЕЖДЕНИЕ: файл паролей \"%s\" - не обычный файл\n"
+
+#: fe-connect.c:6983
+#, c-format
+msgid ""
+"WARNING: password file \"%s\" has group or world access; permissions should "
+"be u=rw (0600) or less\n"
+msgstr ""
+"ПРЕДУПРЕЖДЕНИЕ: к файлу паролей \"%s\" имеют доступ все или группа; права "
+"должны быть u=rw (0600) или более ограниченные\n"
+
+#: fe-connect.c:7091
+#, c-format
+msgid "password retrieved from file \"%s\"\n"
+msgstr "пароль получен из файла \"%s\"\n"
+
+#: fe-exec.c:444 fe-exec.c:2821
+#, c-format
+msgid "row number %d is out of range 0..%d"
+msgstr "номер записи %d вне диапазона 0..%d"
+
+#: fe-exec.c:505 fe-protocol2.c:497 fe-protocol2.c:532 fe-protocol2.c:1050
+#: fe-protocol3.c:206 fe-protocol3.c:233 fe-protocol3.c:250 fe-protocol3.c:330
+#: fe-protocol3.c:723 fe-protocol3.c:954
+msgid "out of memory"
+msgstr "нехватка памяти"
+
+#: fe-exec.c:506 fe-protocol2.c:1396 fe-protocol3.c:1907
+#, c-format
+msgid "%s"
+msgstr "%s"
+
+#: fe-exec.c:815
+msgid "write to server failed\n"
+msgstr "ошибка при передаче данных серверу\n"
+
+#: fe-exec.c:896
+msgid "NOTICE"
+msgstr "ЗАМЕЧАНИЕ"
+
+#: fe-exec.c:954
+msgid "PGresult cannot support more than INT_MAX tuples"
+msgstr "PGresult не может вместить больше чем INT_MAX кортежей"
+
+#: fe-exec.c:966
+msgid "size_t overflow"
+msgstr "переполнение size_t"
+
+#: fe-exec.c:1243 fe-exec.c:1301 fe-exec.c:1347
+msgid "command string is a null pointer\n"
+msgstr "указатель на командную строку нулевой\n"
+
+#: fe-exec.c:1307 fe-exec.c:1353 fe-exec.c:1448
+msgid "number of parameters must be between 0 and 65535\n"
+msgstr "число параметров должно быть от 0 до 65535\n"
+
+#: fe-exec.c:1341 fe-exec.c:1442
+msgid "statement name is a null pointer\n"
+msgstr "указатель на имя оператора нулевой\n"
+
+#: fe-exec.c:1361 fe-exec.c:1524 fe-exec.c:2233 fe-exec.c:2435
+msgid "function requires at least protocol version 3.0\n"
+msgstr "функция требует протокол минимум версии 3.0\n"
+
+#: fe-exec.c:1479
+msgid "no connection to the server\n"
+msgstr "нет соединения с сервером\n"
+
+#: fe-exec.c:1486
+msgid "another command is already in progress\n"
+msgstr "уже выполняется другая команда\n"
+
+#: fe-exec.c:1600
+msgid "length must be given for binary parameter\n"
+msgstr "для двоичного параметра должна быть указана длина\n"
+
+#: fe-exec.c:1863
+#, c-format
+msgid "unexpected asyncStatus: %d\n"
+msgstr "неожиданный asyncStatus: %d\n"
+
+#: fe-exec.c:1883
+#, c-format
+msgid "PGEventProc \"%s\" failed during PGEVT_RESULTCREATE event\n"
+msgstr "ошибка в PGEventProc \"%s\" при обработке события PGEVT_RESULTCREATE\n"
+
+#: fe-exec.c:2043
+msgid "COPY terminated by new PQexec"
+msgstr "операция COPY прервана вызовом PQexec"
+
+#: fe-exec.c:2051
+msgid "COPY IN state must be terminated first\n"
+msgstr "сначала должно завершиться состояние COPY IN\n"
+
+#: fe-exec.c:2071
+msgid "COPY OUT state must be terminated first\n"
+msgstr "сначала должно завершиться состояние COPY OUT\n"
+
+#: fe-exec.c:2079
+msgid "PQexec not allowed during COPY BOTH\n"
+msgstr "вызов PQexec не допускается в процессе COPY BOTH\n"
+
+#: fe-exec.c:2325 fe-exec.c:2392 fe-exec.c:2482 fe-protocol2.c:1353
+#: fe-protocol3.c:1838
+msgid "no COPY in progress\n"
+msgstr "операция COPY не выполняется\n"
+
+#: fe-exec.c:2672
+msgid "connection in wrong state\n"
+msgstr "соединение в неправильном состоянии\n"
+
+#: fe-exec.c:2703
+msgid "invalid ExecStatusType code"
+msgstr "неверный код ExecStatusType"
+
+#: fe-exec.c:2730
+msgid "PGresult is not an error result\n"
+msgstr "В PGresult не передан результат ошибки\n"
+
+#: fe-exec.c:2805 fe-exec.c:2828
+#, c-format
+msgid "column number %d is out of range 0..%d"
+msgstr "номер столбца %d вне диапазона 0..%d"
+
+#: fe-exec.c:2843
+#, c-format
+msgid "parameter number %d is out of range 0..%d"
+msgstr "номер параметра %d вне диапазона 0..%d"
+
+#: fe-exec.c:3153
+#, c-format
+msgid "could not interpret result from server: %s"
+msgstr "не удалось интерпретировать ответ сервера: %s"
+
+#: fe-exec.c:3392 fe-exec.c:3476
+msgid "incomplete multibyte character\n"
+msgstr "неполный многобайтный символ\n"
+
+#: fe-gssapi-common.c:124
+msgid "GSSAPI name import error"
+msgstr "ошибка импорта имени в GSSAPI"
+
+#: fe-lobj.c:154
+msgid "cannot determine OID of function lo_truncate\n"
+msgstr "не удалось определить OID функции lo_truncate\n"
+
+#: fe-lobj.c:170
+msgid "argument of lo_truncate exceeds integer range\n"
+msgstr "аргумент lo_truncate не умещается в обычном целом\n"
+
+#: fe-lobj.c:221
+msgid "cannot determine OID of function lo_truncate64\n"
+msgstr "не удалось определить OID функции lo_truncate64\n"
+
+#: fe-lobj.c:279
+msgid "argument of lo_read exceeds integer range\n"
+msgstr "аргумент lo_read не умещается в обычном целом\n"
+
+#: fe-lobj.c:334
+msgid "argument of lo_write exceeds integer range\n"
+msgstr "аргумент lo_write не умещается в обычном целом\n"
+
+#: fe-lobj.c:425
+msgid "cannot determine OID of function lo_lseek64\n"
+msgstr "не удалось определить OID функции lo_lseek64\n"
+
+#: fe-lobj.c:521
+msgid "cannot determine OID of function lo_create\n"
+msgstr "не удалось определить OID функции lo_create\n"
+
+#: fe-lobj.c:600
+msgid "cannot determine OID of function lo_tell64\n"
+msgstr "не удалось определить OID функции lo_tell64\n"
+
+#: fe-lobj.c:706 fe-lobj.c:815
+#, c-format
+msgid "could not open file \"%s\": %s\n"
+msgstr "не удалось открыть файл \"%s\": %s\n"
+
+#: fe-lobj.c:761
+#, c-format
+msgid "could not read from file \"%s\": %s\n"
+msgstr "не удалось прочитать файл \"%s\": %s\n"
+
+#: fe-lobj.c:835 fe-lobj.c:859
+#, c-format
+msgid "could not write to file \"%s\": %s\n"
+msgstr "не удалось записать файл \"%s\": %s\n"
+
+#: fe-lobj.c:946
+msgid "query to initialize large object functions did not return data\n"
+msgstr "запрос инициализации функций для больших объектов не вернул данные\n"
+
+#: fe-lobj.c:995
+msgid "cannot determine OID of function lo_open\n"
+msgstr "не удалось определить OID функции lo_open\n"
+
+#: fe-lobj.c:1002
+msgid "cannot determine OID of function lo_close\n"
+msgstr "не удалось определить OID функции lo_close\n"
+
+#: fe-lobj.c:1009
+msgid "cannot determine OID of function lo_creat\n"
+msgstr "не удалось определить OID функции lo_creat\n"
+
+#: fe-lobj.c:1016
+msgid "cannot determine OID of function lo_unlink\n"
+msgstr "не удалось определить OID функции lo_unlink\n"
+
+#: fe-lobj.c:1023
+msgid "cannot determine OID of function lo_lseek\n"
+msgstr "не удалось определить OID функции lo_lseek\n"
+
+#: fe-lobj.c:1030
+msgid "cannot determine OID of function lo_tell\n"
+msgstr "не удалось определить OID функции lo_tell\n"
+
+#: fe-lobj.c:1037
+msgid "cannot determine OID of function loread\n"
+msgstr "не удалось определить OID функции loread\n"
+
+#: fe-lobj.c:1044
+msgid "cannot determine OID of function lowrite\n"
+msgstr "не удалось определить OID функции lowrite\n"
+
+#: fe-misc.c:289
+#, c-format
+msgid "integer of size %lu not supported by pqGetInt"
+msgstr "функция pqGetInt не поддерживает integer размером %lu байт"
+
+#: fe-misc.c:325
+#, c-format
+msgid "integer of size %lu not supported by pqPutInt"
+msgstr "функция pqPutInt не поддерживает integer размером %lu байт"
+
+#: fe-misc.c:636 fe-misc.c:869
+msgid "connection not open\n"
+msgstr "соединение не открыто\n"
+
+#: fe-misc.c:805 fe-secure-openssl.c:209 fe-secure-openssl.c:316
+#: fe-secure.c:267 fe-secure.c:383
+msgid ""
+"server closed the connection unexpectedly\n"
+"\tThis probably means the server terminated abnormally\n"
+"\tbefore or while processing the request.\n"
+msgstr ""
+"сервер неожиданно закрыл соединение\n"
+"\tСкорее всего сервер прекратил работу из-за сбоя\n"
+"\tдо или в процессе выполнения запроса.\n"
+
+#: fe-misc.c:1063
+msgid "timeout expired\n"
+msgstr "тайм-аут\n"
+
+#: fe-misc.c:1108
+msgid "invalid socket\n"
+msgstr "неверный сокет\n"
+
+#: fe-misc.c:1131
+#, c-format
+msgid "select() failed: %s\n"
+msgstr "ошибка в select(): %s\n"
+
+#: fe-protocol2.c:87
+#, c-format
+msgid "invalid setenv state %c, probably indicative of memory corruption\n"
+msgstr "неверное состояние setenv %c - возможно разрушение памяти\n"
+
+#: fe-protocol2.c:384
+#, c-format
+msgid "invalid state %c, probably indicative of memory corruption\n"
+msgstr "неверное состояние %c - возможно разрушение памяти\n"
+
+#: fe-protocol2.c:473 fe-protocol3.c:183
+#, c-format
+msgid "message type 0x%02x arrived from server while idle"
+msgstr "от сервера во время простоя получено сообщение типа 0x%02x"
+
+#: fe-protocol2.c:523
+#, c-format
+msgid "unexpected character %c following empty query response (\"I\" message)"
+msgstr "неожиданный символ %c вслед за пустым ответом (сообщение \"I\")"
+
+#: fe-protocol2.c:589
+#, c-format
+msgid ""
+"server sent data (\"D\" message) without prior row description (\"T\" "
+"message)"
+msgstr ""
+"сервер отправил данные (сообщение \"D\") без предварительного описания "
+"строки (сообщение \"T\")"
+
+#: fe-protocol2.c:607
+#, c-format
+msgid ""
+"server sent binary data (\"B\" message) without prior row description (\"T\" "
+"message)"
+msgstr ""
+"сервер отправил двоичные данные (сообщение \"B\") без предварительного "
+"описания строки (сообщение \"T\")"
+
+#: fe-protocol2.c:626 fe-protocol3.c:408
+#, c-format
+msgid "unexpected response from server; first received character was \"%c\"\n"
+msgstr "неожиданный ответ сервера; первый полученный символ: \"%c\"\n"
+
+#: fe-protocol2.c:755 fe-protocol2.c:930 fe-protocol3.c:622 fe-protocol3.c:849
+msgid "out of memory for query result"
+msgstr "недостаточно памяти для результата запроса"
+
+#: fe-protocol2.c:1408
+#, c-format
+msgid "lost synchronization with server, resetting connection"
+msgstr "потеряна синхронизация с сервером; попытка восстановить соединение"
+
+#: fe-protocol2.c:1530 fe-protocol2.c:1562 fe-protocol3.c:2095
+#, c-format
+msgid "protocol error: id=0x%x\n"
+msgstr "ошибка протокола: id=0x%x\n"
+
+#: fe-protocol3.c:365
+msgid ""
+"server sent data (\"D\" message) without prior row description (\"T\" "
+"message)\n"
+msgstr ""
+"сервер отправил данные (сообщение \"D\") без предварительного описания "
+"строки (сообщение \"T\")\n"
+
+#: fe-protocol3.c:429
+#, c-format
+msgid "message contents do not agree with length in message type \"%c\"\n"
+msgstr "содержимое не соответствует длине в сообщении типа \"%c\"\n"
+
+#: fe-protocol3.c:449
+#, c-format
+msgid "lost synchronization with server: got message type \"%c\", length %d\n"
+msgstr ""
+"потеряна синхронизация с сервером: получено сообщение типа \"%c\", длина %d\n"
+
+#: fe-protocol3.c:500 fe-protocol3.c:540
+msgid "insufficient data in \"T\" message"
+msgstr "недостаточно данных в сообщении \"T\""
+
+#: fe-protocol3.c:573
+msgid "extraneous data in \"T\" message"
+msgstr "лишние данные в сообщении \"T\""
+
+#: fe-protocol3.c:686
+msgid "extraneous data in \"t\" message"
+msgstr "лишние данные в сообщении \"t\""
+
+#: fe-protocol3.c:757 fe-protocol3.c:789 fe-protocol3.c:807
+msgid "insufficient data in \"D\" message"
+msgstr "недостаточно данных в сообщении \"D\""
+
+#: fe-protocol3.c:763
+msgid "unexpected field count in \"D\" message"
+msgstr "неверное число полей в сообщении \"D\""
+
+#: fe-protocol3.c:816
+msgid "extraneous data in \"D\" message"
+msgstr "лишние данные в сообщении \"D\""
+
+#: fe-protocol3.c:1008
+msgid "no error message available\n"
+msgstr "нет сообщения об ошибке\n"
+
+#. translator: %s represents a digit string
+#: fe-protocol3.c:1056 fe-protocol3.c:1075
+#, c-format
+msgid " at character %s"
+msgstr " символ %s"
+
+#: fe-protocol3.c:1088
+#, c-format
+msgid "DETAIL: %s\n"
+msgstr "ПОДРОБНОСТИ: %s\n"
+
+#: fe-protocol3.c:1091
+#, c-format
+msgid "HINT: %s\n"
+msgstr "ПОДСКАЗКА: %s\n"
+
+#: fe-protocol3.c:1094
+#, c-format
+msgid "QUERY: %s\n"
+msgstr "ЗАПРОС: %s\n"
+
+#: fe-protocol3.c:1101
+#, c-format
+msgid "CONTEXT: %s\n"
+msgstr "КОНТЕКСТ: %s\n"
+
+#: fe-protocol3.c:1110
+#, c-format
+msgid "SCHEMA NAME: %s\n"
+msgstr "СХЕМА: %s\n"
+
+#: fe-protocol3.c:1114
+#, c-format
+msgid "TABLE NAME: %s\n"
+msgstr "ТАБЛИЦА: %s\n"
+
+#: fe-protocol3.c:1118
+#, c-format
+msgid "COLUMN NAME: %s\n"
+msgstr "СТОЛБЕЦ: %s\n"
+
+#: fe-protocol3.c:1122
+#, c-format
+msgid "DATATYPE NAME: %s\n"
+msgstr "ТИП ДАННЫХ: %s\n"
+
+#: fe-protocol3.c:1126
+#, c-format
+msgid "CONSTRAINT NAME: %s\n"
+msgstr "ОГРАНИЧЕНИЕ: %s\n"
+
+#: fe-protocol3.c:1138
+msgid "LOCATION: "
+msgstr "ПОЛОЖЕНИЕ: "
+
+#: fe-protocol3.c:1140
+#, c-format
+msgid "%s, "
+msgstr "%s, "
+
+#: fe-protocol3.c:1142
+#, c-format
+msgid "%s:%s"
+msgstr "%s:%s"
+
+#: fe-protocol3.c:1337
+#, c-format
+msgid "LINE %d: "
+msgstr "СТРОКА %d: "
+
+#: fe-protocol3.c:1732
+msgid "PQgetline: not doing text COPY OUT\n"
+msgstr "PQgetline можно вызывать только во время COPY OUT с текстом\n"
+
+#: fe-secure-common.c:124
+msgid "SSL certificate's name contains embedded null\n"
+msgstr "имя в SSL-сертификате включает нулевой байт\n"
+
+#: fe-secure-common.c:171
+msgid "host name must be specified for a verified SSL connection\n"
+msgstr "для проверенного SSL-соединения требуется указать имя узла\n"
+
+#: fe-secure-common.c:196
+#, c-format
+msgid "server certificate for \"%s\" does not match host name \"%s\"\n"
+msgstr ""
+"серверный сертификат для \"%s\" не соответствует имени сервера \"%s\"\n"
+
+#: fe-secure-common.c:202
+msgid "could not get server's host name from server certificate\n"
+msgstr "не удалось получить имя сервера из сертификата\n"
+
+#: fe-secure-gssapi.c:201
+msgid "GSSAPI wrap error"
+msgstr "ошибка обёртывания сообщения в GSSAPI"
+
+#: fe-secure-gssapi.c:209
+msgid "outgoing GSSAPI message would not use confidentiality\n"
+msgstr "исходящее сообщение GSSAPI не будет защищено\n"
+
+#: fe-secure-gssapi.c:217
+#, c-format
+msgid "client tried to send oversize GSSAPI packet (%zu > %zu)\n"
+msgstr "клиент попытался передать чрезмерно большой пакет GSSAPI (%zu > %zu)\n"
+
+#: fe-secure-gssapi.c:354 fe-secure-gssapi.c:596
+#, c-format
+msgid "oversize GSSAPI packet sent by the server (%zu > %zu)\n"
+msgstr "сервер передал чрезмерно большой пакет GSSAPI (%zu > %zu)\n"
+
+#: fe-secure-gssapi.c:393
+msgid "GSSAPI unwrap error"
+msgstr "ошибка развёртывания сообщения в GSSAPI"
+
+#: fe-secure-gssapi.c:403
+msgid "incoming GSSAPI message did not use confidentiality\n"
+msgstr "входящее сообщение GSSAPI не защищено\n"
+
+#: fe-secure-gssapi.c:642
+msgid "could not initiate GSSAPI security context"
+msgstr "не удалось инициализировать контекст безопасности GSSAPI"
+
+#: fe-secure-gssapi.c:670
+msgid "GSSAPI size check error"
+msgstr "ошибка проверки размера в GSSAPI"
+
+#: fe-secure-gssapi.c:681
+msgid "GSSAPI context establishment error"
+msgstr "ошибка установления контекста в GSSAPI"
+
+#: fe-secure-openssl.c:214 fe-secure-openssl.c:321 fe-secure-openssl.c:1291
+#, c-format
+msgid "SSL SYSCALL error: %s\n"
+msgstr "ошибка SSL SYSCALL: %s\n"
+
+#: fe-secure-openssl.c:221 fe-secure-openssl.c:328 fe-secure-openssl.c:1295
+msgid "SSL SYSCALL error: EOF detected\n"
+msgstr "ошибка SSL SYSCALL: конец файла (EOF)\n"
+
+#: fe-secure-openssl.c:232 fe-secure-openssl.c:339 fe-secure-openssl.c:1304
+#, c-format
+msgid "SSL error: %s\n"
+msgstr "ошибка SSL: %s\n"
+
+#: fe-secure-openssl.c:247 fe-secure-openssl.c:354
+msgid "SSL connection has been closed unexpectedly\n"
+msgstr "SSL-соединение было неожиданно закрыто\n"
+
+#: fe-secure-openssl.c:253 fe-secure-openssl.c:360 fe-secure-openssl.c:1354
+#, c-format
+msgid "unrecognized SSL error code: %d\n"
+msgstr "нераспознанный код ошибки SSL: %d\n"
+
+#: fe-secure-openssl.c:400
+msgid "could not determine server certificate signature algorithm\n"
+msgstr "не удалось определить алгоритм подписи сертификата сервера\n"
+
+#: fe-secure-openssl.c:421
+#, c-format
+msgid "could not find digest for NID %s\n"
+msgstr "не удалось найти алгоритм хеширования по NID %s\n"
+
+#: fe-secure-openssl.c:431
+msgid "could not generate peer certificate hash\n"
+msgstr "не удалось сгенерировать хеш сертификата сервера\n"
+
+#: fe-secure-openssl.c:488
+msgid "SSL certificate's name entry is missing\n"
+msgstr "запись имени в SSL-сертификате отсутствует\n"
+
+#: fe-secure-openssl.c:815
+#, c-format
+msgid "could not create SSL context: %s\n"
+msgstr "не удалось создать контекст SSL: %s\n"
+
+#: fe-secure-openssl.c:854
+#, c-format
+msgid "invalid value \"%s\" for minimum SSL protocol version\n"
+msgstr "неверное значение \"%s\" для минимальной версии протокола SSL\n"
+
+#: fe-secure-openssl.c:865
+#, c-format
+msgid "could not set minimum SSL protocol version: %s\n"
+msgstr "не удалось задать минимальную версию протокола SSL: %s\n"
+
+#: fe-secure-openssl.c:883
+#, c-format
+msgid "invalid value \"%s\" for maximum SSL protocol version\n"
+msgstr "неверное значение \"%s\" для максимальной версии протокола SSL\n"
+
+#: fe-secure-openssl.c:894
+#, c-format
+msgid "could not set maximum SSL protocol version: %s\n"
+msgstr "не удалось задать максимальную версию протокола SSL: %s\n"
+
+#: fe-secure-openssl.c:930
+#, c-format
+msgid "could not read root certificate file \"%s\": %s\n"
+msgstr "не удалось прочитать файл корневых сертификатов \"%s\": %s\n"
+
+#: fe-secure-openssl.c:974
+msgid ""
+"could not get home directory to locate root certificate file\n"
+"Either provide the file or change sslmode to disable server certificate "
+"verification.\n"
+msgstr ""
+"не удалось получить домашний каталог для поиска файла корневых сертификатов\n"
+"Укажите полный путь к файлу или отключите проверку сертификата сервера, "
+"изменив sslmode.\n"
+
+#: fe-secure-openssl.c:978
+#, c-format
+msgid ""
+"root certificate file \"%s\" does not exist\n"
+"Either provide the file or change sslmode to disable server certificate "
+"verification.\n"
+msgstr ""
+"файл корневых сертификатов \"%s\" не существует\n"
+"Укажите полный путь к файлу или отключите проверку сертификата сервера, "
+"изменив sslmode.\n"
+
+#: fe-secure-openssl.c:1009
+#, c-format
+msgid "could not open certificate file \"%s\": %s\n"
+msgstr "не удалось открыть файл сертификата \"%s\": %s\n"
+
+#: fe-secure-openssl.c:1028
+#, c-format
+msgid "could not read certificate file \"%s\": %s\n"
+msgstr "не удалось прочитать файл сертификата \"%s\": %s\n"
+
+#: fe-secure-openssl.c:1053
+#, c-format
+msgid "could not establish SSL connection: %s\n"
+msgstr "не удалось установить SSL-соединение: %s\n"
+
+#: fe-secure-openssl.c:1107
+#, c-format
+msgid "could not load SSL engine \"%s\": %s\n"
+msgstr "не удалось загрузить модуль SSL ENGINE \"%s\": %s\n"
+
+#: fe-secure-openssl.c:1119
+#, c-format
+msgid "could not initialize SSL engine \"%s\": %s\n"
+msgstr "не удалось инициализировать модуль SSL ENGINE \"%s\": %s\n"
+
+#: fe-secure-openssl.c:1135
+#, c-format
+msgid "could not read private SSL key \"%s\" from engine \"%s\": %s\n"
+msgstr "не удалось прочитать закрытый ключ SSL \"%s\" из модуля \"%s\": %s\n"
+
+#: fe-secure-openssl.c:1149
+#, c-format
+msgid "could not load private SSL key \"%s\" from engine \"%s\": %s\n"
+msgstr "не удалось загрузить закрытый ключ SSL \"%s\" из модуля \"%s\": %s\n"
+
+#: fe-secure-openssl.c:1186
+#, c-format
+msgid "certificate present, but not private key file \"%s\"\n"
+msgstr "сертификат присутствует, но файла закрытого ключа \"%s\" нет\n"
+
+#: fe-secure-openssl.c:1194
+#, c-format
+msgid ""
+"private key file \"%s\" has group or world access; permissions should be "
+"u=rw (0600) or less\n"
+msgstr ""
+"к файлу закрытого ключа \"%s\" имеют доступ все или группа; права должны "
+"быть u=rw (0600) или более ограниченные\n"
+
+#: fe-secure-openssl.c:1219
+#, c-format
+msgid "could not load private key file \"%s\": %s\n"
+msgstr "не удалось загрузить файл закрытого ключа \"%s\": %s\n"
+
+#: fe-secure-openssl.c:1237
+#, c-format
+msgid "certificate does not match private key file \"%s\": %s\n"
+msgstr "сертификат не соответствует файлу закрытого ключа \"%s\": %s\n"
+
+#: fe-secure-openssl.c:1337
+#, c-format
+msgid ""
+"This may indicate that the server does not support any SSL protocol version "
+"between %s and %s.\n"
+msgstr ""
+"Это может указывать на то, что сервер не поддерживает ни одну версию "
+"протокола SSL между %s и %s.\n"
+
+#: fe-secure-openssl.c:1373
+#, c-format
+msgid "certificate could not be obtained: %s\n"
+msgstr "не удалось получить сертификат: %s\n"
+
+#: fe-secure-openssl.c:1462
+#, c-format
+msgid "no SSL error reported"
+msgstr "нет сообщения об ошибке SSL"
+
+#: fe-secure-openssl.c:1471
+#, c-format
+msgid "SSL error code %lu"
+msgstr "код ошибки SSL: %lu"
+
+#: fe-secure-openssl.c:1718
+#, c-format
+msgid "WARNING: sslpassword truncated\n"
+msgstr "ПРЕДУПРЕЖДЕНИЕ: значение sslpassword усечено\n"
+
+#: fe-secure.c:275
+#, c-format
+msgid "could not receive data from server: %s\n"
+msgstr "не удалось получить данные с сервера: %s\n"
+
+#: fe-secure.c:390
+#, c-format
+msgid "could not send data to server: %s\n"
+msgstr "не удалось передать данные серверу: %s\n"
+
+#: win32.c:314
+#, c-format
+msgid "unrecognized socket error: 0x%08X/%d"
+msgstr "нераспознанная ошибка сокета: 0x%08X/%d"
+
+#~ msgid "SSL library does not support CRL certificates (file \"%s\")\n"
+#~ msgstr "Библиотека SSL не поддерживает проверку CRL (файл \"%s\")\n"
+
+#~ msgid "could not get home directory to locate password file\n"
+#~ msgstr "не удалось получить домашний каталог для загрузки файла паролей\n"
+
+#~ msgid "could not get home directory to locate service definition file"
+#~ msgstr ""
+#~ "не удалось получить домашний каталог для загрузки файла определений служб"
+
+#~ msgid "setsockopt(TCP_KEEPIDLE) failed: %s\n"
+#~ msgstr "ошибка в setsockopt(TCP_KEEPIDLE): %s\n"
+
+#~ msgid "setsockopt(TCP_KEEPALIVE) failed: %s\n"
+#~ msgstr "ошибка в setsockopt(TCP_KEEPALIVE): %s\n"
+
+#~ msgid "setsockopt(TCP_KEEPINTVL) failed: %s\n"
+#~ msgstr "ошибка в setsockopt(TCP_KEEPINTVL): %s\n"
+
+#~ msgid "setsockopt(SO_KEEPALIVE) failed: %s\n"
+#~ msgstr "ошибка в setsockopt(SO_KEEPALIVE): %s\n"
+
+#~ msgid "could not acquire mutex: %s\n"
+#~ msgstr "не удалось заблокировать семафор: %s\n"
+
+#~ msgid "socket not open\n"
+#~ msgstr "сокет не открыт\n"
+
+#~ msgid "unrecognized return value from row processor"
+#~ msgstr "процессор строк вернул нераспознанное значение"
+
+#~ msgid "could not restore nonblocking mode on socket: %s\n"
+#~ msgstr "не удалось вернуть сокет в неблокирующий режим: %s\n"
+
+#~ msgid "Kerberos 5 authentication rejected: %*s\n"
+#~ msgstr "аутентификация Kerberos 5 не пройдена: %*s\n"
+
+#~ msgid "could not set socket to blocking mode: %s\n"
+#~ msgstr "не удалось перевести сокет в блокирующий режим: %s\n"
diff --git a/src/interfaces/libpq/po/sv.po b/src/interfaces/libpq/po/sv.po
new file mode 100644
index 0000000..6d7f4aa
--- /dev/null
+++ b/src/interfaces/libpq/po/sv.po
@@ -0,0 +1,1294 @@
+# Swedish message translation file for libpq
+# Peter Eisentraut <peter_e@gmx.net>, 2001, 2010.
+# Dennis Björklund <db@zigo.dhs.org>, 2002, 2003, 2004, 2005, 2006, 2017, 2018, 2019, 2020.
+#
+# Use these quotes: "%s"
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: PostgreSQL 13\n"
+"Report-Msgid-Bugs-To: pgsql-bugs@lists.postgresql.org\n"
+"POT-Creation-Date: 2020-08-27 21:39+0000\n"
+"PO-Revision-Date: 2020-08-30 07:05+0200\n"
+"Last-Translator: Dennis Björklund <db@zigo.dhs.org>\n"
+"Language-Team: Swedish <pgsql-translators@postgresql.org>\n"
+"Language: sv\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
+
+#: fe-auth-scram.c:212
+msgid "malformed SCRAM message (empty message)\n"
+msgstr "felaktigt SCRAM-meddelande (tomt meddelande)\n"
+
+#: fe-auth-scram.c:218
+msgid "malformed SCRAM message (length mismatch)\n"
+msgstr "felaktigt SCRAM-meddelande (längden stämmer inte)\n"
+
+#: fe-auth-scram.c:265
+msgid "incorrect server signature\n"
+msgstr "felaktig serversignatur\n"
+
+#: fe-auth-scram.c:274
+msgid "invalid SCRAM exchange state\n"
+msgstr "ogiltig SCRAM-utbytesstatus\n"
+
+#: fe-auth-scram.c:296
+#, c-format
+msgid "malformed SCRAM message (attribute \"%c\" expected)\n"
+msgstr "felaktigt SCRAM-meddelande (förväntade attribut %c)\n"
+
+#: fe-auth-scram.c:305
+#, c-format
+msgid "malformed SCRAM message (expected character \"=\" for attribute \"%c\")\n"
+msgstr "felaktigt SCRAM-meddelande (förväntade tecken \"=\" för attribut '%c')\n"
+
+#: fe-auth-scram.c:346
+msgid "could not generate nonce\n"
+msgstr "kunde inte skapa engångsnummer\n"
+
+#: fe-auth-scram.c:356 fe-auth-scram.c:431 fe-auth-scram.c:579
+#: fe-auth-scram.c:600 fe-auth-scram.c:626 fe-auth-scram.c:641
+#: fe-auth-scram.c:691 fe-auth-scram.c:725 fe-auth.c:289 fe-auth.c:359
+#: fe-auth.c:394 fe-auth.c:611 fe-auth.c:770 fe-auth.c:1129 fe-auth.c:1277
+#: fe-connect.c:892 fe-connect.c:1419 fe-connect.c:1595 fe-connect.c:2200
+#: fe-connect.c:2223 fe-connect.c:2952 fe-connect.c:4598 fe-connect.c:4854
+#: fe-connect.c:4973 fe-connect.c:5226 fe-connect.c:5306 fe-connect.c:5405
+#: fe-connect.c:5661 fe-connect.c:5690 fe-connect.c:5762 fe-connect.c:5786
+#: fe-connect.c:5804 fe-connect.c:5905 fe-connect.c:5914 fe-connect.c:6270
+#: fe-connect.c:6420 fe-exec.c:2747 fe-exec.c:3494 fe-exec.c:3659
+#: fe-gssapi-common.c:111 fe-lobj.c:895 fe-protocol2.c:1207 fe-protocol3.c:995
+#: fe-protocol3.c:1699 fe-secure-common.c:110 fe-secure-gssapi.c:504
+#: fe-secure-openssl.c:440 fe-secure-openssl.c:1091
+msgid "out of memory\n"
+msgstr "slut på minne\n"
+
+#: fe-auth-scram.c:364
+msgid "could not encode nonce\n"
+msgstr "kunde inte koda engångsnummer\n"
+
+#: fe-auth-scram.c:563
+msgid "could not encode client proof\n"
+msgstr "kunde inte koda klientbevis\n"
+
+#: fe-auth-scram.c:618
+msgid "invalid SCRAM response (nonce mismatch)\n"
+msgstr "ogiltigt SCRAM-svar (engångsnummer matchar inte)\n"
+
+#: fe-auth-scram.c:651
+msgid "malformed SCRAM message (invalid salt)\n"
+msgstr "felaktigt SCRAM-meddelande (ogiltigt salt)\n"
+
+#: fe-auth-scram.c:665
+msgid "malformed SCRAM message (invalid iteration count)\n"
+msgstr "felaktigt SCRAM-meddelande (ogiltig iterationsräknare)\n"
+
+#: fe-auth-scram.c:671
+msgid "malformed SCRAM message (garbage at end of server-first-message)\n"
+msgstr "felaktigt SCRAM-meddelande (skräp i slutet på server-first-message)\n"
+
+#: fe-auth-scram.c:702
+#, c-format
+msgid "error received from server in SCRAM exchange: %s\n"
+msgstr "fel mottaget från server i SCRAM-utbyte: %s\n"
+
+#: fe-auth-scram.c:718
+msgid "malformed SCRAM message (garbage at end of server-final-message)\n"
+msgstr "felaktigt SCRAM-meddelande (skräp i slutet av server-final-message)\n"
+
+#: fe-auth-scram.c:737
+msgid "malformed SCRAM message (invalid server signature)\n"
+msgstr "felaktigt SCRAM-meddelande (ogiltigt serversignatur)\n"
+
+#: fe-auth.c:76
+#, c-format
+msgid "out of memory allocating GSSAPI buffer (%d)\n"
+msgstr "slut på minne vid allokering av buffer till GSSAPI (%d)\n"
+
+#: fe-auth.c:131
+msgid "GSSAPI continuation error"
+msgstr "GSSAPI fortsättningsfel"
+
+#: fe-auth.c:158 fe-auth.c:388 fe-gssapi-common.c:98 fe-secure-common.c:98
+msgid "host name must be specified\n"
+msgstr "värdnamn måste anges\n"
+
+#: fe-auth.c:165
+msgid "duplicate GSS authentication request\n"
+msgstr "duplicerad autentiseringsbegäran från GSS\n"
+
+#: fe-auth.c:230
+#, c-format
+msgid "out of memory allocating SSPI buffer (%d)\n"
+msgstr "slut på minne vid allokering av buffer till GSSAPI (%d)\n"
+
+#: fe-auth.c:278
+msgid "SSPI continuation error"
+msgstr "SSPI fortsättningsfel"
+
+#: fe-auth.c:349
+msgid "duplicate SSPI authentication request\n"
+msgstr "duplicerad autentiseringsbegäran från SSPI\n"
+
+#: fe-auth.c:374
+msgid "could not acquire SSPI credentials"
+msgstr "kunde inte hämta SSPI-referenser"
+
+#: fe-auth.c:429
+msgid "channel binding required, but SSL not in use\n"
+msgstr "kräver kanalbindning, men SSL används inte\n"
+
+#: fe-auth.c:436
+msgid "duplicate SASL authentication request\n"
+msgstr "duplicerad autentiseringsbegäran från SASL\n"
+
+#: fe-auth.c:492
+msgid "channel binding is required, but client does not support it\n"
+msgstr "kanalbindning krävs men klienten stöder inte det\n"
+
+#: fe-auth.c:509
+msgid "server offered SCRAM-SHA-256-PLUS authentication over a non-SSL connection\n"
+msgstr "servern erbjöd SCRAM-SHA-256-PLUS-autentisering över en icke-SSL-anslutning\n"
+
+#: fe-auth.c:521
+msgid "none of the server's SASL authentication mechanisms are supported\n"
+msgstr "ingen av serverns SASL-autentiseringsmekanismer stöds\n"
+
+#: fe-auth.c:529
+msgid "channel binding is required, but server did not offer an authentication method that supports channel binding\n"
+msgstr "kräver kanalbindning, men servern erbjöd ingen autentiseringsmetod som stöder kanalbindning\n"
+
+#: fe-auth.c:635
+#, c-format
+msgid "out of memory allocating SASL buffer (%d)\n"
+msgstr "slut på minne vid allokering av buffer till SASL (%d)\n"
+
+#: fe-auth.c:660
+msgid "AuthenticationSASLFinal received from server, but SASL authentication was not completed\n"
+msgstr "mottog AuthenticationSASLFinal från server, men SASL-autentisering slutfördes ej\n"
+
+#: fe-auth.c:737
+msgid "SCM_CRED authentication method not supported\n"
+msgstr "autentiseringsmetoden SCM_CRED stöds ej\n"
+
+#: fe-auth.c:836
+msgid "channel binding required, but server authenticated client without channel binding\n"
+msgstr "kräver kanalbindning, men servern autentiserade klienten utan kanalbindning\n"
+
+#: fe-auth.c:842
+msgid "channel binding required but not supported by server's authentication request\n"
+msgstr "kanalbindning krävs men stöds inte av serverns autentiseringsförfrågan\n"
+
+#: fe-auth.c:875
+msgid "Kerberos 4 authentication not supported\n"
+msgstr "Kerberos-4-autentisering stöds ej\n"
+
+#: fe-auth.c:880
+msgid "Kerberos 5 authentication not supported\n"
+msgstr "Kerberos-5-autentisering stöds ej\n"
+
+#: fe-auth.c:951
+msgid "GSSAPI authentication not supported\n"
+msgstr "GSSAPI-autentisering stöds ej\n"
+
+#: fe-auth.c:983
+msgid "SSPI authentication not supported\n"
+msgstr "SSPI-autentisering stöds ej\n"
+
+#: fe-auth.c:991
+msgid "Crypt authentication not supported\n"
+msgstr "Crypt-autentisering stöds ej\n"
+
+#: fe-auth.c:1057
+#, c-format
+msgid "authentication method %u not supported\n"
+msgstr "autentiseringsmetod %u stöds ej\n"
+
+#: fe-auth.c:1104
+#, c-format
+msgid "user name lookup failure: error code %lu\n"
+msgstr "misslyckad sökning efter användarnamn: felkod %lu\n"
+
+#: fe-auth.c:1114 fe-connect.c:2834
+#, c-format
+msgid "could not look up local user ID %d: %s\n"
+msgstr "kunde inte slå upp lokalt användar-id %d: %s\n"
+
+#: fe-auth.c:1119 fe-connect.c:2839
+#, c-format
+msgid "local user with ID %d does not exist\n"
+msgstr "lokal användare med ID %d existerar inte\n"
+
+#: fe-auth.c:1221
+msgid "unexpected shape of result set returned for SHOW\n"
+msgstr "oväntad form på resultatmängden som returnerades för SHOW\n"
+
+#: fe-auth.c:1230
+msgid "password_encryption value too long\n"
+msgstr "password_encryption-värdet är för långt\n"
+
+#: fe-auth.c:1270
+#, c-format
+msgid "unrecognized password encryption algorithm \"%s\"\n"
+msgstr "okänd lösenordskrypteringsalgoritm \"%s\"\n"
+
+#: fe-connect.c:1075
+#, c-format
+msgid "could not match %d host names to %d hostaddr values\n"
+msgstr "kunde inte matcha %d värdnamn till %d värdadresser\n"
+
+#: fe-connect.c:1156
+#, c-format
+msgid "could not match %d port numbers to %d hosts\n"
+msgstr "kunde inte matcha %d portnummer med %d värdar\n"
+
+#: fe-connect.c:1249
+#, c-format
+msgid "invalid channel_binding value: \"%s\"\n"
+msgstr "ogiltigt channel_binding-värde: \"%s\"\n"
+
+#: fe-connect.c:1275
+#, c-format
+msgid "invalid sslmode value: \"%s\"\n"
+msgstr "ogiltigt värde för ssl-läge: \"%s\"\n"
+
+#: fe-connect.c:1296
+#, c-format
+msgid "sslmode value \"%s\" invalid when SSL support is not compiled in\n"
+msgstr "värde för ssl-läge, \"%s\", är ogiltigt när SSL-stöd inte kompilerats in\n"
+
+#: fe-connect.c:1317
+#, c-format
+msgid "invalid ssl_min_protocol_version value: \"%s\"\n"
+msgstr "ogiltigt ssl_min_protocol_version-värde: \"%s\"\n"
+
+#: fe-connect.c:1325
+#, c-format
+msgid "invalid ssl_max_protocol_version value: \"%s\"\n"
+msgstr "ogiltigt ssl_max_protocol_version-värde: \"%s\"\n"
+
+#: fe-connect.c:1342
+msgid "invalid SSL protocol version range\n"
+msgstr "ogiltigt intervall för SSL-protokollversion\n"
+
+#: fe-connect.c:1357
+#, c-format
+msgid "invalid gssencmode value: \"%s\"\n"
+msgstr "ogiltigt värde för gssencmode: \"%s\"\n"
+
+#: fe-connect.c:1366
+#, c-format
+msgid "gssencmode value \"%s\" invalid when GSSAPI support is not compiled in\n"
+msgstr "värde för gssenc-läge, \"%s\", är ogiltigt när GSSAPI-stöd inte kompilerats in\n"
+
+#: fe-connect.c:1401
+#, c-format
+msgid "invalid target_session_attrs value: \"%s\"\n"
+msgstr "ogiltigt target_session_attrs-värde: \"%s\"\n"
+
+#: fe-connect.c:1619
+#, c-format
+msgid "could not set socket to TCP no delay mode: %s\n"
+msgstr "kunde inte sätta uttag (socket) till läget TCP-ingen-fördröjning: %s\n"
+
+#: fe-connect.c:1680
+#, c-format
+msgid ""
+"could not connect to server: %s\n"
+"\tIs the server running locally and accepting\n"
+"\tconnections on Unix domain socket \"%s\"?\n"
+msgstr ""
+"kan inte ansluta till servern: %s\n"
+"\tKör servern på lokalt och accepterar den\n"
+"\tanslutningar på Unix-uttaget \"%s\"?\n"
+
+#: fe-connect.c:1717
+#, c-format
+msgid ""
+"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"
+msgstr ""
+"kunde inte ansluta till servern: %s\n"
+"\tKör servern på värden \"%s\" (%s) och accepterar\n"
+"\tden TCP/IP-uppkopplingar på port %s?\n"
+
+#: fe-connect.c:1725
+#, c-format
+msgid ""
+"could not connect to server: %s\n"
+"\tIs the server running on host \"%s\" and accepting\n"
+"\tTCP/IP connections on port %s?\n"
+msgstr ""
+"kunde inte ansluta till servern: %s\n"
+"\tKör servern på värden \"%s\" och accepterar\n"
+"\tden TCP/IP-uppkopplingar på porten %s?\n"
+
+#: fe-connect.c:1795
+#, c-format
+msgid "invalid integer value \"%s\" for connection option \"%s\"\n"
+msgstr "ogiltigt heltalsvärde \"%s\" för anslutningsflagga \"%s\"\n"
+
+#: fe-connect.c:1825 fe-connect.c:1859 fe-connect.c:1894 fe-connect.c:1981
+#: fe-connect.c:2623
+#, c-format
+msgid "setsockopt(%s) failed: %s\n"
+msgstr "setsockopt(%s) misslyckades: %s\n"
+
+#: fe-connect.c:1947
+#, c-format
+msgid "WSAIoctl(SIO_KEEPALIVE_VALS) failed: %ui\n"
+msgstr "WSAIoctl(SIO_KEEPALIVE_VALS) misslyckades: %ui\n"
+
+#: fe-connect.c:2313
+msgid "invalid connection state, probably indicative of memory corruption\n"
+msgstr "ogiltigt förbindelsetillstånd, antagligen korrupt minne\n"
+
+#: fe-connect.c:2379
+#, c-format
+msgid "invalid port number: \"%s\"\n"
+msgstr "ogiltigt portnummer \"%s\"\n"
+
+#: fe-connect.c:2395
+#, c-format
+msgid "could not translate host name \"%s\" to address: %s\n"
+msgstr "kunde inte översätta värdnamn \"%s\" till adress: %s\n"
+
+#: fe-connect.c:2408
+#, c-format
+msgid "could not parse network address \"%s\": %s\n"
+msgstr "kunde inte parsa nätverksadress \"%s\": %s\n"
+
+#: fe-connect.c:2421
+#, c-format
+msgid "Unix-domain socket path \"%s\" is too long (maximum %d bytes)\n"
+msgstr "Sökväg till unixdomänuttag \"%s\" är för lång (maximalt %d byte)\n"
+
+#: fe-connect.c:2436
+#, c-format
+msgid "could not translate Unix-domain socket path \"%s\" to address: %s\n"
+msgstr "kunde inte översätta sökväg till unix-uttag (socket) \"%s\" till adress: %s\n"
+
+#: fe-connect.c:2560
+#, c-format
+msgid "could not create socket: %s\n"
+msgstr "kan inte skapa uttag: %s\n"
+
+#: fe-connect.c:2582
+#, c-format
+msgid "could not set socket to nonblocking mode: %s\n"
+msgstr "kunde inte sätta uttag (socket) till ickeblockerande läge: %s\n"
+
+#: fe-connect.c:2592
+#, c-format
+msgid "could not set socket to close-on-exec mode: %s\n"
+msgstr "kunde inte ställa in uttag (socket) i \"close-on-exec\"-läge: %s\n"
+
+#: fe-connect.c:2610
+msgid "keepalives parameter must be an integer\n"
+msgstr "keepalives-parameter måste vara ett heltal\n"
+
+#: fe-connect.c:2750
+#, c-format
+msgid "could not get socket error status: %s\n"
+msgstr "kunde inte hämta felstatus för uttag (socket): %s\n"
+
+#: fe-connect.c:2778
+#, c-format
+msgid "could not get client address from socket: %s\n"
+msgstr "kunde inte få tag på klientadressen från uttag (socket): %s\n"
+
+#: fe-connect.c:2820
+msgid "requirepeer parameter is not supported on this platform\n"
+msgstr "requirepeer-parameter stöds inte på denna plattform\n"
+
+#: fe-connect.c:2823
+#, c-format
+msgid "could not get peer credentials: %s\n"
+msgstr "kunde inte hämta andra sidans referenser: %s\n"
+
+#: fe-connect.c:2847
+#, c-format
+msgid "requirepeer specifies \"%s\", but actual peer user name is \"%s\"\n"
+msgstr "requirepeer anger \"%s\", men andra sidans användarnamn är \"%s\"\n"
+
+#: fe-connect.c:2887
+#, c-format
+msgid "could not send GSSAPI negotiation packet: %s\n"
+msgstr "kunde inte skicka GSSAPI-paket för uppkopplingsförhandling: %s\n"
+
+#: fe-connect.c:2899
+msgid "GSSAPI encryption required but was impossible (possibly no credential cache, no server support, or using a local socket)\n"
+msgstr "GSSAPI-kryptering krävdes men var omöjligt (kanske ingen credential-cache, inget serverstöd eller använder ett lokalt uttag)\n"
+
+#: fe-connect.c:2926
+#, c-format
+msgid "could not send SSL negotiation packet: %s\n"
+msgstr "kunde inte skicka SSL-paket för uppkopplingsförhandling: %s\n"
+
+#: fe-connect.c:2965
+#, c-format
+msgid "could not send startup packet: %s\n"
+msgstr "kan inte skicka startpaketet: %s\n"
+
+#: fe-connect.c:3035
+msgid "server does not support SSL, but SSL was required\n"
+msgstr "SSL stöds inte av servern, men SSL krävdes\n"
+
+#: fe-connect.c:3061
+#, c-format
+msgid "received invalid response to SSL negotiation: %c\n"
+msgstr "tog emot ogiltigt svar till SSL-uppkopplingsförhandling: %c\n"
+
+#: fe-connect.c:3151
+msgid "server doesn't support GSSAPI encryption, but it was required\n"
+msgstr "GSSAPI stöds inte av servern, men det krävdes\n"
+
+#: fe-connect.c:3162
+#, c-format
+msgid "received invalid response to GSSAPI negotiation: %c\n"
+msgstr "tog emot ogiltigt svar till GSSAPI-uppkopplingsförhandling: %c\n"
+
+#: fe-connect.c:3229 fe-connect.c:3260
+#, c-format
+msgid "expected authentication request from server, but received %c\n"
+msgstr "förväntade autentiseringsförfrågan från servern, men fick %c\n"
+
+#: fe-connect.c:3502
+msgid "unexpected message from server during startup\n"
+msgstr "oväntat meddelande från servern under starten\n"
+
+#: fe-connect.c:3707
+#, c-format
+msgid "could not make a writable connection to server \"%s:%s\"\n"
+msgstr "kunde inte upprätta en skrivbar anslutning till server \"%s:%s\"\n"
+
+#: fe-connect.c:3753
+#, c-format
+msgid "test \"SHOW transaction_read_only\" failed on server \"%s:%s\"\n"
+msgstr "test \"SHOW transaction_read_only\" misslyckades på server \"%s:%s\"\n"
+
+#: fe-connect.c:3768
+#, c-format
+msgid "invalid connection state %d, probably indicative of memory corruption\n"
+msgstr "ogiltigt förbindelsetillstånd %d, antagligen korrupt minne\n"
+
+#: fe-connect.c:4204 fe-connect.c:4264
+#, c-format
+msgid "PGEventProc \"%s\" failed during PGEVT_CONNRESET event\n"
+msgstr "PGEventProc \"%s\" misslyckades under PGEVT_CONNRESET-händelse\n"
+
+#: fe-connect.c:4611
+#, c-format
+msgid "invalid LDAP URL \"%s\": scheme must be ldap://\n"
+msgstr "ogiltig LDAP URL \"%s\": schemat måste vara ldap://\n"
+
+#: fe-connect.c:4626
+#, c-format
+msgid "invalid LDAP URL \"%s\": missing distinguished name\n"
+msgstr "ogiltig LDAP URL \"%s\": saknar urskiljbart namn\n"
+
+#: fe-connect.c:4638 fe-connect.c:4693
+#, c-format
+msgid "invalid LDAP URL \"%s\": must have exactly one attribute\n"
+msgstr "ogiltig LDAP URL \"%s\": måste finnas exakt ett attribut\n"
+
+#: fe-connect.c:4649 fe-connect.c:4708
+#, c-format
+msgid "invalid LDAP URL \"%s\": must have search scope (base/one/sub)\n"
+msgstr "ogiltig LDAP URL \"%s\": måste ha sök-scope (base/one/sub)\n"
+
+#: fe-connect.c:4660
+#, c-format
+msgid "invalid LDAP URL \"%s\": no filter\n"
+msgstr "ogiltigt LDAP URL \"%s\": inget filter\n"
+
+#: fe-connect.c:4681
+#, c-format
+msgid "invalid LDAP URL \"%s\": invalid port number\n"
+msgstr "ogiltig LDAP URL \"%s\": ogiltigt portnummer\n"
+
+#: fe-connect.c:4717
+msgid "could not create LDAP structure\n"
+msgstr "kunde inte skapa LDAP-struktur\n"
+
+#: fe-connect.c:4793
+#, c-format
+msgid "lookup on LDAP server failed: %s\n"
+msgstr "uppslagning av LDAP-server misslyckades: %s\n"
+
+#: fe-connect.c:4804
+msgid "more than one entry found on LDAP lookup\n"
+msgstr "mer än en post hittad i LDAP-uppslagning\n"
+
+#: fe-connect.c:4805 fe-connect.c:4817
+msgid "no entry found on LDAP lookup\n"
+msgstr "ingen post hittad i LDAP-uppslagning\n"
+
+#: fe-connect.c:4828 fe-connect.c:4841
+msgid "attribute has no values on LDAP lookup\n"
+msgstr "attributet har inga värden i LDAP-uppslagning\n"
+
+#: fe-connect.c:4893 fe-connect.c:4912 fe-connect.c:5444
+#, c-format
+msgid "missing \"=\" after \"%s\" in connection info string\n"
+msgstr "\"=\" efter \"%s\" saknas i förbindelseinfosträng\n"
+
+#: fe-connect.c:4985 fe-connect.c:5629 fe-connect.c:6403
+#, c-format
+msgid "invalid connection option \"%s\"\n"
+msgstr "ogiltig förbindelseparameter \"%s\"\n"
+
+#: fe-connect.c:5001 fe-connect.c:5493
+msgid "unterminated quoted string in connection info string\n"
+msgstr "icke terminerad sträng i uppkopplingsinformationen\n"
+
+#: fe-connect.c:5084
+#, c-format
+msgid "definition of service \"%s\" not found\n"
+msgstr "definition av service \"%s\" hittades inte\n"
+
+#: fe-connect.c:5107
+#, c-format
+msgid "service file \"%s\" not found\n"
+msgstr "servicefil \"%s\" hittades inte\n"
+
+#: fe-connect.c:5122
+#, c-format
+msgid "line %d too long in service file \"%s\"\n"
+msgstr "rad %d för lång i servicefil \"%s\"\n"
+
+#: fe-connect.c:5194 fe-connect.c:5238
+#, c-format
+msgid "syntax error in service file \"%s\", line %d\n"
+msgstr "syntaxfel i servicefel \"%s\", rad %d\n"
+
+#: fe-connect.c:5205
+#, c-format
+msgid "nested service specifications not supported in service file \"%s\", line %d\n"
+msgstr "nästlade servicespecifikationer stöds inte i servicefil \"%s\", rad %d\n"
+
+#: fe-connect.c:5925
+#, c-format
+msgid "invalid URI propagated to internal parser routine: \"%s\"\n"
+msgstr "ogiltig URI propagerad till intern parsningsrutin: \"%s\"\n"
+
+#: fe-connect.c:6002
+#, c-format
+msgid "end of string reached when looking for matching \"]\" in IPv6 host address in URI: \"%s\"\n"
+msgstr "nådde slutet på strängen när vi letade efter matchande \"]\" i IPv6-värdadress i URI: \"%s\"\n"
+
+#: fe-connect.c:6009
+#, c-format
+msgid "IPv6 host address may not be empty in URI: \"%s\"\n"
+msgstr "IPv6-värdadress får ej vara tom i URI: \"%s\"\n"
+
+#: fe-connect.c:6024
+#, c-format
+msgid "unexpected character \"%c\" at position %d in URI (expected \":\" or \"/\"): \"%s\"\n"
+msgstr "oväntat tecken \"%c\" vid position %d i URI (förväntade \":\" eller \"/\"): \"%s\"\n"
+
+#: fe-connect.c:6153
+#, c-format
+msgid "extra key/value separator \"=\" in URI query parameter: \"%s\"\n"
+msgstr "extra nyckel/värde-separator \"=\" i URI-frågeparameter: \"%s\"\n"
+
+#: fe-connect.c:6173
+#, c-format
+msgid "missing key/value separator \"=\" in URI query parameter: \"%s\"\n"
+msgstr "saknar nyckel/värde-separator \"=\" i URI-frågeparameter: \"%s\"\n"
+
+#: fe-connect.c:6224
+#, c-format
+msgid "invalid URI query parameter: \"%s\"\n"
+msgstr "ogiltig URI-frågeparameter: \"%s\"\n"
+
+#: fe-connect.c:6298
+#, c-format
+msgid "invalid percent-encoded token: \"%s\"\n"
+msgstr "ogiltigt procent-kodad symbol: \"%s\"\n"
+
+#: fe-connect.c:6308
+#, c-format
+msgid "forbidden value %%00 in percent-encoded value: \"%s\"\n"
+msgstr "förbjudet värde %%00 i procentkodat värde: \"%s\"\n"
+
+#: fe-connect.c:6671
+msgid "connection pointer is NULL\n"
+msgstr "anslutningspekare är NULL\n"
+
+#: fe-connect.c:6970
+#, c-format
+msgid "WARNING: password file \"%s\" is not a plain file\n"
+msgstr "FEL: lösenordsfil \"%s\" är inte en vanlig fil\n"
+
+#: fe-connect.c:6979
+#, c-format
+msgid "WARNING: password file \"%s\" has group or world access; permissions should be u=rw (0600) or less\n"
+msgstr "VARNING: lösenordsfilen \"%s\" har läsrättigheter för gruppen eller världen; rättigheten skall vara u=rw (0600) eller mindre\n"
+
+#: fe-connect.c:7020
+#, c-format
+msgid "WARNING: line %d too long in password file \"%s\"\n"
+msgstr "VARNING: rad %d för lång i lösenordsfil \"%s\"\n"
+
+#: fe-connect.c:7099
+#, c-format
+msgid "password retrieved from file \"%s\"\n"
+msgstr "lösenord hämtat från fil \"%s\"\n"
+
+#: fe-exec.c:444 fe-exec.c:2821
+#, c-format
+msgid "row number %d is out of range 0..%d"
+msgstr "radnummer %d är utanför giltigt intervall 0..%d"
+
+#: fe-exec.c:505 fe-protocol2.c:497 fe-protocol2.c:532 fe-protocol2.c:1050
+#: fe-protocol3.c:206 fe-protocol3.c:233 fe-protocol3.c:250 fe-protocol3.c:330
+#: fe-protocol3.c:723 fe-protocol3.c:954
+msgid "out of memory"
+msgstr "slut på minne"
+
+#: fe-exec.c:506 fe-protocol2.c:1396 fe-protocol3.c:1907
+#, c-format
+msgid "%s"
+msgstr "%s"
+
+#: fe-exec.c:815
+msgid "write to server failed\n"
+msgstr "skrivning till servern misslyckades\n"
+
+#: fe-exec.c:896
+msgid "NOTICE"
+msgstr "NOTIS"
+
+#: fe-exec.c:954
+msgid "PGresult cannot support more than INT_MAX tuples"
+msgstr "PGresult stöder inte mer än INT_MAX tupler"
+
+#: fe-exec.c:966
+msgid "size_t overflow"
+msgstr "size_t-överspill"
+
+#: fe-exec.c:1243 fe-exec.c:1301 fe-exec.c:1347
+msgid "command string is a null pointer\n"
+msgstr "kommandosträngen är en null-pekare\n"
+
+#: fe-exec.c:1307 fe-exec.c:1353 fe-exec.c:1448
+msgid "number of parameters must be between 0 and 65535\n"
+msgstr "antal parametrar måste bara mellan 0 och 65535\n"
+
+#: fe-exec.c:1341 fe-exec.c:1442
+msgid "statement name is a null pointer\n"
+msgstr "satsens namn är en null-pekare\n"
+
+#: fe-exec.c:1361 fe-exec.c:1524 fe-exec.c:2233 fe-exec.c:2435
+msgid "function requires at least protocol version 3.0\n"
+msgstr "funktionen kräver minst protokollversion 3.0\n"
+
+#: fe-exec.c:1479
+msgid "no connection to the server\n"
+msgstr "inte förbunden till servern\n"
+
+#: fe-exec.c:1486
+msgid "another command is already in progress\n"
+msgstr "ett annat kommando pågår redan\n"
+
+#: fe-exec.c:1600
+msgid "length must be given for binary parameter\n"
+msgstr "längden måste anges för en binär parameter\n"
+
+#: fe-exec.c:1863
+#, c-format
+msgid "unexpected asyncStatus: %d\n"
+msgstr "oväntad asyncStatus: %d\n"
+
+#: fe-exec.c:1883
+#, c-format
+msgid "PGEventProc \"%s\" failed during PGEVT_RESULTCREATE event\n"
+msgstr "PGEventProc \"%s\" misslyckades under PGEVT_RESULTCREATE-händelse\n"
+
+#: fe-exec.c:2043
+msgid "COPY terminated by new PQexec"
+msgstr "COPY terminerad av ny PQexec"
+
+#: fe-exec.c:2051
+msgid "COPY IN state must be terminated first\n"
+msgstr "COPY IN-läge måste avslutas först\n"
+
+#: fe-exec.c:2071
+msgid "COPY OUT state must be terminated first\n"
+msgstr "COPY OUT-läge måste avslutas först\n"
+
+#: fe-exec.c:2079
+msgid "PQexec not allowed during COPY BOTH\n"
+msgstr "PQexec tillåts inte under COPY BOTH\n"
+
+#: fe-exec.c:2325 fe-exec.c:2392 fe-exec.c:2482 fe-protocol2.c:1353
+#: fe-protocol3.c:1838
+msgid "no COPY in progress\n"
+msgstr "ingen COPY pågår\n"
+
+#: fe-exec.c:2672
+msgid "connection in wrong state\n"
+msgstr "förbindelse i felaktigt tillstånd\n"
+
+#: fe-exec.c:2703
+msgid "invalid ExecStatusType code"
+msgstr "ogiltig ExecStatusType-kod"
+
+#: fe-exec.c:2730
+msgid "PGresult is not an error result\n"
+msgstr "PGresult är inte ett felresultat\n"
+
+#: fe-exec.c:2805 fe-exec.c:2828
+#, c-format
+msgid "column number %d is out of range 0..%d"
+msgstr "kolumnnummer %d är utanför giltigt intervall 0..%d"
+
+#: fe-exec.c:2843
+#, c-format
+msgid "parameter number %d is out of range 0..%d"
+msgstr "parameter nummer %d är utanför giltigt intervall 0..%d"
+
+#: fe-exec.c:3153
+#, c-format
+msgid "could not interpret result from server: %s"
+msgstr "kunde inte tolka svaret från servern: %s"
+
+#: fe-exec.c:3392 fe-exec.c:3476
+msgid "incomplete multibyte character\n"
+msgstr "ofullständigt multibyte-tecken\n"
+
+#: fe-gssapi-common.c:124
+msgid "GSSAPI name import error"
+msgstr "GSSAPI-fel vid import av namn"
+
+#: fe-lobj.c:154
+msgid "cannot determine OID of function lo_truncate\n"
+msgstr "kan inte ta reda på OID för funktionen lo_truncate\n"
+
+#: fe-lobj.c:170
+msgid "argument of lo_truncate exceeds integer range\n"
+msgstr "argumentet till lo_truncate överskrider heltalsintervallet\n"
+
+#: fe-lobj.c:221
+msgid "cannot determine OID of function lo_truncate64\n"
+msgstr "kan inte ta reda på OID för funktionen lo_truncate64\n"
+
+#: fe-lobj.c:279
+msgid "argument of lo_read exceeds integer range\n"
+msgstr "ett argument till lo_read överskriver heltalsintervallet\n"
+
+#: fe-lobj.c:334
+msgid "argument of lo_write exceeds integer range\n"
+msgstr "ett argument till lo_write överskriver heltalsintervallet\n"
+
+#: fe-lobj.c:425
+msgid "cannot determine OID of function lo_lseek64\n"
+msgstr "kan inte ta reda på OID för funktionen lo_lseek64\n"
+
+#: fe-lobj.c:521
+msgid "cannot determine OID of function lo_create\n"
+msgstr "kan inte ta reda på OID för funktionen lo_create\n"
+
+#: fe-lobj.c:600
+msgid "cannot determine OID of function lo_tell64\n"
+msgstr "kan inte ta reda på OID för funktionen lo_tell64\n"
+
+#: fe-lobj.c:706 fe-lobj.c:815
+#, c-format
+msgid "could not open file \"%s\": %s\n"
+msgstr "kan inte öppna fil \"%s\": %s\n"
+
+#: fe-lobj.c:761
+#, c-format
+msgid "could not read from file \"%s\": %s\n"
+msgstr "kunde inte läsa från fil \"%s\": %s\n"
+
+#: fe-lobj.c:835 fe-lobj.c:859
+#, c-format
+msgid "could not write to file \"%s\": %s\n"
+msgstr "kan inte skriva till fil \"%s\": %s\n"
+
+#: fe-lobj.c:946
+msgid "query to initialize large object functions did not return data\n"
+msgstr "fråga för att initiera stort objekt-funktion returnerade ingen data\n"
+
+#: fe-lobj.c:995
+msgid "cannot determine OID of function lo_open\n"
+msgstr "kan inte ta reda på OID för funktionen lo_open\n"
+
+#: fe-lobj.c:1002
+msgid "cannot determine OID of function lo_close\n"
+msgstr "kan inte ta reda på OID för funktionen lo_close\n"
+
+#: fe-lobj.c:1009
+msgid "cannot determine OID of function lo_creat\n"
+msgstr "kan inte ta reda på OID för funktionen lo_create\n"
+
+#: fe-lobj.c:1016
+msgid "cannot determine OID of function lo_unlink\n"
+msgstr "kan inte ta reda på OID för funktionen lo_unlink\n"
+
+#: fe-lobj.c:1023
+msgid "cannot determine OID of function lo_lseek\n"
+msgstr "kan inte ta reda på OID för funktionen lo_lseek\n"
+
+#: fe-lobj.c:1030
+msgid "cannot determine OID of function lo_tell\n"
+msgstr "kan inte ta reda på OID för funktionen lo_tell\n"
+
+#: fe-lobj.c:1037
+msgid "cannot determine OID of function loread\n"
+msgstr "kan inte ta reda på OID för funktionen loread\n"
+
+#: fe-lobj.c:1044
+msgid "cannot determine OID of function lowrite\n"
+msgstr "kan inte ta reda på OID för funktionen lowrite\n"
+
+#: fe-misc.c:289
+#, c-format
+msgid "integer of size %lu not supported by pqGetInt"
+msgstr "heltal med storlek %lu stöds inte av pqGetInt"
+
+#: fe-misc.c:325
+#, c-format
+msgid "integer of size %lu not supported by pqPutInt"
+msgstr "heltal med storlek %lu stöds inte av pqPutInt"
+
+#: fe-misc.c:636 fe-misc.c:869
+msgid "connection not open\n"
+msgstr "förbindelse inte öppen\n"
+
+#: fe-misc.c:805 fe-secure-openssl.c:209 fe-secure-openssl.c:316
+#: fe-secure.c:267 fe-secure.c:383
+msgid ""
+"server closed the connection unexpectedly\n"
+"\tThis probably means the server terminated abnormally\n"
+"\tbefore or while processing the request.\n"
+msgstr ""
+"servern stängde oväntat ner uppkopplingen\n"
+"\tTroligen så terminerade servern pga något fel antingen\n"
+"\tinnan eller under tiden den bearbetade en förfrågan.\n"
+
+#: fe-misc.c:1063
+msgid "timeout expired\n"
+msgstr "timeout utgången\n"
+
+#: fe-misc.c:1108
+msgid "invalid socket\n"
+msgstr "ogiltigt uttag\n"
+
+#: fe-misc.c:1131
+#, c-format
+msgid "select() failed: %s\n"
+msgstr "select() misslyckades: %s\n"
+
+#: fe-protocol2.c:87
+#, c-format
+msgid "invalid setenv state %c, probably indicative of memory corruption\n"
+msgstr "ogiltigt setenv-tillstånd %c, indikerar troligen ett minnesfel\n"
+
+#: fe-protocol2.c:384
+#, c-format
+msgid "invalid state %c, probably indicative of memory corruption\n"
+msgstr "ogiltigt tillstånd %c, indikerar troligen ett minnesfel\n"
+
+#: fe-protocol2.c:473 fe-protocol3.c:183
+#, c-format
+msgid "message type 0x%02x arrived from server while idle"
+msgstr "meddelandetyp 0x%02x kom från server under viloperiod"
+
+#: fe-protocol2.c:523
+#, c-format
+msgid "unexpected character %c following empty query response (\"I\" message)"
+msgstr "oväntat tecken %c följer på ett tomt frågesvar (meddelande \"I\")"
+
+#: fe-protocol2.c:589
+#, c-format
+msgid "server sent data (\"D\" message) without prior row description (\"T\" message)"
+msgstr "servern skickade data (meddelande \"D\") utan föregående radbeskrivning (meddelande \"T\")"
+
+#: fe-protocol2.c:607
+#, c-format
+msgid "server sent binary data (\"B\" message) without prior row description (\"T\" message)"
+msgstr "servern skickade binärdata (meddelande \"B\") utan föregående radbeskrivning (meddelande \"T\")"
+
+#: fe-protocol2.c:626 fe-protocol3.c:408
+#, c-format
+msgid "unexpected response from server; first received character was \"%c\"\n"
+msgstr "oväntat svar för servern; första mottagna tecknet var \"%c\"\n"
+
+#: fe-protocol2.c:755 fe-protocol2.c:930 fe-protocol3.c:622 fe-protocol3.c:849
+msgid "out of memory for query result"
+msgstr "slut på minnet för frågeresultat"
+
+#: fe-protocol2.c:1408
+#, c-format
+msgid "lost synchronization with server, resetting connection"
+msgstr "tappade synkronisering med servern, startar o, uppkopplingen"
+
+#: fe-protocol2.c:1530 fe-protocol2.c:1562 fe-protocol3.c:2095
+#, c-format
+msgid "protocol error: id=0x%x\n"
+msgstr "protokollfel: id=0x%x\n"
+
+#: fe-protocol3.c:365
+msgid "server sent data (\"D\" message) without prior row description (\"T\" message)\n"
+msgstr "servern skickade data (meddelande \"D\") utan att först skicka en radbeskrivning (meddelande \"T\")\n"
+
+#: fe-protocol3.c:429
+#, c-format
+msgid "message contents do not agree with length in message type \"%c\"\n"
+msgstr "meddelandeinnehåll stämmer inte med längden för meddelandetyp \"%c\"\n"
+
+#: fe-protocol3.c:449
+#, c-format
+msgid "lost synchronization with server: got message type \"%c\", length %d\n"
+msgstr "tappade synkronisering med servern: fick meddelandetyp \"%c\", längd %d\n"
+
+#: fe-protocol3.c:500 fe-protocol3.c:540
+msgid "insufficient data in \"T\" message"
+msgstr "otillräckligt med data i \"T\"-meddelande"
+
+#: fe-protocol3.c:573
+msgid "extraneous data in \"T\" message"
+msgstr "extra data i \"T\"-meddelande"
+
+#: fe-protocol3.c:686
+msgid "extraneous data in \"t\" message"
+msgstr "extra data i \"t\"-meddelande"
+
+#: fe-protocol3.c:757 fe-protocol3.c:789 fe-protocol3.c:807
+msgid "insufficient data in \"D\" message"
+msgstr "otillräckligt med data i \"D\"-meddelande"
+
+#: fe-protocol3.c:763
+msgid "unexpected field count in \"D\" message"
+msgstr "oväntat fältantal i \"D\"-meddelande"
+
+#: fe-protocol3.c:816
+msgid "extraneous data in \"D\" message"
+msgstr "extra data i \"D\"-meddelande"
+
+#: fe-protocol3.c:1008
+msgid "no error message available\n"
+msgstr "inget felmeddelande finns tillgängligt\n"
+
+#. translator: %s represents a digit string
+#: fe-protocol3.c:1056 fe-protocol3.c:1075
+#, c-format
+msgid " at character %s"
+msgstr " vid tecken %s"
+
+#: fe-protocol3.c:1088
+#, c-format
+msgid "DETAIL: %s\n"
+msgstr "DETALJ: %s\n"
+
+#: fe-protocol3.c:1091
+#, c-format
+msgid "HINT: %s\n"
+msgstr "TIPS: %s\n"
+
+#: fe-protocol3.c:1094
+#, c-format
+msgid "QUERY: %s\n"
+msgstr "FRÅGA: %s\n"
+
+#: fe-protocol3.c:1101
+#, c-format
+msgid "CONTEXT: %s\n"
+msgstr "KONTEXT: %s\n"
+
+#: fe-protocol3.c:1110
+#, c-format
+msgid "SCHEMA NAME: %s\n"
+msgstr "SCHEMANAMN: %s\n"
+
+#: fe-protocol3.c:1114
+#, c-format
+msgid "TABLE NAME: %s\n"
+msgstr "TABELLNAMN: %s\n"
+
+#: fe-protocol3.c:1118
+#, c-format
+msgid "COLUMN NAME: %s\n"
+msgstr "KOLUMNNAMN: %s\n"
+
+#: fe-protocol3.c:1122
+#, c-format
+msgid "DATATYPE NAME: %s\n"
+msgstr "DATATYPNAMN: %s\n"
+
+#: fe-protocol3.c:1126
+#, c-format
+msgid "CONSTRAINT NAME: %s\n"
+msgstr "VILLKORSNAMN: %s\n"
+
+#: fe-protocol3.c:1138
+msgid "LOCATION: "
+msgstr "PLATS: "
+
+#: fe-protocol3.c:1140
+#, c-format
+msgid "%s, "
+msgstr "%s, "
+
+#: fe-protocol3.c:1142
+#, c-format
+msgid "%s:%s"
+msgstr "%s:%s"
+
+#: fe-protocol3.c:1337
+#, c-format
+msgid "LINE %d: "
+msgstr "RAD %d: "
+
+#: fe-protocol3.c:1732
+msgid "PQgetline: not doing text COPY OUT\n"
+msgstr "PQgetline: utför inte text-COPY OUT\n"
+
+#: fe-secure-common.c:124
+msgid "SSL certificate's name contains embedded null\n"
+msgstr "SSL-certifikatets namn innehåller null-värden\n"
+
+#: fe-secure-common.c:171
+msgid "host name must be specified for a verified SSL connection\n"
+msgstr "värdnamn måste anges för en verifierad SSL-anslutning\n"
+
+#: fe-secure-common.c:196
+#, c-format
+msgid "server certificate for \"%s\" does not match host name \"%s\"\n"
+msgstr "servercertifikat för \"%s\" matchar inte värdnamn \"%s\"\n"
+
+#: fe-secure-common.c:202
+msgid "could not get server's host name from server certificate\n"
+msgstr "kan inte hämta ut serverns värdnamn från servercertifikatet\n"
+
+#: fe-secure-gssapi.c:201
+msgid "GSSAPI wrap error"
+msgstr "GSSAPI-fel vid inpackning"
+
+#: fe-secure-gssapi.c:209
+msgid "outgoing GSSAPI message would not use confidentiality\n"
+msgstr "utgående GSSAPI-meddelande skulle inte använda sekretess\n"
+
+#: fe-secure-gssapi.c:217
+#, c-format
+msgid "client tried to send oversize GSSAPI packet (%zu > %zu)\n"
+msgstr "klienten försöke skicka för stort GSSAPI-paket (%zu > %zu)\n"
+
+#: fe-secure-gssapi.c:354 fe-secure-gssapi.c:596
+#, c-format
+msgid "oversize GSSAPI packet sent by the server (%zu > %zu)\n"
+msgstr "för stort GSSAPI-paket skickat av servern (%zu > %zu)\n"
+
+#: fe-secure-gssapi.c:393
+msgid "GSSAPI unwrap error"
+msgstr "GSSAPI-fel vid uppackning"
+
+#: fe-secure-gssapi.c:403
+msgid "incoming GSSAPI message did not use confidentiality\n"
+msgstr "inkommande GSSAPI-meddelande använde inte sekretess\n"
+
+#: fe-secure-gssapi.c:642
+msgid "could not initiate GSSAPI security context"
+msgstr "kunde inte initiera GSSAPI-säkerhetskontext"
+
+#: fe-secure-gssapi.c:673
+msgid "GSSAPI size check error"
+msgstr "GSSAPI-fel vid kontroll av storlek"
+
+#: fe-secure-gssapi.c:684
+msgid "GSSAPI context establishment error"
+msgstr "GSSAPI-fel vid skapande av kontext"
+
+#: fe-secure-openssl.c:214 fe-secure-openssl.c:321 fe-secure-openssl.c:1291
+#, c-format
+msgid "SSL SYSCALL error: %s\n"
+msgstr "SSL SYSCALL fel: %s\n"
+
+#: fe-secure-openssl.c:221 fe-secure-openssl.c:328 fe-secure-openssl.c:1295
+msgid "SSL SYSCALL error: EOF detected\n"
+msgstr "SSL SYSCALL-fel: EOF upptäckt\n"
+
+#: fe-secure-openssl.c:232 fe-secure-openssl.c:339 fe-secure-openssl.c:1304
+#, c-format
+msgid "SSL error: %s\n"
+msgstr "SSL-fel: %s\n"
+
+#: fe-secure-openssl.c:247 fe-secure-openssl.c:354
+msgid "SSL connection has been closed unexpectedly\n"
+msgstr "SSL-anslutning har oväntat stängts\n"
+
+#: fe-secure-openssl.c:253 fe-secure-openssl.c:360 fe-secure-openssl.c:1354
+#, c-format
+msgid "unrecognized SSL error code: %d\n"
+msgstr "okänd SSL-felkod: %d\n"
+
+#: fe-secure-openssl.c:400
+msgid "could not determine server certificate signature algorithm\n"
+msgstr "kunde inte lista ut serverns algoritm för certifikatsignering\n"
+
+#: fe-secure-openssl.c:421
+#, c-format
+msgid "could not find digest for NID %s\n"
+msgstr "kunde inte hitta kontrollsumma för NID %s\n"
+
+#: fe-secure-openssl.c:431
+msgid "could not generate peer certificate hash\n"
+msgstr "kunde inte generera peer-certifikathash\n"
+
+#: fe-secure-openssl.c:488
+msgid "SSL certificate's name entry is missing\n"
+msgstr "SSL-certifikatets namn saknas\n"
+
+#: fe-secure-openssl.c:815
+#, c-format
+msgid "could not create SSL context: %s\n"
+msgstr "kan inte skapa SSL-omgivning: %s\n"
+
+#: fe-secure-openssl.c:854
+#, c-format
+msgid "invalid value \"%s\" for minimum SSL protocol version\n"
+msgstr "ogiltigt värde \"%s\" för minimal SSL-protokollversion\n"
+
+#: fe-secure-openssl.c:865
+#, c-format
+msgid "could not set minimum SSL protocol version: %s\n"
+msgstr "kunde inte sätta minimal SSL-protokollversion: %s\n"
+
+#: fe-secure-openssl.c:883
+#, c-format
+msgid "invalid value \"%s\" for maximum SSL protocol version\n"
+msgstr "ogiltigt värde \"%s\" för maximal SSL-protokollversion\n"
+
+#: fe-secure-openssl.c:894
+#, c-format
+msgid "could not set maximum SSL protocol version: %s\n"
+msgstr "kunde inte sätta maximal SSL-protokollversion: %s\n"
+
+#: fe-secure-openssl.c:930
+#, c-format
+msgid "could not read root certificate file \"%s\": %s\n"
+msgstr "kunde inte läsa root-certifikatfilen \"%s\": %s\n"
+
+#: fe-secure-openssl.c:974
+msgid ""
+"could not get home directory to locate root certificate file\n"
+"Either provide the file or change sslmode to disable server certificate verification.\n"
+msgstr ""
+"kunde inte hämta hemkatalogen för att lokalisera root-certifikatfilen\n"
+"Antingen tillhandahåll filen eller ändra sslmode för att stänga av serverns certifikatverifiering.\n"
+
+#: fe-secure-openssl.c:978
+#, c-format
+msgid ""
+"root certificate file \"%s\" does not exist\n"
+"Either provide the file or change sslmode to disable server certificate verification.\n"
+msgstr ""
+"root-certifikatfilen \"%s\" finns inte\n"
+"Antingen tillhandahåll filen eller ändra sslmode för att stänga av serverns certifikatverifiering.\n"
+
+#: fe-secure-openssl.c:1009
+#, c-format
+msgid "could not open certificate file \"%s\": %s\n"
+msgstr "kunde inte öppna certifikatfil \"%s\": %s\n"
+
+#: fe-secure-openssl.c:1028
+#, c-format
+msgid "could not read certificate file \"%s\": %s\n"
+msgstr "kunde inte läsa certifikatfil \"%s\": %s\n"
+
+#: fe-secure-openssl.c:1053
+#, c-format
+msgid "could not establish SSL connection: %s\n"
+msgstr "kan inte skapa SSL-förbindelse: %s\n"
+
+#: fe-secure-openssl.c:1107
+#, c-format
+msgid "could not load SSL engine \"%s\": %s\n"
+msgstr "kunde inte ladda SSL-motor \"%s\": %s\n"
+
+#: fe-secure-openssl.c:1119
+#, c-format
+msgid "could not initialize SSL engine \"%s\": %s\n"
+msgstr "kunde inte initiera SSL-motor \"%s\": %s\n"
+
+#: fe-secure-openssl.c:1135
+#, c-format
+msgid "could not read private SSL key \"%s\" from engine \"%s\": %s\n"
+msgstr "kunde inte läsa privat SSL-nyckel \"%s\" från motor \"%s\": %s\n"
+
+#: fe-secure-openssl.c:1149
+#, c-format
+msgid "could not load private SSL key \"%s\" from engine \"%s\": %s\n"
+msgstr "kunde inte ladda privat SSL-nyckel \"%s\" från motor \"%s\": %s\n"
+
+#: fe-secure-openssl.c:1186
+#, c-format
+msgid "certificate present, but not private key file \"%s\"\n"
+msgstr "certifikat tillgängligt, men inte den privata nyckelfilen \"%s\"\n"
+
+#: fe-secure-openssl.c:1194
+#, c-format
+msgid "private key file \"%s\" has group or world access; permissions should be u=rw (0600) or less\n"
+msgstr "privata nyckelfilen \"%s\" har läsrättigheter för gruppen eller världen; rättigheten skall vara u=rw (0600) eller mindre\n"
+
+#: fe-secure-openssl.c:1219
+#, c-format
+msgid "could not load private key file \"%s\": %s\n"
+msgstr "kunde inte ladda privata nyckelfilen \"%s\": %s\n"
+
+#: fe-secure-openssl.c:1237
+#, c-format
+msgid "certificate does not match private key file \"%s\": %s\n"
+msgstr "certifikatet matchar inte den privata nyckelfilen \"%s\": %s\n"
+
+#: fe-secure-openssl.c:1337
+#, c-format
+msgid "This may indicate that the server does not support any SSL protocol version between %s and %s.\n"
+msgstr "Detta kan tyda på att servern inte stöder någon SSL-protokolversion mellan %s och %s.\n"
+
+#: fe-secure-openssl.c:1373
+#, c-format
+msgid "certificate could not be obtained: %s\n"
+msgstr "certifikatet kunde inte hämtas: %s\n"
+
+#: fe-secure-openssl.c:1462
+#, c-format
+msgid "no SSL error reported"
+msgstr "inget SSL-fel rapporterat"
+
+#: fe-secure-openssl.c:1471
+#, c-format
+msgid "SSL error code %lu"
+msgstr "SSL-felkod %lu"
+
+#: fe-secure-openssl.c:1718
+#, c-format
+msgid "WARNING: sslpassword truncated\n"
+msgstr "VARNING: sslpassword trunkerat\n"
+
+#: fe-secure.c:275
+#, c-format
+msgid "could not receive data from server: %s\n"
+msgstr "kan inte ta emot data från servern: %s\n"
+
+#: fe-secure.c:390
+#, c-format
+msgid "could not send data to server: %s\n"
+msgstr "kan inte skicka data till servern: %s\n"
+
+#: win32.c:314
+#, c-format
+msgid "unrecognized socket error: 0x%08X/%d"
+msgstr "okänt uttagsfel: 0x%08X/%d"
+
+#~ msgid "could not set minimum version of SSL protocol: %s\n"
+#~ msgstr "kunde inte sätta minimal version för SSL-protokoll: %s\n"
+
+#~ msgid "could not set maximum version of SSL protocol: %s\n"
+#~ msgstr "kunde inte sätta maximal version för SSL-protokollet: %s\n"
diff --git a/src/interfaces/libpq/po/tr.po b/src/interfaces/libpq/po/tr.po
new file mode 100644
index 0000000..855dad7
--- /dev/null
+++ b/src/interfaces/libpq/po/tr.po
@@ -0,0 +1,1247 @@
+# translation of libpq.po to Turkish
+# Devrim GUNDUZ <devrim@CommandPrompt.com> 2004, 2005, 2006, 2007.
+# Nicolai TUFAR <ntufar@gmail.com> 2004, 2005, 2006, 2007.
+# Abdullah GÜLNER <agulner@gmail.com> 2017, 2018.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: libpq-tr\n"
+"Report-Msgid-Bugs-To: pgsql-bugs@lists.postgresql.org\n"
+"POT-Creation-Date: 2019-05-05 21:38+0000\n"
+"PO-Revision-Date: 2019-06-14 10:49+0300\n"
+"Last-Translator: Abdullah GÜLNER\n"
+"Language-Team: Turkish <ceviri@postgresql.org.tr>\n"
+"Language: tr\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Poedit 1.8.7.1\n"
+"X-Poedit-Basepath: /home/ntufar/pg/pgsql/src/interfaces/libpq\n"
+"X-Poedit-SearchPath-0: /home/ntufar/pg/pgsql/src/interfaces/libpq\n"
+
+#: fe-auth-scram.c:183
+msgid "malformed SCRAM message (empty message)\n"
+msgstr "hatalı SCRAM mesajı (boş mesaj)\n"
+
+#: fe-auth-scram.c:189
+msgid "malformed SCRAM message (length mismatch)\n"
+msgstr "hatalı SCRAM mesajı (uzunluk uyuşmazlığı)\n"
+
+#: fe-auth-scram.c:238
+msgid "incorrect server signature\n"
+msgstr "sunucu imzası yanlış\n"
+
+#: fe-auth-scram.c:247
+msgid "invalid SCRAM exchange state\n"
+msgstr "geçersiz SCRAM değişim durumu\n"
+
+#: fe-auth-scram.c:270
+#, c-format
+msgid "malformed SCRAM message (attribute \"%c\" expected)\n"
+msgstr "hatalı SCRAM mesajı (\"%c\" niteliği bekleniyor)\n"
+
+#: fe-auth-scram.c:279
+#, c-format
+msgid "malformed SCRAM message (expected character \"=\" for attribute \"%c\")\n"
+msgstr "hatalı SCRAM mesajı (\"%c\" niteliği için \"=\" karakteri bekleniyor)\n"
+
+#: fe-auth-scram.c:320
+msgid "could not generate nonce\n"
+msgstr "nonce oluşturulamadı\n"
+
+#: fe-auth-scram.c:328 fe-auth-scram.c:395 fe-auth-scram.c:517
+#: fe-auth-scram.c:537 fe-auth-scram.c:563 fe-auth-scram.c:577
+#: fe-auth-scram.c:619 fe-auth.c:290 fe-auth.c:360 fe-auth.c:395 fe-auth.c:581
+#: fe-auth.c:740 fe-auth.c:1052 fe-auth.c:1200 fe-connect.c:858
+#: fe-connect.c:1320 fe-connect.c:1496 fe-connect.c:2085 fe-connect.c:2108
+#: fe-connect.c:2830 fe-connect.c:4512 fe-connect.c:4764 fe-connect.c:4883
+#: fe-connect.c:5133 fe-connect.c:5213 fe-connect.c:5312 fe-connect.c:5568
+#: fe-connect.c:5597 fe-connect.c:5669 fe-connect.c:5693 fe-connect.c:5711
+#: fe-connect.c:5812 fe-connect.c:5821 fe-connect.c:6177 fe-connect.c:6327
+#: fe-exec.c:2748 fe-exec.c:3495 fe-exec.c:3660 fe-lobj.c:895
+#: fe-protocol2.c:1213 fe-protocol3.c:999 fe-protocol3.c:1703
+#: fe-secure-common.c:110 fe-secure-openssl.c:438 fe-secure-openssl.c:1025
+msgid "out of memory\n"
+msgstr "yetersiz bellek\n"
+
+#: fe-auth-scram.c:555
+msgid "invalid SCRAM response (nonce mismatch)\n"
+msgstr "geçersiz SCRAM cevabı (nonce uyuşmazlığı)\n"
+
+#: fe-auth-scram.c:594
+msgid "malformed SCRAM message (invalid iteration count)\n"
+msgstr "hatalı SCRAM mesajı (geçersiz iterasyon sayısı)\n"
+
+#: fe-auth-scram.c:600
+msgid "malformed SCRAM message (garbage at end of server-first-message)\n"
+msgstr "hatalı SCRAM mesajı (sunucu-ilk-mesajı sonunda anlamsız değer)\n"
+
+#: fe-auth-scram.c:630
+#, c-format
+msgid "error received from server in SCRAM exchange: %s\n"
+msgstr "SCRAM değişimi işleminde sunucudan hata alındı: %s\n"
+
+#: fe-auth-scram.c:646
+msgid "malformed SCRAM message (garbage at end of server-final-message)\n"
+msgstr "hatalı SCRAM mesajı (sunucu-son-mesajı sonunda anlamsız değer)\n"
+
+#: fe-auth-scram.c:654
+msgid "malformed SCRAM message (invalid server signature)\n"
+msgstr "hatalı SCRAM mesajı (geçersiz sunucu imzası)\n"
+
+#: fe-auth.c:77
+#, c-format
+msgid "out of memory allocating GSSAPI buffer (%d)\n"
+msgstr "GSSAPI tamponu ayrılırken yetersiz bellek hatası (%d)\n"
+
+#: fe-auth.c:132
+msgid "GSSAPI continuation error"
+msgstr "GSSAPI devam hatası"
+
+#: fe-auth.c:159 fe-auth.c:389 fe-secure-common.c:98
+msgid "host name must be specified\n"
+msgstr "sunucu adı belirtilmelidir\n"
+
+#: fe-auth.c:166
+msgid "duplicate GSS authentication request\n"
+msgstr "çift GSS yetkilendirme isteği\n"
+
+#: fe-auth.c:231
+#, c-format
+msgid "out of memory allocating SSPI buffer (%d)\n"
+msgstr "GSSAPI tamponu ayrılırken yetersiz bellek hatası (%d)\n"
+
+#: fe-auth.c:279
+msgid "SSPI continuation error"
+msgstr "SSPI devam hatası"
+
+#: fe-auth.c:350
+msgid "duplicate SSPI authentication request\n"
+msgstr "çift SSPI yetkilendirme isteği\n"
+
+#: fe-auth.c:375
+msgid "could not acquire SSPI credentials"
+msgstr "SSPI kimlik bilgileri alınamadı"
+
+#: fe-auth.c:429
+msgid "duplicate SASL authentication request\n"
+msgstr "çift SASL yetkilendirme isteği\n"
+
+#: fe-auth.c:487
+msgid "server offered SCRAM-SHA-256-PLUS authentication over a non-SSL connection\n"
+msgstr ""
+"sunucu SSL'li olmayan bir bağlantı üzerinde SCRAM-SHA-256-PLUS kimlik doğrulaması önerdi\n"
+" \n"
+
+#: fe-auth.c:499
+msgid "none of the server's SASL authentication mechanisms are supported\n"
+msgstr "sunucunun SASL yetkilendirme mekanizmalarından hiçbiri desteklenmiyor\n"
+
+#: fe-auth.c:605
+#, c-format
+msgid "out of memory allocating SASL buffer (%d)\n"
+msgstr "SASL tamponu ayrılırken yetersiz bellek hatası (%d)\n"
+
+#: fe-auth.c:630
+msgid "AuthenticationSASLFinal received from server, but SASL authentication was not completed\n"
+msgstr "sunucudan AuthenticationSASLFinal alındı, fakat SASL yetkilendirmesi tamamlanmadı\n"
+
+#: fe-auth.c:707
+msgid "SCM_CRED authentication method not supported\n"
+msgstr "SCM_CRED yetkilendirme yöntemi desteklenmiyor.\n"
+
+#: fe-auth.c:798
+msgid "Kerberos 4 authentication not supported\n"
+msgstr "Kerberos 4 yetkilendirmesi desteklenmiyor\n"
+
+#: fe-auth.c:803
+msgid "Kerberos 5 authentication not supported\n"
+msgstr "Kerberos 5 yetkilendirmesi desteklenmiyor\n"
+
+#: fe-auth.c:874
+msgid "GSSAPI authentication not supported\n"
+msgstr "GSSAPI yetkilendirmesi desteklenmiyor\n"
+
+#: fe-auth.c:906
+msgid "SSPI authentication not supported\n"
+msgstr "SSPI yetkilendirmesi desteklenmiyor\n"
+
+#: fe-auth.c:914
+msgid "Crypt authentication not supported\n"
+msgstr "Crypt yetkilendirmesi desteklenmiyor\n"
+
+#: fe-auth.c:980
+#, c-format
+msgid "authentication method %u not supported\n"
+msgstr "%u yetkilendirme sistemi desteklenmiyor\n"
+
+#: fe-auth.c:1027
+#, c-format
+msgid "user name lookup failure: error code %lu\n"
+msgstr "kullanıcı adı arama başarısız: hata kodu %lu\n"
+
+#: fe-auth.c:1037 fe-connect.c:2717
+#, c-format
+msgid "could not look up local user ID %d: %s\n"
+msgstr "yerel kullanıcı ID %d bulunamadı: %s\n"
+
+#: fe-auth.c:1042 fe-connect.c:2722
+#, c-format
+msgid "local user with ID %d does not exist\n"
+msgstr "yerel kullanıcı ID %d mevcut değildir\n"
+
+#: fe-auth.c:1144
+msgid "unexpected shape of result set returned for SHOW\n"
+msgstr "SHOW için döndürülen sonuç kümesi beklenmeyen şekilde \n"
+
+#: fe-auth.c:1153
+msgid "password_encryption value too long\n"
+msgstr "parola şifreleme (password_encryption) değeri çok uzun\n"
+
+#: fe-auth.c:1193
+#, c-format
+msgid "unrecognized password encryption algorithm \"%s\"\n"
+msgstr "bilinmeyen parola şifreleme algoritması \"%s\"\n"
+
+#: fe-connect.c:1041
+#, c-format
+msgid "could not match %d host names to %d hostaddr values\n"
+msgstr "%d sunucu adları %d sunucu adresleriyle eşleştirilemedi\n"
+
+#: fe-connect.c:1117
+#, c-format
+msgid "could not match %d port numbers to %d hosts\n"
+msgstr "%d kapı (port) numaraları %d sunucuları ile eşleştirilemedi\n"
+
+#: fe-connect.c:1213
+#, c-format
+msgid "invalid sslmode value: \"%s\"\n"
+msgstr "geçersiz sslmode değeri: \"%s\"\n"
+
+#: fe-connect.c:1234
+#, c-format
+msgid "sslmode value \"%s\" invalid when SSL support is not compiled in\n"
+msgstr "\"%s\" sslmode değeri, SSL desteği derlenmeyince geçersizdir\n"
+
+#: fe-connect.c:1258
+#, c-format
+msgid "invalid gssencmode value: \"%s\"\n"
+msgstr "geçersiz gssencmode değeri: \"%s\"\n"
+
+#: fe-connect.c:1268
+msgid "no GSSAPI support; cannot require GSSAPI\n"
+msgstr "GSSAPI desteği yok; GSSAPI gerektiremez\n"
+
+#: fe-connect.c:1302
+#, c-format
+msgid "invalid target_session_attrs value: \"%s\"\n"
+msgstr "geçersiz target_session_attrs değeri: \"%s\"\n"
+
+#: fe-connect.c:1520
+#, c-format
+msgid "could not set socket to TCP no delay mode: %s\n"
+msgstr "soket TCP no delay moduna ayarlanamadı: %s\n"
+
+#: fe-connect.c:1583
+#, c-format
+msgid ""
+"could not connect to server: %s\n"
+"\tIs the server running locally and accepting\n"
+"\tconnections on Unix domain socket \"%s\"?\n"
+msgstr ""
+"sunucuya bağlanılamadı: %s\n"
+"\tSunucu yerelde çalışıyor ve \"%s\" Unix domain\n"
+"\tsoketi üzerinden bağlantılara izin veriyor mu?\n"
+
+#: fe-connect.c:1620
+#, c-format
+msgid ""
+"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"
+msgstr ""
+"sunucuya bağlanılamadı: %s\n"
+"\tSunucu \"%s\" (%s) makinasında çalışıyor ve\n"
+"\t %s portundan TCP/IP bağlantılarına izin veriyor mu?\n"
+
+#: fe-connect.c:1628
+#, c-format
+msgid ""
+"could not connect to server: %s\n"
+"\tIs the server running on host \"%s\" and accepting\n"
+"\tTCP/IP connections on port %s?\n"
+msgstr ""
+"sunucuya bağlanılamadı: %s\n"
+"\tSunucu \"%s\" makiansında çalışıyor ve\n"
+"\t %s portundan TCP/IP bağlantılarına izin veriyor mu?\n"
+
+#: fe-connect.c:1679
+#, c-format
+msgid "invalid integer value \"%s\" for keyword \"%s\"\n"
+msgstr "\"%2$s\" anahtar kelimesi için geçersiz tamsayı değeri \"%1$s\"\n"
+
+#: fe-connect.c:1709 fe-connect.c:1743 fe-connect.c:1778 fe-connect.c:1865
+#: fe-connect.c:2507
+#, c-format
+msgid "setsockopt(%s) failed: %s\n"
+msgstr "setsockopt(%s) başarısız oldu: %s\n"
+
+#: fe-connect.c:1831
+#, c-format
+msgid "WSAIoctl(SIO_KEEPALIVE_VALS) failed: %ui\n"
+msgstr "WSAIoctl(SIO_KEEPALIVE_VALS) başarısız oldu: %ui\n"
+
+#: fe-connect.c:2199
+msgid "invalid connection state, probably indicative of memory corruption\n"
+msgstr "geçersiz bağlantı durumu, hafızanın zarar görmüş olmasının işareti olabilir\n"
+
+#: fe-connect.c:2267
+#, c-format
+msgid "invalid port number: \"%s\"\n"
+msgstr "geçersiz port numarası: \"%s\"\n"
+
+#: fe-connect.c:2283
+#, c-format
+msgid "could not translate host name \"%s\" to address: %s\n"
+msgstr "\"%s\" makine adı bir adrese çevirilemedi: %s\n"
+
+#: fe-connect.c:2296
+#, c-format
+msgid "could not parse network address \"%s\": %s\n"
+msgstr "ağ adresi \"%s\" ayrıştırılamadı: %s\n"
+
+#: fe-connect.c:2309
+#, c-format
+msgid "Unix-domain socket path \"%s\" is too long (maximum %d bytes)\n"
+msgstr "Unix-domain soket yolu \"%s\" çok uzun (azami %d bayt)\n"
+
+#: fe-connect.c:2324
+#, c-format
+msgid "could not translate Unix-domain socket path \"%s\" to address: %s\n"
+msgstr "\"%s\" Unix domain soket yolu adrese çevirilemedi: %s\n"
+
+#: fe-connect.c:2444
+#, c-format
+msgid "could not create socket: %s\n"
+msgstr "soket oluşturulamadı: %s\n"
+
+#: fe-connect.c:2466
+#, c-format
+msgid "could not set socket to nonblocking mode: %s\n"
+msgstr "soket bloklamasız ( non-blocking ) moda ayarlanamadı: %s\n"
+
+#: fe-connect.c:2476
+#, c-format
+msgid "could not set socket to close-on-exec mode: %s\n"
+msgstr "soket close-on-exec moduna ayarlanamadı: %s\n"
+
+#: fe-connect.c:2494
+msgid "keepalives parameter must be an integer\n"
+msgstr "keepalives parametresi tamsayı olmalıdır\n"
+
+#: fe-connect.c:2634
+#, c-format
+msgid "could not get socket error status: %s\n"
+msgstr "soket hata durumu alınamadı: %s\n"
+
+#: fe-connect.c:2662
+#, c-format
+msgid "could not get client address from socket: %s\n"
+msgstr "soketten istemci adresi alınamadı: %s\n"
+
+#: fe-connect.c:2704
+msgid "requirepeer parameter is not supported on this platform\n"
+msgstr "bu platformda requirepeer parametresi desteklenmiyor \n"
+
+#: fe-connect.c:2707
+#, c-format
+msgid "could not get peer credentials: %s\n"
+msgstr "karşı tarafın kimlik bilgileri alınamadı: %s \n"
+
+#: fe-connect.c:2730
+#, c-format
+msgid "requirepeer specifies \"%s\", but actual peer user name is \"%s\"\n"
+msgstr "requirepeer \"%s\" belirtiyor, ancak gerçek karşı taraf (peer) kullanıcı adı \"%s\"\n"
+
+#: fe-connect.c:2765
+#, c-format
+msgid "could not send GSSAPI negotiation packet: %s\n"
+msgstr "GSSAPI anlaşma (negotiation) paketi gönderilemedi: %s\n"
+
+#: fe-connect.c:2777
+msgid "GSSAPI encryption required, but was impossible (possibly no ccache, no server support, or using a local socket)\n"
+msgstr "GSSAPI şifrelemesi gerekli, fakat mümkün değil (muhtemelen ccache yok, sunucu desteği yok ya da yerel soket kullanılıyor)\n"
+
+#: fe-connect.c:2804
+#, c-format
+msgid "could not send SSL negotiation packet: %s\n"
+msgstr "SSL anlaşma (negotiation) paketi gönderilemedi: %s\n"
+
+#: fe-connect.c:2843
+#, c-format
+msgid "could not send startup packet: %s\n"
+msgstr "başlangıç paketi gönderilemedi: %s\n"
+
+#: fe-connect.c:2913
+msgid "server does not support SSL, but SSL was required\n"
+msgstr "sunucu SSL desteklemiyor, ama SSL gerekli idi\n"
+
+#: fe-connect.c:2939
+#, c-format
+msgid "received invalid response to SSL negotiation: %c\n"
+msgstr "ssl görüşmesine geçersiz yanıt alındı: %c\n"
+
+#: fe-connect.c:3029
+msgid "server doesn't support GSSAPI encryption, but it was required\n"
+msgstr "sunucu GSSAPI şifrelemesi desteklemiyor, ama gerekli idi\n"
+
+#: fe-connect.c:3040
+#, c-format
+msgid "received invalid response to GSSAPI negotiation: %c\n"
+msgstr "GSSAPI görüşmesine geçersiz yanıt alındı: %c\n"
+
+#: fe-connect.c:3108 fe-connect.c:3141
+#, c-format
+msgid "expected authentication request from server, but received %c\n"
+msgstr "sunucudan yetkilendirme isteği beklendi ancak %c alındı\n"
+
+#: fe-connect.c:3388
+msgid "unexpected message from server during startup\n"
+msgstr "başlangıç sırasında sunucudan beklenmeyen bir mesaj alındı\n"
+
+#: fe-connect.c:3615
+#, c-format
+msgid "could not make a writable connection to server \"%s:%s\"\n"
+msgstr "sunucuya yazılabilir (writable) bağlantı sağlanamadı \"%s:%s\"\n"
+
+#: fe-connect.c:3661
+#, c-format
+msgid "test \"SHOW transaction_read_only\" failed on server \"%s:%s\"\n"
+msgstr "\"SHOW transaction_read_only\" testi sunucuda başarısız oldu \"%s:%s\"\n"
+
+#: fe-connect.c:3676
+#, c-format
+msgid "invalid connection state %d, probably indicative of memory corruption\n"
+msgstr "%d - geçersiz bağlantı durumu, bellekteki veri zarar görmüş olabilir\n"
+
+#: fe-connect.c:4118 fe-connect.c:4178
+#, c-format
+msgid "PGEventProc \"%s\" failed during PGEVT_CONNRESET event\n"
+msgstr "PGEventProc \"%s\" işlemi PGEVT_CONNRESET işlemi sırasında başarısız oldu\n"
+
+#: fe-connect.c:4525
+#, c-format
+msgid "invalid LDAP URL \"%s\": scheme must be ldap://\n"
+msgstr "geçersiz LDAP URL \"%s\": şema, ldap:// ile başlamalıdir\n"
+
+#: fe-connect.c:4540
+#, c-format
+msgid "invalid LDAP URL \"%s\": missing distinguished name\n"
+msgstr "geçersiz LDAP URL \"%s\": distinguished isim eksik\n"
+
+#: fe-connect.c:4551 fe-connect.c:4604
+#, c-format
+msgid "invalid LDAP URL \"%s\": must have exactly one attribute\n"
+msgstr "geçersiz LDAP URL \"%s\": tam olarak bir nitelik (attribute) içermelidir\n"
+
+#: fe-connect.c:4561 fe-connect.c:4618
+#, c-format
+msgid "invalid LDAP URL \"%s\": must have search scope (base/one/sub)\n"
+msgstr "geçersiz LDAP URL \"%s\": arama kapsamı içermelidir (base/one/sub)\n"
+
+#: fe-connect.c:4572
+#, c-format
+msgid "invalid LDAP URL \"%s\": no filter\n"
+msgstr "geçersiz LDAP URL \"%s\": filtre eksik\n"
+
+#: fe-connect.c:4593
+#, c-format
+msgid "invalid LDAP URL \"%s\": invalid port number\n"
+msgstr "geçersiz LDAP URL \"%s\": geçersiz port numarası\n"
+
+#: fe-connect.c:4627
+msgid "could not create LDAP structure\n"
+msgstr "LDAP yapısı oluşturma hatası\n"
+
+#: fe-connect.c:4703
+#, c-format
+msgid "lookup on LDAP server failed: %s\n"
+msgstr "LDAP sonucunda sorgulama hatası: %s\n"
+
+#: fe-connect.c:4714
+msgid "more than one entry found on LDAP lookup\n"
+msgstr "LDAP sorgusu sonucunda birden fazla giriş bulundu\n"
+
+#: fe-connect.c:4715 fe-connect.c:4727
+msgid "no entry found on LDAP lookup\n"
+msgstr "LDAP sorgusu sonucunda hiçbir giriş bulunamadı\n"
+
+#: fe-connect.c:4738 fe-connect.c:4751
+msgid "attribute has no values on LDAP lookup\n"
+msgstr "LDAP sorgusu sonucunda bulunan attribute, hiçbir değer içermiyor\n"
+
+#: fe-connect.c:4803 fe-connect.c:4822 fe-connect.c:5351
+#, c-format
+msgid "missing \"=\" after \"%s\" in connection info string\n"
+msgstr "bağlantı bilgi katarında \"%s\" bilgisinden sonra \"=\" işareti eksik\n"
+
+#: fe-connect.c:4895 fe-connect.c:5536 fe-connect.c:6310
+#, c-format
+msgid "invalid connection option \"%s\"\n"
+msgstr "geçersiz bağlantı seçeneği \"%s\"\n"
+
+#: fe-connect.c:4911 fe-connect.c:5400
+msgid "unterminated quoted string in connection info string\n"
+msgstr "bağlantı bilgi katarında sonlandırılmamış tırnaklı katar\n"
+
+#: fe-connect.c:4994
+#, c-format
+msgid "definition of service \"%s\" not found\n"
+msgstr "\"%s\" servisinin tanımı bulunamadı\n"
+
+#: fe-connect.c:5017
+#, c-format
+msgid "service file \"%s\" not found\n"
+msgstr "\"%s\" servis dosyası bulunamadı\n"
+
+#: fe-connect.c:5030
+#, c-format
+msgid "line %d too long in service file \"%s\"\n"
+msgstr "\"%2$s\" servis dosyasında %1$d no'lu satır çok uzun \n"
+
+#: fe-connect.c:5101 fe-connect.c:5145
+#, c-format
+msgid "syntax error in service file \"%s\", line %d\n"
+msgstr "\"%s\" servis dosyasında yazım hatası, satır no %d\n"
+
+#: fe-connect.c:5112
+#, c-format
+msgid "nested service specifications not supported in service file \"%s\", line %d\n"
+msgstr "\"%s\" servis dosyası satır no %d , desteklenmeyen içiçe servis tanımlamaları\n"
+
+#: fe-connect.c:5832
+#, c-format
+msgid "invalid URI propagated to internal parser routine: \"%s\"\n"
+msgstr "dahili çözümleyici yordamına aktarılan geçersiz URI: \"%s\"\n"
+
+#: fe-connect.c:5909
+#, c-format
+msgid "end of string reached when looking for matching \"]\" in IPv6 host address in URI: \"%s\"\n"
+msgstr "URI içinde IPv6 sunucu adresinde eşleşen \"]\" aranırken dize sonuna ulaşıldı: \"%s\"\n"
+
+#: fe-connect.c:5916
+#, c-format
+msgid "IPv6 host address may not be empty in URI: \"%s\"\n"
+msgstr "URI içinde IPv6 sunuu adresi boş olamaz: \"%s\"\n"
+
+#: fe-connect.c:5931
+#, c-format
+msgid "unexpected character \"%c\" at position %d in URI (expected \":\" or \"/\"): \"%s\"\n"
+msgstr "URI içinde %2$d pozisyonunda beklenmeyen karakter \"%1$c\" (\":\" veya \"/\" bekleniyordu): \"%3$s\"\n"
+
+#: fe-connect.c:6060
+#, c-format
+msgid "extra key/value separator \"=\" in URI query parameter: \"%s\"\n"
+msgstr "URI sorgu parametresinde fazla anahtar/değer ayıracı \"=\": \"%s\"\n"
+
+#: fe-connect.c:6080
+#, c-format
+msgid "missing key/value separator \"=\" in URI query parameter: \"%s\"\n"
+msgstr "URI sorgu parametresinde eksik anahtar/değer ayıracı \"=\": \"%s\"\n"
+
+#: fe-connect.c:6131
+#, c-format
+msgid "invalid URI query parameter: \"%s\"\n"
+msgstr "geçersiz URI sorgu parametresi: \"%s\"\n"
+
+#: fe-connect.c:6205
+#, c-format
+msgid "invalid percent-encoded token: \"%s\"\n"
+msgstr "geçersiz percent-encoded andacı (token)\"%s\"\n"
+
+#: fe-connect.c:6215
+#, c-format
+msgid "forbidden value %%00 in percent-encoded value: \"%s\"\n"
+msgstr "percent-encoded değeri içinde yasak değer %%00: \"%s\"\n"
+
+#: fe-connect.c:6580
+msgid "connection pointer is NULL\n"
+msgstr "bağlantı belirteci NULL'dur\n"
+
+#: fe-connect.c:6878
+#, c-format
+msgid "WARNING: password file \"%s\" is not a plain file\n"
+msgstr "UYARI: \"%s\" password dosyası düz metin dosyası değildir\n"
+
+#: fe-connect.c:6887
+#, c-format
+msgid "WARNING: password file \"%s\" has group or world access; permissions should be u=rw (0600) or less\n"
+msgstr "UYARI: \"%s\" şifre dosyası herkes ya da grup tarafından erişilebilir durumda; dosyanın izinleri u=rw (0600) ya da daha az olmalı\n"
+
+#: fe-connect.c:6981
+#, c-format
+msgid "password retrieved from file \"%s\"\n"
+msgstr "\"%s\" dosyasından parola okundu\n"
+
+#: fe-exec.c:445 fe-exec.c:2822
+#, c-format
+msgid "row number %d is out of range 0..%d"
+msgstr "%d satır numarası, 0..%d sınırının dışında"
+
+#: fe-exec.c:506 fe-protocol2.c:502 fe-protocol2.c:537 fe-protocol2.c:1056
+#: fe-protocol3.c:208 fe-protocol3.c:235 fe-protocol3.c:252 fe-protocol3.c:332
+#: fe-protocol3.c:727 fe-protocol3.c:958
+msgid "out of memory"
+msgstr "yetersiz bellek"
+
+#: fe-exec.c:507 fe-protocol2.c:1402 fe-protocol3.c:1911
+#, c-format
+msgid "%s"
+msgstr "%s"
+
+#: fe-exec.c:816
+msgid "write to server failed\n"
+msgstr "sunucuya yazma başarısız oldu\n"
+
+#: fe-exec.c:897
+msgid "NOTICE"
+msgstr "BİLGİ"
+
+#: fe-exec.c:955
+msgid "PGresult cannot support more than INT_MAX tuples"
+msgstr "PGresult INT_MAX değerinden daha fazla satır (Tuple) destekleyemez"
+
+#: fe-exec.c:967
+msgid "size_t overflow"
+msgstr "size_t taşması"
+
+#: fe-exec.c:1244 fe-exec.c:1302 fe-exec.c:1348
+msgid "command string is a null pointer\n"
+msgstr "komut katarı null belirteçtir\n"
+
+#: fe-exec.c:1308 fe-exec.c:1354 fe-exec.c:1449
+msgid "number of parameters must be between 0 and 65535\n"
+msgstr "parametrelerin sayısı 0 ve 65535 arasında olmalı\n"
+
+#: fe-exec.c:1342 fe-exec.c:1443
+msgid "statement name is a null pointer\n"
+msgstr "durum adı null belirteçtir\n"
+
+#: fe-exec.c:1362 fe-exec.c:1525 fe-exec.c:2234 fe-exec.c:2436
+msgid "function requires at least protocol version 3.0\n"
+msgstr "fonksiyon en az 3.0 prokolüne gereksinim duyuyor.\n"
+
+#: fe-exec.c:1480
+msgid "no connection to the server\n"
+msgstr "sunucuya bağlantı yok\n"
+
+#: fe-exec.c:1487
+msgid "another command is already in progress\n"
+msgstr "şu anda işlenen başka bir komut var\n"
+
+#: fe-exec.c:1601
+msgid "length must be given for binary parameter\n"
+msgstr "binary parametresinin uzunluğu belirtilmelidir\n"
+
+#: fe-exec.c:1864
+#, c-format
+msgid "unexpected asyncStatus: %d\n"
+msgstr "beklenmeyen asyncStatus: %d\n"
+
+#: fe-exec.c:1884
+#, c-format
+msgid "PGEventProc \"%s\" failed during PGEVT_RESULTCREATE event\n"
+msgstr "PGEventProc \"%s\" işlemi PGEVT_RESULTCREATE işlemi sırasında başarısız oldu\n"
+
+#: fe-exec.c:2044
+msgid "COPY terminated by new PQexec"
+msgstr "COPY, yeni PQexec tarafından sonlandırıldı"
+
+#: fe-exec.c:2052
+msgid "COPY IN state must be terminated first\n"
+msgstr "Öncelikle COPY IN durumu sonlandırılmalıdır\n"
+
+#: fe-exec.c:2072
+msgid "COPY OUT state must be terminated first\n"
+msgstr "Öncelikle COPY OUT durumu sonlandırılmalıdır\n"
+
+#: fe-exec.c:2080
+msgid "PQexec not allowed during COPY BOTH\n"
+msgstr "PQexec için COPY BOTH sırasında izin verilmiyor\n"
+
+#: fe-exec.c:2326 fe-exec.c:2393 fe-exec.c:2483 fe-protocol2.c:1359
+#: fe-protocol3.c:1842
+msgid "no COPY in progress\n"
+msgstr "çalışan COPY süreci yok\n"
+
+#: fe-exec.c:2673
+msgid "connection in wrong state\n"
+msgstr "bağlantı yanlış durumda\n"
+
+#: fe-exec.c:2704
+msgid "invalid ExecStatusType code"
+msgstr "geçersiz ExecStatusType kodu"
+
+#: fe-exec.c:2731
+msgid "PGresult is not an error result\n"
+msgstr "PGresult bir hata sonucu değildir\n"
+
+#: fe-exec.c:2806 fe-exec.c:2829
+#, c-format
+msgid "column number %d is out of range 0..%d"
+msgstr "%d kolon numarası, 0..%d aralığının dışında"
+
+#: fe-exec.c:2844
+#, c-format
+msgid "parameter number %d is out of range 0..%d"
+msgstr "%d sayılı parametre aralık dışında: 0..%d"
+
+#: fe-exec.c:3154
+#, c-format
+msgid "could not interpret result from server: %s"
+msgstr "sunucudan gelen yanıt yorumlanamadı: %s"
+
+#: fe-exec.c:3393 fe-exec.c:3477
+msgid "incomplete multibyte character\n"
+msgstr "tamamlanmamış çoklu bayt karakteri\n"
+
+#: fe-lobj.c:154
+msgid "cannot determine OID of function lo_truncate\n"
+msgstr "lo_truncate fonksiyonunun OID'i belirlenemiyor\n"
+
+#: fe-lobj.c:170
+msgid "argument of lo_truncate exceeds integer range\n"
+msgstr "lo_truncate argümanı tamsayı aralığını aşıyor\n"
+
+#: fe-lobj.c:221
+msgid "cannot determine OID of function lo_truncate64\n"
+msgstr "lo_truncate64 fonksiyonunun OID'i belirlenemiyor\n"
+
+#: fe-lobj.c:279
+msgid "argument of lo_read exceeds integer range\n"
+msgstr "lo_read argümanı tamsayı aralığını aşıyor\n"
+
+#: fe-lobj.c:334
+msgid "argument of lo_write exceeds integer range\n"
+msgstr "lo_write argümanı tamsayı aralığını aşıyor\n"
+
+#: fe-lobj.c:425
+msgid "cannot determine OID of function lo_lseek64\n"
+msgstr "lo_lseek64 fonksiyonunun OID'i belirlenemiyor\n"
+
+#: fe-lobj.c:521
+msgid "cannot determine OID of function lo_create\n"
+msgstr "lo_create fonksiyonunun OID'i belirlenemiyor\n"
+
+#: fe-lobj.c:600
+msgid "cannot determine OID of function lo_tell64\n"
+msgstr "lo_tell64 fonksiyonunun OID'i belirlenemiyor\n"
+
+#: fe-lobj.c:706 fe-lobj.c:815
+#, c-format
+msgid "could not open file \"%s\": %s\n"
+msgstr "\"%s\" dosyası açılamadı: %s\n"
+
+#: fe-lobj.c:761
+#, c-format
+msgid "could not read from file \"%s\": %s\n"
+msgstr "\"%s\" dosyasından okuma hatası: %s\n"
+
+#: fe-lobj.c:835 fe-lobj.c:859
+#, c-format
+msgid "could not write to file \"%s\": %s\n"
+msgstr "\"%s\" dosyasına yazılamadı: %s\n"
+
+#: fe-lobj.c:946
+msgid "query to initialize large object functions did not return data\n"
+msgstr "large object fonksiyonlarını ilklendirecek sorgu veri döndürmedi\n"
+
+#: fe-lobj.c:995
+msgid "cannot determine OID of function lo_open\n"
+msgstr "lo_open fonksiyonunun OID'i belirlenemiyor\n"
+
+#: fe-lobj.c:1002
+msgid "cannot determine OID of function lo_close\n"
+msgstr "lo_close fonksiyonunun OID'i belirlenemiyor\n"
+
+#: fe-lobj.c:1009
+msgid "cannot determine OID of function lo_creat\n"
+msgstr "lo_create fonksiyonunun OID'i belirlenemiyor\n"
+
+#: fe-lobj.c:1016
+msgid "cannot determine OID of function lo_unlink\n"
+msgstr "lo_unlink fonksiyonunun OID'i belirlenemiyor\n"
+
+#: fe-lobj.c:1023
+msgid "cannot determine OID of function lo_lseek\n"
+msgstr "lo_lseek fonksiyonunun OID'i belirlenemiyor\n"
+
+#: fe-lobj.c:1030
+msgid "cannot determine OID of function lo_tell\n"
+msgstr "lo_tell fonksiyonunun OID'i belirlenemiyor\n"
+
+#: fe-lobj.c:1037
+msgid "cannot determine OID of function loread\n"
+msgstr "loread fonksiyonunun OID'i belirlenemiyor\n"
+
+#: fe-lobj.c:1044
+msgid "cannot determine OID of function lowrite\n"
+msgstr "lowrite fonksiyonunun OID'i belirlenemiyor\n"
+
+#: fe-misc.c:290
+#, c-format
+msgid "integer of size %lu not supported by pqGetInt"
+msgstr "%lu büyüklüğündeki tamsayılar pqGetInt tarafından desteklenmez"
+
+#: fe-misc.c:326
+#, c-format
+msgid "integer of size %lu not supported by pqPutInt"
+msgstr "%lu büyüklüğündeki tamsayılar pqPutInt tarafından desteklenmez"
+
+#: fe-misc.c:637 fe-misc.c:859
+msgid "connection not open\n"
+msgstr "bağlantı açık değil\n"
+
+#: fe-misc.c:807 fe-secure-openssl.c:206 fe-secure-openssl.c:314
+#: fe-secure.c:268 fe-secure.c:385
+msgid ""
+"server closed the connection unexpectedly\n"
+"\tThis probably means the server terminated abnormally\n"
+"\tbefore or while processing the request.\n"
+msgstr ""
+"sunucu bağlantıyı beklenmedik şekilde kapattı\n"
+"\tBu muhtemelen sunucunun isteği işlemeden önce ya da \n"
+"\tisteği işlerken anormal olarak kapandığı anlamına gelir.\n"
+
+#: fe-misc.c:1046
+msgid "timeout expired\n"
+msgstr "zamanaşımı süresi sona derdi\n"
+
+#: fe-misc.c:1091
+msgid "invalid socket\n"
+msgstr "geçersiz soket\n"
+
+#: fe-misc.c:1114
+#, c-format
+msgid "select() failed: %s\n"
+msgstr "select() başarısız oldu: %s\n"
+
+#: fe-protocol2.c:90
+#, c-format
+msgid "invalid setenv state %c, probably indicative of memory corruption\n"
+msgstr "geçersiz setenv durumu %c, belleğin zarar görmesinin bir işareti olabilir\n"
+
+#: fe-protocol2.c:389
+#, c-format
+msgid "invalid state %c, probably indicative of memory corruption\n"
+msgstr "geçersiz %c durumu, belleğin zarar görmesinin bir işareti olabilir\n"
+
+#: fe-protocol2.c:478 fe-protocol3.c:185
+#, c-format
+msgid "message type 0x%02x arrived from server while idle"
+msgstr "sunucu boş durumdayken sunucudan 0x%02x ileti tipi geldi"
+
+#: fe-protocol2.c:528
+#, c-format
+msgid "unexpected character %c following empty query response (\"I\" message)"
+msgstr "boş sorgu yanıtını takip eden geçersiz karakter:%c (\"I\" mesajı)"
+
+#: fe-protocol2.c:594
+#, c-format
+msgid "server sent data (\"D\" message) without prior row description (\"T\" message)"
+msgstr "sunucu önceki satır tanımı (\"T\" mesajı) olmadan veri (\"D\" mesajı) gönderdi"
+
+#: fe-protocol2.c:612
+#, c-format
+msgid "server sent binary data (\"B\" message) without prior row description (\"T\" message)"
+msgstr "sunucu önceki satır tanımı (\"T\" mesajı) olmadan ikili veri (\"B\" mesajı) gönderdi"
+
+#: fe-protocol2.c:632 fe-protocol3.c:411
+#, c-format
+msgid "unexpected response from server; first received character was \"%c\"\n"
+msgstr "sunucudan beklenmeyen bir yanıt alındı; alınan ilk karakter\"%c\" idi\n"
+
+#: fe-protocol2.c:761 fe-protocol2.c:936 fe-protocol3.c:626 fe-protocol3.c:853
+msgid "out of memory for query result"
+msgstr "sorgu sonucu için yetersiz bellek"
+
+#: fe-protocol2.c:1414
+#, c-format
+msgid "lost synchronization with server, resetting connection"
+msgstr "sunucu ile eşzamanlama kayboldu, bağlantı yeniden açılıyor"
+
+#: fe-protocol2.c:1536 fe-protocol2.c:1568 fe-protocol3.c:2099
+#, c-format
+msgid "protocol error: id=0x%x\n"
+msgstr "protokol hatası: id=0x%x\n"
+
+#: fe-protocol3.c:367
+msgid "server sent data (\"D\" message) without prior row description (\"T\" message)\n"
+msgstr ""
+"sunucu, önceki satır tanımı (\"T\" mesajı) olmadan veri (\"D\" mesajı) gönderdi\n"
+"\n"
+
+#: fe-protocol3.c:432
+#, c-format
+msgid "message contents do not agree with length in message type \"%c\"\n"
+msgstr "ileti içeriği,\"%c\" ileti tipinin içindeki uzunlukla aynı değil\n"
+
+#: fe-protocol3.c:453
+#, c-format
+msgid "lost synchronization with server: got message type \"%c\", length %d\n"
+msgstr "sunucu ile eşzamanlılık kayboldu: \"%c\" ileti tipi alındı, uzunluğu %d\n"
+
+#: fe-protocol3.c:504 fe-protocol3.c:544
+msgid "insufficient data in \"T\" message"
+msgstr "\"T\" mesajında yetersiz veri"
+
+#: fe-protocol3.c:577
+msgid "extraneous data in \"T\" message"
+msgstr "\"T\" mesajında ilgisiz veri"
+
+#: fe-protocol3.c:690
+msgid "extraneous data in \"t\" message"
+msgstr "\"t\" mesajında ilgisiz veri"
+
+#: fe-protocol3.c:761 fe-protocol3.c:793 fe-protocol3.c:811
+msgid "insufficient data in \"D\" message"
+msgstr "\"D\" mesajında yetersiz veri"
+
+#: fe-protocol3.c:767
+msgid "unexpected field count in \"D\" message"
+msgstr "\"D\" mesajında beklenmeyen alan sayısı"
+
+#: fe-protocol3.c:820
+msgid "extraneous data in \"D\" message"
+msgstr "\"D\" mesajında ilgisiz veri"
+
+#: fe-protocol3.c:1012
+msgid "no error message available\n"
+msgstr "hata mesajı bulunmuyor\n"
+
+#. translator: %s represents a digit string
+#: fe-protocol3.c:1060 fe-protocol3.c:1079
+#, c-format
+msgid " at character %s"
+msgstr " %s. karakterde"
+
+#: fe-protocol3.c:1092
+#, c-format
+msgid "DETAIL: %s\n"
+msgstr "AYRINTI: %s\n"
+
+#: fe-protocol3.c:1095
+#, c-format
+msgid "HINT: %s\n"
+msgstr "İPUCU: %s\n"
+
+#: fe-protocol3.c:1098
+#, c-format
+msgid "QUERY: %s\n"
+msgstr "SORGU: %s\n"
+
+#: fe-protocol3.c:1105
+#, c-format
+msgid "CONTEXT: %s\n"
+msgstr "BAĞLAM: %s\n"
+
+#: fe-protocol3.c:1114
+#, c-format
+msgid "SCHEMA NAME: %s\n"
+msgstr "ŞEMA ADI: %s\n"
+
+#: fe-protocol3.c:1118
+#, c-format
+msgid "TABLE NAME: %s\n"
+msgstr "TABLO ADI: %s\n"
+
+#: fe-protocol3.c:1122
+#, c-format
+msgid "COLUMN NAME: %s\n"
+msgstr "SÜTUN ADI: %s\n"
+
+#: fe-protocol3.c:1126
+#, c-format
+msgid "DATATYPE NAME: %s\n"
+msgstr "VERİ TİPİ ADI: %s\n"
+
+#: fe-protocol3.c:1130
+#, c-format
+msgid "CONSTRAINT NAME: %s\n"
+msgstr "KISITLAMA ADI: %s\n"
+
+#: fe-protocol3.c:1142
+msgid "LOCATION: "
+msgstr "YER: "
+
+#: fe-protocol3.c:1144
+#, c-format
+msgid "%s, "
+msgstr "%s, "
+
+#: fe-protocol3.c:1146
+#, c-format
+msgid "%s:%s"
+msgstr "%s:%s"
+
+#: fe-protocol3.c:1341
+#, c-format
+msgid "LINE %d: "
+msgstr "SATIR %d: "
+
+#: fe-protocol3.c:1736
+msgid "PQgetline: not doing text COPY OUT\n"
+msgstr "PQgetline: COPY OUT metnini yapmıyor\n"
+
+#: fe-secure-common.c:124
+msgid "SSL certificate's name contains embedded null\n"
+msgstr "SSL sertifikasının ismi gömülü olarak null içeriyor\n"
+
+#: fe-secure-common.c:171
+msgid "host name must be specified for a verified SSL connection\n"
+msgstr "doğrulanmış bir SSL bağlantısı için makina adı belirtilmelidir\n"
+
+#: fe-secure-common.c:196
+#, c-format
+msgid "server certificate for \"%s\" does not match host name \"%s\"\n"
+msgstr "\"%s\" için olan sunucu sertifikası \"%s\" olan makina adı ile eşleşmiyor\n"
+
+#: fe-secure-common.c:202
+msgid "could not get server's host name from server certificate\n"
+msgstr "sunucunun makina adı sunucu sertifikasından alınamadı\n"
+
+#: fe-secure-openssl.c:211 fe-secure-openssl.c:319 fe-secure-openssl.c:1219
+#, c-format
+msgid "SSL SYSCALL error: %s\n"
+msgstr "SSL SYSCALL hatası: %s\n"
+
+#: fe-secure-openssl.c:218 fe-secure-openssl.c:326 fe-secure-openssl.c:1223
+msgid "SSL SYSCALL error: EOF detected\n"
+msgstr "SSL SYSCALL hatası: EOF bulundu\n"
+
+#: fe-secure-openssl.c:229 fe-secure-openssl.c:337 fe-secure-openssl.c:1232
+#, c-format
+msgid "SSL error: %s\n"
+msgstr "SSL hatası: %s\n"
+
+#: fe-secure-openssl.c:244 fe-secure-openssl.c:352
+msgid "SSL connection has been closed unexpectedly\n"
+msgstr "SSL bağlantısı beklenmeyen şekilde sonlandırıldı\n"
+
+#: fe-secure-openssl.c:250 fe-secure-openssl.c:358 fe-secure-openssl.c:1241
+#, c-format
+msgid "unrecognized SSL error code: %d\n"
+msgstr "tanımlanamayan SSL hata kodu: %d\n"
+
+#: fe-secure-openssl.c:398
+msgid "could not determine server certificate signature algorithm\n"
+msgstr ""
+"sunucu sertifika imza algoritması belirlenemedi\n"
+"\n"
+
+#: fe-secure-openssl.c:419
+#, c-format
+msgid "could not find digest for NID %s\n"
+msgstr "NID %s için özet (digest) bulunamadı\n"
+
+#: fe-secure-openssl.c:429
+msgid "could not generate peer certificate hash\n"
+msgstr "karşı tarafın sertifika hash'i oluşturulamadı\n"
+
+#: fe-secure-openssl.c:486
+msgid "SSL certificate's name entry is missing\n"
+msgstr ""
+"SSL sertifikasının isim girişi eksik\n"
+"\n"
+
+#: fe-secure-openssl.c:815
+#, c-format
+msgid "could not create SSL context: %s\n"
+msgstr "SSL bağlamı oluşturulamadı: %s\n"
+
+#: fe-secure-openssl.c:852
+#, c-format
+msgid "could not read root certificate file \"%s\": %s\n"
+msgstr "\"%s\"kök sertifika dosyası okunamadı: %s\n"
+
+#: fe-secure-openssl.c:880
+#, c-format
+msgid "SSL library does not support CRL certificates (file \"%s\")\n"
+msgstr "kurulu SSL kütüphanesi CRL sertifikalarını desteklemiyor (dosya adı \"%s\")\n"
+
+#: fe-secure-openssl.c:908
+msgid ""
+"could not get home directory to locate root certificate file\n"
+"Either provide the file or change sslmode to disable server certificate verification.\n"
+msgstr ""
+"kök sertifika dosyasının ev dizini bulunamadı\n"
+"Ya bir dosya adı belirtin, ya da sunucu sertifika doğrulamasını kapatmak için sslmode ayarını değiştirin.\n"
+
+#: fe-secure-openssl.c:912
+#, c-format
+msgid ""
+"root certificate file \"%s\" does not exist\n"
+"Either provide the file or change sslmode to disable server certificate verification.\n"
+msgstr ""
+"\"%s\" kök sertifika dosyası mevcut değil\n"
+"Ya bu dosyayı oluşturun ya da sslmode ayarını sunucu sertifika doğrulamasını kapatmak için değiştirin.\n"
+
+#: fe-secure-openssl.c:943
+#, c-format
+msgid "could not open certificate file \"%s\": %s\n"
+msgstr "\"%s\" sertifikası açılamadı: %s\n"
+
+#: fe-secure-openssl.c:962
+#, c-format
+msgid "could not read certificate file \"%s\": %s\n"
+msgstr "\"%s\" sertifikası okunamadı: %s\n"
+
+#: fe-secure-openssl.c:987
+#, c-format
+msgid "could not establish SSL connection: %s\n"
+msgstr "SSL bağlantısı sağlanamadı: %s\n"
+
+#: fe-secure-openssl.c:1041
+#, c-format
+msgid "could not load SSL engine \"%s\": %s\n"
+msgstr "\"%s\" SSL motoru yüklenemedi: %s\n"
+
+#: fe-secure-openssl.c:1053
+#, c-format
+msgid "could not initialize SSL engine \"%s\": %s\n"
+msgstr "\"%s\" SSL motoru ilklendirilemedi: %s\n"
+
+#: fe-secure-openssl.c:1069
+#, c-format
+msgid "could not read private SSL key \"%s\" from engine \"%s\": %s\n"
+msgstr "\"%2$s\" motorundan \"%1$s\" özel SSL anahtarı okunamadı: %3$s\n"
+
+#: fe-secure-openssl.c:1083
+#, c-format
+msgid "could not load private SSL key \"%s\" from engine \"%s\": %s\n"
+msgstr "\"%2$s\" motorundan \"%1$s\" özel SSL anahtarı yüklenemedi: %3$s\n"
+
+#: fe-secure-openssl.c:1120
+#, c-format
+msgid "certificate present, but not private key file \"%s\"\n"
+msgstr "sertifika mevcut ancak özel anahtar (private key) mevcut değil \"%s\"\n"
+
+#: fe-secure-openssl.c:1128
+#, c-format
+msgid "private key file \"%s\" has group or world access; permissions should be u=rw (0600) or less\n"
+msgstr "\"%s\" özel anahtar (private key) dosyası herkes ya da grup tarafından erişilebilir durumda; dosyanın izinleri u=rw (0600) ya da daha az olmalı\n"
+
+#: fe-secure-openssl.c:1139
+#, c-format
+msgid "could not load private key file \"%s\": %s\n"
+msgstr "özel anahtar (private key) dosyası \"%s\" okunamıyor: %s\n"
+
+#: fe-secure-openssl.c:1153
+#, c-format
+msgid "certificate does not match private key file \"%s\": %s\n"
+msgstr "sertifika, \"%s\" özel anahtar (private key) dosyası ile uyuşmuyor: %s\n"
+
+#: fe-secure-openssl.c:1262
+#, c-format
+msgid "certificate could not be obtained: %s\n"
+msgstr "sertifika elde edilemedi: %s\n"
+
+#: fe-secure-openssl.c:1351
+#, c-format
+msgid "no SSL error reported"
+msgstr "SSL hatası raporlanmadı"
+
+#: fe-secure-openssl.c:1360
+#, c-format
+msgid "SSL error code %lu"
+msgstr "SSL hata kodu: %lu"
+
+#: fe-secure.c:276
+#, c-format
+msgid "could not receive data from server: %s\n"
+msgstr "sunucudan veri alınamadı: %s\n"
+
+#: fe-secure.c:392
+#, c-format
+msgid "could not send data to server: %s\n"
+msgstr "sunucuya veri gönderilemedi: %s\n"
+
+#: win32.c:317
+#, c-format
+msgid "unrecognized socket error: 0x%08X/%d"
+msgstr "bilinmeyen soket hatası: 0x%08X/%d"
+
+#~ msgid "certificate could not be validated: %s\n"
+#~ msgstr "sertifika doğrulanamadı: %s\n"
+
+#~ msgid "private key file \"%s\" has wrong permissions\n"
+#~ msgstr "\"%s\" özel anahtarı yanlış izinlere sahip\n"
+
+#~ msgid "invalid value of PGSSLKEY environment variable\n"
+#~ msgstr "PGSSLKEY ortam değişkeni için geçersiz değer\n"
+
+#~ msgid "could not get user information\n"
+#~ msgstr "kullanıcı bilgisi alınamadı\n"
+
+#~ msgid "server common name \"%s\" does not resolve to %ld.%ld.%ld.%ld\n"
+#~ msgstr "Sunucu ortak adı olan \"%s\" %ld.%ld.%ld.%ld adresine çözülemiyor\n"
+
+#~ msgid "unsupported protocol\n"
+#~ msgstr "desteklenmeyen protokol\n"
+
+#~ msgid "could not get information about host \"%s\": %s\n"
+#~ msgstr "\"%s\" sunucusu hakkında bilgi alınamadı: %s\n"
+
+#~ msgid "error querying socket: %s\n"
+#~ msgstr "soketi sorgularken hata oluştu: %s\n"
+
+#~ msgid "root certificate file \"%s\" does not exist"
+#~ msgstr "kök sertifika dosyası \"%s\" mevcut değildir"
+
+#~ msgid "invalid sslverify value: \"%s\"\n"
+#~ msgstr "geçersiz sslverify değeri: \"%s\"\n"
+
+#~ msgid "could not read private key file \"%s\": %s\n"
+#~ msgstr "\"%s\" özel anahtar dosyası okunamadı: %s\n"
+
+#~ msgid "private key file \"%s\" changed during execution\n"
+#~ msgstr "\"%s\" özel anahtar dosyası çalışma anında açılamadı\n"
+
+#~ msgid "could not open private key file \"%s\": %s\n"
+#~ msgstr "\"%s\" özel anahtar dosyası açılamadı: %s\n"
+
+#~ msgid "verified SSL connections are only supported when connecting to a host name"
+#~ msgstr "Onaylanmış SSL bağlantıları sadece bir sunucu adına bağlanıldığı zaman geçerlidir"
+
+#~ msgid "could not get home directory to locate client certificate files\n"
+#~ msgstr "İstemci sertifika dosyalarının olduğu ev dizini bulunamadı\n"
+
+#~ msgid "socket not open\n"
+#~ msgstr "soket açık değil\n"
+
+#~ msgid "could not get home directory to locate service definition file"
+#~ msgstr "servis dosyasının olduğu ev dizini bulunamadı"
+
+#~ msgid "setsockopt(SO_KEEPALIVE) failed: %s\n"
+#~ msgstr "setsockopt(SO_KEEPALIVE) başarısız oldu: %s\n"
+
+#~ msgid "setsockopt(TCP_KEEPINTVL) failed: %s\n"
+#~ msgstr "setsockopt(TCP_KEEPINTVL) başarısız oldu: %s\n"
+
+#~ msgid "setsockopt(TCP_KEEPALIVE) failed: %s\n"
+#~ msgstr "setsockopt(SO_REUSEADDR) başarısız oldu: %s\n"
+
+#~ msgid "setsockopt(TCP_KEEPIDLE) failed: %s\n"
+#~ msgstr "setsockopt(TCP_KEEPIDLE) başarısız oldu: %s\n"
+
+#~ msgid "could not restore non-blocking mode on socket: %s\n"
+#~ msgstr "could not restore non-blocking mode on socket: %s\n"
+
+#~ msgid "Kerberos 5 authentication rejected: %*s\n"
+#~ msgstr "Kerberos 5 yetkilendirmesi kabul edilmedi: %*s\n"
+
+#~ msgid "could not set socket to blocking mode: %s\n"
+#~ msgstr "soket engelleme moduna ayarlanamadı: %s\n"
+
+#~ msgid "GSSAPI name import error"
+#~ msgstr "GSSAPI ad aktarma hatası"
diff --git a/src/interfaces/libpq/po/uk.po b/src/interfaces/libpq/po/uk.po
new file mode 100644
index 0000000..3920b7d
--- /dev/null
+++ b/src/interfaces/libpq/po/uk.po
@@ -0,0 +1,1273 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: postgresql\n"
+"Report-Msgid-Bugs-To: pgsql-bugs@lists.postgresql.org\n"
+"POT-Creation-Date: 2020-09-21 21:09+0000\n"
+"PO-Revision-Date: 2020-09-22 13:43\n"
+"Last-Translator: pasha_golub\n"
+"Language-Team: Ukrainian\n"
+"Language: uk\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=4; plural=((n%10==1 && n%100!=11) ? 0 : ((n%10 >= 2 && n%10 <=4 && (n%100 < 12 || n%100 > 14)) ? 1 : ((n%10 == 0 || (n%10 >= 5 && n%10 <=9)) || (n%100 >= 11 && n%100 <= 14)) ? 2 : 3));\n"
+"X-Crowdin-Project: postgresql\n"
+"X-Crowdin-Project-ID: 324573\n"
+"X-Crowdin-Language: uk\n"
+"X-Crowdin-File: /DEV_13/libpq.pot\n"
+"X-Crowdin-File-ID: 486\n"
+
+#: fe-auth-scram.c:212
+msgid "malformed SCRAM message (empty message)\n"
+msgstr "неправильне повідомлення SCRAM (пусте повідомлення)\n"
+
+#: fe-auth-scram.c:218
+msgid "malformed SCRAM message (length mismatch)\n"
+msgstr "неправильне повідомлення SCRAM (невідповідність довжини)\n"
+
+#: fe-auth-scram.c:265
+msgid "incorrect server signature\n"
+msgstr "невірний підпис сервера\n"
+
+#: fe-auth-scram.c:274
+msgid "invalid SCRAM exchange state\n"
+msgstr "неприпустимий стан обміну SCRAM\n"
+
+#: fe-auth-scram.c:296
+#, c-format
+msgid "malformed SCRAM message (attribute \"%c\" expected)\n"
+msgstr "неправильне повідомлення SCRAM (очікувався атрибут \"%c\")\n"
+
+#: fe-auth-scram.c:305
+#, c-format
+msgid "malformed SCRAM message (expected character \"=\" for attribute \"%c\")\n"
+msgstr "неправильне повідомлення SCRAM (очікувався символ \"=\" для атрибута \"%c\")\n"
+
+#: fe-auth-scram.c:346
+msgid "could not generate nonce\n"
+msgstr "не вдалося згенерувати одноразовий ідентифікатор\n"
+
+#: fe-auth-scram.c:356 fe-auth-scram.c:431 fe-auth-scram.c:579
+#: fe-auth-scram.c:600 fe-auth-scram.c:626 fe-auth-scram.c:641
+#: fe-auth-scram.c:691 fe-auth-scram.c:725 fe-auth.c:289 fe-auth.c:359
+#: fe-auth.c:394 fe-auth.c:611 fe-auth.c:770 fe-auth.c:1129 fe-auth.c:1277
+#: fe-connect.c:892 fe-connect.c:1419 fe-connect.c:1595 fe-connect.c:2200
+#: fe-connect.c:2223 fe-connect.c:2952 fe-connect.c:4598 fe-connect.c:4854
+#: fe-connect.c:4973 fe-connect.c:5226 fe-connect.c:5306 fe-connect.c:5405
+#: fe-connect.c:5661 fe-connect.c:5690 fe-connect.c:5762 fe-connect.c:5786
+#: fe-connect.c:5804 fe-connect.c:5905 fe-connect.c:5914 fe-connect.c:6270
+#: fe-connect.c:6420 fe-exec.c:2747 fe-exec.c:3494 fe-exec.c:3659
+#: fe-gssapi-common.c:111 fe-lobj.c:895 fe-protocol2.c:1207 fe-protocol3.c:995
+#: fe-protocol3.c:1699 fe-secure-common.c:110 fe-secure-gssapi.c:504
+#: fe-secure-openssl.c:440 fe-secure-openssl.c:1091
+msgid "out of memory\n"
+msgstr "недостатньо пам'яті\n"
+
+#: fe-auth-scram.c:364
+msgid "could not encode nonce\n"
+msgstr "не вдалося закодувати одноразовий ідентифікатор\n"
+
+#: fe-auth-scram.c:563
+msgid "could not encode client proof\n"
+msgstr "не вдалося закодувати підтвердження клієнта\n"
+
+#: fe-auth-scram.c:618
+msgid "invalid SCRAM response (nonce mismatch)\n"
+msgstr "неприпустима відповідь SCRAM (невідповідність одноразового ідентифікатора)\n"
+
+#: fe-auth-scram.c:651
+msgid "malformed SCRAM message (invalid salt)\n"
+msgstr "неправильне повідомлення SCRAM (неприпустима сіль)\n"
+
+#: fe-auth-scram.c:665
+msgid "malformed SCRAM message (invalid iteration count)\n"
+msgstr "неправильне повідомлення SCRAM (неприпустима кількість ітерацій)\n"
+
+#: fe-auth-scram.c:671
+msgid "malformed SCRAM message (garbage at end of server-first-message)\n"
+msgstr "неправильне повідомлення SCRAM (сміття в кінці першого повідомлення сервера)\n"
+
+#: fe-auth-scram.c:702
+#, c-format
+msgid "error received from server in SCRAM exchange: %s\n"
+msgstr "отримано помилку від сервера під час обміну SCRAM: %s\n"
+
+#: fe-auth-scram.c:718
+msgid "malformed SCRAM message (garbage at end of server-final-message)\n"
+msgstr "неправильне повідомлення SCRAM (сміття в кінці останнього повідомлення сервера)\n"
+
+#: fe-auth-scram.c:737
+msgid "malformed SCRAM message (invalid server signature)\n"
+msgstr "неправильне повідомлення SCRAM (неприпустимий підпис сервера)\n"
+
+#: fe-auth.c:76
+#, c-format
+msgid "out of memory allocating GSSAPI buffer (%d)\n"
+msgstr "недостатньо пам'яті для буфера GSSAPI (%d)\n"
+
+#: fe-auth.c:131
+msgid "GSSAPI continuation error"
+msgstr "Помилка продовження у GSSAPI"
+
+#: fe-auth.c:158 fe-auth.c:388 fe-gssapi-common.c:98 fe-secure-common.c:98
+msgid "host name must be specified\n"
+msgstr "потрібно вказати ім’я хоста\n"
+
+#: fe-auth.c:165
+msgid "duplicate GSS authentication request\n"
+msgstr "дублікат запиту автентифікації GSS\n"
+
+#: fe-auth.c:230
+#, c-format
+msgid "out of memory allocating SSPI buffer (%d)\n"
+msgstr "недостатньо пам'яті для буфера SSPI (%d)\n"
+
+#: fe-auth.c:278
+msgid "SSPI continuation error"
+msgstr "Помилка продовження SSPI"
+
+#: fe-auth.c:349
+msgid "duplicate SSPI authentication request\n"
+msgstr "дублікат запиту автентифікації SSPI\n"
+
+#: fe-auth.c:374
+msgid "could not acquire SSPI credentials"
+msgstr "не вдалось отримати облікові дані SSPI"
+
+#: fe-auth.c:429
+msgid "channel binding required, but SSL not in use\n"
+msgstr "необхідно зв’язування каналів, але SSL не використовується\n"
+
+#: fe-auth.c:436
+msgid "duplicate SASL authentication request\n"
+msgstr "дублікат запиту автентифікації SASL\n"
+
+#: fe-auth.c:492
+msgid "channel binding is required, but client does not support it\n"
+msgstr "потрібно зв'язування каналів, але клієнт не підтримує його\n"
+
+#: fe-auth.c:509
+msgid "server offered SCRAM-SHA-256-PLUS authentication over a non-SSL connection\n"
+msgstr "сервер запропонував автентифікацію SCRAM-SHA-256-PLUS через підключення без SSL\n"
+
+#: fe-auth.c:521
+msgid "none of the server's SASL authentication mechanisms are supported\n"
+msgstr "жоден з серверних механізмів автентифікації SASL не підтримується\n"
+
+#: fe-auth.c:529
+msgid "channel binding is required, but server did not offer an authentication method that supports channel binding\n"
+msgstr "потрібно зв'язування каналів, але сервер не запропонував метод аутентифікації, який підтримує зв’язування каналів\n"
+
+#: fe-auth.c:635
+#, c-format
+msgid "out of memory allocating SASL buffer (%d)\n"
+msgstr "недостатньо пам'яті для буфера SASL (%d)\n"
+
+#: fe-auth.c:660
+msgid "AuthenticationSASLFinal received from server, but SASL authentication was not completed\n"
+msgstr "Від сервера отримано AuthenticationSASLFinal, але автентифікація SASL не була завершена\n"
+
+#: fe-auth.c:737
+msgid "SCM_CRED authentication method not supported\n"
+msgstr "Спосіб автентифікації SCM_CRED не підтримується\n"
+
+#: fe-auth.c:836
+msgid "channel binding required, but server authenticated client without channel binding\n"
+msgstr "потрібно зв'язування каналів, але сервер автентифікував клієнта без зв’язування каналів\n"
+
+#: fe-auth.c:842
+msgid "channel binding required but not supported by server's authentication request\n"
+msgstr "потрібно зв'язування каналів, але не підтримується запитом на аутентифікацію сервера\n"
+
+#: fe-auth.c:875
+msgid "Kerberos 4 authentication not supported\n"
+msgstr "Автентифікація Kerberos 4 не підтримується\n"
+
+#: fe-auth.c:880
+msgid "Kerberos 5 authentication not supported\n"
+msgstr "Автентифікація Kerberos 5 не підтримується\n"
+
+#: fe-auth.c:951
+msgid "GSSAPI authentication not supported\n"
+msgstr "Автентифікація GSSAPI не підтримується\n"
+
+#: fe-auth.c:983
+msgid "SSPI authentication not supported\n"
+msgstr "Автентифікація SSPI не підтримується\n"
+
+#: fe-auth.c:991
+msgid "Crypt authentication not supported\n"
+msgstr "Автентифікація Crypt не підтримується\n"
+
+#: fe-auth.c:1057
+#, c-format
+msgid "authentication method %u not supported\n"
+msgstr "спосіб автентифікації %u не підтримується\n"
+
+#: fe-auth.c:1104
+#, c-format
+msgid "user name lookup failure: error code %lu\n"
+msgstr "невдала підстановка імені користувача: код помилки %lu\n"
+
+#: fe-auth.c:1114 fe-connect.c:2834
+#, c-format
+msgid "could not look up local user ID %d: %s\n"
+msgstr "не вдалося знайти локального користувача за ідентифікатором: %d: %s\n"
+
+#: fe-auth.c:1119 fe-connect.c:2839
+#, c-format
+msgid "local user with ID %d does not exist\n"
+msgstr "локального користувача з ідентифікатором %d не існує\n"
+
+#: fe-auth.c:1221
+msgid "unexpected shape of result set returned for SHOW\n"
+msgstr "неочікувану форму набору результатів повернуто для SHOW\n"
+
+#: fe-auth.c:1230
+msgid "password_encryption value too long\n"
+msgstr "занадто довге значення password_encryption \n"
+
+#: fe-auth.c:1270
+#, c-format
+msgid "unrecognized password encryption algorithm \"%s\"\n"
+msgstr "нерозпізнаний алгоритм шифрування пароля \"%s\"\n"
+
+#: fe-connect.c:1075
+#, c-format
+msgid "could not match %d host names to %d hostaddr values\n"
+msgstr "не вдалося зіставити %d імен хостів зі %d значеннями hostaddr\n"
+
+#: fe-connect.c:1156
+#, c-format
+msgid "could not match %d port numbers to %d hosts\n"
+msgstr "не вдалося зіставити %d номерів портів з %d хостами\n"
+
+#: fe-connect.c:1249
+#, c-format
+msgid "invalid channel_binding value: \"%s\"\n"
+msgstr "неприпустиме значення channel_binding : \"%s\"\n"
+
+#: fe-connect.c:1275
+#, c-format
+msgid "invalid sslmode value: \"%s\"\n"
+msgstr "неприпустиме значення sslmode: \"%s\"\n"
+
+#: fe-connect.c:1296
+#, c-format
+msgid "sslmode value \"%s\" invalid when SSL support is not compiled in\n"
+msgstr "значення sslmode \"%s\" неприпустиме, якщо підтримку протоколу SSL не скомпільовано\n"
+
+#: fe-connect.c:1317
+#, c-format
+msgid "invalid ssl_min_protocol_version value: \"%s\"\n"
+msgstr "неприпустиме значення ssl_min_protocol_version: \"%s\"\n"
+
+#: fe-connect.c:1325
+#, c-format
+msgid "invalid ssl_max_protocol_version value: \"%s\"\n"
+msgstr "неприпустиме значення ssl_max_protocol_version: \"%s\"\n"
+
+#: fe-connect.c:1342
+msgid "invalid SSL protocol version range\n"
+msgstr "неприпустимий діапазон версії протоколу SSL\n"
+
+#: fe-connect.c:1357
+#, c-format
+msgid "invalid gssencmode value: \"%s\"\n"
+msgstr "неприпустиме значення gssencmode: \"%s\"\n"
+
+#: fe-connect.c:1366
+#, c-format
+msgid "gssencmode value \"%s\" invalid when GSSAPI support is not compiled in\n"
+msgstr "значення gssencmode \"%s\" неприпустиме, якщо підтримку протоколу GSSAPI не скомпільовано\n"
+
+#: fe-connect.c:1401
+#, c-format
+msgid "invalid target_session_attrs value: \"%s\"\n"
+msgstr "неприпустиме значення target_session_attrs: \"%s\"\n"
+
+#: fe-connect.c:1619
+#, c-format
+msgid "could not set socket to TCP no delay mode: %s\n"
+msgstr "не вдалося встановити сокет у TCP-режим без затримки: %s\n"
+
+#: fe-connect.c:1680
+#, c-format
+msgid "could not connect to server: %s\n"
+"\tIs the server running locally and accepting\n"
+"\tconnections on Unix domain socket \"%s\"?\n"
+msgstr "не вдалося підключитися до сервера: %s\n"
+" Чи дійсно працює сервер локально і приймає\n"
+" підключення через домен сокету Unix \"%s\"?\n"
+
+#: fe-connect.c:1717
+#, c-format
+msgid "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"
+msgstr "не вдалося підключитися до сервера: %s\n"
+" Чи дійсно сервер працює на хості \"%s\" (%s) і приймає\n"
+" TCP/IP підключення на порту %s?\n"
+
+#: fe-connect.c:1725
+#, c-format
+msgid "could not connect to server: %s\n"
+"\tIs the server running on host \"%s\" and accepting\n"
+"\tTCP/IP connections on port %s?\n"
+msgstr "не вдалося підключитися до сервера: %s\n"
+" Чи дійсно сервер працює на хості \"%s\" і приймає\n"
+" TCP/IP підключення на порту %s?\n"
+
+#: fe-connect.c:1795
+#, c-format
+msgid "invalid integer value \"%s\" for connection option \"%s\"\n"
+msgstr "неприпустиме ціле значення \"%s\" для параметра з'єднання \"%s\"\n"
+
+#: fe-connect.c:1825 fe-connect.c:1859 fe-connect.c:1894 fe-connect.c:1981
+#: fe-connect.c:2623
+#, c-format
+msgid "setsockopt(%s) failed: %s\n"
+msgstr "помилка у setsockopt(%s): %s\n"
+
+#: fe-connect.c:1947
+#, c-format
+msgid "WSAIoctl(SIO_KEEPALIVE_VALS) failed: %ui\n"
+msgstr "Помилка у WSAIoctl(SIO_KEEPALIVE_VALS): %ui\n"
+
+#: fe-connect.c:2313
+msgid "invalid connection state, probably indicative of memory corruption\n"
+msgstr "неприпустимий стан підключення, можливо, пошкоджена пам'ять\n"
+
+#: fe-connect.c:2379
+#, c-format
+msgid "invalid port number: \"%s\"\n"
+msgstr "неприпустимий номер порту: \"%s\"\n"
+
+#: fe-connect.c:2395
+#, c-format
+msgid "could not translate host name \"%s\" to address: %s\n"
+msgstr "не вдалося перекласти ім’я хоста \"%s\" в адресу: %s\n"
+
+#: fe-connect.c:2408
+#, c-format
+msgid "could not parse network address \"%s\": %s\n"
+msgstr "не вдалося проаналізувати адресу мережі \"%s\": %s\n"
+
+#: fe-connect.c:2421
+#, c-format
+msgid "Unix-domain socket path \"%s\" is too long (maximum %d bytes)\n"
+msgstr "Шлях Unix-сокету \"%s\" занадто довгий (максимум %d байтів)\n"
+
+#: fe-connect.c:2436
+#, c-format
+msgid "could not translate Unix-domain socket path \"%s\" to address: %s\n"
+msgstr "не вдалося перекласти шлях Unix-сокету \"%s\" в адресу: %s\n"
+
+#: fe-connect.c:2560
+#, c-format
+msgid "could not create socket: %s\n"
+msgstr "не вдалося створити сокет: %s\n"
+
+#: fe-connect.c:2582
+#, c-format
+msgid "could not set socket to nonblocking mode: %s\n"
+msgstr "не вдалося встановити сокет у режим без блокування: %s\n"
+
+#: fe-connect.c:2592
+#, c-format
+msgid "could not set socket to close-on-exec mode: %s\n"
+msgstr "не вдалося встановити сокет у режим закриття по виконанню: %s\n"
+
+#: fe-connect.c:2610
+msgid "keepalives parameter must be an integer\n"
+msgstr "параметр keepalives має бути цілим числом\n"
+
+#: fe-connect.c:2750
+#, c-format
+msgid "could not get socket error status: %s\n"
+msgstr "не вдалося отримати статус помилки сокету: %s\n"
+
+#: fe-connect.c:2778
+#, c-format
+msgid "could not get client address from socket: %s\n"
+msgstr "не вдалося отримати адресу клієнта з сокету: %s\n"
+
+#: fe-connect.c:2820
+msgid "requirepeer parameter is not supported on this platform\n"
+msgstr "параметр requirepeer не підтримується на цій платформі\n"
+
+#: fe-connect.c:2823
+#, c-format
+msgid "could not get peer credentials: %s\n"
+msgstr "не вдалося отримати облікові дані сервера: %s\n"
+
+#: fe-connect.c:2847
+#, c-format
+msgid "requirepeer specifies \"%s\", but actual peer user name is \"%s\"\n"
+msgstr "requirepeer вказує на \"%s\", але фактичне ім'я вузла \"%s\"\n"
+
+#: fe-connect.c:2887
+#, c-format
+msgid "could not send GSSAPI negotiation packet: %s\n"
+msgstr "не вдалося передати пакет узгодження протоколу GSSAPI: %s\n"
+
+#: fe-connect.c:2899
+msgid "GSSAPI encryption required but was impossible (possibly no credential cache, no server support, or using a local socket)\n"
+msgstr "вимагалося шифрування GSSAPI, але не було неможливим (можливо, без кешу облікових даних, підтримки сервера, або використання локального сокета)\n"
+
+#: fe-connect.c:2926
+#, c-format
+msgid "could not send SSL negotiation packet: %s\n"
+msgstr "не вдалося передати пакет узгодження протоколу SSL: %s\n"
+
+#: fe-connect.c:2965
+#, c-format
+msgid "could not send startup packet: %s\n"
+msgstr "не вдалося передати стартовий пакет: %s\n"
+
+#: fe-connect.c:3035
+msgid "server does not support SSL, but SSL was required\n"
+msgstr "сервер не підтримує протокол SSL, але протокол SSL вимагається\n"
+
+#: fe-connect.c:3061
+#, c-format
+msgid "received invalid response to SSL negotiation: %c\n"
+msgstr "отримано неприпустиму відповідь на узгодження SSL: %c\n"
+
+#: fe-connect.c:3151
+msgid "server doesn't support GSSAPI encryption, but it was required\n"
+msgstr "сервер не підтримує шифрування GSSAPI, але це було необхідно\n"
+
+#: fe-connect.c:3162
+#, c-format
+msgid "received invalid response to GSSAPI negotiation: %c\n"
+msgstr "отримано неприпустиму відповідь на узгодження GSSAPI: %c\n"
+
+#: fe-connect.c:3229 fe-connect.c:3260
+#, c-format
+msgid "expected authentication request from server, but received %c\n"
+msgstr "очікувався запит автентифікації від сервера, але отримано %c\n"
+
+#: fe-connect.c:3502
+msgid "unexpected message from server during startup\n"
+msgstr "неочікуване повідомлення від сервера під час запуску\n"
+
+#: fe-connect.c:3707
+#, c-format
+msgid "could not make a writable connection to server \"%s:%s\"\n"
+msgstr "не вдалося встановити підключення для запису з сервером \"%s:%s\"\n"
+
+#: fe-connect.c:3753
+#, c-format
+msgid "test \"SHOW transaction_read_only\" failed on server \"%s:%s\"\n"
+msgstr "помилка тесту \"SHOW transaction_read_only\" на сервері \"%s:%s\"\n"
+
+#: fe-connect.c:3768
+#, c-format
+msgid "invalid connection state %d, probably indicative of memory corruption\n"
+msgstr "неприпустимий стан підключення %d, можливо, пошкоджена пам'ять\n"
+
+#: fe-connect.c:4204 fe-connect.c:4264
+#, c-format
+msgid "PGEventProc \"%s\" failed during PGEVT_CONNRESET event\n"
+msgstr "Помилка у PGEventProc \"%s\" під час події PGEVT_CONNRESET\n"
+
+#: fe-connect.c:4611
+#, c-format
+msgid "invalid LDAP URL \"%s\": scheme must be ldap://\n"
+msgstr "неприпустима URL-адреса протоколу LDAP \"%s\": схема має бути ldap://\n"
+
+#: fe-connect.c:4626
+#, c-format
+msgid "invalid LDAP URL \"%s\": missing distinguished name\n"
+msgstr "неприпустима URL-адреса протоколу LDAP \"%s\": відсутнє унікальне ім'я\n"
+
+#: fe-connect.c:4638 fe-connect.c:4693
+#, c-format
+msgid "invalid LDAP URL \"%s\": must have exactly one attribute\n"
+msgstr "неприпустима URL-адреса протоколу LDAP \"%s\": має бути лише один атрибут\n"
+
+#: fe-connect.c:4649 fe-connect.c:4708
+#, c-format
+msgid "invalid LDAP URL \"%s\": must have search scope (base/one/sub)\n"
+msgstr "неприпустима URL-адреса протоколу LDAP \"%s\": відсутня область пошуку (base/one/sub)\n"
+
+#: fe-connect.c:4660
+#, c-format
+msgid "invalid LDAP URL \"%s\": no filter\n"
+msgstr "неприпустима URL-адреса протоколу LDAP \"%s\": відсутній фільтр\n"
+
+#: fe-connect.c:4681
+#, c-format
+msgid "invalid LDAP URL \"%s\": invalid port number\n"
+msgstr "неприпустима URL-адреса протоколу LDAP \"%s\": неприпустимий номер порту\n"
+
+#: fe-connect.c:4717
+msgid "could not create LDAP structure\n"
+msgstr "не вдалося створити структуру протоколу LDAP\n"
+
+#: fe-connect.c:4793
+#, c-format
+msgid "lookup on LDAP server failed: %s\n"
+msgstr "помилка підстановки на сервері протоколу LDAP: %s\n"
+
+#: fe-connect.c:4804
+msgid "more than one entry found on LDAP lookup\n"
+msgstr "знайдено більше одного входження при підстановці протоколу LDAP\n"
+
+#: fe-connect.c:4805 fe-connect.c:4817
+msgid "no entry found on LDAP lookup\n"
+msgstr "не знайдено входження при підстановці протоколу LDAP\n"
+
+#: fe-connect.c:4828 fe-connect.c:4841
+msgid "attribute has no values on LDAP lookup\n"
+msgstr "атрибут не має значення при підстановці протоколу LDAP\n"
+
+#: fe-connect.c:4893 fe-connect.c:4912 fe-connect.c:5444
+#, c-format
+msgid "missing \"=\" after \"%s\" in connection info string\n"
+msgstr "відсутній \"=\" після \"%s\" у рядку інформації про підключення\n"
+
+#: fe-connect.c:4985 fe-connect.c:5629 fe-connect.c:6403
+#, c-format
+msgid "invalid connection option \"%s\"\n"
+msgstr "неприпустимий параметр підключення \"%s\"\n"
+
+#: fe-connect.c:5001 fe-connect.c:5493
+msgid "unterminated quoted string in connection info string\n"
+msgstr "відкриті лапки у рядку інформації про підключення\n"
+
+#: fe-connect.c:5084
+#, c-format
+msgid "definition of service \"%s\" not found\n"
+msgstr "не знайдено визначення сервера \"%s\"\n"
+
+#: fe-connect.c:5107
+#, c-format
+msgid "service file \"%s\" not found\n"
+msgstr "не знайдено сервісний файл \"%s\"\n"
+
+#: fe-connect.c:5122
+#, c-format
+msgid "line %d too long in service file \"%s\"\n"
+msgstr "рядок %d занадто довгий у сервісному файлі \"%s\"\n"
+
+#: fe-connect.c:5194 fe-connect.c:5238
+#, c-format
+msgid "syntax error in service file \"%s\", line %d\n"
+msgstr "синтаксична помилка у сервісному файлі \"%s\", рядок %d\n"
+
+#: fe-connect.c:5205
+#, c-format
+msgid "nested service specifications not supported in service file \"%s\", line %d\n"
+msgstr "вкладені сервісні специфікації не підтримуються у сервісному файлі \"%s\", рядок %d\n"
+
+#: fe-connect.c:5925
+#, c-format
+msgid "invalid URI propagated to internal parser routine: \"%s\"\n"
+msgstr "у внутрішню процедуру аналізу рядка передано помилковий URI: \"%s\"\n"
+
+#: fe-connect.c:6002
+#, c-format
+msgid "end of string reached when looking for matching \"]\" in IPv6 host address in URI: \"%s\"\n"
+msgstr "досягнуто кінця рядка під час пошуку відповідного \"]\" в адресі IPv6 URI: \"%s\"\n"
+
+#: fe-connect.c:6009
+#, c-format
+msgid "IPv6 host address may not be empty in URI: \"%s\"\n"
+msgstr "IPv6, що знаходиться в URI, не може бути пустим: \"%s\"\n"
+
+#: fe-connect.c:6024
+#, c-format
+msgid "unexpected character \"%c\" at position %d in URI (expected \":\" or \"/\"): \"%s\"\n"
+msgstr "неочікуваний символ \"%c\" на позиції %d в URI (очікувалося \":\" або \"/\"): \"%s\"\n"
+
+#: fe-connect.c:6153
+#, c-format
+msgid "extra key/value separator \"=\" in URI query parameter: \"%s\"\n"
+msgstr "зайвий розділювач ключа/значення \"=\" в параметрі запиту URI: \"%s\"\n"
+
+#: fe-connect.c:6173
+#, c-format
+msgid "missing key/value separator \"=\" in URI query parameter: \"%s\"\n"
+msgstr "відсутній розділювач ключа/значення \"=\" у параметрі запиту URI: \"%s\"\n"
+
+#: fe-connect.c:6224
+#, c-format
+msgid "invalid URI query parameter: \"%s\"\n"
+msgstr "неприпустимий параметр запиту URI: \"%s\"\n"
+
+#: fe-connect.c:6298
+#, c-format
+msgid "invalid percent-encoded token: \"%s\"\n"
+msgstr "неприпустимий токен, закодований відсотками: \"%s\"\n"
+
+#: fe-connect.c:6308
+#, c-format
+msgid "forbidden value %%00 in percent-encoded value: \"%s\"\n"
+msgstr "неприпустиме значення %%00 для значення, закодованого відсотками: \"%s\"\n"
+
+#: fe-connect.c:6671
+msgid "connection pointer is NULL\n"
+msgstr "нульове значення вказівника підключення \n"
+
+#: fe-connect.c:6967
+#, c-format
+msgid "WARNING: password file \"%s\" is not a plain file\n"
+msgstr "ПОПЕРЕДЖЕННЯ: файл паролів \"%s\" не є простим файлом\n"
+
+#: fe-connect.c:6976
+#, c-format
+msgid "WARNING: password file \"%s\" has group or world access; permissions should be u=rw (0600) or less\n"
+msgstr "ПОПЕРЕДЖЕННЯ: до файлу паролів \"%s\" мають доступ група або всі; дозволи мають бути u=rw (0600) або менше\n"
+
+#: fe-connect.c:7084
+#, c-format
+msgid "password retrieved from file \"%s\"\n"
+msgstr "пароль отримано з файлу \"%s\"\n"
+
+#: fe-exec.c:444 fe-exec.c:2821
+#, c-format
+msgid "row number %d is out of range 0..%d"
+msgstr "число рядків %d поза діапазоном 0..%d"
+
+#: fe-exec.c:505 fe-protocol2.c:497 fe-protocol2.c:532 fe-protocol2.c:1050
+#: fe-protocol3.c:206 fe-protocol3.c:233 fe-protocol3.c:250 fe-protocol3.c:330
+#: fe-protocol3.c:723 fe-protocol3.c:954
+msgid "out of memory"
+msgstr "недостатньо пам'яті"
+
+#: fe-exec.c:506 fe-protocol2.c:1396 fe-protocol3.c:1907
+#, c-format
+msgid "%s"
+msgstr "%s"
+
+#: fe-exec.c:815
+msgid "write to server failed\n"
+msgstr "записати на сервер не вдалося\n"
+
+#: fe-exec.c:896
+msgid "NOTICE"
+msgstr "ПОВІДОМЛЕННЯ"
+
+#: fe-exec.c:954
+msgid "PGresult cannot support more than INT_MAX tuples"
+msgstr "PGresult не може підтримувати більше ніж INT_MAX кортежів"
+
+#: fe-exec.c:966
+msgid "size_t overflow"
+msgstr "переповнення size_t"
+
+#: fe-exec.c:1243 fe-exec.c:1301 fe-exec.c:1347
+msgid "command string is a null pointer\n"
+msgstr "рядок команди є нульовим вказівником\n"
+
+#: fe-exec.c:1307 fe-exec.c:1353 fe-exec.c:1448
+msgid "number of parameters must be between 0 and 65535\n"
+msgstr "число параметрів має бути між 0 і 65535\n"
+
+#: fe-exec.c:1341 fe-exec.c:1442
+msgid "statement name is a null pointer\n"
+msgstr "ім’я оператора є пустим вказівником\n"
+
+#: fe-exec.c:1361 fe-exec.c:1524 fe-exec.c:2233 fe-exec.c:2435
+msgid "function requires at least protocol version 3.0\n"
+msgstr "функція вимагає протокол мінімум версії 3.0\n"
+
+#: fe-exec.c:1479
+msgid "no connection to the server\n"
+msgstr "немає підключення до сервера\n"
+
+#: fe-exec.c:1486
+msgid "another command is already in progress\n"
+msgstr "інша команда уже в прогресі\n"
+
+#: fe-exec.c:1600
+msgid "length must be given for binary parameter\n"
+msgstr "для бінарного параметра має бути надана довжина\n"
+
+#: fe-exec.c:1863
+#, c-format
+msgid "unexpected asyncStatus: %d\n"
+msgstr "неочікуваний asyncStatus: %d\n"
+
+#: fe-exec.c:1883
+#, c-format
+msgid "PGEventProc \"%s\" failed during PGEVT_RESULTCREATE event\n"
+msgstr "Помилка у PGEventProc \"%s\" під час події PGEVT_RESULTCREAT\n"
+
+#: fe-exec.c:2043
+msgid "COPY terminated by new PQexec"
+msgstr "COPY завершено новим PQexec"
+
+#: fe-exec.c:2051
+msgid "COPY IN state must be terminated first\n"
+msgstr "Спочатку стан COPY IN має завершитися\n"
+
+#: fe-exec.c:2071
+msgid "COPY OUT state must be terminated first\n"
+msgstr "Спочатку стан COPY OUT має завершитися\n"
+
+#: fe-exec.c:2079
+msgid "PQexec not allowed during COPY BOTH\n"
+msgstr "PQexec не дозволяється під час COPY BOTH\n"
+
+#: fe-exec.c:2325 fe-exec.c:2392 fe-exec.c:2482 fe-protocol2.c:1353
+#: fe-protocol3.c:1838
+msgid "no COPY in progress\n"
+msgstr "Немає COPY у процесі\n"
+
+#: fe-exec.c:2672
+msgid "connection in wrong state\n"
+msgstr "підключення у неправильному стані\n"
+
+#: fe-exec.c:2703
+msgid "invalid ExecStatusType code"
+msgstr "неприпустимий код ExecStatusType"
+
+#: fe-exec.c:2730
+msgid "PGresult is not an error result\n"
+msgstr "PGresult не є помилковим результатом\n"
+
+#: fe-exec.c:2805 fe-exec.c:2828
+#, c-format
+msgid "column number %d is out of range 0..%d"
+msgstr "число стовпців %d поза діапазоном 0..%d"
+
+#: fe-exec.c:2843
+#, c-format
+msgid "parameter number %d is out of range 0..%d"
+msgstr "число параметрів %d поза діапазоном 0..%d"
+
+#: fe-exec.c:3153
+#, c-format
+msgid "could not interpret result from server: %s"
+msgstr "не вдалося інтерпретувати результат від сервера: %s"
+
+#: fe-exec.c:3392 fe-exec.c:3476
+msgid "incomplete multibyte character\n"
+msgstr "неповний мультибайтний символ\n"
+
+#: fe-gssapi-common.c:124
+msgid "GSSAPI name import error"
+msgstr "Помилка імпорту імені у GSSAPI"
+
+#: fe-lobj.c:154
+msgid "cannot determine OID of function lo_truncate\n"
+msgstr "неможливо визначити ідентифікатор OID функції lo_truncate\n"
+
+#: fe-lobj.c:170
+msgid "argument of lo_truncate exceeds integer range\n"
+msgstr "аргумент lo_truncate перевищує діапазон цілого числа\n"
+
+#: fe-lobj.c:221
+msgid "cannot determine OID of function lo_truncate64\n"
+msgstr "неможливо визначити ідентифікатор OID функції lo_truncate64\n"
+
+#: fe-lobj.c:279
+msgid "argument of lo_read exceeds integer range\n"
+msgstr "аргумент lo_read перевищує діапазон цілого числа\n"
+
+#: fe-lobj.c:334
+msgid "argument of lo_write exceeds integer range\n"
+msgstr "аргумент lo_write перевищує діапазон цілого числа\n"
+
+#: fe-lobj.c:425
+msgid "cannot determine OID of function lo_lseek64\n"
+msgstr "неможливо визначити ідентифікатор OID функції lo_lseek64\n"
+
+#: fe-lobj.c:521
+msgid "cannot determine OID of function lo_create\n"
+msgstr "неможливо визначити ідентифікатор OID функції lo_create\n"
+
+#: fe-lobj.c:600
+msgid "cannot determine OID of function lo_tell64\n"
+msgstr "неможливо визначити ідентифікатор OID функції lo_tell64\n"
+
+#: fe-lobj.c:706 fe-lobj.c:815
+#, c-format
+msgid "could not open file \"%s\": %s\n"
+msgstr "не вдалося відкрити файл \"%s\": %s\n"
+
+#: fe-lobj.c:761
+#, c-format
+msgid "could not read from file \"%s\": %s\n"
+msgstr "не вдалося прочитати з файлу \"%s\": %s\n"
+
+#: fe-lobj.c:835 fe-lobj.c:859
+#, c-format
+msgid "could not write to file \"%s\": %s\n"
+msgstr "не вдалося записати у файл \"%s\": %s\n"
+
+#: fe-lobj.c:946
+msgid "query to initialize large object functions did not return data\n"
+msgstr "запит на ініціалізацію функцій для великих об’єктів не повернув дані\n"
+
+#: fe-lobj.c:995
+msgid "cannot determine OID of function lo_open\n"
+msgstr "неможливо визначити ідентифікатор OID функції lo_open\n"
+
+#: fe-lobj.c:1002
+msgid "cannot determine OID of function lo_close\n"
+msgstr "неможливо визначити ідентифікатор OID функції lo_close\n"
+
+#: fe-lobj.c:1009
+msgid "cannot determine OID of function lo_creat\n"
+msgstr "неможливо визначити ідентифікатор OID функції lo_creat\n"
+
+#: fe-lobj.c:1016
+msgid "cannot determine OID of function lo_unlink\n"
+msgstr "неможливо визначити ідентифікатор OID функції lo_unlink\n"
+
+#: fe-lobj.c:1023
+msgid "cannot determine OID of function lo_lseek\n"
+msgstr "неможливо визначити ідентифікатор OID функції lo_lseek\n"
+
+#: fe-lobj.c:1030
+msgid "cannot determine OID of function lo_tell\n"
+msgstr "неможливо визначити ідентифікатор OID функції lo_tell\n"
+
+#: fe-lobj.c:1037
+msgid "cannot determine OID of function loread\n"
+msgstr "неможливо визначити ідентифікатор OID функції loread\n"
+
+#: fe-lobj.c:1044
+msgid "cannot determine OID of function lowrite\n"
+msgstr "неможливо визначити ідентифікатор OID функції lowrite\n"
+
+#: fe-misc.c:289
+#, c-format
+msgid "integer of size %lu not supported by pqGetInt"
+msgstr "pqGetInt не підтримує ціле число розміром %lu"
+
+#: fe-misc.c:325
+#, c-format
+msgid "integer of size %lu not supported by pqPutInt"
+msgstr "pqPutInt не підтримує ціле число розміром %lu"
+
+#: fe-misc.c:636 fe-misc.c:869
+msgid "connection not open\n"
+msgstr "підключення не відкрито\n"
+
+#: fe-misc.c:805 fe-secure-openssl.c:209 fe-secure-openssl.c:316
+#: fe-secure.c:267 fe-secure.c:383
+msgid "server closed the connection unexpectedly\n"
+"\tThis probably means the server terminated abnormally\n"
+"\tbefore or while processing the request.\n"
+msgstr "сервер неочікувано закрив підключення\n"
+" Це може означати, що сервер завершив роботу ненормально до або під час обробки запиту.\n"
+
+#: fe-misc.c:1063
+msgid "timeout expired\n"
+msgstr "тайм-аут минув\n"
+
+#: fe-misc.c:1108
+msgid "invalid socket\n"
+msgstr "неприпустимий сокет\n"
+
+#: fe-misc.c:1131
+#, c-format
+msgid "select() failed: %s\n"
+msgstr "помилка в select(): %s\n"
+
+#: fe-protocol2.c:87
+#, c-format
+msgid "invalid setenv state %c, probably indicative of memory corruption\n"
+msgstr "неприпустимий стан setenv %c, можливо, пошкоджена пам'ять\n"
+
+#: fe-protocol2.c:384
+#, c-format
+msgid "invalid state %c, probably indicative of memory corruption\n"
+msgstr "неприпустимий стан %c, можливо, пошкоджена пам'ять\n"
+
+#: fe-protocol2.c:473 fe-protocol3.c:183
+#, c-format
+msgid "message type 0x%02x arrived from server while idle"
+msgstr "отримано тип повідомлення 0x%02x від сервера під час бездіяльності"
+
+#: fe-protocol2.c:523
+#, c-format
+msgid "unexpected character %c following empty query response (\"I\" message)"
+msgstr "неочікуваний символ %c слідом за пустою відповіддю на запит (\"I\" повідомлення)"
+
+#: fe-protocol2.c:589
+#, c-format
+msgid "server sent data (\"D\" message) without prior row description (\"T\" message)"
+msgstr "сервер передав дані (повідомлення \"D\") без попереднього опису рядка (повідомлення \"T\")"
+
+#: fe-protocol2.c:607
+#, c-format
+msgid "server sent binary data (\"B\" message) without prior row description (\"T\" message)"
+msgstr "сервер передав бінарні дані (повідомлення \"B\") без попереднього опису рядка (повідомлення \"T\")"
+
+#: fe-protocol2.c:626 fe-protocol3.c:408
+#, c-format
+msgid "unexpected response from server; first received character was \"%c\"\n"
+msgstr "неочікувана відповідь від сервера; перший отриманий символ був \"%c\"\n"
+
+#: fe-protocol2.c:755 fe-protocol2.c:930 fe-protocol3.c:622 fe-protocol3.c:849
+msgid "out of memory for query result"
+msgstr "недостатньо пам'яті для результату запиту"
+
+#: fe-protocol2.c:1408
+#, c-format
+msgid "lost synchronization with server, resetting connection"
+msgstr "втрачено синхронізацію з сервером, відновлення підключення"
+
+#: fe-protocol2.c:1530 fe-protocol2.c:1562 fe-protocol3.c:2095
+#, c-format
+msgid "protocol error: id=0x%x\n"
+msgstr "помилка протоколу: id=0x%x\n"
+
+#: fe-protocol3.c:365
+msgid "server sent data (\"D\" message) without prior row description (\"T\" message)\n"
+msgstr "сервер передав дані (повідомлення \"D\") без попереднього опису рядка (повідомлення \"T\")\n"
+
+#: fe-protocol3.c:429
+#, c-format
+msgid "message contents do not agree with length in message type \"%c\"\n"
+msgstr "вміст повідомлення не відповідає довжині у типі повідомлення \"%c\"\n"
+
+#: fe-protocol3.c:449
+#, c-format
+msgid "lost synchronization with server: got message type \"%c\", length %d\n"
+msgstr "втрачено синхронізацію з сервером: отримано тип повідомлення \"%c\", довжина %d\n"
+
+#: fe-protocol3.c:500 fe-protocol3.c:540
+msgid "insufficient data in \"T\" message"
+msgstr "недостатньо даних у повідомленні \"T\""
+
+#: fe-protocol3.c:573
+msgid "extraneous data in \"T\" message"
+msgstr "зайві дані у повідомленні \"T\""
+
+#: fe-protocol3.c:686
+msgid "extraneous data in \"t\" message"
+msgstr "зайві дані у повідомленні \"t\""
+
+#: fe-protocol3.c:757 fe-protocol3.c:789 fe-protocol3.c:807
+msgid "insufficient data in \"D\" message"
+msgstr "зайві дані у повідомленні \"D\""
+
+#: fe-protocol3.c:763
+msgid "unexpected field count in \"D\" message"
+msgstr "неочікувана кількість полів у повідомленні \"D\""
+
+#: fe-protocol3.c:816
+msgid "extraneous data in \"D\" message"
+msgstr "зайві дані у повідомленні \"D\""
+
+#: fe-protocol3.c:1008
+msgid "no error message available\n"
+msgstr "немає доступного повідомлення про помилку\n"
+
+#. translator: %s represents a digit string
+#: fe-protocol3.c:1056 fe-protocol3.c:1075
+#, c-format
+msgid " at character %s"
+msgstr " в символі %s"
+
+#: fe-protocol3.c:1088
+#, c-format
+msgid "DETAIL: %s\n"
+msgstr "ДЕТАЛІ: %s\n"
+
+#: fe-protocol3.c:1091
+#, c-format
+msgid "HINT: %s\n"
+msgstr "ПІДКАЗКА: %s\n"
+
+#: fe-protocol3.c:1094
+#, c-format
+msgid "QUERY: %s\n"
+msgstr "ЗАПИТ: %s\n"
+
+#: fe-protocol3.c:1101
+#, c-format
+msgid "CONTEXT: %s\n"
+msgstr "КОНТЕКСТ: %s\n"
+
+#: fe-protocol3.c:1110
+#, c-format
+msgid "SCHEMA NAME: %s\n"
+msgstr "ІМ'Я СХЕМИ: %s\n"
+
+#: fe-protocol3.c:1114
+#, c-format
+msgid "TABLE NAME: %s\n"
+msgstr "ІМ'Я ТАБЛИЦІ: %s\n"
+
+#: fe-protocol3.c:1118
+#, c-format
+msgid "COLUMN NAME: %s\n"
+msgstr "ІМ'Я СТОВПЦЯ: %s\n"
+
+#: fe-protocol3.c:1122
+#, c-format
+msgid "DATATYPE NAME: %s\n"
+msgstr "ІМ'Я ТИПУ ДАНИХ: %s\n"
+
+#: fe-protocol3.c:1126
+#, c-format
+msgid "CONSTRAINT NAME: %s\n"
+msgstr "ІМ'Я ОБМЕЖЕННЯ: %s\n"
+
+#: fe-protocol3.c:1138
+msgid "LOCATION: "
+msgstr "РОЗТАШУВАННЯ: "
+
+#: fe-protocol3.c:1140
+#, c-format
+msgid "%s, "
+msgstr "%s, "
+
+#: fe-protocol3.c:1142
+#, c-format
+msgid "%s:%s"
+msgstr "%s:%s"
+
+#: fe-protocol3.c:1337
+#, c-format
+msgid "LINE %d: "
+msgstr "РЯДОК %d: "
+
+#: fe-protocol3.c:1732
+msgid "PQgetline: not doing text COPY OUT\n"
+msgstr "PQgetline можна викликати лише під час COPY OUT\n"
+
+#: fe-secure-common.c:124
+msgid "SSL certificate's name contains embedded null\n"
+msgstr "Ім'я сертифікату SSL містить вбудоване Null-значення\n"
+
+#: fe-secure-common.c:171
+msgid "host name must be specified for a verified SSL connection\n"
+msgstr "має бути вказано ім'я хосту для перевіреного SSL підключення\n"
+
+#: fe-secure-common.c:196
+#, c-format
+msgid "server certificate for \"%s\" does not match host name \"%s\"\n"
+msgstr "серверний сертифікат \"%s\" не співпадає з іменем хосту \"%s\"\n"
+
+#: fe-secure-common.c:202
+msgid "could not get server's host name from server certificate\n"
+msgstr "не вдалося отримати ім'я хосту від серверного сертифікату\n"
+
+#: fe-secure-gssapi.c:201
+msgid "GSSAPI wrap error"
+msgstr "помилка при згортанні GSSAPI"
+
+#: fe-secure-gssapi.c:209
+msgid "outgoing GSSAPI message would not use confidentiality\n"
+msgstr "вихідне повідомлення GSSAPI не буде використовувати конфіденційність\n"
+
+#: fe-secure-gssapi.c:217
+#, c-format
+msgid "client tried to send oversize GSSAPI packet (%zu > %zu)\n"
+msgstr "клієнт намагався відправити переповнений пакет GSSAPI: (%zu > %zu)\n"
+
+#: fe-secure-gssapi.c:354 fe-secure-gssapi.c:596
+#, c-format
+msgid "oversize GSSAPI packet sent by the server (%zu > %zu)\n"
+msgstr "переповнений пакет GSSAPI відправлений сервером: (%zu > %zu)\n"
+
+#: fe-secure-gssapi.c:393
+msgid "GSSAPI unwrap error"
+msgstr "помилка при розгортанні GSSAPI"
+
+#: fe-secure-gssapi.c:403
+msgid "incoming GSSAPI message did not use confidentiality\n"
+msgstr "вхідне повідомлення GSSAPI не використовувує конфіденційність\n"
+
+#: fe-secure-gssapi.c:642
+msgid "could not initiate GSSAPI security context"
+msgstr "не вдалося ініціювати контекст безпеки GSSAPI"
+
+#: fe-secure-gssapi.c:673
+msgid "GSSAPI size check error"
+msgstr "помилка перевірки розміру GSSAPI"
+
+#: fe-secure-gssapi.c:684
+msgid "GSSAPI context establishment error"
+msgstr "помилка встановлення контексту GSSAPI"
+
+#: fe-secure-openssl.c:214 fe-secure-openssl.c:321 fe-secure-openssl.c:1291
+#, c-format
+msgid "SSL SYSCALL error: %s\n"
+msgstr "Помилка SSL SYSCALL: %s\n"
+
+#: fe-secure-openssl.c:221 fe-secure-openssl.c:328 fe-secure-openssl.c:1295
+msgid "SSL SYSCALL error: EOF detected\n"
+msgstr "Помилка SSL SYSCALL: виявлено EOF\n"
+
+#: fe-secure-openssl.c:232 fe-secure-openssl.c:339 fe-secure-openssl.c:1304
+#, c-format
+msgid "SSL error: %s\n"
+msgstr "Помилка протоколу SSL: %s\n"
+
+#: fe-secure-openssl.c:247 fe-secure-openssl.c:354
+msgid "SSL connection has been closed unexpectedly\n"
+msgstr "SSL підключення було неочікувано перервано\n"
+
+#: fe-secure-openssl.c:253 fe-secure-openssl.c:360 fe-secure-openssl.c:1354
+#, c-format
+msgid "unrecognized SSL error code: %d\n"
+msgstr "нерозпізнаний код помилки протоколу SSL: %d\n"
+
+#: fe-secure-openssl.c:400
+msgid "could not determine server certificate signature algorithm\n"
+msgstr "не вдалося визначити алгоритм підпису серверного сертифікату\n"
+
+#: fe-secure-openssl.c:421
+#, c-format
+msgid "could not find digest for NID %s\n"
+msgstr "не вдалося знайти дайджест для NID %s\n"
+
+#: fe-secure-openssl.c:431
+msgid "could not generate peer certificate hash\n"
+msgstr "не вдалося згенерувати хеш сертифікату вузла\n"
+
+#: fe-secure-openssl.c:488
+msgid "SSL certificate's name entry is missing\n"
+msgstr "Відсутня ім'я в сертифікаті SSL\n"
+
+#: fe-secure-openssl.c:815
+#, c-format
+msgid "could not create SSL context: %s\n"
+msgstr "не вдалося створити контекст SSL: %s\n"
+
+#: fe-secure-openssl.c:854
+#, c-format
+msgid "invalid value \"%s\" for minimum SSL protocol version\n"
+msgstr "неприпустиме значення \"%s\" для мінімальної версії протоколу SSL\n"
+
+#: fe-secure-openssl.c:865
+#, c-format
+msgid "could not set minimum SSL protocol version: %s\n"
+msgstr "не вдалося встановити мінімальну версію протоколу SSL: %s\n"
+
+#: fe-secure-openssl.c:883
+#, c-format
+msgid "invalid value \"%s\" for maximum SSL protocol version\n"
+msgstr "неприпустиме значення \"%s\" для максимальної версії протоколу SSL\n"
+
+#: fe-secure-openssl.c:894
+#, c-format
+msgid "could not set maximum SSL protocol version: %s\n"
+msgstr "не вдалося встановити максимальну версію протоколу SSL: %s\n"
+
+#: fe-secure-openssl.c:930
+#, c-format
+msgid "could not read root certificate file \"%s\": %s\n"
+msgstr "не вдалося прочитати файл кореневого сертифікату \"%s\": %s\n"
+
+#: fe-secure-openssl.c:974
+msgid "could not get home directory to locate root certificate file\n"
+"Either provide the file or change sslmode to disable server certificate verification.\n"
+msgstr "не вдалося отримати домашній каталог, щоб знайти файл кореневого сертифікату\n"
+"Надайте файл або змініть sslmode, щоб вимкнути перевірку серверного сертифікату.\n"
+
+#: fe-secure-openssl.c:978
+#, c-format
+msgid "root certificate file \"%s\" does not exist\n"
+"Either provide the file or change sslmode to disable server certificate verification.\n"
+msgstr "файлу кореневого сертифікату \"%s\" не існує\n"
+"Вкажіть повний шлях до файлу або вимкніть перевірку сертифікату сервера, змінивши sslmode.\n"
+
+#: fe-secure-openssl.c:1009
+#, c-format
+msgid "could not open certificate file \"%s\": %s\n"
+msgstr "не вдалося відкрити файл сертифікату \"%s\": %s\n"
+
+#: fe-secure-openssl.c:1028
+#, c-format
+msgid "could not read certificate file \"%s\": %s\n"
+msgstr "не вдалося прочитати файл сертифікату \"%s\": %s\n"
+
+#: fe-secure-openssl.c:1053
+#, c-format
+msgid "could not establish SSL connection: %s\n"
+msgstr "не вдалося встановити SSL-підключення: %s\n"
+
+#: fe-secure-openssl.c:1107
+#, c-format
+msgid "could not load SSL engine \"%s\": %s\n"
+msgstr "не вдалося завантажити модуль SSL \"%s\": %s\n"
+
+#: fe-secure-openssl.c:1119
+#, c-format
+msgid "could not initialize SSL engine \"%s\": %s\n"
+msgstr "не вдалося ініціалізувати модуль SSL \"%s\": %s\n"
+
+#: fe-secure-openssl.c:1135
+#, c-format
+msgid "could not read private SSL key \"%s\" from engine \"%s\": %s\n"
+msgstr "не вдалося прочитати закритий ключ SSL \"%s\" з модуля \"%s\": %s\n"
+
+#: fe-secure-openssl.c:1149
+#, c-format
+msgid "could not load private SSL key \"%s\" from engine \"%s\": %s\n"
+msgstr "не вдалося завантажити закритий ключ SSL \"%s\" з модуля \"%s\": %s\n"
+
+#: fe-secure-openssl.c:1186
+#, c-format
+msgid "certificate present, but not private key file \"%s\"\n"
+msgstr "сертифікат присутній, але файл закритого ключа \"%s\" ні\n"
+
+#: fe-secure-openssl.c:1194
+#, c-format
+msgid "private key file \"%s\" has group or world access; permissions should be u=rw (0600) or less\n"
+msgstr "файл закритого ключа \"%s\" має груповий або всесвітній доступ; права повинні бути u=rw (0600) або більш обмежені\n"
+
+#: fe-secure-openssl.c:1219
+#, c-format
+msgid "could not load private key file \"%s\": %s\n"
+msgstr "не вдалося завантажити файл закритого ключа \"%s\": %s\n"
+
+#: fe-secure-openssl.c:1237
+#, c-format
+msgid "certificate does not match private key file \"%s\": %s\n"
+msgstr "сертифікат не відповідає файлу закритого ключа \"%s\": %s\n"
+
+#: fe-secure-openssl.c:1337
+#, c-format
+msgid "This may indicate that the server does not support any SSL protocol version between %s and %s.\n"
+msgstr "Це може вказувати, що сервер не підтримує жодної версії протоколу SSL між %s і %s.\n"
+
+#: fe-secure-openssl.c:1373
+#, c-format
+msgid "certificate could not be obtained: %s\n"
+msgstr "не вдалося отримати сертифікат: %s\n"
+
+#: fe-secure-openssl.c:1462
+#, c-format
+msgid "no SSL error reported"
+msgstr "немає повідомлення про помилку SSL"
+
+#: fe-secure-openssl.c:1471
+#, c-format
+msgid "SSL error code %lu"
+msgstr "Код помилки SSL %lu"
+
+#: fe-secure-openssl.c:1718
+#, c-format
+msgid "WARNING: sslpassword truncated\n"
+msgstr "ПОПЕРЕДЖЕННЯ: sslpassword скорочено\n"
+
+#: fe-secure.c:275
+#, c-format
+msgid "could not receive data from server: %s\n"
+msgstr "не вдалося отримати дані з серверу: %s\n"
+
+#: fe-secure.c:390
+#, c-format
+msgid "could not send data to server: %s\n"
+msgstr "не вдалося передати дані серверу: %s\n"
+
+#: win32.c:314
+#, c-format
+msgid "unrecognized socket error: 0x%08X/%d"
+msgstr "нерозпізнана помилка сокету: 0x%08X/%d"
+
+#~ msgid "WARNING: line %d too long in password file \"%s\"\n"
+#~ msgstr "ПОПЕРЕДЖЕННЯ: рядок %d занадто довгий у файлі паролю \"%s\"\n"
+
diff --git a/src/interfaces/libpq/po/zh_CN.po b/src/interfaces/libpq/po/zh_CN.po
new file mode 100644
index 0000000..c128306
--- /dev/null
+++ b/src/interfaces/libpq/po/zh_CN.po
@@ -0,0 +1,1280 @@
+# simplified Chinese translation file for libpq
+# Bao Wei <weibao@forevertek.com>, 2002
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: libpq (PostgreSQL) 13\n"
+"Report-Msgid-Bugs-To: pgsql-bugs@lists.postgresql.org\n"
+"POT-Creation-Date: 2020-06-05 01:39+0000\n"
+"PO-Revision-Date: 2020-06-20 16:25+0800\n"
+"Last-Translator: Jie Zhang <zhangjie2@cn.fujitsu.com>\n"
+"Language-Team: Chinese (Simplified) <zhangjie2@cn.fujitsu.com>\n"
+"Language: zh_CN\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Poedit 1.5.7\n"
+
+#: fe-auth-scram.c:212
+msgid "malformed SCRAM message (empty message)\n"
+msgstr "错误的SCRAM消息(空消息)\n"
+
+#: fe-auth-scram.c:218
+msgid "malformed SCRAM message (length mismatch)\n"
+msgstr "错误的SCRAM消息(长度不匹配)\n"
+
+#: fe-auth-scram.c:265
+msgid "incorrect server signature\n"
+msgstr "服务器签名不正确\n"
+
+#: fe-auth-scram.c:274
+msgid "invalid SCRAM exchange state\n"
+msgstr "SCRAM交换状态无效\n"
+
+#: fe-auth-scram.c:296
+#, c-format
+msgid "malformed SCRAM message (attribute \"%c\" expected)\n"
+msgstr "错误的SCRAM消息(应为属性\"%c\")\n"
+
+#: fe-auth-scram.c:305
+#, c-format
+msgid "malformed SCRAM message (expected character \"=\" for attribute \"%c\")\n"
+msgstr "错误的SCRAM消息(属性\"%c\"需要字符\"=\")\n"
+
+#: fe-auth-scram.c:346
+msgid "could not generate nonce\n"
+msgstr "无法生成nonce\n"
+
+#: fe-auth-scram.c:356 fe-auth-scram.c:431 fe-auth-scram.c:579
+#: fe-auth-scram.c:600 fe-auth-scram.c:626 fe-auth-scram.c:641
+#: fe-auth-scram.c:691 fe-auth-scram.c:725 fe-auth.c:289 fe-auth.c:359
+#: fe-auth.c:394 fe-auth.c:611 fe-auth.c:770 fe-auth.c:1129 fe-auth.c:1277
+#: fe-connect.c:886 fe-connect.c:1413 fe-connect.c:1589 fe-connect.c:2199
+#: fe-connect.c:2222 fe-connect.c:2948 fe-connect.c:4617 fe-connect.c:4873
+#: fe-connect.c:4992 fe-connect.c:5245 fe-connect.c:5325 fe-connect.c:5424
+#: fe-connect.c:5680 fe-connect.c:5709 fe-connect.c:5781 fe-connect.c:5805
+#: fe-connect.c:5823 fe-connect.c:5924 fe-connect.c:5933 fe-connect.c:6289
+#: fe-connect.c:6439 fe-exec.c:2747 fe-exec.c:3494 fe-exec.c:3659
+#: fe-gssapi-common.c:111 fe-lobj.c:895 fe-protocol2.c:1207 fe-protocol3.c:995
+#: fe-protocol3.c:1699 fe-secure-common.c:110 fe-secure-gssapi.c:504
+#: fe-secure-openssl.c:440 fe-secure-openssl.c:1091
+msgid "out of memory\n"
+msgstr "内存用尽\n"
+
+#: fe-auth-scram.c:364
+msgid "could not encode nonce\n"
+msgstr "无法编码nonce\n"
+
+#: fe-auth-scram.c:563
+msgid "could not encode client proof\n"
+msgstr "无法对客户端证明进行编码\n"
+
+#: fe-auth-scram.c:618
+msgid "invalid SCRAM response (nonce mismatch)\n"
+msgstr "SCRAM响应无效(非匹配)\n"
+
+#: fe-auth-scram.c:651
+msgid "malformed SCRAM message (invalid salt)\n"
+msgstr "错误的SCRAM消息 (无效的salt)\n"
+
+#: fe-auth-scram.c:665
+msgid "malformed SCRAM message (invalid iteration count)\n"
+msgstr "错误的SCRAM消息(迭代计数无效)\n"
+
+#: fe-auth-scram.c:671
+msgid "malformed SCRAM message (garbage at end of server-first-message)\n"
+msgstr "错误的SCRAM消息 (服务器第一条消息结束时为垃圾消息)\n"
+
+#: fe-auth-scram.c:702
+#, c-format
+msgid "error received from server in SCRAM exchange: %s\n"
+msgstr "在SCRAM交换中从服务器接收到错误: %s\n"
+
+#: fe-auth-scram.c:718
+msgid "malformed SCRAM message (garbage at end of server-final-message)\n"
+msgstr "错误的SCRAM消息 (服务器最后一条消息结束时为垃圾消息)\n"
+
+#: fe-auth-scram.c:737
+msgid "malformed SCRAM message (invalid server signature)\n"
+msgstr "错误的SCRAM消息 (服务器签名无效)\n"
+
+#: fe-auth.c:76
+#, c-format
+msgid "out of memory allocating GSSAPI buffer (%d)\n"
+msgstr "在分配GSSAPI缓冲区(%d)时内存用尽\n"
+
+#: fe-auth.c:131
+msgid "GSSAPI continuation error"
+msgstr "GSSAPI连续出现错误"
+
+#: fe-auth.c:158 fe-auth.c:388 fe-gssapi-common.c:98 fe-secure-common.c:98
+msgid "host name must be specified\n"
+msgstr "必须指定主机名\n"
+
+#: fe-auth.c:165
+msgid "duplicate GSS authentication request\n"
+msgstr "重复的GSS认证请求\n"
+
+#: fe-auth.c:230
+#, c-format
+msgid "out of memory allocating SSPI buffer (%d)\n"
+msgstr "在分配SSPI缓冲区(%d)时内存用尽\n"
+
+#: fe-auth.c:278
+msgid "SSPI continuation error"
+msgstr "SSPI连续出现错误"
+
+#: fe-auth.c:349
+msgid "duplicate SSPI authentication request\n"
+msgstr "重复的SSPI认证请求\n"
+
+#: fe-auth.c:374
+msgid "could not acquire SSPI credentials"
+msgstr "无法获得SSPI证书"
+
+#: fe-auth.c:429
+msgid "channel binding required, but SSL not in use\n"
+msgstr "需要通道绑定,但未使用SSL\n"
+
+#: fe-auth.c:436
+msgid "duplicate SASL authentication request\n"
+msgstr "重复的SASL认证请求\n"
+
+#: fe-auth.c:492
+msgid "channel binding is required, but client does not support it\n"
+msgstr "通道绑定是必需的,但客户端不支持它\n"
+
+#: fe-auth.c:509
+msgid "server offered SCRAM-SHA-256-PLUS authentication over a non-SSL connection\n"
+msgstr "服务器通过非SSL连接提供了SCRAM-SHA-256-PLUS身份验证\n"
+
+#: fe-auth.c:521
+msgid "none of the server's SASL authentication mechanisms are supported\n"
+msgstr "不支持服务器的SASL身份验证机制\n"
+
+#: fe-auth.c:529
+msgid "channel binding is required, but server did not offer an authentication method that supports channel binding\n"
+msgstr "需要通道绑定,但服务器未提供支持通道绑定的身份验证方法\n"
+
+#: fe-auth.c:635
+#, c-format
+msgid "out of memory allocating SASL buffer (%d)\n"
+msgstr "在分配SASL缓冲区(%d)时内存用尽\n"
+
+#: fe-auth.c:660
+msgid "AuthenticationSASLFinal received from server, but SASL authentication was not completed\n"
+msgstr "从服务器接收到AuthenticationSASLFinal,但未完成SASL身份验证\n"
+
+#: fe-auth.c:737
+msgid "SCM_CRED authentication method not supported\n"
+msgstr "不支持 SCM_CRED 认证方式\n"
+
+#: fe-auth.c:836
+msgid "channel binding required, but server authenticated client without channel binding\n"
+msgstr "需要通道绑定,但服务器验证的客户端没有通道绑定\n"
+
+#: fe-auth.c:842
+msgid "channel binding required but not supported by server's authentication request\n"
+msgstr "服务器的身份验证请求需要但不支持通道绑定\n"
+
+#: fe-auth.c:875
+msgid "Kerberos 4 authentication not supported\n"
+msgstr "不支持 Kerberos 4 认证\n"
+
+#: fe-auth.c:880
+msgid "Kerberos 5 authentication not supported\n"
+msgstr "不支持 Kerberos 5 认证\n"
+
+#: fe-auth.c:951
+msgid "GSSAPI authentication not supported\n"
+msgstr "不支持GSSAPI认证\n"
+
+#: fe-auth.c:983
+msgid "SSPI authentication not supported\n"
+msgstr "不支持SSPI认证\n"
+
+#: fe-auth.c:991
+msgid "Crypt authentication not supported\n"
+msgstr "不支持Crypt认证\n"
+
+#: fe-auth.c:1057
+#, c-format
+msgid "authentication method %u not supported\n"
+msgstr "不支持 %u 认证方式\n"
+
+#: fe-auth.c:1104
+#, c-format
+msgid "user name lookup failure: error code %lu\n"
+msgstr "用户名查找失败:错误代码%lu\n"
+
+#: fe-auth.c:1114 fe-connect.c:2830
+#, c-format
+msgid "could not look up local user ID %d: %s\n"
+msgstr "无法查找本地用户ID %d: %s\n"
+
+#: fe-auth.c:1119 fe-connect.c:2835
+#, c-format
+msgid "local user with ID %d does not exist\n"
+msgstr "ID 为 %d 的本地用户不存在\n"
+
+#: fe-auth.c:1221
+msgid "unexpected shape of result set returned for SHOW\n"
+msgstr "SHOW出现意外的结果状态\n"
+
+#: fe-auth.c:1230
+msgid "password_encryption value too long\n"
+msgstr "密码_加密值太长\n"
+
+#: fe-auth.c:1270
+#, c-format
+msgid "unrecognized password encryption algorithm \"%s\"\n"
+msgstr "无法识别的密码加密算法 \"%s\"\n"
+
+#: fe-connect.c:1069
+#, c-format
+msgid "could not match %d host names to %d hostaddr values\n"
+msgstr "无法将主机名 %d 与主机地址 %d匹配\n"
+
+#: fe-connect.c:1150
+#, c-format
+msgid "could not match %d port numbers to %d hosts\n"
+msgstr "无法将端口号 %d与主机%d匹配\n"
+
+#: fe-connect.c:1243
+#, c-format
+msgid "invalid channel_binding value: \"%s\"\n"
+msgstr "通道绑定值无效: \"%s\"\n"
+
+#: fe-connect.c:1269
+#, c-format
+msgid "invalid sslmode value: \"%s\"\n"
+msgstr "无效的 sslmode 值: \"%s\"\n"
+
+#: fe-connect.c:1290
+#, c-format
+msgid "sslmode value \"%s\" invalid when SSL support is not compiled in\n"
+msgstr "无效的 sslmode 值 \"%s\" 当没有把 SSL 支持编译进来时\n"
+
+#: fe-connect.c:1311
+#, c-format
+msgid "invalid ssl_min_protocol_version value: \"%s\"\n"
+msgstr "无效的 ssl_min_protocol_version 值: \"%s\"\n"
+
+#: fe-connect.c:1319
+#, c-format
+msgid "invalid ssl_max_protocol_version value: \"%s\"\n"
+msgstr "无效的 ssl_max_protocol_version 值: \"%s\"\n"
+
+#: fe-connect.c:1336
+msgid "invalid SSL protocol version range\n"
+msgstr "无效的SSL协议版本范围\n"
+
+#: fe-connect.c:1351
+#, c-format
+msgid "invalid gssencmode value: \"%s\"\n"
+msgstr "无效的 gssencmode 值: \"%s\"\n"
+
+#: fe-connect.c:1360
+#, c-format
+msgid "gssencmode value \"%s\" invalid when GSSAPI support is not compiled in\n"
+msgstr "无效的 gssencmode 值 \"%s\" 当没有把 GSSAPI 支持编译进来时\n"
+
+#: fe-connect.c:1395
+#, c-format
+msgid "invalid target_session_attrs value: \"%s\"\n"
+msgstr "无效的 target_session_attrs 值: \"%s\"\n"
+
+#: fe-connect.c:1613
+#, c-format
+msgid "could not set socket to TCP no delay mode: %s\n"
+msgstr "无法将套接字设置为 TCP 无延迟模式: %s\n"
+
+#: fe-connect.c:1674
+#, c-format
+msgid ""
+"could not connect to server: %s\n"
+"\tIs the server running locally and accepting\n"
+"\tconnections on Unix domain socket \"%s\"?\n"
+msgstr ""
+"无法联接到服务器: %s\n"
+"\t服务器是否在本地运行并且在 Unix 域套接字\n"
+"\t\"%s\"上准备接受联接?\n"
+
+#: fe-connect.c:1711
+#, c-format
+msgid ""
+"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"
+msgstr ""
+"无法联接到服务器: %s\n"
+"\t服务器是否在主机 \"%s\"(%s) 上运行并且准备接受在端口\n"
+"%s 上的 TCP/IP 联接?\n"
+
+#: fe-connect.c:1719
+#, c-format
+msgid ""
+"could not connect to server: %s\n"
+"\tIs the server running on host \"%s\" and accepting\n"
+"\tTCP/IP connections on port %s?\n"
+msgstr ""
+"无法联接到服务器: %s\n"
+"\t服务器是否在主机 \"%s\" 上运行并且准备接受在端口\n"
+"%s 上的 TCP/IP 联接?\n"
+
+#: fe-connect.c:1789
+#, c-format
+msgid "invalid integer value \"%s\" for connection option \"%s\"\n"
+msgstr "连接选项\"%2$s\"的整数值\"%1$s\"无效\n"
+
+#: fe-connect.c:1819 fe-connect.c:1853 fe-connect.c:1888 fe-connect.c:1975
+#: fe-connect.c:2619
+#, c-format
+msgid "setsockopt(%s) failed: %s\n"
+msgstr "执行setsockopt(%s) 失败: %s\n"
+
+#: fe-connect.c:1941
+#, c-format
+msgid "WSAIoctl(SIO_KEEPALIVE_VALS) failed: %ui\n"
+msgstr "执行WSAIoctl(SIO_KEEPALIVE_VALS)失败:%u\n"
+
+#: fe-connect.c:2312
+msgid "invalid connection state, probably indicative of memory corruption\n"
+msgstr "无效的联接状态, 可能是存储器数据被破坏的标志\n"
+
+#: fe-connect.c:2378
+#, c-format
+msgid "invalid port number: \"%s\"\n"
+msgstr "无效端口号: \"%s\"\n"
+
+#: fe-connect.c:2394
+#, c-format
+msgid "could not translate host name \"%s\" to address: %s\n"
+msgstr "无法解释主机名 \"%s\" 到地址: %s\n"
+
+#: fe-connect.c:2407
+#, c-format
+msgid "could not parse network address \"%s\": %s\n"
+msgstr "无法分析网络地址\"%s\": %s\n"
+
+#: fe-connect.c:2420
+#, c-format
+msgid "Unix-domain socket path \"%s\" is too long (maximum %d bytes)\n"
+msgstr "Unix域的套接字路径\"%s\"超长(最大为%d字节)\n"
+
+#: fe-connect.c:2435
+#, c-format
+msgid "could not translate Unix-domain socket path \"%s\" to address: %s\n"
+msgstr "无法解释 Unix-domian 套接字路径 \"%s\" 到地址: %s\n"
+
+#: fe-connect.c:2556
+#, c-format
+msgid "could not create socket: %s\n"
+msgstr "无法创建套接字: %s\n"
+
+#: fe-connect.c:2578
+#, c-format
+msgid "could not set socket to nonblocking mode: %s\n"
+msgstr "无法设置套接字为非阻塞模式: %s\n"
+
+#: fe-connect.c:2588
+#, c-format
+msgid "could not set socket to close-on-exec mode: %s\n"
+msgstr "无法将套接字设置为执行时关闭 (close-on-exec) 模式: %s\n"
+
+#: fe-connect.c:2606
+msgid "keepalives parameter must be an integer\n"
+msgstr "参数keepalives必须是一个整数\n"
+
+#: fe-connect.c:2746
+#, c-format
+msgid "could not get socket error status: %s\n"
+msgstr "无法获取套接字错误状态: %s\n"
+
+#: fe-connect.c:2774
+#, c-format
+msgid "could not get client address from socket: %s\n"
+msgstr "无法从套接字获取客户端地址: %s\n"
+
+#: fe-connect.c:2816
+msgid "requirepeer parameter is not supported on this platform\n"
+msgstr "在此平台上不支持requirepeer参数\n"
+
+#: fe-connect.c:2819
+#, c-format
+msgid "could not get peer credentials: %s\n"
+msgstr "无法获得对等(peer)证书:%s\n"
+
+#: fe-connect.c:2843
+#, c-format
+msgid "requirepeer specifies \"%s\", but actual peer user name is \"%s\"\n"
+msgstr "期望对方用户指定值为 \"%s\", 但实际的对方用户名为 \"%s\"\n"
+
+#: fe-connect.c:2883
+#, c-format
+msgid "could not send GSSAPI negotiation packet: %s\n"
+msgstr "无法发送 GSSAPI 握手包: %s\n"
+
+#: fe-connect.c:2895
+msgid "GSSAPI encryption required but was impossible (possibly no credential cache, no server support, or using a local socket)\n"
+msgstr "需要GSSAPI加密,但不可能(可能没有凭据缓存、服务器不支持或使用本地套接字)\n"
+
+#: fe-connect.c:2922
+#, c-format
+msgid "could not send SSL negotiation packet: %s\n"
+msgstr "无法发送 SSL 握手包: %s\n"
+
+#: fe-connect.c:2961
+#, c-format
+msgid "could not send startup packet: %s\n"
+msgstr "无法发送启动包: %s\n"
+
+#: fe-connect.c:3031
+msgid "server does not support SSL, but SSL was required\n"
+msgstr "服务器不支持 SSL, 但是要求使用 SSL\n"
+
+#: fe-connect.c:3057
+#, c-format
+msgid "received invalid response to SSL negotiation: %c\n"
+msgstr "收到对 SSL 握手的无效响应: %c\n"
+
+#: fe-connect.c:3147
+msgid "server doesn't support GSSAPI encryption, but it was required\n"
+msgstr "服务器不支持 GSSAPI, 但这是必须的\n"
+
+#: fe-connect.c:3158
+#, c-format
+msgid "received invalid response to GSSAPI negotiation: %c\n"
+msgstr "收到对 GSSAPI 握手的无效响应: %c\n"
+
+#: fe-connect.c:3225 fe-connect.c:3256
+#, c-format
+msgid "expected authentication request from server, but received %c\n"
+msgstr "期待来自服务器的认证请求, 却收到 %c\n"
+
+#: fe-connect.c:3502
+msgid "unexpected message from server during startup\n"
+msgstr "启动过程中收到来自服务器的非预期信息\n"
+
+#: fe-connect.c:3707
+#, c-format
+msgid "could not make a writable connection to server \"%s:%s\"\n"
+msgstr "无法与服务器建立可写连接\"%s:%s\"\n"
+
+#: fe-connect.c:3753
+#, c-format
+msgid "test \"SHOW transaction_read_only\" failed on server \"%s:%s\"\n"
+msgstr "在服务器\"%s:%s\"上测试\"SHOW transaction_read_only\"失败\n"
+
+#: fe-connect.c:3768
+#, c-format
+msgid "invalid connection state %d, probably indicative of memory corruption\n"
+msgstr "无效的连接状态 %d, 这可能表示内存出现问题\n"
+
+#: fe-connect.c:4223 fe-connect.c:4283
+#, c-format
+msgid "PGEventProc \"%s\" failed during PGEVT_CONNRESET event\n"
+msgstr "在PGEVT_CONNRESET事件触发期间执行PGEventProc \"%s\"错误\n"
+
+#: fe-connect.c:4630
+#, c-format
+msgid "invalid LDAP URL \"%s\": scheme must be ldap://\n"
+msgstr "无效LDAP URL\"%s\": 模式必须是ldap://\n"
+
+#: fe-connect.c:4645
+#, c-format
+msgid "invalid LDAP URL \"%s\": missing distinguished name\n"
+msgstr "无效LDAP URL \"%s\": 丢失可区分的名称\n"
+
+#: fe-connect.c:4657 fe-connect.c:4712
+#, c-format
+msgid "invalid LDAP URL \"%s\": must have exactly one attribute\n"
+msgstr "无效LDAP URL \"%s\": 只能有一个属性\n"
+
+#: fe-connect.c:4668 fe-connect.c:4727
+#, c-format
+msgid "invalid LDAP URL \"%s\": must have search scope (base/one/sub)\n"
+msgstr "无效LDAP URL \"%s\": 必须有搜索范围(base/one/sub)\n"
+
+#: fe-connect.c:4679
+#, c-format
+msgid "invalid LDAP URL \"%s\": no filter\n"
+msgstr "无效的 LDAP URL \"%s\": 没有过滤器\n"
+
+#: fe-connect.c:4700
+#, c-format
+msgid "invalid LDAP URL \"%s\": invalid port number\n"
+msgstr "无效LDAP URL \"%s\": 无效端口号\n"
+
+#: fe-connect.c:4736
+msgid "could not create LDAP structure\n"
+msgstr "无法创建LDAP结构\n"
+
+#: fe-connect.c:4812
+#, c-format
+msgid "lookup on LDAP server failed: %s\n"
+msgstr "在LDAP服务器上的查找失败: %s\n"
+
+#: fe-connect.c:4823
+msgid "more than one entry found on LDAP lookup\n"
+msgstr "在LDAP搜索上找到多个入口\n"
+
+#: fe-connect.c:4824 fe-connect.c:4836
+msgid "no entry found on LDAP lookup\n"
+msgstr "在LDAP查找上没有发现入口\n"
+
+#: fe-connect.c:4847 fe-connect.c:4860
+msgid "attribute has no values on LDAP lookup\n"
+msgstr "在LDAP查找上的属性没有值\n"
+
+#: fe-connect.c:4912 fe-connect.c:4931 fe-connect.c:5463
+#, c-format
+msgid "missing \"=\" after \"%s\" in connection info string\n"
+msgstr "在联接信息字串里的 \"%s\" 后面缺少 \"=\"\n"
+
+#: fe-connect.c:5004 fe-connect.c:5648 fe-connect.c:6422
+#, c-format
+msgid "invalid connection option \"%s\"\n"
+msgstr "非法联接选项 \"%s\"\n"
+
+#: fe-connect.c:5020 fe-connect.c:5512
+msgid "unterminated quoted string in connection info string\n"
+msgstr "联接信息字串中未结束的引号字串\n"
+
+#: fe-connect.c:5103
+#, c-format
+msgid "definition of service \"%s\" not found\n"
+msgstr "错误:没有找到服务\"%s\"的定义\n"
+
+#: fe-connect.c:5126
+#, c-format
+msgid "service file \"%s\" not found\n"
+msgstr "错误:没有找到服务文件\"%s\"\n"
+
+#: fe-connect.c:5141
+#, c-format
+msgid "line %d too long in service file \"%s\"\n"
+msgstr "在服务文件\"%2$s\"中的第%1$d行的长度太长\n"
+
+#: fe-connect.c:5213 fe-connect.c:5257
+#, c-format
+msgid "syntax error in service file \"%s\", line %d\n"
+msgstr "在服务文件\"%s\"的第%d行出现语法错误\n"
+
+#: fe-connect.c:5224
+#, c-format
+msgid "nested service specifications not supported in service file \"%s\", line %d\n"
+msgstr "在服务文件\"%s\"的第%d行出现不支持的嵌套服务说明\n"
+
+#: fe-connect.c:5944
+#, c-format
+msgid "invalid URI propagated to internal parser routine: \"%s\"\n"
+msgstr "无效的URI传入内部解析器处理程序: \"%s\"\n"
+
+#: fe-connect.c:6021
+#, c-format
+msgid "end of string reached when looking for matching \"]\" in IPv6 host address in URI: \"%s\"\n"
+msgstr "在 URI: \"%s\"中的IPv6主机地址里查找匹配符\"]\"时遇到了字符串结束符\n"
+
+#: fe-connect.c:6028
+#, c-format
+msgid "IPv6 host address may not be empty in URI: \"%s\"\n"
+msgstr "URI:\"%s\"中的IPv6主机地址可能不为空\n"
+
+#: fe-connect.c:6043
+#, c-format
+msgid "unexpected character \"%c\" at position %d in URI (expected \":\" or \"/\"): \"%s\"\n"
+msgstr "非预期的字符\"%c\"出现在在位置%d, URI (expected \":\" or \"/\"):\"%s\"\n"
+
+#: fe-connect.c:6172
+#, c-format
+msgid "extra key/value separator \"=\" in URI query parameter: \"%s\"\n"
+msgstr "遇到多余的键/值分隔符\"=\"在URI查询参数里: \"%s\"\n"
+
+#: fe-connect.c:6192
+#, c-format
+msgid "missing key/value separator \"=\" in URI query parameter: \"%s\"\n"
+msgstr "缺少相应的键/值分隔符\"=\"在URI查询参数里: \"%s\"\n"
+
+#: fe-connect.c:6243
+#, c-format
+msgid "invalid URI query parameter: \"%s\"\n"
+msgstr "无效的URI查询参数: \"%s\"\n"
+
+#: fe-connect.c:6317
+#, c-format
+msgid "invalid percent-encoded token: \"%s\"\n"
+msgstr "无效的百分号编码令牌: \"%s\"\n"
+
+#: fe-connect.c:6327
+#, c-format
+msgid "forbidden value %%00 in percent-encoded value: \"%s\"\n"
+msgstr "在百分值编码的值: \"%s\"里禁止使用 %%00\n"
+
+#: fe-connect.c:6690
+msgid "connection pointer is NULL\n"
+msgstr "联接指针是 NULL\n"
+
+#: fe-connect.c:6989
+#, c-format
+msgid "WARNING: password file \"%s\" is not a plain file\n"
+msgstr "警告: 口令文件\"%s\"不是普通文本文件\n"
+
+#: fe-connect.c:6998
+#, c-format
+msgid "WARNING: password file \"%s\" has group or world access; permissions should be u=rw (0600) or less\n"
+msgstr "警告: 口令文件\"%s\"的访问权限过大; 权限应设置 为 u=rw (0600)或更少\n"
+
+#: fe-connect.c:7039
+#, c-format
+msgid "WARNING: line %d too long in password file \"%s\"\n"
+msgstr "警告:在密码文件\"%2$s\"中的第%1$d行的长度太长\n"
+
+#: fe-connect.c:7118
+#, c-format
+msgid "password retrieved from file \"%s\"\n"
+msgstr "从文件\"%s\"中获取口令\n"
+
+#: fe-exec.c:444 fe-exec.c:2821
+#, c-format
+msgid "row number %d is out of range 0..%d"
+msgstr "行号码 %d 超出了范围 0..%d"
+
+#: fe-exec.c:505 fe-protocol2.c:497 fe-protocol2.c:532 fe-protocol2.c:1050
+#: fe-protocol3.c:206 fe-protocol3.c:233 fe-protocol3.c:250 fe-protocol3.c:330
+#: fe-protocol3.c:723 fe-protocol3.c:954
+msgid "out of memory"
+msgstr "内存用尽"
+
+#: fe-exec.c:506 fe-protocol2.c:1396 fe-protocol3.c:1907
+#, c-format
+msgid "%s"
+msgstr "%s"
+
+#: fe-exec.c:815
+msgid "write to server failed\n"
+msgstr "写入服务器失败\n"
+
+#: fe-exec.c:896
+msgid "NOTICE"
+msgstr "注意"
+
+#: fe-exec.c:954
+msgid "PGresult cannot support more than INT_MAX tuples"
+msgstr "PGresult不能支持超过INT_MAX元组"
+
+#: fe-exec.c:966
+msgid "size_t overflow"
+msgstr "size_t溢出"
+
+#: fe-exec.c:1243 fe-exec.c:1301 fe-exec.c:1347
+msgid "command string is a null pointer\n"
+msgstr "命令字串是一个空指针\n"
+
+#: fe-exec.c:1307 fe-exec.c:1353 fe-exec.c:1448
+msgid "number of parameters must be between 0 and 65535\n"
+msgstr "参数的个数必须介于0到65535之间\n"
+
+#: fe-exec.c:1341 fe-exec.c:1442
+msgid "statement name is a null pointer\n"
+msgstr "声明名字是一个空指针\n"
+
+#: fe-exec.c:1361 fe-exec.c:1524 fe-exec.c:2233 fe-exec.c:2435
+msgid "function requires at least protocol version 3.0\n"
+msgstr "函数至少需要 3.0 版本的协议\n"
+
+#: fe-exec.c:1479
+msgid "no connection to the server\n"
+msgstr "没有到服务器的联接\n"
+
+#: fe-exec.c:1486
+msgid "another command is already in progress\n"
+msgstr "已经有另外一条命令在处理\n"
+
+#: fe-exec.c:1600
+msgid "length must be given for binary parameter\n"
+msgstr "对于2进制参数必须指定长度\n"
+
+#: fe-exec.c:1863
+#, c-format
+msgid "unexpected asyncStatus: %d\n"
+msgstr "意外的 asyncStatus(异步状态): %d\n"
+
+#: fe-exec.c:1883
+#, c-format
+msgid "PGEventProc \"%s\" failed during PGEVT_RESULTCREATE event\n"
+msgstr "在PGEVT_CONNRESET事件触发期间执行PGEventProc \"%s\"错误\n"
+
+#: fe-exec.c:2043
+msgid "COPY terminated by new PQexec"
+msgstr "COPY 被一个新的 PQexec 终止"
+
+#: fe-exec.c:2051
+msgid "COPY IN state must be terminated first\n"
+msgstr "COPY IN 状态必须先结束\n"
+
+#: fe-exec.c:2071
+msgid "COPY OUT state must be terminated first\n"
+msgstr "COPY OUT 状态必须先结束\n"
+
+#: fe-exec.c:2079
+msgid "PQexec not allowed during COPY BOTH\n"
+msgstr "在 COPY BOTH时不允许调用PQexec\n"
+
+#: fe-exec.c:2325 fe-exec.c:2392 fe-exec.c:2482 fe-protocol2.c:1353
+#: fe-protocol3.c:1838
+msgid "no COPY in progress\n"
+msgstr "没有正在处理的 COPY\n"
+
+#: fe-exec.c:2672
+msgid "connection in wrong state\n"
+msgstr "联接处于错误状态\n"
+
+#: fe-exec.c:2703
+msgid "invalid ExecStatusType code"
+msgstr "非法 ExecStatusType 代码"
+
+#: fe-exec.c:2730
+msgid "PGresult is not an error result\n"
+msgstr "PGresult不是错误的结果\n"
+
+#: fe-exec.c:2805 fe-exec.c:2828
+#, c-format
+msgid "column number %d is out of range 0..%d"
+msgstr "列号码 %d 超出了范围 0..%d"
+
+#: fe-exec.c:2843
+#, c-format
+msgid "parameter number %d is out of range 0..%d"
+msgstr "参数号%d超出了范围 0..%d"
+
+#: fe-exec.c:3153
+#, c-format
+msgid "could not interpret result from server: %s"
+msgstr "无法解释来自服务器的结果: %s"
+
+#: fe-exec.c:3392 fe-exec.c:3476
+msgid "incomplete multibyte character\n"
+msgstr "无效的多字节字符\n"
+
+#: fe-gssapi-common.c:124
+msgid "GSSAPI name import error"
+msgstr "GSSAPI名称导入错误"
+
+#: fe-lobj.c:154
+msgid "cannot determine OID of function lo_truncate\n"
+msgstr "无法确定函数 lo_creat 的 OID\n"
+
+#: fe-lobj.c:170
+msgid "argument of lo_truncate exceeds integer range\n"
+msgstr "lo_truncate的参数超出整数范围\n"
+
+#: fe-lobj.c:221
+msgid "cannot determine OID of function lo_truncate64\n"
+msgstr "无法确定函数lo_truncate64的OID值\n"
+
+#: fe-lobj.c:279
+msgid "argument of lo_read exceeds integer range\n"
+msgstr "lo_read的参数值已超出整数范围\n"
+
+#: fe-lobj.c:334
+msgid "argument of lo_write exceeds integer range\n"
+msgstr "lo_write的参数值已超出整数范围\n"
+
+#: fe-lobj.c:425
+msgid "cannot determine OID of function lo_lseek64\n"
+msgstr "无法确定函数lo_lseek64的OID值\n"
+
+#: fe-lobj.c:521
+msgid "cannot determine OID of function lo_create\n"
+msgstr "无法确定函数 lo_creat 的 OID\n"
+
+#: fe-lobj.c:600
+msgid "cannot determine OID of function lo_tell64\n"
+msgstr "无法确定函数lo_tell64的OID值\n"
+
+#: fe-lobj.c:706 fe-lobj.c:815
+#, c-format
+msgid "could not open file \"%s\": %s\n"
+msgstr "无法打开文件 \"%s\": %s\n"
+
+#: fe-lobj.c:761
+#, c-format
+msgid "could not read from file \"%s\": %s\n"
+msgstr "无法读取文件 \"%s\": %s\n"
+
+#: fe-lobj.c:835 fe-lobj.c:859
+#, c-format
+msgid "could not write to file \"%s\": %s\n"
+msgstr "无法写入文件 \"%s\": %s\n"
+
+#: fe-lobj.c:946
+msgid "query to initialize large object functions did not return data\n"
+msgstr "初始化大对象函数的查询没有返回数据\n"
+
+#: fe-lobj.c:995
+msgid "cannot determine OID of function lo_open\n"
+msgstr "无法判断函数 lo_open 的 OID\n"
+
+#: fe-lobj.c:1002
+msgid "cannot determine OID of function lo_close\n"
+msgstr "无法判断函数 lo_close 的 OID\n"
+
+#: fe-lobj.c:1009
+msgid "cannot determine OID of function lo_creat\n"
+msgstr "无法判断函数 lo_creat 的 OID\n"
+
+#: fe-lobj.c:1016
+msgid "cannot determine OID of function lo_unlink\n"
+msgstr "无法判断函数 lo_unlink 的 OID\n"
+
+#: fe-lobj.c:1023
+msgid "cannot determine OID of function lo_lseek\n"
+msgstr "无法判断函数 lo_lseek 的 OID\n"
+
+#: fe-lobj.c:1030
+msgid "cannot determine OID of function lo_tell\n"
+msgstr "无法判断函数 lo_tell 的 OID\n"
+
+#: fe-lobj.c:1037
+msgid "cannot determine OID of function loread\n"
+msgstr "无法判断函数 loread 的 OID\n"
+
+#: fe-lobj.c:1044
+msgid "cannot determine OID of function lowrite\n"
+msgstr "无法判断函数 lowrite 的 OID\n"
+
+#: fe-misc.c:289
+#, c-format
+msgid "integer of size %lu not supported by pqGetInt"
+msgstr "pqGetInt 不支持大小为 %lu 的整数"
+
+#: fe-misc.c:325
+#, c-format
+msgid "integer of size %lu not supported by pqPutInt"
+msgstr "pqPutInt 不支持大小为 %lu 的整数"
+
+#: fe-misc.c:636 fe-misc.c:869
+msgid "connection not open\n"
+msgstr "联接未打开\n"
+
+#: fe-misc.c:805 fe-secure-openssl.c:209 fe-secure-openssl.c:316
+#: fe-secure.c:267 fe-secure.c:383
+msgid ""
+"server closed the connection unexpectedly\n"
+"\tThis probably means the server terminated abnormally\n"
+"\tbefore or while processing the request.\n"
+msgstr ""
+"服务器意外地关闭了联接\n"
+"\t这种现象通常意味着服务器在处理请求之前\n"
+"或者正在处理请求的时候意外中止\n"
+
+#: fe-misc.c:1063
+msgid "timeout expired\n"
+msgstr "超时满\n"
+
+#: fe-misc.c:1108
+msgid "invalid socket\n"
+msgstr "无效套接字\n"
+
+#: fe-misc.c:1131
+#, c-format
+msgid "select() failed: %s\n"
+msgstr "select() 失败: %s\n"
+
+#: fe-protocol2.c:87
+#, c-format
+msgid "invalid setenv state %c, probably indicative of memory corruption\n"
+msgstr "无效的 setenv 状态 %c, 可能是内存被破坏\n"
+
+#: fe-protocol2.c:384
+#, c-format
+msgid "invalid state %c, probably indicative of memory corruption\n"
+msgstr "无效状态 %c, 可能是内存被破坏\n"
+
+#: fe-protocol2.c:473 fe-protocol3.c:183
+#, c-format
+msgid "message type 0x%02x arrived from server while idle"
+msgstr "当空闲时收到服务起发送过来的消息类型 0x%02x"
+
+#: fe-protocol2.c:523
+#, c-format
+msgid "unexpected character %c following empty query response (\"I\" message)"
+msgstr "unexpected character %c following empty query response (\"I\" message)"
+
+#: fe-protocol2.c:589
+#, c-format
+msgid "server sent data (\"D\" message) without prior row description (\"T\" message)"
+msgstr "server sent data (\"D\" message) without prior row description (\"T\" message)"
+
+#: fe-protocol2.c:607
+#, c-format
+msgid "server sent binary data (\"B\" message) without prior row description (\"T\" message)"
+msgstr "server sent binary data (\"B\" message) without prior row description (\"T\" message)"
+
+#: fe-protocol2.c:626 fe-protocol3.c:408
+#, c-format
+msgid "unexpected response from server; first received character was \"%c\"\n"
+msgstr "来自服务器意外的回执, 第一个收到的字符是 \"%c\"\n"
+
+#: fe-protocol2.c:755 fe-protocol2.c:930 fe-protocol3.c:622 fe-protocol3.c:849
+msgid "out of memory for query result"
+msgstr "查询结果时内存耗尽"
+
+#: fe-protocol2.c:1408
+#, c-format
+msgid "lost synchronization with server, resetting connection"
+msgstr "失去与服务器同步, 重置连接"
+
+#: fe-protocol2.c:1530 fe-protocol2.c:1562 fe-protocol3.c:2095
+#, c-format
+msgid "protocol error: id=0x%x\n"
+msgstr "协议错误: id=0x%x\n"
+
+#: fe-protocol3.c:365
+msgid "server sent data (\"D\" message) without prior row description (\"T\" message)\n"
+msgstr "server sent data (\"D\" message) without prior row description (\"T\" message)\n"
+
+#: fe-protocol3.c:429
+#, c-format
+msgid "message contents do not agree with length in message type \"%c\"\n"
+msgstr "在消息类型 \"%c\" 中, 消息内容与长度不匹配\n"
+
+#: fe-protocol3.c:449
+#, c-format
+msgid "lost synchronization with server: got message type \"%c\", length %d\n"
+msgstr "失去与服务器同步: 获取到消息类型 \"%c\", 长度 %d\n"
+
+#: fe-protocol3.c:500 fe-protocol3.c:540
+msgid "insufficient data in \"T\" message"
+msgstr "\"T\"消息中剩下的数据不够"
+
+#: fe-protocol3.c:573
+msgid "extraneous data in \"T\" message"
+msgstr "\"T\"消息中有无关的数据"
+
+#: fe-protocol3.c:686
+msgid "extraneous data in \"t\" message"
+msgstr "\"t\"消息中有无关的数据"
+
+#: fe-protocol3.c:757 fe-protocol3.c:789 fe-protocol3.c:807
+msgid "insufficient data in \"D\" message"
+msgstr "\"D\"消息中剩下的数据不够"
+
+#: fe-protocol3.c:763
+msgid "unexpected field count in \"D\" message"
+msgstr "在 \"D\" 消息中, 意外的字段个数"
+
+#: fe-protocol3.c:816
+msgid "extraneous data in \"D\" message"
+msgstr "\"D\"消息中已经没有数据了"
+
+#: fe-protocol3.c:1008
+msgid "no error message available\n"
+msgstr "没有可用的错误消息\n"
+
+#. translator: %s represents a digit string
+#: fe-protocol3.c:1056 fe-protocol3.c:1075
+#, c-format
+msgid " at character %s"
+msgstr " 在字符 %s"
+
+#: fe-protocol3.c:1088
+#, c-format
+msgid "DETAIL: %s\n"
+msgstr "描述: %s\n"
+
+#: fe-protocol3.c:1091
+#, c-format
+msgid "HINT: %s\n"
+msgstr "提示: %s\n"
+
+#: fe-protocol3.c:1094
+#, c-format
+msgid "QUERY: %s\n"
+msgstr "查询: %s\n"
+
+#: fe-protocol3.c:1101
+#, c-format
+msgid "CONTEXT: %s\n"
+msgstr "背景: %s\n"
+
+#: fe-protocol3.c:1110
+#, c-format
+msgid "SCHEMA NAME: %s\n"
+msgstr "方案名: %s\n"
+
+#: fe-protocol3.c:1114
+#, c-format
+msgid "TABLE NAME: %s\n"
+msgstr "表名: %s\n"
+
+#: fe-protocol3.c:1118
+#, c-format
+msgid "COLUMN NAME: %s\n"
+msgstr "列名: %s\n"
+
+#: fe-protocol3.c:1122
+#, c-format
+msgid "DATATYPE NAME: %s\n"
+msgstr "数据类型名: %s\n"
+
+#: fe-protocol3.c:1126
+#, c-format
+msgid "CONSTRAINT NAME: %s\n"
+msgstr "约束名: %s\n"
+
+#: fe-protocol3.c:1138
+msgid "LOCATION: "
+msgstr "位置: "
+
+#: fe-protocol3.c:1140
+#, c-format
+msgid "%s, "
+msgstr "%s, "
+
+#: fe-protocol3.c:1142
+#, c-format
+msgid "%s:%s"
+msgstr "%s:%s"
+
+#: fe-protocol3.c:1337
+#, c-format
+msgid "LINE %d: "
+msgstr "第%d行"
+
+#: fe-protocol3.c:1732
+msgid "PQgetline: not doing text COPY OUT\n"
+msgstr "PQgetline: not doing text COPY OUT\n"
+
+#: fe-secure-common.c:124
+msgid "SSL certificate's name contains embedded null\n"
+msgstr "SSL证书的名称包含嵌入的空值\n"
+
+#: fe-secure-common.c:171
+msgid "host name must be specified for a verified SSL connection\n"
+msgstr "必须为一个已验证的SSL连接指定主机名\n"
+
+#: fe-secure-common.c:196
+#, c-format
+msgid "server certificate for \"%s\" does not match host name \"%s\"\n"
+msgstr "\"%s\"的服务器证书与主机名不匹配\"%s\"\n"
+
+#: fe-secure-common.c:202
+msgid "could not get server's host name from server certificate\n"
+msgstr "无法从服务器证书得到服务器的主机名\n"
+
+#: fe-secure-gssapi.c:201
+msgid "GSSAPI wrap error"
+msgstr "GSSAPI包装错误"
+
+#: fe-secure-gssapi.c:209
+msgid "outgoing GSSAPI message would not use confidentiality\n"
+msgstr "传出的GSSAPI消息将不使用机密性\n"
+
+#: fe-secure-gssapi.c:217
+#, c-format
+msgid "client tried to send oversize GSSAPI packet (%zu > %zu)\n"
+msgstr "客户端试图发送过大的GSSAPI数据包 (%zu > %zu)\n"
+
+#: fe-secure-gssapi.c:354 fe-secure-gssapi.c:596
+#, c-format
+msgid "oversize GSSAPI packet sent by the server (%zu > %zu)\n"
+msgstr "服务器端发送的超大GSSAPI数据包(%zu > %zu)\n"
+
+#: fe-secure-gssapi.c:393
+msgid "GSSAPI unwrap error"
+msgstr "GSSAPI展开错误"
+
+#: fe-secure-gssapi.c:403
+msgid "incoming GSSAPI message did not use confidentiality\n"
+msgstr "传入的GSSAPI消息未使用机密性\n"
+
+#: fe-secure-gssapi.c:642
+msgid "could not initiate GSSAPI security context"
+msgstr "无法初始化GSSAPI安全上下文"
+
+#: fe-secure-gssapi.c:673
+msgid "GSSAPI size check error"
+msgstr "GSSAPI大小检查错误"
+
+#: fe-secure-gssapi.c:684
+msgid "GSSAPI context establishment error"
+msgstr "GSSAPI上下文创建错误"
+
+#: fe-secure-openssl.c:214 fe-secure-openssl.c:321 fe-secure-openssl.c:1291
+#, c-format
+msgid "SSL SYSCALL error: %s\n"
+msgstr "SSL SYSCALL 错误: %s\n"
+
+#: fe-secure-openssl.c:221 fe-secure-openssl.c:328 fe-secure-openssl.c:1295
+msgid "SSL SYSCALL error: EOF detected\n"
+msgstr "SSL SYSCALL 错误: 发现结束符\n"
+
+#: fe-secure-openssl.c:232 fe-secure-openssl.c:339 fe-secure-openssl.c:1304
+#, c-format
+msgid "SSL error: %s\n"
+msgstr "SSL 错误: %s\n"
+
+#: fe-secure-openssl.c:247 fe-secure-openssl.c:354
+msgid "SSL connection has been closed unexpectedly\n"
+msgstr "SSL连接异常关闭\n"
+
+#: fe-secure-openssl.c:253 fe-secure-openssl.c:360 fe-secure-openssl.c:1313
+#, c-format
+msgid "unrecognized SSL error code: %d\n"
+msgstr "未知的 SSL 错误码: %d\n"
+
+#: fe-secure-openssl.c:400
+msgid "could not determine server certificate signature algorithm\n"
+msgstr "无法确定服务器证书签名算法\n"
+
+#: fe-secure-openssl.c:421
+#, c-format
+msgid "could not find digest for NID %s\n"
+msgstr "找不到NID %s的摘要\n"
+
+#: fe-secure-openssl.c:431
+msgid "could not generate peer certificate hash\n"
+msgstr "无法生成对等证书哈希\n"
+
+#: fe-secure-openssl.c:488
+msgid "SSL certificate's name entry is missing\n"
+msgstr "SSL证书的名称项缺失\n"
+
+#: fe-secure-openssl.c:815
+#, c-format
+msgid "could not create SSL context: %s\n"
+msgstr "无法创建 SSL 环境: %s\n"
+
+#: fe-secure-openssl.c:854
+#, c-format
+msgid "invalid value \"%s\" for minimum SSL protocol version\n"
+msgstr "最小SSL协议版本的值\"%s\"无效\n"
+
+#: fe-secure-openssl.c:865
+#, c-format
+msgid "could not set minimum SSL protocol version: %s\n"
+msgstr "无法设置最低SSL协议版本: %s\n"
+
+#: fe-secure-openssl.c:883
+#, c-format
+msgid "invalid value \"%s\" for maximum SSL protocol version\n"
+msgstr "最大SSL协议版本的值\"%s\"无效\n"
+
+#: fe-secure-openssl.c:894
+#, c-format
+msgid "could not set maximum SSL protocol version: %s\n"
+msgstr "无法设置最大SSL协议版本: %s\n"
+
+#: fe-secure-openssl.c:930
+#, c-format
+msgid "could not read root certificate file \"%s\": %s\n"
+msgstr "无法读取根证书文件 \"%s\": %s\n"
+
+#: fe-secure-openssl.c:974
+msgid ""
+"could not get home directory to locate root certificate file\n"
+"Either provide the file or change sslmode to disable server certificate verification.\n"
+msgstr ""
+"无法获取home目录以定位根认证文件\n"
+"可以提供该文件或者将sslmode改为禁用服务器证书认证.\n"
+
+#: fe-secure-openssl.c:978
+#, c-format
+msgid ""
+"root certificate file \"%s\" does not exist\n"
+"Either provide the file or change sslmode to disable server certificate verification.\n"
+msgstr ""
+"根认证文件\"%s\"不存在\n"
+"可以提供这个文件或者将sslmode改为禁用服务器认证检验.\n"
+
+#: fe-secure-openssl.c:1009
+#, c-format
+msgid "could not open certificate file \"%s\": %s\n"
+msgstr "无法打开证书文件 \"%s\": %s\n"
+
+#: fe-secure-openssl.c:1028
+#, c-format
+msgid "could not read certificate file \"%s\": %s\n"
+msgstr "无法读取证书文件 \"%s\": %s\n"
+
+#: fe-secure-openssl.c:1053
+#, c-format
+msgid "could not establish SSL connection: %s\n"
+msgstr "无法建立 SSL 联接: %s\n"
+
+#: fe-secure-openssl.c:1107
+#, c-format
+msgid "could not load SSL engine \"%s\": %s\n"
+msgstr "无法加载SSL引擎 \"%s\": %s\n"
+
+#: fe-secure-openssl.c:1119
+#, c-format
+msgid "could not initialize SSL engine \"%s\": %s\n"
+msgstr "无法初始化SSL引擎\"%s\": %s\n"
+
+#: fe-secure-openssl.c:1135
+#, c-format
+msgid "could not read private SSL key \"%s\" from engine \"%s\": %s\n"
+msgstr "无法从引擎\"%2$s\"读取私有SSL钥\"%1$s\": %3$s\n"
+
+#: fe-secure-openssl.c:1149
+#, c-format
+msgid "could not load private SSL key \"%s\" from engine \"%s\": %s\n"
+msgstr "无法从引擎\"%2$s\"读取私有SSL钥\"%1$s\": %3$s\n"
+
+#: fe-secure-openssl.c:1186
+#, c-format
+msgid "certificate present, but not private key file \"%s\"\n"
+msgstr "有证书, 但不是私钥文件 \"%s\"\n"
+
+#: fe-secure-openssl.c:1194
+#, c-format
+msgid "private key file \"%s\" has group or world access; permissions should be u=rw (0600) or less\n"
+msgstr "警告: 私钥文件 \"%s\"的访问权限过大; 权限应设置 为 u=rw (0600)或更小\n"
+
+#: fe-secure-openssl.c:1219
+#, c-format
+msgid "could not load private key file \"%s\": %s\n"
+msgstr "无法装载私钥文件 \"%s\": %s\n"
+
+#: fe-secure-openssl.c:1237
+#, c-format
+msgid "certificate does not match private key file \"%s\": %s\n"
+msgstr "证书不匹配私钥文件 \"%s\": %s\n"
+
+#: fe-secure-openssl.c:1332
+#, c-format
+msgid "certificate could not be obtained: %s\n"
+msgstr "无法获得证书: %s\n"
+
+#: fe-secure-openssl.c:1421
+#, c-format
+msgid "no SSL error reported"
+msgstr "没有报告SSL错误"
+
+#: fe-secure-openssl.c:1430
+#, c-format
+msgid "SSL error code %lu"
+msgstr "SSL 错误代码 %lu"
+
+#: fe-secure-openssl.c:1677
+#, c-format
+msgid "WARNING: sslpassword truncated\n"
+msgstr "警告:ssl密码被截断\n"
+
+#: fe-secure.c:275
+#, c-format
+msgid "could not receive data from server: %s\n"
+msgstr "无法从服务器接收数据: %s\n"
+
+#: fe-secure.c:390
+#, c-format
+msgid "could not send data to server: %s\n"
+msgstr "无法向服务器发送数据: %s\n"
+
+#: win32.c:314
+#, c-format
+msgid "unrecognized socket error: 0x%08X/%d"
+msgstr "不可识别的套接字错误: 0x%08X/%d"
diff --git a/src/interfaces/libpq/pqexpbuffer.c b/src/interfaces/libpq/pqexpbuffer.c
new file mode 100644
index 0000000..bd5ef77
--- /dev/null
+++ b/src/interfaces/libpq/pqexpbuffer.c
@@ -0,0 +1,414 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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-2020, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/interfaces/libpq/pqexpbuffer.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres_fe.h"
+
+#include <limits.h>
+
+#include "pqexpbuffer.h"
+
+#ifdef WIN32
+#include "win32.h"
+#endif
+
+
+/* All "broken" PQExpBuffers point to this string. */
+static const char oom_buffer[1] = "";
+
+/* Need a char * for unconstify() compatibility */
+static const char *oom_buffer_ptr = oom_buffer;
+
+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 = unconstify(char *, oom_buffer_ptr);
+ 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 = unconstify(char *, oom_buffer_ptr); /* 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 = unconstify(char *, oom_buffer_ptr); /* 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,...)
+{
+ int save_errno = errno;
+ 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
+ {
+ errno = save_errno;
+ 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,...)
+{
+ int save_errno = errno;
+ va_list args;
+ bool done;
+
+ if (PQExpBufferBroken(str))
+ return; /* already failed */
+
+ /* Loop in case we have to retry after enlarging the buffer. */
+ do
+ {
+ errno = save_errno;
+ 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.
+ *
+ * Caution: callers must be sure to preserve their entry-time errno
+ * when looping, in case the fmt contains "%m".
+ */
+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)
+ {
+ avail = str->maxlen - str->len;
+
+ nprinted = vsnprintf(str->data + str->len, avail, fmt, args);
+
+ /*
+ * If vsnprintf reports an error, fail (we assume this means there's
+ * something wrong with the format string).
+ */
+ if (unlikely(nprinted < 0))
+ {
+ markPQExpBufferBroken(str);
+ return true;
+ }
+
+ if ((size_t) nprinted < avail)
+ {
+ /* Success. Note nprinted does not include trailing null. */
+ str->len += nprinted;
+ return true;
+ }
+
+ /*
+ * We assume a C99-compliant vsnprintf, so believe its estimate of the
+ * required space, and add one for the trailing null. (If it's wrong,
+ * the logic will still work, but we may loop multiple times.)
+ *
+ * Choke if the required space would exceed INT_MAX, since str->maxlen
+ * can't represent more than that.
+ */
+ if (unlikely(nprinted > INT_MAX - 1))
+ {
+ markPQExpBufferBroken(str);
+ return true;
+ }
+ needed = nprinted + 1;
+ }
+ else
+ {
+ /*
+ * We have to guess at how much to enlarge, since we're skipping the
+ * formatting work. Fortunately, because of enlargePQExpBuffer's
+ * preference for power-of-2 sizes, this number isn't very sensitive;
+ * the net effect is that we'll double the buffer size before trying
+ * to run vsnprintf, which seems sensible.
+ */
+ 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/src/interfaces/libpq/pqexpbuffer.h b/src/interfaces/libpq/pqexpbuffer.h
new file mode 100644
index 0000000..65cd367
--- /dev/null
+++ b/src/interfaces/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-2020, 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/src/interfaces/libpq/pthread-win32.c b/src/interfaces/libpq/pthread-win32.c
new file mode 100644
index 0000000..fd801c1
--- /dev/null
+++ b/src/interfaces/libpq/pthread-win32.c
@@ -0,0 +1,60 @@
+/*-------------------------------------------------------------------------
+*
+* pthread-win32.c
+* partial pthread implementation for win32
+*
+* Copyright (c) 2004-2020, PostgreSQL Global Development Group
+* IDENTIFICATION
+* src/interfaces/libpq/pthread-win32.c
+*
+*-------------------------------------------------------------------------
+*/
+
+#include "postgres_fe.h"
+
+#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/src/interfaces/libpq/test/.gitignore b/src/interfaces/libpq/test/.gitignore
new file mode 100644
index 0000000..5387b3b
--- /dev/null
+++ b/src/interfaces/libpq/test/.gitignore
@@ -0,0 +1,3 @@
+/uri-regress
+/regress.diff
+/regress.out
diff --git a/src/interfaces/libpq/test/Makefile b/src/interfaces/libpq/test/Makefile
new file mode 100644
index 0000000..4832fab
--- /dev/null
+++ b/src/interfaces/libpq/test/Makefile
@@ -0,0 +1,22 @@
+subdir = src/interfaces/libpq/test
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+
+ifeq ($(PORTNAME), win32)
+LDFLAGS += -lws2_32
+endif
+
+override CPPFLAGS := -I$(libpq_srcdir) $(CPPFLAGS)
+LDFLAGS_INTERNAL += $(libpq_pgport)
+
+PROGS = uri-regress
+
+all: $(PROGS)
+
+installcheck: all
+ SRCDIR='$(top_srcdir)' SUBDIR='$(subdir)' \
+ $(PERL) $(top_srcdir)/$(subdir)/regress.pl
+
+clean distclean maintainer-clean:
+ rm -f $(PROGS) *.o
+ rm -f regress.out regress.diff
diff --git a/src/interfaces/libpq/test/README b/src/interfaces/libpq/test/README
new file mode 100644
index 0000000..a05eb6b
--- /dev/null
+++ b/src/interfaces/libpq/test/README
@@ -0,0 +1,7 @@
+This is a testsuite for testing libpq URI connection string syntax.
+
+To run the suite, use 'make installcheck' command. It works by
+running 'regress.pl' from this directory with appropriate environment
+set up, which in turn feeds up lines from 'regress.in' to
+'uri-regress' test program and compares the output against the correct
+one in 'expected.out' file.
diff --git a/src/interfaces/libpq/test/expected.out b/src/interfaces/libpq/test/expected.out
new file mode 100644
index 0000000..d375e82
--- /dev/null
+++ b/src/interfaces/libpq/test/expected.out
@@ -0,0 +1,171 @@
+trying postgresql://uri-user:secret@host:12345/db
+user='uri-user' password='secret' dbname='db' host='host' port='12345' (inet)
+
+trying postgresql://uri-user@host:12345/db
+user='uri-user' dbname='db' host='host' port='12345' (inet)
+
+trying postgresql://uri-user@host/db
+user='uri-user' dbname='db' host='host' (inet)
+
+trying postgresql://host:12345/db
+dbname='db' host='host' port='12345' (inet)
+
+trying postgresql://host/db
+dbname='db' host='host' (inet)
+
+trying postgresql://uri-user@host:12345/
+user='uri-user' host='host' port='12345' (inet)
+
+trying postgresql://uri-user@host/
+user='uri-user' host='host' (inet)
+
+trying postgresql://uri-user@
+user='uri-user' (local)
+
+trying postgresql://host:12345/
+host='host' port='12345' (inet)
+
+trying postgresql://host:12345
+host='host' port='12345' (inet)
+
+trying postgresql://host/db
+dbname='db' host='host' (inet)
+
+trying postgresql://host/
+host='host' (inet)
+
+trying postgresql://host
+host='host' (inet)
+
+trying postgresql://
+(local)
+
+trying postgresql://?hostaddr=127.0.0.1
+hostaddr='127.0.0.1' (inet)
+
+trying postgresql://example.com?hostaddr=63.1.2.4
+host='example.com' hostaddr='63.1.2.4' (inet)
+
+trying postgresql://%68ost/
+host='host' (inet)
+
+trying postgresql://host/db?user=uri-user
+user='uri-user' dbname='db' host='host' (inet)
+
+trying postgresql://host/db?user=uri-user&port=12345
+user='uri-user' dbname='db' host='host' port='12345' (inet)
+
+trying postgresql://host/db?u%73er=someotheruser&port=12345
+user='someotheruser' dbname='db' host='host' port='12345' (inet)
+
+trying postgresql://host/db?u%7aer=someotheruser&port=12345
+uri-regress: invalid URI query parameter: "uzer"
+
+trying postgresql://host:12345?user=uri-user
+user='uri-user' host='host' port='12345' (inet)
+
+trying postgresql://host?user=uri-user
+user='uri-user' host='host' (inet)
+
+trying postgresql://host?
+host='host' (inet)
+
+trying postgresql://[::1]:12345/db
+dbname='db' host='::1' port='12345' (inet)
+
+trying postgresql://[::1]/db
+dbname='db' host='::1' (inet)
+
+trying postgresql://[2001:db8::1234]/
+host='2001:db8::1234' (inet)
+
+trying postgresql://[200z:db8::1234]/
+host='200z:db8::1234' (inet)
+
+trying postgresql://[::1]
+host='::1' (inet)
+
+trying postgres://
+(local)
+
+trying postgres:///
+(local)
+
+trying postgres:///db
+dbname='db' (local)
+
+trying postgres://uri-user@/db
+user='uri-user' dbname='db' (local)
+
+trying postgres://?host=/path/to/socket/dir
+host='/path/to/socket/dir' (local)
+
+trying postgresql://host?uzer=
+uri-regress: invalid URI query parameter: "uzer"
+
+trying postgre://
+uri-regress: missing "=" after "postgre://" in connection info string
+
+trying postgres://[::1
+uri-regress: end of string reached when looking for matching "]" in IPv6 host address in URI: "postgres://[::1"
+
+trying postgres://[]
+uri-regress: IPv6 host address may not be empty in URI: "postgres://[]"
+
+trying postgres://[::1]z
+uri-regress: unexpected character "z" at position 17 in URI (expected ":" or "/"): "postgres://[::1]z"
+
+trying postgresql://host?zzz
+uri-regress: missing key/value separator "=" in URI query parameter: "zzz"
+
+trying postgresql://host?value1&value2
+uri-regress: missing key/value separator "=" in URI query parameter: "value1"
+
+trying postgresql://host?key=key=value
+uri-regress: extra key/value separator "=" in URI query parameter: "key"
+
+trying postgres://host?dbname=%XXfoo
+uri-regress: invalid percent-encoded token: "%XXfoo"
+
+trying postgresql://a%00b
+uri-regress: forbidden value %00 in percent-encoded value: "a%00b"
+
+trying postgresql://%zz
+uri-regress: invalid percent-encoded token: "%zz"
+
+trying postgresql://%1
+uri-regress: invalid percent-encoded token: "%1"
+
+trying postgresql://%
+uri-regress: invalid percent-encoded token: "%"
+
+trying postgres://@host
+host='host' (inet)
+
+trying postgres://host:/
+host='host' (inet)
+
+trying postgres://:12345/
+port='12345' (local)
+
+trying postgres://otheruser@?host=/no/such/directory
+user='otheruser' host='/no/such/directory' (local)
+
+trying postgres://otheruser@/?host=/no/such/directory
+user='otheruser' host='/no/such/directory' (local)
+
+trying postgres://otheruser@:12345?host=/no/such/socket/path
+user='otheruser' host='/no/such/socket/path' port='12345' (local)
+
+trying postgres://otheruser@:12345/db?host=/path/to/socket
+user='otheruser' dbname='db' host='/path/to/socket' port='12345' (local)
+
+trying postgres://:12345/db?host=/path/to/socket
+dbname='db' host='/path/to/socket' port='12345' (local)
+
+trying postgres://:12345?host=/path/to/socket
+host='/path/to/socket' port='12345' (local)
+
+trying postgres://%2Fvar%2Flib%2Fpostgresql/dbname
+dbname='dbname' host='/var/lib/postgresql' (local)
+
diff --git a/src/interfaces/libpq/test/regress.in b/src/interfaces/libpq/test/regress.in
new file mode 100644
index 0000000..de034f3
--- /dev/null
+++ b/src/interfaces/libpq/test/regress.in
@@ -0,0 +1,57 @@
+postgresql://uri-user:secret@host:12345/db
+postgresql://uri-user@host:12345/db
+postgresql://uri-user@host/db
+postgresql://host:12345/db
+postgresql://host/db
+postgresql://uri-user@host:12345/
+postgresql://uri-user@host/
+postgresql://uri-user@
+postgresql://host:12345/
+postgresql://host:12345
+postgresql://host/db
+postgresql://host/
+postgresql://host
+postgresql://
+postgresql://?hostaddr=127.0.0.1
+postgresql://example.com?hostaddr=63.1.2.4
+postgresql://%68ost/
+postgresql://host/db?user=uri-user
+postgresql://host/db?user=uri-user&port=12345
+postgresql://host/db?u%73er=someotheruser&port=12345
+postgresql://host/db?u%7aer=someotheruser&port=12345
+postgresql://host:12345?user=uri-user
+postgresql://host?user=uri-user
+postgresql://host?
+postgresql://[::1]:12345/db
+postgresql://[::1]/db
+postgresql://[2001:db8::1234]/
+postgresql://[200z:db8::1234]/
+postgresql://[::1]
+postgres://
+postgres:///
+postgres:///db
+postgres://uri-user@/db
+postgres://?host=/path/to/socket/dir
+postgresql://host?uzer=
+postgre://
+postgres://[::1
+postgres://[]
+postgres://[::1]z
+postgresql://host?zzz
+postgresql://host?value1&value2
+postgresql://host?key=key=value
+postgres://host?dbname=%XXfoo
+postgresql://a%00b
+postgresql://%zz
+postgresql://%1
+postgresql://%
+postgres://@host
+postgres://host:/
+postgres://:12345/
+postgres://otheruser@?host=/no/such/directory
+postgres://otheruser@/?host=/no/such/directory
+postgres://otheruser@:12345?host=/no/such/socket/path
+postgres://otheruser@:12345/db?host=/path/to/socket
+postgres://:12345/db?host=/path/to/socket
+postgres://:12345?host=/path/to/socket
+postgres://%2Fvar%2Flib%2Fpostgresql/dbname
diff --git a/src/interfaces/libpq/test/regress.pl b/src/interfaces/libpq/test/regress.pl
new file mode 100644
index 0000000..54db4f1
--- /dev/null
+++ b/src/interfaces/libpq/test/regress.pl
@@ -0,0 +1,63 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+# use of SRCDIR/SUBDIR is required for supporting VPath builds
+my $srcdir = $ENV{'SRCDIR'} or die 'SRCDIR environment variable is not set';
+my $subdir = $ENV{'SUBDIR'} or die 'SUBDIR environment variable is not set';
+
+my $regress_in = "$srcdir/$subdir/regress.in";
+my $expected_out = "$srcdir/$subdir/expected.out";
+
+# the output file should land in the build_dir of VPath, or just in
+# the current dir, if VPath isn't used
+my $regress_out = "regress.out";
+
+# open input file first, so possible error isn't sent to redirected STDERR
+open(my $regress_in_fh, "<", $regress_in)
+ or die "can't open $regress_in for reading: $!";
+
+# save STDOUT/ERR and redirect both to regress.out
+open(my $oldout_fh, ">&", \*STDOUT) or die "can't dup STDOUT: $!";
+open(my $olderr_fh, ">&", \*STDERR) or die "can't dup STDERR: $!";
+
+open(STDOUT, ">", $regress_out)
+ or die "can't open $regress_out for writing: $!";
+open(STDERR, ">&", \*STDOUT) or die "can't dup STDOUT: $!";
+
+# read lines from regress.in and run uri-regress on them
+while (<$regress_in_fh>)
+{
+ chomp;
+ print "trying $_\n";
+ system("./uri-regress \"$_\"");
+ print "\n";
+}
+
+# restore STDOUT/ERR so we can print the outcome to the user
+open(STDERR, ">&", $olderr_fh)
+ or die; # can't complain as STDERR is still duped
+open(STDOUT, ">&", $oldout_fh) or die "can't restore STDOUT: $!";
+
+# just in case
+close $regress_in_fh;
+
+my $diff_status = system(
+ "diff -c \"$srcdir/$subdir/expected.out\" regress.out >regress.diff");
+
+print "=" x 70, "\n";
+if ($diff_status == 0)
+{
+ print "All tests passed\n";
+ exit 0;
+}
+else
+{
+ print <<EOF;
+FAILED: the test result differs from the expected output
+
+Review the difference in "$subdir/regress.diff"
+EOF
+ exit 1;
+}
diff --git a/src/interfaces/libpq/test/uri-regress.c b/src/interfaces/libpq/test/uri-regress.c
new file mode 100644
index 0000000..1fb25e8
--- /dev/null
+++ b/src/interfaces/libpq/test/uri-regress.c
@@ -0,0 +1,84 @@
+/*
+ * uri-regress.c
+ * A test program for libpq URI format
+ *
+ * This is a helper for libpq conninfo regression testing. It takes a single
+ * conninfo string as a parameter, parses it using PQconninfoParse, and then
+ * prints out the values from the parsed PQconninfoOption struct that differ
+ * from the defaults (obtained from PQconndefaults).
+ *
+ * Portions Copyright (c) 2012-2020, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ * src/interfaces/libpq/test/uri-regress.c
+ */
+
+#include "postgres_fe.h"
+
+#include "libpq-fe.h"
+
+int
+main(int argc, char *argv[])
+{
+ PQconninfoOption *opts;
+ PQconninfoOption *defs;
+ PQconninfoOption *opt;
+ PQconninfoOption *def;
+ char *errmsg = NULL;
+ bool local = true;
+
+ if (argc != 2)
+ return 1;
+
+ opts = PQconninfoParse(argv[1], &errmsg);
+ if (opts == NULL)
+ {
+ fprintf(stderr, "uri-regress: %s", errmsg);
+ return 1;
+ }
+
+ defs = PQconndefaults();
+ if (defs == NULL)
+ {
+ fprintf(stderr, "uri-regress: cannot fetch default options\n");
+ return 1;
+ }
+
+ /*
+ * Loop on the options, and print the value of each if not the default.
+ *
+ * XXX this coding assumes that PQconninfoOption structs always have the
+ * keywords in the same order.
+ */
+ for (opt = opts, def = defs; opt->keyword; ++opt, ++def)
+ {
+ if (opt->val != NULL)
+ {
+ if (def->val == NULL || strcmp(opt->val, def->val) != 0)
+ printf("%s='%s' ", opt->keyword, opt->val);
+
+ /*
+ * Try to detect if this is a Unix-domain socket or inet. This is
+ * a bit grotty but it's the same thing that libpq itself does.
+ *
+ * Note that we directly test for '/' instead of using
+ * is_absolute_path, as that would be considerably more messy.
+ * This would fail on Windows, but that platform doesn't have
+ * Unix-domain sockets anyway.
+ */
+ if (*opt->val &&
+ (strcmp(opt->keyword, "hostaddr") == 0 ||
+ (strcmp(opt->keyword, "host") == 0 && *opt->val != '/')))
+ {
+ local = false;
+ }
+ }
+ }
+
+ if (local)
+ printf("(local)\n");
+ else
+ printf("(inet)\n");
+
+ return 0;
+}
diff --git a/src/interfaces/libpq/win32.c b/src/interfaces/libpq/win32.c
new file mode 100644
index 0000000..cd19ec9
--- /dev/null
+++ b/src/interfaces/libpq/win32.c
@@ -0,0 +1,324 @@
+/*
+ * 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-2020, 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/src/interfaces/libpq/win32.h b/src/interfaces/libpq/win32.h
new file mode 100644
index 0000000..c42d7ab
--- /dev/null
+++ b/src/interfaces/libpq/win32.h
@@ -0,0 +1,34 @@
+/*
+ * src/interfaces/libpq/win32.h
+ */
+#ifndef __win32_h_included
+#define __win32_h_included
+
+/*
+ * Some compatibility functions
+ */
+
+/* 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)
+
+#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