summaryrefslogtreecommitdiffstats
path: root/src/interfaces/libpq
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:15:05 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:15:05 +0000
commit46651ce6fe013220ed397add242004d764fc0153 (patch)
tree6e5299f990f88e60174a1d3ae6e48eedd2688b2b /src/interfaces/libpq
parentInitial commit. (diff)
downloadpostgresql-14-upstream.tar.xz
postgresql-14-upstream.zip
Adding upstream version 14.5.upstream/14.5upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--src/interfaces/libpq/.gitignore1
-rw-r--r--src/interfaces/libpq/Makefile142
-rw-r--r--src/interfaces/libpq/README3
-rw-r--r--src/interfaces/libpq/exports.txt188
-rw-r--r--src/interfaces/libpq/fe-auth-scram.c905
-rw-r--r--src/interfaces/libpq/fe-auth.c1285
-rw-r--r--src/interfaces/libpq/fe-auth.h36
-rw-r--r--src/interfaces/libpq/fe-connect.c7367
-rw-r--r--src/interfaces/libpq/fe-exec.c4451
-rw-r--r--src/interfaces/libpq/fe-gssapi-common.c130
-rw-r--r--src/interfaces/libpq/fe-gssapi-common.h28
-rw-r--r--src/interfaces/libpq/fe-lobj.c1084
-rw-r--r--src/interfaces/libpq/fe-misc.c1289
-rw-r--r--src/interfaces/libpq/fe-print.c781
-rw-r--r--src/interfaces/libpq/fe-protocol3.c2254
-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.c1884
-rw-r--r--src/interfaces/libpq/fe-secure.c552
-rw-r--r--src/interfaces/libpq/fe-trace.c729
-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.h672
-rw-r--r--src/interfaces/libpq/libpq-int.h866
-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.po1221
-rw-r--r--src/interfaces/libpq/po/el.po1314
-rw-r--r--src/interfaces/libpq/po/es.po1227
-rw-r--r--src/interfaces/libpq/po/fr.po1461
-rw-r--r--src/interfaces/libpq/po/ja.po1231
-rw-r--r--src/interfaces/libpq/po/ko.po1340
-rw-r--r--src/interfaces/libpq/po/ru.po1465
-rw-r--r--src/interfaces/libpq/po/sv.po1225
-rw-r--r--src/interfaces/libpq/po/uk.po1212
-rw-r--r--src/interfaces/libpq/po/zh_CN.po1193
-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.pl65
-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.h23
51 files changed, 41633 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..4368257
--- /dev/null
+++ b/src/interfaces/libpq/Makefile
@@ -0,0 +1,142 @@
+#-------------------------------------------------------------------------
+#
+# Makefile for src/interfaces/libpq library
+#
+# Portions Copyright (c) 1996-2021, 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-protocol3.o \
+ fe-secure.o \
+ fe-trace.o \
+ legacy-pqsignal.o \
+ libpq-events.o \
+ pqexpbuffer.o \
+ fe-auth.o
+
+# File shared across all SSL implementations supported.
+ifneq ($(with_ssl),no)
+OBJS += \
+ fe-secure-common.o
+endif
+
+ifeq ($(with_ssl),openssl)
+OBJS += \
+ 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
+
+ifeq ($(with_ssl),openssl)
+PKG_CONFIG_REQUIRES_PRIVATE = libssl libcrypto
+endif
+
+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..e8bcc88
--- /dev/null
+++ b/src/interfaces/libpq/exports.txt
@@ -0,0 +1,188 @@
+# 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
+PQenterPipelineMode 180
+PQexitPipelineMode 181
+PQpipelineSync 182
+PQpipelineStatus 183
+PQsetTraceFlags 184
+PQmblenBounded 185
+PQsendFlushRequest 186
diff --git a/src/interfaces/libpq/fe-auth-scram.c b/src/interfaces/libpq/fe-auth-scram.c
new file mode 100644
index 0000000..5881386
--- /dev/null
+++ b/src/interfaces/libpq/fe-auth-scram.c
@@ -0,0 +1,905 @@
+/*-------------------------------------------------------------------------
+ *
+ * fe-auth-scram.c
+ * The front-end (client) implementation of SCRAM authentication.
+ *
+ * Portions Copyright (c) 1996-2021, 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/hmac.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, bool *match);
+static bool 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)
+ {
+ appendPQExpBufferStr(&conn->errorMessage,
+ libpq_gettext("malformed SCRAM message (empty message)\n"));
+ goto error;
+ }
+ if (inputlen != strlen(input))
+ {
+ appendPQExpBufferStr(&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))
+ {
+ appendPQExpBufferStr(&conn->errorMessage,
+ libpq_gettext("could not verify server signature\n"));
+ goto error;
+ }
+
+ if (!*success)
+ {
+ appendPQExpBufferStr(&conn->errorMessage,
+ libpq_gettext("incorrect server signature\n"));
+ }
+ *done = true;
+ state->state = FE_SCRAM_FINISHED;
+ break;
+
+ default:
+ /* shouldn't happen */
+ appendPQExpBufferStr(&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.
+ *
+ * The buffer at **input is destructively modified, and *input is
+ * advanced over the "attr=value" string and any following comma.
+ *
+ * On failure, append an error message to *errorMessage and return NULL.
+ */
+static char *
+read_attr_value(char **input, char attr, PQExpBuffer errorMessage)
+{
+ char *begin = *input;
+ char *end;
+
+ if (*begin != attr)
+ {
+ appendPQExpBuffer(errorMessage,
+ libpq_gettext("malformed SCRAM message (attribute \"%c\" expected)\n"),
+ attr);
+ return NULL;
+ }
+ begin++;
+
+ if (*begin != '=')
+ {
+ appendPQExpBuffer(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))
+ {
+ appendPQExpBufferStr(&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)
+ {
+ appendPQExpBufferStr(&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)
+ {
+ appendPQExpBufferStr(&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);
+ appendPQExpBufferStr(&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);
+ appendPQExpBufferStr(&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);
+ appendPQExpBufferStr(&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. */
+ if (!calculate_client_proof(state,
+ state->client_final_message_without_proof,
+ client_proof))
+ {
+ termPQExpBuffer(&buf);
+ appendPQExpBufferStr(&conn->errorMessage,
+ libpq_gettext("could not calculate client proof\n"));
+ return NULL;
+ }
+
+ 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);
+ appendPQExpBufferStr(&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);
+ appendPQExpBufferStr(&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)
+ {
+ appendPQExpBufferStr(&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 appended 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)
+ {
+ appendPQExpBufferStr(&conn->errorMessage,
+ libpq_gettext("invalid SCRAM response (nonce mismatch)\n"));
+ return false;
+ }
+
+ state->nonce = strdup(nonce);
+ if (state->nonce == NULL)
+ {
+ appendPQExpBufferStr(&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 appended 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)
+ {
+ appendPQExpBufferStr(&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)
+ {
+ appendPQExpBufferStr(&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 appended an error string */
+ return false;
+ }
+ state->iterations = strtol(iterations_str, &endptr, 10);
+ if (*endptr != '\0' || state->iterations < 1)
+ {
+ appendPQExpBufferStr(&conn->errorMessage,
+ libpq_gettext("malformed SCRAM message (invalid iteration count)\n"));
+ return false;
+ }
+
+ if (*input != '\0')
+ appendPQExpBufferStr(&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)
+ {
+ appendPQExpBufferStr(&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);
+
+ if (errmsg == NULL)
+ {
+ /* read_attr_value() has appended an error message */
+ return false;
+ }
+ appendPQExpBuffer(&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 appended an error message */
+ return false;
+ }
+
+ if (*input != '\0')
+ appendPQExpBufferStr(&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)
+ {
+ appendPQExpBufferStr(&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);
+ appendPQExpBufferStr(&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. Returns true on success, false on failure.
+ */
+static bool
+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;
+ pg_hmac_ctx *ctx;
+
+ ctx = pg_hmac_create(PG_SHA256);
+ if (ctx == NULL)
+ return false;
+
+ /*
+ * Calculate SaltedPassword, and store it in 'state' so that we can reuse
+ * it later in verify_server_signature.
+ */
+ if (scram_SaltedPassword(state->password, state->salt, state->saltlen,
+ state->iterations, state->SaltedPassword) < 0 ||
+ scram_ClientKey(state->SaltedPassword, ClientKey) < 0 ||
+ scram_H(ClientKey, SCRAM_KEY_LEN, StoredKey) < 0 ||
+ pg_hmac_init(ctx, StoredKey, SCRAM_KEY_LEN) < 0 ||
+ pg_hmac_update(ctx,
+ (uint8 *) state->client_first_message_bare,
+ strlen(state->client_first_message_bare)) < 0 ||
+ pg_hmac_update(ctx, (uint8 *) ",", 1) < 0 ||
+ pg_hmac_update(ctx,
+ (uint8 *) state->server_first_message,
+ strlen(state->server_first_message)) < 0 ||
+ pg_hmac_update(ctx, (uint8 *) ",", 1) < 0 ||
+ pg_hmac_update(ctx,
+ (uint8 *) client_final_message_without_proof,
+ strlen(client_final_message_without_proof)) < 0 ||
+ pg_hmac_final(ctx, ClientSignature, sizeof(ClientSignature)) < 0)
+ {
+ pg_hmac_free(ctx);
+ return false;
+ }
+
+ for (i = 0; i < SCRAM_KEY_LEN; i++)
+ result[i] = ClientKey[i] ^ ClientSignature[i];
+
+ pg_hmac_free(ctx);
+ return true;
+}
+
+/*
+ * Validate the server signature, received as part of the final exchange
+ * message received from the server. *match tracks if the server signature
+ * matched or not. Returns true if the server signature got verified, and
+ * false for a processing error.
+ */
+static bool
+verify_server_signature(fe_scram_state *state, bool *match)
+{
+ uint8 expected_ServerSignature[SCRAM_KEY_LEN];
+ uint8 ServerKey[SCRAM_KEY_LEN];
+ pg_hmac_ctx *ctx;
+
+ ctx = pg_hmac_create(PG_SHA256);
+ if (ctx == NULL)
+ return false;
+
+ if (scram_ServerKey(state->SaltedPassword, ServerKey) < 0 ||
+ /* calculate ServerSignature */
+ pg_hmac_init(ctx, ServerKey, SCRAM_KEY_LEN) < 0 ||
+ pg_hmac_update(ctx,
+ (uint8 *) state->client_first_message_bare,
+ strlen(state->client_first_message_bare)) < 0 ||
+ pg_hmac_update(ctx, (uint8 *) ",", 1) < 0 ||
+ pg_hmac_update(ctx,
+ (uint8 *) state->server_first_message,
+ strlen(state->server_first_message)) < 0 ||
+ pg_hmac_update(ctx, (uint8 *) ",", 1) < 0 ||
+ pg_hmac_update(ctx,
+ (uint8 *) state->client_final_message_without_proof,
+ strlen(state->client_final_message_without_proof)) < 0 ||
+ pg_hmac_final(ctx, expected_ServerSignature,
+ sizeof(expected_ServerSignature)) < 0)
+ {
+ pg_hmac_free(ctx);
+ return false;
+ }
+
+ pg_hmac_free(ctx);
+
+ /* signature processed, so now check after it */
+ if (memcmp(expected_ServerSignature, state->ServerSignature, SCRAM_KEY_LEN) != 0)
+ *match = false;
+ else
+ *match = true;
+
+ 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..e806264
--- /dev/null
+++ b/src/interfaces/libpq/fe-auth.c
@@ -0,0 +1,1285 @@
+/*-------------------------------------------------------------------------
+ *
+ * fe-auth.c
+ * The front-end (client) authorization routines
+ *
+ * Portions Copyright (c) 1996-2021, 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)
+ {
+ appendPQExpBuffer(&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'))
+ {
+ appendPQExpBufferStr(&conn->errorMessage,
+ libpq_gettext("host name must be specified\n"));
+ return STATUS_ERROR;
+ }
+
+ if (conn->gctx)
+ {
+ appendPQExpBufferStr(&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)
+ appendPQExpBuffer(&conn->errorMessage, "%s: SSPI error %x\n",
+ mprefix, (unsigned int) r);
+ else
+ appendPQExpBuffer(&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)
+ {
+ appendPQExpBuffer(&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)
+ {
+ appendPQExpBufferStr(&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.
+ */
+ appendPQExpBufferStr(&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)
+ {
+ appendPQExpBufferStr(&conn->errorMessage,
+ libpq_gettext("duplicate SSPI authentication request\n"));
+ return STATUS_ERROR;
+ }
+
+ /*
+ * Retrieve credentials handle
+ */
+ conn->sspicred = malloc(sizeof(CredHandle));
+ if (conn->sspicred == NULL)
+ {
+ appendPQExpBufferStr(&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'))
+ {
+ appendPQExpBufferStr(&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)
+ {
+ appendPQExpBufferStr(&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)
+ {
+ appendPQExpBufferStr(&conn->errorMessage,
+ libpq_gettext("channel binding required, but SSL not in use\n"));
+ goto error;
+ }
+
+ if (conn->sasl_state)
+ {
+ appendPQExpBufferStr(&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))
+ {
+ appendPQExpBufferStr(&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 */
+ {
+ appendPQExpBufferStr(&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.
+ */
+ appendPQExpBufferStr(&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)
+ {
+ appendPQExpBufferStr(&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)
+ {
+ appendPQExpBufferStr(&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')
+ {
+ appendPQExpBufferStr(&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', 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);
+ appendPQExpBufferStr(&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)
+ {
+ appendPQExpBuffer(&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);
+
+ appendPQExpBufferStr(&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];
+
+ appendPQExpBuffer(&conn->errorMessage,
+ "pg_local_sendauth: sendmsg: %s\n",
+ strerror_r(errno, sebuf, sizeof(sebuf)));
+ return STATUS_ERROR;
+ }
+ return STATUS_OK;
+#else
+ appendPQExpBufferStr(&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)
+ {
+ appendPQExpBufferStr(&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;
+ }
+ ret = pqPacketSend(conn, 'p', 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))
+ {
+ appendPQExpBufferStr(&conn->errorMessage,
+ libpq_gettext("channel binding required, but server authenticated client without channel binding\n"));
+ result = false;
+ }
+ break;
+ default:
+ appendPQExpBufferStr(&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)
+{
+ int oldmsglen;
+
+ if (!check_expected_areq(areq, conn))
+ return STATUS_ERROR;
+
+ switch (areq)
+ {
+ case AUTH_REQ_OK:
+ break;
+
+ case AUTH_REQ_KRB4:
+ appendPQExpBufferStr(&conn->errorMessage,
+ libpq_gettext("Kerberos 4 authentication not supported\n"));
+ return STATUS_ERROR;
+
+ case AUTH_REQ_KRB5:
+ appendPQExpBufferStr(&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:
+ appendPQExpBufferStr(&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:
+ appendPQExpBufferStr(&conn->errorMessage,
+ libpq_gettext("SSPI authentication not supported\n"));
+ return STATUS_ERROR;
+#endif /* !define(ENABLE_GSS) */
+#endif /* ENABLE_SSPI */
+
+
+ case AUTH_REQ_CRYPT:
+ appendPQExpBufferStr(&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')
+ {
+ appendPQExpBufferStr(&conn->errorMessage,
+ PQnoPasswordSupplied);
+ return STATUS_ERROR;
+ }
+ if (pg_password_sendauth(conn, password, areq) != STATUS_OK)
+ {
+ appendPQExpBufferStr(&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)
+ {
+ appendPQExpBufferStr(&conn->errorMessage,
+ "fe_sendauth: invalid authentication request from server: AUTH_REQ_SASL_CONT without AUTH_REQ_SASL\n");
+ return STATUS_ERROR;
+ }
+ oldmsglen = conn->errorMessage.len;
+ if (pg_SASL_continue(conn, payloadlen,
+ (areq == AUTH_REQ_SASL_FIN)) != STATUS_OK)
+ {
+ /* Use this message if pg_SASL_continue didn't supply one */
+ if (conn->errorMessage.len == oldmsglen)
+ appendPQExpBufferStr(&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:
+ appendPQExpBuffer(&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 append a suitable error message to *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)
+ appendPQExpBuffer(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)
+ appendPQExpBuffer(errorMessage,
+ libpq_gettext("could not look up local user ID %d: %s\n"),
+ (int) user_id,
+ strerror_r(pwerr, pwdbuf, sizeof(pwdbuf)));
+ else
+ appendPQExpBuffer(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)
+ appendPQExpBufferStr(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;
+
+ resetPQExpBuffer(&conn->errorMessage);
+
+ /* 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);
+ appendPQExpBufferStr(&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);
+ appendPQExpBufferStr(&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
+ {
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("unrecognized password encryption algorithm \"%s\"\n"),
+ algorithm);
+ return NULL;
+ }
+
+ if (!crypt_pwd)
+ appendPQExpBufferStr(&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..7877dcb
--- /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-2021, 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..709ba15
--- /dev/null
+++ b/src/interfaces/libpq/fe-connect.c
@@ -0,0 +1,7367 @@
+/*-------------------------------------------------------------------------
+ *
+ * fe-connect.c
+ * functions related to setting up a connection to the backend
+ *
+ * Portions Copyright (c) 1996-2021, 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 DefaultOption ""
+#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[] = {
+ {"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)},
+
+ {"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)},
+
+ {"sslcrldir", "PGSSLCRLDIR", NULL, NULL,
+ "SSL-Revocation-List-Dir", "", 64,
+ offsetof(struct pg_conn, sslcrldir)},
+
+ {"sslsni", "PGSSLSNI", "1", NULL,
+ "SSL-SNI", "", 1,
+ offsetof(struct pg_conn, sslsni)},
+
+ {"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", "", 15, /* sizeof("prefer-standby") = 15 */
+ 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;
+ }
+}
+
+/*
+ * pqFreeCommandQueue
+ * Free all the entries of PGcmdQueueEntry queue passed.
+ */
+static void
+pqFreeCommandQueue(PGcmdQueueEntry *queue)
+{
+ while (queue != NULL)
+ {
+ PGcmdQueueEntry *cur = queue;
+
+ queue = cur->next;
+ if (cur->query)
+ free(cur->query);
+ free(cur);
+ }
+}
+
+/*
+ * 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;
+
+ pqFreeCommandQueue(conn->cmd_queue_head);
+ conn->cmd_queue_head = conn->cmd_queue_tail = NULL;
+
+ pqFreeCommandQueue(conn->cmd_queue_recycle);
+ conn->cmd_queue_recycle = 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->default_transaction_read_only = PG_BOOL_UNKNOWN;
+ conn->in_hot_standby = PG_BOOL_UNKNOWN;
+ 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. Note that we also expect this
+ * to initialize conn->errorMessage to empty. All subsequent steps during
+ * connection initialization will only append to that buffer.
+ */
+ 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. Note that we also expect this
+ * to initialize conn->errorMessage to empty. All subsequent steps during
+ * connection initialization will only append to that buffer.
+ */
+ 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)
+ {
+ appendPQExpBufferStr(&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;
+ appendPQExpBuffer(&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_unixsock_path(ch->host))
+ ch->type = CHT_UNIX_SOCKET;
+#endif
+ }
+ else
+ {
+ if (ch->host)
+ free(ch->host);
+
+ /*
+ * This bit selects the default host location. If you change
+ * this, see also pg_regress.
+ */
+#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;
+ appendPQExpBuffer(&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;
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("invalid %s value: \"%s\"\n"),
+ "channel_binding", 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;
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("invalid %s value: \"%s\"\n"),
+ "sslmode", 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;
+ appendPQExpBuffer(&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;
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("invalid %s value: \"%s\"\n"),
+ "ssl_min_protocol_version",
+ conn->ssl_min_protocol_version);
+ return false;
+ }
+ if (!sslVerifyProtocolVersion(conn->ssl_max_protocol_version))
+ {
+ conn->status = CONNECTION_BAD;
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("invalid %s value: \"%s\"\n"),
+ "ssl_max_protocol_version",
+ 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;
+ appendPQExpBufferStr(&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;
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("invalid %s value: \"%s\"\n"),
+ "gssencmode",
+ conn->gssencmode);
+ return false;
+ }
+#ifndef ENABLE_GSS
+ if (strcmp(conn->gssencmode, "require") == 0)
+ {
+ conn->status = CONNECTION_BAD;
+ appendPQExpBuffer(&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;
+ }
+
+ /*
+ * validate target_session_attrs option, and set target_server_type
+ */
+ if (conn->target_session_attrs)
+ {
+ if (strcmp(conn->target_session_attrs, "any") == 0)
+ conn->target_server_type = SERVER_TYPE_ANY;
+ else if (strcmp(conn->target_session_attrs, "read-write") == 0)
+ conn->target_server_type = SERVER_TYPE_READ_WRITE;
+ else if (strcmp(conn->target_session_attrs, "read-only") == 0)
+ conn->target_server_type = SERVER_TYPE_READ_ONLY;
+ else if (strcmp(conn->target_session_attrs, "primary") == 0)
+ conn->target_server_type = SERVER_TYPE_PRIMARY;
+ else if (strcmp(conn->target_session_attrs, "standby") == 0)
+ conn->target_server_type = SERVER_TYPE_STANDBY;
+ else if (strcmp(conn->target_session_attrs, "prefer-standby") == 0)
+ conn->target_server_type = SERVER_TYPE_PREFER_STANDBY;
+ else
+ {
+ conn->status = CONNECTION_BAD;
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("invalid %s value: \"%s\"\n"),
+ "target_session_attrs",
+ conn->target_session_attrs);
+ return false;
+ }
+ }
+ else
+ conn->target_server_type = SERVER_TYPE_ANY;
+
+ /*
+ * Resolve special "auto" client_encoding from the locale
+ */
+ if (conn->client_encoding_initial &&
+ strcmp(conn->client_encoding_initial, "auto") == 0)
+ {
+ free(conn->client_encoding_initial);
+ conn->client_encoding_initial = strdup(pg_encoding_to_char(pg_get_encoding_from_locale(NULL, true)));
+ if (!conn->client_encoding_initial)
+ goto oom_error;
+ }
+
+ /*
+ * Only if we get this far is it appropriate to try to connect. (We need a
+ * state flag, rather than just the boolean result of this function, in
+ * case someone tries to PQreset() the PGconn.)
+ */
+ conn->options_valid = true;
+
+ return true;
+
+oom_error:
+ conn->status = CONNECTION_BAD;
+ appendPQExpBufferStr(&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. Note that we also expect this
+ * to initialize conn->errorMessage to empty. All subsequent steps during
+ * connection initialization will only append to that buffer.
+ */
+ 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 (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;
+ appendPQExpBufferStr(&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';
+}
+
+/*
+ * emitHostIdentityInfo -
+ * Speculatively append "connection to server so-and-so failed: " to
+ * conn->errorMessage once we've identified the current connection target
+ * address. This ensures that any subsequent error message will be properly
+ * attributed to the server we couldn't connect to. conn->raddr must be
+ * valid, and the result of getHostaddr() must be supplied.
+ */
+static void
+emitHostIdentityInfo(PGconn *conn, const char *host_addr)
+{
+#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("connection to server on socket \"%s\" failed: "),
+ service);
+ }
+ else
+#endif /* HAVE_UNIX_SOCKETS */
+ {
+ const char *displayed_host;
+ const char *displayed_port;
+
+ /* 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 &&
+ host_addr[0] &&
+ strcmp(displayed_host, host_addr) != 0)
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("connection to server at \"%s\" (%s), port %s failed: "),
+ displayed_host, host_addr,
+ displayed_port);
+ else
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("connection to server at \"%s\", port %s failed: "),
+ displayed_host,
+ displayed_port);
+ }
+}
+
+/* ----------
+ * connectFailureMessage -
+ * create a friendly error message on connection failure,
+ * using the given errno value. Use this for error cases that
+ * imply that there's no server there.
+ * ----------
+ */
+static void
+connectFailureMessage(PGconn *conn, int errorno)
+{
+ char sebuf[PG_STRERROR_R_BUFLEN];
+
+ appendPQExpBuffer(&conn->errorMessage,
+ "%s\n",
+ SOCK_STRERROR(errorno, sebuf, sizeof(sebuf)));
+
+#ifdef HAVE_UNIX_SOCKETS
+ if (IS_AF_UNIX(conn->raddr.addr.ss_family))
+ appendPQExpBufferStr(&conn->errorMessage,
+ libpq_gettext("\tIs the server running locally and accepting connections on that socket?\n"));
+ else
+#endif
+ appendPQExpBufferStr(&conn->errorMessage,
+ libpq_gettext("\tIs the server running on that host and accepting TCP/IP connections?\n"));
+}
+
+/*
+ * 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("%s(%s) failed: %s\n"),
+ "setsockopt",
+ 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("%s(%s) failed: %s\n"),
+ "setsockopt",
+ "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("%s(%s) failed: %s\n"),
+ "setsockopt",
+ "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("%s(%s) failed: error code %d\n"),
+ "WSAIoctl", "SIO_KEEPALIVE_VALS",
+ 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("%s(%s) failed: %s\n"),
+ "setsockopt",
+ "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())
+ {
+ appendPQExpBufferStr(&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;
+
+ /*
+ * 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;
+
+ /* Also reset the target_server_type state if needed */
+ if (conn->target_server_type == SERVER_TYPE_PREFER_STANDBY_PASS2)
+ conn->target_server_type = SERVER_TYPE_PREFER_STANDBY;
+
+ /*
+ * 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:
+ 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);
+ }
+}
+
+/* ----------------
+ * 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;
+
+ 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:
+ case CONNECTION_CHECK_WRITABLE:
+ case CONNECTION_CONSUME:
+ case CONNECTION_CHECK_STANDBY:
+ {
+ /* 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;
+
+ /* Special cases: proceed without waiting. */
+ case CONNECTION_SSL_STARTUP:
+ case CONNECTION_NEEDED:
+ case CONNECTION_GSS_STARTUP:
+ case CONNECTION_CHECK_TARGET:
+ 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)
+ conn->whichhost++;
+ else
+ {
+ /*
+ * Oops, no more hosts.
+ *
+ * If we are trying to connect in "prefer-standby" mode, then drop
+ * the standby requirement and start over.
+ *
+ * Otherwise, an appropriate error message is already set up, so
+ * we just need to set the right status.
+ */
+ if (conn->target_server_type == SERVER_TYPE_PREFER_STANDBY &&
+ conn->nconnhost > 0)
+ {
+ conn->target_server_type = SERVER_TYPE_PREFER_STANDBY_PASS2;
+ conn->whichhost = 0;
+ }
+ else
+ goto error_return;
+ }
+
+ /* 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;
+ conn->pipelineStatus = PQ_PIPELINE_OFF;
+ 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 use later */
+ memcpy(&conn->raddr.addr, addr_cur->ai_addr,
+ addr_cur->ai_addrlen);
+ conn->raddr.salen = addr_cur->ai_addrlen;
+
+ /*
+ * Set connip, too. Note we purposely ignore strdup
+ * failure; not a big problem if it fails.
+ */
+ if (conn->connip != NULL)
+ {
+ free(conn->connip);
+ conn->connip = NULL;
+ }
+ getHostaddr(conn, host_addr, NI_MAXHOST);
+ if (host_addr[0])
+ conn->connip = strdup(host_addr);
+
+ /* Try to create the socket */
+ conn->sock = socket(addr_cur->ai_family, SOCK_STREAM, 0);
+ if (conn->sock == PGINVALID_SOCKET)
+ {
+ int errorno = SOCK_ERRNO;
+
+ /*
+ * 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;
+ }
+ emitHostIdentityInfo(conn, host_addr);
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("could not create socket: %s\n"),
+ SOCK_STRERROR(errorno, sebuf, sizeof(sebuf)));
+ goto error_return;
+ }
+
+ /*
+ * Once we've identified a target address, all errors
+ * except the preceding socket()-failure case should be
+ * prefixed with host-identity information. (If the
+ * connection succeeds, the contents of conn->errorMessage
+ * won't matter, so this is harmless.)
+ */
+ emitHostIdentityInfo(conn, host_addr);
+
+ /*
+ * 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("%s(%s) failed: %s\n"),
+ "setsockopt",
+ "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
+
+ /*
+ * Enable the libcrypto callbacks before checking if SSL needs
+ * to be done. This is done before sending the startup packet
+ * as depending on the type of authentication done, like MD5
+ * or SCRAM that use cryptohashes, the callbacks would be
+ * required even without a SSL connection
+ */
+ if (pqsecure_initialize(conn, false, true) < 0)
+ goto error_return;
+
+ /*
+ * 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.
+ */
+ startpacket = pqBuildStartupPacket3(conn, &packetlen,
+ EnvironmentOptions);
+ if (!startpacket)
+ {
+ appendPQExpBufferStr(&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. The crypto
+ * state has already been set if libpq took care of
+ * doing that, so there is no need to make that happen
+ * again.
+ */
+ if (pqsecure_initialize(conn, true, false) != 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)
+ {
+ /*
+ * At this point we should have no data already buffered.
+ * If we do, it was received before we performed the SSL
+ * handshake, so it wasn't encrypted and indeed may have
+ * been injected by a man-in-the-middle.
+ */
+ if (conn->inCursor != conn->inEnd)
+ {
+ appendPQExpBufferStr(&conn->errorMessage,
+ libpq_gettext("received unencrypted data after SSL response\n"));
+ goto error_return;
+ }
+
+ /* 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)
+ {
+ /*
+ * At this point we should have no data already buffered.
+ * If we do, it was received before we performed the GSS
+ * handshake, so it wasn't encrypted and indeed may have
+ * been injected by a man-in-the-middle.
+ */
+ if (conn->inCursor != conn->inEnd)
+ {
+ appendPQExpBufferStr(&conn->errorMessage,
+ libpq_gettext("received unencrypted data after GSSAPI encryption response\n"));
+ goto error_return;
+ }
+
+ /* 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;
+ }
+
+ /* Read message length word */
+ if (pqGetInt(&msgLength, 4, conn))
+ {
+ /* We'll come back when there is more data */
+ return PGRES_POLLING_READING;
+ }
+
+ /*
+ * 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. (Before version 14, the
+ * server also used the old protocol for errors that happened
+ * before processing the startup packet.)
+ */
+ 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;
+
+ /*
+ * Before 7.2, the postmaster didn't always end its
+ * messages with a newline, so add one if needed to
+ * conform to libpq conventions.
+ */
+ if (conn->errorMessage.len == 0 ||
+ conn->errorMessage.data[conn->errorMessage.len - 1] != '\n')
+ {
+ appendPQExpBufferChar(&conn->errorMessage, '\n');
+ }
+
+ goto error_return;
+ }
+
+ /*
+ * Can't process if message body isn't all here yet.
+ */
+ 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 (pqGetErrorNotice3(conn, true))
+ {
+ /* 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;
+
+ /*
+ * If error is "cannot connect now", try the next host if
+ * any (but we don't want to consider additional addresses
+ * for this host, nor is there much point in changing SSL
+ * or GSS mode). This is helpful when dealing with
+ * standby servers that might not be in hot-standby state.
+ */
+ if (strcmp(conn->last_sqlstate,
+ ERRCODE_CANNOT_CONNECT_NOW) == 0)
+ {
+ conn->try_next_host = true;
+ goto keep_going;
+ }
+
+ /* 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;
+
+ /*
+ * 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);
+
+ /* 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;
+ }
+
+ /* Almost there now ... */
+ conn->status = CONNECTION_CHECK_TARGET;
+ goto keep_going;
+ }
+
+ case CONNECTION_CHECK_TARGET:
+ {
+ /*
+ * If a read-write, read-only, primary, or standby connection
+ * is required, see if we have one.
+ */
+ if (conn->target_server_type == SERVER_TYPE_READ_WRITE ||
+ conn->target_server_type == SERVER_TYPE_READ_ONLY)
+ {
+ bool read_only_server;
+
+ /*
+ * If the server didn't report
+ * "default_transaction_read_only" or "in_hot_standby" at
+ * startup, we must determine its state by sending the
+ * query "SHOW transaction_read_only". This GUC exists in
+ * all server versions that support 3.0 protocol.
+ */
+ if (conn->default_transaction_read_only == PG_BOOL_UNKNOWN ||
+ conn->in_hot_standby == PG_BOOL_UNKNOWN)
+ {
+ /*
+ * We use PQsendQueryContinue so that
+ * conn->errorMessage does not get cleared. We need
+ * to preserve any error messages related to previous
+ * hosts we have tried and failed to connect to.
+ */
+ conn->status = CONNECTION_OK;
+ if (!PQsendQueryContinue(conn,
+ "SHOW transaction_read_only"))
+ goto error_return;
+ /* We'll return to this state when we have the answer */
+ conn->status = CONNECTION_CHECK_WRITABLE;
+ return PGRES_POLLING_READING;
+ }
+
+ /* OK, we can make the test */
+ read_only_server =
+ (conn->default_transaction_read_only == PG_BOOL_YES ||
+ conn->in_hot_standby == PG_BOOL_YES);
+
+ if ((conn->target_server_type == SERVER_TYPE_READ_WRITE) ?
+ read_only_server : !read_only_server)
+ {
+ /* Wrong server state, reject and try the next host */
+ if (conn->target_server_type == SERVER_TYPE_READ_WRITE)
+ appendPQExpBufferStr(&conn->errorMessage,
+ libpq_gettext("session is read-only\n"));
+ else
+ appendPQExpBufferStr(&conn->errorMessage,
+ libpq_gettext("session is not read-only\n"));
+
+ /* 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;
+ }
+ }
+ else if (conn->target_server_type == SERVER_TYPE_PRIMARY ||
+ conn->target_server_type == SERVER_TYPE_STANDBY ||
+ conn->target_server_type == SERVER_TYPE_PREFER_STANDBY)
+ {
+ /*
+ * If the server didn't report "in_hot_standby" at
+ * startup, we must determine its state by sending the
+ * query "SELECT pg_catalog.pg_is_in_recovery()". Servers
+ * before 9.0 don't have that function, but by the same
+ * token they don't have any standby mode, so we may just
+ * assume the result.
+ */
+ if (conn->sversion < 90000)
+ conn->in_hot_standby = PG_BOOL_NO;
+
+ if (conn->in_hot_standby == PG_BOOL_UNKNOWN)
+ {
+ /*
+ * We use PQsendQueryContinue so that
+ * conn->errorMessage does not get cleared. We need
+ * to preserve any error messages related to previous
+ * hosts we have tried and failed to connect to.
+ */
+ conn->status = CONNECTION_OK;
+ if (!PQsendQueryContinue(conn,
+ "SELECT pg_catalog.pg_is_in_recovery()"))
+ goto error_return;
+ /* We'll return to this state when we have the answer */
+ conn->status = CONNECTION_CHECK_STANDBY;
+ return PGRES_POLLING_READING;
+ }
+
+ /* OK, we can make the test */
+ if ((conn->target_server_type == SERVER_TYPE_PRIMARY) ?
+ (conn->in_hot_standby == PG_BOOL_YES) :
+ (conn->in_hot_standby == PG_BOOL_NO))
+ {
+ /* Wrong server state, reject and try the next host */
+ if (conn->target_server_type == SERVER_TYPE_PRIMARY)
+ appendPQExpBufferStr(&conn->errorMessage,
+ libpq_gettext("server is in hot standby mode\n"));
+ else
+ appendPQExpBufferStr(&conn->errorMessage,
+ libpq_gettext("server is not in hot standby mode\n"));
+
+ /* 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;
+ }
+ }
+
+ /* We can release the address list now. */
+ release_conn_addrinfo(conn);
+
+ /*
+ * Contents of conn->errorMessage are no longer interesting
+ * (and it seems some clients expect it to be empty after a
+ * successful connection).
+ */
+ resetPQExpBuffer(&conn->errorMessage);
+
+ /* We are open for business! */
+ conn->status = CONNECTION_OK;
+ return PGRES_POLLING_OK;
+ }
+
+ case CONNECTION_CONSUME:
+ {
+ /*
+ * This state just makes sure the connection is idle after
+ * we've obtained the result of a SHOW or SELECT query. Once
+ * we're clear, return to CONNECTION_CHECK_TARGET state to
+ * decide what to do next. We must transiently set status =
+ * CONNECTION_OK in order to use the result-consuming
+ * subroutines.
+ */
+ conn->status = CONNECTION_OK;
+ if (!PQconsumeInput(conn))
+ goto error_return;
+
+ if (PQisBusy(conn))
+ {
+ conn->status = CONNECTION_CONSUME;
+ return PGRES_POLLING_READING;
+ }
+
+ /* Call PQgetResult() again until we get a NULL result */
+ res = PQgetResult(conn);
+ if (res != NULL)
+ {
+ PQclear(res);
+ conn->status = CONNECTION_CONSUME;
+ return PGRES_POLLING_READING;
+ }
+
+ conn->status = CONNECTION_CHECK_TARGET;
+ goto keep_going;
+ }
+
+ case CONNECTION_CHECK_WRITABLE:
+ {
+ /*
+ * Waiting for result of "SHOW transaction_read_only". We
+ * must transiently set status = CONNECTION_OK in order to use
+ * the result-consuming subroutines.
+ */
+ conn->status = CONNECTION_OK;
+ if (!PQconsumeInput(conn))
+ goto error_return;
+
+ if (PQisBusy(conn))
+ {
+ conn->status = CONNECTION_CHECK_WRITABLE;
+ return PGRES_POLLING_READING;
+ }
+
+ res = PQgetResult(conn);
+ if (res && PQresultStatus(res) == PGRES_TUPLES_OK &&
+ PQntuples(res) == 1)
+ {
+ char *val = PQgetvalue(res, 0, 0);
+
+ /*
+ * "transaction_read_only = on" proves that at least one
+ * of default_transaction_read_only and in_hot_standby is
+ * on, but we don't actually know which. We don't care
+ * though for the purpose of identifying a read-only
+ * session, so satisfy the CONNECTION_CHECK_TARGET code by
+ * claiming they are both on. On the other hand, if it's
+ * a read-write session, they are certainly both off.
+ */
+ if (strncmp(val, "on", 2) == 0)
+ {
+ conn->default_transaction_read_only = PG_BOOL_YES;
+ conn->in_hot_standby = PG_BOOL_YES;
+ }
+ else
+ {
+ conn->default_transaction_read_only = PG_BOOL_NO;
+ conn->in_hot_standby = PG_BOOL_NO;
+ }
+ PQclear(res);
+
+ /* Finish reading messages before continuing */
+ conn->status = CONNECTION_CONSUME;
+ goto keep_going;
+ }
+
+ /* Something went wrong with "SHOW transaction_read_only". */
+ if (res)
+ PQclear(res);
+
+ /* Append error report to conn->errorMessage. */
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("\"%s\" failed\n"),
+ "SHOW transaction_read_only");
+
+ /* Close connection politely. */
+ conn->status = CONNECTION_OK;
+ sendTerminateConn(conn);
+
+ /* Try next host. */
+ conn->try_next_host = true;
+ goto keep_going;
+ }
+
+ case CONNECTION_CHECK_STANDBY:
+ {
+ /*
+ * Waiting for result of "SELECT pg_is_in_recovery()". We
+ * must transiently set status = CONNECTION_OK in order to use
+ * the result-consuming subroutines.
+ */
+ conn->status = CONNECTION_OK;
+ if (!PQconsumeInput(conn))
+ goto error_return;
+
+ if (PQisBusy(conn))
+ {
+ conn->status = CONNECTION_CHECK_STANDBY;
+ return PGRES_POLLING_READING;
+ }
+
+ res = PQgetResult(conn);
+ if (res && PQresultStatus(res) == PGRES_TUPLES_OK &&
+ PQntuples(res) == 1)
+ {
+ char *val = PQgetvalue(res, 0, 0);
+
+ if (strncmp(val, "t", 1) == 0)
+ conn->in_hot_standby = PG_BOOL_YES;
+ else
+ conn->in_hot_standby = PG_BOOL_NO;
+ PQclear(res);
+
+ /* Finish reading messages before continuing */
+ conn->status = CONNECTION_CONSUME;
+ goto keep_going;
+ }
+
+ /* Something went wrong with "SELECT pg_is_in_recovery()". */
+ if (res)
+ PQclear(res);
+
+ /* Append error report to conn->errorMessage. */
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("\"%s\" failed\n"),
+ "SELECT pg_is_in_recovery()");
+
+ /* Close connection politely. */
+ conn->status = CONNECTION_OK;
+ sendTerminateConn(conn);
+
+ /* Try next host. */
+ conn->try_next_host = 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(2, 2), &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->pipelineStatus = PQ_PIPELINE_OFF;
+ conn->xactStatus = PQTRANS_IDLE;
+ conn->options_valid = false;
+ conn->nonblocking = false;
+ conn->client_encoding = PG_SQL_ASCII;
+ conn->std_strings = false; /* unless server says differently */
+ conn->default_transaction_read_only = PG_BOOL_UNKNOWN;
+ conn->in_hot_standby = PG_BOOL_UNKNOWN;
+ conn->verbosity = PQERRORS_DEFAULT;
+ conn->show_context = PQSHOW_CONTEXT_ERRORS;
+ conn->sock = PGINVALID_SOCKET;
+ conn->Pfdebug = NULL;
+
+ /*
+ * 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->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->sslcrldir)
+ free(conn->sslcrldir);
+ if (conn->sslcompression)
+ free(conn->sslcompression);
+ if (conn->sslsni)
+ free(conn->sslsni);
+ 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->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', 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.
+ * Note that this includes clearing conn->errorMessage; we're no longer
+ * interested in any failures associated with the old connection, and we
+ * want a clean slate for any new connection attempt.
+ */
+ pqDropConnection(conn, true);
+ conn->status = CONNECTION_BAD; /* Well, not really _bad_ - just absent */
+ conn->asyncStatus = PGASYNC_IDLE;
+ conn->xactStatus = PQTRANS_IDLE;
+ conn->pipelineStatus = PQ_PIPELINE_OFF;
+ 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;
+ appendPQExpBuffer(&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;
+ appendPQExpBuffer(&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;
+ 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)
+ {
+ /*
+ * We can't invoke strerror here, since it's not signal-safe. Settle
+ * for printing the decimal value of errno. Even that has to be done
+ * the hard way.
+ */
+ int val = SOCK_ERRNO;
+ char buf[32];
+ char *bufp;
+
+ bufp = buf + sizeof(buf) - 1;
+ *bufp = '\0';
+ do
+ {
+ *(--bufp) = (val % 10) + '0';
+ val /= 10;
+ } while (val > 0);
+ bufp -= 6;
+ memcpy(bufp, "error ", 6);
+ strncat(errbuf, bufp, 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.
+ */
+int
+pqPacketSend(PGconn *conn, char pack_type,
+ const void *buf, size_t buf_len)
+{
+ /* Start the message. */
+ if (pqPutMsgStart(pack_type, 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 appended to *errorMessage 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)
+ {
+ appendPQExpBufferStr(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)
+ {
+ appendPQExpBuffer(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) == '?')
+ {
+ appendPQExpBuffer(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) == '?')
+ {
+ appendPQExpBuffer(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) == '?')
+ {
+ appendPQExpBuffer(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) == '?')
+ {
+ appendPQExpBuffer(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)
+ {
+ appendPQExpBuffer(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)
+ {
+ appendPQExpBuffer(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
+ {
+ appendPQExpBuffer(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)
+ {
+ appendPQExpBufferStr(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);
+ appendPQExpBuffer(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)
+ {
+ appendPQExpBufferStr(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 */
+ appendPQExpBufferStr(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)
+ {
+ appendPQExpBufferStr(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)
+ {
+ appendPQExpBufferStr(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)
+ {
+ appendPQExpBufferStr(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))
+ {
+ appendPQExpBuffer(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))
+ {
+ appendPQExpBuffer(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)
+ {
+ appendPQExpBufferStr(errorMessage,
+ libpq_gettext("out of memory\n"));
+ free(result);
+ return 3;
+ }
+ }
+ found_keyword = true;
+ break;
+ }
+ }
+ if (!found_keyword)
+ {
+ appendPQExpBuffer(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)
+ {
+ appendPQExpBufferStr(errorMessage,
+ libpq_gettext("unterminated quoted string in connection info string\n"));
+ return 3;
+ }
+
+ return 0;
+}
+
+#endif /* USE_LDAP */
+
+/*
+ * 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)
+ {
+ appendPQExpBuffer(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 result = 0,
+ linenr = 0,
+ i;
+ FILE *f;
+ char *line;
+ char buf[1024];
+
+ *group_found = false;
+
+ f = fopen(serviceFile, "r");
+ if (f == NULL)
+ {
+ appendPQExpBuffer(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)
+ {
+ appendPQExpBuffer(errorMessage,
+ libpq_gettext("line %d too long in service file \"%s\"\n"),
+ linenr,
+ serviceFile);
+ result = 2;
+ goto exit;
+ }
+
+ /* 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)
+ {
+ /* end of desired group reached; return success */
+ goto exit;
+ }
+
+ 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:
+ goto exit;
+ case 1:
+ case 3:
+ result = 3;
+ goto exit;
+ case 2:
+ continue;
+ }
+ }
+#endif
+
+ key = line;
+ val = strchr(line, '=');
+ if (val == NULL)
+ {
+ appendPQExpBuffer(errorMessage,
+ libpq_gettext("syntax error in service file \"%s\", line %d\n"),
+ serviceFile,
+ linenr);
+ result = 3;
+ goto exit;
+ }
+ *val++ = '\0';
+
+ if (strcmp(key, "service") == 0)
+ {
+ appendPQExpBuffer(errorMessage,
+ libpq_gettext("nested service specifications not supported in service file \"%s\", line %d\n"),
+ serviceFile,
+ linenr);
+ result = 3;
+ goto exit;
+ }
+
+ /*
+ * 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)
+ {
+ appendPQExpBufferStr(errorMessage,
+ libpq_gettext("out of memory\n"));
+ result = 3;
+ goto exit;
+ }
+ found_keyword = true;
+ break;
+ }
+ }
+
+ if (!found_keyword)
+ {
+ appendPQExpBuffer(errorMessage,
+ libpq_gettext("syntax error in service file \"%s\", line %d\n"),
+ serviceFile,
+ linenr);
+ result = 3;
+ goto exit;
+ }
+ }
+ }
+ }
+
+exit:
+ fclose(f);
+
+ return result;
+}
+
+
+/*
+ * 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)
+ {
+ appendPQExpBufferStr(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 added to 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)
+ {
+ appendPQExpBufferStr(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 != '=')
+ {
+ appendPQExpBuffer(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')
+ {
+ appendPQExpBufferStr(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
+ * appended to 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)
+ {
+ appendPQExpBuffer(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)
+ {
+ appendPQExpBufferStr(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)
+ {
+ appendPQExpBufferStr(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)
+ appendPQExpBufferStr(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)
+ appendPQExpBufferStr(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)
+ appendPQExpBufferStr(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))
+ {
+ appendPQExpBufferStr(errorMessage,
+ libpq_gettext("out of memory\n"));
+ goto cleanup;
+ }
+
+ /* need a modifiable copy of the input URI */
+ buf = strdup(uri);
+ if (buf == NULL)
+ {
+ appendPQExpBufferStr(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 */
+ appendPQExpBuffer(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)
+ {
+ appendPQExpBuffer(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)
+ {
+ appendPQExpBuffer(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 != ',')
+ {
+ appendPQExpBuffer(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;
+ int oldmsglen;
+
+ /*
+ * Scan the params string for '=' and '&', marking the end of keyword
+ * and value respectively.
+ */
+ for (;;)
+ {
+ if (*p == '=')
+ {
+ /* Was there '=' already? */
+ if (value != NULL)
+ {
+ appendPQExpBuffer(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)
+ {
+ appendPQExpBuffer(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.
+ */
+ oldmsglen = errorMessage->len;
+ if (!conninfo_storeval(connOptions, keyword, value,
+ errorMessage, true, false))
+ {
+ /* Insert generic message if conninfo_storeval didn't give one. */
+ if (errorMessage->len == oldmsglen)
+ appendPQExpBuffer(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)
+ {
+ appendPQExpBufferStr(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)))
+ {
+ appendPQExpBuffer(errorMessage,
+ libpq_gettext("invalid percent-encoded token: \"%s\"\n"),
+ str);
+ free(buf);
+ return NULL;
+ }
+
+ c = (hi << 4) | lo;
+ if (c == 0)
+ {
+ appendPQExpBuffer(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)
+ appendPQExpBuffer(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)
+ {
+ appendPQExpBufferStr(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,
+ * and we prefer not to trash the conn's errorMessage.
+ */
+ 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 "";
+}
+
+/*
+ * No longer does anything, but the function remains for API backwards
+ * compatibility.
+ */
+char *
+PQtty(const PGconn *conn)
+{
+ if (!conn)
+ return NULL;
+ return "";
+}
+
+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");
+
+ /*
+ * The errorMessage buffer might be marked "broken" due to having
+ * previously failed to allocate enough memory for the message. In that
+ * case, tell the application we ran out of memory.
+ */
+ if (PQExpBufferBroken(&conn->errorMessage))
+ return libpq_gettext("out of memory\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;
+}
+
+PGpipelineStatus
+PQpipelineStatus(const PGconn *conn)
+{
+ if (!conn)
+ return PQ_PIPELINE_OFF;
+
+ return conn->pipelineStatus;
+}
+
+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
+ {
+ /*
+ * We rely on the backend to report the parameter value, and we'll
+ * change state at that time.
+ */
+ 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;
+}
+
+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_unixsock_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 protocol 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..0920370
--- /dev/null
+++ b/src/interfaces/libpq/fe-exec.c
@@ -0,0 +1,4451 @@
+/*-------------------------------------------------------------------------
+ *
+ * fe-exec.c
+ * functions related to sending a query down to the backend
+ *
+ * Portions Copyright (c) 1996-2021, 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",
+ "PGRES_PIPELINE_SYNC",
+ "PGRES_PIPELINE_ABORTED"
+};
+
+/*
+ * 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 int PQsendQueryInternal(PGconn *conn, const char *query, bool newQuery);
+static bool PQsendQueryStart(PGconn *conn, bool newQuery);
+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);
+static void pqPipelineProcessQueue(PGconn *conn);
+static int pqPipelineFlush(PGconn *conn);
+
+
+/* ----------------
+ * 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);
+ 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, PQExpBuffer errorMessage)
+{
+ char *msg;
+
+ if (!res)
+ return;
+
+ /*
+ * We handle two OOM scenarios here. The errorMessage buffer might be
+ * marked "broken" due to having previously failed to allocate enough
+ * memory for the message, or it might be fine but pqResultStrdup fails
+ * and returns NULL. In either case, just make res->errMsg point directly
+ * at a constant "out of memory" string.
+ */
+ if (!PQExpBufferBroken(errorMessage))
+ msg = pqResultStrdup(res, errorMessage->data);
+ else
+ msg = NULL;
+ if (msg)
+ res->errMsg = msg;
+ else
+ res->errMsg = libpq_gettext("out of memory\n");
+}
+
+/*
+ * 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.
+ */
+void
+pqSaveErrorResult(PGconn *conn)
+{
+ pqClearAsyncResult(conn);
+ conn->result = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR);
+}
+
+/*
+ * As above, after appending 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)
+{
+ /*
+ * If write_err_msg is null because of previous strdup failure, do what we
+ * can. (It's likely our machinations here will get OOM failures as well,
+ * but might as well try.)
+ */
+ if (conn->write_err_msg)
+ {
+ appendPQExpBufferStr(&conn->errorMessage, conn->write_err_msg);
+ /* Avoid possibly appending the same message twice */
+ conn->write_err_msg[0] = '\0';
+ }
+ else
+ appendPQExpBufferStr(&conn->errorMessage,
+ libpq_gettext("write to server failed\n"));
+
+ pqSaveErrorResult(conn);
+}
+
+/*
+ * 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.
+ */
+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);
+
+ /*
+ * 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, substitute "out of memory", as in pqSetResultError.
+ */
+ res->errMsg = (char *) pqResultAlloc(res, strlen(msgBuf) + 2, false);
+ if (res->errMsg)
+ sprintf(res->errMsg, "%s\n", msgBuf);
+ else
+ res->errMsg = libpq_gettext("out of memory\n");
+
+ /*
+ * 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;
+
+ /*
+ * 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;
+ }
+
+ /*
+ * Save values of settings that are of interest to libpq in fields of the
+ * PGconn object. We keep client_encoding and standard_conforming_strings
+ * 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)
+ {
+ /* We convert the server version to numeric form. */
+ 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 */
+ }
+ else if (strcmp(name, "default_transaction_read_only") == 0)
+ {
+ conn->default_transaction_read_only =
+ (strcmp(value, "on") == 0) ? PG_BOOL_YES : PG_BOOL_NO;
+ }
+ else if (strcmp(name, "in_hot_standby") == 0)
+ {
+ conn->in_hot_standby =
+ (strcmp(value, "on") == 0) ? PG_BOOL_YES : PG_BOOL_NO;
+ }
+}
+
+
+/*
+ * 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_MORE;
+ }
+
+ return 1;
+
+fail:
+ /* release locally allocated PGresult, if we made one */
+ if (res != conn->result)
+ PQclear(res);
+ return 0;
+}
+
+
+/*
+ * pqAllocCmdQueueEntry
+ * Get a command queue entry for caller to fill.
+ *
+ * If the recycle queue has a free element, that is returned; if not, a
+ * fresh one is allocated. Caller is responsible for adding it to the
+ * command queue (pqAppendCmdQueueEntry) once the struct is filled in, or
+ * releasing the memory (pqRecycleCmdQueueEntry) if an error occurs.
+ *
+ * If allocation fails, sets the error message and returns NULL.
+ */
+static PGcmdQueueEntry *
+pqAllocCmdQueueEntry(PGconn *conn)
+{
+ PGcmdQueueEntry *entry;
+
+ if (conn->cmd_queue_recycle == NULL)
+ {
+ entry = (PGcmdQueueEntry *) malloc(sizeof(PGcmdQueueEntry));
+ if (entry == NULL)
+ {
+ appendPQExpBufferStr(&conn->errorMessage,
+ libpq_gettext("out of memory\n"));
+ return NULL;
+ }
+ }
+ else
+ {
+ entry = conn->cmd_queue_recycle;
+ conn->cmd_queue_recycle = entry->next;
+ }
+ entry->next = NULL;
+ entry->query = NULL;
+
+ return entry;
+}
+
+/*
+ * pqAppendCmdQueueEntry
+ * Append a caller-allocated entry to the command queue, and update
+ * conn->asyncStatus to account for it.
+ *
+ * The query itself must already have been put in the output buffer by the
+ * caller.
+ */
+static void
+pqAppendCmdQueueEntry(PGconn *conn, PGcmdQueueEntry *entry)
+{
+ Assert(entry->next == NULL);
+
+ if (conn->cmd_queue_head == NULL)
+ conn->cmd_queue_head = entry;
+ else
+ conn->cmd_queue_tail->next = entry;
+
+ conn->cmd_queue_tail = entry;
+
+ switch (conn->pipelineStatus)
+ {
+ case PQ_PIPELINE_OFF:
+ case PQ_PIPELINE_ON:
+
+ /*
+ * When not in pipeline aborted state, if there's a result ready
+ * to be consumed, let it be so (that is, don't change away from
+ * READY or READY_MORE); otherwise set us busy to wait for
+ * something to arrive from the server.
+ */
+ if (conn->asyncStatus == PGASYNC_IDLE)
+ conn->asyncStatus = PGASYNC_BUSY;
+ break;
+
+ case PQ_PIPELINE_ABORTED:
+
+ /*
+ * In aborted pipeline state, we don't expect anything from the
+ * server (since we don't send any queries that are queued).
+ * Therefore, if IDLE then do what PQgetResult would do to let
+ * itself consume commands from the queue; if we're in any other
+ * state, we don't have to do anything.
+ */
+ if (conn->asyncStatus == PGASYNC_IDLE ||
+ conn->asyncStatus == PGASYNC_PIPELINE_IDLE)
+ {
+ resetPQExpBuffer(&conn->errorMessage);
+ pqPipelineProcessQueue(conn);
+ }
+ break;
+ }
+}
+
+/*
+ * pqRecycleCmdQueueEntry
+ * Push a command queue entry onto the freelist.
+ */
+static void
+pqRecycleCmdQueueEntry(PGconn *conn, PGcmdQueueEntry *entry)
+{
+ if (entry == NULL)
+ return;
+
+ /* recyclable entries should not have a follow-on command */
+ Assert(entry->next == NULL);
+
+ if (entry->query)
+ {
+ free(entry->query);
+ entry->query = NULL;
+ }
+
+ entry->next = conn->cmd_queue_recycle;
+ conn->cmd_queue_recycle = entry;
+}
+
+
+/*
+ * PQsendQuery
+ * Submit a query, but don't wait for it to finish
+ *
+ * Returns: 1 if successfully submitted
+ * 0 if error (conn->errorMessage is set)
+ *
+ * PQsendQueryContinue is a non-exported version that behaves identically
+ * except that it doesn't reset conn->errorMessage.
+ */
+int
+PQsendQuery(PGconn *conn, const char *query)
+{
+ return PQsendQueryInternal(conn, query, true);
+}
+
+int
+PQsendQueryContinue(PGconn *conn, const char *query)
+{
+ return PQsendQueryInternal(conn, query, false);
+}
+
+static int
+PQsendQueryInternal(PGconn *conn, const char *query, bool newQuery)
+{
+ PGcmdQueueEntry *entry = NULL;
+ PGcmdQueueEntry *entry2 = NULL;
+
+ if (!PQsendQueryStart(conn, newQuery))
+ return 0;
+
+ /* check the argument */
+ if (!query)
+ {
+ appendPQExpBufferStr(&conn->errorMessage,
+ libpq_gettext("command string is a null pointer\n"));
+ return 0;
+ }
+
+ entry = pqAllocCmdQueueEntry(conn);
+ if (entry == NULL)
+ return 0; /* error msg already set */
+ if (conn->pipelineStatus != PQ_PIPELINE_OFF)
+ {
+ entry2 = pqAllocCmdQueueEntry(conn);
+ if (entry2 == NULL)
+ goto sendFailed;
+ }
+
+ /* Send the query message(s) */
+ if (conn->pipelineStatus == PQ_PIPELINE_OFF)
+ {
+ /* construct the outgoing Query message */
+ if (pqPutMsgStart('Q', conn) < 0 ||
+ pqPuts(query, conn) < 0 ||
+ pqPutMsgEnd(conn) < 0)
+ {
+ /* error message should be set up already */
+ pqRecycleCmdQueueEntry(conn, entry);
+ return 0;
+ }
+
+ /* remember we are using simple query protocol */
+ entry->queryclass = PGQUERY_SIMPLE;
+ /* and remember the query text too, if possible */
+ entry->query = strdup(query);
+ }
+ else
+ {
+ /*
+ * In pipeline mode we cannot use the simple protocol, so we send
+ * Parse, Bind, Describe Portal, Execute, Close Portal (with the
+ * unnamed portal).
+ */
+ if (pqPutMsgStart('P', conn) < 0 ||
+ pqPuts("", conn) < 0 ||
+ pqPuts(query, conn) < 0 ||
+ pqPutInt(0, 2, conn) < 0 ||
+ pqPutMsgEnd(conn) < 0)
+ goto sendFailed;
+ if (pqPutMsgStart('B', conn) < 0 ||
+ pqPuts("", conn) < 0 ||
+ pqPuts("", conn) < 0 ||
+ pqPutInt(0, 2, conn) < 0 ||
+ pqPutInt(0, 2, conn) < 0 ||
+ pqPutInt(0, 2, conn) < 0 ||
+ pqPutMsgEnd(conn) < 0)
+ goto sendFailed;
+ if (pqPutMsgStart('D', conn) < 0 ||
+ pqPutc('P', conn) < 0 ||
+ pqPuts("", conn) < 0 ||
+ pqPutMsgEnd(conn) < 0)
+ goto sendFailed;
+ if (pqPutMsgStart('E', conn) < 0 ||
+ pqPuts("", conn) < 0 ||
+ pqPutInt(0, 4, conn) < 0 ||
+ pqPutMsgEnd(conn) < 0)
+ goto sendFailed;
+ if (pqPutMsgStart('C', conn) < 0 ||
+ pqPutc('P', conn) < 0 ||
+ pqPuts("", conn) < 0 ||
+ pqPutMsgEnd(conn) < 0)
+ goto sendFailed;
+
+ entry->queryclass = PGQUERY_EXTENDED;
+ entry->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 (pqPipelineFlush(conn) < 0)
+ goto sendFailed;
+
+ /* OK, it's launched! */
+ pqAppendCmdQueueEntry(conn, entry);
+
+ /*
+ * When pipeline mode is in use, we need a second entry in the command
+ * queue to represent Close Portal message. This allows us later to wait
+ * for the CloseComplete message to be received before getting in IDLE
+ * state.
+ */
+ if (conn->pipelineStatus != PQ_PIPELINE_OFF)
+ {
+ entry2->queryclass = PGQUERY_CLOSE;
+ entry2->query = NULL;
+ pqAppendCmdQueueEntry(conn, entry2);
+ }
+
+ return 1;
+
+sendFailed:
+ pqRecycleCmdQueueEntry(conn, entry);
+ pqRecycleCmdQueueEntry(conn, entry2);
+ /* error message should be set up already */
+ return 0;
+}
+
+/*
+ * PQsendQueryParams
+ * Like PQsendQuery, but use extended query protocol 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, true))
+ return 0;
+
+ /* check the arguments */
+ if (!command)
+ {
+ appendPQExpBufferStr(&conn->errorMessage,
+ libpq_gettext("command string is a null pointer\n"));
+ return 0;
+ }
+ if (nParams < 0 || nParams > PQ_QUERY_PARAM_MAX_LIMIT)
+ {
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("number of parameters must be between 0 and %d\n"),
+ PQ_QUERY_PARAM_MAX_LIMIT);
+ 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)
+{
+ PGcmdQueueEntry *entry = NULL;
+
+ if (!PQsendQueryStart(conn, true))
+ return 0;
+
+ /* check the arguments */
+ if (!stmtName)
+ {
+ appendPQExpBufferStr(&conn->errorMessage,
+ libpq_gettext("statement name is a null pointer\n"));
+ return 0;
+ }
+ if (!query)
+ {
+ appendPQExpBufferStr(&conn->errorMessage,
+ libpq_gettext("command string is a null pointer\n"));
+ return 0;
+ }
+ if (nParams < 0 || nParams > PQ_QUERY_PARAM_MAX_LIMIT)
+ {
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("number of parameters must be between 0 and %d\n"),
+ PQ_QUERY_PARAM_MAX_LIMIT);
+ return 0;
+ }
+
+ entry = pqAllocCmdQueueEntry(conn);
+ if (entry == NULL)
+ return 0; /* error msg already set */
+
+ /* construct the Parse message */
+ if (pqPutMsgStart('P', 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;
+
+ /* Add a Sync, unless in pipeline mode. */
+ if (conn->pipelineStatus == PQ_PIPELINE_OFF)
+ {
+ if (pqPutMsgStart('S', conn) < 0 ||
+ pqPutMsgEnd(conn) < 0)
+ goto sendFailed;
+ }
+
+ /* remember we are doing just a Parse */
+ entry->queryclass = PGQUERY_PREPARE;
+
+ /* and remember the query text too, if possible */
+ /* if insufficient memory, query just winds up NULL */
+ entry->query = strdup(query);
+
+ /*
+ * Give the data a push (in pipeline mode, only if we're past the size
+ * threshold). In nonblock mode, don't complain if we're unable to send
+ * it all; PQgetResult() will do any additional flushing needed.
+ */
+ if (pqPipelineFlush(conn) < 0)
+ goto sendFailed;
+
+ /* OK, it's launched! */
+ pqAppendCmdQueueEntry(conn, entry);
+
+ return 1;
+
+sendFailed:
+ pqRecycleCmdQueueEntry(conn, entry);
+ /* error message should be set up already */
+ return 0;
+}
+
+/*
+ * PQsendQueryPrepared
+ * Like PQsendQuery, but execute a previously prepared statement,
+ * using extended query protocol 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, true))
+ return 0;
+
+ /* check the arguments */
+ if (!stmtName)
+ {
+ appendPQExpBufferStr(&conn->errorMessage,
+ libpq_gettext("statement name is a null pointer\n"));
+ return 0;
+ }
+ if (nParams < 0 || nParams > PQ_QUERY_PARAM_MAX_LIMIT)
+ {
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("number of parameters must be between 0 and %d\n"),
+ PQ_QUERY_PARAM_MAX_LIMIT);
+ return 0;
+ }
+
+ return PQsendQueryGuts(conn,
+ NULL, /* no command to parse */
+ stmtName,
+ nParams,
+ NULL, /* no param types */
+ paramValues,
+ paramLengths,
+ paramFormats,
+ resultFormat);
+}
+
+/*
+ * PQsendQueryStart
+ * Common startup code for PQsendQuery and sibling routines
+ */
+static bool
+PQsendQueryStart(PGconn *conn, bool newQuery)
+{
+ if (!conn)
+ return false;
+
+ /*
+ * If this is the beginning of a query cycle, reset the error buffer.
+ */
+ if (newQuery)
+ resetPQExpBuffer(&conn->errorMessage);
+
+ /* Don't try to send if we know there's no live connection. */
+ if (conn->status != CONNECTION_OK)
+ {
+ appendPQExpBufferStr(&conn->errorMessage,
+ libpq_gettext("no connection to the server\n"));
+ return false;
+ }
+
+ /* Can't send while already busy, either, unless enqueuing for later */
+ if (conn->asyncStatus != PGASYNC_IDLE &&
+ conn->pipelineStatus == PQ_PIPELINE_OFF)
+ {
+ appendPQExpBufferStr(&conn->errorMessage,
+ libpq_gettext("another command is already in progress\n"));
+ return false;
+ }
+
+ if (conn->pipelineStatus != PQ_PIPELINE_OFF)
+ {
+ /*
+ * When enqueuing commands we don't change much of the connection
+ * state since it's already in use for the current command. The
+ * connection state will get updated when pqPipelineProcessQueue()
+ * advances to start processing the queued message.
+ *
+ * Just make sure we can safely enqueue given the current connection
+ * state. We can enqueue behind another queue item, or behind a
+ * non-queue command (one that sends its own sync), but we can't
+ * enqueue if the connection is in a copy state.
+ */
+ switch (conn->asyncStatus)
+ {
+ case PGASYNC_IDLE:
+ case PGASYNC_PIPELINE_IDLE:
+ case PGASYNC_READY:
+ case PGASYNC_READY_MORE:
+ case PGASYNC_BUSY:
+ /* ok to queue */
+ break;
+
+ case PGASYNC_COPY_IN:
+ case PGASYNC_COPY_OUT:
+ case PGASYNC_COPY_BOTH:
+ appendPQExpBufferStr(&conn->errorMessage,
+ libpq_gettext("cannot queue commands during COPY\n"));
+ return false;
+ }
+ }
+ else
+ {
+ /*
+ * This command's results will come in immediately. 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 sending a query with extended query protocol
+ * 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;
+ PGcmdQueueEntry *entry;
+
+ entry = pqAllocCmdQueueEntry(conn);
+ if (entry == NULL)
+ return 0; /* error msg already set */
+
+ /*
+ * We will send Parse (if needed), Bind, Describe Portal, Execute, Sync
+ * (if not in pipeline mode), using specified statement name and the
+ * unnamed portal.
+ */
+
+ if (command)
+ {
+ /* construct the Parse message */
+ if (pqPutMsgStart('P', 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', 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
+ {
+ appendPQExpBufferStr(&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', conn) < 0 ||
+ pqPutc('P', conn) < 0 ||
+ pqPuts("", conn) < 0 ||
+ pqPutMsgEnd(conn) < 0)
+ goto sendFailed;
+
+ /* construct the Execute message */
+ if (pqPutMsgStart('E', conn) < 0 ||
+ pqPuts("", conn) < 0 ||
+ pqPutInt(0, 4, conn) < 0 ||
+ pqPutMsgEnd(conn) < 0)
+ goto sendFailed;
+
+ /* construct the Sync message if not in pipeline mode */
+ if (conn->pipelineStatus == PQ_PIPELINE_OFF)
+ {
+ if (pqPutMsgStart('S', conn) < 0 ||
+ pqPutMsgEnd(conn) < 0)
+ goto sendFailed;
+ }
+
+ /* remember we are using extended query protocol */
+ entry->queryclass = PGQUERY_EXTENDED;
+
+ /* and remember the query text too, if possible */
+ /* if insufficient memory, query just winds up NULL */
+ if (command)
+ entry->query = strdup(command);
+
+ /*
+ * Give the data a push (in pipeline mode, only if we're past the size
+ * threshold). In nonblock mode, don't complain if we're unable to send
+ * it all; PQgetResult() will do any additional flushing needed.
+ */
+ if (pqPipelineFlush(conn) < 0)
+ goto sendFailed;
+
+ /* OK, it's launched! */
+ pqAppendCmdQueueEntry(conn, entry);
+
+ return 1;
+
+sendFailed:
+ pqRecycleCmdQueueEntry(conn, entry);
+ /* 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->cmd_queue_head ||
+ (conn->cmd_queue_head->queryclass != PGQUERY_SIMPLE &&
+ conn->cmd_queue_head->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)
+{
+ pqParseInput3(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. Also,
+ * if we've detected read EOF and dropped the connection, we can expect
+ * that PQgetResult will fail immediately. Note that we do *not* check
+ * conn->write_failed here --- once that's become set, we know we have
+ * trouble, but we need to keep trying to read until we have a complete
+ * server message or detect read EOF.
+ */
+ return conn->asyncStatus == PGASYNC_BUSY && conn->status != CONNECTION_BAD;
+}
+
+/*
+ * 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).
+ *
+ * In pipeline mode, once all the result of a query have been returned,
+ * PQgetResult returns NULL to let the user know that the next
+ * query is being processed. At the end of the pipeline, returns a
+ * result with PQresultStatus(result) == PGRES_PIPELINE_SYNC.
+ */
+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_PIPELINE_IDLE:
+ Assert(conn->pipelineStatus != PQ_PIPELINE_OFF);
+
+ /*
+ * We're about to return the NULL that terminates the round of
+ * results from the current query; prepare to send the results
+ * of the next query, if any, when we're called next. If there's
+ * no next element in the command queue, this gets us in IDLE
+ * state.
+ */
+ resetPQExpBuffer(&conn->errorMessage);
+ pqPipelineProcessQueue(conn);
+ res = NULL; /* query is complete */
+ break;
+
+ case PGASYNC_READY:
+
+ /*
+ * For any query type other than simple query protocol, we advance
+ * the command queue here. This is because for simple query
+ * protocol we can get the READY state multiple times before the
+ * command is actually complete, since the command string can
+ * contain many queries. In simple query protocol, the queue
+ * advance is done by fe-protocol3 when it receives ReadyForQuery.
+ */
+ if (conn->cmd_queue_head &&
+ conn->cmd_queue_head->queryclass != PGQUERY_SIMPLE)
+ pqCommandQueueAdvance(conn);
+ res = pqPrepareAsyncResult(conn);
+ if (conn->pipelineStatus != PQ_PIPELINE_OFF)
+ {
+ /*
+ * We're about to send the results of the current query. Set
+ * us idle now, and ...
+ */
+ conn->asyncStatus = PGASYNC_PIPELINE_IDLE;
+
+ /*
+ * ... in cases when we're sending a pipeline-sync result,
+ * move queue processing forwards immediately, so that next
+ * time we're called, we're prepared to return the next result
+ * received from the server. In all other cases, leave the
+ * queue state change for next time, so that a terminating
+ * NULL result is sent.
+ *
+ * (In other words: we don't return a NULL after a pipeline
+ * sync.)
+ */
+ if (res && res->resultStatus == PGRES_PIPELINE_SYNC)
+ pqPipelineProcessQueue(conn);
+ }
+ else
+ {
+ /* Set the state back to BUSY, allowing parsing to proceed. */
+ conn->asyncStatus = PGASYNC_BUSY;
+ }
+ break;
+ case PGASYNC_READY_MORE:
+ 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:
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("unexpected asyncStatus: %d\n"),
+ (int) conn->asyncStatus);
+ res = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR);
+ break;
+ }
+
+ /* If the next command we expect is CLOSE, read and consume it */
+ if (conn->asyncStatus == PGASYNC_PIPELINE_IDLE &&
+ conn->cmd_queue_head &&
+ conn->cmd_queue_head->queryclass == PGQUERY_CLOSE)
+ {
+ if (res && res->resultStatus != PGRES_FATAL_ERROR)
+ {
+ conn->asyncStatus = PGASYNC_BUSY;
+ parseInput(conn);
+ conn->asyncStatus = PGASYNC_PIPELINE_IDLE;
+ }
+ else
+ /* we won't ever see the Close */
+ pqCommandQueueAdvance(conn);
+ }
+
+ 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))
+ {
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("PGEventProc \"%s\" failed during PGEVT_RESULTCREATE event\n"),
+ res->events[i].name);
+ pqSetResultError(res, &conn->errorMessage);
+ 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 extended query protocol 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 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 extended query protocol 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;
+
+ if (conn->pipelineStatus != PQ_PIPELINE_OFF)
+ {
+ appendPQExpBufferStr(&conn->errorMessage,
+ libpq_gettext("synchronous command execution functions are not allowed in pipeline mode\n"));
+ return false;
+ }
+
+ /*
+ * Since this is the beginning of a query cycle, reset the error buffer.
+ */
+ resetPQExpBuffer(&conn->errorMessage);
+
+ /*
+ * 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)
+ {
+ /* 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 if (resultStatus == PGRES_COPY_OUT)
+ {
+ /*
+ * 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 if (resultStatus == PGRES_COPY_BOTH)
+ {
+ /* We don't allow PQexec during COPY BOTH */
+ appendPQExpBufferStr(&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. (We used to have logic here to concatenate successive error
+ * messages, but now that happens automatically, since conn->errorMessage
+ * will continue to accumulate errors throughout this loop.)
+ *
+ * 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)
+ 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)
+{
+ PGcmdQueueEntry *entry = NULL;
+
+ /* Treat null desc_target as empty string */
+ if (!desc_target)
+ desc_target = "";
+
+ if (!PQsendQueryStart(conn, true))
+ return 0;
+
+ entry = pqAllocCmdQueueEntry(conn);
+ if (entry == NULL)
+ return 0; /* error msg already set */
+
+ /* construct the Describe message */
+ if (pqPutMsgStart('D', conn) < 0 ||
+ pqPutc(desc_type, conn) < 0 ||
+ pqPuts(desc_target, conn) < 0 ||
+ pqPutMsgEnd(conn) < 0)
+ goto sendFailed;
+
+ /* construct the Sync message */
+ if (conn->pipelineStatus == PQ_PIPELINE_OFF)
+ {
+ if (pqPutMsgStart('S', conn) < 0 ||
+ pqPutMsgEnd(conn) < 0)
+ goto sendFailed;
+ }
+
+ /* remember we are doing a Describe */
+ entry->queryclass = PGQUERY_DESCRIBE;
+
+ /*
+ * Give the data a push (in pipeline mode, only if we're past the size
+ * threshold). In nonblock mode, don't complain if we're unable to send
+ * it all; PQgetResult() will do any additional flushing needed.
+ */
+ if (pqPipelineFlush(conn) < 0)
+ goto sendFailed;
+
+ /* OK, it's launched! */
+ pqAppendCmdQueueEntry(conn, entry);
+
+ return 1;
+
+sendFailed:
+ pqRecycleCmdQueueEntry(conn, entry);
+ /* 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)
+ {
+ appendPQExpBufferStr(&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.)
+ */
+ 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 (pqPutMsgStart('d', 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)
+ {
+ appendPQExpBufferStr(&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 (errormsg)
+ {
+ /* Send COPY FAIL */
+ if (pqPutMsgStart('f', conn) < 0 ||
+ pqPuts(errormsg, conn) < 0 ||
+ pqPutMsgEnd(conn) < 0)
+ return -1;
+ }
+ else
+ {
+ /* Send COPY DONE */
+ if (pqPutMsgStart('c', 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->cmd_queue_head &&
+ conn->cmd_queue_head->queryclass != PGQUERY_SIMPLE)
+ {
+ if (pqPutMsgStart('S', 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;
+
+ /* 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)
+ {
+ appendPQExpBufferStr(&conn->errorMessage,
+ libpq_gettext("no COPY in progress\n"));
+ return -2;
+ }
+ return pqGetCopyData3(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;
+
+ return pqGetline3(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;
+
+ return pqGetlineAsync3(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.
+ *
+ * This is deprecated; it's cleaner to use PQgetResult to get the transfer
+ * status.
+ *
+ * RETURNS:
+ * 0 on success
+ * 1 on failure
+ */
+int
+PQendcopy(PGconn *conn)
+{
+ if (!conn)
+ return 0;
+
+ return pqEndcopy3(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;
+
+ /*
+ * Since this is the beginning of a query cycle, reset the error buffer.
+ */
+ resetPQExpBuffer(&conn->errorMessage);
+
+ if (conn->pipelineStatus != PQ_PIPELINE_OFF)
+ {
+ appendPQExpBufferStr(&conn->errorMessage,
+ libpq_gettext("PQfn not allowed in pipeline mode\n"));
+ return NULL;
+ }
+
+ if (conn->sock == PGINVALID_SOCKET || conn->asyncStatus != PGASYNC_IDLE ||
+ conn->result != NULL)
+ {
+ appendPQExpBufferStr(&conn->errorMessage,
+ libpq_gettext("connection in wrong state\n"));
+ return NULL;
+ }
+
+ return pqFunctionCall3(conn, fnid,
+ result_buf, result_len,
+ result_is_int,
+ args, nargs);
+}
+
+/* ====== Pipeline mode support ======== */
+
+/*
+ * PQenterPipelineMode
+ * Put an idle connection in pipeline mode.
+ *
+ * Returns 1 on success. On failure, errorMessage is set and 0 is returned.
+ *
+ * Commands submitted after this can be pipelined on the connection;
+ * there's no requirement to wait for one to finish before the next is
+ * dispatched.
+ *
+ * Queuing of a new query or syncing during COPY is not allowed.
+ *
+ * A set of commands is terminated by a PQpipelineSync. Multiple sync
+ * points can be established while in pipeline mode. Pipeline mode can
+ * be exited by calling PQexitPipelineMode() once all results are processed.
+ *
+ * This doesn't actually send anything on the wire, it just puts libpq
+ * into a state where it can pipeline work.
+ */
+int
+PQenterPipelineMode(PGconn *conn)
+{
+ if (!conn)
+ return 0;
+
+ /* succeed with no action if already in pipeline mode */
+ if (conn->pipelineStatus != PQ_PIPELINE_OFF)
+ return 1;
+
+ if (conn->asyncStatus != PGASYNC_IDLE)
+ {
+ appendPQExpBufferStr(&conn->errorMessage,
+ libpq_gettext("cannot enter pipeline mode, connection not idle\n"));
+ return 0;
+ }
+
+ conn->pipelineStatus = PQ_PIPELINE_ON;
+
+ return 1;
+}
+
+/*
+ * PQexitPipelineMode
+ * End pipeline mode and return to normal command mode.
+ *
+ * Returns 1 in success (pipeline mode successfully ended, or not in pipeline
+ * mode).
+ *
+ * Returns 0 if in pipeline mode and cannot be ended yet. Error message will
+ * be set.
+ */
+int
+PQexitPipelineMode(PGconn *conn)
+{
+ if (!conn)
+ return 0;
+
+ if (conn->pipelineStatus == PQ_PIPELINE_OFF &&
+ (conn->asyncStatus == PGASYNC_IDLE ||
+ conn->asyncStatus == PGASYNC_PIPELINE_IDLE) &&
+ conn->cmd_queue_head == NULL)
+ return 1;
+
+ switch (conn->asyncStatus)
+ {
+ case PGASYNC_READY:
+ case PGASYNC_READY_MORE:
+ /* there are some uncollected results */
+ appendPQExpBufferStr(&conn->errorMessage,
+ libpq_gettext("cannot exit pipeline mode with uncollected results\n"));
+ return 0;
+
+ case PGASYNC_BUSY:
+ appendPQExpBufferStr(&conn->errorMessage,
+ libpq_gettext("cannot exit pipeline mode while busy\n"));
+ return 0;
+
+ case PGASYNC_IDLE:
+ case PGASYNC_PIPELINE_IDLE:
+ /* OK */
+ break;
+
+ case PGASYNC_COPY_IN:
+ case PGASYNC_COPY_OUT:
+ case PGASYNC_COPY_BOTH:
+ appendPQExpBufferStr(&conn->errorMessage,
+ libpq_gettext("cannot exit pipeline mode while in COPY\n"));
+ }
+
+ /* still work to process */
+ if (conn->cmd_queue_head != NULL)
+ {
+ appendPQExpBufferStr(&conn->errorMessage,
+ libpq_gettext("cannot exit pipeline mode with uncollected results\n"));
+ return 0;
+ }
+
+ conn->pipelineStatus = PQ_PIPELINE_OFF;
+ conn->asyncStatus = PGASYNC_IDLE;
+
+ /* Flush any pending data in out buffer */
+ if (pqFlush(conn) < 0)
+ return 0; /* error message is setup already */
+ return 1;
+}
+
+/*
+ * pqCommandQueueAdvance
+ * Remove one query from the command queue, when we receive
+ * all results from the server that pertain to it.
+ */
+void
+pqCommandQueueAdvance(PGconn *conn)
+{
+ PGcmdQueueEntry *prevquery;
+
+ if (conn->cmd_queue_head == NULL)
+ return;
+
+ /* delink from queue */
+ prevquery = conn->cmd_queue_head;
+ conn->cmd_queue_head = conn->cmd_queue_head->next;
+
+ /* If the queue is now empty, reset the tail too */
+ if (conn->cmd_queue_head == NULL)
+ conn->cmd_queue_tail = NULL;
+
+ /* and make it recyclable */
+ prevquery->next = NULL;
+ pqRecycleCmdQueueEntry(conn, prevquery);
+}
+
+/*
+ * pqPipelineProcessQueue: subroutine for PQgetResult
+ * In pipeline mode, start processing the results of the next query in the queue.
+ */
+static void
+pqPipelineProcessQueue(PGconn *conn)
+{
+ switch (conn->asyncStatus)
+ {
+ case PGASYNC_COPY_IN:
+ case PGASYNC_COPY_OUT:
+ case PGASYNC_COPY_BOTH:
+ case PGASYNC_READY:
+ case PGASYNC_READY_MORE:
+ case PGASYNC_BUSY:
+ /* client still has to process current query or results */
+ return;
+
+ case PGASYNC_IDLE:
+ /*
+ * If we're in IDLE mode and there's some command in the queue,
+ * get us into PIPELINE_IDLE mode and process normally. Otherwise
+ * there's nothing for us to do.
+ */
+ if (conn->cmd_queue_head != NULL)
+ {
+ conn->asyncStatus = PGASYNC_PIPELINE_IDLE;
+ break;
+ }
+ return;
+
+ case PGASYNC_PIPELINE_IDLE:
+ Assert(conn->pipelineStatus != PQ_PIPELINE_OFF);
+ /* next query please */
+ break;
+ }
+
+ /*
+ * If there are no further commands to process in the queue, get us in
+ * "real idle" mode now.
+ */
+ if (conn->cmd_queue_head == NULL)
+ {
+ conn->asyncStatus = PGASYNC_IDLE;
+ return;
+ }
+
+ /* Initialize async result-accumulation state */
+ pqClearAsyncResult(conn);
+
+ /*
+ * Reset single-row processing mode. (Client has to set it up for each
+ * query, if desired.)
+ */
+ conn->singleRowMode = false;
+
+ if (conn->pipelineStatus == PQ_PIPELINE_ABORTED &&
+ conn->cmd_queue_head->queryclass != PGQUERY_SYNC)
+ {
+ /*
+ * In an aborted pipeline we don't get anything from the server for
+ * each result; we're just discarding commands from the queue until we
+ * get to the next sync from the server.
+ *
+ * The PGRES_PIPELINE_ABORTED results tell the client that its queries
+ * got aborted.
+ */
+ conn->result = PQmakeEmptyPGresult(conn, PGRES_PIPELINE_ABORTED);
+ if (!conn->result)
+ {
+ appendPQExpBufferStr(&conn->errorMessage,
+ libpq_gettext("out of memory\n"));
+ pqSaveErrorResult(conn);
+ return;
+ }
+ conn->asyncStatus = PGASYNC_READY;
+ }
+ else
+ {
+ /* allow parsing to continue */
+ conn->asyncStatus = PGASYNC_BUSY;
+ }
+}
+
+/*
+ * PQpipelineSync
+ * Send a Sync message as part of a pipeline, and flush to server
+ *
+ * It's legal to start submitting more commands in the pipeline immediately,
+ * without waiting for the results of the current pipeline. There's no need to
+ * end pipeline mode and start it again.
+ *
+ * If a command in a pipeline fails, every subsequent command up to and including
+ * the result to the Sync message sent by PQpipelineSync gets set to
+ * PGRES_PIPELINE_ABORTED state. If the whole pipeline is processed without
+ * error, a PGresult with PGRES_PIPELINE_SYNC is produced.
+ *
+ * Queries can already have been sent before PQpipelineSync is called, but
+ * PQpipelineSync need to be called before retrieving command results.
+ *
+ * The connection will remain in pipeline mode and unavailable for new
+ * synchronous command execution functions until all results from the pipeline
+ * are processed by the client.
+ */
+int
+PQpipelineSync(PGconn *conn)
+{
+ PGcmdQueueEntry *entry;
+
+ if (!conn)
+ return 0;
+
+ if (conn->pipelineStatus == PQ_PIPELINE_OFF)
+ {
+ appendPQExpBufferStr(&conn->errorMessage,
+ libpq_gettext("cannot send pipeline when not in pipeline mode\n"));
+ return 0;
+ }
+
+ switch (conn->asyncStatus)
+ {
+ case PGASYNC_COPY_IN:
+ case PGASYNC_COPY_OUT:
+ case PGASYNC_COPY_BOTH:
+ /* should be unreachable */
+ appendPQExpBufferStr(&conn->errorMessage,
+ "internal error: cannot send pipeline while in COPY\n");
+ return 0;
+ case PGASYNC_READY:
+ case PGASYNC_READY_MORE:
+ case PGASYNC_BUSY:
+ case PGASYNC_IDLE:
+ case PGASYNC_PIPELINE_IDLE:
+ /* OK to send sync */
+ break;
+ }
+
+ entry = pqAllocCmdQueueEntry(conn);
+ if (entry == NULL)
+ return 0; /* error msg already set */
+
+ entry->queryclass = PGQUERY_SYNC;
+ entry->query = NULL;
+
+ /* construct the Sync message */
+ if (pqPutMsgStart('S', conn) < 0 ||
+ pqPutMsgEnd(conn) < 0)
+ goto sendFailed;
+
+ /*
+ * 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! */
+ pqAppendCmdQueueEntry(conn, entry);
+
+ return 1;
+
+sendFailed:
+ pqRecycleCmdQueueEntry(conn, entry);
+ /* error message should be set up already */
+ return 0;
+}
+
+/*
+ * PQsendFlushRequest
+ * Send request for server to flush its buffer. Useful in pipeline
+ * mode when a sync point is not desired.
+ */
+int
+PQsendFlushRequest(PGconn *conn)
+{
+ if (!conn)
+ return 0;
+
+ /* Don't try to send if we know there's no live connection. */
+ if (conn->status != CONNECTION_OK)
+ {
+ appendPQExpBufferStr(&conn->errorMessage,
+ libpq_gettext("no connection to the server\n"));
+ return 0;
+ }
+
+ /* Can't send while already busy, either, unless enqueuing for later */
+ if (conn->asyncStatus != PGASYNC_IDLE &&
+ conn->pipelineStatus == PQ_PIPELINE_OFF)
+ {
+ appendPQExpBufferStr(&conn->errorMessage,
+ libpq_gettext("another command is already in progress\n"));
+ return 0;
+ }
+
+ if (pqPutMsgStart('H', conn) < 0 ||
+ pqPutMsgEnd(conn) < 0)
+ {
+ return 0;
+ }
+
+ return 1;
+}
+
+/* ====== 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 >= lengthof(pgresStatus))
+ 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);
+
+ 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.
+ *
+ * Clear errorMessage in case pqFlush adds to it.
+ */
+ resetPQExpBuffer(&conn->errorMessage);
+
+ /* 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);
+}
+
+/*
+ * pqPipelineFlush
+ *
+ * In pipeline mode, data will be flushed only when the out buffer reaches the
+ * threshold value. In non-pipeline mode, it behaves as stock pqFlush.
+ *
+ * Returns 0 on success.
+ */
+static int
+pqPipelineFlush(PGconn *conn)
+{
+ if ((conn->pipelineStatus != PQ_PIPELINE_ON) ||
+ (conn->outCount >= OUTBUFFER_THRESHOLD))
+ return pqFlush(conn);
+ return 0;
+}
+
+
+/*
+ * 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)
+ appendPQExpBufferStr(&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;
+ }
+
+ resetPQExpBuffer(&conn->errorMessage);
+
+ 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;
+
+ resetPQExpBuffer(&conn->errorMessage);
+
+ /* 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)
+ {
+ appendPQExpBufferStr(&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)
+ {
+ appendPQExpBufferStr(&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)
+ appendPQExpBufferStr(&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;
+
+ resetPQExpBuffer(&conn->errorMessage);
+
+ 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..9e8aeae
--- /dev/null
+++ b/src/interfaces/libpq/fe-gssapi-common.c
@@ -0,0 +1,130 @@
+/*-------------------------------------------------------------------------
+ *
+ * fe-gssapi-common.c
+ * The front-end (client) GSSAPI common code
+ *
+ * Portions Copyright (c) 1996-2021, 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)
+{
+ 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'))
+ {
+ appendPQExpBufferStr(&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)
+ {
+ appendPQExpBufferStr(&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..4776606
--- /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-2021, 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..ffd9926
--- /dev/null
+++ b/src/interfaces/libpq/fe-lobj.c
@@ -0,0 +1,1084 @@
+/*-------------------------------------------------------------------------
+ *
+ * fe-lobj.c
+ * Front-end large object interface
+ *
+ * Portions Copyright (c) 1996-2021, 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 (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 (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 (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)
+ {
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("cannot determine OID of function %s\n"),
+ "lo_truncate");
+ 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)
+ {
+ appendPQExpBufferStr(&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 (lo_initialize(conn) < 0)
+ return -1;
+
+ if (conn->lobjfuncs->fn_lo_truncate64 == 0)
+ {
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("cannot determine OID of function %s\n"),
+ "lo_truncate64");
+ 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 (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)
+ {
+ appendPQExpBufferStr(&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 (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)
+ {
+ appendPQExpBufferStr(&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 (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 (lo_initialize(conn) < 0)
+ return -1;
+
+ if (conn->lobjfuncs->fn_lo_lseek64 == 0)
+ {
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("cannot determine OID of function %s\n"),
+ "lo_lseek64");
+ 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 (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 (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)
+ {
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("cannot determine OID of function %s\n"),
+ "lo_create");
+ 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 (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 (lo_initialize(conn) < 0)
+ return -1;
+
+ if (conn->lobjfuncs->fn_lo_tell64 == 0)
+ {
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("cannot determine OID of function %s\n"),
+ "lo_tell64");
+ 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 (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];
+
+ if (conn == NULL)
+ return InvalidOid;
+
+ /* Since this is the beginning of a query cycle, reset the error buffer */
+ resetPQExpBuffer(&conn->errorMessage);
+
+ /*
+ * open the file to be read in
+ */
+ fd = open(filename, O_RDONLY | PG_BINARY, 0666);
+ if (fd < 0)
+ { /* error */
+ appendPQExpBuffer(&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);
+ /* deliberately overwrite any error from lo_close */
+ 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);
+ /* deliberately overwrite any error from lo_close */
+ 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);
+ /* deliberately overwrite any error from lo_close */
+ 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)
+ {
+ appendPQExpBuffer(&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 for a new large-object operation on an existing connection.
+ * Return 0 if OK, -1 on failure.
+ *
+ * If we haven't previously done so, we collect the function OIDs from
+ * 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;
+
+ /* Nothing we can do with no connection */
+ if (conn == NULL)
+ return -1;
+
+ /* Since this is the beginning of a query cycle, reset the error buffer */
+ resetPQExpBuffer(&conn->errorMessage);
+
+ /* Nothing else to do if we already collected info */
+ if (conn->lobjfuncs != NULL)
+ return 0;
+
+ /*
+ * Allocate the structure to hold the function OIDs. We don't store it
+ * into the PGconn until it's successfully filled.
+ */
+ lobjfuncs = (PGlobjfuncs *) malloc(sizeof(PGlobjfuncs));
+ if (lobjfuncs == NULL)
+ {
+ appendPQExpBufferStr(&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. (Not all of them
+ * may exist in older server versions.)
+ */
+ 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')";
+
+ res = PQexec(conn, query);
+ if (res == NULL)
+ {
+ free(lobjfuncs);
+ return -1;
+ }
+
+ if (res->resultStatus != PGRES_TUPLES_OK)
+ {
+ free(lobjfuncs);
+ PQclear(res);
+ appendPQExpBufferStr(&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)
+ {
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("cannot determine OID of function %s\n"),
+ "lo_open");
+ free(lobjfuncs);
+ return -1;
+ }
+ if (lobjfuncs->fn_lo_close == 0)
+ {
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("cannot determine OID of function %s\n"),
+ "lo_close");
+ free(lobjfuncs);
+ return -1;
+ }
+ if (lobjfuncs->fn_lo_creat == 0)
+ {
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("cannot determine OID of function %s\n"),
+ "lo_creat");
+ free(lobjfuncs);
+ return -1;
+ }
+ if (lobjfuncs->fn_lo_unlink == 0)
+ {
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("cannot determine OID of function %s\n"),
+ "lo_unlink");
+ free(lobjfuncs);
+ return -1;
+ }
+ if (lobjfuncs->fn_lo_lseek == 0)
+ {
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("cannot determine OID of function %s\n"),
+ "lo_lseek");
+ free(lobjfuncs);
+ return -1;
+ }
+ if (lobjfuncs->fn_lo_tell == 0)
+ {
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("cannot determine OID of function %s\n"),
+ "lo_tell");
+ free(lobjfuncs);
+ return -1;
+ }
+ if (lobjfuncs->fn_lo_read == 0)
+ {
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("cannot determine OID of function %s\n"),
+ "loread");
+ free(lobjfuncs);
+ return -1;
+ }
+ if (lobjfuncs->fn_lo_write == 0)
+ {
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("cannot determine OID of function %s\n"),
+ "lowrite");
+ 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..fc0ec03
--- /dev/null
+++ b/src/interfaces/libpq/fe-misc.c
@@ -0,0 +1,1289 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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-2021, 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;
+}
+
+
+/*
+ * 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++];
+
+ return 0;
+}
+
+
+/*
+ * pqPutc: write 1 char to the current message
+ */
+int
+pqPutc(char c, PGconn *conn)
+{
+ if (pqPutMsgBytes(&c, 1, conn))
+ return EOF;
+
+ 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;
+
+ 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;
+
+ 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;
+
+ 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;
+
+ 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;
+
+ 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;
+ }
+
+ 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;
+ }
+
+ 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 */
+ appendPQExpBufferStr(&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 */
+ appendPQExpBufferStr(&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)
+ *
+ * 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. The state variable conn->outMsgEnd is the end of
+ * the data collected so far.
+ */
+int
+pqPutMsgStart(char msg_type, 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? */
+ lenPos = endPos;
+ /* allow room for message length */
+ endPos += 4;
+
+ /* 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 */
+
+ 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)
+{
+ /* 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);
+ }
+
+ /* trace client-to-server message */
+ if (conn->Pfdebug)
+ {
+ if (conn->outCount < conn->outMsgStart)
+ pqTraceOutputMessage(conn, conn->outBuffer + conn->outCount, true);
+ else
+ pqTraceOutputNoTypeByteMessage(conn,
+ conn->outBuffer + conn->outMsgStart);
+ }
+
+ /* 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)
+ {
+ appendPQExpBufferStr(&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)
+ {
+ switch (SOCK_ERRNO)
+ {
+ case EINTR:
+ goto retry3;
+
+ /* Some systems return EAGAIN/EWOULDBLOCK for no data */
+#ifdef EAGAIN
+ case EAGAIN:
+ return someread;
+#endif
+#if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
+ case EWOULDBLOCK:
+ return someread;
+#endif
+
+ /* We might get ECONNRESET etc here if connection failed */
+ case ALL_CONNECTION_FAILURE_ERRNOS:
+ goto definitelyFailed;
+
+ default:
+ /* 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)
+ {
+ switch (SOCK_ERRNO)
+ {
+ case EINTR:
+ goto retry4;
+
+ /* Some systems return EAGAIN/EWOULDBLOCK for no data */
+#ifdef EAGAIN
+ case EAGAIN:
+ return 0;
+#endif
+#if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
+ case EWOULDBLOCK:
+ return 0;
+#endif
+
+ /* We might get ECONNRESET etc here if connection failed */
+ case ALL_CONNECTION_FAILURE_ERRNOS:
+ goto definitelyFailed;
+
+ default:
+ /* 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:
+ appendPQExpBufferStr(&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 oldmsglen = conn->errorMessage.len;
+ 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)
+ {
+ conn->write_failed = true;
+ /* Insert error message into conn->write_err_msg, if possible */
+ /* (strdup failure is OK, we'll cope later) */
+ conn->write_err_msg = strdup(libpq_gettext("connection not open\n"));
+ /* 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).
+ *
+ * We only want to transfer whatever has been appended to
+ * conn->errorMessage since we entered this routine.
+ */
+ if (!PQExpBufferBroken(&conn->errorMessage))
+ {
+ conn->write_err_msg = strdup(conn->errorMessage.data +
+ oldmsglen);
+ conn->errorMessage.len = oldmsglen;
+ conn->errorMessage.data[oldmsglen] = '\0';
+ }
+
+ /* 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->outCount > 0)
+ {
+ if (conn->Pfdebug)
+ fflush(conn->Pfdebug);
+
+ 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)
+ {
+ appendPQExpBufferStr(&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)
+ {
+ appendPQExpBufferStr(&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];
+
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("%s() failed: %s\n"),
+ "select",
+ 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.
+ *
+ * Caution: when dealing with text that is not certainly valid in the
+ * specified encoding, the result may exceed the actual remaining
+ * string length. Callers that are not prepared to deal with that
+ * should use PQmblenBounded() instead.
+ */
+int
+PQmblen(const char *s, int encoding)
+{
+ return pg_encoding_mblen(encoding, s);
+}
+
+/*
+ * Returns the byte length of the character beginning at s, using the
+ * specified encoding; but not more than the distance to end of string.
+ */
+int
+PQmblenBounded(const char *s, int encoding)
+{
+ return strnlen(s, 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)
+{
+ /*
+ * If multiple threads come through here at about the same time, it's okay
+ * for more than one of them to call bindtextdomain(). But it's not okay
+ * for any of them to return to caller before bindtextdomain() is
+ * complete, so don't set the flag till that's done. Use "volatile" just
+ * to be sure the compiler doesn't try to get cute.
+ */
+ static volatile 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;
+
+ /* No relocatable lookup here because the binary could be anywhere */
+ ldir = getenv("PGLOCALEDIR");
+ if (!ldir)
+ ldir = LOCALEDIR;
+ bindtextdomain(PG_TEXTDOMAIN("libpq"), ldir);
+ already_bound = true;
+#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..09bbc66
--- /dev/null
+++ b/src/interfaces/libpq/fe-print.c
@@ -0,0 +1,781 @@
+/*-------------------------------------------------------------------------
+ *
+ * fe-print.c
+ * functions for pretty-printing query results
+ *
+ * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * These functions were formerly part of fe-exec.c, but they
+ * didn't really belong there.
+ *
+ * IDENTIFICATION
+ * src/interfaces/libpq/fe-print.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres_fe.h"
+
+#include <signal.h>
+
+#ifdef WIN32
+#include "win32.h"
+#else
+#include <unistd.h>
+#include <sys/ioctl.h>
+#endif
+
+#ifdef HAVE_TERMIOS_H
+#include <termios.h>
+#else
+#ifndef WIN32
+#include <sys/termios.h>
+#endif
+#endif
+
+#include "libpq-fe.h"
+#include "libpq-int.h"
+
+
+static 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-protocol3.c b/src/interfaces/libpq/fe-protocol3.c
new file mode 100644
index 0000000..c33f904
--- /dev/null
+++ b/src/interfaces/libpq/fe-protocol3.c
@@ -0,0 +1,2254 @@
+/*-------------------------------------------------------------------------
+ *
+ * fe-protocol3.c
+ * functions that are specific to frontend/backend protocol version 3
+ *
+ * Portions Copyright (c) 1996-2021, 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')
+
+
+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
+ {
+ /* Any other case is unexpected and we summarily skip it */
+ 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)
+ {
+ appendPQExpBufferStr(&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': /* sync response, backend is ready for new
+ * query */
+ if (getReadyForQuery(conn))
+ return;
+ if (conn->pipelineStatus != PQ_PIPELINE_OFF)
+ {
+ conn->result = PQmakeEmptyPGresult(conn,
+ PGRES_PIPELINE_SYNC);
+ if (!conn->result)
+ {
+ appendPQExpBufferStr(&conn->errorMessage,
+ libpq_gettext("out of memory"));
+ pqSaveErrorResult(conn);
+ }
+ else
+ {
+ conn->pipelineStatus = PQ_PIPELINE_ON;
+ conn->asyncStatus = PGASYNC_READY;
+ }
+ }
+ else
+ {
+ /*
+ * In simple query protocol, advance the command queue
+ * (see PQgetResult).
+ */
+ if (conn->cmd_queue_head &&
+ conn->cmd_queue_head->queryclass == PGQUERY_SIMPLE)
+ pqCommandQueueAdvance(conn);
+ conn->asyncStatus = PGASYNC_IDLE;
+ }
+ break;
+ case 'I': /* empty query */
+ if (conn->result == NULL)
+ {
+ conn->result = PQmakeEmptyPGresult(conn,
+ PGRES_EMPTY_QUERY);
+ if (!conn->result)
+ {
+ appendPQExpBufferStr(&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->cmd_queue_head &&
+ conn->cmd_queue_head->queryclass == PGQUERY_PREPARE)
+ {
+ if (conn->result == NULL)
+ {
+ conn->result = PQmakeEmptyPGresult(conn,
+ PGRES_COMMAND_OK);
+ if (!conn->result)
+ {
+ appendPQExpBufferStr(&conn->errorMessage,
+ libpq_gettext("out of memory"));
+ pqSaveErrorResult(conn);
+ }
+ }
+ conn->asyncStatus = PGASYNC_READY;
+ }
+ break;
+ case '2': /* Bind Complete */
+ /* Nothing to do for this message type */
+ break;
+ case '3': /* Close Complete */
+ /*
+ * If we get CloseComplete when waiting for it, consume
+ * the queue element and keep going. A result is not
+ * expected from this message; it is just there so that
+ * we know to wait for it when PQsendQuery is used in
+ * pipeline mode, before going in IDLE state. Failing to
+ * do this makes us receive CloseComplete when IDLE, which
+ * creates problems.
+ */
+ if (conn->cmd_queue_head &&
+ conn->cmd_queue_head->queryclass == PGQUERY_CLOSE)
+ {
+ pqCommandQueueAdvance(conn);
+ }
+
+ 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->cmd_queue_head &&
+ conn->cmd_queue_head->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->cmd_queue_head &&
+ conn->cmd_queue_head->queryclass == PGQUERY_DESCRIBE)
+ {
+ if (conn->result == NULL)
+ {
+ conn->result = PQmakeEmptyPGresult(conn,
+ PGRES_COMMAND_OK);
+ if (!conn->result)
+ {
+ appendPQExpBufferStr(&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 */
+ appendPQExpBufferStr(&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:
+ appendPQExpBuffer(&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)
+ {
+ /* trace server-to-client message */
+ if (conn->Pfdebug)
+ pqTraceOutputMessage(conn, conn->inBuffer + conn->inStart, false);
+
+ /* Normal case: parsing agrees with specified length */
+ conn->inStart = conn->inCursor;
+ }
+ else
+ {
+ /* Trouble --- report it */
+ appendPQExpBuffer(&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)
+{
+ appendPQExpBuffer(&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 PQgetResult 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->cmd_queue_head ||
+ (conn->cmd_queue_head &&
+ conn->cmd_queue_head->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->cmd_queue_head) ||
+ (conn->cmd_queue_head &&
+ conn->cmd_queue_head->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");
+
+ appendPQExpBuffer(&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");
+ appendPQExpBuffer(&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");
+
+ appendPQExpBuffer(&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 in pipeline mode, set error indicator for it */
+ if (isError && conn->pipelineStatus != PQ_PIPELINE_OFF)
+ conn->pipelineStatus = PQ_PIPELINE_ABORTED;
+
+ /*
+ * 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 && res && conn->cmd_queue_head && conn->cmd_queue_head->query)
+ res->errQuery = pqResultStrdup(res, conn->cmd_queue_head->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)
+ pqSetResultError(res, &workBuf);
+ pqClearAsyncResult(conn); /* redundant, but be safe */
+ conn->result = res;
+ if (PQExpBufferDataBroken(workBuf))
+ appendPQExpBufferStr(&conn->errorMessage,
+ libpq_gettext("out of memory\n"));
+ 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. But if we
+ * were unlucky enough to run out of memory while filling workBuf,
+ * insert "out of memory", as in pqSetResultError.
+ */
+ if (PQExpBufferDataBroken(workBuf))
+ res->errMsg = libpq_gettext("out of memory\n");
+ else
+ 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;
+ }
+
+ /* trace server-to-client message */
+ if (conn->Pfdebug)
+ pqTraceOutputMessage(conn, conn->inBuffer + conn->inStart, false);
+
+ /* 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)
+ {
+ appendPQExpBufferStr(&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)
+ {
+ appendPQExpBufferStr(&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)
+ {
+ appendPQExpBufferStr(&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', 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->cmd_queue_head &&
+ conn->cmd_queue_head->queryclass != PGQUERY_SIMPLE)
+ {
+ if (pqPutMsgStart('S', 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;
+
+ /*
+ * 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;
+
+ /* already validated by PQfn */
+ Assert(conn->pipelineStatus == PQ_PIPELINE_OFF);
+
+ /* PQfn already validated connection state */
+
+ if (pqPutMsgStart('F', 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. */
+ appendPQExpBuffer(&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);
+ }
+
+ /* trace server-to-client message */
+ if (conn->Pfdebug)
+ pqTraceOutputMessage(conn, conn->inBuffer + conn->inStart, false);
+
+ /* 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..afa5d13
--- /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-2021, 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'))
+ {
+ appendPQExpBufferStr(&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)
+ {
+ appendPQExpBufferStr(&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);
+ appendPQExpBufferStr(&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'))
+ {
+ appendPQExpBufferStr(&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)
+ {
+ appendPQExpBuffer(&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)
+ {
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("server certificate for \"%s\" does not match host name \"%s\"\n"),
+ first_name, host);
+ }
+ else
+ {
+ appendPQExpBufferStr(&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..2389f67
--- /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-2021, 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..c783a53
--- /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-2021, 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 added to 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)
+ {
+ appendPQExpBufferStr(&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)
+ {
+ appendPQExpBufferStr(&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))
+ {
+ appendPQExpBuffer(&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 = pg_hton32(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 added to 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 = pg_ntoh32(*(uint32 *) PqGSSRecvBuffer);
+
+ if (input.length > PQ_GSS_RECV_BUFFER_SIZE - sizeof(uint32))
+ {
+ appendPQExpBuffer(&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)
+ {
+ appendPQExpBufferStr(&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)
+ {
+ appendPQExpBufferStr(&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;
+
+ appendPQExpBuffer(&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 = pg_ntoh32(*(uint32 *) PqGSSRecvBuffer);
+ if (input.length > PQ_GSS_RECV_BUFFER_SIZE - sizeof(uint32))
+ {
+ appendPQExpBuffer(&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 = pg_hton32(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..3725bf4
--- /dev/null
+++ b/src/interfaces/libpq/fe-secure-openssl.c
@@ -0,0 +1,1884 @@
+/*-------------------------------------------------------------------------
+ *
+ * fe-secure-openssl.c
+ * OpenSSL support
+ *
+ *
+ * Portions Copyright (c) 1996-2021, 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-openssl.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 crypto_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 (crypto_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 */
+ appendPQExpBufferStr(&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)
+ appendPQExpBufferStr(&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
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("SSL SYSCALL error: %s\n"),
+ SOCK_STRERROR(result_errno,
+ sebuf, sizeof(sebuf)));
+ }
+ else
+ {
+ appendPQExpBufferStr(&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);
+
+ appendPQExpBuffer(&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.
+ */
+ appendPQExpBufferStr(&conn->errorMessage,
+ libpq_gettext("SSL connection has been closed unexpectedly\n"));
+ result_errno = ECONNRESET;
+ n = -1;
+ break;
+ default:
+ appendPQExpBuffer(&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 */
+ appendPQExpBufferStr(&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)
+ appendPQExpBufferStr(&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
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("SSL SYSCALL error: %s\n"),
+ SOCK_STRERROR(result_errno,
+ sebuf, sizeof(sebuf)));
+ }
+ else
+ {
+ appendPQExpBufferStr(&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);
+
+ appendPQExpBuffer(&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.
+ */
+ appendPQExpBufferStr(&conn->errorMessage,
+ libpq_gettext("SSL connection has been closed unexpectedly\n"));
+ result_errno = ECONNRESET;
+ n = -1;
+ break;
+ default:
+ appendPQExpBuffer(&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))
+ {
+ appendPQExpBufferStr(&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)
+ {
+ appendPQExpBuffer(&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))
+ {
+ appendPQExpBufferStr(&conn->errorMessage,
+ libpq_gettext("could not generate peer certificate hash\n"));
+ return NULL;
+ }
+
+ /* save result */
+ cert_hash = malloc(hash_size);
+ if (cert_hash == NULL)
+ {
+ appendPQExpBufferStr(&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)
+ {
+ appendPQExpBufferStr(&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, bool do_ssl, bool do_crypto)
+{
+#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 (do_crypto && !conn->crypto_loaded)
+ {
+ if (crypto_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);
+ }
+
+ conn->crypto_loaded = true;
+ }
+ }
+#endif /* HAVE_CRYPTO_LOCK */
+#endif /* ENABLE_THREAD_SAFETY */
+
+ if (!ssl_lib_initialized && do_ssl)
+ {
+ 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 && crypto_open_connections > 0)
+ --crypto_open_connections;
+
+ if (pq_init_crypto_lib && crypto_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) ||
+ (conn->sslcrldir && strlen(conn->sslcrldir) > 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());
+
+ appendPQExpBuffer(&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)
+ {
+ appendPQExpBuffer(&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());
+
+ appendPQExpBuffer(&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)
+ {
+ appendPQExpBuffer(&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());
+
+ appendPQExpBuffer(&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());
+
+ appendPQExpBuffer(&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)
+ {
+ char *fname = NULL;
+ char *dname = NULL;
+
+ if (conn->sslcrl && strlen(conn->sslcrl) > 0)
+ fname = conn->sslcrl;
+ if (conn->sslcrldir && strlen(conn->sslcrldir) > 0)
+ dname = conn->sslcrldir;
+
+ /* defaults to use the default CRL file */
+ if (!fname && !dname && have_homedir)
+ {
+ snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, ROOT_CRL_FILE);
+ fname = fnbuf;
+ }
+
+ /* Set the flags to check against the complete CRL chain */
+ if ((fname || dname) &&
+ X509_STORE_load_locations(cvstore, fname, dname) == 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')
+ appendPQExpBuffer(&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
+ appendPQExpBuffer(&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)
+ {
+ appendPQExpBuffer(&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());
+
+ appendPQExpBuffer(&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());
+
+ appendPQExpBuffer(&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;
+
+ /*
+ * Set Server Name Indication (SNI), if enabled by connection parameters.
+ * Per RFC 6066, do not set it if the host is a literal IP address (IPv4
+ * or IPv6).
+ */
+ if (conn->sslsni && conn->sslsni[0] == '1')
+ {
+ const char *host = conn->connhost[conn->whichhost].host;
+
+ if (host && host[0] &&
+ !(strspn(host, "0123456789.") == strlen(host) ||
+ strchr(host, ':')))
+ {
+ if (SSL_set_tlsext_host_name(conn->ssl, host) != 1)
+ {
+ char *err = SSLerrmessage(ERR_get_error());
+
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("could not set SSL Server Name Indication (SNI): %s\n"),
+ err);
+ SSLerrfree(err);
+ return -1;
+ }
+ }
+ }
+
+ /*
+ * 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)
+ {
+ appendPQExpBufferStr(&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());
+
+ appendPQExpBuffer(&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());
+
+ appendPQExpBuffer(&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());
+
+ appendPQExpBuffer(&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());
+
+ appendPQExpBuffer(&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)
+ {
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("certificate present, but not private key file \"%s\"\n"),
+ fnbuf);
+ return -1;
+ }
+
+ /* Key file must be a regular file */
+ if (!S_ISREG(buf.st_mode))
+ {
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("private key file \"%s\" is not a regular file\n"),
+ fnbuf);
+ return -1;
+ }
+
+ /*
+ * Refuse to load world-readable key files. We accept root-owned
+ * files with mode 0640 or less, so that we can access system-wide
+ * certificates if we have a supplementary group membership that
+ * allows us to read 'em. For files with non-root ownership, require
+ * mode 0600 or less. We need not check the file's ownership exactly;
+ * if we're able to read it despite it having such restrictive
+ * permissions, it must have the right ownership.
+ *
+ * Note: be very careful about tightening these rules. Some people
+ * expect, for example, that a client process running as root should
+ * be able to use a non-root-owned key file.
+ *
+ * Note that roughly similar checks are performed in
+ * src/backend/libpq/be-secure-common.c so any changes here may need
+ * to be made there as well. However, this code caters for the case
+ * of current user == root, while that code does not.
+ *
+ * Ideally we would do similar permissions checks on Windows, but it
+ * is not clear how that would work since Unix-style permissions may
+ * not be available.
+ */
+#if !defined(WIN32) && !defined(__CYGWIN__)
+ if (buf.st_uid == 0 ?
+ buf.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO) :
+ buf.st_mode & (S_IRWXG | S_IRWXO))
+ {
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("private key file \"%s\" has group or world access; file must have permissions u=rw (0600) or less if owned by the current user, or permissions u=rw,g=r (0640) or less if owned by root\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)
+ {
+ appendPQExpBuffer(&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());
+
+ appendPQExpBuffer(&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)
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("SSL SYSCALL error: %s\n"),
+ SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
+ else
+ appendPQExpBufferStr(&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);
+
+ appendPQExpBuffer(&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:
+ appendPQExpBuffer(&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());
+
+ appendPQExpBuffer(&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_in_use)
+ {
+ 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.
+ */
+
+ SSL_shutdown(conn->ssl);
+ SSL_free(conn->ssl);
+ conn->ssl = NULL;
+ conn->ssl_in_use = false;
+
+ destroy_needed = true;
+ }
+
+ 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
+ }
+ else
+ {
+ /*
+ * In the non-SSL case, just remove the crypto callbacks if the
+ * connection has then loaded. This code path has no dependency on
+ * any pending SSL calls.
+ */
+ if (conn->crypto_loaded)
+ destroy_needed = true;
+ }
+
+ /*
+ * This will remove our crypto locking hooks if this is the last
+ * connection using libcrypto which means we must wait to call it until
+ * after all the potential 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();
+ conn->crypto_loaded = false;
+ }
+}
+
+
+/*
+ * 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_index |= (BIO_TYPE_DESCRIPTOR | BIO_TYPE_SOURCE_SINK);
+ 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..b15d8d1
--- /dev/null
+++ b/src/interfaces/libpq/fe-secure.c
@@ -0,0 +1,552 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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-2021, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/interfaces/libpq/fe-secure.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#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, bool do_ssl, bool do_crypto)
+{
+ int r = 0;
+
+#ifdef USE_SSL
+ r = pgtls_init(conn, do_ssl, do_crypto);
+#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
+ pgtls_close(conn);
+#endif
+}
+
+/*
+ * Read data from a secure connection.
+ *
+ * On failure, this function is responsible for appending a suitable message
+ * to 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;
+
+ case EPIPE:
+ case ECONNRESET:
+ appendPQExpBufferStr(&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:
+ appendPQExpBuffer(&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 appending a suitable message
+ * to 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);
+
+ /* FALL THRU */
+
+ case ECONNRESET:
+ appendPQExpBufferStr(&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:
+ appendPQExpBuffer(&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;
+}
+#endif /* USE_SSL */
+
+/*
+ * Dummy versions of OpenSSL key password hook functions, when built without
+ * OpenSSL.
+ */
+#ifndef USE_OPENSSL
+
+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_OPENSSL */
+
+/* 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/fe-trace.c b/src/interfaces/libpq/fe-trace.c
new file mode 100644
index 0000000..8660d27
--- /dev/null
+++ b/src/interfaces/libpq/fe-trace.c
@@ -0,0 +1,729 @@
+/*-------------------------------------------------------------------------
+ *
+ * fe-trace.c
+ * functions for libpq protocol tracing
+ *
+ * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * src/interfaces/libpq/fe-trace.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres_fe.h"
+
+#include <ctype.h>
+#include <limits.h>
+#include <sys/time.h>
+#include <time.h>
+
+#ifdef WIN32
+#include "win32.h"
+#else
+#include <unistd.h>
+#endif
+
+#include "libpq-fe.h"
+#include "libpq-int.h"
+#include "port/pg_bswap.h"
+
+
+/* Enable tracing */
+void
+PQtrace(PGconn *conn, FILE *debug_port)
+{
+ if (conn == NULL)
+ return;
+ PQuntrace(conn);
+ if (debug_port == NULL)
+ return;
+
+ conn->Pfdebug = debug_port;
+ conn->traceFlags = 0;
+}
+
+/* Disable tracing */
+void
+PQuntrace(PGconn *conn)
+{
+ if (conn == NULL)
+ return;
+ if (conn->Pfdebug)
+ {
+ fflush(conn->Pfdebug);
+ conn->Pfdebug = NULL;
+ }
+
+ conn->traceFlags = 0;
+}
+
+/* Set flags for current tracing session */
+void
+PQsetTraceFlags(PGconn *conn, int flags)
+{
+ if (conn == NULL)
+ return;
+ /* If PQtrace() failed, do nothing. */
+ if (conn->Pfdebug == NULL)
+ return;
+ conn->traceFlags = flags;
+}
+
+/*
+ * Print the current time, with microseconds, into a caller-supplied
+ * buffer.
+ * Cribbed from setup_formatted_log_time, but much simpler.
+ */
+static void
+pqTraceFormatTimestamp(char *timestr, size_t ts_len)
+{
+ struct timeval tval;
+ time_t now;
+
+ gettimeofday(&tval, NULL);
+
+ /*
+ * MSVC's implementation of timeval uses a long for tv_sec, however,
+ * localtime() expects a time_t pointer. Here we'll assign tv_sec to a
+ * local time_t variable so that we pass localtime() the correct pointer
+ * type.
+ */
+ now = tval.tv_sec;
+ strftime(timestr, ts_len,
+ "%Y-%m-%d %H:%M:%S",
+ localtime(&now));
+ /* append microseconds */
+ snprintf(timestr + strlen(timestr), ts_len - strlen(timestr),
+ ".%06u", (unsigned int) (tval.tv_usec));
+}
+
+/*
+ * pqTraceOutputByte1: output a 1-char message to the log
+ */
+static void
+pqTraceOutputByte1(FILE *pfdebug, const char *data, int *cursor)
+{
+ const char *v = data + *cursor;
+
+ /*
+ * Show non-printable data in hex format, including the terminating \0
+ * that completes ErrorResponse and NoticeResponse messages.
+ */
+ if (!isprint((unsigned char) *v))
+ fprintf(pfdebug, " \\x%02x", *v);
+ else
+ fprintf(pfdebug, " %c", *v);
+ *cursor += 1;
+}
+
+/*
+ * pqTraceOutputInt16: output a 2-byte integer message to the log
+ */
+static int
+pqTraceOutputInt16(FILE *pfdebug, const char *data, int *cursor)
+{
+ uint16 tmp;
+ int result;
+
+ memcpy(&tmp, data + *cursor, 2);
+ *cursor += 2;
+ result = (int) pg_ntoh16(tmp);
+ fprintf(pfdebug, " %d", result);
+
+ return result;
+}
+
+/*
+ * pqTraceOutputInt32: output a 4-byte integer message to the log
+ *
+ * If 'suppress' is true, print a literal NNNN instead of the actual number.
+ */
+static int
+pqTraceOutputInt32(FILE *pfdebug, const char *data, int *cursor, bool suppress)
+{
+ int result;
+
+ memcpy(&result, data + *cursor, 4);
+ *cursor += 4;
+ result = (int) pg_ntoh32(result);
+ if (suppress)
+ fprintf(pfdebug, " NNNN");
+ else
+ fprintf(pfdebug, " %d", result);
+
+ return result;
+}
+
+/*
+ * pqTraceOutputString: output a string message to the log
+ */
+static void
+pqTraceOutputString(FILE *pfdebug, const char *data, int *cursor, bool suppress)
+{
+ int len;
+
+ if (suppress)
+ {
+ fprintf(pfdebug, " \"SSSS\"");
+ *cursor += strlen(data + *cursor) + 1;
+ }
+ else
+ {
+ len = fprintf(pfdebug, " \"%s\"", data + *cursor);
+
+ /*
+ * This is a null-terminated string. So add 1 after subtracting 3
+ * which is the double quotes and space length from len.
+ */
+ *cursor += (len - 3 + 1);
+ }
+}
+
+/*
+ * pqTraceOutputNchar: output a string of exactly len bytes message to the log
+ */
+static void
+pqTraceOutputNchar(FILE *pfdebug, int len, const char *data, int *cursor)
+{
+ int i,
+ next; /* first char not yet printed */
+ const char *v = data + *cursor;
+
+ fprintf(pfdebug, " \'");
+
+ for (next = i = 0; i < len; ++i)
+ {
+ if (isprint((unsigned char) v[i]))
+ continue;
+ else
+ {
+ fwrite(v + next, 1, i - next, pfdebug);
+ fprintf(pfdebug, "\\x%02x", v[i]);
+ next = i + 1;
+ }
+ }
+ if (next < len)
+ fwrite(v + next, 1, len - next, pfdebug);
+
+ fprintf(pfdebug, "\'");
+ *cursor += len;
+}
+
+/*
+ * Output functions by protocol message type
+ */
+
+/* NotificationResponse */
+static void
+pqTraceOutputA(FILE *f, const char *message, int *cursor, bool regress)
+{
+ fprintf(f, "NotificationResponse\t");
+ pqTraceOutputInt32(f, message, cursor, regress);
+ pqTraceOutputString(f, message, cursor, false);
+ pqTraceOutputString(f, message, cursor, false);
+}
+
+/* Bind */
+static void
+pqTraceOutputB(FILE *f, const char *message, int *cursor)
+{
+ int nparams;
+
+ fprintf(f, "Bind\t");
+ pqTraceOutputString(f, message, cursor, false);
+ pqTraceOutputString(f, message, cursor, false);
+ nparams = pqTraceOutputInt16(f, message, cursor);
+
+ for (int i = 0; i < nparams; i++)
+ pqTraceOutputInt16(f, message, cursor);
+
+ nparams = pqTraceOutputInt16(f, message, cursor);
+
+ for (int i = 0; i < nparams; i++)
+ {
+ int nbytes;
+
+ nbytes = pqTraceOutputInt32(f, message, cursor, false);
+ if (nbytes == -1)
+ continue;
+ pqTraceOutputNchar(f, nbytes, message, cursor);
+ }
+
+ nparams = pqTraceOutputInt16(f, message, cursor);
+ for (int i = 0; i < nparams; i++)
+ pqTraceOutputInt16(f, message, cursor);
+}
+
+/* Close(F) or CommandComplete(B) */
+static void
+pqTraceOutputC(FILE *f, bool toServer, const char *message, int *cursor)
+{
+ if (toServer)
+ {
+ fprintf(f, "Close\t");
+ pqTraceOutputByte1(f, message, cursor);
+ pqTraceOutputString(f, message, cursor, false);
+ }
+ else
+ {
+ fprintf(f, "CommandComplete\t");
+ pqTraceOutputString(f, message, cursor, false);
+ }
+}
+
+/* Describe(F) or DataRow(B) */
+static void
+pqTraceOutputD(FILE *f, bool toServer, const char *message, int *cursor)
+{
+ if (toServer)
+ {
+ fprintf(f, "Describe\t");
+ pqTraceOutputByte1(f, message, cursor);
+ pqTraceOutputString(f, message, cursor, false);
+ }
+ else
+ {
+ int nfields;
+ int len;
+ int i;
+
+ fprintf(f, "DataRow\t");
+ nfields = pqTraceOutputInt16(f, message, cursor);
+ for (i = 0; i < nfields; i++)
+ {
+ len = pqTraceOutputInt32(f, message, cursor, false);
+ if (len == -1)
+ continue;
+ pqTraceOutputNchar(f, len, message, cursor);
+ }
+ }
+}
+
+/* NoticeResponse / ErrorResponse */
+static void
+pqTraceOutputNR(FILE *f, const char *type, const char *message, int *cursor,
+ bool regress)
+{
+ fprintf(f, "%s\t", type);
+ for (;;)
+ {
+ char field;
+ bool suppress;
+
+ pqTraceOutputByte1(f, message, cursor);
+ field = message[*cursor - 1];
+ if (field == '\0')
+ break;
+
+ suppress = regress && (field == 'L' || field == 'F' || field == 'R');
+ pqTraceOutputString(f, message, cursor, suppress);
+ }
+}
+
+/* Execute(F) or ErrorResponse(B) */
+static void
+pqTraceOutputE(FILE *f, bool toServer, const char *message, int *cursor, bool regress)
+{
+ if (toServer)
+ {
+ fprintf(f, "Execute\t");
+ pqTraceOutputString(f, message, cursor, false);
+ pqTraceOutputInt32(f, message, cursor, false);
+ }
+ else
+ pqTraceOutputNR(f, "ErrorResponse", message, cursor, regress);
+}
+
+/* CopyFail */
+static void
+pqTraceOutputf(FILE *f, const char *message, int *cursor)
+{
+ fprintf(f, "CopyFail\t");
+ pqTraceOutputString(f, message, cursor, false);
+}
+
+/* FunctionCall */
+static void
+pqTraceOutputF(FILE *f, const char *message, int *cursor, bool regress)
+{
+ int nfields;
+ int nbytes;
+
+ fprintf(f, "FunctionCall\t");
+ pqTraceOutputInt32(f, message, cursor, regress);
+ nfields = pqTraceOutputInt16(f, message, cursor);
+
+ for (int i = 0; i < nfields; i++)
+ pqTraceOutputInt16(f, message, cursor);
+
+ nfields = pqTraceOutputInt16(f, message, cursor);
+
+ for (int i = 0; i < nfields; i++)
+ {
+ nbytes = pqTraceOutputInt32(f, message, cursor, false);
+ if (nbytes == -1)
+ continue;
+ pqTraceOutputNchar(f, nbytes, message, cursor);
+ }
+
+ pqTraceOutputInt16(f, message, cursor);
+}
+
+/* CopyInResponse */
+static void
+pqTraceOutputG(FILE *f, const char *message, int *cursor)
+{
+ int nfields;
+
+ fprintf(f, "CopyInResponse\t");
+ pqTraceOutputByte1(f, message, cursor);
+ nfields = pqTraceOutputInt16(f, message, cursor);
+
+ for (int i = 0; i < nfields; i++)
+ pqTraceOutputInt16(f, message, cursor);
+}
+
+/* CopyOutResponse */
+static void
+pqTraceOutputH(FILE *f, const char *message, int *cursor)
+{
+ int nfields;
+
+ fprintf(f, "CopyOutResponse\t");
+ pqTraceOutputByte1(f, message, cursor);
+ nfields = pqTraceOutputInt16(f, message, cursor);
+
+ for (int i = 0; i < nfields; i++)
+ pqTraceOutputInt16(f, message, cursor);
+}
+
+/* BackendKeyData */
+static void
+pqTraceOutputK(FILE *f, const char *message, int *cursor, bool regress)
+{
+ fprintf(f, "BackendKeyData\t");
+ pqTraceOutputInt32(f, message, cursor, regress);
+ pqTraceOutputInt32(f, message, cursor, regress);
+}
+
+/* Parse */
+static void
+pqTraceOutputP(FILE *f, const char *message, int *cursor, bool regress)
+{
+ int nparams;
+
+ fprintf(f, "Parse\t");
+ pqTraceOutputString(f, message, cursor, false);
+ pqTraceOutputString(f, message, cursor, false);
+ nparams = pqTraceOutputInt16(f, message, cursor);
+
+ for (int i = 0; i < nparams; i++)
+ pqTraceOutputInt32(f, message, cursor, regress);
+}
+
+/* Query */
+static void
+pqTraceOutputQ(FILE *f, const char *message, int *cursor)
+{
+ fprintf(f, "Query\t");
+ pqTraceOutputString(f, message, cursor, false);
+}
+
+/* Authentication */
+static void
+pqTraceOutputR(FILE *f, const char *message, int *cursor)
+{
+ fprintf(f, "Authentication\t");
+ pqTraceOutputInt32(f, message, cursor, false);
+}
+
+/* ParameterStatus */
+static void
+pqTraceOutputS(FILE *f, const char *message, int *cursor)
+{
+ fprintf(f, "ParameterStatus\t");
+ pqTraceOutputString(f, message, cursor, false);
+ pqTraceOutputString(f, message, cursor, false);
+}
+
+/* ParameterDescription */
+static void
+pqTraceOutputt(FILE *f, const char *message, int *cursor, bool regress)
+{
+ int nfields;
+
+ fprintf(f, "ParameterDescription\t");
+ nfields = pqTraceOutputInt16(f, message, cursor);
+
+ for (int i = 0; i < nfields; i++)
+ pqTraceOutputInt32(f, message, cursor, regress);
+}
+
+/* RowDescription */
+static void
+pqTraceOutputT(FILE *f, const char *message, int *cursor, bool regress)
+{
+ int nfields;
+
+ fprintf(f, "RowDescription\t");
+ nfields = pqTraceOutputInt16(f, message, cursor);
+
+ for (int i = 0; i < nfields; i++)
+ {
+ pqTraceOutputString(f, message, cursor, false);
+ pqTraceOutputInt32(f, message, cursor, regress);
+ pqTraceOutputInt16(f, message, cursor);
+ pqTraceOutputInt32(f, message, cursor, regress);
+ pqTraceOutputInt16(f, message, cursor);
+ pqTraceOutputInt32(f, message, cursor, false);
+ pqTraceOutputInt16(f, message, cursor);
+ }
+}
+
+/* NegotiateProtocolVersion */
+static void
+pqTraceOutputv(FILE *f, const char *message, int *cursor)
+{
+ fprintf(f, "NegotiateProtocolVersion\t");
+ pqTraceOutputInt32(f, message, cursor, false);
+ pqTraceOutputInt32(f, message, cursor, false);
+}
+
+/* FunctionCallResponse */
+static void
+pqTraceOutputV(FILE *f, const char *message, int *cursor)
+{
+ int len;
+
+ fprintf(f, "FunctionCallResponse\t");
+ len = pqTraceOutputInt32(f, message, cursor, false);
+ if (len != -1)
+ pqTraceOutputNchar(f, len, message, cursor);
+}
+
+/* CopyBothResponse */
+static void
+pqTraceOutputW(FILE *f, const char *message, int *cursor, int length)
+{
+ fprintf(f, "CopyBothResponse\t");
+ pqTraceOutputByte1(f, message, cursor);
+
+ while (length > *cursor)
+ pqTraceOutputInt16(f, message, cursor);
+}
+
+/* ReadyForQuery */
+static void
+pqTraceOutputZ(FILE *f, const char *message, int *cursor)
+{
+ fprintf(f, "ReadyForQuery\t");
+ pqTraceOutputByte1(f, message, cursor);
+}
+
+/*
+ * Print the given message to the trace output stream.
+ */
+void
+pqTraceOutputMessage(PGconn *conn, const char *message, bool toServer)
+{
+ char id;
+ int length;
+ char *prefix = toServer ? "F" : "B";
+ int logCursor = 0;
+ bool regress;
+
+ if ((conn->traceFlags & PQTRACE_SUPPRESS_TIMESTAMPS) == 0)
+ {
+ char timestr[128];
+
+ pqTraceFormatTimestamp(timestr, sizeof(timestr));
+ fprintf(conn->Pfdebug, "%s\t", timestr);
+ }
+ regress = (conn->traceFlags & PQTRACE_REGRESS_MODE) != 0;
+
+ id = message[logCursor++];
+
+ memcpy(&length, message + logCursor, 4);
+ length = (int) pg_ntoh32(length);
+ logCursor += 4;
+
+ /*
+ * In regress mode, suppress the length of ErrorResponse and
+ * NoticeResponse. The F (file name), L (line number) and R (routine
+ * name) fields can change as server code is modified, and if their
+ * lengths differ from the originals, that would break tests.
+ */
+ if (regress && !toServer && (id == 'E' || id == 'N'))
+ fprintf(conn->Pfdebug, "%s\tNN\t", prefix);
+ else
+ fprintf(conn->Pfdebug, "%s\t%d\t", prefix, length);
+
+ switch (id)
+ {
+ case '1':
+ fprintf(conn->Pfdebug, "ParseComplete");
+ /* No message content */
+ break;
+ case '2':
+ fprintf(conn->Pfdebug, "BindComplete");
+ /* No message content */
+ break;
+ case '3':
+ fprintf(conn->Pfdebug, "CloseComplete");
+ /* No message content */
+ break;
+ case 'A': /* Notification Response */
+ pqTraceOutputA(conn->Pfdebug, message, &logCursor, regress);
+ break;
+ case 'B': /* Bind */
+ pqTraceOutputB(conn->Pfdebug, message, &logCursor);
+ break;
+ case 'c':
+ fprintf(conn->Pfdebug, "CopyDone");
+ /* No message content */
+ break;
+ case 'C': /* Close(F) or Command Complete(B) */
+ pqTraceOutputC(conn->Pfdebug, toServer, message, &logCursor);
+ break;
+ case 'd': /* Copy Data */
+ /* Drop COPY data to reduce the overhead of logging. */
+ break;
+ case 'D': /* Describe(F) or Data Row(B) */
+ pqTraceOutputD(conn->Pfdebug, toServer, message, &logCursor);
+ break;
+ case 'E': /* Execute(F) or Error Response(B) */
+ pqTraceOutputE(conn->Pfdebug, toServer, message, &logCursor,
+ regress);
+ break;
+ case 'f': /* Copy Fail */
+ pqTraceOutputf(conn->Pfdebug, message, &logCursor);
+ break;
+ case 'F': /* Function Call */
+ pqTraceOutputF(conn->Pfdebug, message, &logCursor, regress);
+ break;
+ case 'G': /* Start Copy In */
+ pqTraceOutputG(conn->Pfdebug, message, &logCursor);
+ break;
+ case 'H': /* Flush(F) or Start Copy Out(B) */
+ if (!toServer)
+ pqTraceOutputH(conn->Pfdebug, message, &logCursor);
+ else
+ fprintf(conn->Pfdebug, "Flush"); /* no message content */
+ break;
+ case 'I':
+ fprintf(conn->Pfdebug, "EmptyQueryResponse");
+ /* No message content */
+ break;
+ case 'K': /* secret key data from the backend */
+ pqTraceOutputK(conn->Pfdebug, message, &logCursor, regress);
+ break;
+ case 'n':
+ fprintf(conn->Pfdebug, "NoData");
+ /* No message content */
+ break;
+ case 'N':
+ pqTraceOutputNR(conn->Pfdebug, "NoticeResponse", message,
+ &logCursor, regress);
+ break;
+ case 'P': /* Parse */
+ pqTraceOutputP(conn->Pfdebug, message, &logCursor, regress);
+ break;
+ case 'Q': /* Query */
+ pqTraceOutputQ(conn->Pfdebug, message, &logCursor);
+ break;
+ case 'R': /* Authentication */
+ pqTraceOutputR(conn->Pfdebug, message, &logCursor);
+ break;
+ case 's':
+ fprintf(conn->Pfdebug, "PortalSuspended");
+ /* No message content */
+ break;
+ case 'S': /* Parameter Status(B) or Sync(F) */
+ if (!toServer)
+ pqTraceOutputS(conn->Pfdebug, message, &logCursor);
+ else
+ fprintf(conn->Pfdebug, "Sync"); /* no message content */
+ break;
+ case 't': /* Parameter Description */
+ pqTraceOutputt(conn->Pfdebug, message, &logCursor, regress);
+ break;
+ case 'T': /* Row Description */
+ pqTraceOutputT(conn->Pfdebug, message, &logCursor, regress);
+ break;
+ case 'v': /* Negotiate Protocol Version */
+ pqTraceOutputv(conn->Pfdebug, message, &logCursor);
+ break;
+ case 'V': /* Function Call response */
+ pqTraceOutputV(conn->Pfdebug, message, &logCursor);
+ break;
+ case 'W': /* Start Copy Both */
+ pqTraceOutputW(conn->Pfdebug, message, &logCursor, length);
+ break;
+ case 'X':
+ fprintf(conn->Pfdebug, "Terminate");
+ /* No message content */
+ break;
+ case 'Z': /* Ready For Query */
+ pqTraceOutputZ(conn->Pfdebug, message, &logCursor);
+ break;
+ default:
+ fprintf(conn->Pfdebug, "Unknown message: %02x", id);
+ break;
+ }
+
+ fputc('\n', conn->Pfdebug);
+
+ /*
+ * Verify the printing routine did it right. Note that the one-byte
+ * message identifier is not included in the length, but our cursor does
+ * include it.
+ */
+ if (logCursor - 1 != length)
+ fprintf(conn->Pfdebug,
+ "mismatched message length: consumed %d, expected %d\n",
+ logCursor - 1, length);
+}
+
+/*
+ * Print special messages (those containing no type byte) to the trace output
+ * stream.
+ */
+void
+pqTraceOutputNoTypeByteMessage(PGconn *conn, const char *message)
+{
+ int length;
+ int logCursor = 0;
+
+ if ((conn->traceFlags & PQTRACE_SUPPRESS_TIMESTAMPS) == 0)
+ {
+ char timestr[128];
+
+ pqTraceFormatTimestamp(timestr, sizeof(timestr));
+ fprintf(conn->Pfdebug, "%s\t", timestr);
+ }
+
+ memcpy(&length, message + logCursor, 4);
+ length = (int) pg_ntoh32(length);
+ logCursor += 4;
+
+ fprintf(conn->Pfdebug, "F\t%d\t", length);
+
+ switch (length)
+ {
+ case 16: /* CancelRequest */
+ fprintf(conn->Pfdebug, "CancelRequest\t");
+ pqTraceOutputInt32(conn->Pfdebug, message, &logCursor, false);
+ pqTraceOutputInt32(conn->Pfdebug, message, &logCursor, false);
+ pqTraceOutputInt32(conn->Pfdebug, message, &logCursor, false);
+ break;
+ case 8: /* GSSENCRequest or SSLRequest */
+ /* These messages do not reach here. */
+ default:
+ fprintf(conn->Pfdebug, "Unknown message: length is %d", length);
+ break;
+ }
+
+ fputc('\n', conn->Pfdebug);
+}
diff --git a/src/interfaces/libpq/legacy-pqsignal.c b/src/interfaces/libpq/legacy-pqsignal.c
new file mode 100644
index 0000000..37fa9e5
--- /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-2021, 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..8e24489
--- /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-2021, 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..30ed851
--- /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-2021, 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..a6fd69a
--- /dev/null
+++ b/src/interfaces/libpq/libpq-fe.h
@@ -0,0 +1,672 @@
+/*-------------------------------------------------------------------------
+ *
+ * libpq-fe.h
+ * This file contains definitions for structures and
+ * externs for functions used by frontend postgres applications.
+ *
+ * Portions Copyright (c) 1996-2021, 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"
+
+/*
+ * These symbols may be used in compile-time #ifdef tests for the availability
+ * of newer libpq features.
+ */
+/* Indicates presence of PQenterPipelineMode and friends */
+#define LIBPQ_HAS_PIPELINING 1
+/* Indicates presence of PQsetTraceFlags; also new PQtrace output format */
+#define LIBPQ_HAS_TRACE_FLAGS 1
+
+/*
+ * 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, /* This state is no longer used. */
+ CONNECTION_SSL_STARTUP, /* Negotiating SSL. */
+ CONNECTION_NEEDED, /* Internal state: connect() needed */
+ CONNECTION_CHECK_WRITABLE, /* Checking if session is read-write. */
+ CONNECTION_CONSUME, /* Consuming any extra messages. */
+ CONNECTION_GSS_STARTUP, /* Negotiating GSSAPI. */
+ CONNECTION_CHECK_TARGET, /* Checking target server properties. */
+ CONNECTION_CHECK_STANDBY /* Checking if server is in standby mode. */
+} 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 */
+ PGRES_PIPELINE_SYNC, /* pipeline synchronization point */
+ PGRES_PIPELINE_ABORTED /* Command didn't run because of an abort
+ * earlier in a pipeline */
+} 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;
+
+/*
+ * PGpipelineStatus - Current status of pipeline mode
+ */
+typedef enum
+{
+ PQ_PIPELINE_OFF,
+ PQ_PIPELINE_ON,
+ PQ_PIPELINE_ABORTED
+} PGpipelineStatus;
+
+/* 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 PGpipelineStatus PQpipelineStatus(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);
+
+/* 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-trace.c === */
+extern void PQtrace(PGconn *conn, FILE *debug_port);
+extern void PQuntrace(PGconn *conn);
+
+/* flags controlling trace output: */
+/* omit timestamps from each line */
+#define PQTRACE_SUPPRESS_TIMESTAMPS (1<<0)
+/* redact portions of some messages, for testing frameworks */
+#define PQTRACE_REGRESS_MODE (1<<1)
+extern void PQsetTraceFlags(PGconn *conn, int flags);
+
+/* === 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 */
+#define PQ_QUERY_PARAM_MAX_LIMIT 65535
+
+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);
+
+/* Routines for pipeline mode management */
+extern int PQenterPipelineMode(PGconn *conn);
+extern int PQexitPipelineMode(PGconn *conn);
+extern int PQpipelineSync(PGconn *conn);
+extern int PQsendFlushRequest(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);
+
+/* Same, but not more than the distance to the end of string s */
+extern int PQmblenBounded(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..df2f177
--- /dev/null
+++ b/src/interfaces/libpq/libpq-int.h
@@ -0,0 +1,866 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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-2021, 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, /* query done, waiting for client to fetch
+ * result */
+ PGASYNC_READY_MORE, /* query done, waiting for client to fetch
+ * result, more results expected from this
+ * query */
+ 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 */
+ PGASYNC_PIPELINE_IDLE, /* "Idle" between commands in pipeline mode */
+} PGAsyncStatusType;
+
+/* Target server type (decoded value of target_session_attrs) */
+typedef enum
+{
+ SERVER_TYPE_ANY = 0, /* Any server (default) */
+ SERVER_TYPE_READ_WRITE, /* Read-write server */
+ SERVER_TYPE_READ_ONLY, /* Read-only server */
+ SERVER_TYPE_PRIMARY, /* Primary server */
+ SERVER_TYPE_STANDBY, /* Standby server */
+ SERVER_TYPE_PREFER_STANDBY, /* Prefer standby server */
+ SERVER_TYPE_PREFER_STANDBY_PASS2 /* second pass - behaves same as ANY */
+} PGTargetServerType;
+
+/* Boolean value plus a not-known state, for GUCs we might have to fetch */
+typedef enum
+{
+ PG_BOOL_UNKNOWN = 0, /* Currently unknown */
+ PG_BOOL_YES, /* Yes (true) */
+ PG_BOOL_NO /* No (false) */
+} PGTernaryBool;
+
+/* 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;
+
+/*
+ * PGQueryClass tracks which query protocol is in use for each command queue
+ * entry, or special operation in execution
+ */
+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 */
+ PGQUERY_SYNC, /* Sync (at end of a pipeline) */
+ PGQUERY_CLOSE
+} PGQueryClass;
+
+/*
+ * An entry in the pending command queue.
+ */
+typedef struct PGcmdQueueEntry
+{
+ PGQueryClass queryclass; /* Query type */
+ char *query; /* SQL command, or NULL if none/unknown/OOM */
+ struct PGcmdQueueEntry *next; /* list link */
+} PGcmdQueueEntry;
+
+/*
+ * 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 *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 *sslcrldir; /* certificate revocation list directory name */
+ char *sslsni; /* use SSL SNI extension (0 or 1) */
+ 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 */
+ char *target_session_attrs; /* desired session properties */
+
+ /* Optional file to write trace info to */
+ FILE *Pfdebug;
+ int traceFlags;
+
+ /* 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 */
+ 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 */
+ PGpipelineStatus pipelineStatus; /* status of pipeline mode */
+ 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 */
+
+ /*
+ * The pending command queue as a singly-linked list. Head is the command
+ * currently in execution, tail is where new commands are added.
+ */
+ PGcmdQueueEntry *cmd_queue_head;
+ PGcmdQueueEntry *cmd_queue_tail;
+
+ /*
+ * To save malloc traffic, we don't free entries right away; instead we
+ * save them in this list for possible reuse.
+ */
+ PGcmdQueueEntry *cmd_queue_recycle;
+
+ /* 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 */
+ PGTargetServerType target_server_type; /* desired session properties */
+ 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 */
+ 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 */
+ PGTernaryBool default_transaction_read_only; /* default_transaction_read_only */
+ PGTernaryBool in_hot_standby; /* in_hot_standby */
+ 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
+ bool crypto_loaded; /* Track if libcrypto locking callbacks have
+ * been done for this connection. This can be
+ * removed once support for OpenSSL 1.0.2 is
+ * removed as this locking is handled
+ * internally in OpenSSL >= 1.1.0. */
+#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. This is cleared at the start of any
+ * connection attempt or query cycle; after that, all code should append
+ * messages to it, never overwrite.
+ */
+ 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, PQExpBuffer errorMessage);
+extern void *pqResultAlloc(PGresult *res, size_t nBytes, bool isBinary);
+extern char *pqResultStrdup(PGresult *res, const char *str);
+extern void pqClearAsyncResult(PGconn *conn);
+extern void pqSaveErrorResult(PGconn *conn);
+extern PGresult *pqPrepareAsyncResult(PGconn *conn);
+extern void pqInternalNotice(const PGNoticeHooks *hooks, const char *fmt,...) pg_attribute_printf(2, 3);
+extern void pqSaveMessageField(PGresult *res, char code,
+ const char *value);
+extern void pqSaveParameterStatus(PGconn *conn, const char *name,
+ const char *value);
+extern int pqRowProcessor(PGconn *conn, const char **errmsgp);
+extern void pqCommandQueueAdvance(PGconn *conn);
+extern int PQsendQueryContinue(PGconn *conn, const char *query);
+
+/* === 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, 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 *, bool, bool);
+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. do_ssl controls
+ * if SSL is initialized, and do_crypto does the same for the crypto
+ * part.
+ *
+ * Returns 0 if OK, -1 on failure (adding a message to conn->errorMessage).
+ */
+extern int pgtls_init(PGconn *conn, bool do_ssl, bool do_crypto);
+
+/*
+ * 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 appending a suitable message
+ * to 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 appending a suitable message
+ * to 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
+
+/* === in libpq-trace.c === */
+
+extern void pqTraceOutputMessage(PGconn *conn, const char *message,
+ bool toServer);
+extern void pqTraceOutputNoTypeByteMessage(PGconn *conn, const char *message);
+
+/* === 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)
+
+/*
+ * Connection's outbuffer threshold, for pipeline mode.
+ */
+#define OUTBUFFER_THRESHOLD 65536
+
+#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..a7e54cd
--- /dev/null
+++ b/src/interfaces/libpq/nls.mk
@@ -0,0 +1,6 @@
+# src/interfaces/libpq/nls.mk
+CATALOG_NAME = libpq
+AVAIL_LANGUAGES = cs de el es fr ja ko ru sv 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-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..1546180
--- /dev/null
+++ b/src/interfaces/libpq/po/de.po
@@ -0,0 +1,1221 @@
+# German message translation file for libpq
+# Peter Eisentraut <peter@eisentraut.org>, 2001 - 2022.
+#
+# Use these quotes: »%s«
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: PostgreSQL 14\n"
+"Report-Msgid-Bugs-To: pgsql-bugs@lists.postgresql.org\n"
+"POT-Creation-Date: 2022-07-19 08:55+0000\n"
+"PO-Revision-Date: 2022-04-01 11:08+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:213
+msgid "malformed SCRAM message (empty message)\n"
+msgstr "fehlerhafte SCRAM-Nachricht (leere Nachricht)\n"
+
+#: fe-auth-scram.c:219
+msgid "malformed SCRAM message (length mismatch)\n"
+msgstr "fehlerhafte SCRAM-Nachricht (Länge stimmt nicht überein)\n"
+
+#: fe-auth-scram.c:263
+msgid "could not verify server signature\n"
+msgstr "konnte Serversignatur nicht überprüfen\n"
+
+#: fe-auth-scram.c:270
+msgid "incorrect server signature\n"
+msgstr "falsche Serversignatur\n"
+
+#: fe-auth-scram.c:279
+msgid "invalid SCRAM exchange state\n"
+msgstr "ungültiger Zustand des SCRAM-Austauschs\n"
+
+#: fe-auth-scram.c:306
+#, c-format
+msgid "malformed SCRAM message (attribute \"%c\" expected)\n"
+msgstr "fehlerhafte SCRAM-Nachricht (Attribut »%c« erwartet)\n"
+
+#: fe-auth-scram.c:315
+#, 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:356
+msgid "could not generate nonce\n"
+msgstr "konnte Nonce nicht erzeugen\n"
+
+#: fe-auth-scram.c:366 fe-auth-scram.c:441 fe-auth-scram.c:595
+#: fe-auth-scram.c:616 fe-auth-scram.c:642 fe-auth-scram.c:657
+#: fe-auth-scram.c:707 fe-auth-scram.c:746 fe-auth.c:290 fe-auth.c:362
+#: fe-auth.c:398 fe-auth.c:615 fe-auth.c:774 fe-auth.c:1132 fe-auth.c:1282
+#: fe-connect.c:911 fe-connect.c:1460 fe-connect.c:1629 fe-connect.c:2981
+#: fe-connect.c:4711 fe-connect.c:4972 fe-connect.c:5091 fe-connect.c:5343
+#: fe-connect.c:5424 fe-connect.c:5523 fe-connect.c:5779 fe-connect.c:5808
+#: fe-connect.c:5880 fe-connect.c:5904 fe-connect.c:5922 fe-connect.c:6023
+#: fe-connect.c:6032 fe-connect.c:6390 fe-connect.c:6540 fe-connect.c:6806
+#: fe-exec.c:686 fe-exec.c:876 fe-exec.c:1223 fe-exec.c:3125 fe-exec.c:3309
+#: fe-exec.c:4082 fe-exec.c:4247 fe-gssapi-common.c:111 fe-lobj.c:881
+#: fe-protocol3.c:979 fe-protocol3.c:994 fe-protocol3.c:1027
+#: fe-protocol3.c:1735 fe-secure-common.c:110 fe-secure-gssapi.c:504
+#: fe-secure-openssl.c:440 fe-secure-openssl.c:1133
+msgid "out of memory\n"
+msgstr "Speicher aufgebraucht\n"
+
+#: fe-auth-scram.c:374
+msgid "could not encode nonce\n"
+msgstr "konnte Nonce nicht kodieren\n"
+
+#: fe-auth-scram.c:563
+msgid "could not calculate client proof\n"
+msgstr "konnte Client-Proof nicht berechnen\n"
+
+#: fe-auth-scram.c:579
+msgid "could not encode client proof\n"
+msgstr "konnte Client-Proof nicht kodieren\n"
+
+#: fe-auth-scram.c:634
+msgid "invalid SCRAM response (nonce mismatch)\n"
+msgstr "ungültige SCRAM-Antwort (Nonce stimmt nicht überein)\n"
+
+#: fe-auth-scram.c:667
+msgid "malformed SCRAM message (invalid salt)\n"
+msgstr "fehlerhafte SCRAM-Nachricht (ungültiges Salt)\n"
+
+#: fe-auth-scram.c:681
+msgid "malformed SCRAM message (invalid iteration count)\n"
+msgstr "fehlerhafte SCRAM-Nachricht (ungültige Iterationszahl)\n"
+
+#: fe-auth-scram.c:687
+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:723
+#, 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:739
+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:758
+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:391 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:351
+msgid "duplicate SSPI authentication request\n"
+msgstr "doppelte SSPI-Authentifizierungsanfrage\n"
+
+#: fe-auth.c:377
+msgid "could not acquire SSPI credentials"
+msgstr "konnte SSPI-Credentials nicht erhalten"
+
+#: fe-auth.c:433
+msgid "channel binding required, but SSL not in use\n"
+msgstr "Channel-Binding wurde verlangt, aber SSL wird nicht verwendet\n"
+
+#: fe-auth.c:440
+msgid "duplicate SASL authentication request\n"
+msgstr "doppelte SASL-Authentifizierungsanfrage\n"
+
+#: fe-auth.c:496
+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:513
+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:525
+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:533
+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:639
+#, c-format
+msgid "out of memory allocating SASL buffer (%d)\n"
+msgstr "Speicher aufgebraucht beim Anlegen des SASL-Puffers (%d)\n"
+
+#: fe-auth.c:664
+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:741
+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:877
+msgid "Kerberos 4 authentication not supported\n"
+msgstr "Authentifizierung mit Kerberos 4 nicht unterstützt\n"
+
+#: fe-auth.c:882
+msgid "Kerberos 5 authentication not supported\n"
+msgstr "Authentifizierung mit Kerberos 5 nicht unterstützt\n"
+
+#: fe-auth.c:953
+msgid "GSSAPI authentication not supported\n"
+msgstr "Authentifizierung mit GSSAPI nicht unterstützt\n"
+
+#: fe-auth.c:985
+msgid "SSPI authentication not supported\n"
+msgstr "Authentifizierung mit SSPI nicht unterstützt\n"
+
+#: fe-auth.c:993
+msgid "Crypt authentication not supported\n"
+msgstr "Authentifizierung mit Crypt nicht unterstützt\n"
+
+#: fe-auth.c:1060
+#, c-format
+msgid "authentication method %u not supported\n"
+msgstr "Authentifizierungsmethode %u nicht unterstützt\n"
+
+#: fe-auth.c:1107
+#, c-format
+msgid "user name lookup failure: error code %lu\n"
+msgstr "Fehler beim Nachschlagen des Benutzernamens: Fehlercode %lu\n"
+
+#: fe-auth.c:1117 fe-connect.c:2856
+#, 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:1122 fe-connect.c:2861
+#, c-format
+msgid "local user with ID %d does not exist\n"
+msgstr "lokaler Benutzer mit ID %d existiert nicht\n"
+
+#: fe-auth.c:1226
+msgid "unexpected shape of result set returned for SHOW\n"
+msgstr "unerwartete Form der Ergebnismenge von SHOW\n"
+
+#: fe-auth.c:1235
+msgid "password_encryption value too long\n"
+msgstr "Wert von password_encryption ist zu lang\n"
+
+#: fe-auth.c:1275
+#, c-format
+msgid "unrecognized password encryption algorithm \"%s\"\n"
+msgstr "unbekannter Passwortverschlüsselungsalgorithmus »%s«\n"
+
+#: fe-connect.c:1094
+#, 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:1180
+#, 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:1273 fe-connect.c:1299 fe-connect.c:1341 fe-connect.c:1350
+#: fe-connect.c:1383 fe-connect.c:1427
+#, c-format
+msgid "invalid %s value: \"%s\"\n"
+msgstr "ungültiger %s-Wert: »%s«\n"
+
+#: fe-connect.c:1320
+#, 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:1368
+msgid "invalid SSL protocol version range\n"
+msgstr "ungültiges SSL-Protokollsintervall\n"
+
+#: fe-connect.c:1393
+#, 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:1653
+#, 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:1715
+#, c-format
+msgid "connection to server on socket \"%s\" failed: "
+msgstr "Verbindung zum Server auf Socket »%s« fehlgeschlagen: "
+
+#: fe-connect.c:1742
+#, c-format
+msgid "connection to server at \"%s\" (%s), port %s failed: "
+msgstr "Verbindung zum Server auf »%s« (%s), Port %s fehlgeschlagen: "
+
+#: fe-connect.c:1747
+#, c-format
+msgid "connection to server at \"%s\", port %s failed: "
+msgstr "Verbindung zum Server auf »%s«, Port %s fehlgeschlagen: "
+
+#: fe-connect.c:1772
+msgid "\tIs the server running locally and accepting connections on that socket?\n"
+msgstr "\tLäuft der Server lokal und akzeptiert er Verbindungen auf diesem Socket?\n"
+
+#: fe-connect.c:1776
+msgid "\tIs the server running on that host and accepting TCP/IP connections?\n"
+msgstr "\tLäuft der Server auf diesem Host und akzeptiert er TCP/IP-Verbindungen?\n"
+
+#: fe-connect.c:1840
+#, 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:1870 fe-connect.c:1905 fe-connect.c:1941 fe-connect.c:2030
+#: fe-connect.c:2644
+#, c-format
+msgid "%s(%s) failed: %s\n"
+msgstr "%s(%s) fehlgeschlagen: %s\n"
+
+#: fe-connect.c:1995
+#, c-format
+msgid "%s(%s) failed: error code %d\n"
+msgstr "%s(%s) fehlgeschlagen: Fehlercode %d\n"
+
+#: fe-connect.c:2310
+msgid "invalid connection state, probably indicative of memory corruption\n"
+msgstr "ungültiger Verbindungszustand, möglicherweise ein Speicherproblem\n"
+
+#: fe-connect.c:2389
+#, c-format
+msgid "invalid port number: \"%s\"\n"
+msgstr "ungültige Portnummer: »%s«\n"
+
+#: fe-connect.c:2405
+#, 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:2418
+#, c-format
+msgid "could not parse network address \"%s\": %s\n"
+msgstr "konnte Netzwerkadresse »%s« nicht interpretieren: %s\n"
+
+#: fe-connect.c:2431
+#, 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:2446
+#, 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:2572
+#, c-format
+msgid "could not create socket: %s\n"
+msgstr "konnte Socket nicht erzeugen: %s\n"
+
+#: fe-connect.c:2603
+#, 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:2613
+#, 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:2631
+msgid "keepalives parameter must be an integer\n"
+msgstr "Parameter »keepalives« muss eine ganze Zahl sein\n"
+
+#: fe-connect.c:2772
+#, c-format
+msgid "could not get socket error status: %s\n"
+msgstr "konnte Socket-Fehlerstatus nicht ermitteln: %s\n"
+
+#: fe-connect.c:2800
+#, 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:2842
+msgid "requirepeer parameter is not supported on this platform\n"
+msgstr "Parameter »requirepeer« wird auf dieser Plattform nicht unterstützt\n"
+
+#: fe-connect.c:2845
+#, c-format
+msgid "could not get peer credentials: %s\n"
+msgstr "konnte Credentials von Gegenstelle nicht ermitteln: %s\n"
+
+#: fe-connect.c:2869
+#, 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:2909
+#, c-format
+msgid "could not send GSSAPI negotiation packet: %s\n"
+msgstr "konnte Paket zur GSSAPI-Verhandlung nicht senden: %s\n"
+
+#: fe-connect.c:2921
+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:2963
+#, c-format
+msgid "could not send SSL negotiation packet: %s\n"
+msgstr "konnte Paket zur SSL-Verhandlung nicht senden: %s\n"
+
+#: fe-connect.c:2994
+#, c-format
+msgid "could not send startup packet: %s\n"
+msgstr "konnte Startpaket nicht senden: %s\n"
+
+#: fe-connect.c:3070
+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:3097
+#, c-format
+msgid "received invalid response to SSL negotiation: %c\n"
+msgstr "ungültige Antwort auf SSL-Verhandlungspaket empfangen: %c\n"
+
+#: fe-connect.c:3118
+msgid "received unencrypted data after SSL response\n"
+msgstr "unverschlüsselte Daten nach SSL-Antwort empfangen\n"
+
+#: fe-connect.c:3199
+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:3211
+#, c-format
+msgid "received invalid response to GSSAPI negotiation: %c\n"
+msgstr "ungültige Antwort auf GSSAPI-Verhandlungspaket empfangen: %c\n"
+
+#: fe-connect.c:3230
+msgid "received unencrypted data after GSSAPI encryption response\n"
+msgstr "unverschlüsselte Daten nach GSSAPI-Verschlüsselungsantwort empfangen\n"
+
+#: fe-connect.c:3290 fe-connect.c:3315
+#, 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:3522
+msgid "unexpected message from server during startup\n"
+msgstr "unerwartete Nachricht vom Server beim Start\n"
+
+#: fe-connect.c:3614
+msgid "session is read-only\n"
+msgstr "Sitzung ist read-only\n"
+
+#: fe-connect.c:3617
+msgid "session is not read-only\n"
+msgstr "Sitzung ist nicht read-only\n"
+
+#: fe-connect.c:3671
+msgid "server is in hot standby mode\n"
+msgstr "Server ist im Hot-Standby-Modus\n"
+
+#: fe-connect.c:3674
+msgid "server is not in hot standby mode\n"
+msgstr "Server ist nicht im Hot-Standby-Modus\n"
+
+#: fe-connect.c:3792 fe-connect.c:3844
+#, c-format
+msgid "\"%s\" failed\n"
+msgstr "»%s« fehlgeschlagen\n"
+
+#: fe-connect.c:3858
+#, 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:4304 fe-connect.c:4364
+#, c-format
+msgid "PGEventProc \"%s\" failed during PGEVT_CONNRESET event\n"
+msgstr "PGEventProc »%s« während PGEVT_CONNRESET-Ereignis fehlgeschlagen\n"
+
+#: fe-connect.c:4724
+#, 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:4739
+#, c-format
+msgid "invalid LDAP URL \"%s\": missing distinguished name\n"
+msgstr "ungültige LDAP-URL »%s«: Distinguished Name fehlt\n"
+
+#: fe-connect.c:4751 fe-connect.c:4809
+#, 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:4763 fe-connect.c:4825
+#, 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:4775
+#, c-format
+msgid "invalid LDAP URL \"%s\": no filter\n"
+msgstr "ungültige LDAP-URL »%s«: kein Filter\n"
+
+#: fe-connect.c:4797
+#, c-format
+msgid "invalid LDAP URL \"%s\": invalid port number\n"
+msgstr "ungültige LDAP-URL »%s«: ungültige Portnummer\n"
+
+#: fe-connect.c:4835
+msgid "could not create LDAP structure\n"
+msgstr "konnte LDAP-Struktur nicht erzeugen\n"
+
+#: fe-connect.c:4911
+#, c-format
+msgid "lookup on LDAP server failed: %s\n"
+msgstr "Suche auf LDAP-Server fehlgeschlagen: %s\n"
+
+#: fe-connect.c:4922
+msgid "more than one entry found on LDAP lookup\n"
+msgstr "LDAP-Suche ergab mehr als einen Eintrag\n"
+
+#: fe-connect.c:4923 fe-connect.c:4935
+msgid "no entry found on LDAP lookup\n"
+msgstr "kein Eintrag gefunden bei LDAP-Suche\n"
+
+#: fe-connect.c:4946 fe-connect.c:4959
+msgid "attribute has no values on LDAP lookup\n"
+msgstr "Attribut hat keine Werte bei LDAP-Suche\n"
+
+#: fe-connect.c:5011 fe-connect.c:5030 fe-connect.c:5562
+#, c-format
+msgid "missing \"=\" after \"%s\" in connection info string\n"
+msgstr "fehlendes »=« nach »%s« in der Zeichenkette der Verbindungsdaten\n"
+
+#: fe-connect.c:5103 fe-connect.c:5747 fe-connect.c:6523
+#, c-format
+msgid "invalid connection option \"%s\"\n"
+msgstr "ungültige Verbindungsoption »%s«\n"
+
+#: fe-connect.c:5119 fe-connect.c:5611
+msgid "unterminated quoted string in connection info string\n"
+msgstr "fehlendes schließendes Anführungszeichen (\") in der Zeichenkette der Verbindungsdaten\n"
+
+#: fe-connect.c:5200
+#, c-format
+msgid "definition of service \"%s\" not found\n"
+msgstr "Definition von Service »%s« nicht gefunden\n"
+
+#: fe-connect.c:5226
+#, c-format
+msgid "service file \"%s\" not found\n"
+msgstr "Servicedatei »%s« nicht gefunden\n"
+
+#: fe-connect.c:5240
+#, c-format
+msgid "line %d too long in service file \"%s\"\n"
+msgstr "Zeile %d zu lang in Servicedatei »%s«\n"
+
+#: fe-connect.c:5311 fe-connect.c:5355
+#, c-format
+msgid "syntax error in service file \"%s\", line %d\n"
+msgstr "Syntaxfehler in Servicedatei »%s«, Zeile %d\n"
+
+#: fe-connect.c:5322
+#, 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:6043
+#, 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:6120
+#, 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:6127
+#, 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:6142
+#, 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:6272
+#, 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:6292
+#, 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:6344
+#, c-format
+msgid "invalid URI query parameter: \"%s\"\n"
+msgstr "ungültiger URI-Query-Parameter: »%s«\n"
+
+#: fe-connect.c:6418
+#, c-format
+msgid "invalid percent-encoded token: \"%s\"\n"
+msgstr "ungültiges Prozent-kodiertes Token: »%s«\n"
+
+#: fe-connect.c:6428
+#, 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:6798
+msgid "connection pointer is NULL\n"
+msgstr "Verbindung ist ein NULL-Zeiger\n"
+
+#: fe-connect.c:7086
+#, 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:7095
+#, 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:7203
+#, c-format
+msgid "password retrieved from file \"%s\"\n"
+msgstr "Passwort wurde aus Datei »%s« gelesen\n"
+
+#: fe-exec.c:449 fe-exec.c:3383
+#, 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:510 fe-protocol3.c:207 fe-protocol3.c:232 fe-protocol3.c:261
+#: fe-protocol3.c:279 fe-protocol3.c:375 fe-protocol3.c:747
+msgid "out of memory"
+msgstr "Speicher aufgebraucht"
+
+#: fe-exec.c:511 fe-protocol3.c:1943
+#, c-format
+msgid "%s"
+msgstr "%s"
+
+#: fe-exec.c:792
+msgid "write to server failed\n"
+msgstr "Schreiben zum Server fehlgeschlagen\n"
+
+#: fe-exec.c:864
+msgid "NOTICE"
+msgstr "HINWEIS"
+
+#: fe-exec.c:922
+msgid "PGresult cannot support more than INT_MAX tuples"
+msgstr "PGresult kann nicht mehr als INT_MAX Tupel enthalten"
+
+#: fe-exec.c:934
+msgid "size_t overflow"
+msgstr "Überlauf von size_t"
+
+#: fe-exec.c:1351 fe-exec.c:1477 fe-exec.c:1526
+msgid "command string is a null pointer\n"
+msgstr "Befehlszeichenkette ist ein NULL-Zeiger\n"
+
+#: fe-exec.c:1483 fe-exec.c:1532 fe-exec.c:1628
+#, c-format
+msgid "number of parameters must be between 0 and %d\n"
+msgstr "Anzahl der Parameter muss zwischen 0 und %d sein\n"
+
+#: fe-exec.c:1520 fe-exec.c:1622
+msgid "statement name is a null pointer\n"
+msgstr "Anweisungsname ist ein NULL-Zeiger\n"
+
+#: fe-exec.c:1664 fe-exec.c:3236
+msgid "no connection to the server\n"
+msgstr "keine Verbindung mit dem Server\n"
+
+#: fe-exec.c:1673 fe-exec.c:3245
+msgid "another command is already in progress\n"
+msgstr "ein anderer Befehl ist bereits in Ausführung\n"
+
+#: fe-exec.c:1704
+msgid "cannot queue commands during COPY\n"
+msgstr "während COPY können keine Befehle aufgereiht werden\n"
+
+#: fe-exec.c:1822
+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:2149
+#, c-format
+msgid "unexpected asyncStatus: %d\n"
+msgstr "unerwarteter asyncStatus: %d\n"
+
+#: fe-exec.c:2185
+#, c-format
+msgid "PGEventProc \"%s\" failed during PGEVT_RESULTCREATE event\n"
+msgstr "PGEventProc »%s« während PGEVT_RESULTCREATE-Ereignis fehlgeschlagen\n"
+
+#: fe-exec.c:2333
+msgid "synchronous command execution functions are not allowed in pipeline mode\n"
+msgstr "synchrone Befehlsausführungsfunktionen sind im Pipeline-Modus nicht erlaubt\n"
+
+#: fe-exec.c:2355
+msgid "COPY terminated by new PQexec"
+msgstr "COPY von neuem PQexec beendet"
+
+#: fe-exec.c:2372
+msgid "PQexec not allowed during COPY BOTH\n"
+msgstr "PQexec ist während COPY BOTH nicht erlaubt\n"
+
+#: fe-exec.c:2600 fe-exec.c:2656 fe-exec.c:2725 fe-protocol3.c:1874
+msgid "no COPY in progress\n"
+msgstr "keine COPY in Ausführung\n"
+
+#: fe-exec.c:2902
+msgid "PQfn not allowed in pipeline mode\n"
+msgstr "PQfn im Pipeline-Modus nicht erlaubt\n"
+
+#: fe-exec.c:2910
+msgid "connection in wrong state\n"
+msgstr "Verbindung im falschen Zustand\n"
+
+#: fe-exec.c:2954
+msgid "cannot enter pipeline mode, connection not idle\n"
+msgstr "kann Pipeline-Modus nicht einschalten, Verbindung ist nicht inaktiv\n"
+
+#: fe-exec.c:2991 fe-exec.c:3015
+msgid "cannot exit pipeline mode with uncollected results\n"
+msgstr "kann Pipeline-Modus nicht beenden, wegen nicht eingesammelter Ergebnisse\n"
+
+#: fe-exec.c:2996
+msgid "cannot exit pipeline mode while busy\n"
+msgstr "kann Pipeline-Modus nicht beenden während die Verbindung beschäftigt ist\n"
+
+#: fe-exec.c:3008
+msgid "cannot exit pipeline mode while in COPY\n"
+msgstr "kann Pipeline-Modus nicht beenden während COPY aktiv ist\n"
+
+#: fe-exec.c:3169
+msgid "cannot send pipeline when not in pipeline mode\n"
+msgstr "Pipeline kann nicht gesendet werden, wenn der Pipeline-Modus aus ist\n"
+
+#: fe-exec.c:3272
+msgid "invalid ExecStatusType code"
+msgstr "ungültiger ExecStatusType-Kode"
+
+#: fe-exec.c:3299
+msgid "PGresult is not an error result\n"
+msgstr "PGresult ist kein Fehlerresultat\n"
+
+#: fe-exec.c:3367 fe-exec.c:3390
+#, 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:3405
+#, 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:3715
+#, c-format
+msgid "could not interpret result from server: %s"
+msgstr "konnte Ergebnis vom Server nicht interpretieren: %s"
+
+#: fe-exec.c:3975 fe-exec.c:4064
+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:145 fe-lobj.c:210 fe-lobj.c:403 fe-lobj.c:494 fe-lobj.c:568
+#: fe-lobj.c:969 fe-lobj.c:977 fe-lobj.c:985 fe-lobj.c:993 fe-lobj.c:1001
+#: fe-lobj.c:1009 fe-lobj.c:1017 fe-lobj.c:1025
+#, c-format
+msgid "cannot determine OID of function %s\n"
+msgstr "kann OID der Funktion %s nicht ermitteln\n"
+
+#: fe-lobj.c:162
+msgid "argument of lo_truncate exceeds integer range\n"
+msgstr "Argument von lo_truncate überschreitet Bereich für ganze Zahlen\n"
+
+#: fe-lobj.c:266
+msgid "argument of lo_read exceeds integer range\n"
+msgstr "Argument von lo_read überschreitet Bereich für ganze Zahlen\n"
+
+#: fe-lobj.c:318
+msgid "argument of lo_write exceeds integer range\n"
+msgstr "Argument von lo_write überschreitet Bereich für ganze Zahlen\n"
+
+#: fe-lobj.c:678 fe-lobj.c:789
+#, c-format
+msgid "could not open file \"%s\": %s\n"
+msgstr "konnte Datei »%s« nicht öffnen: %s\n"
+
+#: fe-lobj.c:734
+#, c-format
+msgid "could not read from file \"%s\": %s\n"
+msgstr "konnte nicht aus Datei »%s« lesen: %s\n"
+
+#: fe-lobj.c:810 fe-lobj.c:834
+#, c-format
+msgid "could not write to file \"%s\": %s\n"
+msgstr "konnte nicht in Datei »%s« schreiben: %s\n"
+
+#: fe-lobj.c:920
+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-misc.c:242
+#, 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:275
+#, 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:576 fe-misc.c:822
+msgid "connection not open\n"
+msgstr "Verbindung nicht offen\n"
+
+#: fe-misc.c:755 fe-secure-openssl.c:209 fe-secure-openssl.c:316
+#: fe-secure.c:260 fe-secure.c:373
+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:1015
+msgid "timeout expired\n"
+msgstr "Timeout abgelaufen\n"
+
+#: fe-misc.c:1060
+msgid "invalid socket\n"
+msgstr "ungültiges Socket\n"
+
+#: fe-misc.c:1083
+#, c-format
+msgid "%s() failed: %s\n"
+msgstr "%s() fehlgeschlagen: %s\n"
+
+#: fe-protocol3.c:184
+#, c-format
+msgid "message type 0x%02x arrived from server while idle"
+msgstr "Nachricht vom Typ 0x%02x kam vom Server im Ruhezustand"
+
+#: fe-protocol3.c:407
+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:450
+#, 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-protocol3.c:475
+#, 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:495
+#, 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:547 fe-protocol3.c:587
+msgid "insufficient data in \"T\" message"
+msgstr "nicht genug Daten in »T«-Nachricht"
+
+#: fe-protocol3.c:658 fe-protocol3.c:864
+msgid "out of memory for query result"
+msgstr "Speicher für Anfrageergebnis aufgebraucht"
+
+#: fe-protocol3.c:727
+msgid "insufficient data in \"t\" message"
+msgstr "nicht genug Daten in »t«-Nachricht"
+
+#: fe-protocol3.c:786 fe-protocol3.c:818 fe-protocol3.c:836
+msgid "insufficient data in \"D\" message"
+msgstr "nicht genug Daten in »D«-Nachricht"
+
+#: fe-protocol3.c:792
+msgid "unexpected field count in \"D\" message"
+msgstr "unerwartete Feldzahl in »D«-Nachricht"
+
+#: fe-protocol3.c:1040
+msgid "no error message available\n"
+msgstr "keine Fehlermeldung verfügbar\n"
+
+#. translator: %s represents a digit string
+#: fe-protocol3.c:1088 fe-protocol3.c:1107
+#, c-format
+msgid " at character %s"
+msgstr " bei Zeichen %s"
+
+#: fe-protocol3.c:1120
+#, c-format
+msgid "DETAIL: %s\n"
+msgstr "DETAIL: %s\n"
+
+#: fe-protocol3.c:1123
+#, c-format
+msgid "HINT: %s\n"
+msgstr "TIP: %s\n"
+
+#: fe-protocol3.c:1126
+#, c-format
+msgid "QUERY: %s\n"
+msgstr "ANFRAGE: %s\n"
+
+#: fe-protocol3.c:1133
+#, c-format
+msgid "CONTEXT: %s\n"
+msgstr "KONTEXT: %s\n"
+
+#: fe-protocol3.c:1142
+#, c-format
+msgid "SCHEMA NAME: %s\n"
+msgstr "SCHEMANAME: %s\n"
+
+#: fe-protocol3.c:1146
+#, c-format
+msgid "TABLE NAME: %s\n"
+msgstr "TABELLENNAME: %s\n"
+
+#: fe-protocol3.c:1150
+#, c-format
+msgid "COLUMN NAME: %s\n"
+msgstr "SPALTENNAME: %s\n"
+
+#: fe-protocol3.c:1154
+#, c-format
+msgid "DATATYPE NAME: %s\n"
+msgstr "DATENTYPNAME: %s\n"
+
+#: fe-protocol3.c:1158
+#, c-format
+msgid "CONSTRAINT NAME: %s\n"
+msgstr "CONSTRAINT-NAME: %s\n"
+
+#: fe-protocol3.c:1170
+msgid "LOCATION: "
+msgstr "ORT: "
+
+#: fe-protocol3.c:1172
+#, c-format
+msgid "%s, "
+msgstr "%s, "
+
+#: fe-protocol3.c:1174
+#, c-format
+msgid "%s:%s"
+msgstr "%s:%s"
+
+#: fe-protocol3.c:1369
+#, c-format
+msgid "LINE %d: "
+msgstr "ZEILE %d: "
+
+#: fe-protocol3.c:1768
+msgid "PQgetline: not doing text COPY OUT\n"
+msgstr "PQgetline: Text COPY OUT nicht ausgeführt\n"
+
+#: fe-protocol3.c:2134
+#, c-format
+msgid "protocol error: id=0x%x\n"
+msgstr "Protokollfehler: id=0x%x\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:1367
+#, 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:1371
+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:1380
+#, 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:1430
+#, 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:822
+#, c-format
+msgid "could not create SSL context: %s\n"
+msgstr "konnte SSL-Kontext nicht erzeugen: %s\n"
+
+#: fe-secure-openssl.c:861
+#, 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:872
+#, 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:890
+#, 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:901
+#, 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:937
+#, 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:990
+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:994
+#, 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:1025
+#, c-format
+msgid "could not open certificate file \"%s\": %s\n"
+msgstr "konnte Zertifikatdatei »%s« nicht öffnen: %s\n"
+
+#: fe-secure-openssl.c:1044
+#, c-format
+msgid "could not read certificate file \"%s\": %s\n"
+msgstr "konnte Zertifikatdatei »%s« nicht lesen: %s\n"
+
+#: fe-secure-openssl.c:1069
+#, c-format
+msgid "could not establish SSL connection: %s\n"
+msgstr "konnte SSL-Verbindung nicht aufbauen: %s\n"
+
+#: fe-secure-openssl.c:1103
+#, c-format
+msgid "could not set SSL Server Name Indication (SNI): %s\n"
+msgstr "konnte SSL-Server-Name-Indication (SNI) nicht setzen: %s\n"
+
+#: fe-secure-openssl.c:1149
+#, c-format
+msgid "could not load SSL engine \"%s\": %s\n"
+msgstr "konnte SSL-Engine »%s« nicht laden: %s\n"
+
+#: fe-secure-openssl.c:1161
+#, c-format
+msgid "could not initialize SSL engine \"%s\": %s\n"
+msgstr "konnte SSL-Engine »%s« nicht initialisieren: %s\n"
+
+#: fe-secure-openssl.c:1177
+#, 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:1191
+#, 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:1228
+#, 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:1237
+#, c-format
+msgid "private key file \"%s\" is not a regular file\n"
+msgstr "private Schlüsseldatei »%s« ist keine normale Datei\n"
+
+#: fe-secure-openssl.c:1270
+#, c-format
+msgid "private key file \"%s\" has group or world access; file must have permissions u=rw (0600) or less if owned by the current user, or permissions u=rw,g=r (0640) or less if owned by root\n"
+msgstr "private Schlüsseldatei »%s« erlaubt Lesezugriff für Gruppe oder Andere; Dateirechte müssen u=rw (0600) oder weniger sein, wenn der Eigentümer der aktuelle Benutzer ist, oder u=rw,g=r (0640) oder weniger, wenn der Eigentümer »root« ist\n"
+
+#: fe-secure-openssl.c:1295
+#, 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:1313
+#, 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:1413
+#, 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:1449
+#, c-format
+msgid "certificate could not be obtained: %s\n"
+msgstr "Zertifikat konnte nicht ermittelt werden: %s\n"
+
+#: fe-secure-openssl.c:1555
+#, c-format
+msgid "no SSL error reported"
+msgstr "kein SSL-Fehler berichtet"
+
+#: fe-secure-openssl.c:1564
+#, c-format
+msgid "SSL error code %lu"
+msgstr "SSL-Fehlercode %lu"
+
+#: fe-secure-openssl.c:1812
+#, c-format
+msgid "WARNING: sslpassword truncated\n"
+msgstr "WARNUNG: sslpassword abgeschnitten\n"
+
+#: fe-secure.c:267
+#, c-format
+msgid "could not receive data from server: %s\n"
+msgstr "konnte keine Daten vom Server empfangen: %s\n"
+
+#: fe-secure.c:380
+#, 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/el.po b/src/interfaces/libpq/po/el.po
new file mode 100644
index 0000000..cb286d4
--- /dev/null
+++ b/src/interfaces/libpq/po/el.po
@@ -0,0 +1,1314 @@
+# Greek message translation file for libpq
+# Copyright (C) 2021 PostgreSQL Global Development Group
+# This file is distributed under the same license as the libpq (PostgreSQL) package.
+# Georgios Kokolatos <gkokolatos@pm.me>, 2021
+#
+#
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: libpq (PostgreSQL) 14\n"
+"Report-Msgid-Bugs-To: pgsql-bugs@lists.postgresql.org\n"
+"POT-Creation-Date: 2021-07-14 05:09+0000\n"
+"PO-Revision-Date: 2021-07-14 10:16+0200\n"
+"Last-Translator: Georgios Kokolatos <gkokolatos@pm.me>\n"
+"Language-Team: \n"
+"Language: el\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Poedit 3.0\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: fe-auth-scram.c:213
+msgid "malformed SCRAM message (empty message)\n"
+msgstr "κακοσχηματισμένο μήνυμα SCRAM (κενό μήνυμα)\n"
+
+#: fe-auth-scram.c:219
+msgid "malformed SCRAM message (length mismatch)\n"
+msgstr "κακοσχηματισμένο μήνυμα SCRAM (αναντιστοιχία μήκους)\n"
+
+#: fe-auth-scram.c:263
+msgid "could not verify server signature\n"
+msgstr "δεν ήταν δυνατή πιστοποίηση της υπογραφής του διακομιστή\n"
+
+#: fe-auth-scram.c:270
+msgid "incorrect server signature\n"
+msgstr "λανθασμένη υπογραφή διακομιστή\n"
+
+#: fe-auth-scram.c:279
+msgid "invalid SCRAM exchange state\n"
+msgstr "άκυρη κατάσταση ανταλλαγής SCRAM\n"
+
+#: fe-auth-scram.c:306
+#, c-format
+msgid "malformed SCRAM message (attribute \"%c\" expected)\n"
+msgstr "κακοσχηματισμένο μήνυμα SCRAM (αναμένεται χαρακτηριστικό «%c»)\n"
+
+#: fe-auth-scram.c:315
+#, c-format
+msgid "malformed SCRAM message (expected character \"=\" for attribute \"%c\")\n"
+msgstr "κακοσχηματισμένο μήνυμα SCRAM (αναμένεται χαρακτήρας «=» για το χαρακτηριστικό «%c»)\n"
+
+#: fe-auth-scram.c:356
+msgid "could not generate nonce\n"
+msgstr "δεν δύναται να δημιουργήσει nonce\n"
+
+#: fe-auth-scram.c:366 fe-auth-scram.c:441 fe-auth-scram.c:595
+#: fe-auth-scram.c:616 fe-auth-scram.c:642 fe-auth-scram.c:657
+#: fe-auth-scram.c:707 fe-auth-scram.c:746 fe-auth.c:290 fe-auth.c:362
+#: fe-auth.c:398 fe-auth.c:615 fe-auth.c:774 fe-auth.c:1132 fe-auth.c:1282
+#: fe-connect.c:911 fe-connect.c:1455 fe-connect.c:1624 fe-connect.c:2976
+#: fe-connect.c:4657 fe-connect.c:4918 fe-connect.c:5037 fe-connect.c:5289
+#: fe-connect.c:5370 fe-connect.c:5469 fe-connect.c:5725 fe-connect.c:5754
+#: fe-connect.c:5826 fe-connect.c:5850 fe-connect.c:5868 fe-connect.c:5969
+#: fe-connect.c:5978 fe-connect.c:6336 fe-connect.c:6486 fe-exec.c:1209
+#: fe-exec.c:3029 fe-exec.c:3212 fe-exec.c:3985 fe-exec.c:4150
+#: fe-gssapi-common.c:111 fe-lobj.c:881 fe-protocol3.c:1016 fe-protocol3.c:1724
+#: fe-secure-common.c:110 fe-secure-gssapi.c:504 fe-secure-openssl.c:440
+#: fe-secure-openssl.c:1133
+msgid "out of memory\n"
+msgstr "έλλειψη μνήμης\n"
+
+#: fe-auth-scram.c:374
+msgid "could not encode nonce\n"
+msgstr "δεν δύναται να κωδικοποιήσει nonce\n"
+
+#: fe-auth-scram.c:563
+msgid "could not calculate client proof\n"
+msgstr "δεν δύναται να υπολογίσει την απόδειξη του πελάτη\n"
+
+#: fe-auth-scram.c:579
+msgid "could not encode client proof\n"
+msgstr "δεν δύναται να κωδικοποιήσει την απόδειξη του πελάτη\n"
+
+#: fe-auth-scram.c:634
+msgid "invalid SCRAM response (nonce mismatch)\n"
+msgstr "μη έγκυρη απόκριση SCRAM (ασυμφωνία nonce)\n"
+
+#: fe-auth-scram.c:667
+msgid "malformed SCRAM message (invalid salt)\n"
+msgstr "κακοσχηματισμένο μήνυμα SCRAM (άκυρο salt)\n"
+
+#: fe-auth-scram.c:681
+msgid "malformed SCRAM message (invalid iteration count)\n"
+msgstr "κακοσχηματισμένο μήνυμα SCRAM (άκυρη μέτρηση επαναλήψεων)\n"
+
+#: fe-auth-scram.c:687
+msgid "malformed SCRAM message (garbage at end of server-first-message)\n"
+msgstr "κακοσχηματισμένο μήνυμα SCRAM (σκουπίδια στο τέλος του πρώτου-μηνύματος-διακομιστή)\n"
+
+#: fe-auth-scram.c:723
+#, c-format
+msgid "error received from server in SCRAM exchange: %s\n"
+msgstr "ελήφθει σφάλμα από τον διακομιστή κατά την ανταλλαγή SCRAM: %s\n"
+
+#: fe-auth-scram.c:739
+msgid "malformed SCRAM message (garbage at end of server-final-message)\n"
+msgstr "κακοσχηματισμένο μήνυμα SCRAM (σκουπίδια στο τέλος του τελικού-μηνύματος-διακομιστή)\n"
+
+#: fe-auth-scram.c:758
+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:391 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:351
+msgid "duplicate SSPI authentication request\n"
+msgstr "διπλότυπη αίτηση ελέγχου ταυτότητας SSPI\n"
+
+#: fe-auth.c:377
+msgid "could not acquire SSPI credentials"
+msgstr "δεν δύναται η απόκτηση διαπιστευτηρίων SSPI"
+
+#: fe-auth.c:433
+msgid "channel binding required, but SSL not in use\n"
+msgstr "απαιτείται σύνδεση καναλιού, αλλά δεν χρησιμοποιείται SSL\n"
+
+#: fe-auth.c:440
+msgid "duplicate SASL authentication request\n"
+msgstr "διπλότυπη αίτηση ελέγχου ταυτότητας SASL\n"
+
+#: fe-auth.c:496
+msgid "channel binding is required, but client does not support it\n"
+msgstr "απαιτείται σύνδεση καναλιού, αλλά ο πελάτης δεν την υποστηρίζει\n"
+
+#: fe-auth.c:513
+msgid "server offered SCRAM-SHA-256-PLUS authentication over a non-SSL connection\n"
+msgstr "ο διακομιστής προσέφερε έλεγχο ταυτότητας SCRAM-SHA-256-PLUS μέσω σύνδεσης που δεν είναι SSL\n"
+
+#: fe-auth.c:525
+msgid "none of the server's SASL authentication mechanisms are supported\n"
+msgstr "δεν υποστηρίζεται κανένας από τους μηχανισμούς ελέγχου ταυτότητας SASL του διακομιστή\n"
+
+#: fe-auth.c:533
+msgid "channel binding is required, but server did not offer an authentication method that supports channel binding\n"
+msgstr "απαιτείται σύνδεση καναλιού, αλλά ο διακομιστής δεν προσέφερε καμία μέθοδο ελέγχου ταυτότητας που να υποστηρίζει σύνδεση καναλιού\n"
+
+#: fe-auth.c:639
+#, c-format
+msgid "out of memory allocating SASL buffer (%d)\n"
+msgstr "η μνήμη δεν επαρκεί για την εκχώρηση της ενδιάμεσης μνήμης του SASL (%d)\n"
+
+#: fe-auth.c:664
+msgid "AuthenticationSASLFinal received from server, but SASL authentication was not completed\n"
+msgstr "παραλήφθηκε AuthenticationSASLFinal από το διακομιστή, αλλά ο έλεγχος ταυτότητας SASL έχει ολοκληρωθεί\n"
+
+#: fe-auth.c:741
+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:877
+msgid "Kerberos 4 authentication not supported\n"
+msgstr "δεν υποστηρίζεται η μέθοδος πιστοποίησης Kerberos 4\n"
+
+#: fe-auth.c:882
+msgid "Kerberos 5 authentication not supported\n"
+msgstr "δεν υποστηρίζεται η μέθοδος πιστοποίησης Kerberos 5\n"
+
+#: fe-auth.c:953
+msgid "GSSAPI authentication not supported\n"
+msgstr "δεν υποστηρίζεται η μέθοδος πιστοποίησης GSSAPI\n"
+
+#: fe-auth.c:985
+msgid "SSPI authentication not supported\n"
+msgstr "δεν υποστηρίζεται η μέθοδος πιστοποίησης SSPI\n"
+
+#: fe-auth.c:993
+msgid "Crypt authentication not supported\n"
+msgstr "δεν υποστηρίζεται η μέθοδος πιστοποίησης Crypt\n"
+
+#: fe-auth.c:1060
+#, c-format
+msgid "authentication method %u not supported\n"
+msgstr "δεν υποστηρίζεται η μέθοδος πιστοποίησης %u\n"
+
+#: fe-auth.c:1107
+#, c-format
+msgid "user name lookup failure: error code %lu\n"
+msgstr "αποτυχία αναζήτησης ονόματος χρήστη: κωδικός σφάλματος % lu\n"
+
+#: fe-auth.c:1117 fe-connect.c:2851
+#, c-format
+msgid "could not look up local user ID %d: %s\n"
+msgstr "δεν ήταν δυνατή η αναζήτηση ID τοπικού χρήστη %d: %s\n"
+
+#: fe-auth.c:1122 fe-connect.c:2856
+#, c-format
+msgid "local user with ID %d does not exist\n"
+msgstr "δεν υπάρχει τοπικός χρήστης με ID %d\n"
+
+#: fe-auth.c:1226
+msgid "unexpected shape of result set returned for SHOW\n"
+msgstr "μη αναμενόμενο σχήμα συνόλου αποτελεσμάτων που επιστράφηκε από την εντολή SHOW\n"
+
+#: fe-auth.c:1235
+msgid "password_encryption value too long\n"
+msgstr "πολύ μακρυά τιμή password_encryption\n"
+
+#: fe-auth.c:1275
+#, c-format
+msgid "unrecognized password encryption algorithm \"%s\"\n"
+msgstr "μη αναγνωρίσιμος αλγόριθμος κρυπτογράφησης «%s» κωδικού πρόσβασης\n"
+
+#: fe-connect.c:1094
+#, c-format
+msgid "could not match %d host names to %d hostaddr values\n"
+msgstr "δεν μπόρεσε να ταιριάξει %d ονομασίες διακομιστών με %d τιμές hostaddr\n"
+
+#: fe-connect.c:1175
+#, c-format
+msgid "could not match %d port numbers to %d hosts\n"
+msgstr "δεν μπόρεσε να ταιριάξει %d αριθμούς θυρών με %d διακομιστές\n"
+
+#: fe-connect.c:1268 fe-connect.c:1294 fe-connect.c:1336 fe-connect.c:1345
+#: fe-connect.c:1378 fe-connect.c:1422
+#, c-format
+msgid "invalid %s value: \"%s\"\n"
+msgstr "άκυρο %s τιμή: «%s»\n"
+
+#: fe-connect.c:1315
+#, c-format
+msgid "sslmode value \"%s\" invalid when SSL support is not compiled in\n"
+msgstr "η τιμή SSLmode «%s» είναι άκυρη όταν η υποστήριξη SSL δεν έχει μεταγλωττιστεί (compiled)\n"
+
+#: fe-connect.c:1363
+msgid "invalid SSL protocol version range\n"
+msgstr "άκυρο εύρος εκδόσεων πρωτοκόλλου SSL\n"
+
+#: fe-connect.c:1388
+#, c-format
+msgid "gssencmode value \"%s\" invalid when GSSAPI support is not compiled in\n"
+msgstr "η τιμή SSLmode «%s» είναι άκυρη όταν η υποστήριξη GSSAPI δεν έχει μεταγλωττιστεί (compiled)\n"
+
+#: fe-connect.c:1648
+#, c-format
+msgid "could not set socket to TCP no delay mode: %s\n"
+msgstr "δεν μπόρεσε να ορίσει τον υποδοχέα σε λειτουργία TCP χωρίς καθυστέρηση: %s\n"
+
+#: fe-connect.c:1710
+#, c-format
+msgid "connection to server on socket \"%s\" failed: "
+msgstr "σύνδεση στον διακομιστή στην υποδοχή «%s» απέτυχε: "
+
+#: fe-connect.c:1737
+#, c-format
+msgid "connection to server at \"%s\" (%s), port %s failed: "
+msgstr "σύνδεση στον διακομιστή σε «%s» (%s), θύρα %s απέτυχε: "
+
+#: fe-connect.c:1742
+#, c-format
+msgid "connection to server at \"%s\", port %s failed: "
+msgstr "σύνδεση στον διακομιστή σε «%s», θύρα %s απέτυχε: "
+
+#: fe-connect.c:1767
+msgid "\tIs the server running locally and accepting connections on that socket?\n"
+msgstr "\tΕκτελείται τοπικά ο διακομιστής και αποδέχεται συνδέσεις σε αυτή την υποδοχή;\n"
+
+#: fe-connect.c:1771
+msgid "\tIs the server running on that host and accepting TCP/IP connections?\n"
+msgstr "\tΕκτελείται ο διακομιστής σε αυτόν τον κεντρικό υπολογιστή και αποδέχεται συνδέσεις TCP/IP;\n"
+
+#: fe-connect.c:1835
+#, c-format
+msgid "invalid integer value \"%s\" for connection option \"%s\"\n"
+msgstr "άκυρη τιμή ακεραίου »%s» για την επιλογή σύνδεσης «%s»\n"
+
+#: fe-connect.c:1865 fe-connect.c:1900 fe-connect.c:1936 fe-connect.c:2025
+#: fe-connect.c:2639
+#, c-format
+msgid "%s(%s) failed: %s\n"
+msgstr "%s(%s) απέτυχε: %s\n"
+
+#: fe-connect.c:1990
+#, c-format
+msgid "%s(%s) failed: error code %d\n"
+msgstr "%s(%s) απέτυχε: κωδικός σφάλματος %d\n"
+
+#: fe-connect.c:2305
+msgid "invalid connection state, probably indicative of memory corruption\n"
+msgstr "μη έγκυρη κατάσταση σύνδεσης, πιθανώς ενδεικτική αλλοίωσης μνήμης\n"
+
+#: fe-connect.c:2384
+#, c-format
+msgid "invalid port number: \"%s\"\n"
+msgstr "μη έγκυρος αριθμός πύλης: «%s»\n"
+
+#: fe-connect.c:2400
+#, c-format
+msgid "could not translate host name \"%s\" to address: %s\n"
+msgstr "δεν ήταν δυνατή η μετάφραση του ονόματος κεντρικού υπολογιστή «%s» στη διεύθυνση: %s\n"
+
+#: fe-connect.c:2413
+#, c-format
+msgid "could not parse network address \"%s\": %s\n"
+msgstr "δεν ήταν δυνατή η ανάλυση της διεύθυνσης δικτύου «%s»: %s\n"
+
+#: fe-connect.c:2426
+#, c-format
+msgid "Unix-domain socket path \"%s\" is too long (maximum %d bytes)\n"
+msgstr "η διαδρομή υποδοχής τομέα Unix «%s» είναι πολύ μακρυά (μέγιστο %d bytes)\n"
+
+#: fe-connect.c:2441
+#, c-format
+msgid "could not translate Unix-domain socket path \"%s\" to address: %s\n"
+msgstr "δεν ήταν δυνατή η μετάφραση της διαδρομής υποδοχής πεδίου-Unix «%s» στη διεύθυνση: %s\n"
+
+#: fe-connect.c:2567
+#, c-format
+msgid "could not create socket: %s\n"
+msgstr "δεν ήταν δυνατή η δημιουργία υποδοχέα: %s\n"
+
+#: fe-connect.c:2598
+#, c-format
+msgid "could not set socket to nonblocking mode: %s\n"
+msgstr "δεν ήταν δυνατή η ρύθμιση της υποδοχής σε λειτουργία μη αποκλεισμού: %s\n"
+
+#: fe-connect.c:2608
+#, c-format
+msgid "could not set socket to close-on-exec mode: %s\n"
+msgstr "δεν ήταν δυνατή η ρύθμιση της υποδοχής σε λειτουργία close-on-exec: %s\n"
+
+#: fe-connect.c:2626
+msgid "keepalives parameter must be an integer\n"
+msgstr "η παράμετρος keepalives πρέπει να είναι ακέραιος\n"
+
+#: fe-connect.c:2767
+#, c-format
+msgid "could not get socket error status: %s\n"
+msgstr "δεν ήταν δυνατή η απόκτηση κατάστασης σφάλματος της υποδοχής: %s\n"
+
+#: fe-connect.c:2795
+#, c-format
+msgid "could not get client address from socket: %s\n"
+msgstr "δεν ήταν δυνατή η απόκτηση διεύθυνσης πελάτη από την υποδοχή: %s\n"
+
+#: fe-connect.c:2837
+msgid "requirepeer parameter is not supported on this platform\n"
+msgstr "η παράμετρος requirepeer δεν υποστηρίζεται από την παρούσα πλατφόρμα\n"
+
+#: fe-connect.c:2840
+#, c-format
+msgid "could not get peer credentials: %s\n"
+msgstr "δεν ήταν δυνατή η απόκτηση διαπιστευτηρίων από peer: %s\n"
+
+#: fe-connect.c:2864
+#, c-format
+msgid "requirepeer specifies \"%s\", but actual peer user name is \"%s\"\n"
+msgstr "το requirepeer καθορίζει «%s», αλλά το πραγματικό όνομα ομότιμου χρήστη είναι «%s»\n"
+
+#: fe-connect.c:2904
+#, c-format
+msgid "could not send GSSAPI negotiation packet: %s\n"
+msgstr "δεν ήταν δυνατή η αποστολή GSSAPI πακέτου διαπραγμάτευσης: %s\n"
+
+#: fe-connect.c:2916
+msgid "GSSAPI encryption required but was impossible (possibly no credential cache, no server support, or using a local socket)\n"
+msgstr "GSSAPI κρυπτογράφηση απαιτείται αλλά ήταν αδύνατη (πιθανώς απουσία cache διαπιστευτηρίων, απουσία υποστήριξης διακομιστή, ή χρησιμοποιείται τοπική υποδοχή)\n"
+
+#: fe-connect.c:2958
+#, c-format
+msgid "could not send SSL negotiation packet: %s\n"
+msgstr ""
+"δεν ήταν δυνατή η αποστολή SSL πακέτου διαπραγμάτευσης: %s\n"
+"\n"
+
+#: fe-connect.c:2989
+#, c-format
+msgid "could not send startup packet: %s\n"
+msgstr "δεν ήταν δυνατή η αποστολή πακέτου εκκίνησης: %s\n"
+
+#: fe-connect.c:3065
+msgid "server does not support SSL, but SSL was required\n"
+msgstr "ο διακομιστής δεν υποστηρίζει SSL, αλλά απαιτείται SSL\n"
+
+#: fe-connect.c:3092
+#, c-format
+msgid "received invalid response to SSL negotiation: %c\n"
+msgstr "έλαβε μη έγκυρη απάντηση κατά τη διαπραγμάτευση SSL: %c\n"
+
+#: fe-connect.c:3181
+msgid "server doesn't support GSSAPI encryption, but it was required\n"
+msgstr "ο διακομιστής δεν υποστηρίζει κρυπτογράφηση GSSAPI, αλλά αυτή ήταν απαραίτητη\n"
+
+#: fe-connect.c:3193
+#, c-format
+msgid "received invalid response to GSSAPI negotiation: %c\n"
+msgstr "έλαβε μη έγκυρη απάντηση κατά τη διαπραγμάτευση GSSAPI: %c\n"
+
+#: fe-connect.c:3259 fe-connect.c:3284
+#, c-format
+msgid "expected authentication request from server, but received %c\n"
+msgstr "ανέμενε αίτηση ελέγχου ταυτότητας από το διακομιστή, αλλά αντί αυτής ελήφθη %c\n"
+
+#: fe-connect.c:3491
+msgid "unexpected message from server during startup\n"
+msgstr "μη αναμενόμενο μήνυμα από το διακομιστή κατά την εκκίνηση\n"
+
+#: fe-connect.c:3583
+msgid "session is read-only\n"
+msgstr "η περίοδος λειτουργίας είναι μόνο για ανάγνωση\n"
+
+#: fe-connect.c:3586
+msgid "session is not read-only\n"
+msgstr "η περίοδος λειτουργίας δεν είναι μόνο για ανάγνωση\n"
+
+#: fe-connect.c:3640
+msgid "server is in hot standby mode\n"
+msgstr "%s: ο διακομιστής βρίσκεται σε hot κατάσταση αναμονής\n"
+
+#: fe-connect.c:3643
+msgid "server is not in hot standby mode\n"
+msgstr "ο διακομιστής δεν βρίσκεται σε hot κατάσταση αναμονής\n"
+
+#: fe-connect.c:3754 fe-connect.c:3806
+#, c-format
+msgid "\"%s\" failed\n"
+msgstr "«%s» απέτυχε.\n"
+
+#: fe-connect.c:3820
+#, c-format
+msgid "invalid connection state %d, probably indicative of memory corruption\n"
+msgstr "κατάσταση μη έγκυρης σύνδεσης %d, πιθανώς ενδεικτική αλλοίωσης της μνήμης\n"
+
+#: fe-connect.c:4266 fe-connect.c:4326
+#, c-format
+msgid "PGEventProc \"%s\" failed during PGEVT_CONNRESET event\n"
+msgstr "PGEventProc «%s» απέτυχε κατά τη διάρκεια συμβάντος PGEVT_CONNRESET\n"
+
+#: fe-connect.c:4670
+#, c-format
+msgid "invalid LDAP URL \"%s\": scheme must be ldap://\n"
+msgstr "άκυρη διεύθυνση URL LDAP «%s»: ο συνδυασμός πρέπει να είναι ldap://\n"
+
+#: fe-connect.c:4685
+#, c-format
+msgid "invalid LDAP URL \"%s\": missing distinguished name\n"
+msgstr "άκυρη διεύθυνση URL LDAP «%s»: λείπει το αποκλειστικό όνομα\n"
+
+#: fe-connect.c:4697 fe-connect.c:4755
+#, c-format
+msgid "invalid LDAP URL \"%s\": must have exactly one attribute\n"
+msgstr "άκυρη διεύθυνση URL LDAP «%s»: πρέπει να περιέχει ακριβώς ένα χαρακτηριστικό\n"
+
+#: fe-connect.c:4709 fe-connect.c:4771
+#, 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:4721
+#, c-format
+msgid "invalid LDAP URL \"%s\": no filter\n"
+msgstr "άκυρη διεύθυνση URL LDAP «%s»: κανένα φίλτρο\n"
+
+#: fe-connect.c:4743
+#, c-format
+msgid "invalid LDAP URL \"%s\": invalid port number\n"
+msgstr "άκυρη διεύθυνση URL LDAP «%s»: άκυρος αριθμός θύρας\n"
+
+#: fe-connect.c:4781
+msgid "could not create LDAP structure\n"
+msgstr "δεν ήταν δυνατή η δημιουργία δομής LDAP\n"
+
+#: fe-connect.c:4857
+#, c-format
+msgid "lookup on LDAP server failed: %s\n"
+msgstr "απέτυχε η αναζήτηση στον διακομιστή LDAP: %s\n"
+
+#: fe-connect.c:4868
+msgid "more than one entry found on LDAP lookup\n"
+msgstr "βρέθηκαν περισσότερες από μία καταχωρήσεις στην αναζήτηση LDAP\n"
+
+#: fe-connect.c:4869 fe-connect.c:4881
+msgid "no entry found on LDAP lookup\n"
+msgstr "δεν βρέθηκε καταχώρηση στην αναζήτηση LDAP\n"
+
+#: fe-connect.c:4892 fe-connect.c:4905
+msgid "attribute has no values on LDAP lookup\n"
+msgstr "το χαρακτηριστικό δεν έχει τιμές στην αναζήτηση LDAP\n"
+
+#: fe-connect.c:4957 fe-connect.c:4976 fe-connect.c:5508
+#, c-format
+msgid "missing \"=\" after \"%s\" in connection info string\n"
+msgstr "λείπει το «=» μετά από «%s» στην συμβολοσειρά πληροφορίας σύνδεσης\n"
+
+#: fe-connect.c:5049 fe-connect.c:5693 fe-connect.c:6469
+#, c-format
+msgid "invalid connection option \"%s\"\n"
+msgstr "άκυρη επιλογή σύνδεσης «%s»\n"
+
+#: fe-connect.c:5065 fe-connect.c:5557
+msgid "unterminated quoted string in connection info string\n"
+msgstr "ατερμάτιστη συμβολοσειρά με εισαγωγικά στην συμβολοσειρά πληροφορίας σύνδεσης\n"
+
+#: fe-connect.c:5146
+#, c-format
+msgid "definition of service \"%s\" not found\n"
+msgstr "δεν βρέθηκε ο ορισμός της υπηρεσίας «%s»\n"
+
+#: fe-connect.c:5172
+#, c-format
+msgid "service file \"%s\" not found\n"
+msgstr "δεν βρέθηκε αρχείο υπηρεσίας «%s»\n"
+
+#: fe-connect.c:5186
+#, c-format
+msgid "line %d too long in service file \"%s\"\n"
+msgstr "Πολύ μακρυά γραμμή %d στο αρχείο υπηρεσίας «%s»\n"
+
+#: fe-connect.c:5257 fe-connect.c:5301
+#, c-format
+msgid "syntax error in service file \"%s\", line %d\n"
+msgstr "συντακτικό σφάλμα στο αρχείο υπηρεσίας «%s», γραμμή %d\n"
+
+#: fe-connect.c:5268
+#, c-format
+msgid "nested service specifications not supported in service file \"%s\", line %d\n"
+msgstr "οι ένθετες προδιαγραφές υπηρεσίας δεν υποστηρίζονται στο αρχείο υπηρεσίας «%s», γραμμή %d\n"
+
+#: fe-connect.c:5989
+#, c-format
+msgid "invalid URI propagated to internal parser routine: \"%s\"\n"
+msgstr "μη έγκυρο URI διαδόθηκε στη ρουτίνα εσωτερικής ανάλυσης: «%s»\n"
+
+#: fe-connect.c:6066
+#, 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:6073
+#, c-format
+msgid "IPv6 host address may not be empty in URI: \"%s\"\n"
+msgstr "η διεύθυνση IPv6 κεντρικού υπολογιστή δεν δύναται να είναι κενή στο URI: «%s»\n"
+
+#: fe-connect.c:6088
+#, c-format
+msgid "unexpected character \"%c\" at position %d in URI (expected \":\" or \"/\"): \"%s\"\n"
+msgstr "μη αναμενόμενος χαρακτήρας «%c» στη θέση %d του URI (αναμένεται «:» ή «/»): «%s»\n"
+
+#: fe-connect.c:6218
+#, c-format
+msgid "extra key/value separator \"=\" in URI query parameter: \"%s\"\n"
+msgstr "επιπλέον διαχωριστικό κλειδιού/τιμής «=» στην παράμετρο ερωτήματος URI: «%s»\n"
+
+#: fe-connect.c:6238
+#, c-format
+msgid "missing key/value separator \"=\" in URI query parameter: \"%s\"\n"
+msgstr "λείπει διαχωριστικό κλειδιού/τιμής «=» στην παράμετρο ερωτήματος URI: «%s»\n"
+
+#: fe-connect.c:6290
+#, c-format
+msgid "invalid URI query parameter: \"%s\"\n"
+msgstr "άκυρη παράμετρος ερωτήματος URI: «%s»\n"
+
+#: fe-connect.c:6364
+#, c-format
+msgid "invalid percent-encoded token: \"%s\"\n"
+msgstr "άκυρο διακριτικό με κωδικοποίηση ποσοστού: «%s»\n"
+
+#: fe-connect.c:6374
+#, c-format
+msgid "forbidden value %%00 in percent-encoded value: \"%s\"\n"
+msgstr "απαγορευμένη τιμή %%00 σε τιμή κωδικοποιημένου ποσοστού: «%s»\n"
+
+#: fe-connect.c:6744
+msgid "connection pointer is NULL\n"
+msgstr "ο δείκτης σύνδεσης είναι NULL\n"
+
+#: fe-connect.c:7024
+#, c-format
+msgid "WARNING: password file \"%s\" is not a plain file\n"
+msgstr "ΠΡΟΕΙΔΟΠΟΙΗΣΗ: το αρχείο κωδικών πρόσβασης «%s» δεν είναι ένα απλό αρχείο\n"
+
+#: fe-connect.c:7033
+#, 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:7141
+#, c-format
+msgid "password retrieved from file \"%s\"\n"
+msgstr "ο κωδικός πρόσβασης ελήφθει από αρχείο «%s»\n"
+
+#: fe-exec.c:449 fe-exec.c:3286
+#, c-format
+msgid "row number %d is out of range 0..%d"
+msgstr "ο αριθμός σειράς %d βρίσκεται εκτός εύρους 0..%d"
+
+#: fe-exec.c:510 fe-protocol3.c:219 fe-protocol3.c:244 fe-protocol3.c:273
+#: fe-protocol3.c:291 fe-protocol3.c:371 fe-protocol3.c:743 fe-protocol3.c:975
+msgid "out of memory"
+msgstr "έλλειψη μνήμης"
+
+#: fe-exec.c:511 fe-protocol3.c:1932
+#, c-format
+msgid "%s"
+msgstr "%s"
+
+#: fe-exec.c:778
+msgid "write to server failed\n"
+msgstr "απέτυχε η εγγραφή στον διακομιστή\n"
+
+#: fe-exec.c:850
+msgid "NOTICE"
+msgstr "NOTICE"
+
+#: fe-exec.c:908
+msgid "PGresult cannot support more than INT_MAX tuples"
+msgstr "το PGresult δεν μπορεί να υποστηρίξει περισσότερες πλείαδες από INT_MAX"
+
+#: fe-exec.c:920
+msgid "size_t overflow"
+msgstr "υπερχείλιση overflow"
+
+#: fe-exec.c:1335 fe-exec.c:1440 fe-exec.c:1489
+msgid "command string is a null pointer\n"
+msgstr "η συμβολοσειρά εντολής είναι ένας κενός δείκτης\n"
+
+#: fe-exec.c:1446 fe-exec.c:1495 fe-exec.c:1591
+#, c-format
+msgid "number of parameters must be between 0 and %d\n"
+msgstr "ο αριθμός των παραμέτρων πρέπει να είναι μεταξύ 0 και %d\n"
+
+#: fe-exec.c:1483 fe-exec.c:1585
+msgid "statement name is a null pointer\n"
+msgstr "η ονομασία της δήλωσης είναι ένας κενός δείκτης\n"
+
+#: fe-exec.c:1627 fe-exec.c:3139
+msgid "no connection to the server\n"
+msgstr "καμία σύνδεση στον διακομιστή\n"
+
+#: fe-exec.c:1636 fe-exec.c:3148
+msgid "another command is already in progress\n"
+msgstr "υπάρχει άλλη εντολή σε πρόοδο\n"
+
+#: fe-exec.c:1665
+msgid "cannot queue commands during COPY\n"
+msgstr "δεν είναι δυνατή η ουρά εντολών κατά τη διάρκεια του COPY\n"
+
+#: fe-exec.c:1783
+msgid "length must be given for binary parameter\n"
+msgstr "το μήκος πρέπει να περαστεί ως δυαδική παράμετρος\n"
+
+#: fe-exec.c:2103
+#, c-format
+msgid "unexpected asyncStatus: %d\n"
+msgstr "μη αναμενόμενο asyncStatus: %d\n"
+
+#: fe-exec.c:2123
+#, c-format
+msgid "PGEventProc \"%s\" failed during PGEVT_RESULTCREATE event\n"
+msgstr "PGEventProc «%s» κατά τη διάρκεια συμβάντος PGEVT_RESULTCREATE\n"
+
+#: fe-exec.c:2271
+msgid "synchronous command execution functions are not allowed in pipeline mode\n"
+msgstr "οι συναρτήσεις σύγχρονης εκτέλεσης εντολών δεν επιτρέπονται σε λειτουργία διοχέτευσης\n"
+
+#: fe-exec.c:2293
+msgid "COPY terminated by new PQexec"
+msgstr "COPY τερματίστηκε από νέο PQexec"
+
+#: fe-exec.c:2310
+msgid "PQexec not allowed during COPY BOTH\n"
+msgstr "PQexec δεν επιτρέπεται κατά τη διάρκεια COPY BOTH\n"
+
+#: fe-exec.c:2538 fe-exec.c:2594 fe-exec.c:2663 fe-protocol3.c:1863
+msgid "no COPY in progress\n"
+msgstr "κανένα COPY σε εξέλιξη\n"
+
+#: fe-exec.c:2840
+msgid "PQfn not allowed in pipeline mode\n"
+msgstr "το PQfn δεν επιτρέπεται σε λειτουργία διοχέτευσης\n"
+
+#: fe-exec.c:2848
+msgid "connection in wrong state\n"
+msgstr "σύνδεση σε λανθάνουσα κατάσταση\n"
+
+#: fe-exec.c:2892
+msgid "cannot enter pipeline mode, connection not idle\n"
+msgstr "δεν ήταν δυνατή η είσοδος σε λειτουργία διοχέτευσης, σύνδεση μη αδρανή\n"
+
+#: fe-exec.c:2926 fe-exec.c:2943
+msgid "cannot exit pipeline mode with uncollected results\n"
+msgstr "δεν είναι δυνατή η έξοδος από τη λειτουργία διοχέτευσης με μη λαμβανόμενα αποτελέσματα\n"
+
+#: fe-exec.c:2931
+msgid "cannot exit pipeline mode while busy\n"
+msgstr "δεν είναι δυνατή η έξοδος από τη λειτουργία διοχέτευσης ενώ απασχολείται\n"
+
+#: fe-exec.c:3073
+msgid "cannot send pipeline when not in pipeline mode\n"
+msgstr "δεν είναι δυνατή η αποστολή διοχέτευσης όταν δεν βρίσκεται σε λειτουργία διοχέτευσης\n"
+
+#: fe-exec.c:3175
+msgid "invalid ExecStatusType code"
+msgstr "άκυρος κωδικός ExecStatusType"
+
+#: fe-exec.c:3202
+msgid "PGresult is not an error result\n"
+msgstr "PGresult δεν είναι ένα αποτέλεσμα σφάλματος\n"
+
+#: fe-exec.c:3270 fe-exec.c:3293
+#, c-format
+msgid "column number %d is out of range 0..%d"
+msgstr "αριθμός στήλης %d βρίσκεται εκτός εύρους 0..%d"
+
+#: fe-exec.c:3308
+#, c-format
+msgid "parameter number %d is out of range 0..%d"
+msgstr "αριθμός παραμέτρου %d βρίσκεται εκτός εύρους 0..%d"
+
+#: fe-exec.c:3618
+#, c-format
+msgid "could not interpret result from server: %s"
+msgstr "δεν μπόρεσε να ερμηνεύσει το αποτέλεσμα από τον διακομιστή: %s"
+
+#: fe-exec.c:3878 fe-exec.c:3967
+msgid "incomplete multibyte character\n"
+msgstr "ελλιπής χαρακτήρας πολλαπλών byte\n"
+
+#: fe-gssapi-common.c:124
+msgid "GSSAPI name import error"
+msgstr "σφάλμα εισαγωγής ονόματος GSSAPI"
+
+#: fe-lobj.c:145 fe-lobj.c:210 fe-lobj.c:403 fe-lobj.c:494 fe-lobj.c:568
+#: fe-lobj.c:969 fe-lobj.c:977 fe-lobj.c:985 fe-lobj.c:993 fe-lobj.c:1001
+#: fe-lobj.c:1009 fe-lobj.c:1017 fe-lobj.c:1025
+#, c-format
+msgid "cannot determine OID of function %s\n"
+msgstr "δεν ήταν δυνατός ο προσδιορισμός του OID της συνάρτησης %s\n"
+
+#: fe-lobj.c:162
+msgid "argument of lo_truncate exceeds integer range\n"
+msgstr "η παράμετρος του lo_truncate υπερβαίνει το εύρος ακέραιων\n"
+
+#: fe-lobj.c:266
+msgid "argument of lo_read exceeds integer range\n"
+msgstr "η παράμετρος του lo_read υπερβαίνει το εύρος ακέραιων\n"
+
+#: fe-lobj.c:318
+msgid "argument of lo_write exceeds integer range\n"
+msgstr "η παράμετρος του lo_write υπερβαίνει το εύρος ακέραιων\n"
+
+#: fe-lobj.c:678 fe-lobj.c:789
+#, c-format
+msgid "could not open file \"%s\": %s\n"
+msgstr "δεν ήταν δυνατό το άνοιγμα του αρχείου «%s»: %s\n"
+
+#: fe-lobj.c:734
+#, c-format
+msgid "could not read from file \"%s\": %s\n"
+msgstr "δεν ήταν δυνατή η ανάγνωση από το αρχείο «%s»: %s\n"
+
+#: fe-lobj.c:810 fe-lobj.c:834
+#, c-format
+msgid "could not write to file \"%s\": %s\n"
+msgstr "δεν ήταν δυνατή η εγγραφή στο αρχείο »%s»: %s\n"
+
+#: fe-lobj.c:920
+msgid "query to initialize large object functions did not return data\n"
+msgstr "το ερώτημα αρχικοποίησης συναρτήσεων μεγάλων αντικειμένων δεν επέστρεψε δεδομένα\n"
+
+#: fe-misc.c:242
+#, c-format
+msgid "integer of size %lu not supported by pqGetInt"
+msgstr "ακέραιος μεγέθους %lu δεν υποστηρίζεται από pqGetInt"
+
+#: fe-misc.c:275
+#, c-format
+msgid "integer of size %lu not supported by pqPutInt"
+msgstr "ακέραιος μεγέθους %lu δεν υποστηρίζεται από pqPutInt"
+
+#: fe-misc.c:576 fe-misc.c:822
+msgid "connection not open\n"
+msgstr "μη ανοικτή σύνδεση\n"
+
+#: fe-misc.c:755 fe-secure-openssl.c:209 fe-secure-openssl.c:316
+#: fe-secure.c:260 fe-secure.c:373
+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:1015
+msgid "timeout expired\n"
+msgstr "έληξε το χρονικό όριο\n"
+
+#: fe-misc.c:1060
+msgid "invalid socket\n"
+msgstr "άκυρος υποδοχέας\n"
+
+#: fe-misc.c:1083
+#, c-format
+msgid "%s() failed: %s\n"
+msgstr "%s() απέτυχε: %s\n"
+
+#: fe-protocol3.c:196
+#, c-format
+msgid "message type 0x%02x arrived from server while idle"
+msgstr "μήνυμα τύπου 0x%02x έφτασε από το διακομιστή ενώ αυτός ήταν αδρανής"
+
+#: fe-protocol3.c:403
+msgid "server sent data (\"D\" message) without prior row description (\"T\" message)\n"
+msgstr "ο διακομιστής έστειλε δεδομένα («D» μήνυμα) χωρίς προηγούμενη περιγραφή γραμμής («T» μήνυμα)\n"
+
+#: fe-protocol3.c:446
+#, c-format
+msgid "unexpected response from server; first received character was \"%c\"\n"
+msgstr "μη αναμενόμενη απόκριση από το διακομιστή· πρώτος χαρακτήρας που ελήφθη ήταν «%c»\n"
+
+#: fe-protocol3.c:471
+#, c-format
+msgid "message contents do not agree with length in message type \"%c\"\n"
+msgstr "τα περιεχόμενα του μηνύματος δεν συμφωνούν με το μήκος του σε τύπο μηνύματος «%c»\n"
+
+#: fe-protocol3.c:491
+#, c-format
+msgid "lost synchronization with server: got message type \"%c\", length %d\n"
+msgstr "χάθηκε ο συγχρονισμός με το διακομιστή: ελήφθει τύπος μηνύματος «%c», μήκους %d\n"
+
+#: fe-protocol3.c:543 fe-protocol3.c:583
+msgid "insufficient data in \"T\" message"
+msgstr "ανεπαρκή δεδομένα σε «T» μήνυμα"
+
+#: fe-protocol3.c:654 fe-protocol3.c:860
+msgid "out of memory for query result"
+msgstr "έλλειψη μνήμης για το αποτέλεσμα ερωτήματος"
+
+#: fe-protocol3.c:723
+msgid "insufficient data in \"t\" message"
+msgstr "ανεπαρκή δεδομένα σε «t» μήνυμα"
+
+#: fe-protocol3.c:782 fe-protocol3.c:814 fe-protocol3.c:832
+msgid "insufficient data in \"D\" message"
+msgstr "ανεπαρκή δεδομένα σε «D» μήνυμα"
+
+#: fe-protocol3.c:788
+msgid "unexpected field count in \"D\" message"
+msgstr "μη αναμενόμενο πλήθος πεδίων σε »D» μήνυμα"
+
+#: fe-protocol3.c:1029
+msgid "no error message available\n"
+msgstr "κανένα μήνυμα σφάλματος διαθέσιμο\n"
+
+#. translator: %s represents a digit string
+#: fe-protocol3.c:1077 fe-protocol3.c:1096
+#, c-format
+msgid " at character %s"
+msgstr "σε χαρακτήρα %s"
+
+#: fe-protocol3.c:1109
+#, c-format
+msgid "DETAIL: %s\n"
+msgstr "DETAIL: %s\n"
+
+#: fe-protocol3.c:1112
+#, c-format
+msgid "HINT: %s\n"
+msgstr "HINT: %s\n"
+
+#: fe-protocol3.c:1115
+#, c-format
+msgid "QUERY: %s\n"
+msgstr "ΕΡΩΤΗΜΑ: %s\n"
+
+#: fe-protocol3.c:1122
+#, c-format
+msgid "CONTEXT: %s\n"
+msgstr "CONTEXT: %s\n"
+
+#: fe-protocol3.c:1131
+#, c-format
+msgid "SCHEMA NAME: %s\n"
+msgstr "SCHEMA NAME: %s\n"
+
+#: fe-protocol3.c:1135
+#, c-format
+msgid "TABLE NAME: %s\n"
+msgstr "TABLE NAME: %s\n"
+
+#: fe-protocol3.c:1139
+#, c-format
+msgid "COLUMN NAME: %s\n"
+msgstr "COLUMN NAME: %s\n"
+
+#: fe-protocol3.c:1143
+#, c-format
+msgid "DATATYPE NAME: %s\n"
+msgstr "DATATYPE NAME: %s\n"
+
+#: fe-protocol3.c:1147
+#, c-format
+msgid "CONSTRAINT NAME: %s\n"
+msgstr "CONSTRAINT NAME: %s\n"
+
+#: fe-protocol3.c:1159
+msgid "LOCATION: "
+msgstr "LOCATION: "
+
+#: fe-protocol3.c:1161
+#, c-format
+msgid "%s, "
+msgstr "%s, "
+
+#: fe-protocol3.c:1163
+#, c-format
+msgid "%s:%s"
+msgstr "%s: %s"
+
+#: fe-protocol3.c:1358
+#, c-format
+msgid "LINE %d: "
+msgstr "ΓΡΑΜΜΗ %d: "
+
+#: fe-protocol3.c:1757
+msgid "PQgetline: not doing text COPY OUT\n"
+msgstr "PQgetline: δεν κάνει το κείμενο COPY OUT\n"
+
+#: fe-protocol3.c:2123
+#, c-format
+msgid "protocol error: id=0x%x\n"
+msgstr "σφάλμα πρωτοκόλλου: id=0x%x\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: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:1333
+#, 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:1337
+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:1346
+#, 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:1396
+#, 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 "δεν μπόρεσε να βρεθεί σύνοψη (digest) για NID %s\n"
+
+#: fe-secure-openssl.c:431
+msgid "could not generate peer certificate hash\n"
+msgstr "δεν ήταν δυνατή η δημιουργία ομότιμου πιστοποιητικού hash\n"
+
+#: fe-secure-openssl.c:488
+msgid "SSL certificate's name entry is missing\n"
+msgstr "λείπει καταχώρηση ονόματος του πιστοποιητικού SSL\n"
+
+#: fe-secure-openssl.c:822
+#, c-format
+msgid "could not create SSL context: %s\n"
+msgstr "δεν ήταν δυνατή η δημιουργία περιεχομένου SSL: %s\n"
+
+#: fe-secure-openssl.c:861
+#, c-format
+msgid "invalid value \"%s\" for minimum SSL protocol version\n"
+msgstr "άκυρη τιμή «%s» για την ελάχιστη έκδοση πρωτοκόλλου SSL\n"
+
+#: fe-secure-openssl.c:872
+#, c-format
+msgid "could not set minimum SSL protocol version: %s\n"
+msgstr "δεν ήταν δυνατό να ορίσει ελάχιστη έκδοση πρωτοκόλλου SSL: %s\n"
+
+#: fe-secure-openssl.c:890
+#, c-format
+msgid "invalid value \"%s\" for maximum SSL protocol version\n"
+msgstr "άκυρη τιμή «%s» για μέγιστη έκδοση πρωτοκόλλου SSL\n"
+
+#: fe-secure-openssl.c:901
+#, c-format
+msgid "could not set maximum SSL protocol version: %s\n"
+msgstr "δεν ήταν δυνατό να ορίσει μέγιστη έκδοση πρωτοκόλλου SSL: %s\n"
+
+#: fe-secure-openssl.c:937
+#, c-format
+msgid "could not read root certificate file \"%s\": %s\n"
+msgstr "δεν ήταν δυνατή η ανάγνωση βασικού αρχείου πιστοποιητικού «%s»: %s\n"
+
+#: fe-secure-openssl.c:990
+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:994
+#, 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:1025
+#, c-format
+msgid "could not open certificate file \"%s\": %s\n"
+msgstr "δεν ήταν δυνατό το άνοιγμα αρχείου πιστοποιητικού «%s»: %s\n"
+
+#: fe-secure-openssl.c:1044
+#, c-format
+msgid "could not read certificate file \"%s\": %s\n"
+msgstr "δεν ήταν δυνατή η ανάγνωση αρχείου πιστοποιητικού «%s»: %s\n"
+
+#: fe-secure-openssl.c:1069
+#, c-format
+msgid "could not establish SSL connection: %s\n"
+msgstr "δεν ήταν δυνατή η δημιουργία σύνδεσης SSL: %s\n"
+
+#: fe-secure-openssl.c:1103
+#, c-format
+msgid "could not set SSL Server Name Indication (SNI): %s\n"
+msgstr "δεν ήταν δυνατός ο ορισμός SSL Server Name Indication (SNI): %s\n"
+
+#: fe-secure-openssl.c:1149
+#, c-format
+msgid "could not load SSL engine \"%s\": %s\n"
+msgstr "δεν ήταν δυνατή η φόρτωση της μηχανής SSL «%s»: %s\n"
+
+#: fe-secure-openssl.c:1161
+#, c-format
+msgid "could not initialize SSL engine \"%s\": %s\n"
+msgstr ""
+"δεν ήταν δυνατή η εκκίνηση του κινητήρα SSL »%s»: %s\n"
+"\n"
+
+#: fe-secure-openssl.c:1177
+#, 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:1191
+#, 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:1228
+#, c-format
+msgid "certificate present, but not private key file \"%s\"\n"
+msgstr "υπάρχει πιστοποιητικό, αλλά όχι αρχείο ιδιωτικού κλειδιού «%s»\n"
+
+#: fe-secure-openssl.c:1236
+#, 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:1261
+#, c-format
+msgid "could not load private key file \"%s\": %s\n"
+msgstr "δεν ήταν δυνατή η φόρτωση αρχείου ιδιωτικού κλειδιού «%s»: %s\n"
+
+#: fe-secure-openssl.c:1279
+#, c-format
+msgid "certificate does not match private key file \"%s\": %s\n"
+msgstr "το πιστοποιητικό δεν ταιριάζει με το αρχείο ιδιωτικού κλειδιού «%s»: %s\n"
+
+#: fe-secure-openssl.c:1379
+#, 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:1415
+#, c-format
+msgid "certificate could not be obtained: %s\n"
+msgstr ""
+"δεν ήταν δυνατή η λήψη πιστοποιητικού: %s\n"
+"\n"
+
+#: fe-secure-openssl.c:1521
+#, c-format
+msgid "no SSL error reported"
+msgstr "δεν αναφέρθηκε κανένα σφάλμα SSL"
+
+#: fe-secure-openssl.c:1530
+#, c-format
+msgid "SSL error code %lu"
+msgstr "κωδικός σφάλματος SSL %lu"
+
+#: fe-secure-openssl.c:1777
+#, c-format
+msgid "WARNING: sslpassword truncated\n"
+msgstr "WARNING: περικομμένο sslpassword\n"
+
+#: fe-secure.c:267
+#, c-format
+msgid "could not receive data from server: %s\n"
+msgstr "δεν ήταν δυνατή η λήψη δεδομένων από το διακομιστή: %s\n"
+
+#: fe-secure.c:380
+#, 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 "invalid channel_binding value: \"%s\"\n"
+#~ msgstr "άκυρη τιμή channel_binding: «%s»\n"
+
+#~ msgid "invalid ssl_min_protocol_version value: \"%s\"\n"
+#~ msgstr "άκυρη τιμή ssl_min_protocol_version: «%s»\n"
+
+#~ msgid "invalid ssl_max_protocol_version value: \"%s\"\n"
+#~ msgstr "άκυρη τιμή ssl_max_protocol_version: «%s»\n"
+
+#~ msgid "invalid gssencmode value: \"%s\"\n"
+#~ msgstr "άκυρη τιμή gssencmode: «%s»\n"
+
+#~ msgid "invalid target_session_attrs value: \"%s\"\n"
+#~ msgstr "άκυρη τιμή target_session_attrs: «%s»\n"
+
+#~ 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/IP στην θύρα %s;\n"
+
+#~ msgid "setsockopt(%s) failed: %s\n"
+#~ msgstr "setsockopt(%s) απέτυχε: %s\n"
+
+#~ msgid "WSAIoctl(SIO_KEEPALIVE_VALS) failed: %ui\n"
+#~ msgstr "WSAIoctl(SIO_KEEPALIVE_VALS) απέτυχε: %ui\n"
+
+#~ msgid "could not make a writable connection to server \"%s:%s\"\n"
+#~ msgstr "δεν ήταν δυνατή η πραγματοποίηση εγγράψιμης σύνδεσης με το διακομιστή \"%s:%s\"\n"
+
+#~ msgid "test \"SHOW transaction_read_only\" failed on server \"%s:%s\"\n"
+#~ msgstr "το τεστ «SHOW transaction_read_only» απέτυχε στον διακομιστή «%s:%s»\n"
+
+#~ msgid "function requires at least protocol version 3.0\n"
+#~ msgstr "η συνάρτηση απαιτεί πρωτόκολλο ελάχιστης έκδοσης 3.0\n"
+
+#~ msgid "COPY IN state must be terminated first\n"
+#~ msgstr ""
+#~ "πρέπει πρώτα να τερματιστεί η κατάσταση COPY IN\n"
+#~ "\n"
+
+#~ msgid "COPY OUT state must be terminated first\n"
+#~ msgstr "πρέπει πρώτα να τερματιστεί η κατάσταση COPY OUT\n"
+
+#~ msgid "cannot determine OID of function lo_truncate\n"
+#~ msgstr "δεν μπόρεσε να προσδιορίσει το OID της συνάρτησης lo_truncate\n"
+
+#~ msgid "cannot determine OID of function lo_truncate64\n"
+#~ msgstr "δεν μπόρεσε να προσδιορίσει το OID της συνάρτησης lo_truncate64\n"
+
+#~ msgid "cannot determine OID of function lo_lseek64\n"
+#~ msgstr "δεν μπόρεσε να προσδιορίσει το OID της συνάρτησης lo_lseek64\n"
+
+#~ msgid "cannot determine OID of function lo_create\n"
+#~ msgstr "δεν μπόρεσε να προσδιορίσει το OID της συνάρτησης lo_create\n"
+
+#~ msgid "cannot determine OID of function lo_tell64\n"
+#~ msgstr "δεν μπόρεσε να προσδιορίσει το OID της συνάρτησης lo_tell64\n"
+
+#~ msgid "cannot determine OID of function lo_open\n"
+#~ msgstr "δεν είναι δυνατός ο προσδιορισμός του OID της συνάρτησης lo_open\n"
+
+#~ msgid "cannot determine OID of function lo_creat\n"
+#~ msgstr "δεν είναι δυνατός ο προσδιορισμός του OID της συνάρτησης lo_creat\n"
+
+#~ msgid "cannot determine OID of function lo_unlink\n"
+#~ msgstr "δεν είναι δυνατός ο προσδιορισμός του OID της συνάρτησης lo_unlink\n"
+
+#~ msgid "cannot determine OID of function lo_lseek\n"
+#~ msgstr "δεν είναι δυνατός ο προσδιορισμός του OID της συνάρτησης lo_lseek\n"
+
+#~ msgid "cannot determine OID of function loread\n"
+#~ msgstr "δεν είναι δυνατός ο προσδιορισμός του OID της συνάρτησης loread\n"
+
+#~ msgid "cannot determine OID of function lowrite\n"
+#~ msgstr "δεν είναι δυνατός ο προσδιορισμός του OID της συνάρτησης lowrite\n"
+
+#~ msgid "select() failed: %s\n"
+#~ msgstr "απέτυχε το select(): %s\n"
+
+#~ msgid "invalid setenv state %c, probably indicative of memory corruption\n"
+#~ msgstr "μη έγκυρη κατάσταση %c setenv, πιθανώς ενδεικτική αλλοίωσης μνήμης\n"
+
+#~ msgid "invalid state %c, probably indicative of memory corruption\n"
+#~ msgstr "μη έγκυρη κατάσταση %c, πιθανώς ενδεικτική αλλοίωσης μνήμης\n"
+
+#~ msgid "unexpected character %c following empty query response (\"I\" message)"
+#~ msgstr "μη αναμενόμενος χαρακτήρας %c μετά από κενή απόκριση ερωτήματος («I» μήνυμα)"
+
+#~ msgid "server sent data (\"D\" message) without prior row description (\"T\" message)"
+#~ msgstr "ο διακομιστής έστειλε δεδομένα («D» μήνυμα) χωρίς προηγούμενη περιγραφή γραμμής («T» μήνυμα)"
+
+#~ msgid "server sent binary data (\"B\" message) without prior row description (\"T\" message)"
+#~ msgstr "ο διακομιστής έστειλε δυαδικά δεδομένα («B» μήνυμα) χωρίς προηγούμενη περιγραφή γραμμής («T» μήνυμα)"
+
+#~ msgid "lost synchronization with server, resetting connection"
+#~ msgstr "χάθηκε ο συγχρονισμός με τον διακομιστή, επαναρυθμίζεται η σύνδεση"
diff --git a/src/interfaces/libpq/po/es.po b/src/interfaces/libpq/po/es.po
new file mode 100644
index 0000000..f5b4ee7
--- /dev/null
+++ b/src/interfaces/libpq/po/es.po
@@ -0,0 +1,1227 @@
+# Spanish message translation file for libpq 2013-08-30 12:42-0400\n"
+#
+# Copyright (c) 2002-2021, 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, 2021
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: libpq (PostgreSQL) 14\n"
+"Report-Msgid-Bugs-To: pgsql-bugs@lists.postgresql.org\n"
+"POT-Creation-Date: 2022-08-07 20:26+0000\n"
+"PO-Revision-Date: 2022-08-08 00:59+0200\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: BlackCAT 1.1\n"
+
+#: fe-auth-scram.c:213
+msgid "malformed SCRAM message (empty message)\n"
+msgstr "mensaje SCRAM mal formado (mensaje vacío)\n"
+
+#: fe-auth-scram.c:219
+msgid "malformed SCRAM message (length mismatch)\n"
+msgstr "mensaje SCRAM mal formado (longitud no coincide)\n"
+
+#: fe-auth-scram.c:263
+msgid "could not verify server signature\n"
+msgstr "no se pudo verificar la signatura del servidor\n"
+
+#: fe-auth-scram.c:270
+msgid "incorrect server signature\n"
+msgstr "signatura de servidor incorrecta\n"
+
+#: fe-auth-scram.c:279
+msgid "invalid SCRAM exchange state\n"
+msgstr "estado de intercambio SCRAM no es válido\n"
+
+#: fe-auth-scram.c:306
+#, c-format
+msgid "malformed SCRAM message (attribute \"%c\" expected)\n"
+msgstr "mensaje SCRAM mal formado (se esperaba atributo «%c»)\n"
+
+#: fe-auth-scram.c:315
+#, 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:356
+msgid "could not generate nonce\n"
+msgstr "no se pude generar nonce\n"
+
+#: fe-auth-scram.c:366 fe-auth-scram.c:441 fe-auth-scram.c:595
+#: fe-auth-scram.c:616 fe-auth-scram.c:642 fe-auth-scram.c:657
+#: fe-auth-scram.c:707 fe-auth-scram.c:746 fe-auth.c:290 fe-auth.c:362
+#: fe-auth.c:398 fe-auth.c:615 fe-auth.c:774 fe-auth.c:1132 fe-auth.c:1282
+#: fe-connect.c:911 fe-connect.c:1460 fe-connect.c:1629 fe-connect.c:2981
+#: fe-connect.c:4711 fe-connect.c:4972 fe-connect.c:5091 fe-connect.c:5343
+#: fe-connect.c:5424 fe-connect.c:5523 fe-connect.c:5779 fe-connect.c:5808
+#: fe-connect.c:5880 fe-connect.c:5904 fe-connect.c:5922 fe-connect.c:6023
+#: fe-connect.c:6032 fe-connect.c:6390 fe-connect.c:6540 fe-connect.c:6806
+#: fe-exec.c:686 fe-exec.c:876 fe-exec.c:1223 fe-exec.c:3125 fe-exec.c:3309
+#: fe-exec.c:4082 fe-exec.c:4247 fe-gssapi-common.c:111 fe-lobj.c:881
+#: fe-protocol3.c:979 fe-protocol3.c:994 fe-protocol3.c:1027
+#: fe-protocol3.c:1735 fe-secure-common.c:110 fe-secure-gssapi.c:504
+#: fe-secure-openssl.c:440 fe-secure-openssl.c:1133
+msgid "out of memory\n"
+msgstr "memoria agotada\n"
+
+#: fe-auth-scram.c:374
+msgid "could not encode nonce\n"
+msgstr "no se pude generar nonce\n"
+
+#: fe-auth-scram.c:563
+msgid "could not calculate client proof\n"
+msgstr "no se pudo calcular la prueba del cliente\n"
+
+#: fe-auth-scram.c:579
+msgid "could not encode client proof\n"
+msgstr "no se pudo codificar la prueba del cliente\n"
+
+#: fe-auth-scram.c:634
+msgid "invalid SCRAM response (nonce mismatch)\n"
+msgstr "respuesta SCRAM no es válida (nonce no coincide)\n"
+
+#: fe-auth-scram.c:667
+msgid "malformed SCRAM message (invalid salt)\n"
+msgstr "mensaje SCRAM mal formado (sal no válida)\n"
+
+#: fe-auth-scram.c:681
+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:687
+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:723
+#, 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:739
+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:758
+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:391 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:351
+msgid "duplicate SSPI authentication request\n"
+msgstr "petición de autentificación SSPI duplicada\n"
+
+#: fe-auth.c:377
+msgid "could not acquire SSPI credentials"
+msgstr "no se pudo obtener las credenciales SSPI"
+
+#: fe-auth.c:433
+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:440
+msgid "duplicate SASL authentication request\n"
+msgstr "petición de autentificación SASL duplicada\n"
+
+#: fe-auth.c:496
+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:513
+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:525
+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:533
+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:639
+#, c-format
+msgid "out of memory allocating SASL buffer (%d)\n"
+msgstr "memoria agotada creando el búfer SASL (%d)\n"
+
+#: fe-auth.c:664
+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:741
+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:877
+msgid "Kerberos 4 authentication not supported\n"
+msgstr "el método de autentificación Kerberos 4 no está soportado\n"
+
+#: fe-auth.c:882
+msgid "Kerberos 5 authentication not supported\n"
+msgstr "el método de autentificación Kerberos 5 no está soportado\n"
+
+#: fe-auth.c:953
+msgid "GSSAPI authentication not supported\n"
+msgstr "el método de autentificación GSSAPI no está soportado\n"
+
+#: fe-auth.c:985
+msgid "SSPI authentication not supported\n"
+msgstr "el método de autentificación SSPI no está soportado\n"
+
+#: fe-auth.c:993
+msgid "Crypt authentication not supported\n"
+msgstr "el método de autentificación Crypt no está soportado\n"
+
+#: fe-auth.c:1060
+#, c-format
+msgid "authentication method %u not supported\n"
+msgstr "el método de autentificación %u no está soportado\n"
+
+#: fe-auth.c:1107
+#, 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:1117 fe-connect.c:2856
+#, 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:1122 fe-connect.c:2861
+#, 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:1226
+msgid "unexpected shape of result set returned for SHOW\n"
+msgstr "SHOW retornó un conjunto de resultados con estructura inesperada\n"
+
+#: fe-auth.c:1235
+msgid "password_encryption value too long\n"
+msgstr "el valor para password_encryption es demasiado largo\n"
+
+#: fe-auth.c:1275
+#, c-format
+msgid "unrecognized password encryption algorithm \"%s\"\n"
+msgstr "algoritmo para cifrado de contraseña «%s» desconocido\n"
+
+#: fe-connect.c:1094
+#, 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:1180
+#, 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:1273 fe-connect.c:1299 fe-connect.c:1341 fe-connect.c:1350
+#: fe-connect.c:1383 fe-connect.c:1427
+#, c-format
+msgid "invalid %s value: \"%s\"\n"
+msgstr "valor %s no válido: «%s»\n"
+
+#: fe-connect.c:1320
+#, 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:1368
+msgid "invalid SSL protocol version range\n"
+msgstr "rango de protocolo SSL no válido \n"
+
+#: fe-connect.c:1393
+#, 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:1653
+#, 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:1715
+#, c-format
+msgid "connection to server on socket \"%s\" failed: "
+msgstr "falló la conexión al servidor en el socket «%s»: "
+
+#: fe-connect.c:1742
+#, c-format
+msgid "connection to server at \"%s\" (%s), port %s failed: "
+msgstr "falló la conexión al servidor en «%s» (%s), puerto %s: "
+
+#: fe-connect.c:1747
+#, c-format
+msgid "connection to server at \"%s\", port %s failed: "
+msgstr "falló la conexión al servidor en «%s», puerto %s: "
+
+#: fe-connect.c:1772
+msgid "\tIs the server running locally and accepting connections on that socket?\n"
+msgstr "\t¿Está el servidor en ejecución localmente y aceptando conexiones en ese socket?\n"
+
+#: fe-connect.c:1776
+msgid "\tIs the server running on that host and accepting TCP/IP connections?\n"
+msgstr "\t¿Está el servidor en ejecución en ese host y aceptando conexiones TCP/IP?\n"
+
+#: fe-connect.c:1840
+#, 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:1870 fe-connect.c:1905 fe-connect.c:1941 fe-connect.c:2030
+#: fe-connect.c:2644
+#, c-format
+msgid "%s(%s) failed: %s\n"
+msgstr "%s(%s) falló: %s\n"
+
+#: fe-connect.c:1995
+#, c-format
+msgid "%s(%s) failed: error code %d\n"
+msgstr "%s(%s) falló: código de error %d\n"
+
+#: fe-connect.c:2310
+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:2389
+#, c-format
+msgid "invalid port number: \"%s\"\n"
+msgstr "número de puerto no válido: «%s»\n"
+
+#: fe-connect.c:2405
+#, 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:2418
+#, 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:2431
+#, 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:2446
+#, 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:2572
+#, c-format
+msgid "could not create socket: %s\n"
+msgstr "no se pudo crear el socket: %s\n"
+
+#: fe-connect.c:2603
+#, 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:2613
+#, 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:2631
+msgid "keepalives parameter must be an integer\n"
+msgstr "el parámetro de keepalives debe ser un entero\n"
+
+#: fe-connect.c:2772
+#, 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:2800
+#, 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:2842
+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:2845
+#, c-format
+msgid "could not get peer credentials: %s\n"
+msgstr "no se pudo obtener credenciales de la contraparte: %s\n"
+
+#: fe-connect.c:2869
+#, 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:2909
+#, 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:2921
+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:2963
+#, 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:2994
+#, c-format
+msgid "could not send startup packet: %s\n"
+msgstr "no se pudo enviar el paquete de inicio: %s\n"
+
+#: fe-connect.c:3070
+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:3097
+#, 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:3118
+msgid "received unencrypted data after SSL response\n"
+msgstr "se recibieron datos no cifrados después de la respuesta SSL\n"
+
+#: fe-connect.c:3199
+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:3211
+#, 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:3230
+msgid "received unencrypted data after GSSAPI encryption response\n"
+msgstr "se recibieron datos no cifrados después de la respuesta de cifrado GSSAPI\n"
+
+#: fe-connect.c:3290 fe-connect.c:3315
+#, 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:3522
+msgid "unexpected message from server during startup\n"
+msgstr "se ha recibido un mensaje inesperado del servidor durante el inicio\n"
+
+#: fe-connect.c:3614
+msgid "session is read-only\n"
+msgstr "la sesión es de solo lectura\n"
+
+#: fe-connect.c:3617
+msgid "session is not read-only\n"
+msgstr "la sesión no es de solo lectura\n"
+
+#: fe-connect.c:3671
+msgid "server is in hot standby mode\n"
+msgstr "el servidor está en modo hot standby\n"
+
+#: fe-connect.c:3674
+msgid "server is not in hot standby mode\n"
+msgstr "el servidor no está en modo hot standby\n"
+
+#: fe-connect.c:3792 fe-connect.c:3844
+#, c-format
+msgid "\"%s\" failed\n"
+msgstr "«%s» falló\n"
+
+#: fe-connect.c:3858
+#, 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:4304 fe-connect.c:4364
+#, c-format
+msgid "PGEventProc \"%s\" failed during PGEVT_CONNRESET event\n"
+msgstr "PGEventProc «%s» falló durante el evento PGEVT_CONNRESET\n"
+
+#: fe-connect.c:4724
+#, 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:4739
+#, 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:4751 fe-connect.c:4809
+#, 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:4763 fe-connect.c:4825
+#, 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:4775
+#, c-format
+msgid "invalid LDAP URL \"%s\": no filter\n"
+msgstr "URL LDAP no válida «%s»: no tiene filtro\n"
+
+#: fe-connect.c:4797
+#, 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:4835
+msgid "could not create LDAP structure\n"
+msgstr "no se pudo crear estructura LDAP\n"
+
+#: fe-connect.c:4911
+#, c-format
+msgid "lookup on LDAP server failed: %s\n"
+msgstr "búsqueda en servidor LDAP falló: %s\n"
+
+#: fe-connect.c:4922
+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:4923 fe-connect.c:4935
+msgid "no entry found on LDAP lookup\n"
+msgstr "no se encontró ninguna entrada en búsqueda LDAP\n"
+
+#: fe-connect.c:4946 fe-connect.c:4959
+msgid "attribute has no values on LDAP lookup\n"
+msgstr "la búsqueda LDAP entregó atributo sin valores\n"
+
+#: fe-connect.c:5011 fe-connect.c:5030 fe-connect.c:5562
+#, 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:5103 fe-connect.c:5747 fe-connect.c:6523
+#, c-format
+msgid "invalid connection option \"%s\"\n"
+msgstr "opción de conexión no válida «%s»\n"
+
+#: fe-connect.c:5119 fe-connect.c:5611
+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:5200
+#, c-format
+msgid "definition of service \"%s\" not found\n"
+msgstr "la definición de servicio «%s» no fue encontrada\n"
+
+#: fe-connect.c:5226
+#, c-format
+msgid "service file \"%s\" not found\n"
+msgstr "el archivo de servicio «%s» no fue encontrado\n"
+
+#: fe-connect.c:5240
+#, 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:5311 fe-connect.c:5355
+#, 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:5322
+#, 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:6043
+#, 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:6120
+#, 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:6127
+#, 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:6142
+#, 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:6272
+#, 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:6292
+#, 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:6344
+#, c-format
+msgid "invalid URI query parameter: \"%s\"\n"
+msgstr "parámetro de URI no válido: «%s»\n"
+
+#: fe-connect.c:6418
+#, c-format
+msgid "invalid percent-encoded token: \"%s\"\n"
+msgstr "elemento escapado con %% no válido: «%s»\n"
+
+#: fe-connect.c:6428
+#, 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:6798
+msgid "connection pointer is NULL\n"
+msgstr "el puntero de conexión es NULL\n"
+
+#: fe-connect.c:7086
+#, 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:7095
+#, 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:7203
+#, c-format
+msgid "password retrieved from file \"%s\"\n"
+msgstr "contraseña obtenida desde el archivo «%s»\n"
+
+#: fe-exec.c:449 fe-exec.c:3383
+#, 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:510 fe-protocol3.c:207 fe-protocol3.c:232 fe-protocol3.c:261
+#: fe-protocol3.c:279 fe-protocol3.c:375 fe-protocol3.c:747
+msgid "out of memory"
+msgstr "memoria agotada"
+
+#: fe-exec.c:511 fe-protocol3.c:1943
+#, c-format
+msgid "%s"
+msgstr "%s"
+
+#: fe-exec.c:792
+msgid "write to server failed\n"
+msgstr "falló escritura al servidor\n"
+
+#: fe-exec.c:864
+msgid "NOTICE"
+msgstr "AVISO"
+
+#: fe-exec.c:922
+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:934
+msgid "size_t overflow"
+msgstr "desbordamiento de size_t"
+
+#: fe-exec.c:1351 fe-exec.c:1477 fe-exec.c:1526
+msgid "command string is a null pointer\n"
+msgstr "la cadena de orden es un puntero nulo\n"
+
+#: fe-exec.c:1483 fe-exec.c:1532 fe-exec.c:1628
+#, c-format
+msgid "number of parameters must be between 0 and %d\n"
+msgstr "el número de parámetros debe estar entre 0 y %d\n"
+
+#: fe-exec.c:1520 fe-exec.c:1622
+msgid "statement name is a null pointer\n"
+msgstr "el nombre de sentencia es un puntero nulo\n"
+
+#: fe-exec.c:1664 fe-exec.c:3236
+msgid "no connection to the server\n"
+msgstr "no hay conexión con el servidor\n"
+
+#: fe-exec.c:1673 fe-exec.c:3245
+msgid "another command is already in progress\n"
+msgstr "hay otra orden en ejecución\n"
+
+#: fe-exec.c:1704
+msgid "cannot queue commands during COPY\n"
+msgstr "no se puede agregar órdenes a la cola mientras se hace COPY\n"
+
+#: fe-exec.c:1822
+msgid "length must be given for binary parameter\n"
+msgstr "el largo debe ser especificado para un parámetro binario\n"
+
+#: fe-exec.c:2149
+#, c-format
+msgid "unexpected asyncStatus: %d\n"
+msgstr "asyncStatus no esperado: %d\n"
+
+#: fe-exec.c:2185
+#, c-format
+msgid "PGEventProc \"%s\" failed during PGEVT_RESULTCREATE event\n"
+msgstr "PGEventProc «%s» falló durante el evento PGEVT_RESULTCREATE\n"
+
+#: fe-exec.c:2333
+msgid "synchronous command execution functions are not allowed in pipeline mode\n"
+msgstr "no se permiten funciones que ejecuten órdenes sincrónicas en modo pipeline\n"
+
+#: fe-exec.c:2355
+msgid "COPY terminated by new PQexec"
+msgstr "COPY terminado por un nuevo PQexec"
+
+#: fe-exec.c:2372
+msgid "PQexec not allowed during COPY BOTH\n"
+msgstr "PQexec no está permitido durante COPY BOTH\n"
+
+#: fe-exec.c:2600 fe-exec.c:2656 fe-exec.c:2725 fe-protocol3.c:1874
+msgid "no COPY in progress\n"
+msgstr "no hay COPY alguno en ejecución\n"
+
+#: fe-exec.c:2902
+msgid "PQfn not allowed in pipeline mode\n"
+msgstr "no se permite PQfn en modo pipeline\n"
+
+#: fe-exec.c:2910
+msgid "connection in wrong state\n"
+msgstr "la conexión está en un estado incorrecto\n"
+
+#: fe-exec.c:2954
+msgid "cannot enter pipeline mode, connection not idle\n"
+msgstr "no se puede entrar en modo pipeline, la conexión no está inactiva\n"
+
+#: fe-exec.c:2991 fe-exec.c:3015
+msgid "cannot exit pipeline mode with uncollected results\n"
+msgstr "no se puede salir de modo pipeline al tener resultados sin recolectar\n"
+
+#: fe-exec.c:2996
+msgid "cannot exit pipeline mode while busy\n"
+msgstr "no se puede salir de modo pipeline mientras haya actividad\n"
+
+#: fe-exec.c:3008
+msgid "cannot exit pipeline mode while in COPY\n"
+msgstr "no se puede salir de modo pipeline mientras se está en COPY\n"
+
+#: fe-exec.c:3169
+msgid "cannot send pipeline when not in pipeline mode\n"
+msgstr "no se puede enviar pipeline cuando no se está en modo pipeline\n"
+
+#: fe-exec.c:3272
+msgid "invalid ExecStatusType code"
+msgstr "el código de ExecStatusType no es válido"
+
+#: fe-exec.c:3299
+msgid "PGresult is not an error result\n"
+msgstr "PGresult no es un resultado de error\n"
+
+#: fe-exec.c:3367 fe-exec.c:3390
+#, 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:3405
+#, 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:3715
+#, c-format
+msgid "could not interpret result from server: %s"
+msgstr "no se pudo interpretar el resultado del servidor: %s"
+
+#: fe-exec.c:3975 fe-exec.c:4064
+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:145 fe-lobj.c:210 fe-lobj.c:403 fe-lobj.c:494 fe-lobj.c:568
+#: fe-lobj.c:969 fe-lobj.c:977 fe-lobj.c:985 fe-lobj.c:993 fe-lobj.c:1001
+#: fe-lobj.c:1009 fe-lobj.c:1017 fe-lobj.c:1025
+#, c-format
+msgid "cannot determine OID of function %s\n"
+msgstr "no se puede determinar el OID de la función %s\n"
+
+#: fe-lobj.c:162
+msgid "argument of lo_truncate exceeds integer range\n"
+msgstr "el argumento de lo_truncate excede el rango de enteros\n"
+
+#: fe-lobj.c:266
+msgid "argument of lo_read exceeds integer range\n"
+msgstr "el argumento de lo_read excede el rango de enteros\n"
+
+#: fe-lobj.c:318
+msgid "argument of lo_write exceeds integer range\n"
+msgstr "el argumento de lo_write excede el rango de enteros\n"
+
+#: fe-lobj.c:678 fe-lobj.c:789
+#, c-format
+msgid "could not open file \"%s\": %s\n"
+msgstr "no se pudo abrir el archivo «%s»: %s\n"
+
+#: fe-lobj.c:734
+#, c-format
+msgid "could not read from file \"%s\": %s\n"
+msgstr "no se pudo leer el archivo «%s»: %s\n"
+
+#: fe-lobj.c:810 fe-lobj.c:834
+#, c-format
+msgid "could not write to file \"%s\": %s\n"
+msgstr "no se pudo escribir a archivo «%s»: %s\n"
+
+#: fe-lobj.c:920
+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-misc.c:242
+#, 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:275
+#, 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:576 fe-misc.c:822
+msgid "connection not open\n"
+msgstr "la conexión no está abierta\n"
+
+#: fe-misc.c:755 fe-secure-openssl.c:209 fe-secure-openssl.c:316
+#: fe-secure.c:260 fe-secure.c:373
+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:1015
+msgid "timeout expired\n"
+msgstr "tiempo de espera agotado\n"
+
+#: fe-misc.c:1060
+msgid "invalid socket\n"
+msgstr "socket no válido\n"
+
+#: fe-misc.c:1083
+#, c-format
+msgid "%s() failed: %s\n"
+msgstr "%s() falló: %s\n"
+
+#: fe-protocol3.c:184
+#, 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-protocol3.c:407
+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:450
+#, 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-protocol3.c:475
+#, 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:495
+#, 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:547 fe-protocol3.c:587
+msgid "insufficient data in \"T\" message"
+msgstr "datos insuficientes en el mensaje «T»"
+
+#: fe-protocol3.c:658 fe-protocol3.c:864
+msgid "out of memory for query result"
+msgstr "no hay suficiente memoria para el resultado de la consulta"
+
+#: fe-protocol3.c:727
+msgid "insufficient data in \"t\" message"
+msgstr "datos insuficientes en el mensaje «t»"
+
+#: fe-protocol3.c:786 fe-protocol3.c:818 fe-protocol3.c:836
+msgid "insufficient data in \"D\" message"
+msgstr "datos insuficientes en el mensaje «D»"
+
+#: fe-protocol3.c:792
+msgid "unexpected field count in \"D\" message"
+msgstr "cantidad de campos inesperada en mensaje «D»"
+
+#: fe-protocol3.c:1040
+msgid "no error message available\n"
+msgstr "no hay mensaje de error disponible\n"
+
+#. translator: %s represents a digit string
+#: fe-protocol3.c:1088 fe-protocol3.c:1107
+#, c-format
+msgid " at character %s"
+msgstr " en el carácter %s"
+
+#: fe-protocol3.c:1120
+#, c-format
+msgid "DETAIL: %s\n"
+msgstr "DETALLE: %s\n"
+
+#: fe-protocol3.c:1123
+#, c-format
+msgid "HINT: %s\n"
+msgstr "SUGERENCIA: %s\n"
+
+#: fe-protocol3.c:1126
+#, c-format
+msgid "QUERY: %s\n"
+msgstr "CONSULTA: %s\n"
+
+#: fe-protocol3.c:1133
+#, c-format
+msgid "CONTEXT: %s\n"
+msgstr "CONTEXTO: %s\n"
+
+#: fe-protocol3.c:1142
+#, c-format
+msgid "SCHEMA NAME: %s\n"
+msgstr "NOMBRE DE ESQUEMA: %s\n"
+
+#: fe-protocol3.c:1146
+#, c-format
+msgid "TABLE NAME: %s\n"
+msgstr "NOMBRE DE TABLA: %s\n"
+
+#: fe-protocol3.c:1150
+#, c-format
+msgid "COLUMN NAME: %s\n"
+msgstr "NOMBRE DE COLUMNA: %s\n"
+
+#: fe-protocol3.c:1154
+#, c-format
+msgid "DATATYPE NAME: %s\n"
+msgstr "NOMBRE TIPO DE DATO: %s\n"
+
+#: fe-protocol3.c:1158
+#, c-format
+msgid "CONSTRAINT NAME: %s\n"
+msgstr "NOMBRE DE RESTRICCIÓN: %s\n"
+
+#: fe-protocol3.c:1170
+msgid "LOCATION: "
+msgstr "UBICACIÓN: "
+
+#: fe-protocol3.c:1172
+#, c-format
+msgid "%s, "
+msgstr "%s, "
+
+#: fe-protocol3.c:1174
+#, c-format
+msgid "%s:%s"
+msgstr "%s:%s"
+
+#: fe-protocol3.c:1369
+#, c-format
+msgid "LINE %d: "
+msgstr "LÍNEA %d: "
+
+#: fe-protocol3.c:1768
+msgid "PQgetline: not doing text COPY OUT\n"
+msgstr "PQgetline: no se está haciendo COPY OUT de texto\n"
+
+#: fe-protocol3.c:2134
+#, c-format
+msgid "protocol error: id=0x%x\n"
+msgstr "error de protocolo: id=0x%x\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:1367
+#, 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:1371
+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:1380
+#, 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:1430
+#, 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:822
+#, c-format
+msgid "could not create SSL context: %s\n"
+msgstr "no se pudo crear un contexto SSL: %s\n"
+
+#: fe-secure-openssl.c:861
+#, 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:872
+#, 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:890
+#, 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:901
+#, 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:937
+#, 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:990
+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:994
+#, 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:1025
+#, 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:1044
+#, 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:1069
+#, c-format
+msgid "could not establish SSL connection: %s\n"
+msgstr "no se pudo establecer conexión SSL: %s\n"
+
+#: fe-secure-openssl.c:1103
+#, c-format
+msgid "could not set SSL Server Name Indication (SNI): %s\n"
+msgstr "no se pudo establecer el Indicador de Nombre del Servidor (SNI) de SSL: %s\n"
+
+#: fe-secure-openssl.c:1149
+#, 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:1161
+#, 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:1177
+#, 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:1191
+#, 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:1228
+#, 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:1237
+#, c-format
+msgid "private key file \"%s\" is not a regular file\n"
+msgstr "el archivo de llave privada «%s» no es un archivo regular\n"
+
+#: fe-secure-openssl.c:1270
+#, c-format
+msgid "private key file \"%s\" has group or world access; file must have permissions u=rw (0600) or less if owned by the current user, or permissions u=rw,g=r (0640) or less if owned by root\n"
+msgstr "el archivo de llave privada «%s» tiene acceso de grupo o para todos; debe tener permisos u=rw (0600) o menos si es de propiedad del usuario de base de datos, o permisos u=rw,g=r (0640) o menos si es de root\n"
+
+#: fe-secure-openssl.c:1295
+#, 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:1313
+#, 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:1413
+#, 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:1449
+#, c-format
+msgid "certificate could not be obtained: %s\n"
+msgstr "el certificado no pudo ser obtenido: %s\n"
+
+#: fe-secure-openssl.c:1555
+#, c-format
+msgid "no SSL error reported"
+msgstr "código de error SSL no reportado"
+
+#: fe-secure-openssl.c:1564
+#, c-format
+msgid "SSL error code %lu"
+msgstr "código de error SSL %lu"
+
+#: fe-secure-openssl.c:1812
+#, c-format
+msgid "WARNING: sslpassword truncated\n"
+msgstr "ADVERTENCIA: sslpassword truncada\n"
+
+#: fe-secure.c:267
+#, c-format
+msgid "could not receive data from server: %s\n"
+msgstr "no se pudo recibir datos del servidor: %s\n"
+
+#: fe-secure.c:380
+#, 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"
diff --git a/src/interfaces/libpq/po/fr.po b/src/interfaces/libpq/po/fr.po
new file mode 100644
index 0000000..b67f3eb
--- /dev/null
+++ b/src/interfaces/libpq/po/fr.po
@@ -0,0 +1,1461 @@
+# 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 14\n"
+"Report-Msgid-Bugs-To: pgsql-bugs@lists.postgresql.org\n"
+"POT-Creation-Date: 2022-07-18 13:25+0000\n"
+"PO-Revision-Date: 2022-07-18 17:18+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 3.1\n"
+
+#: fe-auth-scram.c:213
+msgid "malformed SCRAM message (empty message)\n"
+msgstr "message SCRAM malformé (message vide)\n"
+
+#: fe-auth-scram.c:219
+msgid "malformed SCRAM message (length mismatch)\n"
+msgstr "message SCRAM malformé (pas de correspondance sur la longueur)\n"
+
+#: fe-auth-scram.c:263
+msgid "could not verify server signature\n"
+msgstr "n'a pas pu vérifier la signature du serveur\n"
+
+#: fe-auth-scram.c:270
+msgid "incorrect server signature\n"
+msgstr "signature invalide du serveur\n"
+
+#: fe-auth-scram.c:279
+msgid "invalid SCRAM exchange state\n"
+msgstr "état d'échange SCRAM invalide\n"
+
+#: fe-auth-scram.c:306
+#, c-format
+msgid "malformed SCRAM message (attribute \"%c\" expected)\n"
+msgstr "message SCRAM malformé (attribut « %c » attendu)\n"
+
+#: fe-auth-scram.c:315
+#, 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:356
+msgid "could not generate nonce\n"
+msgstr "n'a pas pu générer le nonce\n"
+
+#: fe-auth-scram.c:366 fe-auth-scram.c:441 fe-auth-scram.c:595
+#: fe-auth-scram.c:616 fe-auth-scram.c:642 fe-auth-scram.c:657
+#: fe-auth-scram.c:707 fe-auth-scram.c:746 fe-auth.c:290 fe-auth.c:362
+#: fe-auth.c:398 fe-auth.c:615 fe-auth.c:774 fe-auth.c:1132 fe-auth.c:1282
+#: fe-connect.c:911 fe-connect.c:1460 fe-connect.c:1629 fe-connect.c:2981
+#: fe-connect.c:4711 fe-connect.c:4972 fe-connect.c:5091 fe-connect.c:5343
+#: fe-connect.c:5424 fe-connect.c:5523 fe-connect.c:5779 fe-connect.c:5808
+#: fe-connect.c:5880 fe-connect.c:5904 fe-connect.c:5922 fe-connect.c:6023
+#: fe-connect.c:6032 fe-connect.c:6390 fe-connect.c:6540 fe-connect.c:6806
+#: fe-exec.c:686 fe-exec.c:876 fe-exec.c:1223 fe-exec.c:3125 fe-exec.c:3309
+#: fe-exec.c:4082 fe-exec.c:4247 fe-gssapi-common.c:111 fe-lobj.c:881
+#: fe-protocol3.c:979 fe-protocol3.c:994 fe-protocol3.c:1027
+#: fe-protocol3.c:1735 fe-secure-common.c:110 fe-secure-gssapi.c:504
+#: fe-secure-openssl.c:440 fe-secure-openssl.c:1133
+msgid "out of memory\n"
+msgstr "mémoire épuisée\n"
+
+#: fe-auth-scram.c:374
+msgid "could not encode nonce\n"
+msgstr "n'a pas pu encoder le nonce\n"
+
+#: fe-auth-scram.c:563
+msgid "could not calculate client proof\n"
+msgstr "n'a pas pu calculer la preuve du client\n"
+
+#: fe-auth-scram.c:579
+msgid "could not encode client proof\n"
+msgstr "n'a pas pu encoder la preuve du client\n"
+
+#: fe-auth-scram.c:634
+msgid "invalid SCRAM response (nonce mismatch)\n"
+msgstr "réponse SCRAM invalide (pas de correspondance sur nonce)\n"
+
+#: fe-auth-scram.c:667
+msgid "malformed SCRAM message (invalid salt)\n"
+msgstr "message SCRAM malformé (sel invalide)\n"
+
+#: fe-auth-scram.c:681
+msgid "malformed SCRAM message (invalid iteration count)\n"
+msgstr "message SCRAM malformé (décompte d'itération invalide)\n"
+
+#: fe-auth-scram.c:687
+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:723
+#, 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:739
+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:758
+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:391 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:351
+msgid "duplicate SSPI authentication request\n"
+msgstr "requête d'authentification SSPI dupliquée\n"
+
+#: fe-auth.c:377
+msgid "could not acquire SSPI credentials"
+msgstr "n'a pas pu obtenir les pièces d'identité SSPI"
+
+#: fe-auth.c:433
+msgid "channel binding required, but SSL not in use\n"
+msgstr "lien de canal requis, mais SSL non utilisé\n"
+
+#: fe-auth.c:440
+msgid "duplicate SASL authentication request\n"
+msgstr "requête d'authentification SASL dupliquée\n"
+
+#: fe-auth.c:496
+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:513
+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:525
+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:533
+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:639
+#, 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:664
+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:741
+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:877
+msgid "Kerberos 4 authentication not supported\n"
+msgstr "authentification Kerberos 4 non supportée\n"
+
+#: fe-auth.c:882
+msgid "Kerberos 5 authentication not supported\n"
+msgstr "authentification Kerberos 5 non supportée\n"
+
+#: fe-auth.c:953
+msgid "GSSAPI authentication not supported\n"
+msgstr "authentification GSSAPI non supportée\n"
+
+#: fe-auth.c:985
+msgid "SSPI authentication not supported\n"
+msgstr "authentification SSPI non supportée\n"
+
+#: fe-auth.c:993
+msgid "Crypt authentication not supported\n"
+msgstr "authentification crypt non supportée\n"
+
+#: fe-auth.c:1060
+#, c-format
+msgid "authentication method %u not supported\n"
+msgstr "méthode d'authentification %u non supportée\n"
+
+#: fe-auth.c:1107
+#, c-format
+msgid "user name lookup failure: error code %lu\n"
+msgstr "échec de la recherche du nom d'utilisateur : code d'erreur %lu\n"
+
+#: fe-auth.c:1117 fe-connect.c:2856
+#, 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:1122 fe-connect.c:2861
+#, 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:1226
+msgid "unexpected shape of result set returned for SHOW\n"
+msgstr "forme du résultat inattendu pour SHOW\n"
+
+#: fe-auth.c:1235
+msgid "password_encryption value too long\n"
+msgstr "la valeur de password_encryption est trop longue\n"
+
+#: fe-auth.c:1275
+#, c-format
+msgid "unrecognized password encryption algorithm \"%s\"\n"
+msgstr "algorithme de chiffrement du mot de passe « %s » non reconnu\n"
+
+#: fe-connect.c:1094
+#, 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:1180
+#, 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:1273 fe-connect.c:1299 fe-connect.c:1341 fe-connect.c:1350
+#: fe-connect.c:1383 fe-connect.c:1427
+#, c-format
+msgid "invalid %s value: \"%s\"\n"
+msgstr "valeur %s invalide : « %s »\n"
+
+#: fe-connect.c:1320
+#, 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:1368
+msgid "invalid SSL protocol version range\n"
+msgstr "intervalle de version invalide pour le protocole SSL\n"
+
+#: fe-connect.c:1393
+#, 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:1653
+#, 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:1715
+#, c-format
+msgid "connection to server on socket \"%s\" failed: "
+msgstr "la connexion au serveur sur le socket « %s » a échoué : "
+
+#: fe-connect.c:1742
+#, c-format
+msgid "connection to server at \"%s\" (%s), port %s failed: "
+msgstr "la connexion au serveur sur « %s » (%s), port %s a échoué : "
+
+#: fe-connect.c:1747
+#, c-format
+msgid "connection to server at \"%s\", port %s failed: "
+msgstr "la connexion au serveur sur « %s », port %s a échoué : "
+
+#: fe-connect.c:1772
+msgid "\tIs the server running locally and accepting connections on that socket?\n"
+msgstr "\tLe serveur est-il actif localement et accepte-t-il les connexions sur ce socket ?\n"
+
+#: fe-connect.c:1776
+msgid "\tIs the server running on that host and accepting TCP/IP connections?\n"
+msgstr "\tLe serveur est-il actif sur cet hôte et accepte-t-il les connexions ?\n"
+
+#: fe-connect.c:1840
+#, 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:1870 fe-connect.c:1905 fe-connect.c:1941 fe-connect.c:2030
+#: fe-connect.c:2644
+#, c-format
+msgid "%s(%s) failed: %s\n"
+msgstr "échec de %s(%s) : %s\n"
+
+#: fe-connect.c:1995
+#, c-format
+msgid "%s(%s) failed: error code %d\n"
+msgstr "échec de %s(%s) : code d'erreur %d\n"
+
+#: fe-connect.c:2310
+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:2389
+#, c-format
+msgid "invalid port number: \"%s\"\n"
+msgstr "numéro de port invalide : « %s »\n"
+
+#: fe-connect.c:2405
+#, 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:2418
+#, 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:2431
+#, 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:2446
+#, 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:2572
+#, c-format
+msgid "could not create socket: %s\n"
+msgstr "n'a pas pu créer la socket : %s\n"
+
+#: fe-connect.c:2603
+#, 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:2613
+#, 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:2631
+msgid "keepalives parameter must be an integer\n"
+msgstr "le paramètre keepalives doit être un entier\n"
+
+#: fe-connect.c:2772
+#, 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:2800
+#, 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:2842
+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:2845
+#, 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:2869
+#, 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:2909
+#, 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:2921
+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:2963
+#, 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:2994
+#, 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:3070
+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:3097
+#, 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:3118
+msgid "received unencrypted data after SSL response\n"
+msgstr "a reçu des données non chiffrées après la réponse SSL\n"
+
+#: fe-connect.c:3199
+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:3211
+#, 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:3230
+msgid "received unencrypted data after GSSAPI encryption response\n"
+msgstr "a reçu des données non chiffrées après la réponse de chiffrement GSSAPI\n"
+
+#: fe-connect.c:3290 fe-connect.c:3315
+#, 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:3522
+msgid "unexpected message from server during startup\n"
+msgstr "message inattendu du serveur lors du démarrage\n"
+
+#: fe-connect.c:3614
+msgid "session is read-only\n"
+msgstr "la session est en lecture seule\n"
+
+#: fe-connect.c:3617
+msgid "session is not read-only\n"
+msgstr "la session n'est pas en lecture seule\n"
+
+#: fe-connect.c:3671
+msgid "server is in hot standby mode\n"
+msgstr "le serveur est dans le mode hot standby\n"
+
+#: fe-connect.c:3674
+msgid "server is not in hot standby mode\n"
+msgstr "le serveur n'est pas dans le mode hot standby\n"
+
+#: fe-connect.c:3792 fe-connect.c:3844
+#, c-format
+msgid "\"%s\" failed\n"
+msgstr "échec de « %s »\n"
+
+#: fe-connect.c:3858
+#, 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:4304 fe-connect.c:4364
+#, 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:4724
+#, 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:4739
+#, c-format
+msgid "invalid LDAP URL \"%s\": missing distinguished name\n"
+msgstr "URL LDAP « %s » invalide : le « distinguished name » manque\n"
+
+#: fe-connect.c:4751 fe-connect.c:4809
+#, 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:4763 fe-connect.c:4825
+#, 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:4775
+#, c-format
+msgid "invalid LDAP URL \"%s\": no filter\n"
+msgstr "URL LDAP « %s » invalide : aucun filtre\n"
+
+#: fe-connect.c:4797
+#, 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:4835
+msgid "could not create LDAP structure\n"
+msgstr "n'a pas pu créer la structure LDAP\n"
+
+#: fe-connect.c:4911
+#, c-format
+msgid "lookup on LDAP server failed: %s\n"
+msgstr "échec de la recherche sur le serveur LDAP : %s\n"
+
+#: fe-connect.c:4922
+msgid "more than one entry found on LDAP lookup\n"
+msgstr "plusieurs entrées trouvées pendant la recherche LDAP\n"
+
+#: fe-connect.c:4923 fe-connect.c:4935
+msgid "no entry found on LDAP lookup\n"
+msgstr "aucune entrée trouvée pendant la recherche LDAP\n"
+
+#: fe-connect.c:4946 fe-connect.c:4959
+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:5011 fe-connect.c:5030 fe-connect.c:5562
+#, 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:5103 fe-connect.c:5747 fe-connect.c:6523
+#, c-format
+msgid "invalid connection option \"%s\"\n"
+msgstr "option de connexion « %s » invalide\n"
+
+#: fe-connect.c:5119 fe-connect.c:5611
+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:5200
+#, c-format
+msgid "definition of service \"%s\" not found\n"
+msgstr "définition du service « %s » introuvable\n"
+
+#: fe-connect.c:5226
+#, c-format
+msgid "service file \"%s\" not found\n"
+msgstr "fichier de service « %s » introuvable\n"
+
+#: fe-connect.c:5240
+#, 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:5311 fe-connect.c:5355
+#, 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:5322
+#, 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:6043
+#, 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:6120
+#, 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:6127
+#, 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:6142
+#, 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:6272
+#, 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:6292
+#, 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:6344
+#, c-format
+msgid "invalid URI query parameter: \"%s\"\n"
+msgstr "paramètre de la requête URI invalide : « %s »\n"
+
+#: fe-connect.c:6418
+#, c-format
+msgid "invalid percent-encoded token: \"%s\"\n"
+msgstr "jeton encodé en pourcentage invalide : « %s »\n"
+
+#: fe-connect.c:6428
+#, 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:6798
+msgid "connection pointer is NULL\n"
+msgstr "le pointeur de connexion est NULL\n"
+
+#: fe-connect.c:7086
+#, 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:7095
+#, 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:7203
+#, c-format
+msgid "password retrieved from file \"%s\"\n"
+msgstr "mot de passe récupéré dans le fichier « %s »\n"
+
+#: fe-exec.c:449 fe-exec.c:3383
+#, 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:510 fe-protocol3.c:207 fe-protocol3.c:232 fe-protocol3.c:261
+#: fe-protocol3.c:279 fe-protocol3.c:375 fe-protocol3.c:747
+msgid "out of memory"
+msgstr "mémoire épuisée"
+
+#: fe-exec.c:511 fe-protocol3.c:1943
+#, c-format
+msgid "%s"
+msgstr "%s"
+
+#: fe-exec.c:792
+msgid "write to server failed\n"
+msgstr "échec en écriture vers le serveur\n"
+
+#: fe-exec.c:864
+msgid "NOTICE"
+msgstr "NOTICE"
+
+#: fe-exec.c:922
+msgid "PGresult cannot support more than INT_MAX tuples"
+msgstr "PGresult ne supporte pas plus de INT_MAX lignes"
+
+#: fe-exec.c:934
+msgid "size_t overflow"
+msgstr "saturation de size_t"
+
+#: fe-exec.c:1351 fe-exec.c:1477 fe-exec.c:1526
+msgid "command string is a null pointer\n"
+msgstr "la chaîne de commande est un pointeur nul\n"
+
+#: fe-exec.c:1483 fe-exec.c:1532 fe-exec.c:1628
+#, c-format
+msgid "number of parameters must be between 0 and %d\n"
+msgstr "le nombre de paramètres doit être compris entre 0 et %d\n"
+
+#: fe-exec.c:1520 fe-exec.c:1622
+msgid "statement name is a null pointer\n"
+msgstr "le nom de l'instruction est un pointeur nul\n"
+
+#: fe-exec.c:1664 fe-exec.c:3236
+msgid "no connection to the server\n"
+msgstr "aucune connexion au serveur\n"
+
+#: fe-exec.c:1673 fe-exec.c:3245
+msgid "another command is already in progress\n"
+msgstr "une autre commande est déjà en cours\n"
+
+#: fe-exec.c:1704
+msgid "cannot queue commands during COPY\n"
+msgstr "ne peut pas mettre en queue les commandes lors du COPY\n"
+
+#: fe-exec.c:1822
+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:2149
+#, c-format
+msgid "unexpected asyncStatus: %d\n"
+msgstr "asyncStatus inattendu : %d\n"
+
+#: fe-exec.c:2185
+#, 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:2333
+msgid "synchronous command execution functions are not allowed in pipeline mode\n"
+msgstr "les fonctions d'exécution de commande synchrone ne sont pas autorisées en mode pipeline\n"
+
+#: fe-exec.c:2355
+msgid "COPY terminated by new PQexec"
+msgstr "COPY terminé par un nouveau PQexec"
+
+#: fe-exec.c:2372
+msgid "PQexec not allowed during COPY BOTH\n"
+msgstr "PQexec non autorisé pendant COPY BOTH\n"
+
+#: fe-exec.c:2600 fe-exec.c:2656 fe-exec.c:2725 fe-protocol3.c:1874
+msgid "no COPY in progress\n"
+msgstr "aucun COPY en cours\n"
+
+#: fe-exec.c:2902
+msgid "PQfn not allowed in pipeline mode\n"
+msgstr "PQfn non autorisé dans le mode pipeline\n"
+
+#: fe-exec.c:2910
+msgid "connection in wrong state\n"
+msgstr "connexion dans un état erroné\n"
+
+#: fe-exec.c:2954
+msgid "cannot enter pipeline mode, connection not idle\n"
+msgstr "ne peut pas entrer dans le mode pipeline, connexion active\n"
+
+#: fe-exec.c:2991 fe-exec.c:3015
+msgid "cannot exit pipeline mode with uncollected results\n"
+msgstr "ne peut pas sortir du mode pipeline avec des résultats non récupérés\n"
+
+#: fe-exec.c:2996
+msgid "cannot exit pipeline mode while busy\n"
+msgstr "ne peut pas sortir du mode pipeline alors qu'il est occupé\n"
+
+#: fe-exec.c:3008
+msgid "cannot exit pipeline mode while in COPY\n"
+msgstr "ne peut pas sortir du mode pipeline pendant un COPY\n"
+
+#: fe-exec.c:3169
+msgid "cannot send pipeline when not in pipeline mode\n"
+msgstr "ne peut pas envoyer le pipeline lorsqu'il n'est pas en mode pipeline\n"
+
+#: fe-exec.c:3272
+msgid "invalid ExecStatusType code"
+msgstr "code ExecStatusType invalide"
+
+#: fe-exec.c:3299
+msgid "PGresult is not an error result\n"
+msgstr "PGresult n'est pas un résultat d'erreur\n"
+
+#: fe-exec.c:3367 fe-exec.c:3390
+#, 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:3405
+#, 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:3715
+#, 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:3975 fe-exec.c:4064
+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:145 fe-lobj.c:210 fe-lobj.c:403 fe-lobj.c:494 fe-lobj.c:568
+#: fe-lobj.c:969 fe-lobj.c:977 fe-lobj.c:985 fe-lobj.c:993 fe-lobj.c:1001
+#: fe-lobj.c:1009 fe-lobj.c:1017 fe-lobj.c:1025
+#, c-format
+msgid "cannot determine OID of function %s\n"
+msgstr "ne peut pas déterminer l'OID de la fonction %s\n"
+
+#: fe-lobj.c:162
+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:266
+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:318
+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:678 fe-lobj.c:789
+#, c-format
+msgid "could not open file \"%s\": %s\n"
+msgstr "n'a pas pu ouvrir le fichier « %s » : %s\n"
+
+#: fe-lobj.c:734
+#, 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:810 fe-lobj.c:834
+#, 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:920
+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-misc.c:242
+#, c-format
+msgid "integer of size %lu not supported by pqGetInt"
+msgstr "entier de taille %lu non supporté par pqGetInt"
+
+#: fe-misc.c:275
+#, c-format
+msgid "integer of size %lu not supported by pqPutInt"
+msgstr "entier de taille %lu non supporté par pqPutInt"
+
+#: fe-misc.c:576 fe-misc.c:822
+msgid "connection not open\n"
+msgstr "la connexion n'est pas active\n"
+
+#: fe-misc.c:755 fe-secure-openssl.c:209 fe-secure-openssl.c:316
+#: fe-secure.c:260 fe-secure.c:373
+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:1015
+msgid "timeout expired\n"
+msgstr "le délai est dépassé\n"
+
+#: fe-misc.c:1060
+msgid "invalid socket\n"
+msgstr "socket invalide\n"
+
+#: fe-misc.c:1083
+#, c-format
+msgid "%s() failed: %s\n"
+msgstr "échec de %s() : %s\n"
+
+#: fe-protocol3.c:184
+#, 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-protocol3.c:407
+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:450
+#, 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-protocol3.c:475
+#, 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:495
+#, 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:547 fe-protocol3.c:587
+msgid "insufficient data in \"T\" message"
+msgstr "données insuffisantes dans le message « T »"
+
+#: fe-protocol3.c:658 fe-protocol3.c:864
+msgid "out of memory for query result"
+msgstr "mémoire épuisée pour le résultat de la requête"
+
+#: fe-protocol3.c:727
+msgid "insufficient data in \"t\" message"
+msgstr "données insuffisantes dans le message « t »"
+
+#: fe-protocol3.c:786 fe-protocol3.c:818 fe-protocol3.c:836
+msgid "insufficient data in \"D\" message"
+msgstr "données insuffisantes dans le message « D »"
+
+#: fe-protocol3.c:792
+msgid "unexpected field count in \"D\" message"
+msgstr "nombre de champs inattendu dans le message « D »"
+
+#: fe-protocol3.c:1040
+msgid "no error message available\n"
+msgstr "aucun message d'erreur disponible\n"
+
+#. translator: %s represents a digit string
+#: fe-protocol3.c:1088 fe-protocol3.c:1107
+#, c-format
+msgid " at character %s"
+msgstr " au caractère %s"
+
+#: fe-protocol3.c:1120
+#, c-format
+msgid "DETAIL: %s\n"
+msgstr "DÉTAIL : %s\n"
+
+#: fe-protocol3.c:1123
+#, c-format
+msgid "HINT: %s\n"
+msgstr "ASTUCE : %s\n"
+
+#: fe-protocol3.c:1126
+#, c-format
+msgid "QUERY: %s\n"
+msgstr "REQUÊTE : %s\n"
+
+#: fe-protocol3.c:1133
+#, c-format
+msgid "CONTEXT: %s\n"
+msgstr "CONTEXTE : %s\n"
+
+#: fe-protocol3.c:1142
+#, c-format
+msgid "SCHEMA NAME: %s\n"
+msgstr "NOM DE SCHÉMA : %s\n"
+
+#: fe-protocol3.c:1146
+#, c-format
+msgid "TABLE NAME: %s\n"
+msgstr "NOM DE TABLE : %s\n"
+
+#: fe-protocol3.c:1150
+#, c-format
+msgid "COLUMN NAME: %s\n"
+msgstr "NOM DE COLONNE : %s\n"
+
+#: fe-protocol3.c:1154
+#, c-format
+msgid "DATATYPE NAME: %s\n"
+msgstr "NOM DU TYPE DE DONNÉES : %s\n"
+
+#: fe-protocol3.c:1158
+#, c-format
+msgid "CONSTRAINT NAME: %s\n"
+msgstr "NOM DE CONTRAINTE : %s\n"
+
+#: fe-protocol3.c:1170
+msgid "LOCATION: "
+msgstr "EMPLACEMENT : "
+
+#: fe-protocol3.c:1172
+#, c-format
+msgid "%s, "
+msgstr "%s, "
+
+#: fe-protocol3.c:1174
+#, c-format
+msgid "%s:%s"
+msgstr "%s : %s"
+
+#: fe-protocol3.c:1369
+#, c-format
+msgid "LINE %d: "
+msgstr "LIGNE %d : "
+
+#: fe-protocol3.c:1768
+msgid "PQgetline: not doing text COPY OUT\n"
+msgstr "PQgetline : ne va pas réaliser un COPY OUT au format texte\n"
+
+#: fe-protocol3.c:2134
+#, c-format
+msgid "protocol error: id=0x%x\n"
+msgstr "erreur de protocole : id=0x%x\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 d'emballage GSSAPI"
+
+#: fe-secure-gssapi.c:209
+msgid "outgoing GSSAPI message would not use confidentiality\n"
+msgstr "le message sortant GSSAPI n'utiliserait 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 de dépaquetage GSSAPI"
+
+#: fe-secure-gssapi.c:403
+msgid "incoming GSSAPI message did not use confidentiality\n"
+msgstr "le message entrant GSSAPI n'a pas utilisé 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:1367
+#, 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:1371
+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:1380
+#, 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:1430
+#, 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:822
+#, 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:861
+#, 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:872
+#, 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:890
+#, 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:901
+#, 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:937
+#, 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:990
+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:994
+#, 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:1025
+#, 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:1044
+#, 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:1069
+#, 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:1103
+#, c-format
+msgid "could not set SSL Server Name Indication (SNI): %s\n"
+msgstr "n'a pas pu configurer le SSL Server Name Indication (SNI) : %s\n"
+
+#: fe-secure-openssl.c:1149
+#, 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:1161
+#, 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:1177
+#, 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:1191
+#, 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:1228
+#, 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:1237
+#, c-format
+msgid "private key file \"%s\" is not a regular file\n"
+msgstr "le fichier de clé privée « %s » n'est pas un fichier standard\n"
+
+#: fe-secure-openssl.c:1270
+#, c-format
+msgid "private key file \"%s\" has group or world access; file must have permissions u=rw (0600) or less if owned by the current user, or permissions u=rw,g=r (0640) or less if owned by root\n"
+msgstr "le fichier de clé privée « %s » a des droits d'accès pour le groupe ou le monde ; le fichier doit avoir les droits u=rw (0600) ou moins si le propriétaire est l'utilisateur courant, ou les droits u=rw,g=r (0640) ou moins si le propriétaire est root\n"
+
+#: fe-secure-openssl.c:1295
+#, 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:1313
+#, 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:1413
+#, 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:1449
+#, 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:1555
+#, c-format
+msgid "no SSL error reported"
+msgstr "aucune erreur SSL reportée"
+
+#: fe-secure-openssl.c:1564
+#, c-format
+msgid "SSL error code %lu"
+msgstr "code d'erreur SSL %lu"
+
+#: fe-secure-openssl.c:1812
+#, c-format
+msgid "WARNING: sslpassword truncated\n"
+msgstr "ATTENTION : sslpassword tronqué\n"
+
+#: fe-secure.c:267
+#, 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:380
+#, 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 "\"SELECT pg_is_in_recovery()\" failed\n"
+#~ msgstr "\"SELECT pg_is_in_recovery()\" a échoué\n"
+
+#~ msgid "\"SHOW transaction_read_only\" failed\n"
+#~ msgstr "\"SHOW transaction_read_only\" a échoué\n"
+
+#~ msgid "COPY IN state must be terminated first\n"
+#~ msgstr "l'état COPY IN doit d'abord être terminé\n"
+
+#~ msgid "COPY OUT state must be terminated first\n"
+#~ msgstr "l'état COPY OUT doit d'abord être terminé\n"
+
+#~ msgid "Kerberos 5 authentication rejected: %*s\n"
+#~ msgstr "authentification Kerberos 5 rejetée : %*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 "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 "WSAIoctl(SIO_KEEPALIVE_VALS) failed: %d\n"
+#~ msgstr "échec de WSAIoctl(SIO_KEEPALIVE_VALS) : %d\n"
+
+#~ msgid "cannot determine OID of function lo_creat\n"
+#~ msgstr "ne peut pas déterminer l'OID de la fonction lo_creat\n"
+
+#~ msgid "cannot determine OID of function lo_create\n"
+#~ msgstr "ne peut pas déterminer l'OID de la fonction lo_create\n"
+
+#~ msgid "cannot determine OID of function lo_lseek\n"
+#~ msgstr "ne peut pas déterminer l'OID de la fonction lo_lseek\n"
+
+#~ msgid "cannot determine OID of function lo_lseek64\n"
+#~ msgstr "ne peut pas déterminer l'OID de la fonction lo_lseek64\n"
+
+#~ msgid "cannot determine OID of function lo_open\n"
+#~ msgstr "ne peut pas déterminer l'OID de la fonction lo_open\n"
+
+#~ msgid "cannot determine OID of function lo_tell64\n"
+#~ msgstr "ne peut pas déterminer l'OID de la fonction lo_tell64\n"
+
+#~ msgid "cannot determine OID of function lo_truncate\n"
+#~ msgstr "ne peut pas déterminer l'OID de la fonction lo_truncate\n"
+
+#~ msgid "cannot determine OID of function lo_truncate64\n"
+#~ msgstr "ne peut pas déterminer l'OID de la fonction lo_truncate64\n"
+
+#~ msgid "cannot determine OID of function lo_unlink\n"
+#~ msgstr "ne peut pas déterminer l'OID de la fonction lo_unlink\n"
+
+#~ msgid "cannot determine OID of function loread\n"
+#~ msgstr "ne peut pas déterminer l'OID de la fonction loread\n"
+
+#~ msgid "cannot determine OID of function lowrite\n"
+#~ msgstr "ne peut pas déterminer l'OID de la fonction lowrite\n"
+
+#~ msgid "could not acquire mutex: %s\n"
+#~ msgstr "n'a pas pu acquérir le mutex : %s\n"
+
+#~ 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"
+
+#, c-format
+#~ msgid "could not encrypt password: %s\n"
+#~ msgstr "n'a pas pu chiffré le mot de passe : %s\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 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 "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"
+
+#~ 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 "could not read private key file \"%s\": %s\n"
+#~ msgstr "n'a pas pu lire la clé privée « %s » : %s\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 "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 "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 socket to blocking mode: %s\n"
+#~ msgstr "n'a pas pu activer le mode bloquant pour la socket : %s\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 »"
+
+#~ msgid "failed to generate nonce\n"
+#~ msgstr "échec pour la génération de nonce\n"
+
+#~ msgid "function requires at least protocol version 3.0\n"
+#~ msgstr "la fonction nécessite au minimum le protocole 3.0\n"
+
+#~ 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 "invalid channel_binding value: \"%s\"\n"
+#~ msgstr "valeur de channel_binding invalide : « %s »\n"
+
+#~ msgid "invalid gssencmode value: \"%s\"\n"
+#~ msgstr "valeur gssencmode invalide : « %s »\n"
+
+#~ 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"
+
+#~ msgid "invalid ssl_max_protocol_version value: \"%s\"\n"
+#~ msgstr "valeur ssl_max_protocol_version invalide : « %s »\n"
+
+#~ msgid "invalid ssl_min_protocol_version value: \"%s\"\n"
+#~ msgstr "valeur ssl_min_protocol_version invalide : « %s »\n"
+
+#~ msgid "invalid state %c, probably indicative of memory corruption\n"
+#~ msgstr "état %c invalide, indiquant probablement une corruption de la mémoire\n"
+
+#~ msgid "invalid target_session_attrs value: \"%s\"\n"
+#~ msgstr "valeur target_session_attrs invalide : « %s »\n"
+
+#~ msgid "lost synchronization with server, resetting connection"
+#~ msgstr "synchronisation perdue avec le serveur, réinitialisation de la connexion"
+
+#~ msgid "no GSSAPI support; cannot require GSSAPI\n"
+#~ msgstr "pas de support de GSSAPI : ne peut pas nécessiter GSSAPI\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"
+
+#, 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"
+
+#, c-format
+#~ msgid "private key file \"%s\" must be owned by the current user or root\n"
+#~ msgstr "le fichier de clé privée « %s » doit avoir comme propriétaire l'utilisateur courant ou root\n"
+
+#~ msgid "select() failed: %s\n"
+#~ msgstr "échec de select() : %s\n"
+
+#~ 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 »)"
+
+#~ 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 »)"
+
+#~ msgid "setsockopt(%s) failed: %s\n"
+#~ msgstr "setsockopt(%s) a échoué : %s\n"
+
+#~ msgid "setsockopt(SO_KEEPALIVE) failed: %s\n"
+#~ msgstr "setsockopt(SO_KEEPALIVE) a échoué : %s\n"
+
+#~ msgid "setsockopt(TCP_KEEPALIVE) failed: %s\n"
+#~ msgstr "setsockopt(TCP_KEEPALIVE) a échoué : %s\n"
+
+#~ msgid "setsockopt(TCP_KEEPIDLE) failed: %s\n"
+#~ msgstr "setsockopt(TCP_KEEPIDLE) a échoué : %s\n"
+
+#~ msgid "setsockopt(TCP_KEEPINTVL) failed: %s\n"
+#~ msgstr "setsockopt(TCP_KEEPINTVL) a échoué : %s\n"
+
+#~ msgid "socket not open\n"
+#~ msgstr "socket non ouvert\n"
+
+#~ 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 »)"
+
+#~ msgid "unrecognized return value from row processor"
+#~ msgstr "valeur de retour du traitement de la ligne non reconnue"
+
+#~ 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"
diff --git a/src/interfaces/libpq/po/ja.po b/src/interfaces/libpq/po/ja.po
new file mode 100644
index 0000000..cf219cb
--- /dev/null
+++ b/src/interfaces/libpq/po/ja.po
@@ -0,0 +1,1231 @@
+# libpq.po
+# Japanese message translation file for libpq
+#
+# Copyright (C) 2011-2022 PostgreSQL Global Development Group
+#
+# This file is distributed under the same license as the PostgreSQL package.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: libpq (PostgreSQL 14)\n"
+"Report-Msgid-Bugs-To: pgsql-bugs@lists.postgresql.org\n"
+"POT-Creation-Date: 2022-07-06 10:32+0900\n"
+"PO-Revision-Date: 2022-07-06 10:52+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:213
+msgid "malformed SCRAM message (empty message)\n"
+msgstr "SCRAMメッセージのフォーマット異常 (空のメッセージ)\n"
+
+#: fe-auth-scram.c:219
+msgid "malformed SCRAM message (length mismatch)\n"
+msgstr "SCRAMメッセージのフォーマット異常 (長さの不整合)\n"
+
+#: fe-auth-scram.c:263
+msgid "could not verify server signature\n"
+msgstr "サーバーシグネチャの検証ができませんでした\n"
+
+#: fe-auth-scram.c:270
+msgid "incorrect server signature\n"
+msgstr "正しくないサーバー署名\n"
+
+#: fe-auth-scram.c:279
+msgid "invalid SCRAM exchange state\n"
+msgstr "不正なSCRAM交換状態\n"
+
+#: fe-auth-scram.c:306
+#, c-format
+msgid "malformed SCRAM message (attribute \"%c\" expected)\n"
+msgstr "SCRAMメッセージのフォーマット異常 (属性 \"%c\" が必要)\n"
+
+#: fe-auth-scram.c:315
+#, c-format
+msgid "malformed SCRAM message (expected character \"=\" for attribute \"%c\")\n"
+msgstr "SCRAMメッセージのフォーマット異常 (属性 \"%c\" に文字 \"=\" が必要)\n"
+
+#: fe-auth-scram.c:356
+msgid "could not generate nonce\n"
+msgstr "nonce を生成できませんでした\n"
+
+#: fe-auth-scram.c:366 fe-auth-scram.c:441 fe-auth-scram.c:595
+#: fe-auth-scram.c:616 fe-auth-scram.c:642 fe-auth-scram.c:657
+#: fe-auth-scram.c:707 fe-auth-scram.c:746 fe-auth.c:290 fe-auth.c:362
+#: fe-auth.c:398 fe-auth.c:615 fe-auth.c:774 fe-auth.c:1132 fe-auth.c:1282
+#: fe-connect.c:911 fe-connect.c:1460 fe-connect.c:1629 fe-connect.c:2981
+#: fe-connect.c:4711 fe-connect.c:4972 fe-connect.c:5091 fe-connect.c:5343
+#: fe-connect.c:5424 fe-connect.c:5523 fe-connect.c:5779 fe-connect.c:5808
+#: fe-connect.c:5880 fe-connect.c:5904 fe-connect.c:5922 fe-connect.c:6023
+#: fe-connect.c:6032 fe-connect.c:6390 fe-connect.c:6540 fe-connect.c:6806
+#: fe-exec.c:686 fe-exec.c:876 fe-exec.c:1223 fe-exec.c:3124 fe-exec.c:3308
+#: fe-exec.c:4081 fe-exec.c:4246 fe-gssapi-common.c:111 fe-lobj.c:881
+#: fe-protocol3.c:979 fe-protocol3.c:994 fe-protocol3.c:1027
+#: fe-protocol3.c:1735 fe-secure-common.c:110 fe-secure-gssapi.c:504
+#: fe-secure-openssl.c:440 fe-secure-openssl.c:1133
+msgid "out of memory\n"
+msgstr "メモリ不足\n"
+
+#: fe-auth-scram.c:374
+msgid "could not encode nonce\n"
+msgstr "nonceをエンコードに失敗しました\n"
+
+#: fe-auth-scram.c:563
+msgid "could not calculate client proof\n"
+msgstr "クライアント証明の算出に失敗しました\n"
+
+#: fe-auth-scram.c:579
+msgid "could not encode client proof\n"
+msgstr "クライアントの証明のエンコードに失敗しました\n"
+
+#: fe-auth-scram.c:634
+msgid "invalid SCRAM response (nonce mismatch)\n"
+msgstr "不正なSCRAM応答 (nonce の不一致)\n"
+
+#: fe-auth-scram.c:667
+msgid "malformed SCRAM message (invalid salt)\n"
+msgstr "SCRAMメッセージのフォーマット異常 (不正なソルト)\n"
+
+#: fe-auth-scram.c:681
+msgid "malformed SCRAM message (invalid iteration count)\n"
+msgstr "SCRAMメッセージのフォーマット異常 (不正な繰り返し回数)\n"
+
+#: fe-auth-scram.c:687
+msgid "malformed SCRAM message (garbage at end of server-first-message)\n"
+msgstr "SCRAMメッセージのフォーマット異常 (server-first-message 終端の余分なデータ)\n"
+
+#: fe-auth-scram.c:723
+#, c-format
+msgid "error received from server in SCRAM exchange: %s\n"
+msgstr "SCRAM交換中にサーバーからのエラーを受信しました: %s\n"
+
+#: fe-auth-scram.c:739
+msgid "malformed SCRAM message (garbage at end of server-final-message)\n"
+msgstr "SCRAMメッセージのフォーマット異常 (server-final-message 終端の余分なデータ)\n"
+
+#: fe-auth-scram.c:758
+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:391 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:351
+msgid "duplicate SSPI authentication request\n"
+msgstr "重複するSSPI認証要求\n"
+
+#: fe-auth.c:377
+msgid "could not acquire SSPI credentials"
+msgstr "SSPIクレデンシャルを取得できませんでした"
+
+#: fe-auth.c:433
+msgid "channel binding required, but SSL not in use\n"
+msgstr "チャネルバインディングが要求されていますが、SSLが使用されていません\n"
+
+#: fe-auth.c:440
+msgid "duplicate SASL authentication request\n"
+msgstr "重複するSASL認証要求\n"
+
+#: fe-auth.c:496
+msgid "channel binding is required, but client does not support it\n"
+msgstr "チャネルバインディングが要求されていますが、クライアントがサポートしていません\n"
+
+#: fe-auth.c:513
+msgid "server offered SCRAM-SHA-256-PLUS authentication over a non-SSL connection\n"
+msgstr "サーバーが非SSL接続上で SCRAM-SHA-256-PLUS 認証を提示してきました\n"
+
+#: fe-auth.c:525
+msgid "none of the server's SASL authentication mechanisms are supported\n"
+msgstr "サーバー側のいずれのSASL認証機構もサポートされていません\n"
+
+#: fe-auth.c:533
+msgid "channel binding is required, but server did not offer an authentication method that supports channel binding\n"
+msgstr "チャネルバインディングが要求されていますが、サーバーがチャネルバインディングをサポートする認証方式を提供しませんでした\n"
+
+#: fe-auth.c:639
+#, c-format
+msgid "out of memory allocating SASL buffer (%d)\n"
+msgstr "SASLバッファの割り当ての際のメモリ不足(%d)\n"
+
+#: fe-auth.c:664
+msgid "AuthenticationSASLFinal received from server, but SASL authentication was not completed\n"
+msgstr "サーバーからAuthenticationSASLFinalを受信しました、しかしSASL認証は完了していません\n"
+
+#: fe-auth.c:741
+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:877
+msgid "Kerberos 4 authentication not supported\n"
+msgstr "Kerberos 4認証はサポートされていません\n"
+
+#: fe-auth.c:882
+msgid "Kerberos 5 authentication not supported\n"
+msgstr "Kerberos 5認証はサポートされていません\n"
+
+#: fe-auth.c:953
+msgid "GSSAPI authentication not supported\n"
+msgstr "GSSAPI認証はサポートされていません\n"
+
+#: fe-auth.c:985
+msgid "SSPI authentication not supported\n"
+msgstr "SSPI認証はサポートされていません\n"
+
+#: fe-auth.c:993
+msgid "Crypt authentication not supported\n"
+msgstr "Crypt認証はサポートされていません\n"
+
+#: fe-auth.c:1060
+#, c-format
+msgid "authentication method %u not supported\n"
+msgstr "認証方式%uはサポートされていません\n"
+
+#: fe-auth.c:1107
+#, c-format
+msgid "user name lookup failure: error code %lu\n"
+msgstr "ユーザー名の参照に失敗: エラーコード %lu\n"
+
+#: fe-auth.c:1117 fe-connect.c:2856
+#, c-format
+msgid "could not look up local user ID %d: %s\n"
+msgstr "ローカルユーザID%dが見つかりませんでした: %s\n"
+
+#: fe-auth.c:1122 fe-connect.c:2861
+#, c-format
+msgid "local user with ID %d does not exist\n"
+msgstr "ID %d を持つローカルユーザは存在しません\n"
+
+#: fe-auth.c:1226
+msgid "unexpected shape of result set returned for SHOW\n"
+msgstr "SHOW に対する予期しない形のリザルトセット\n"
+
+#: fe-auth.c:1235
+msgid "password_encryption value too long\n"
+msgstr "password_encryptionの値が長すぎます\n"
+
+#: fe-auth.c:1275
+#, c-format
+msgid "unrecognized password encryption algorithm \"%s\"\n"
+msgstr "認識できないパスワード暗号化アルゴリズム \"%s\"\n"
+
+#: fe-connect.c:1094
+#, c-format
+msgid "could not match %d host names to %d hostaddr values\n"
+msgstr "%d個のホスト名と%d個のhostaddrの値との突き合せはできません\n"
+
+#: fe-connect.c:1180
+#, c-format
+msgid "could not match %d port numbers to %d hosts\n"
+msgstr "%d個のポート番号と%d個のホストとの突き合せはできません\n"
+
+#: fe-connect.c:1273 fe-connect.c:1299 fe-connect.c:1341 fe-connect.c:1350
+#: fe-connect.c:1383 fe-connect.c:1427
+#, c-format
+msgid "invalid %s value: \"%s\"\n"
+msgstr "%s の値が不正: \"%s\"\n"
+
+#: fe-connect.c:1320
+#, c-format
+msgid "sslmode value \"%s\" invalid when SSL support is not compiled in\n"
+msgstr "SSLサポートが組み込まれていない場合sslmodeの値\"%s\"は不正です\n"
+
+#: fe-connect.c:1368
+msgid "invalid SSL protocol version range\n"
+msgstr "不正なSSLプロトコルバージョン範囲\n"
+
+#: fe-connect.c:1393
+#, c-format
+msgid "gssencmode value \"%s\" invalid when GSSAPI support is not compiled in\n"
+msgstr "gssencmodeの値\"%s\"はGSSAPIサポートがコンパイルされていない場合は不正\n"
+
+#: fe-connect.c:1653
+#, c-format
+msgid "could not set socket to TCP no delay mode: %s\n"
+msgstr "TCPソケットを非遅延モードに設定できませんでした: %s\n"
+
+#: fe-connect.c:1715
+#, c-format
+msgid "connection to server on socket \"%s\" failed: "
+msgstr "ソケット\"%s\"上でのサーバーへの接続に失敗しました: "
+
+#: fe-connect.c:1742
+#, c-format
+msgid "connection to server at \"%s\" (%s), port %s failed: "
+msgstr "\"%s\" (%s)、ポート %sでのサーバーへの接続に失敗しました: "
+
+#: fe-connect.c:1747
+#, c-format
+msgid "connection to server at \"%s\", port %s failed: "
+msgstr "\"%s\"、ポート%sでのサーバーへの接続に失敗しました: "
+
+#: fe-connect.c:1772
+msgid "\tIs the server running locally and accepting connections on that socket?\n"
+msgstr "\tサーバーは同一マシン上で動作していて、そのソケットで接続の待ち受けをしていますか?\n"
+
+#: fe-connect.c:1776
+msgid "\tIs the server running on that host and accepting TCP/IP connections?\n"
+msgstr "\tサーバーはそのホストで動作していて、TCP/IP接続を受け付けていますか?\n"
+
+#: fe-connect.c:1840
+#, c-format
+msgid "invalid integer value \"%s\" for connection option \"%s\"\n"
+msgstr "接続オプション\"%2$s\"に対する不正な整数値\"%1$s\"\n"
+
+#: fe-connect.c:1870 fe-connect.c:1905 fe-connect.c:1941 fe-connect.c:2030
+#: fe-connect.c:2644
+#, c-format
+msgid "%s(%s) failed: %s\n"
+msgstr "%s(%s)が失敗しました: %s\n"
+
+#: fe-connect.c:1995
+#, c-format
+msgid "%s(%s) failed: error code %d\n"
+msgstr "%s(%s)が失敗しました: エラーコード %d\n"
+
+#: fe-connect.c:2310
+msgid "invalid connection state, probably indicative of memory corruption\n"
+msgstr "接続状態が不正です。メモリ障害の可能性があります\n"
+
+#: fe-connect.c:2389
+#, c-format
+msgid "invalid port number: \"%s\"\n"
+msgstr "不正なポート番号です: \"%s\"\n"
+
+#: fe-connect.c:2405
+#, c-format
+msgid "could not translate host name \"%s\" to address: %s\n"
+msgstr "ホスト名\"%s\"をアドレスに変換できませんでした: %s\n"
+
+#: fe-connect.c:2418
+#, c-format
+msgid "could not parse network address \"%s\": %s\n"
+msgstr "ネットワークアドレス\"%s\"をパースできませんでした: %s\n"
+
+#: fe-connect.c:2431
+#, c-format
+msgid "Unix-domain socket path \"%s\" is too long (maximum %d bytes)\n"
+msgstr "Unixドメインソケットのパス\"%s\"が長すぎます(最大 %d バイト)\n"
+
+#: fe-connect.c:2446
+#, c-format
+msgid "could not translate Unix-domain socket path \"%s\" to address: %s\n"
+msgstr "Unixドメインソケットのパス\"%s\"をアドレスに変換できませんでした: %s\n"
+
+#: fe-connect.c:2572
+#, c-format
+msgid "could not create socket: %s\n"
+msgstr "ソケットを作成できませんでした: %s\n"
+
+#: fe-connect.c:2603
+#, c-format
+msgid "could not set socket to nonblocking mode: %s\n"
+msgstr "ソケットを非ブロッキングモードに設定できませんでした: %s\n"
+
+#: fe-connect.c:2613
+#, c-format
+msgid "could not set socket to close-on-exec mode: %s\n"
+msgstr "ソケットをclose-on-execモードに設定できませんでした: %s\n"
+
+#: fe-connect.c:2631
+msgid "keepalives parameter must be an integer\n"
+msgstr "keepaliveのパラメータは整数でなければなりません\n"
+
+#: fe-connect.c:2772
+#, c-format
+msgid "could not get socket error status: %s\n"
+msgstr "ソケットのエラー状態を入手できませんでした: %s\n"
+
+#: fe-connect.c:2800
+#, c-format
+msgid "could not get client address from socket: %s\n"
+msgstr "ソケットからクライアントアドレスを入手できませんでした: %s\n"
+
+#: fe-connect.c:2842
+msgid "requirepeer parameter is not supported on this platform\n"
+msgstr "このプラットフォームでは requirepeer パラメータはサポートされていません\n"
+
+#: fe-connect.c:2845
+#, c-format
+msgid "could not get peer credentials: %s\n"
+msgstr "ピアの資格証明を入手できませんでした: %s\n"
+
+#: fe-connect.c:2869
+#, c-format
+msgid "requirepeer specifies \"%s\", but actual peer user name is \"%s\"\n"
+msgstr "requirepeerは\"%s\"を指定していますが、実際のピア名は\"%s\"です\n"
+
+#: fe-connect.c:2909
+#, c-format
+msgid "could not send GSSAPI negotiation packet: %s\n"
+msgstr "GSSAPIネゴシエーションパケットを送信できませんでした: %s\n"
+
+#: fe-connect.c:2921
+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:2963
+#, c-format
+msgid "could not send SSL negotiation packet: %s\n"
+msgstr "SSLネゴシエーションパケットを送信できませんでした: %s\n"
+
+#: fe-connect.c:2994
+#, c-format
+msgid "could not send startup packet: %s\n"
+msgstr "開始パケットを送信できませんでした: %s\n"
+
+#: fe-connect.c:3070
+msgid "server does not support SSL, but SSL was required\n"
+msgstr "サーバーはSSLをサポートしていませんが、SSLが要求されました\n"
+
+#: fe-connect.c:3097
+#, c-format
+msgid "received invalid response to SSL negotiation: %c\n"
+msgstr "SSLネゴシエーションに対して不正な応答を受信しました: %c\n"
+
+#: fe-connect.c:3118
+msgid "received unencrypted data after SSL response\n"
+msgstr "SSL応答の後に非暗号化データを受信しました\n"
+
+#: fe-connect.c:3199
+msgid "server doesn't support GSSAPI encryption, but it was required\n"
+msgstr "サーバーはGSSAPI暗号化をサポートしていませんが、要求されました\n"
+
+#: fe-connect.c:3211
+#, c-format
+msgid "received invalid response to GSSAPI negotiation: %c\n"
+msgstr "GSSAPIネゴシエーションに対して不正な応答を受信しました: %c\n"
+
+#: fe-connect.c:3230
+msgid "received unencrypted data after GSSAPI encryption response\n"
+msgstr "GSSAPI暗号化応答の後に非暗号化データを受信しました\n"
+
+#: fe-connect.c:3290 fe-connect.c:3315
+#, c-format
+msgid "expected authentication request from server, but received %c\n"
+msgstr "サーバーからの認証要求を想定していましたが、%cを受信しました\n"
+
+#: fe-connect.c:3522
+msgid "unexpected message from server during startup\n"
+msgstr "起動時にサーバーから想定外のメッセージがありました\n"
+
+#: fe-connect.c:3614
+msgid "session is read-only\n"
+msgstr "セッションは読み取り専用です\n"
+
+#: fe-connect.c:3617
+msgid "session is not read-only\n"
+msgstr "セッションは読み取り専用ではありません\n"
+
+#: fe-connect.c:3671
+msgid "server is in hot standby mode\n"
+msgstr "サーバーはホットスタンバイモードです\n"
+
+#: fe-connect.c:3674
+msgid "server is not in hot standby mode\n"
+msgstr "サーバーはホットスタンバイモードではありません\n"
+
+#: fe-connect.c:3792 fe-connect.c:3844
+#, c-format
+msgid "\"%s\" failed\n"
+msgstr "\"%s\"が失敗しました\n"
+
+#: fe-connect.c:3858
+#, c-format
+msgid "invalid connection state %d, probably indicative of memory corruption\n"
+msgstr "接続状態%dが不正です。メモリ障害の可能性があります\n"
+
+#: fe-connect.c:4304 fe-connect.c:4364
+#, c-format
+msgid "PGEventProc \"%s\" failed during PGEVT_CONNRESET event\n"
+msgstr "PGEVT_CONNRESETイベント中にPGEventProc \"%s\"に失敗しました\n"
+
+#: fe-connect.c:4724
+#, c-format
+msgid "invalid LDAP URL \"%s\": scheme must be ldap://\n"
+msgstr "不正なLDAP URL\"%s\":スキーマはldap://でなければなりません\n"
+
+#: fe-connect.c:4739
+#, c-format
+msgid "invalid LDAP URL \"%s\": missing distinguished name\n"
+msgstr "不正なLDAP URL \"%s\": 区別名がありません\n"
+
+#: fe-connect.c:4751 fe-connect.c:4809
+#, c-format
+msgid "invalid LDAP URL \"%s\": must have exactly one attribute\n"
+msgstr "不正なLDAP URL \"%s\": 正確に1つの属性を持たなければなりません\n"
+
+#: fe-connect.c:4763 fe-connect.c:4825
+#, 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:4775
+#, c-format
+msgid "invalid LDAP URL \"%s\": no filter\n"
+msgstr "不正なLDAP URL \"%s\": フィルタがありません\n"
+
+#: fe-connect.c:4797
+#, c-format
+msgid "invalid LDAP URL \"%s\": invalid port number\n"
+msgstr "不正なLDAP URL \"%s\": ポート番号が不正です\n"
+
+#: fe-connect.c:4835
+msgid "could not create LDAP structure\n"
+msgstr "LDAP構造体を作成できませんでした\n"
+
+#: fe-connect.c:4911
+#, c-format
+msgid "lookup on LDAP server failed: %s\n"
+msgstr "LDAPサーバーで検索に失敗しました: %s\n"
+
+#: fe-connect.c:4922
+msgid "more than one entry found on LDAP lookup\n"
+msgstr "LDAP検索結果が複数ありました\n"
+
+#: fe-connect.c:4923 fe-connect.c:4935
+msgid "no entry found on LDAP lookup\n"
+msgstr "LDAP検索結果が空でした\n"
+
+#: fe-connect.c:4946 fe-connect.c:4959
+msgid "attribute has no values on LDAP lookup\n"
+msgstr "LDAP検索で属性に値がありませんでした\n"
+
+#: fe-connect.c:5011 fe-connect.c:5030 fe-connect.c:5562
+#, c-format
+msgid "missing \"=\" after \"%s\" in connection info string\n"
+msgstr "接続情報文字列において\"%s\"の後に\"=\"がありませんでした\n"
+
+#: fe-connect.c:5103 fe-connect.c:5747 fe-connect.c:6523
+#, c-format
+msgid "invalid connection option \"%s\"\n"
+msgstr "接続オプション\"%s\"は不正です\n"
+
+#: fe-connect.c:5119 fe-connect.c:5611
+msgid "unterminated quoted string in connection info string\n"
+msgstr "接続情報文字列において閉じていない引用符がありました\n"
+
+#: fe-connect.c:5200
+#, c-format
+msgid "definition of service \"%s\" not found\n"
+msgstr "サービス定義\"%s\"がみつかりません\n"
+
+#: fe-connect.c:5226
+#, c-format
+msgid "service file \"%s\" not found\n"
+msgstr "サービスファイル\"%s\"がみつかりません\n"
+
+#: fe-connect.c:5240
+#, c-format
+msgid "line %d too long in service file \"%s\"\n"
+msgstr "サービスファイル\"%2$s\"の行%1$dが長すぎます\n"
+
+#: fe-connect.c:5311 fe-connect.c:5355
+#, c-format
+msgid "syntax error in service file \"%s\", line %d\n"
+msgstr "サービスファイル\"%s\"の行%dに構文エラーがあります\n"
+
+#: fe-connect.c:5322
+#, c-format
+msgid "nested service specifications not supported in service file \"%s\", line %d\n"
+msgstr "サービスファイル\"%s\"ではネストしたサービス指定はサポートされていません、行%d\n"
+
+#: fe-connect.c:6043
+#, c-format
+msgid "invalid URI propagated to internal parser routine: \"%s\"\n"
+msgstr "内部パーサ処理へ伝わった不正なURI: \"%s\"\n"
+
+#: fe-connect.c:6120
+#, 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:6127
+#, c-format
+msgid "IPv6 host address may not be empty in URI: \"%s\"\n"
+msgstr "URI \"%s\"内のIPv6ホストアドレスが空である可能性があります\n"
+
+#: fe-connect.c:6142
+#, 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:6272
+#, c-format
+msgid "extra key/value separator \"=\" in URI query parameter: \"%s\"\n"
+msgstr "URI問い合わせパラメータ内に余分なキーと値を分ける\"=\"があります: \"%s\"\n"
+
+#: fe-connect.c:6292
+#, c-format
+msgid "missing key/value separator \"=\" in URI query parameter: \"%s\"\n"
+msgstr "URI問い合わせパラメータ内にキーと値を分ける\\\"=\\\"がありません: \"%s\"\n"
+
+#: fe-connect.c:6344
+#, c-format
+msgid "invalid URI query parameter: \"%s\"\n"
+msgstr "不正なURI問い合わせパラメータ:\"%s\"\n"
+
+#: fe-connect.c:6418
+#, c-format
+msgid "invalid percent-encoded token: \"%s\"\n"
+msgstr "不正なパーセント符号化トークン: \"%s\"\n"
+
+#: fe-connect.c:6428
+#, c-format
+msgid "forbidden value %%00 in percent-encoded value: \"%s\"\n"
+msgstr "パーセント符号化された値では%%00値は許されません: \"%s\"\n"
+
+#: fe-connect.c:6798
+msgid "connection pointer is NULL\n"
+msgstr "接続ポインタはNULLです\n"
+
+#: fe-connect.c:7086
+#, c-format
+msgid "WARNING: password file \"%s\" is not a plain file\n"
+msgstr "警告: パスワードファイル\"%s\"がテキストファイルではありません\n"
+
+#: fe-connect.c:7095
+#, 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:7203
+#, c-format
+msgid "password retrieved from file \"%s\"\n"
+msgstr "ファイル\"%s\"からパスワードを読み込みました\n"
+
+#: fe-exec.c:449 fe-exec.c:3382
+#, c-format
+msgid "row number %d is out of range 0..%d"
+msgstr "行番号%dは0..%dの範囲を超えています"
+
+#: fe-exec.c:510 fe-protocol3.c:207 fe-protocol3.c:232 fe-protocol3.c:261
+#: fe-protocol3.c:279 fe-protocol3.c:375 fe-protocol3.c:747
+msgid "out of memory"
+msgstr "メモリ不足"
+
+#: fe-exec.c:511 fe-protocol3.c:1943
+#, c-format
+msgid "%s"
+msgstr "%s"
+
+#: fe-exec.c:792
+msgid "write to server failed\n"
+msgstr "サーバーへの書き込みに失敗\n"
+
+#: fe-exec.c:864
+msgid "NOTICE"
+msgstr "NOTICE"
+
+#: fe-exec.c:922
+msgid "PGresult cannot support more than INT_MAX tuples"
+msgstr "PGresultはINT_MAX個以上のタプルを扱えません"
+
+#: fe-exec.c:934
+msgid "size_t overflow"
+msgstr "size_t オーバーフロー"
+
+#: fe-exec.c:1351 fe-exec.c:1476 fe-exec.c:1525
+msgid "command string is a null pointer\n"
+msgstr "コマンド文字列がヌルポインタです\n"
+
+#: fe-exec.c:1482 fe-exec.c:1531 fe-exec.c:1627
+#, c-format
+msgid "number of parameters must be between 0 and %d\n"
+msgstr "パラメータ数は0から%dまでの間でなければなりません\n"
+
+#: fe-exec.c:1519 fe-exec.c:1621
+msgid "statement name is a null pointer\n"
+msgstr "文の名前がヌルポインタです\n"
+
+#: fe-exec.c:1663 fe-exec.c:3235
+msgid "no connection to the server\n"
+msgstr "サーバーへの接続がありません\n"
+
+#: fe-exec.c:1672 fe-exec.c:3244
+msgid "another command is already in progress\n"
+msgstr "他のコマンドを処理しています\n"
+
+#: fe-exec.c:1703
+msgid "cannot queue commands during COPY\n"
+msgstr "COPYの実行中はコマンドの先行積み込みはできません\n"
+
+#: fe-exec.c:1821
+msgid "length must be given for binary parameter\n"
+msgstr "バイナリパラメータには長さを指定しなければなりません\n"
+
+#: fe-exec.c:2148
+#, c-format
+msgid "unexpected asyncStatus: %d\n"
+msgstr "想定外のasyncStatus: %d\n"
+
+#: fe-exec.c:2184
+#, c-format
+msgid "PGEventProc \"%s\" failed during PGEVT_RESULTCREATE event\n"
+msgstr "PGEVT_RESULTCREATEイベント中にPGEventProc \"%s\"に失敗しました\n"
+
+#: fe-exec.c:2332
+msgid "synchronous command execution functions are not allowed in pipeline mode\n"
+msgstr "同期コマンド実行関数はパイプラインモードでは使用できません\n"
+
+#: fe-exec.c:2354
+msgid "COPY terminated by new PQexec"
+msgstr "新たなPQexec\"によりCOPYが終了しました"
+
+#: fe-exec.c:2371
+msgid "PQexec not allowed during COPY BOTH\n"
+msgstr "COPY BOTH 実行中の PQexec は許可されていません\n"
+
+#: fe-exec.c:2599 fe-exec.c:2655 fe-exec.c:2724 fe-protocol3.c:1874
+msgid "no COPY in progress\n"
+msgstr "実行中のCOPYはありません\n"
+
+#: fe-exec.c:2901
+msgid "PQfn not allowed in pipeline mode\n"
+msgstr "PQfnはパイプラインモードでは実行できません\n"
+
+#: fe-exec.c:2909
+msgid "connection in wrong state\n"
+msgstr "接続状態が異常です\n"
+
+#: fe-exec.c:2953
+msgid "cannot enter pipeline mode, connection not idle\n"
+msgstr "パイプラインモードに移行できません、接続はアイドル状態です\n"
+
+#: fe-exec.c:2990 fe-exec.c:3014
+msgid "cannot exit pipeline mode with uncollected results\n"
+msgstr "未受信の結果がある状態でパイプラインモードを終了することはできません\n"
+
+#: fe-exec.c:2995
+msgid "cannot exit pipeline mode while busy\n"
+msgstr "ビジー状態でパイプラインモードを終了することはできません\n"
+
+#: fe-exec.c:3007
+msgid "cannot exit pipeline mode while in COPY\n"
+msgstr "COPY実行中にパイプラインモードを抜けることはできません\n"
+
+#: fe-exec.c:3168
+msgid "cannot send pipeline when not in pipeline mode\n"
+msgstr "パイプラインモード中ではないためパイプラインの送出はできません\n"
+
+#: fe-exec.c:3271
+msgid "invalid ExecStatusType code"
+msgstr "ExecStatusTypeコードが不正です"
+
+#: fe-exec.c:3298
+msgid "PGresult is not an error result\n"
+msgstr "PGresutがエラー結果ではありません\n"
+
+#: fe-exec.c:3366 fe-exec.c:3389
+#, c-format
+msgid "column number %d is out of range 0..%d"
+msgstr "列番号%dは0..%dの範囲を超えています"
+
+#: fe-exec.c:3404
+#, c-format
+msgid "parameter number %d is out of range 0..%d"
+msgstr "パラメータ%dは0..%dの範囲を超えています"
+
+#: fe-exec.c:3714
+#, c-format
+msgid "could not interpret result from server: %s"
+msgstr "サーバーからの結果を解釈できませんでした: %s"
+
+#: fe-exec.c:3974 fe-exec.c:4063
+msgid "incomplete multibyte character\n"
+msgstr "不完全なマルチバイト文字\n"
+
+#: fe-gssapi-common.c:124
+msgid "GSSAPI name import error"
+msgstr "GSSAPI名のインポートエラー"
+
+#: fe-lobj.c:145 fe-lobj.c:210 fe-lobj.c:403 fe-lobj.c:494 fe-lobj.c:568
+#: fe-lobj.c:969 fe-lobj.c:977 fe-lobj.c:985 fe-lobj.c:993 fe-lobj.c:1001
+#: fe-lobj.c:1009 fe-lobj.c:1017 fe-lobj.c:1025
+#, c-format
+msgid "cannot determine OID of function %s\n"
+msgstr "関数%sのOIDを確定できません\n"
+
+#: fe-lobj.c:162
+msgid "argument of lo_truncate exceeds integer range\n"
+msgstr "lo_truncateへの引数が整数範囲を超えています。\n"
+
+#: fe-lobj.c:266
+msgid "argument of lo_read exceeds integer range\n"
+msgstr "lo_readへの引数が整数範囲を超えています。\n"
+
+#: fe-lobj.c:318
+msgid "argument of lo_write exceeds integer range\n"
+msgstr "lo_writeへの引数が整数範囲を超えています。\n"
+
+#: fe-lobj.c:678 fe-lobj.c:789
+#, c-format
+msgid "could not open file \"%s\": %s\n"
+msgstr "ファイル\"%s\"をオープンできませんでした: %s\n"
+
+#: fe-lobj.c:734
+#, c-format
+msgid "could not read from file \"%s\": %s\n"
+msgstr "ファイル\"%s\"を読み込めませんでした: %s\n"
+
+#: fe-lobj.c:810 fe-lobj.c:834
+#, c-format
+msgid "could not write to file \"%s\": %s\n"
+msgstr "ファイル\"%s\"に書き込めませんでした: %s\n"
+
+#: fe-lobj.c:920
+msgid "query to initialize large object functions did not return data\n"
+msgstr "ラージオブジェクト機能を初期化する問い合わせがデータを返しませんでした\n"
+
+#: fe-misc.c:242
+#, c-format
+msgid "integer of size %lu not supported by pqGetInt"
+msgstr "サイズ%luの整数はpqGetIntでサポートされていません"
+
+#: fe-misc.c:275
+#, c-format
+msgid "integer of size %lu not supported by pqPutInt"
+msgstr "サイズ%luの整数はpqPutIntでサポートされていません"
+
+#: fe-misc.c:576 fe-misc.c:822
+msgid "connection not open\n"
+msgstr "接続はオープンされていません\n"
+
+#: fe-misc.c:755 fe-secure-openssl.c:209 fe-secure-openssl.c:316
+#: fe-secure.c:260 fe-secure.c:373
+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:1015
+msgid "timeout expired\n"
+msgstr "タイムアウト期間が過ぎました\n"
+
+#: fe-misc.c:1060
+msgid "invalid socket\n"
+msgstr "不正なソケットです\n"
+
+#: fe-misc.c:1083
+#, c-format
+msgid "%s() failed: %s\n"
+msgstr "%s()が失敗しました: %s\n"
+
+#: fe-protocol3.c:184
+#, c-format
+msgid "message type 0x%02x arrived from server while idle"
+msgstr "待機中にサーバーからメッセージ種類0x%02xが届きました"
+
+#: fe-protocol3.c:407
+msgid "server sent data (\"D\" message) without prior row description (\"T\" message)\n"
+msgstr "サーバーが事前の行記述(\"T\"メッセージ)なしにデータ(\"D\"メッセージ)を送信しました\"\n"
+
+#: fe-protocol3.c:450
+#, c-format
+msgid "unexpected response from server; first received character was \"%c\"\n"
+msgstr "サーバーから想定外の応答がありました。受け付けた先頭文字は\"%c\"です\n"
+
+#: fe-protocol3.c:475
+#, c-format
+msgid "message contents do not agree with length in message type \"%c\"\n"
+msgstr "メッセージの内容がメッセージ種類\"%c\"の長さに合いません\n"
+
+#: fe-protocol3.c:495
+#, c-format
+msgid "lost synchronization with server: got message type \"%c\", length %d\n"
+msgstr "サーバーとの同期が失われました。受信したメッセージ種類は\"%c\"、長さは%d\n"
+
+#: fe-protocol3.c:547 fe-protocol3.c:587
+msgid "insufficient data in \"T\" message"
+msgstr "\"T\"メッセージ内のデータが不十分です"
+
+#: fe-protocol3.c:658 fe-protocol3.c:864
+msgid "out of memory for query result"
+msgstr "問い合わせ結果用のメモリが不足しています"
+
+#: fe-protocol3.c:727
+msgid "insufficient data in \"t\" message"
+msgstr "\"t\"メッセージ内のデータが不十分です"
+
+#: fe-protocol3.c:786 fe-protocol3.c:818 fe-protocol3.c:836
+msgid "insufficient data in \"D\" message"
+msgstr "\"D\"\"メッセージ内のデータが不十分です"
+
+#: fe-protocol3.c:792
+msgid "unexpected field count in \"D\" message"
+msgstr "\"D\"メッセージ内のフィールド数が想定外です。"
+
+#: fe-protocol3.c:1040
+msgid "no error message available\n"
+msgstr "エラーメッセージがありません\n"
+
+#. translator: %s represents a digit string
+#: fe-protocol3.c:1088 fe-protocol3.c:1107
+#, c-format
+msgid " at character %s"
+msgstr "(文字位置: %s)"
+
+#: fe-protocol3.c:1120
+#, c-format
+msgid "DETAIL: %s\n"
+msgstr "DETAIL: %s\n"
+
+#: fe-protocol3.c:1123
+#, c-format
+msgid "HINT: %s\n"
+msgstr "HINT: %s\n"
+
+#: fe-protocol3.c:1126
+#, c-format
+msgid "QUERY: %s\n"
+msgstr "QUERY: %s\n"
+
+#: fe-protocol3.c:1133
+#, c-format
+msgid "CONTEXT: %s\n"
+msgstr "CONTEXT: %s\n"
+
+#: fe-protocol3.c:1142
+#, c-format
+msgid "SCHEMA NAME: %s\n"
+msgstr "SCHEMA NAME: %s\n"
+
+#: fe-protocol3.c:1146
+#, c-format
+msgid "TABLE NAME: %s\n"
+msgstr "TABLE NAME: %s\n"
+
+#: fe-protocol3.c:1150
+#, c-format
+msgid "COLUMN NAME: %s\n"
+msgstr "COLUMN NAME: %s\n"
+
+#: fe-protocol3.c:1154
+#, c-format
+msgid "DATATYPE NAME: %s\n"
+msgstr "DATATYPE NAME: %s\n"
+
+#: fe-protocol3.c:1158
+#, c-format
+msgid "CONSTRAINT NAME: %s\n"
+msgstr "CONSTRAINT NAME: %s\n"
+
+#: fe-protocol3.c:1170
+msgid "LOCATION: "
+msgstr "LOCATION: "
+
+#: fe-protocol3.c:1172
+#, c-format
+msgid "%s, "
+msgstr "%s, "
+
+#: fe-protocol3.c:1174
+#, c-format
+msgid "%s:%s"
+msgstr "%s:%s"
+
+#: fe-protocol3.c:1369
+#, c-format
+msgid "LINE %d: "
+msgstr "行 %d: "
+
+#: fe-protocol3.c:1768
+msgid "PQgetline: not doing text COPY OUT\n"
+msgstr "PQgetline: テキストのCOPY OUTを行っていません\n"
+
+#: fe-protocol3.c:2134
+#, c-format
+msgid "protocol error: id=0x%x\n"
+msgstr "プロトコルエラー: id=0x%x\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: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:1367
+#, 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:1371
+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:1380
+#, 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:1430
+#, 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:822
+#, c-format
+msgid "could not create SSL context: %s\n"
+msgstr "SSLコンテキストを作成できませんでした: %s\n"
+
+#: fe-secure-openssl.c:861
+#, c-format
+msgid "invalid value \"%s\" for minimum SSL protocol version\n"
+msgstr "不正なSSLプロトコル最小バージョンの値\"%s\"\n"
+
+#: fe-secure-openssl.c:872
+#, c-format
+msgid "could not set minimum SSL protocol version: %s\n"
+msgstr "SSLプロトコル最小バージョンを設定できませんでした: %s\n"
+
+#: fe-secure-openssl.c:890
+#, c-format
+msgid "invalid value \"%s\" for maximum SSL protocol version\n"
+msgstr "不正なSSLプロトコル最大バージョンの値\"%s\"\n"
+
+#: fe-secure-openssl.c:901
+#, c-format
+msgid "could not set maximum SSL protocol version: %s\n"
+msgstr "SSLプロトコル最大バージョンを設定できませんでした: %s\n"
+
+#: fe-secure-openssl.c:937
+#, c-format
+msgid "could not read root certificate file \"%s\": %s\n"
+msgstr "ルート証明書\"%s\"を読み取れませんでした: %s\n"
+
+#: fe-secure-openssl.c:990
+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:994
+#, 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:1025
+#, c-format
+msgid "could not open certificate file \"%s\": %s\n"
+msgstr "証明書ファイル\"%s\"をオープンできませんでした: %s\n"
+
+#: fe-secure-openssl.c:1044
+#, c-format
+msgid "could not read certificate file \"%s\": %s\n"
+msgstr "証明書ファイル\"%s\"を読み込めませんでした: %s\n"
+
+#: fe-secure-openssl.c:1069
+#, c-format
+msgid "could not establish SSL connection: %s\n"
+msgstr "SSL接続を確立できませんでした: %s\n"
+
+#: fe-secure-openssl.c:1103
+#, c-format
+msgid "could not set SSL Server Name Indication (SNI): %s\n"
+msgstr "SSLサーバー名表示を設定できませんでした: %s\n"
+
+#: fe-secure-openssl.c:1149
+#, c-format
+msgid "could not load SSL engine \"%s\": %s\n"
+msgstr "SSLエンジン\"%s\"を読み込みできませんでした: %s\n"
+
+#: fe-secure-openssl.c:1161
+#, c-format
+msgid "could not initialize SSL engine \"%s\": %s\n"
+msgstr "SSLエンジン\"%s\"を初期化できませんでした: %s\n"
+
+#: fe-secure-openssl.c:1177
+#, 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:1191
+#, 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:1228
+#, c-format
+msgid "certificate present, but not private key file \"%s\"\n"
+msgstr "証明書はありましたが、秘密鍵ファイル\"%s\"はありませんでした\n"
+
+#: fe-secure-openssl.c:1237
+#, c-format
+msgid "private key file \"%s\" is not a regular file\n"
+msgstr "秘密鍵ファイル\"%s\"は通常のファイルではありません\n"
+
+#: fe-secure-openssl.c:1270
+#, c-format
+msgid "private key file \"%s\" has group or world access; file must have permissions u=rw (0600) or less if owned by the current user, or permissions u=rw,g=r (0640) or less if owned by root\n"
+msgstr "秘密鍵ファイル\"%s\"はグループに対して、もしくは無制限にアクセスを許可しています; ファイルのパーミッションは u=rw (0600) かそれよりも狭い必要があります、rootが所有している場合は u=rw,g=r (0640) かそれよりも狭い必要があります\n"
+
+#: fe-secure-openssl.c:1295
+#, c-format
+msgid "could not load private key file \"%s\": %s\n"
+msgstr "秘密鍵ファイル\"%s\"をロードできませんでした: %s\n"
+
+#: fe-secure-openssl.c:1313
+#, c-format
+msgid "certificate does not match private key file \"%s\": %s\n"
+msgstr "証明書と秘密鍵ファイル\"%s\"が一致しません: %s\n"
+
+#: fe-secure-openssl.c:1413
+#, 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:1449
+#, c-format
+msgid "certificate could not be obtained: %s\n"
+msgstr "証明書を入手できませんでした: %s\n"
+
+#: fe-secure-openssl.c:1555
+#, c-format
+msgid "no SSL error reported"
+msgstr "SSLエラーはありませんでした"
+
+#: fe-secure-openssl.c:1564
+#, c-format
+msgid "SSL error code %lu"
+msgstr "SSLエラーコード: %lu"
+
+#: fe-secure-openssl.c:1812
+#, c-format
+msgid "WARNING: sslpassword truncated\n"
+msgstr "警告: sslpasswordが切り詰められました\n"
+
+#: fe-secure.c:267
+#, c-format
+msgid "could not receive data from server: %s\n"
+msgstr "サーバーからデータを受信できませんでした: %s\n"
+
+#: fe-secure.c:380
+#, 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 "private key file \"%s\" has group or world access; permissions should be u=rw (0600) or less\n"
+#~ msgstr "警告:秘密鍵ファイル\"%s\"がグループメンバや第三者から読める状態になっています。この権限はu=rw (0600)またはそれ以下とすべきです\n"
+
+#~ msgid "private key file \"%s\" must be owned by the current user or root\n"
+#~ msgstr "秘密鍵ファイル\"%s\"は現在のユーザーもしくはrootの所有である必要があります\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..6d265da
--- /dev/null
+++ b/src/interfaces/libpq/po/ru.po
@@ -0,0 +1,1465 @@
+# 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, 2021, 2022.
+# Maxim Yablokov <m.yablokov@postgrespro.ru>, 2021.
+msgid ""
+msgstr ""
+"Project-Id-Version: libpq (PostgreSQL current)\n"
+"Report-Msgid-Bugs-To: pgsql-bugs@lists.postgresql.org\n"
+"POT-Creation-Date: 2022-05-07 06:06+0300\n"
+"PO-Revision-Date: 2022-05-07 06:33+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:213
+msgid "malformed SCRAM message (empty message)\n"
+msgstr "неправильное сообщение SCRAM (пустое содержимое)\n"
+
+#: fe-auth-scram.c:219
+msgid "malformed SCRAM message (length mismatch)\n"
+msgstr "неправильное сообщение SCRAM (некорректная длина)\n"
+
+#: fe-auth-scram.c:263
+msgid "could not verify server signature\n"
+msgstr "не удалось проверить сигнатуру сервера\n"
+
+#: fe-auth-scram.c:270
+msgid "incorrect server signature\n"
+msgstr "некорректная сигнатура сервера\n"
+
+#: fe-auth-scram.c:279
+msgid "invalid SCRAM exchange state\n"
+msgstr "ошибочное состояние обмена SCRAM\n"
+
+#: fe-auth-scram.c:306
+#, c-format
+msgid "malformed SCRAM message (attribute \"%c\" expected)\n"
+msgstr "неправильное сообщение SCRAM (ожидался атрибут \"%c\")\n"
+
+#: fe-auth-scram.c:315
+#, c-format
+msgid ""
+"malformed SCRAM message (expected character \"=\" for attribute \"%c\")\n"
+msgstr ""
+"неправильное сообщение SCRAM (для атрибута \"%c\" ожидался символ \"=\")\n"
+
+#: fe-auth-scram.c:356
+msgid "could not generate nonce\n"
+msgstr "не удалось сгенерировать разовый код\n"
+
+#: fe-auth-scram.c:366 fe-auth-scram.c:441 fe-auth-scram.c:595
+#: fe-auth-scram.c:616 fe-auth-scram.c:642 fe-auth-scram.c:657
+#: fe-auth-scram.c:707 fe-auth-scram.c:746 fe-auth.c:290 fe-auth.c:362
+#: fe-auth.c:398 fe-auth.c:615 fe-auth.c:774 fe-auth.c:1132 fe-auth.c:1282
+#: fe-connect.c:911 fe-connect.c:1460 fe-connect.c:1629 fe-connect.c:2981
+#: fe-connect.c:4711 fe-connect.c:4972 fe-connect.c:5091 fe-connect.c:5343
+#: fe-connect.c:5424 fe-connect.c:5523 fe-connect.c:5779 fe-connect.c:5808
+#: fe-connect.c:5880 fe-connect.c:5904 fe-connect.c:5922 fe-connect.c:6023
+#: fe-connect.c:6032 fe-connect.c:6390 fe-connect.c:6540 fe-connect.c:6806
+#: fe-exec.c:686 fe-exec.c:876 fe-exec.c:1223 fe-exec.c:3047 fe-exec.c:3230
+#: fe-exec.c:4003 fe-exec.c:4168 fe-gssapi-common.c:111 fe-lobj.c:881
+#: fe-protocol3.c:975 fe-protocol3.c:990 fe-protocol3.c:1023
+#: fe-protocol3.c:1731 fe-secure-common.c:110 fe-secure-gssapi.c:504
+#: fe-secure-openssl.c:440 fe-secure-openssl.c:1133
+msgid "out of memory\n"
+msgstr "нехватка памяти\n"
+
+#: fe-auth-scram.c:374
+msgid "could not encode nonce\n"
+msgstr "не удалось оформить разовый код\n"
+
+#: fe-auth-scram.c:563
+msgid "could not calculate client proof\n"
+msgstr "не удалось вычислить подтверждение клиента\n"
+
+#: fe-auth-scram.c:579
+msgid "could not encode client proof\n"
+msgstr "не удалось закодировать подтверждение клиента\n"
+
+#: fe-auth-scram.c:634
+msgid "invalid SCRAM response (nonce mismatch)\n"
+msgstr "неверный ответ SCRAM (несовпадение проверочного кода)\n"
+
+#: fe-auth-scram.c:667
+msgid "malformed SCRAM message (invalid salt)\n"
+msgstr "неправильное сообщение SCRAM (некорректная соль)\n"
+
+#: fe-auth-scram.c:681
+msgid "malformed SCRAM message (invalid iteration count)\n"
+msgstr "неправильное сообщение SCRAM (некорректное число итераций)\n"
+
+#: fe-auth-scram.c:687
+msgid "malformed SCRAM message (garbage at end of server-first-message)\n"
+msgstr ""
+"неправильное сообщение SCRAM (мусор в конце первого сообщения сервера)\n"
+
+#: fe-auth-scram.c:723
+#, c-format
+msgid "error received from server in SCRAM exchange: %s\n"
+msgstr "в ходе обмена SCRAM от сервера получена ошибка: %s\n"
+
+#: fe-auth-scram.c:739
+msgid "malformed SCRAM message (garbage at end of server-final-message)\n"
+msgstr ""
+"неправильное сообщение SCRAM (мусор в конце последнего сообщения сервера)\n"
+
+#: fe-auth-scram.c:758
+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:391 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:351
+msgid "duplicate SSPI authentication request\n"
+msgstr "повторный запрос аутентификации SSPI\n"
+
+#: fe-auth.c:377
+msgid "could not acquire SSPI credentials"
+msgstr "не удалось получить удостоверение SSPI"
+
+#: fe-auth.c:433
+msgid "channel binding required, but SSL not in use\n"
+msgstr "требуется привязка каналов, но SSL не используется\n"
+
+#: fe-auth.c:440
+msgid "duplicate SASL authentication request\n"
+msgstr "повторный запрос аутентификации SASL\n"
+
+#: fe-auth.c:496
+msgid "channel binding is required, but client does not support it\n"
+msgstr "требуется привязка каналов, но клиент её не поддерживает\n"
+
+#: fe-auth.c:513
+msgid ""
+"server offered SCRAM-SHA-256-PLUS authentication over a non-SSL connection\n"
+msgstr ""
+"сервер предложил аутентификацию SCRAM-SHA-256-PLUS для соединения, не "
+"защищённого SSL\n"
+
+#: fe-auth.c:525
+msgid "none of the server's SASL authentication mechanisms are supported\n"
+msgstr ""
+"ни один из серверных механизмов аутентификации SASL не поддерживается\n"
+
+#: fe-auth.c:533
+msgid ""
+"channel binding is required, but server did not offer an authentication "
+"method that supports channel binding\n"
+msgstr ""
+"требуется привязка каналов, но сервер не предложил поддерживающий её метод "
+"аутентификации\n"
+
+#: fe-auth.c:639
+#, c-format
+msgid "out of memory allocating SASL buffer (%d)\n"
+msgstr "недостаточно памяти для буфера SASL (%d)\n"
+
+#: fe-auth.c:664
+msgid ""
+"AuthenticationSASLFinal received from server, but SASL authentication was "
+"not completed\n"
+msgstr ""
+"c сервера получено сообщение AuthenticationSASLFinal, но аутентификация SASL "
+"ещё не завершена\n"
+
+#: fe-auth.c:741
+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:877
+msgid "Kerberos 4 authentication not supported\n"
+msgstr "аутентификация Kerberos 4 не поддерживается\n"
+
+#: fe-auth.c:882
+msgid "Kerberos 5 authentication not supported\n"
+msgstr "аутентификация Kerberos 5 не поддерживается\n"
+
+#: fe-auth.c:953
+msgid "GSSAPI authentication not supported\n"
+msgstr "аутентификация через GSSAPI не поддерживается\n"
+
+#: fe-auth.c:985
+msgid "SSPI authentication not supported\n"
+msgstr "аутентификация через SSPI не поддерживается\n"
+
+#: fe-auth.c:993
+msgid "Crypt authentication not supported\n"
+msgstr "аутентификация Crypt не поддерживается\n"
+
+#: fe-auth.c:1060
+#, c-format
+msgid "authentication method %u not supported\n"
+msgstr "метод аутентификации %u не поддерживается\n"
+
+#: fe-auth.c:1107
+#, c-format
+msgid "user name lookup failure: error code %lu\n"
+msgstr "распознать имя пользователя не удалось (код ошибки: %lu)\n"
+
+#: fe-auth.c:1117 fe-connect.c:2856
+#, c-format
+msgid "could not look up local user ID %d: %s\n"
+msgstr "найти локального пользователя по идентификатору (%d) не удалось: %s\n"
+
+#: fe-auth.c:1122 fe-connect.c:2861
+#, c-format
+msgid "local user with ID %d does not exist\n"
+msgstr "локальный пользователь с ID %d не существует\n"
+
+#: fe-auth.c:1226
+msgid "unexpected shape of result set returned for SHOW\n"
+msgstr "неожиданная форма набора результатов, возвращённого для SHOW\n"
+
+#: fe-auth.c:1235
+msgid "password_encryption value too long\n"
+msgstr "слишком длинное значение password_encryption\n"
+
+#: fe-auth.c:1275
+#, c-format
+msgid "unrecognized password encryption algorithm \"%s\"\n"
+msgstr "нераспознанный алгоритм шифрования пароля \"%s\"\n"
+
+#: fe-connect.c:1094
+#, c-format
+msgid "could not match %d host names to %d hostaddr values\n"
+msgstr "не удалось сопоставить имена узлов (%d) со значениями hostaddr (%d)\n"
+
+#: fe-connect.c:1180
+#, c-format
+msgid "could not match %d port numbers to %d hosts\n"
+msgstr "не удалось сопоставить номера портов (%d) с узлами (%d)\n"
+
+#: fe-connect.c:1273 fe-connect.c:1299 fe-connect.c:1341 fe-connect.c:1350
+#: fe-connect.c:1383 fe-connect.c:1427
+#, c-format
+msgid "invalid %s value: \"%s\"\n"
+msgstr "неверное значение %s: \"%s\"\n"
+
+#: fe-connect.c:1320
+#, c-format
+msgid "sslmode value \"%s\" invalid when SSL support is not compiled in\n"
+msgstr "значение sslmode \"%s\" недопустимо для сборки без поддержки SSL\n"
+
+#: fe-connect.c:1368
+msgid "invalid SSL protocol version range\n"
+msgstr "неверный диапазон версий протокола SSL\n"
+
+#: fe-connect.c:1393
+#, c-format
+msgid ""
+"gssencmode value \"%s\" invalid when GSSAPI support is not compiled in\n"
+msgstr ""
+"значение gssencmode \"%s\" недопустимо для сборки без поддержки GSSAPI\n"
+
+#: fe-connect.c:1653
+#, c-format
+msgid "could not set socket to TCP no delay mode: %s\n"
+msgstr "не удалось перевести сокет в режим TCP-передачи без задержки: %s\n"
+
+#: fe-connect.c:1715
+#, c-format
+msgid "connection to server on socket \"%s\" failed: "
+msgstr "подключиться к серверу через сокет \"%s\" не удалось: "
+
+#: fe-connect.c:1742
+#, c-format
+msgid "connection to server at \"%s\" (%s), port %s failed: "
+msgstr "подключиться к серверу \"%s\" (%s), порту %s не удалось: "
+
+#: fe-connect.c:1747
+#, c-format
+msgid "connection to server at \"%s\", port %s failed: "
+msgstr "подключиться к серверу \"%s\", порту %s не удалось: "
+
+#: fe-connect.c:1772
+msgid ""
+"\tIs the server running locally and accepting connections on that socket?\n"
+msgstr ""
+"\tСервер действительно работает локально и принимает подключения через этот "
+"сокет?\n"
+
+#: fe-connect.c:1776
+msgid ""
+"\tIs the server running on that host and accepting TCP/IP connections?\n"
+msgstr ""
+"\tСервер действительно работает по данному адресу и принимает TCP-"
+"соединения?\n"
+
+#: fe-connect.c:1840
+#, c-format
+msgid "invalid integer value \"%s\" for connection option \"%s\"\n"
+msgstr ""
+"неверное целочисленное значение \"%s\" для параметра соединения \"%s\"\n"
+
+#: fe-connect.c:1870 fe-connect.c:1905 fe-connect.c:1941 fe-connect.c:2030
+#: fe-connect.c:2644
+#, c-format
+msgid "%s(%s) failed: %s\n"
+msgstr "ошибка в %s(%s): %s\n"
+
+#: fe-connect.c:1995
+#, c-format
+msgid "%s(%s) failed: error code %d\n"
+msgstr "ошибка в %s(%s): код ошибки %d\n"
+
+#: fe-connect.c:2310
+msgid "invalid connection state, probably indicative of memory corruption\n"
+msgstr "неверное состояние соединения - возможно разрушение памяти\n"
+
+#: fe-connect.c:2389
+#, c-format
+msgid "invalid port number: \"%s\"\n"
+msgstr "неверный номер порта: \"%s\"\n"
+
+#: fe-connect.c:2405
+#, c-format
+msgid "could not translate host name \"%s\" to address: %s\n"
+msgstr "преобразовать имя \"%s\" в адрес не удалось: %s\n"
+
+#: fe-connect.c:2418
+#, c-format
+msgid "could not parse network address \"%s\": %s\n"
+msgstr "не удалось разобрать сетевой адрес \"%s\": %s\n"
+
+#: fe-connect.c:2431
+#, c-format
+msgid "Unix-domain socket path \"%s\" is too long (maximum %d bytes)\n"
+msgstr "длина пути Unix-сокета \"%s\" превышает предел (%d байт)\n"
+
+#: fe-connect.c:2446
+#, c-format
+msgid "could not translate Unix-domain socket path \"%s\" to address: %s\n"
+msgstr "преобразовать путь Unix-сокета \"%s\" в адрес не удалось: %s\n"
+
+#: fe-connect.c:2572
+#, c-format
+msgid "could not create socket: %s\n"
+msgstr "не удалось создать сокет: %s\n"
+
+#: fe-connect.c:2603
+#, c-format
+msgid "could not set socket to nonblocking mode: %s\n"
+msgstr "не удалось перевести сокет в неблокирующий режим: %s\n"
+
+#: fe-connect.c:2613
+#, c-format
+msgid "could not set socket to close-on-exec mode: %s\n"
+msgstr ""
+"не удалось перевести сокет в режим закрытия при выполнении (close-on-exec): "
+"%s\n"
+
+#: fe-connect.c:2631
+msgid "keepalives parameter must be an integer\n"
+msgstr "параметр keepalives должен быть целым числом\n"
+
+#: fe-connect.c:2772
+#, c-format
+msgid "could not get socket error status: %s\n"
+msgstr "не удалось получить статус ошибки сокета: %s\n"
+
+#: fe-connect.c:2800
+#, c-format
+msgid "could not get client address from socket: %s\n"
+msgstr "не удалось получить адрес клиента из сокета: %s\n"
+
+#: fe-connect.c:2842
+msgid "requirepeer parameter is not supported on this platform\n"
+msgstr "параметр requirepeer не поддерживается в этой ОС\n"
+
+#: fe-connect.c:2845
+#, c-format
+msgid "could not get peer credentials: %s\n"
+msgstr "не удалось получить учётные данные сервера: %s\n"
+
+#: fe-connect.c:2869
+#, c-format
+msgid "requirepeer specifies \"%s\", but actual peer user name is \"%s\"\n"
+msgstr ""
+"requirepeer допускает подключение только к \"%s\", но сервер работает под "
+"именем \"%s\"\n"
+
+#: fe-connect.c:2909
+#, c-format
+msgid "could not send GSSAPI negotiation packet: %s\n"
+msgstr "не удалось отправить пакет согласования GSSAPI: %s\n"
+
+#: fe-connect.c:2921
+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:2963
+#, c-format
+msgid "could not send SSL negotiation packet: %s\n"
+msgstr "не удалось отправить пакет согласования SSL: %s\n"
+
+#: fe-connect.c:2994
+#, c-format
+msgid "could not send startup packet: %s\n"
+msgstr "не удалось отправить стартовый пакет: %s\n"
+
+#: fe-connect.c:3070
+msgid "server does not support SSL, but SSL was required\n"
+msgstr "затребовано подключение через SSL, но сервер не поддерживает SSL\n"
+
+#: fe-connect.c:3097
+#, c-format
+msgid "received invalid response to SSL negotiation: %c\n"
+msgstr "получен неверный ответ при согласовании SSL: %c\n"
+
+#: fe-connect.c:3118
+msgid "received unencrypted data after SSL response\n"
+msgstr "после ответа SSL получены незашифрованные данные\n"
+
+#: fe-connect.c:3199
+msgid "server doesn't support GSSAPI encryption, but it was required\n"
+msgstr "затребовано шифрование GSSAPI, но сервер его не поддерживает\n"
+
+#: fe-connect.c:3211
+#, c-format
+msgid "received invalid response to GSSAPI negotiation: %c\n"
+msgstr "получен неверный ответ при согласовании GSSAPI: %c\n"
+
+#: fe-connect.c:3230
+msgid "received unencrypted data after GSSAPI encryption response\n"
+msgstr ""
+"после ответа на запрос шифрования GSSAPI получены незашифрованные данные\n"
+
+#: fe-connect.c:3290 fe-connect.c:3315
+#, c-format
+msgid "expected authentication request from server, but received %c\n"
+msgstr "ожидался запрос аутентификации от сервера, но получено: %c\n"
+
+#: fe-connect.c:3522
+msgid "unexpected message from server during startup\n"
+msgstr "неожиданное сообщение от сервера в начале работы\n"
+
+#: fe-connect.c:3614
+msgid "session is read-only\n"
+msgstr "сеанс не допускает запись\n"
+
+#: fe-connect.c:3617
+msgid "session is not read-only\n"
+msgstr "сеанс допускает запись\n"
+
+#: fe-connect.c:3671
+msgid "server is in hot standby mode\n"
+msgstr "сервер работает в режиме горячего резерва\n"
+
+#: fe-connect.c:3674
+msgid "server is not in hot standby mode\n"
+msgstr "сервер работает не в режиме горячего резерва\n"
+
+#: fe-connect.c:3792 fe-connect.c:3844
+#, c-format
+msgid "\"%s\" failed\n"
+msgstr "выполнить \"%s\" не удалось\n"
+
+#: fe-connect.c:3858
+#, c-format
+msgid "invalid connection state %d, probably indicative of memory corruption\n"
+msgstr "неверное состояние соединения %d - возможно разрушение памяти\n"
+
+#: fe-connect.c:4304 fe-connect.c:4364
+#, c-format
+msgid "PGEventProc \"%s\" failed during PGEVT_CONNRESET event\n"
+msgstr "ошибка в PGEventProc \"%s\" при обработке события PGEVT_CONNRESET\n"
+
+#: fe-connect.c:4724
+#, c-format
+msgid "invalid LDAP URL \"%s\": scheme must be ldap://\n"
+msgstr "некорректный адрес LDAP \"%s\": схема должна быть ldap://\n"
+
+#: fe-connect.c:4739
+#, c-format
+msgid "invalid LDAP URL \"%s\": missing distinguished name\n"
+msgstr "некорректный адрес LDAP \"%s\": отсутствует уникальное имя\n"
+
+#: fe-connect.c:4751 fe-connect.c:4809
+#, c-format
+msgid "invalid LDAP URL \"%s\": must have exactly one attribute\n"
+msgstr "некорректный адрес LDAP \"%s\": должен быть только один атрибут\n"
+
+#: fe-connect.c:4763 fe-connect.c:4825
+#, 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:4775
+#, c-format
+msgid "invalid LDAP URL \"%s\": no filter\n"
+msgstr "некорректный адрес LDAP \"%s\": нет фильтра\n"
+
+#: fe-connect.c:4797
+#, c-format
+msgid "invalid LDAP URL \"%s\": invalid port number\n"
+msgstr "некорректный адрес LDAP \"%s\": неверный номер порта\n"
+
+#: fe-connect.c:4835
+msgid "could not create LDAP structure\n"
+msgstr "не удалось создать структуру LDAP\n"
+
+#: fe-connect.c:4911
+#, c-format
+msgid "lookup on LDAP server failed: %s\n"
+msgstr "ошибка поиска на сервере LDAP: %s\n"
+
+#: fe-connect.c:4922
+msgid "more than one entry found on LDAP lookup\n"
+msgstr "при поиске LDAP найдено более одного вхождения\n"
+
+#: fe-connect.c:4923 fe-connect.c:4935
+msgid "no entry found on LDAP lookup\n"
+msgstr "при поиске LDAP ничего не найдено\n"
+
+#: fe-connect.c:4946 fe-connect.c:4959
+msgid "attribute has no values on LDAP lookup\n"
+msgstr "атрибут не содержит значений при поиске LDAP\n"
+
+#: fe-connect.c:5011 fe-connect.c:5030 fe-connect.c:5562
+#, c-format
+msgid "missing \"=\" after \"%s\" in connection info string\n"
+msgstr "в строке соединения нет \"=\" после \"%s\"\n"
+
+#: fe-connect.c:5103 fe-connect.c:5747 fe-connect.c:6523
+#, c-format
+msgid "invalid connection option \"%s\"\n"
+msgstr "неверный параметр соединения \"%s\"\n"
+
+#: fe-connect.c:5119 fe-connect.c:5611
+msgid "unterminated quoted string in connection info string\n"
+msgstr "в строке соединения не хватает закрывающей кавычки\n"
+
+#: fe-connect.c:5200
+#, c-format
+msgid "definition of service \"%s\" not found\n"
+msgstr "определение службы \"%s\" не найдено\n"
+
+#: fe-connect.c:5226
+#, c-format
+msgid "service file \"%s\" not found\n"
+msgstr "файл определений служб \"%s\" не найден\n"
+
+#: fe-connect.c:5240
+#, c-format
+msgid "line %d too long in service file \"%s\"\n"
+msgstr "слишком длинная строка (%d) в файле определений служб \"%s\"\n"
+
+#: fe-connect.c:5311 fe-connect.c:5355
+#, c-format
+msgid "syntax error in service file \"%s\", line %d\n"
+msgstr "синтаксическая ошибка в файле определения служб \"%s\" (строка %d)\n"
+
+#: fe-connect.c:5322
+#, c-format
+msgid ""
+"nested service specifications not supported in service file \"%s\", line %d\n"
+msgstr ""
+"рекурсивные определения служб не поддерживаются (файл определения служб \"%s"
+"\", строка %d)\n"
+
+#: fe-connect.c:6043
+#, c-format
+msgid "invalid URI propagated to internal parser routine: \"%s\"\n"
+msgstr "во внутреннюю процедуру разбора строки передан ошибочный URI: \"%s\"\n"
+
+#: fe-connect.c:6120
+#, 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:6127
+#, c-format
+msgid "IPv6 host address may not be empty in URI: \"%s\"\n"
+msgstr "IPv6, содержащийся в URI, не может быть пустым: \"%s\"\n"
+
+#: fe-connect.c:6142
+#, c-format
+msgid ""
+"unexpected character \"%c\" at position %d in URI (expected \":\" or \"/\"): "
+"\"%s\"\n"
+msgstr ""
+"неожиданный символ \"%c\" в позиции %d в URI (ожидалось \":\" или \"/\"): "
+"\"%s\"\n"
+
+#: fe-connect.c:6272
+#, c-format
+msgid "extra key/value separator \"=\" in URI query parameter: \"%s\"\n"
+msgstr "лишний разделитель ключа/значения \"=\" в параметрах URI: \"%s\"\n"
+
+#: fe-connect.c:6292
+#, c-format
+msgid "missing key/value separator \"=\" in URI query parameter: \"%s\"\n"
+msgstr "в параметрах URI не хватает разделителя ключа/значения \"=\": \"%s\"\n"
+
+#: fe-connect.c:6344
+#, c-format
+msgid "invalid URI query parameter: \"%s\"\n"
+msgstr "неверный параметр в URI: \"%s\"\n"
+
+#: fe-connect.c:6418
+#, c-format
+msgid "invalid percent-encoded token: \"%s\"\n"
+msgstr "неверный символ, закодированный с %%: \"%s\"\n"
+
+#: fe-connect.c:6428
+#, c-format
+msgid "forbidden value %%00 in percent-encoded value: \"%s\"\n"
+msgstr "недопустимое значение %%00 для символа, закодированного с %%: \"%s\"\n"
+
+#: fe-connect.c:6798
+msgid "connection pointer is NULL\n"
+msgstr "нулевой указатель соединения\n"
+
+#: fe-connect.c:7086
+#, c-format
+msgid "WARNING: password file \"%s\" is not a plain file\n"
+msgstr "ПРЕДУПРЕЖДЕНИЕ: файл паролей \"%s\" - не обычный файл\n"
+
+#: fe-connect.c:7095
+#, 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:7203
+#, c-format
+msgid "password retrieved from file \"%s\"\n"
+msgstr "пароль получен из файла \"%s\"\n"
+
+#: fe-exec.c:449 fe-exec.c:3304
+#, c-format
+msgid "row number %d is out of range 0..%d"
+msgstr "номер записи %d вне диапазона 0..%d"
+
+#: fe-exec.c:510 fe-protocol3.c:219 fe-protocol3.c:244 fe-protocol3.c:273
+#: fe-protocol3.c:291 fe-protocol3.c:371 fe-protocol3.c:743
+msgid "out of memory"
+msgstr "нехватка памяти"
+
+#: fe-exec.c:511 fe-protocol3.c:1939
+#, c-format
+msgid "%s"
+msgstr "%s"
+
+#: fe-exec.c:792
+msgid "write to server failed\n"
+msgstr "ошибка при передаче данных серверу\n"
+
+#: fe-exec.c:864
+msgid "NOTICE"
+msgstr "ЗАМЕЧАНИЕ"
+
+#: fe-exec.c:922
+msgid "PGresult cannot support more than INT_MAX tuples"
+msgstr "PGresult не может вместить больше чем INT_MAX кортежей"
+
+#: fe-exec.c:934
+msgid "size_t overflow"
+msgstr "переполнение size_t"
+
+#: fe-exec.c:1349 fe-exec.c:1454 fe-exec.c:1503
+msgid "command string is a null pointer\n"
+msgstr "указатель на командную строку нулевой\n"
+
+#: fe-exec.c:1460 fe-exec.c:1509 fe-exec.c:1605
+#, c-format
+msgid "number of parameters must be between 0 and %d\n"
+msgstr "число параметров должно быть от 0 до %d\n"
+
+#: fe-exec.c:1497 fe-exec.c:1599
+msgid "statement name is a null pointer\n"
+msgstr "указатель на имя оператора нулевой\n"
+
+#: fe-exec.c:1641 fe-exec.c:3157
+msgid "no connection to the server\n"
+msgstr "нет соединения с сервером\n"
+
+#: fe-exec.c:1650 fe-exec.c:3166
+msgid "another command is already in progress\n"
+msgstr "уже выполняется другая команда\n"
+
+#: fe-exec.c:1679
+msgid "cannot queue commands during COPY\n"
+msgstr "во время COPY нельзя добавлять команды в очередь\n"
+
+#: fe-exec.c:1797
+msgid "length must be given for binary parameter\n"
+msgstr "для двоичного параметра должна быть указана длина\n"
+
+#: fe-exec.c:2121
+#, c-format
+msgid "unexpected asyncStatus: %d\n"
+msgstr "неожиданный asyncStatus: %d\n"
+
+#: fe-exec.c:2141
+#, c-format
+msgid "PGEventProc \"%s\" failed during PGEVT_RESULTCREATE event\n"
+msgstr "ошибка в PGEventProc \"%s\" при обработке события PGEVT_RESULTCREATE\n"
+
+#: fe-exec.c:2289
+msgid ""
+"synchronous command execution functions are not allowed in pipeline mode\n"
+msgstr ""
+"функции синхронного выполнения команд не допускаются в конвейерном режиме\n"
+
+#: fe-exec.c:2311
+msgid "COPY terminated by new PQexec"
+msgstr "операция COPY прервана вызовом PQexec"
+
+#: fe-exec.c:2328
+msgid "PQexec not allowed during COPY BOTH\n"
+msgstr "вызов PQexec не допускается в процессе COPY BOTH\n"
+
+#: fe-exec.c:2556 fe-exec.c:2612 fe-exec.c:2681 fe-protocol3.c:1870
+msgid "no COPY in progress\n"
+msgstr "операция COPY не выполняется\n"
+
+#: fe-exec.c:2858
+msgid "PQfn not allowed in pipeline mode\n"
+msgstr "PQfn не допускается в конвейерном режиме\n"
+
+#: fe-exec.c:2866
+msgid "connection in wrong state\n"
+msgstr "соединение в неправильном состоянии\n"
+
+#: fe-exec.c:2910
+msgid "cannot enter pipeline mode, connection not idle\n"
+msgstr "перейти в конвейерный режиме нельзя, соединение не простаивает\n"
+
+#: fe-exec.c:2944 fe-exec.c:2961
+msgid "cannot exit pipeline mode with uncollected results\n"
+msgstr "выйти из конвейерного режима нельзя, не собрав все результаты\n"
+
+#: fe-exec.c:2949
+msgid "cannot exit pipeline mode while busy\n"
+msgstr "выйти из конвейерного режима в занятом состоянии нельзя\n"
+
+#: fe-exec.c:3091
+msgid "cannot send pipeline when not in pipeline mode\n"
+msgstr "отправить конвейер, не перейдя в конвейерный режим, нельзя\n"
+
+#: fe-exec.c:3193
+msgid "invalid ExecStatusType code"
+msgstr "неверный код ExecStatusType"
+
+#: fe-exec.c:3220
+msgid "PGresult is not an error result\n"
+msgstr "В PGresult не передан результат ошибки\n"
+
+#: fe-exec.c:3288 fe-exec.c:3311
+#, c-format
+msgid "column number %d is out of range 0..%d"
+msgstr "номер столбца %d вне диапазона 0..%d"
+
+#: fe-exec.c:3326
+#, c-format
+msgid "parameter number %d is out of range 0..%d"
+msgstr "номер параметра %d вне диапазона 0..%d"
+
+#: fe-exec.c:3636
+#, c-format
+msgid "could not interpret result from server: %s"
+msgstr "не удалось интерпретировать ответ сервера: %s"
+
+#: fe-exec.c:3896 fe-exec.c:3985
+msgid "incomplete multibyte character\n"
+msgstr "неполный многобайтный символ\n"
+
+#: fe-gssapi-common.c:124
+msgid "GSSAPI name import error"
+msgstr "ошибка импорта имени в GSSAPI"
+
+#: fe-lobj.c:145 fe-lobj.c:210 fe-lobj.c:403 fe-lobj.c:494 fe-lobj.c:568
+#: fe-lobj.c:969 fe-lobj.c:977 fe-lobj.c:985 fe-lobj.c:993 fe-lobj.c:1001
+#: fe-lobj.c:1009 fe-lobj.c:1017 fe-lobj.c:1025
+#, c-format
+msgid "cannot determine OID of function %s\n"
+msgstr "определить OID функции %s нельзя\n"
+
+#: fe-lobj.c:162
+msgid "argument of lo_truncate exceeds integer range\n"
+msgstr "аргумент lo_truncate не умещается в обычном целом\n"
+
+#: fe-lobj.c:266
+msgid "argument of lo_read exceeds integer range\n"
+msgstr "аргумент lo_read не умещается в обычном целом\n"
+
+#: fe-lobj.c:318
+msgid "argument of lo_write exceeds integer range\n"
+msgstr "аргумент lo_write не умещается в обычном целом\n"
+
+#: fe-lobj.c:678 fe-lobj.c:789
+#, c-format
+msgid "could not open file \"%s\": %s\n"
+msgstr "не удалось открыть файл \"%s\": %s\n"
+
+#: fe-lobj.c:734
+#, c-format
+msgid "could not read from file \"%s\": %s\n"
+msgstr "не удалось прочитать файл \"%s\": %s\n"
+
+#: fe-lobj.c:810 fe-lobj.c:834
+#, c-format
+msgid "could not write to file \"%s\": %s\n"
+msgstr "не удалось записать файл \"%s\": %s\n"
+
+#: fe-lobj.c:920
+msgid "query to initialize large object functions did not return data\n"
+msgstr "запрос инициализации функций для больших объектов не вернул данные\n"
+
+#: fe-misc.c:242
+#, c-format
+msgid "integer of size %lu not supported by pqGetInt"
+msgstr "функция pqGetInt не поддерживает integer размером %lu байт"
+
+#: fe-misc.c:275
+#, c-format
+msgid "integer of size %lu not supported by pqPutInt"
+msgstr "функция pqPutInt не поддерживает integer размером %lu байт"
+
+#: fe-misc.c:576 fe-misc.c:822
+msgid "connection not open\n"
+msgstr "соединение не открыто\n"
+
+#: fe-misc.c:755 fe-secure-openssl.c:209 fe-secure-openssl.c:316
+#: fe-secure.c:260 fe-secure.c:373
+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:1015
+msgid "timeout expired\n"
+msgstr "тайм-аут\n"
+
+#: fe-misc.c:1060
+msgid "invalid socket\n"
+msgstr "неверный сокет\n"
+
+#: fe-misc.c:1083
+#, c-format
+msgid "%s() failed: %s\n"
+msgstr "ошибка в %s(): %s\n"
+
+#: fe-protocol3.c:196
+#, c-format
+msgid "message type 0x%02x arrived from server while idle"
+msgstr "от сервера во время простоя получено сообщение типа 0x%02x"
+
+#: fe-protocol3.c:403
+msgid ""
+"server sent data (\"D\" message) without prior row description (\"T\" "
+"message)\n"
+msgstr ""
+"сервер отправил данные (сообщение \"D\") без предварительного описания "
+"строки (сообщение \"T\")\n"
+
+#: fe-protocol3.c:446
+#, c-format
+msgid "unexpected response from server; first received character was \"%c\"\n"
+msgstr "неожиданный ответ сервера; первый полученный символ: \"%c\"\n"
+
+#: fe-protocol3.c:471
+#, c-format
+msgid "message contents do not agree with length in message type \"%c\"\n"
+msgstr "содержимое не соответствует длине в сообщении типа \"%c\"\n"
+
+#: fe-protocol3.c:491
+#, c-format
+msgid "lost synchronization with server: got message type \"%c\", length %d\n"
+msgstr ""
+"потеряна синхронизация с сервером: получено сообщение типа \"%c\", длина %d\n"
+
+#: fe-protocol3.c:543 fe-protocol3.c:583
+msgid "insufficient data in \"T\" message"
+msgstr "недостаточно данных в сообщении \"T\""
+
+#: fe-protocol3.c:654 fe-protocol3.c:860
+msgid "out of memory for query result"
+msgstr "недостаточно памяти для результата запроса"
+
+#: fe-protocol3.c:723
+msgid "insufficient data in \"t\" message"
+msgstr "недостаточно данных в сообщении \"t\""
+
+#: fe-protocol3.c:782 fe-protocol3.c:814 fe-protocol3.c:832
+msgid "insufficient data in \"D\" message"
+msgstr "недостаточно данных в сообщении \"D\""
+
+#: fe-protocol3.c:788
+msgid "unexpected field count in \"D\" message"
+msgstr "неверное число полей в сообщении \"D\""
+
+#: fe-protocol3.c:1036
+msgid "no error message available\n"
+msgstr "нет сообщения об ошибке\n"
+
+#. translator: %s represents a digit string
+#: fe-protocol3.c:1084 fe-protocol3.c:1103
+#, c-format
+msgid " at character %s"
+msgstr " символ %s"
+
+#: fe-protocol3.c:1116
+#, c-format
+msgid "DETAIL: %s\n"
+msgstr "ПОДРОБНОСТИ: %s\n"
+
+#: fe-protocol3.c:1119
+#, c-format
+msgid "HINT: %s\n"
+msgstr "ПОДСКАЗКА: %s\n"
+
+#: fe-protocol3.c:1122
+#, c-format
+msgid "QUERY: %s\n"
+msgstr "ЗАПРОС: %s\n"
+
+#: fe-protocol3.c:1129
+#, c-format
+msgid "CONTEXT: %s\n"
+msgstr "КОНТЕКСТ: %s\n"
+
+#: fe-protocol3.c:1138
+#, c-format
+msgid "SCHEMA NAME: %s\n"
+msgstr "СХЕМА: %s\n"
+
+#: fe-protocol3.c:1142
+#, c-format
+msgid "TABLE NAME: %s\n"
+msgstr "ТАБЛИЦА: %s\n"
+
+#: fe-protocol3.c:1146
+#, c-format
+msgid "COLUMN NAME: %s\n"
+msgstr "СТОЛБЕЦ: %s\n"
+
+#: fe-protocol3.c:1150
+#, c-format
+msgid "DATATYPE NAME: %s\n"
+msgstr "ТИП ДАННЫХ: %s\n"
+
+#: fe-protocol3.c:1154
+#, c-format
+msgid "CONSTRAINT NAME: %s\n"
+msgstr "ОГРАНИЧЕНИЕ: %s\n"
+
+#: fe-protocol3.c:1166
+msgid "LOCATION: "
+msgstr "ПОЛОЖЕНИЕ: "
+
+#: fe-protocol3.c:1168
+#, c-format
+msgid "%s, "
+msgstr "%s, "
+
+#: fe-protocol3.c:1170
+#, c-format
+msgid "%s:%s"
+msgstr "%s:%s"
+
+#: fe-protocol3.c:1365
+#, c-format
+msgid "LINE %d: "
+msgstr "СТРОКА %d: "
+
+#: fe-protocol3.c:1764
+msgid "PQgetline: not doing text COPY OUT\n"
+msgstr "PQgetline можно вызывать только во время COPY OUT с текстом\n"
+
+#: fe-protocol3.c:2130
+#, c-format
+msgid "protocol error: id=0x%x\n"
+msgstr "ошибка протокола: id=0x%x\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:1367
+#, 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:1371
+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:1380
+#, 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:1430
+#, 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:822
+#, c-format
+msgid "could not create SSL context: %s\n"
+msgstr "не удалось создать контекст SSL: %s\n"
+
+#: fe-secure-openssl.c:861
+#, c-format
+msgid "invalid value \"%s\" for minimum SSL protocol version\n"
+msgstr "неверное значение \"%s\" для минимальной версии протокола SSL\n"
+
+#: fe-secure-openssl.c:872
+#, c-format
+msgid "could not set minimum SSL protocol version: %s\n"
+msgstr "не удалось задать минимальную версию протокола SSL: %s\n"
+
+#: fe-secure-openssl.c:890
+#, c-format
+msgid "invalid value \"%s\" for maximum SSL protocol version\n"
+msgstr "неверное значение \"%s\" для максимальной версии протокола SSL\n"
+
+#: fe-secure-openssl.c:901
+#, c-format
+msgid "could not set maximum SSL protocol version: %s\n"
+msgstr "не удалось задать максимальную версию протокола SSL: %s\n"
+
+#: fe-secure-openssl.c:937
+#, c-format
+msgid "could not read root certificate file \"%s\": %s\n"
+msgstr "не удалось прочитать файл корневых сертификатов \"%s\": %s\n"
+
+#: fe-secure-openssl.c:990
+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:994
+#, 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:1025
+#, c-format
+msgid "could not open certificate file \"%s\": %s\n"
+msgstr "не удалось открыть файл сертификата \"%s\": %s\n"
+
+#: fe-secure-openssl.c:1044
+#, c-format
+msgid "could not read certificate file \"%s\": %s\n"
+msgstr "не удалось прочитать файл сертификата \"%s\": %s\n"
+
+#: fe-secure-openssl.c:1069
+#, c-format
+msgid "could not establish SSL connection: %s\n"
+msgstr "не удалось установить SSL-соединение: %s\n"
+
+#: fe-secure-openssl.c:1103
+#, c-format
+msgid "could not set SSL Server Name Indication (SNI): %s\n"
+msgstr ""
+"не удалось задать SNI (Server Name Indication) для SSL-подключения: %s\n"
+
+#: fe-secure-openssl.c:1149
+#, c-format
+msgid "could not load SSL engine \"%s\": %s\n"
+msgstr "не удалось загрузить модуль SSL ENGINE \"%s\": %s\n"
+
+#: fe-secure-openssl.c:1161
+#, c-format
+msgid "could not initialize SSL engine \"%s\": %s\n"
+msgstr "не удалось инициализировать модуль SSL ENGINE \"%s\": %s\n"
+
+#: fe-secure-openssl.c:1177
+#, 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:1191
+#, 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:1228
+#, c-format
+msgid "certificate present, but not private key file \"%s\"\n"
+msgstr "сертификат присутствует, но файла закрытого ключа \"%s\" нет\n"
+
+#: fe-secure-openssl.c:1237
+#, c-format
+msgid "private key file \"%s\" is not a regular file\n"
+msgstr "файл закрытого ключа \"%s\" - не обычный файл\n"
+
+#: fe-secure-openssl.c:1261
+#, c-format
+msgid "private key file \"%s\" must be owned by the current user or root\n"
+msgstr ""
+"файл закрытого ключа \"%s\" должен принадлежать root или текущему "
+"пользователю\n"
+
+#: fe-secure-openssl.c:1270
+#, c-format
+msgid ""
+"private key file \"%s\" has group or world access; file must have "
+"permissions u=rw (0600) or less if owned by the current user, or permissions "
+"u=rw,g=r (0640) or less if owned by root\n"
+msgstr ""
+"к файлу закрытого ключа \"%s\" имеют доступ все или группа; для него должны "
+"быть заданы разрешения u=rw (0600) или более строгие, если он принадлежит "
+"текущему пользователю, либо u=rw,g=r (0640) или более строгие, если он "
+"принадлежит root\n"
+
+#: fe-secure-openssl.c:1295
+#, c-format
+msgid "could not load private key file \"%s\": %s\n"
+msgstr "не удалось загрузить файл закрытого ключа \"%s\": %s\n"
+
+#: fe-secure-openssl.c:1313
+#, c-format
+msgid "certificate does not match private key file \"%s\": %s\n"
+msgstr "сертификат не соответствует файлу закрытого ключа \"%s\": %s\n"
+
+#: fe-secure-openssl.c:1413
+#, 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:1449
+#, c-format
+msgid "certificate could not be obtained: %s\n"
+msgstr "не удалось получить сертификат: %s\n"
+
+#: fe-secure-openssl.c:1555
+#, c-format
+msgid "no SSL error reported"
+msgstr "нет сообщения об ошибке SSL"
+
+#: fe-secure-openssl.c:1564
+#, c-format
+msgid "SSL error code %lu"
+msgstr "код ошибки SSL: %lu"
+
+#: fe-secure-openssl.c:1812
+#, c-format
+msgid "WARNING: sslpassword truncated\n"
+msgstr "ПРЕДУПРЕЖДЕНИЕ: значение sslpassword усечено\n"
+
+#: fe-secure.c:267
+#, c-format
+msgid "could not receive data from server: %s\n"
+msgstr "не удалось получить данные с сервера: %s\n"
+
+#: fe-secure.c:380
+#, 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 "invalid channel_binding value: \"%s\"\n"
+#~ msgstr "неверное значение channel_binding: \"%s\"\n"
+
+#~ msgid "invalid ssl_min_protocol_version value: \"%s\"\n"
+#~ msgstr "неверное значение ssl_min_protocol_version: \"%s\"\n"
+
+#~ msgid "invalid ssl_max_protocol_version value: \"%s\"\n"
+#~ msgstr "неверное значение ssl_max_protocol_version: \"%s\"\n"
+
+#~ msgid "invalid gssencmode value: \"%s\"\n"
+#~ msgstr "неверное значение gssencmode: \"%s\"\n"
+
+#~ msgid "invalid target_session_attrs value: \"%s\"\n"
+#~ msgstr "неверное значение target_session_attrs: \"%s\"\n"
+
+#~ 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"
+
+#~ msgid "setsockopt(%s) failed: %s\n"
+#~ msgstr "ошибка в setsockopt(%s): %s\n"
+
+#~ msgid "WSAIoctl(SIO_KEEPALIVE_VALS) failed: %ui\n"
+#~ msgstr "ошибка в WSAIoctl(SIO_KEEPALIVE_VALS): %ui\n"
+
+#~ msgid "could not make a writable connection to server \"%s:%s\"\n"
+#~ msgstr ""
+#~ "не удалось установить подключение для чтения/записи к серверу \"%s:%s\"\n"
+
+#~ msgid "test \"SHOW transaction_read_only\" failed on server \"%s:%s\"\n"
+#~ msgstr ""
+#~ "проверка \"SHOW transaction_read_only\" не пройдена на сервере \"%s:%s\"\n"
+
+#~ msgid "function requires at least protocol version 3.0\n"
+#~ msgstr "функция требует протокол минимум версии 3.0\n"
+
+#~ msgid "COPY IN state must be terminated first\n"
+#~ msgstr "сначала должно завершиться состояние COPY IN\n"
+
+#~ msgid "COPY OUT state must be terminated first\n"
+#~ msgstr "сначала должно завершиться состояние COPY OUT\n"
+
+#~ msgid "cannot determine OID of function lo_truncate\n"
+#~ msgstr "не удалось определить OID функции lo_truncate\n"
+
+#~ msgid "cannot determine OID of function lo_truncate64\n"
+#~ msgstr "не удалось определить OID функции lo_truncate64\n"
+
+#~ msgid "cannot determine OID of function lo_lseek64\n"
+#~ msgstr "не удалось определить OID функции lo_lseek64\n"
+
+#~ msgid "cannot determine OID of function lo_create\n"
+#~ msgstr "не удалось определить OID функции lo_create\n"
+
+#~ msgid "cannot determine OID of function lo_tell64\n"
+#~ msgstr "не удалось определить OID функции lo_tell64\n"
+
+#~ msgid "cannot determine OID of function lo_open\n"
+#~ msgstr "не удалось определить OID функции lo_open\n"
+
+#~ msgid "cannot determine OID of function lo_creat\n"
+#~ msgstr "не удалось определить OID функции lo_creat\n"
+
+#~ msgid "cannot determine OID of function lo_unlink\n"
+#~ msgstr "не удалось определить OID функции lo_unlink\n"
+
+#~ msgid "cannot determine OID of function lo_lseek\n"
+#~ msgstr "не удалось определить OID функции lo_lseek\n"
+
+#~ msgid "cannot determine OID of function loread\n"
+#~ msgstr "не удалось определить OID функции loread\n"
+
+#~ msgid "cannot determine OID of function lowrite\n"
+#~ msgstr "не удалось определить OID функции lowrite\n"
+
+#~ msgid "invalid setenv state %c, probably indicative of memory corruption\n"
+#~ msgstr "неверное состояние setenv %c - возможно разрушение памяти\n"
+
+#~ msgid "invalid state %c, probably indicative of memory corruption\n"
+#~ msgstr "неверное состояние %c - возможно разрушение памяти\n"
+
+#~ msgid ""
+#~ "unexpected character %c following empty query response (\"I\" message)"
+#~ msgstr "неожиданный символ %c вслед за пустым ответом (сообщение \"I\")"
+
+#~ msgid ""
+#~ "server sent data (\"D\" message) without prior row description (\"T\" "
+#~ "message)"
+#~ msgstr ""
+#~ "сервер отправил данные (сообщение \"D\") без предварительного описания "
+#~ "строки (сообщение \"T\")"
+
+#~ msgid ""
+#~ "server sent binary data (\"B\" message) without prior row description (\"T"
+#~ "\" message)"
+#~ msgstr ""
+#~ "сервер отправил двоичные данные (сообщение \"B\") без предварительного "
+#~ "описания строки (сообщение \"T\")"
+
+#~ msgid "lost synchronization with server, resetting connection"
+#~ msgstr "потеряна синхронизация с сервером; попытка восстановить соединение"
+
+#~ msgid "extraneous data in \"T\" message"
+#~ msgstr "лишние данные в сообщении \"T\""
+
+#~ msgid "extraneous data in \"t\" message"
+#~ msgstr "лишние данные в сообщении \"t\""
+
+#~ msgid "extraneous data in \"D\" message"
+#~ msgstr "лишние данные в сообщении \"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..8c84415
--- /dev/null
+++ b/src/interfaces/libpq/po/sv.po
@@ -0,0 +1,1225 @@
+# 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, 2021, 2022.
+#
+# Use these quotes: "%s"
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: PostgreSQL 14\n"
+"Report-Msgid-Bugs-To: pgsql-bugs@lists.postgresql.org\n"
+"POT-Creation-Date: 2022-08-04 09:26+0000\n"
+"PO-Revision-Date: 2022-08-04 12:19+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:213
+msgid "malformed SCRAM message (empty message)\n"
+msgstr "felaktigt SCRAM-meddelande (tomt meddelande)\n"
+
+#: fe-auth-scram.c:219
+msgid "malformed SCRAM message (length mismatch)\n"
+msgstr "felaktigt SCRAM-meddelande (längden stämmer inte)\n"
+
+#: fe-auth-scram.c:263
+msgid "could not verify server signature\n"
+msgstr "kunde inte verifiera serversignaturen\n"
+
+#: fe-auth-scram.c:270
+msgid "incorrect server signature\n"
+msgstr "felaktig serversignatur\n"
+
+#: fe-auth-scram.c:279
+msgid "invalid SCRAM exchange state\n"
+msgstr "ogiltig SCRAM-utbytesstatus\n"
+
+#: fe-auth-scram.c:306
+#, c-format
+msgid "malformed SCRAM message (attribute \"%c\" expected)\n"
+msgstr "felaktigt SCRAM-meddelande (förväntade attribut %c)\n"
+
+#: fe-auth-scram.c:315
+#, 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:356
+msgid "could not generate nonce\n"
+msgstr "kunde inte skapa engångsnummer\n"
+
+#: fe-auth-scram.c:366 fe-auth-scram.c:441 fe-auth-scram.c:595
+#: fe-auth-scram.c:616 fe-auth-scram.c:642 fe-auth-scram.c:657
+#: fe-auth-scram.c:707 fe-auth-scram.c:746 fe-auth.c:290 fe-auth.c:362
+#: fe-auth.c:398 fe-auth.c:615 fe-auth.c:774 fe-auth.c:1132 fe-auth.c:1282
+#: fe-connect.c:911 fe-connect.c:1460 fe-connect.c:1629 fe-connect.c:2981
+#: fe-connect.c:4711 fe-connect.c:4972 fe-connect.c:5091 fe-connect.c:5343
+#: fe-connect.c:5424 fe-connect.c:5523 fe-connect.c:5779 fe-connect.c:5808
+#: fe-connect.c:5880 fe-connect.c:5904 fe-connect.c:5922 fe-connect.c:6023
+#: fe-connect.c:6032 fe-connect.c:6390 fe-connect.c:6540 fe-connect.c:6806
+#: fe-exec.c:686 fe-exec.c:876 fe-exec.c:1223 fe-exec.c:3125 fe-exec.c:3309
+#: fe-exec.c:4082 fe-exec.c:4247 fe-gssapi-common.c:111 fe-lobj.c:881
+#: fe-protocol3.c:979 fe-protocol3.c:994 fe-protocol3.c:1027
+#: fe-protocol3.c:1735 fe-secure-common.c:110 fe-secure-gssapi.c:504
+#: fe-secure-openssl.c:440 fe-secure-openssl.c:1133
+msgid "out of memory\n"
+msgstr "slut på minne\n"
+
+#: fe-auth-scram.c:374
+msgid "could not encode nonce\n"
+msgstr "kunde inte koda engångsnummer\n"
+
+#: fe-auth-scram.c:563
+msgid "could not calculate client proof\n"
+msgstr "kunde inte räkna ut klientbevis\n"
+
+#: fe-auth-scram.c:579
+msgid "could not encode client proof\n"
+msgstr "kunde inte koda klientbevis\n"
+
+#: fe-auth-scram.c:634
+msgid "invalid SCRAM response (nonce mismatch)\n"
+msgstr "ogiltigt SCRAM-svar (engångsnummer matchar inte)\n"
+
+#: fe-auth-scram.c:667
+msgid "malformed SCRAM message (invalid salt)\n"
+msgstr "felaktigt SCRAM-meddelande (ogiltigt salt)\n"
+
+#: fe-auth-scram.c:681
+msgid "malformed SCRAM message (invalid iteration count)\n"
+msgstr "felaktigt SCRAM-meddelande (ogiltig iterationsräknare)\n"
+
+#: fe-auth-scram.c:687
+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:723
+#, 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:739
+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:758
+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:391 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:351
+msgid "duplicate SSPI authentication request\n"
+msgstr "duplicerad autentiseringsbegäran från SSPI\n"
+
+#: fe-auth.c:377
+msgid "could not acquire SSPI credentials"
+msgstr "kunde inte hämta SSPI-referenser"
+
+#: fe-auth.c:433
+msgid "channel binding required, but SSL not in use\n"
+msgstr "kräver kanalbindning, men SSL används inte\n"
+
+#: fe-auth.c:440
+msgid "duplicate SASL authentication request\n"
+msgstr "duplicerad autentiseringsbegäran från SASL\n"
+
+#: fe-auth.c:496
+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:513
+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:525
+msgid "none of the server's SASL authentication mechanisms are supported\n"
+msgstr "ingen av serverns SASL-autentiseringsmekanismer stöds\n"
+
+#: fe-auth.c:533
+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:639
+#, 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:664
+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:741
+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:877
+msgid "Kerberos 4 authentication not supported\n"
+msgstr "Kerberos-4-autentisering stöds ej\n"
+
+#: fe-auth.c:882
+msgid "Kerberos 5 authentication not supported\n"
+msgstr "Kerberos-5-autentisering stöds ej\n"
+
+#: fe-auth.c:953
+msgid "GSSAPI authentication not supported\n"
+msgstr "GSSAPI-autentisering stöds ej\n"
+
+#: fe-auth.c:985
+msgid "SSPI authentication not supported\n"
+msgstr "SSPI-autentisering stöds ej\n"
+
+#: fe-auth.c:993
+msgid "Crypt authentication not supported\n"
+msgstr "Crypt-autentisering stöds ej\n"
+
+#: fe-auth.c:1060
+#, c-format
+msgid "authentication method %u not supported\n"
+msgstr "autentiseringsmetod %u stöds ej\n"
+
+#: fe-auth.c:1107
+#, c-format
+msgid "user name lookup failure: error code %lu\n"
+msgstr "misslyckad sökning efter användarnamn: felkod %lu\n"
+
+#: fe-auth.c:1117 fe-connect.c:2856
+#, 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:1122 fe-connect.c:2861
+#, 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:1226
+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:1235
+msgid "password_encryption value too long\n"
+msgstr "password_encryption-värdet är för långt\n"
+
+#: fe-auth.c:1275
+#, c-format
+msgid "unrecognized password encryption algorithm \"%s\"\n"
+msgstr "okänd lösenordskrypteringsalgoritm \"%s\"\n"
+
+#: fe-connect.c:1094
+#, 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:1180
+#, 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:1273 fe-connect.c:1299 fe-connect.c:1341 fe-connect.c:1350
+#: fe-connect.c:1383 fe-connect.c:1427
+#, c-format
+msgid "invalid %s value: \"%s\"\n"
+msgstr "ogiltigt %s-värde: \"%s\"\n"
+
+#: fe-connect.c:1320
+#, 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:1368
+msgid "invalid SSL protocol version range\n"
+msgstr "ogiltigt intervall för SSL-protokollversion\n"
+
+#: fe-connect.c:1393
+#, 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:1653
+#, 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:1715
+#, c-format
+msgid "connection to server on socket \"%s\" failed: "
+msgstr "anslutning till server på uttag \"%s\" misslyckades: "
+
+#: fe-connect.c:1742
+#, c-format
+msgid "connection to server at \"%s\" (%s), port %s failed: "
+msgstr "anslutning til server på \"%s\" (%s), port %s misslyckades: "
+
+#: fe-connect.c:1747
+#, c-format
+msgid "connection to server at \"%s\", port %s failed: "
+msgstr "anslutning till server på \"%s\", port %s misslyckades: "
+
+#: fe-connect.c:1772
+msgid "\tIs the server running locally and accepting connections on that socket?\n"
+msgstr ""
+"\tKör servern lokalt och accepterar den\n"
+"\tanslutningar till detta uttag (socket)?\n"
+
+#: fe-connect.c:1776
+msgid "\tIs the server running on that host and accepting TCP/IP connections?\n"
+msgstr "\tKör servern på den värden och accepterar den TCP/IP-anslutningar?\n"
+
+#: fe-connect.c:1840
+#, 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:1870 fe-connect.c:1905 fe-connect.c:1941 fe-connect.c:2030
+#: fe-connect.c:2644
+#, c-format
+msgid "%s(%s) failed: %s\n"
+msgstr "%s(%s) misslyckades: %s\n"
+
+#: fe-connect.c:1995
+#, c-format
+msgid "%s(%s) failed: error code %d\n"
+msgstr "%s(%s) misslyckades: felkod %d\n"
+
+#: fe-connect.c:2310
+msgid "invalid connection state, probably indicative of memory corruption\n"
+msgstr "ogiltigt tillstånd i anslutning, antagligen korrupt minne\n"
+
+#: fe-connect.c:2389
+#, c-format
+msgid "invalid port number: \"%s\"\n"
+msgstr "ogiltigt portnummer \"%s\"\n"
+
+#: fe-connect.c:2405
+#, 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:2418
+#, c-format
+msgid "could not parse network address \"%s\": %s\n"
+msgstr "kunde inte parsa nätverksadress \"%s\": %s\n"
+
+#: fe-connect.c:2431
+#, 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:2446
+#, 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:2572
+#, c-format
+msgid "could not create socket: %s\n"
+msgstr "kan inte skapa uttag: %s\n"
+
+#: fe-connect.c:2603
+#, 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:2613
+#, 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:2631
+msgid "keepalives parameter must be an integer\n"
+msgstr "keepalives-parameter måste vara ett heltal\n"
+
+#: fe-connect.c:2772
+#, 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:2800
+#, 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:2842
+msgid "requirepeer parameter is not supported on this platform\n"
+msgstr "requirepeer-parameter stöds inte på denna plattform\n"
+
+#: fe-connect.c:2845
+#, c-format
+msgid "could not get peer credentials: %s\n"
+msgstr "kunde inte hämta andra sidans referenser: %s\n"
+
+#: fe-connect.c:2869
+#, 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:2909
+#, 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:2921
+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:2963
+#, 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:2994
+#, c-format
+msgid "could not send startup packet: %s\n"
+msgstr "kan inte skicka startpaketet: %s\n"
+
+#: fe-connect.c:3070
+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:3097
+#, 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:3118
+msgid "received unencrypted data after SSL response\n"
+msgstr "tog emot okrypterad data efter SSL-svar\n"
+
+#: fe-connect.c:3199
+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:3211
+#, 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:3230
+msgid "received unencrypted data after GSSAPI encryption response\n"
+msgstr "tog emot okrypterad data efter GSSAPI-krypteringssvar\n"
+
+#: fe-connect.c:3290 fe-connect.c:3315
+#, 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:3522
+msgid "unexpected message from server during startup\n"
+msgstr "oväntat meddelande från servern under starten\n"
+
+#: fe-connect.c:3614
+msgid "session is read-only\n"
+msgstr "sessionen är i readonly-läge\n"
+
+#: fe-connect.c:3617
+msgid "session is not read-only\n"
+msgstr "sessionen är inte i readonly-läge\n"
+
+#: fe-connect.c:3671
+msgid "server is in hot standby mode\n"
+msgstr "servern är i varmt standby-läge\n"
+
+#: fe-connect.c:3674
+msgid "server is not in hot standby mode\n"
+msgstr "servern är inte i varmt standby-läge\n"
+
+#: fe-connect.c:3792 fe-connect.c:3844
+#, c-format
+msgid "\"%s\" failed\n"
+msgstr "\"%s\" misslyckades\n"
+
+#: fe-connect.c:3858
+#, c-format
+msgid "invalid connection state %d, probably indicative of memory corruption\n"
+msgstr "ogiltigt tillstånd %d i anslutning, antagligen korrupt minne\n"
+
+#: fe-connect.c:4304 fe-connect.c:4364
+#, c-format
+msgid "PGEventProc \"%s\" failed during PGEVT_CONNRESET event\n"
+msgstr "PGEventProc \"%s\" misslyckades under PGEVT_CONNRESET-händelse\n"
+
+#: fe-connect.c:4724
+#, 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:4739
+#, c-format
+msgid "invalid LDAP URL \"%s\": missing distinguished name\n"
+msgstr "ogiltig LDAP URL \"%s\": saknar urskiljbart namn\n"
+
+#: fe-connect.c:4751 fe-connect.c:4809
+#, 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:4763 fe-connect.c:4825
+#, 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:4775
+#, c-format
+msgid "invalid LDAP URL \"%s\": no filter\n"
+msgstr "ogiltigt LDAP URL \"%s\": inget filter\n"
+
+#: fe-connect.c:4797
+#, c-format
+msgid "invalid LDAP URL \"%s\": invalid port number\n"
+msgstr "ogiltig LDAP URL \"%s\": ogiltigt portnummer\n"
+
+#: fe-connect.c:4835
+msgid "could not create LDAP structure\n"
+msgstr "kunde inte skapa LDAP-struktur\n"
+
+#: fe-connect.c:4911
+#, c-format
+msgid "lookup on LDAP server failed: %s\n"
+msgstr "uppslagning av LDAP-server misslyckades: %s\n"
+
+#: fe-connect.c:4922
+msgid "more than one entry found on LDAP lookup\n"
+msgstr "mer än en post hittad i LDAP-uppslagning\n"
+
+#: fe-connect.c:4923 fe-connect.c:4935
+msgid "no entry found on LDAP lookup\n"
+msgstr "ingen post hittad i LDAP-uppslagning\n"
+
+#: fe-connect.c:4946 fe-connect.c:4959
+msgid "attribute has no values on LDAP lookup\n"
+msgstr "attributet har inga värden i LDAP-uppslagning\n"
+
+#: fe-connect.c:5011 fe-connect.c:5030 fe-connect.c:5562
+#, c-format
+msgid "missing \"=\" after \"%s\" in connection info string\n"
+msgstr "\"=\" efter \"%s\" saknas i anslutningssträng\n"
+
+#: fe-connect.c:5103 fe-connect.c:5747 fe-connect.c:6523
+#, c-format
+msgid "invalid connection option \"%s\"\n"
+msgstr "ogiltig anslutningsparameter \"%s\"\n"
+
+#: fe-connect.c:5119 fe-connect.c:5611
+msgid "unterminated quoted string in connection info string\n"
+msgstr "icke terminerad sträng i uppkopplingsinformationen\n"
+
+#: fe-connect.c:5200
+#, c-format
+msgid "definition of service \"%s\" not found\n"
+msgstr "definition av service \"%s\" hittades inte\n"
+
+#: fe-connect.c:5226
+#, c-format
+msgid "service file \"%s\" not found\n"
+msgstr "servicefil \"%s\" hittades inte\n"
+
+#: fe-connect.c:5240
+#, 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:5311 fe-connect.c:5355
+#, c-format
+msgid "syntax error in service file \"%s\", line %d\n"
+msgstr "syntaxfel i servicefel \"%s\", rad %d\n"
+
+#: fe-connect.c:5322
+#, 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:6043
+#, c-format
+msgid "invalid URI propagated to internal parser routine: \"%s\"\n"
+msgstr "ogiltig URI propagerad till intern parsningsrutin: \"%s\"\n"
+
+#: fe-connect.c:6120
+#, 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:6127
+#, 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:6142
+#, 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:6272
+#, 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:6292
+#, 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:6344
+#, c-format
+msgid "invalid URI query parameter: \"%s\"\n"
+msgstr "ogiltig URI-frågeparameter: \"%s\"\n"
+
+#: fe-connect.c:6418
+#, c-format
+msgid "invalid percent-encoded token: \"%s\"\n"
+msgstr "ogiltigt procent-kodad symbol: \"%s\"\n"
+
+#: fe-connect.c:6428
+#, 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:6798
+msgid "connection pointer is NULL\n"
+msgstr "anslutningspekare är NULL\n"
+
+#: fe-connect.c:7086
+#, 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:7095
+#, 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:7203
+#, c-format
+msgid "password retrieved from file \"%s\"\n"
+msgstr "lösenord hämtat från fil \"%s\"\n"
+
+#: fe-exec.c:449 fe-exec.c:3383
+#, 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:510 fe-protocol3.c:207 fe-protocol3.c:232 fe-protocol3.c:261
+#: fe-protocol3.c:279 fe-protocol3.c:375 fe-protocol3.c:747
+msgid "out of memory"
+msgstr "slut på minne"
+
+#: fe-exec.c:511 fe-protocol3.c:1943
+#, c-format
+msgid "%s"
+msgstr "%s"
+
+#: fe-exec.c:792
+msgid "write to server failed\n"
+msgstr "skrivning till servern misslyckades\n"
+
+#: fe-exec.c:864
+msgid "NOTICE"
+msgstr "NOTIS"
+
+#: fe-exec.c:922
+msgid "PGresult cannot support more than INT_MAX tuples"
+msgstr "PGresult stöder inte mer än INT_MAX tupler"
+
+#: fe-exec.c:934
+msgid "size_t overflow"
+msgstr "size_t-överspill"
+
+#: fe-exec.c:1351 fe-exec.c:1477 fe-exec.c:1526
+msgid "command string is a null pointer\n"
+msgstr "kommandosträngen är en null-pekare\n"
+
+#: fe-exec.c:1483 fe-exec.c:1532 fe-exec.c:1628
+#, c-format
+msgid "number of parameters must be between 0 and %d\n"
+msgstr "antal parametrar måste vara mellan 0 och %d\n"
+
+#: fe-exec.c:1520 fe-exec.c:1622
+msgid "statement name is a null pointer\n"
+msgstr "satsens namn är en null-pekare\n"
+
+#: fe-exec.c:1664 fe-exec.c:3236
+msgid "no connection to the server\n"
+msgstr "inte förbunden till servern\n"
+
+#: fe-exec.c:1673 fe-exec.c:3245
+msgid "another command is already in progress\n"
+msgstr "ett annat kommando pågår redan\n"
+
+#: fe-exec.c:1704
+msgid "cannot queue commands during COPY\n"
+msgstr "kan inte köa kommandon när COPY körs\n"
+
+#: fe-exec.c:1822
+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:2149
+#, c-format
+msgid "unexpected asyncStatus: %d\n"
+msgstr "oväntad asyncStatus: %d\n"
+
+#: fe-exec.c:2185
+#, c-format
+msgid "PGEventProc \"%s\" failed during PGEVT_RESULTCREATE event\n"
+msgstr "PGEventProc \"%s\" misslyckades under PGEVT_RESULTCREATE-händelse\n"
+
+#: fe-exec.c:2333
+msgid "synchronous command execution functions are not allowed in pipeline mode\n"
+msgstr "synkrona kommandoexekveringsfunktioner tillåts inte i pipeline-läge\n"
+
+#: fe-exec.c:2355
+msgid "COPY terminated by new PQexec"
+msgstr "COPY terminerad av ny PQexec"
+
+#: fe-exec.c:2372
+msgid "PQexec not allowed during COPY BOTH\n"
+msgstr "PQexec tillåts inte under COPY BOTH\n"
+
+#: fe-exec.c:2600 fe-exec.c:2656 fe-exec.c:2725 fe-protocol3.c:1874
+msgid "no COPY in progress\n"
+msgstr "ingen COPY pågår\n"
+
+#: fe-exec.c:2902
+msgid "PQfn not allowed in pipeline mode\n"
+msgstr "PQfn tillåts inte i pipeline-läge\n"
+
+#: fe-exec.c:2910
+msgid "connection in wrong state\n"
+msgstr "anslutning i felaktigt tillstånd\n"
+
+#: fe-exec.c:2954
+msgid "cannot enter pipeline mode, connection not idle\n"
+msgstr "kan inte byta till pipeline-läge, anslutningen är inte inaktiv\n"
+
+#: fe-exec.c:2991 fe-exec.c:3015
+msgid "cannot exit pipeline mode with uncollected results\n"
+msgstr "kan inte anvsluta pipeline-läge när alla svar inte tagits emot\n"
+
+#: fe-exec.c:2996
+msgid "cannot exit pipeline mode while busy\n"
+msgstr "är upptagen och kan inte avsluta pipeline-läge\n"
+
+#: fe-exec.c:3008
+msgid "cannot exit pipeline mode while in COPY\n"
+msgstr "kan inte avsluta pipeline-läge inne i en COPY\n"
+
+#: fe-exec.c:3169
+msgid "cannot send pipeline when not in pipeline mode\n"
+msgstr "kan inte skicka en pipeline när vi inte är i pipeline-läge\n"
+
+#: fe-exec.c:3272
+msgid "invalid ExecStatusType code"
+msgstr "ogiltig ExecStatusType-kod"
+
+#: fe-exec.c:3299
+msgid "PGresult is not an error result\n"
+msgstr "PGresult är inte ett felresultat\n"
+
+#: fe-exec.c:3367 fe-exec.c:3390
+#, 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:3405
+#, 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:3715
+#, c-format
+msgid "could not interpret result from server: %s"
+msgstr "kunde inte tolka svaret från servern: %s"
+
+#: fe-exec.c:3975 fe-exec.c:4064
+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:145 fe-lobj.c:210 fe-lobj.c:403 fe-lobj.c:494 fe-lobj.c:568
+#: fe-lobj.c:969 fe-lobj.c:977 fe-lobj.c:985 fe-lobj.c:993 fe-lobj.c:1001
+#: fe-lobj.c:1009 fe-lobj.c:1017 fe-lobj.c:1025
+#, c-format
+msgid "cannot determine OID of function %s\n"
+msgstr "kan inte ta reda på OID för funktionen %s\n"
+
+#: fe-lobj.c:162
+msgid "argument of lo_truncate exceeds integer range\n"
+msgstr "argumentet till lo_truncate överskrider heltalsintervallet\n"
+
+#: fe-lobj.c:266
+msgid "argument of lo_read exceeds integer range\n"
+msgstr "ett argument till lo_read överskriver heltalsintervallet\n"
+
+#: fe-lobj.c:318
+msgid "argument of lo_write exceeds integer range\n"
+msgstr "ett argument till lo_write överskriver heltalsintervallet\n"
+
+#: fe-lobj.c:678 fe-lobj.c:789
+#, c-format
+msgid "could not open file \"%s\": %s\n"
+msgstr "kan inte öppna fil \"%s\": %s\n"
+
+#: fe-lobj.c:734
+#, 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:810 fe-lobj.c:834
+#, c-format
+msgid "could not write to file \"%s\": %s\n"
+msgstr "kan inte skriva till fil \"%s\": %s\n"
+
+#: fe-lobj.c:920
+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-misc.c:242
+#, c-format
+msgid "integer of size %lu not supported by pqGetInt"
+msgstr "heltal med storlek %lu stöds inte av pqGetInt"
+
+#: fe-misc.c:275
+#, c-format
+msgid "integer of size %lu not supported by pqPutInt"
+msgstr "heltal med storlek %lu stöds inte av pqPutInt"
+
+#: fe-misc.c:576 fe-misc.c:822
+msgid "connection not open\n"
+msgstr "anslutningen är inte öppen\n"
+
+#: fe-misc.c:755 fe-secure-openssl.c:209 fe-secure-openssl.c:316
+#: fe-secure.c:260 fe-secure.c:373
+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:1015
+msgid "timeout expired\n"
+msgstr "timeout utgången\n"
+
+#: fe-misc.c:1060
+msgid "invalid socket\n"
+msgstr "ogiltigt uttag\n"
+
+#: fe-misc.c:1083
+#, c-format
+msgid "%s() failed: %s\n"
+msgstr "%s() misslyckades: %s\n"
+
+#: fe-protocol3.c:184
+#, c-format
+msgid "message type 0x%02x arrived from server while idle"
+msgstr "meddelandetyp 0x%02x kom från server under viloperiod"
+
+#: fe-protocol3.c:407
+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:450
+#, 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-protocol3.c:475
+#, 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:495
+#, 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:547 fe-protocol3.c:587
+msgid "insufficient data in \"T\" message"
+msgstr "otillräckligt med data i \"T\"-meddelande"
+
+#: fe-protocol3.c:658 fe-protocol3.c:864
+msgid "out of memory for query result"
+msgstr "slut på minnet för frågeresultat"
+
+#: fe-protocol3.c:727
+msgid "insufficient data in \"t\" message"
+msgstr "otillräckligt med data i \"t\"-meddelande"
+
+#: fe-protocol3.c:786 fe-protocol3.c:818 fe-protocol3.c:836
+msgid "insufficient data in \"D\" message"
+msgstr "otillräckligt med data i \"D\"-meddelande"
+
+#: fe-protocol3.c:792
+msgid "unexpected field count in \"D\" message"
+msgstr "oväntat fältantal i \"D\"-meddelande"
+
+#: fe-protocol3.c:1040
+msgid "no error message available\n"
+msgstr "inget felmeddelande finns tillgängligt\n"
+
+#. translator: %s represents a digit string
+#: fe-protocol3.c:1088 fe-protocol3.c:1107
+#, c-format
+msgid " at character %s"
+msgstr " vid tecken %s"
+
+#: fe-protocol3.c:1120
+#, c-format
+msgid "DETAIL: %s\n"
+msgstr "DETALJ: %s\n"
+
+#: fe-protocol3.c:1123
+#, c-format
+msgid "HINT: %s\n"
+msgstr "TIPS: %s\n"
+
+#: fe-protocol3.c:1126
+#, c-format
+msgid "QUERY: %s\n"
+msgstr "FRÅGA: %s\n"
+
+#: fe-protocol3.c:1133
+#, c-format
+msgid "CONTEXT: %s\n"
+msgstr "KONTEXT: %s\n"
+
+#: fe-protocol3.c:1142
+#, c-format
+msgid "SCHEMA NAME: %s\n"
+msgstr "SCHEMANAMN: %s\n"
+
+#: fe-protocol3.c:1146
+#, c-format
+msgid "TABLE NAME: %s\n"
+msgstr "TABELLNAMN: %s\n"
+
+#: fe-protocol3.c:1150
+#, c-format
+msgid "COLUMN NAME: %s\n"
+msgstr "KOLUMNNAMN: %s\n"
+
+#: fe-protocol3.c:1154
+#, c-format
+msgid "DATATYPE NAME: %s\n"
+msgstr "DATATYPNAMN: %s\n"
+
+#: fe-protocol3.c:1158
+#, c-format
+msgid "CONSTRAINT NAME: %s\n"
+msgstr "VILLKORSNAMN: %s\n"
+
+#: fe-protocol3.c:1170
+msgid "LOCATION: "
+msgstr "PLATS: "
+
+#: fe-protocol3.c:1172
+#, c-format
+msgid "%s, "
+msgstr "%s, "
+
+#: fe-protocol3.c:1174
+#, c-format
+msgid "%s:%s"
+msgstr "%s:%s"
+
+#: fe-protocol3.c:1369
+#, c-format
+msgid "LINE %d: "
+msgstr "RAD %d: "
+
+#: fe-protocol3.c:1768
+msgid "PQgetline: not doing text COPY OUT\n"
+msgstr "PQgetline: utför inte text-COPY OUT\n"
+
+#: fe-protocol3.c:2134
+#, c-format
+msgid "protocol error: id=0x%x\n"
+msgstr "protokollfel: id=0x%x\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:670
+msgid "GSSAPI size check error"
+msgstr "GSSAPI-fel vid kontroll av storlek"
+
+#: fe-secure-gssapi.c:681
+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:1367
+#, 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:1371
+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:1380
+#, 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:1430
+#, 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 namnpost saknas\n"
+
+#: fe-secure-openssl.c:822
+#, c-format
+msgid "could not create SSL context: %s\n"
+msgstr "kan inte skapa SSL-omgivning: %s\n"
+
+#: fe-secure-openssl.c:861
+#, 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:872
+#, 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:890
+#, 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:901
+#, 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:937
+#, 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:990
+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:994
+#, 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:1025
+#, c-format
+msgid "could not open certificate file \"%s\": %s\n"
+msgstr "kunde inte öppna certifikatfil \"%s\": %s\n"
+
+#: fe-secure-openssl.c:1044
+#, c-format
+msgid "could not read certificate file \"%s\": %s\n"
+msgstr "kunde inte läsa certifikatfil \"%s\": %s\n"
+
+#: fe-secure-openssl.c:1069
+#, c-format
+msgid "could not establish SSL connection: %s\n"
+msgstr "kan inte skapa SSL-anslutning: %s\n"
+
+#: fe-secure-openssl.c:1103
+#, c-format
+msgid "could not set SSL Server Name Indication (SNI): %s\n"
+msgstr "kunde inte sätta SSL servernamnsindikering (SNI): %s\n"
+
+#: fe-secure-openssl.c:1149
+#, c-format
+msgid "could not load SSL engine \"%s\": %s\n"
+msgstr "kunde inte ladda SSL-motor \"%s\": %s\n"
+
+#: fe-secure-openssl.c:1161
+#, c-format
+msgid "could not initialize SSL engine \"%s\": %s\n"
+msgstr "kunde inte initiera SSL-motor \"%s\": %s\n"
+
+#: fe-secure-openssl.c:1177
+#, 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:1191
+#, 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:1228
+#, 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:1237
+#, c-format
+msgid "private key file \"%s\" is not a regular file\n"
+msgstr "privat nyckelfilen \"%s\" är inte en vanlig fil\n"
+
+#: fe-secure-openssl.c:1270
+#, c-format
+msgid "private key file \"%s\" has group or world access; file must have permissions u=rw (0600) or less if owned by the current user, or permissions u=rw,g=r (0640) or less if owned by root\n"
+msgstr "privata nyckelfilen \"%s\" har grupp- eller världsrättigheter; filen måste ha rättigheterna u=rw (0600) eller mindre om den ägs av databasanvändaren eller rättigheterna u=rw,g=r (0640) eller mindre om den ägs av root.\n"
+
+#: fe-secure-openssl.c:1295
+#, 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:1313
+#, 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:1413
+#, 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:1449
+#, c-format
+msgid "certificate could not be obtained: %s\n"
+msgstr "certifikatet kunde inte hämtas: %s\n"
+
+#: fe-secure-openssl.c:1555
+#, c-format
+msgid "no SSL error reported"
+msgstr "inget SSL-fel rapporterat"
+
+#: fe-secure-openssl.c:1564
+#, c-format
+msgid "SSL error code %lu"
+msgstr "SSL-felkod %lu"
+
+#: fe-secure-openssl.c:1812
+#, c-format
+msgid "WARNING: sslpassword truncated\n"
+msgstr "VARNING: sslpassword trunkerat\n"
+
+#: fe-secure.c:267
+#, 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:380
+#, 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"
diff --git a/src/interfaces/libpq/po/uk.po b/src/interfaces/libpq/po/uk.po
new file mode 100644
index 0000000..962ea65
--- /dev/null
+++ b/src/interfaces/libpq/po/uk.po
@@ -0,0 +1,1212 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: postgresql\n"
+"Report-Msgid-Bugs-To: pgsql-bugs@lists.postgresql.org\n"
+"POT-Creation-Date: 2022-06-16 03:55+0000\n"
+"PO-Revision-Date: 2022-06-19 10:10\n"
+"Last-Translator: \n"
+"Language-Team: Ukrainian\n"
+"Language: uk_UA\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: /REL_14_STABLE/libpq.pot\n"
+"X-Crowdin-File-ID: 774\n"
+
+#: fe-auth-scram.c:213
+msgid "malformed SCRAM message (empty message)\n"
+msgstr "неправильне повідомлення SCRAM (пусте повідомлення)\n"
+
+#: fe-auth-scram.c:219
+msgid "malformed SCRAM message (length mismatch)\n"
+msgstr "неправильне повідомлення SCRAM (невідповідність довжини)\n"
+
+#: fe-auth-scram.c:263
+msgid "could not verify server signature\n"
+msgstr "не вдалося перевірити підпис сервера\n"
+
+#: fe-auth-scram.c:270
+msgid "incorrect server signature\n"
+msgstr "невірний підпис сервера\n"
+
+#: fe-auth-scram.c:279
+msgid "invalid SCRAM exchange state\n"
+msgstr "неприпустимий стан обміну SCRAM\n"
+
+#: fe-auth-scram.c:306
+#, c-format
+msgid "malformed SCRAM message (attribute \"%c\" expected)\n"
+msgstr "неправильне повідомлення SCRAM (очікувався атрибут \"%c\")\n"
+
+#: fe-auth-scram.c:315
+#, c-format
+msgid "malformed SCRAM message (expected character \"=\" for attribute \"%c\")\n"
+msgstr "неправильне повідомлення SCRAM (очікувався символ \"=\" для атрибута \"%c\")\n"
+
+#: fe-auth-scram.c:356
+msgid "could not generate nonce\n"
+msgstr "не вдалося згенерувати одноразовий ідентифікатор\n"
+
+#: fe-auth-scram.c:366 fe-auth-scram.c:441 fe-auth-scram.c:595
+#: fe-auth-scram.c:616 fe-auth-scram.c:642 fe-auth-scram.c:657
+#: fe-auth-scram.c:707 fe-auth-scram.c:746 fe-auth.c:290 fe-auth.c:362
+#: fe-auth.c:398 fe-auth.c:615 fe-auth.c:774 fe-auth.c:1132 fe-auth.c:1282
+#: fe-connect.c:911 fe-connect.c:1460 fe-connect.c:1629 fe-connect.c:2981
+#: fe-connect.c:4711 fe-connect.c:4972 fe-connect.c:5091 fe-connect.c:5343
+#: fe-connect.c:5424 fe-connect.c:5523 fe-connect.c:5779 fe-connect.c:5808
+#: fe-connect.c:5880 fe-connect.c:5904 fe-connect.c:5922 fe-connect.c:6023
+#: fe-connect.c:6032 fe-connect.c:6390 fe-connect.c:6540 fe-connect.c:6806
+#: fe-exec.c:686 fe-exec.c:876 fe-exec.c:1223 fe-exec.c:3047 fe-exec.c:3230
+#: fe-exec.c:4003 fe-exec.c:4168 fe-gssapi-common.c:111 fe-lobj.c:881
+#: fe-protocol3.c:975 fe-protocol3.c:990 fe-protocol3.c:1023
+#: fe-protocol3.c:1731 fe-secure-common.c:110 fe-secure-gssapi.c:504
+#: fe-secure-openssl.c:440 fe-secure-openssl.c:1133
+msgid "out of memory\n"
+msgstr "недостатньо пам'яті\n"
+
+#: fe-auth-scram.c:374
+msgid "could not encode nonce\n"
+msgstr "не вдалося закодувати одноразовий ідентифікатор\n"
+
+#: fe-auth-scram.c:563
+msgid "could not calculate client proof\n"
+msgstr "не вдалося обчислити підтвердження клієнта\n"
+
+#: fe-auth-scram.c:579
+msgid "could not encode client proof\n"
+msgstr "не вдалося закодувати підтвердження клієнта\n"
+
+#: fe-auth-scram.c:634
+msgid "invalid SCRAM response (nonce mismatch)\n"
+msgstr "неприпустима відповідь SCRAM (невідповідність одноразового ідентифікатора)\n"
+
+#: fe-auth-scram.c:667
+msgid "malformed SCRAM message (invalid salt)\n"
+msgstr "неправильне повідомлення SCRAM (неприпустима сіль)\n"
+
+#: fe-auth-scram.c:681
+msgid "malformed SCRAM message (invalid iteration count)\n"
+msgstr "неправильне повідомлення SCRAM (неприпустима кількість ітерацій)\n"
+
+#: fe-auth-scram.c:687
+msgid "malformed SCRAM message (garbage at end of server-first-message)\n"
+msgstr "неправильне повідомлення SCRAM (сміття в кінці першого повідомлення сервера)\n"
+
+#: fe-auth-scram.c:723
+#, c-format
+msgid "error received from server in SCRAM exchange: %s\n"
+msgstr "отримано помилку від сервера під час обміну SCRAM: %s\n"
+
+#: fe-auth-scram.c:739
+msgid "malformed SCRAM message (garbage at end of server-final-message)\n"
+msgstr "неправильне повідомлення SCRAM (сміття в кінці останнього повідомлення сервера)\n"
+
+#: fe-auth-scram.c:758
+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:391 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:351
+msgid "duplicate SSPI authentication request\n"
+msgstr "дублікат запиту автентифікації SSPI\n"
+
+#: fe-auth.c:377
+msgid "could not acquire SSPI credentials"
+msgstr "не вдалось отримати облікові дані SSPI"
+
+#: fe-auth.c:433
+msgid "channel binding required, but SSL not in use\n"
+msgstr "необхідно зв’язування каналів, але SSL не використовується\n"
+
+#: fe-auth.c:440
+msgid "duplicate SASL authentication request\n"
+msgstr "дублікат запиту автентифікації SASL\n"
+
+#: fe-auth.c:496
+msgid "channel binding is required, but client does not support it\n"
+msgstr "потрібно зв'язування каналів, але клієнт не підтримує його\n"
+
+#: fe-auth.c:513
+msgid "server offered SCRAM-SHA-256-PLUS authentication over a non-SSL connection\n"
+msgstr "сервер запропонував автентифікацію SCRAM-SHA-256-PLUS через підключення без SSL\n"
+
+#: fe-auth.c:525
+msgid "none of the server's SASL authentication mechanisms are supported\n"
+msgstr "жоден з серверних механізмів автентифікації SASL не підтримується\n"
+
+#: fe-auth.c:533
+msgid "channel binding is required, but server did not offer an authentication method that supports channel binding\n"
+msgstr "потрібно зв'язування каналів, але сервер не запропонував метод аутентифікації, який підтримує зв’язування каналів\n"
+
+#: fe-auth.c:639
+#, c-format
+msgid "out of memory allocating SASL buffer (%d)\n"
+msgstr "недостатньо пам'яті для буфера SASL (%d)\n"
+
+#: fe-auth.c:664
+msgid "AuthenticationSASLFinal received from server, but SASL authentication was not completed\n"
+msgstr "Від сервера отримано AuthenticationSASLFinal, але автентифікація SASL не була завершена\n"
+
+#: fe-auth.c:741
+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:877
+msgid "Kerberos 4 authentication not supported\n"
+msgstr "Автентифікація Kerberos 4 не підтримується\n"
+
+#: fe-auth.c:882
+msgid "Kerberos 5 authentication not supported\n"
+msgstr "Автентифікація Kerberos 5 не підтримується\n"
+
+#: fe-auth.c:953
+msgid "GSSAPI authentication not supported\n"
+msgstr "Автентифікація GSSAPI не підтримується\n"
+
+#: fe-auth.c:985
+msgid "SSPI authentication not supported\n"
+msgstr "Автентифікація SSPI не підтримується\n"
+
+#: fe-auth.c:993
+msgid "Crypt authentication not supported\n"
+msgstr "Автентифікація Crypt не підтримується\n"
+
+#: fe-auth.c:1060
+#, c-format
+msgid "authentication method %u not supported\n"
+msgstr "спосіб автентифікації %u не підтримується\n"
+
+#: fe-auth.c:1107
+#, c-format
+msgid "user name lookup failure: error code %lu\n"
+msgstr "невдала підстановка імені користувача: код помилки %lu\n"
+
+#: fe-auth.c:1117 fe-connect.c:2856
+#, c-format
+msgid "could not look up local user ID %d: %s\n"
+msgstr "не вдалося знайти локального користувача за ідентифікатором: %d: %s\n"
+
+#: fe-auth.c:1122 fe-connect.c:2861
+#, c-format
+msgid "local user with ID %d does not exist\n"
+msgstr "локального користувача з ідентифікатором %d не існує\n"
+
+#: fe-auth.c:1226
+msgid "unexpected shape of result set returned for SHOW\n"
+msgstr "неочікувану форму набору результатів повернуто для SHOW\n"
+
+#: fe-auth.c:1235
+msgid "password_encryption value too long\n"
+msgstr "занадто довге значення password_encryption \n"
+
+#: fe-auth.c:1275
+#, c-format
+msgid "unrecognized password encryption algorithm \"%s\"\n"
+msgstr "нерозпізнаний алгоритм шифрування пароля \"%s\"\n"
+
+#: fe-connect.c:1094
+#, c-format
+msgid "could not match %d host names to %d hostaddr values\n"
+msgstr "не вдалося зіставити %d імен хостів зі %d значеннями hostaddr\n"
+
+#: fe-connect.c:1180
+#, c-format
+msgid "could not match %d port numbers to %d hosts\n"
+msgstr "не вдалося зіставити %d номерів портів з %d хостами\n"
+
+#: fe-connect.c:1273 fe-connect.c:1299 fe-connect.c:1341 fe-connect.c:1350
+#: fe-connect.c:1383 fe-connect.c:1427
+#, c-format
+msgid "invalid %s value: \"%s\"\n"
+msgstr "неприпустиме значення %s : \"%s\"\n"
+
+#: fe-connect.c:1320
+#, c-format
+msgid "sslmode value \"%s\" invalid when SSL support is not compiled in\n"
+msgstr "значення sslmode \"%s\" неприпустиме, якщо підтримку протоколу SSL не скомпільовано\n"
+
+#: fe-connect.c:1368
+msgid "invalid SSL protocol version range\n"
+msgstr "неприпустимий діапазон версії протоколу SSL\n"
+
+#: fe-connect.c:1393
+#, c-format
+msgid "gssencmode value \"%s\" invalid when GSSAPI support is not compiled in\n"
+msgstr "значення gssencmode \"%s\" неприпустиме, якщо підтримку протоколу GSSAPI не скомпільовано\n"
+
+#: fe-connect.c:1653
+#, c-format
+msgid "could not set socket to TCP no delay mode: %s\n"
+msgstr "не вдалося встановити сокет у TCP-режим без затримки: %s\n"
+
+#: fe-connect.c:1715
+#, c-format
+msgid "connection to server on socket \"%s\" failed: "
+msgstr "помилка при з'єднанні з сервером через сокет \"%s\": "
+
+#: fe-connect.c:1742
+#, c-format
+msgid "connection to server at \"%s\" (%s), port %s failed: "
+msgstr "підключення до серверу \"%s\" (%s), порт %s провалено: "
+
+#: fe-connect.c:1747
+#, c-format
+msgid "connection to server at \"%s\", port %s failed: "
+msgstr "підключення до серверу \"%s\", порт %s провалено: "
+
+#: fe-connect.c:1772
+msgid "\tIs the server running locally and accepting connections on that socket?\n"
+msgstr "\tЧи працює сервер локально і приймає підключення до цього сокету?\n"
+
+#: fe-connect.c:1776
+msgid "\tIs the server running on that host and accepting TCP/IP connections?\n"
+msgstr "\tЧи працює сервер на цьому хості і приймає TCP/IP підключення?\n"
+
+#: fe-connect.c:1840
+#, c-format
+msgid "invalid integer value \"%s\" for connection option \"%s\"\n"
+msgstr "неприпустиме ціле значення \"%s\" для параметра з'єднання \"%s\"\n"
+
+#: fe-connect.c:1870 fe-connect.c:1905 fe-connect.c:1941 fe-connect.c:2030
+#: fe-connect.c:2644
+#, c-format
+msgid "%s(%s) failed: %s\n"
+msgstr "%s(%s) помилка: %s\n"
+
+#: fe-connect.c:1995
+#, c-format
+msgid "%s(%s) failed: error code %d\n"
+msgstr "%s(%s) помилка: код помилки %d\n"
+
+#: fe-connect.c:2310
+msgid "invalid connection state, probably indicative of memory corruption\n"
+msgstr "неприпустимий стан підключення, можливо, пошкоджена пам'ять\n"
+
+#: fe-connect.c:2389
+#, c-format
+msgid "invalid port number: \"%s\"\n"
+msgstr "неприпустимий номер порту: \"%s\"\n"
+
+#: fe-connect.c:2405
+#, c-format
+msgid "could not translate host name \"%s\" to address: %s\n"
+msgstr "не вдалося перекласти ім’я хоста \"%s\" в адресу: %s\n"
+
+#: fe-connect.c:2418
+#, c-format
+msgid "could not parse network address \"%s\": %s\n"
+msgstr "не вдалося проаналізувати адресу мережі \"%s\": %s\n"
+
+#: fe-connect.c:2431
+#, c-format
+msgid "Unix-domain socket path \"%s\" is too long (maximum %d bytes)\n"
+msgstr "Шлях Unix-сокету \"%s\" занадто довгий (максимум %d байтів)\n"
+
+#: fe-connect.c:2446
+#, c-format
+msgid "could not translate Unix-domain socket path \"%s\" to address: %s\n"
+msgstr "не вдалося перекласти шлях Unix-сокету \"%s\" в адресу: %s\n"
+
+#: fe-connect.c:2572
+#, c-format
+msgid "could not create socket: %s\n"
+msgstr "не вдалося створити сокет: %s\n"
+
+#: fe-connect.c:2603
+#, c-format
+msgid "could not set socket to nonblocking mode: %s\n"
+msgstr "не вдалося встановити сокет у режим без блокування: %s\n"
+
+#: fe-connect.c:2613
+#, c-format
+msgid "could not set socket to close-on-exec mode: %s\n"
+msgstr "не вдалося встановити сокет у режим закриття по виконанню: %s\n"
+
+#: fe-connect.c:2631
+msgid "keepalives parameter must be an integer\n"
+msgstr "параметр keepalives має бути цілим числом\n"
+
+#: fe-connect.c:2772
+#, c-format
+msgid "could not get socket error status: %s\n"
+msgstr "не вдалося отримати статус помилки сокету: %s\n"
+
+#: fe-connect.c:2800
+#, c-format
+msgid "could not get client address from socket: %s\n"
+msgstr "не вдалося отримати адресу клієнта з сокету: %s\n"
+
+#: fe-connect.c:2842
+msgid "requirepeer parameter is not supported on this platform\n"
+msgstr "параметр requirepeer не підтримується на цій платформі\n"
+
+#: fe-connect.c:2845
+#, c-format
+msgid "could not get peer credentials: %s\n"
+msgstr "не вдалося отримати облікові дані сервера: %s\n"
+
+#: fe-connect.c:2869
+#, c-format
+msgid "requirepeer specifies \"%s\", but actual peer user name is \"%s\"\n"
+msgstr "requirepeer вказує на \"%s\", але фактичне ім'я вузла \"%s\"\n"
+
+#: fe-connect.c:2909
+#, c-format
+msgid "could not send GSSAPI negotiation packet: %s\n"
+msgstr "не вдалося передати пакет узгодження протоколу GSSAPI: %s\n"
+
+#: fe-connect.c:2921
+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:2963
+#, c-format
+msgid "could not send SSL negotiation packet: %s\n"
+msgstr "не вдалося передати пакет узгодження протоколу SSL: %s\n"
+
+#: fe-connect.c:2994
+#, c-format
+msgid "could not send startup packet: %s\n"
+msgstr "не вдалося передати стартовий пакет: %s\n"
+
+#: fe-connect.c:3070
+msgid "server does not support SSL, but SSL was required\n"
+msgstr "сервер не підтримує протокол SSL, але протокол SSL вимагається\n"
+
+#: fe-connect.c:3097
+#, c-format
+msgid "received invalid response to SSL negotiation: %c\n"
+msgstr "отримано неприпустиму відповідь на узгодження SSL: %c\n"
+
+#: fe-connect.c:3118
+msgid "received unencrypted data after SSL response\n"
+msgstr "отримані незашифровані дані після відповіді SSL\n"
+
+#: fe-connect.c:3199
+msgid "server doesn't support GSSAPI encryption, but it was required\n"
+msgstr "сервер не підтримує шифрування GSSAPI, але це було необхідно\n"
+
+#: fe-connect.c:3211
+#, c-format
+msgid "received invalid response to GSSAPI negotiation: %c\n"
+msgstr "отримано неприпустиму відповідь на узгодження GSSAPI: %c\n"
+
+#: fe-connect.c:3230
+msgid "received unencrypted data after GSSAPI encryption response\n"
+msgstr "отримані незашифровані дані після відповіді шифрування GSSAPI\n"
+
+#: fe-connect.c:3290 fe-connect.c:3315
+#, c-format
+msgid "expected authentication request from server, but received %c\n"
+msgstr "очікувався запит автентифікації від сервера, але отримано %c\n"
+
+#: fe-connect.c:3522
+msgid "unexpected message from server during startup\n"
+msgstr "неочікуване повідомлення від сервера під час запуску\n"
+
+#: fe-connect.c:3614
+msgid "session is read-only\n"
+msgstr "сесія доступна тільки для читання\n"
+
+#: fe-connect.c:3617
+msgid "session is not read-only\n"
+msgstr "сесія доступна не лише для читання\n"
+
+#: fe-connect.c:3671
+msgid "server is in hot standby mode\n"
+msgstr "сервер знаходиться у режимі hot standby\n"
+
+#: fe-connect.c:3674
+msgid "server is not in hot standby mode\n"
+msgstr "сервер не в режимі hot standby\n"
+
+#: fe-connect.c:3792 fe-connect.c:3844
+#, c-format
+msgid "\"%s\" failed\n"
+msgstr "\"%s\" помилка\n"
+
+#: fe-connect.c:3858
+#, c-format
+msgid "invalid connection state %d, probably indicative of memory corruption\n"
+msgstr "неприпустимий стан підключення %d, можливо, пошкоджена пам'ять\n"
+
+#: fe-connect.c:4304 fe-connect.c:4364
+#, c-format
+msgid "PGEventProc \"%s\" failed during PGEVT_CONNRESET event\n"
+msgstr "Помилка у PGEventProc \"%s\" під час події PGEVT_CONNRESET\n"
+
+#: fe-connect.c:4724
+#, c-format
+msgid "invalid LDAP URL \"%s\": scheme must be ldap://\n"
+msgstr "неприпустима URL-адреса протоколу LDAP \"%s\": схема має бути ldap://\n"
+
+#: fe-connect.c:4739
+#, c-format
+msgid "invalid LDAP URL \"%s\": missing distinguished name\n"
+msgstr "неприпустима URL-адреса протоколу LDAP \"%s\": відсутнє унікальне ім'я\n"
+
+#: fe-connect.c:4751 fe-connect.c:4809
+#, c-format
+msgid "invalid LDAP URL \"%s\": must have exactly one attribute\n"
+msgstr "неприпустима URL-адреса протоколу LDAP \"%s\": має бути лише один атрибут\n"
+
+#: fe-connect.c:4763 fe-connect.c:4825
+#, 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:4775
+#, c-format
+msgid "invalid LDAP URL \"%s\": no filter\n"
+msgstr "неприпустима URL-адреса протоколу LDAP \"%s\": відсутній фільтр\n"
+
+#: fe-connect.c:4797
+#, c-format
+msgid "invalid LDAP URL \"%s\": invalid port number\n"
+msgstr "неприпустима URL-адреса протоколу LDAP \"%s\": неприпустимий номер порту\n"
+
+#: fe-connect.c:4835
+msgid "could not create LDAP structure\n"
+msgstr "не вдалося створити структуру протоколу LDAP\n"
+
+#: fe-connect.c:4911
+#, c-format
+msgid "lookup on LDAP server failed: %s\n"
+msgstr "помилка підстановки на сервері протоколу LDAP: %s\n"
+
+#: fe-connect.c:4922
+msgid "more than one entry found on LDAP lookup\n"
+msgstr "знайдено більше одного входження при підстановці протоколу LDAP\n"
+
+#: fe-connect.c:4923 fe-connect.c:4935
+msgid "no entry found on LDAP lookup\n"
+msgstr "не знайдено входження при підстановці протоколу LDAP\n"
+
+#: fe-connect.c:4946 fe-connect.c:4959
+msgid "attribute has no values on LDAP lookup\n"
+msgstr "атрибут не має значення при підстановці протоколу LDAP\n"
+
+#: fe-connect.c:5011 fe-connect.c:5030 fe-connect.c:5562
+#, c-format
+msgid "missing \"=\" after \"%s\" in connection info string\n"
+msgstr "відсутній \"=\" після \"%s\" у рядку інформації про підключення\n"
+
+#: fe-connect.c:5103 fe-connect.c:5747 fe-connect.c:6523
+#, c-format
+msgid "invalid connection option \"%s\"\n"
+msgstr "неприпустимий параметр підключення \"%s\"\n"
+
+#: fe-connect.c:5119 fe-connect.c:5611
+msgid "unterminated quoted string in connection info string\n"
+msgstr "відкриті лапки у рядку інформації про підключення\n"
+
+#: fe-connect.c:5200
+#, c-format
+msgid "definition of service \"%s\" not found\n"
+msgstr "не знайдено визначення сервера \"%s\"\n"
+
+#: fe-connect.c:5226
+#, c-format
+msgid "service file \"%s\" not found\n"
+msgstr "не знайдено сервісний файл \"%s\"\n"
+
+#: fe-connect.c:5240
+#, c-format
+msgid "line %d too long in service file \"%s\"\n"
+msgstr "рядок %d занадто довгий у сервісному файлі \"%s\"\n"
+
+#: fe-connect.c:5311 fe-connect.c:5355
+#, c-format
+msgid "syntax error in service file \"%s\", line %d\n"
+msgstr "синтаксична помилка у сервісному файлі \"%s\", рядок %d\n"
+
+#: fe-connect.c:5322
+#, c-format
+msgid "nested service specifications not supported in service file \"%s\", line %d\n"
+msgstr "вкладені сервісні специфікації не підтримуються у сервісному файлі \"%s\", рядок %d\n"
+
+#: fe-connect.c:6043
+#, c-format
+msgid "invalid URI propagated to internal parser routine: \"%s\"\n"
+msgstr "у внутрішню процедуру аналізу рядка передано помилковий URI: \"%s\"\n"
+
+#: fe-connect.c:6120
+#, 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:6127
+#, c-format
+msgid "IPv6 host address may not be empty in URI: \"%s\"\n"
+msgstr "IPv6, що знаходиться в URI, не може бути пустим: \"%s\"\n"
+
+#: fe-connect.c:6142
+#, c-format
+msgid "unexpected character \"%c\" at position %d in URI (expected \":\" or \"/\"): \"%s\"\n"
+msgstr "неочікуваний символ \"%c\" на позиції %d в URI (очікувалося \":\" або \"/\"): \"%s\"\n"
+
+#: fe-connect.c:6272
+#, c-format
+msgid "extra key/value separator \"=\" in URI query parameter: \"%s\"\n"
+msgstr "зайвий розділювач ключа/значення \"=\" в параметрі запиту URI: \"%s\"\n"
+
+#: fe-connect.c:6292
+#, c-format
+msgid "missing key/value separator \"=\" in URI query parameter: \"%s\"\n"
+msgstr "відсутній розділювач ключа/значення \"=\" у параметрі запиту URI: \"%s\"\n"
+
+#: fe-connect.c:6344
+#, c-format
+msgid "invalid URI query parameter: \"%s\"\n"
+msgstr "неприпустимий параметр запиту URI: \"%s\"\n"
+
+#: fe-connect.c:6418
+#, c-format
+msgid "invalid percent-encoded token: \"%s\"\n"
+msgstr "неприпустимий токен, закодований відсотками: \"%s\"\n"
+
+#: fe-connect.c:6428
+#, c-format
+msgid "forbidden value %%00 in percent-encoded value: \"%s\"\n"
+msgstr "неприпустиме значення %%00 для значення, закодованого відсотками: \"%s\"\n"
+
+#: fe-connect.c:6798
+msgid "connection pointer is NULL\n"
+msgstr "нульове значення вказівника підключення \n"
+
+#: fe-connect.c:7086
+#, c-format
+msgid "WARNING: password file \"%s\" is not a plain file\n"
+msgstr "ПОПЕРЕДЖЕННЯ: файл паролів \"%s\" не є простим файлом\n"
+
+#: fe-connect.c:7095
+#, 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:7203
+#, c-format
+msgid "password retrieved from file \"%s\"\n"
+msgstr "пароль отримано з файлу \"%s\"\n"
+
+#: fe-exec.c:449 fe-exec.c:3304
+#, c-format
+msgid "row number %d is out of range 0..%d"
+msgstr "число рядків %d поза діапазоном 0..%d"
+
+#: fe-exec.c:510 fe-protocol3.c:219 fe-protocol3.c:244 fe-protocol3.c:273
+#: fe-protocol3.c:291 fe-protocol3.c:371 fe-protocol3.c:743
+msgid "out of memory"
+msgstr "недостатньо пам'яті"
+
+#: fe-exec.c:511 fe-protocol3.c:1939
+#, c-format
+msgid "%s"
+msgstr "%s"
+
+#: fe-exec.c:792
+msgid "write to server failed\n"
+msgstr "записати на сервер не вдалося\n"
+
+#: fe-exec.c:864
+msgid "NOTICE"
+msgstr "ПОВІДОМЛЕННЯ"
+
+#: fe-exec.c:922
+msgid "PGresult cannot support more than INT_MAX tuples"
+msgstr "PGresult не може підтримувати більше ніж INT_MAX кортежів"
+
+#: fe-exec.c:934
+msgid "size_t overflow"
+msgstr "переповнення size_t"
+
+#: fe-exec.c:1349 fe-exec.c:1454 fe-exec.c:1503
+msgid "command string is a null pointer\n"
+msgstr "рядок команди є нульовим вказівником\n"
+
+#: fe-exec.c:1460 fe-exec.c:1509 fe-exec.c:1605
+#, c-format
+msgid "number of parameters must be between 0 and %d\n"
+msgstr "кількість параметрів має бути між 0 і %d\n"
+
+#: fe-exec.c:1497 fe-exec.c:1599
+msgid "statement name is a null pointer\n"
+msgstr "ім’я оператора є пустим вказівником\n"
+
+#: fe-exec.c:1641 fe-exec.c:3157
+msgid "no connection to the server\n"
+msgstr "немає підключення до сервера\n"
+
+#: fe-exec.c:1650 fe-exec.c:3166
+msgid "another command is already in progress\n"
+msgstr "інша команда уже в прогресі\n"
+
+#: fe-exec.c:1679
+msgid "cannot queue commands during COPY\n"
+msgstr "не можна поставити в чергу команди під час COPY\n"
+
+#: fe-exec.c:1797
+msgid "length must be given for binary parameter\n"
+msgstr "для бінарного параметра має бути надана довжина\n"
+
+#: fe-exec.c:2121
+#, c-format
+msgid "unexpected asyncStatus: %d\n"
+msgstr "неочікуваний asyncStatus: %d\n"
+
+#: fe-exec.c:2141
+#, c-format
+msgid "PGEventProc \"%s\" failed during PGEVT_RESULTCREATE event\n"
+msgstr "Помилка у PGEventProc \"%s\" під час події PGEVT_RESULTCREAT\n"
+
+#: fe-exec.c:2289
+msgid "synchronous command execution functions are not allowed in pipeline mode\n"
+msgstr "функції синхронного виконання команд заборонені в режимі конвеєра\n"
+
+#: fe-exec.c:2311
+msgid "COPY terminated by new PQexec"
+msgstr "COPY завершено новим PQexec"
+
+#: fe-exec.c:2328
+msgid "PQexec not allowed during COPY BOTH\n"
+msgstr "PQexec не дозволяється під час COPY BOTH\n"
+
+#: fe-exec.c:2556 fe-exec.c:2612 fe-exec.c:2681 fe-protocol3.c:1870
+msgid "no COPY in progress\n"
+msgstr "Немає COPY у процесі\n"
+
+#: fe-exec.c:2858
+msgid "PQfn not allowed in pipeline mode\n"
+msgstr "PQfn заборонено в режимі конвеєра\n"
+
+#: fe-exec.c:2866
+msgid "connection in wrong state\n"
+msgstr "підключення у неправильному стані\n"
+
+#: fe-exec.c:2910
+msgid "cannot enter pipeline mode, connection not idle\n"
+msgstr "не можна увійти в режим конвеєра, підключення не в очікуванні\n"
+
+#: fe-exec.c:2944 fe-exec.c:2961
+msgid "cannot exit pipeline mode with uncollected results\n"
+msgstr "не можна вийти з режиму конвеєра з незібраними результатами\n"
+
+#: fe-exec.c:2949
+msgid "cannot exit pipeline mode while busy\n"
+msgstr "не можна вийти з режиму конвеєра, коли зайнято\n"
+
+#: fe-exec.c:3091
+msgid "cannot send pipeline when not in pipeline mode\n"
+msgstr "неможливо скористатися конвеєром не у режимі конвеєра\n"
+
+#: fe-exec.c:3193
+msgid "invalid ExecStatusType code"
+msgstr "неприпустимий код ExecStatusType"
+
+#: fe-exec.c:3220
+msgid "PGresult is not an error result\n"
+msgstr "PGresult не є помилковим результатом\n"
+
+#: fe-exec.c:3288 fe-exec.c:3311
+#, c-format
+msgid "column number %d is out of range 0..%d"
+msgstr "число стовпців %d поза діапазоном 0..%d"
+
+#: fe-exec.c:3326
+#, c-format
+msgid "parameter number %d is out of range 0..%d"
+msgstr "число параметрів %d поза діапазоном 0..%d"
+
+#: fe-exec.c:3636
+#, c-format
+msgid "could not interpret result from server: %s"
+msgstr "не вдалося інтерпретувати результат від сервера: %s"
+
+#: fe-exec.c:3896 fe-exec.c:3985
+msgid "incomplete multibyte character\n"
+msgstr "неповний мультибайтний символ\n"
+
+#: fe-gssapi-common.c:124
+msgid "GSSAPI name import error"
+msgstr "Помилка імпорту імені у GSSAPI"
+
+#: fe-lobj.c:145 fe-lobj.c:210 fe-lobj.c:403 fe-lobj.c:494 fe-lobj.c:568
+#: fe-lobj.c:969 fe-lobj.c:977 fe-lobj.c:985 fe-lobj.c:993 fe-lobj.c:1001
+#: fe-lobj.c:1009 fe-lobj.c:1017 fe-lobj.c:1025
+#, c-format
+msgid "cannot determine OID of function %s\n"
+msgstr "неможливо визначити ідентифікатор OID функції %s\n"
+
+#: fe-lobj.c:162
+msgid "argument of lo_truncate exceeds integer range\n"
+msgstr "аргумент lo_truncate перевищує діапазон цілого числа\n"
+
+#: fe-lobj.c:266
+msgid "argument of lo_read exceeds integer range\n"
+msgstr "аргумент lo_read перевищує діапазон цілого числа\n"
+
+#: fe-lobj.c:318
+msgid "argument of lo_write exceeds integer range\n"
+msgstr "аргумент lo_write перевищує діапазон цілого числа\n"
+
+#: fe-lobj.c:678 fe-lobj.c:789
+#, c-format
+msgid "could not open file \"%s\": %s\n"
+msgstr "не вдалося відкрити файл \"%s\": %s\n"
+
+#: fe-lobj.c:734
+#, c-format
+msgid "could not read from file \"%s\": %s\n"
+msgstr "не вдалося прочитати з файлу \"%s\": %s\n"
+
+#: fe-lobj.c:810 fe-lobj.c:834
+#, c-format
+msgid "could not write to file \"%s\": %s\n"
+msgstr "не вдалося записати у файл \"%s\": %s\n"
+
+#: fe-lobj.c:920
+msgid "query to initialize large object functions did not return data\n"
+msgstr "запит на ініціалізацію функцій для великих об’єктів не повернув дані\n"
+
+#: fe-misc.c:242
+#, c-format
+msgid "integer of size %lu not supported by pqGetInt"
+msgstr "pqGetInt не підтримує ціле число розміром %lu"
+
+#: fe-misc.c:275
+#, c-format
+msgid "integer of size %lu not supported by pqPutInt"
+msgstr "pqPutInt не підтримує ціле число розміром %lu"
+
+#: fe-misc.c:576 fe-misc.c:822
+msgid "connection not open\n"
+msgstr "підключення не відкрито\n"
+
+#: fe-misc.c:755 fe-secure-openssl.c:209 fe-secure-openssl.c:316
+#: fe-secure.c:260 fe-secure.c:373
+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:1015
+msgid "timeout expired\n"
+msgstr "тайм-аут минув\n"
+
+#: fe-misc.c:1060
+msgid "invalid socket\n"
+msgstr "неприпустимий сокет\n"
+
+#: fe-misc.c:1083
+#, c-format
+msgid "%s() failed: %s\n"
+msgstr "%s() помилка: %s\n"
+
+#: fe-protocol3.c:196
+#, c-format
+msgid "message type 0x%02x arrived from server while idle"
+msgstr "отримано тип повідомлення 0x%02x від сервера під час бездіяльності"
+
+#: fe-protocol3.c:403
+msgid "server sent data (\"D\" message) without prior row description (\"T\" message)\n"
+msgstr "сервер передав дані (повідомлення \"D\") без попереднього опису рядка (повідомлення \"T\")\n"
+
+#: fe-protocol3.c:446
+#, c-format
+msgid "unexpected response from server; first received character was \"%c\"\n"
+msgstr "неочікувана відповідь від сервера; перший отриманий символ був \"%c\"\n"
+
+#: fe-protocol3.c:471
+#, c-format
+msgid "message contents do not agree with length in message type \"%c\"\n"
+msgstr "вміст повідомлення не відповідає довжині у типі повідомлення \"%c\"\n"
+
+#: fe-protocol3.c:491
+#, c-format
+msgid "lost synchronization with server: got message type \"%c\", length %d\n"
+msgstr "втрачено синхронізацію з сервером: отримано тип повідомлення \"%c\", довжина %d\n"
+
+#: fe-protocol3.c:543 fe-protocol3.c:583
+msgid "insufficient data in \"T\" message"
+msgstr "недостатньо даних у повідомленні \"T\""
+
+#: fe-protocol3.c:654 fe-protocol3.c:860
+msgid "out of memory for query result"
+msgstr "недостатньо пам'яті для результату запиту"
+
+#: fe-protocol3.c:723
+msgid "insufficient data in \"t\" message"
+msgstr "недостатньо даних у повідомленні \"t\""
+
+#: fe-protocol3.c:782 fe-protocol3.c:814 fe-protocol3.c:832
+msgid "insufficient data in \"D\" message"
+msgstr "зайві дані у повідомленні \"D\""
+
+#: fe-protocol3.c:788
+msgid "unexpected field count in \"D\" message"
+msgstr "неочікувана кількість полів у повідомленні \"D\""
+
+#: fe-protocol3.c:1036
+msgid "no error message available\n"
+msgstr "немає доступного повідомлення про помилку\n"
+
+#. translator: %s represents a digit string
+#: fe-protocol3.c:1084 fe-protocol3.c:1103
+#, c-format
+msgid " at character %s"
+msgstr " в символі %s"
+
+#: fe-protocol3.c:1116
+#, c-format
+msgid "DETAIL: %s\n"
+msgstr "ДЕТАЛІ: %s\n"
+
+#: fe-protocol3.c:1119
+#, c-format
+msgid "HINT: %s\n"
+msgstr "ПІДКАЗКА: %s\n"
+
+#: fe-protocol3.c:1122
+#, c-format
+msgid "QUERY: %s\n"
+msgstr "ЗАПИТ: %s\n"
+
+#: fe-protocol3.c:1129
+#, c-format
+msgid "CONTEXT: %s\n"
+msgstr "КОНТЕКСТ: %s\n"
+
+#: fe-protocol3.c:1138
+#, c-format
+msgid "SCHEMA NAME: %s\n"
+msgstr "ІМ'Я СХЕМИ: %s\n"
+
+#: fe-protocol3.c:1142
+#, c-format
+msgid "TABLE NAME: %s\n"
+msgstr "ІМ'Я ТАБЛИЦІ: %s\n"
+
+#: fe-protocol3.c:1146
+#, c-format
+msgid "COLUMN NAME: %s\n"
+msgstr "ІМ'Я СТОВПЦЯ: %s\n"
+
+#: fe-protocol3.c:1150
+#, c-format
+msgid "DATATYPE NAME: %s\n"
+msgstr "ІМ'Я ТИПУ ДАНИХ: %s\n"
+
+#: fe-protocol3.c:1154
+#, c-format
+msgid "CONSTRAINT NAME: %s\n"
+msgstr "ІМ'Я ОБМЕЖЕННЯ: %s\n"
+
+#: fe-protocol3.c:1166
+msgid "LOCATION: "
+msgstr "РОЗТАШУВАННЯ: "
+
+#: fe-protocol3.c:1168
+#, c-format
+msgid "%s, "
+msgstr "%s, "
+
+#: fe-protocol3.c:1170
+#, c-format
+msgid "%s:%s"
+msgstr "%s:%s"
+
+#: fe-protocol3.c:1365
+#, c-format
+msgid "LINE %d: "
+msgstr "РЯДОК %d: "
+
+#: fe-protocol3.c:1764
+msgid "PQgetline: not doing text COPY OUT\n"
+msgstr "PQgetline можна викликати лише під час COPY OUT\n"
+
+#: fe-protocol3.c:2130
+#, c-format
+msgid "protocol error: id=0x%x\n"
+msgstr "помилка протоколу: id=0x%x\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: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:1367
+#, 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:1371
+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:1380
+#, 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:1430
+#, 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:822
+#, c-format
+msgid "could not create SSL context: %s\n"
+msgstr "не вдалося створити контекст SSL: %s\n"
+
+#: fe-secure-openssl.c:861
+#, c-format
+msgid "invalid value \"%s\" for minimum SSL protocol version\n"
+msgstr "неприпустиме значення \"%s\" для мінімальної версії протоколу SSL\n"
+
+#: fe-secure-openssl.c:872
+#, c-format
+msgid "could not set minimum SSL protocol version: %s\n"
+msgstr "не вдалося встановити мінімальну версію протоколу SSL: %s\n"
+
+#: fe-secure-openssl.c:890
+#, c-format
+msgid "invalid value \"%s\" for maximum SSL protocol version\n"
+msgstr "неприпустиме значення \"%s\" для максимальної версії протоколу SSL\n"
+
+#: fe-secure-openssl.c:901
+#, c-format
+msgid "could not set maximum SSL protocol version: %s\n"
+msgstr "не вдалося встановити максимальну версію протоколу SSL: %s\n"
+
+#: fe-secure-openssl.c:937
+#, c-format
+msgid "could not read root certificate file \"%s\": %s\n"
+msgstr "не вдалося прочитати файл кореневого сертифікату \"%s\": %s\n"
+
+#: fe-secure-openssl.c:990
+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:994
+#, 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:1025
+#, c-format
+msgid "could not open certificate file \"%s\": %s\n"
+msgstr "не вдалося відкрити файл сертифікату \"%s\": %s\n"
+
+#: fe-secure-openssl.c:1044
+#, c-format
+msgid "could not read certificate file \"%s\": %s\n"
+msgstr "не вдалося прочитати файл сертифікату \"%s\": %s\n"
+
+#: fe-secure-openssl.c:1069
+#, c-format
+msgid "could not establish SSL connection: %s\n"
+msgstr "не вдалося встановити SSL-підключення: %s\n"
+
+#: fe-secure-openssl.c:1103
+#, c-format
+msgid "could not set SSL Server Name Indication (SNI): %s\n"
+msgstr "не вдалося встановити Індикацію Імені Сервера протокол SSL (SNI): %s\n"
+
+#: fe-secure-openssl.c:1149
+#, c-format
+msgid "could not load SSL engine \"%s\": %s\n"
+msgstr "не вдалося завантажити модуль SSL \"%s\": %s\n"
+
+#: fe-secure-openssl.c:1161
+#, c-format
+msgid "could not initialize SSL engine \"%s\": %s\n"
+msgstr "не вдалося ініціалізувати модуль SSL \"%s\": %s\n"
+
+#: fe-secure-openssl.c:1177
+#, 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:1191
+#, 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:1228
+#, c-format
+msgid "certificate present, but not private key file \"%s\"\n"
+msgstr "сертифікат присутній, але файл закритого ключа \"%s\" ні\n"
+
+#: fe-secure-openssl.c:1237
+#, c-format
+msgid "private key file \"%s\" is not a regular file\n"
+msgstr "файл закритого ключа \"%s\" не є звичайним файлом\n"
+
+#: fe-secure-openssl.c:1270
+#, c-format
+msgid "private key file \"%s\" has group or world access; file must have permissions u=rw (0600) or less if owned by the current user, or permissions u=rw,g=r (0640) or less if owned by root\n"
+msgstr "файл закритого ключа \"%s\" має груповий або загальний доступ; файл повинен мати права u=rw (0600) або менше, якщо він належить поточному користувачу, або права u=rw,g=r (0640) або менше, якщо належить root\n"
+
+#: fe-secure-openssl.c:1295
+#, c-format
+msgid "could not load private key file \"%s\": %s\n"
+msgstr "не вдалося завантажити файл закритого ключа \"%s\": %s\n"
+
+#: fe-secure-openssl.c:1313
+#, c-format
+msgid "certificate does not match private key file \"%s\": %s\n"
+msgstr "сертифікат не відповідає файлу закритого ключа \"%s\": %s\n"
+
+#: fe-secure-openssl.c:1413
+#, 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:1449
+#, c-format
+msgid "certificate could not be obtained: %s\n"
+msgstr "не вдалося отримати сертифікат: %s\n"
+
+#: fe-secure-openssl.c:1555
+#, c-format
+msgid "no SSL error reported"
+msgstr "немає повідомлення про помилку SSL"
+
+#: fe-secure-openssl.c:1564
+#, c-format
+msgid "SSL error code %lu"
+msgstr "Код помилки SSL %lu"
+
+#: fe-secure-openssl.c:1812
+#, c-format
+msgid "WARNING: sslpassword truncated\n"
+msgstr "ПОПЕРЕДЖЕННЯ: sslpassword скорочено\n"
+
+#: fe-secure.c:267
+#, c-format
+msgid "could not receive data from server: %s\n"
+msgstr "не вдалося отримати дані з серверу: %s\n"
+
+#: fe-secure.c:380
+#, 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/zh_CN.po b/src/interfaces/libpq/po/zh_CN.po
new file mode 100644
index 0000000..34a34a8
--- /dev/null
+++ b/src/interfaces/libpq/po/zh_CN.po
@@ -0,0 +1,1193 @@
+# simplified Chinese translation file for libpq
+# Bao Wei <weibao@forevertek.com>, 2002
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: libpq (PostgreSQL) 14\n"
+"Report-Msgid-Bugs-To: pgsql-bugs@lists.postgresql.org\n"
+"POT-Creation-Date: 2021-08-14 05:40+0000\n"
+"PO-Revision-Date: 2021-08-15 16:25+0800\n"
+"Last-Translator: Jie Zhang <zhangjie2@fujitsu.com>\n"
+"Language-Team: Chinese (Simplified) <zhangjie2@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:213
+msgid "malformed SCRAM message (empty message)\n"
+msgstr "错误的SCRAM消息(空消息)\n"
+
+#: fe-auth-scram.c:219
+msgid "malformed SCRAM message (length mismatch)\n"
+msgstr "错误的SCRAM消息(长度不匹配)\n"
+
+#: fe-auth-scram.c:263
+msgid "could not verify server signature\n"
+msgstr "无法验证服务器签名\n"
+
+#: fe-auth-scram.c:270
+msgid "incorrect server signature\n"
+msgstr "服务器签名不正确\n"
+
+#: fe-auth-scram.c:279
+msgid "invalid SCRAM exchange state\n"
+msgstr "SCRAM交换状态无效\n"
+
+#: fe-auth-scram.c:306
+#, c-format
+msgid "malformed SCRAM message (attribute \"%c\" expected)\n"
+msgstr "错误的SCRAM消息(应为属性\"%c\")\n"
+
+#: fe-auth-scram.c:315
+#, c-format
+msgid "malformed SCRAM message (expected character \"=\" for attribute \"%c\")\n"
+msgstr "错误的SCRAM消息(属性\"%c\"需要字符\"=\")\n"
+
+#: fe-auth-scram.c:356
+msgid "could not generate nonce\n"
+msgstr "无法生成nonce\n"
+
+#: fe-auth-scram.c:366 fe-auth-scram.c:441 fe-auth-scram.c:595
+#: fe-auth-scram.c:616 fe-auth-scram.c:642 fe-auth-scram.c:657
+#: fe-auth-scram.c:707 fe-auth-scram.c:746 fe-auth.c:290 fe-auth.c:362
+#: fe-auth.c:398 fe-auth.c:615 fe-auth.c:774 fe-auth.c:1132 fe-auth.c:1282
+#: fe-connect.c:911 fe-connect.c:1455 fe-connect.c:1624 fe-connect.c:2976
+#: fe-connect.c:4657 fe-connect.c:4918 fe-connect.c:5037 fe-connect.c:5289
+#: fe-connect.c:5370 fe-connect.c:5469 fe-connect.c:5725 fe-connect.c:5754
+#: fe-connect.c:5826 fe-connect.c:5850 fe-connect.c:5868 fe-connect.c:5969
+#: fe-connect.c:5978 fe-connect.c:6336 fe-connect.c:6486 fe-connect.c:6752
+#: fe-exec.c:686 fe-exec.c:876 fe-exec.c:1223 fe-exec.c:3043 fe-exec.c:3226
+#: fe-exec.c:3999 fe-exec.c:4164 fe-gssapi-common.c:111 fe-lobj.c:881
+#: fe-protocol3.c:975 fe-protocol3.c:990 fe-protocol3.c:1023
+#: fe-protocol3.c:1731 fe-secure-common.c:110 fe-secure-gssapi.c:504
+#: fe-secure-openssl.c:440 fe-secure-openssl.c:1133
+msgid "out of memory\n"
+msgstr "内存不足\n"
+
+#: fe-auth-scram.c:374
+msgid "could not encode nonce\n"
+msgstr "无法编码nonce\n"
+
+#: fe-auth-scram.c:563
+msgid "could not calculate client proof\n"
+msgstr "无法计算客户端证明\n"
+
+#: fe-auth-scram.c:579
+msgid "could not encode client proof\n"
+msgstr "无法对客户端证明进行编码\n"
+
+#: fe-auth-scram.c:634
+msgid "invalid SCRAM response (nonce mismatch)\n"
+msgstr "SCRAM响应无效(非匹配)\n"
+
+#: fe-auth-scram.c:667
+msgid "malformed SCRAM message (invalid salt)\n"
+msgstr "错误的SCRAM消息 (无效的salt)\n"
+
+#: fe-auth-scram.c:681
+msgid "malformed SCRAM message (invalid iteration count)\n"
+msgstr "错误的SCRAM消息(迭代计数无效)\n"
+
+#: fe-auth-scram.c:687
+msgid "malformed SCRAM message (garbage at end of server-first-message)\n"
+msgstr "错误的SCRAM消息 (服务器第一条消息结束时为垃圾消息)\n"
+
+#: fe-auth-scram.c:723
+#, c-format
+msgid "error received from server in SCRAM exchange: %s\n"
+msgstr "在SCRAM交换中从服务器接收到错误: %s\n"
+
+#: fe-auth-scram.c:739
+msgid "malformed SCRAM message (garbage at end of server-final-message)\n"
+msgstr "错误的SCRAM消息 (服务器最后一条消息结束时为垃圾消息)\n"
+
+#: fe-auth-scram.c:758
+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:391 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:351
+msgid "duplicate SSPI authentication request\n"
+msgstr "重复的SSPI认证请求\n"
+
+#: fe-auth.c:377
+msgid "could not acquire SSPI credentials"
+msgstr "无法获得SSPI证书"
+
+#: fe-auth.c:433
+msgid "channel binding required, but SSL not in use\n"
+msgstr "需要通道绑定,但未使用SSL\n"
+
+#: fe-auth.c:440
+msgid "duplicate SASL authentication request\n"
+msgstr "重复的SASL认证请求\n"
+
+#: fe-auth.c:496
+msgid "channel binding is required, but client does not support it\n"
+msgstr "通道绑定是必需的,但客户端不支持它\n"
+
+#: fe-auth.c:513
+msgid "server offered SCRAM-SHA-256-PLUS authentication over a non-SSL connection\n"
+msgstr "服务器通过非SSL连接提供了SCRAM-SHA-256-PLUS身份验证\n"
+
+#: fe-auth.c:525
+msgid "none of the server's SASL authentication mechanisms are supported\n"
+msgstr "不支持服务器的SASL身份验证机制\n"
+
+#: fe-auth.c:533
+msgid "channel binding is required, but server did not offer an authentication method that supports channel binding\n"
+msgstr "需要通道绑定,但服务器未提供支持通道绑定的身份验证方法\n"
+
+#: fe-auth.c:639
+#, c-format
+msgid "out of memory allocating SASL buffer (%d)\n"
+msgstr "在分配SASL缓冲区(%d)时内存用尽\n"
+
+#: fe-auth.c:664
+msgid "AuthenticationSASLFinal received from server, but SASL authentication was not completed\n"
+msgstr "从服务器接收到AuthenticationSASLFinal,但未完成SASL身份验证\n"
+
+#: fe-auth.c:741
+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:877
+msgid "Kerberos 4 authentication not supported\n"
+msgstr "不支持 Kerberos 4 认证\n"
+
+#: fe-auth.c:882
+msgid "Kerberos 5 authentication not supported\n"
+msgstr "不支持 Kerberos 5 认证\n"
+
+#: fe-auth.c:953
+msgid "GSSAPI authentication not supported\n"
+msgstr "不支持GSSAPI认证\n"
+
+#: fe-auth.c:985
+msgid "SSPI authentication not supported\n"
+msgstr "不支持SSPI认证\n"
+
+#: fe-auth.c:993
+msgid "Crypt authentication not supported\n"
+msgstr "不支持Crypt认证\n"
+
+#: fe-auth.c:1060
+#, c-format
+msgid "authentication method %u not supported\n"
+msgstr "不支持 %u 认证方式\n"
+
+#: fe-auth.c:1107
+#, c-format
+msgid "user name lookup failure: error code %lu\n"
+msgstr "用户名查找失败:错误代码%lu\n"
+
+#: fe-auth.c:1117 fe-connect.c:2851
+#, c-format
+msgid "could not look up local user ID %d: %s\n"
+msgstr "无法查找本地用户ID %d: %s\n"
+
+#: fe-auth.c:1122 fe-connect.c:2856
+#, c-format
+msgid "local user with ID %d does not exist\n"
+msgstr "ID 为 %d 的本地用户不存在\n"
+
+#: fe-auth.c:1226
+msgid "unexpected shape of result set returned for SHOW\n"
+msgstr "SHOW出现意外的结果状态\n"
+
+#: fe-auth.c:1235
+msgid "password_encryption value too long\n"
+msgstr "密码_加密值太长\n"
+
+#: fe-auth.c:1275
+#, c-format
+msgid "unrecognized password encryption algorithm \"%s\"\n"
+msgstr "无法识别的密码加密算法 \"%s\"\n"
+
+#: fe-connect.c:1094
+#, c-format
+msgid "could not match %d host names to %d hostaddr values\n"
+msgstr "无法将主机名 %d 与主机地址 %d匹配\n"
+
+#: fe-connect.c:1175
+#, c-format
+msgid "could not match %d port numbers to %d hosts\n"
+msgstr "无法将端口号 %d与主机%d匹配\n"
+
+#: fe-connect.c:1268 fe-connect.c:1294 fe-connect.c:1336 fe-connect.c:1345
+#: fe-connect.c:1378 fe-connect.c:1422
+msgid "invalid %s value: \"%s\"\n"
+msgstr "无效的 %s值: \"%s\"\n"
+
+#: fe-connect.c:1315
+#, c-format
+msgid "sslmode value \"%s\" invalid when SSL support is not compiled in\n"
+msgstr "无效的 sslmode 值 \"%s\" 当没有把 SSL 支持编译进来时\n"
+
+#: fe-connect.c:1363
+msgid "invalid SSL protocol version range\n"
+msgstr "无效的SSL协议版本范围\n"
+
+#: fe-connect.c:1388
+#, c-format
+msgid "gssencmode value \"%s\" invalid when GSSAPI support is not compiled in\n"
+msgstr "无效的 gssencmode 值 \"%s\" 当没有把 GSSAPI 支持编译进来时\n"
+
+#: fe-connect.c:1648
+#, c-format
+msgid "could not set socket to TCP no delay mode: %s\n"
+msgstr "无法将套接字设置为 TCP 无延迟模式: %s\n"
+
+#: fe-connect.c:1710
+msgid "connection to server on socket \"%s\" failed: "
+msgstr "连接到套接字\"%s\"上的服务器失败:"
+
+#: fe-connect.c:1737
+msgid "connection to server at \"%s\" (%s), port %s failed: "
+msgstr "连接到\"%s\" (%s)上的服务器,端口%s失败:"
+
+#: fe-connect.c:1742
+msgid "connection to server at \"%s\", port %s failed: "
+msgstr "连接到\"%s\"上的服务器,端口%s失败:"
+
+#: fe-connect.c:1767
+msgid "\tIs the server running locally and accepting connections on that socket?\n"
+msgstr "\t服务器是否在本地运行并接受该套接字上的连接?\n"
+
+#: fe-connect.c:1771
+msgid "\tIs the server running on that host and accepting TCP/IP connections?\n"
+msgstr "\t服务器是否在该主机上运行并接受TCP/IP连接?\n"
+
+#: fe-connect.c:1835
+#, c-format
+msgid "invalid integer value \"%s\" for connection option \"%s\"\n"
+msgstr "连接选项\"%2$s\"的整数值\"%1$s\"无效\n"
+
+#: fe-connect.c:1865 fe-connect.c:1900 fe-connect.c:1936 fe-connect.c:2025
+#: fe-connect.c:2639
+msgid "%s(%s) failed: %s\n"
+msgstr "%s(%s)失败: %s\n"
+
+#: fe-connect.c:1990
+msgid "%s(%s) failed: error code %d\n"
+msgstr "%s(%s) 失败: 错误码为 %d\n"
+
+#: fe-connect.c:2305
+msgid "invalid connection state, probably indicative of memory corruption\n"
+msgstr "无效的联接状态, 可能是存储器数据被破坏的标志\n"
+
+#: fe-connect.c:2384
+#, c-format
+msgid "invalid port number: \"%s\"\n"
+msgstr "无效端口号: \"%s\"\n"
+
+#: fe-connect.c:2400
+#, c-format
+msgid "could not translate host name \"%s\" to address: %s\n"
+msgstr "无法解释主机名 \"%s\" 到地址: %s\n"
+
+#: fe-connect.c:2413
+#, c-format
+msgid "could not parse network address \"%s\": %s\n"
+msgstr "无法分析网络地址\"%s\": %s\n"
+
+#: fe-connect.c:2426
+#, c-format
+msgid "Unix-domain socket path \"%s\" is too long (maximum %d bytes)\n"
+msgstr "Unix域的套接字路径\"%s\"超长(最大为%d字节)\n"
+
+#: fe-connect.c:2441
+#, c-format
+msgid "could not translate Unix-domain socket path \"%s\" to address: %s\n"
+msgstr "无法解释 Unix-domian 套接字路径 \"%s\" 到地址: %s\n"
+
+#: fe-connect.c:2567
+#, c-format
+msgid "could not create socket: %s\n"
+msgstr "无法创建套接字: %s\n"
+
+#: fe-connect.c:2598
+#, c-format
+msgid "could not set socket to nonblocking mode: %s\n"
+msgstr "无法设置套接字为非阻塞模式: %s\n"
+
+#: fe-connect.c:2608
+#, c-format
+msgid "could not set socket to close-on-exec mode: %s\n"
+msgstr "无法将套接字设置为执行时关闭 (close-on-exec) 模式: %s\n"
+
+#: fe-connect.c:2626
+msgid "keepalives parameter must be an integer\n"
+msgstr "参数keepalives必须是一个整数\n"
+
+#: fe-connect.c:2767
+#, c-format
+msgid "could not get socket error status: %s\n"
+msgstr "无法获取套接字错误状态: %s\n"
+
+#: fe-connect.c:2795
+#, c-format
+msgid "could not get client address from socket: %s\n"
+msgstr "无法从套接字获取客户端地址: %s\n"
+
+#: fe-connect.c:2837
+msgid "requirepeer parameter is not supported on this platform\n"
+msgstr "在此平台上不支持requirepeer参数\n"
+
+#: fe-connect.c:2840
+#, c-format
+msgid "could not get peer credentials: %s\n"
+msgstr "无法获得对等(peer)证书:%s\n"
+
+#: fe-connect.c:2864
+#, c-format
+msgid "requirepeer specifies \"%s\", but actual peer user name is \"%s\"\n"
+msgstr "期望对方用户指定值为 \"%s\", 但实际的对方用户名为 \"%s\"\n"
+
+#: fe-connect.c:2904
+#, c-format
+msgid "could not send GSSAPI negotiation packet: %s\n"
+msgstr "无法发送 GSSAPI 握手包: %s\n"
+
+#: fe-connect.c:2916
+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:2958
+#, c-format
+msgid "could not send SSL negotiation packet: %s\n"
+msgstr "无法发送 SSL 握手包: %s\n"
+
+#: fe-connect.c:2989
+#, c-format
+msgid "could not send startup packet: %s\n"
+msgstr "无法发送启动包: %s\n"
+
+#: fe-connect.c:3065
+msgid "server does not support SSL, but SSL was required\n"
+msgstr "服务器不支持 SSL, 但是要求使用 SSL\n"
+
+#: fe-connect.c:3092
+#, c-format
+msgid "received invalid response to SSL negotiation: %c\n"
+msgstr "收到对 SSL 握手的无效响应: %c\n"
+
+#: fe-connect.c:3181
+msgid "server doesn't support GSSAPI encryption, but it was required\n"
+msgstr "服务器不支持 GSSAPI, 但这是必须的\n"
+
+#: fe-connect.c:3193
+#, c-format
+msgid "received invalid response to GSSAPI negotiation: %c\n"
+msgstr "收到对 GSSAPI 握手的无效响应: %c\n"
+
+#: fe-connect.c:3259 fe-connect.c:3284
+#, c-format
+msgid "expected authentication request from server, but received %c\n"
+msgstr "期待来自服务器的认证请求, 却收到 %c\n"
+
+#: fe-connect.c:3491
+msgid "unexpected message from server during startup\n"
+msgstr "启动过程中收到来自服务器的非预期信息\n"
+
+#: fe-connect.c:3583
+msgid "session is read-only\n"
+msgstr "会话是只读的\n"
+
+#: fe-connect.c:3586
+msgid "session is not read-only\n"
+msgstr "会话不是只读的\n"
+
+#: fe-connect.c:3640
+msgid "server is in hot standby mode\n"
+msgstr "服务器处于热备份模式\n"
+
+#: fe-connect.c:3643
+msgid "server is not in hot standby mode\n"
+msgstr "服务器不处于热备份模式\n"
+
+#: fe-connect.c:3754 fe-connect.c:3806
+msgid "\"%s\" failed\n"
+msgstr "\"%s\" 失败\n"
+
+#: fe-connect.c:3820
+#, c-format
+msgid "invalid connection state %d, probably indicative of memory corruption\n"
+msgstr "无效的连接状态 %d, 这可能表示内存出现问题\n"
+
+#: fe-connect.c:4266 fe-connect.c:4326
+#, c-format
+msgid "PGEventProc \"%s\" failed during PGEVT_CONNRESET event\n"
+msgstr "在PGEVT_CONNRESET事件触发期间执行PGEventProc \"%s\"错误\n"
+
+#: fe-connect.c:4670
+#, c-format
+msgid "invalid LDAP URL \"%s\": scheme must be ldap://\n"
+msgstr "无效LDAP URL\"%s\": 模式必须是ldap://\n"
+
+#: fe-connect.c:4685
+#, c-format
+msgid "invalid LDAP URL \"%s\": missing distinguished name\n"
+msgstr "无效LDAP URL \"%s\": 丢失可区分的名称\n"
+
+#: fe-connect.c:4697 fe-connect.c:4755
+#, c-format
+msgid "invalid LDAP URL \"%s\": must have exactly one attribute\n"
+msgstr "无效LDAP URL \"%s\": 只能有一个属性\n"
+
+#: fe-connect.c:4709 fe-connect.c:4771
+#, 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:4721
+#, c-format
+msgid "invalid LDAP URL \"%s\": no filter\n"
+msgstr "无效的 LDAP URL \"%s\": 没有过滤器\n"
+
+#: fe-connect.c:4743
+#, c-format
+msgid "invalid LDAP URL \"%s\": invalid port number\n"
+msgstr "无效LDAP URL \"%s\": 无效端口号\n"
+
+#: fe-connect.c:4781
+msgid "could not create LDAP structure\n"
+msgstr "无法创建LDAP结构\n"
+
+#: fe-connect.c:4857
+#, c-format
+msgid "lookup on LDAP server failed: %s\n"
+msgstr "在LDAP服务器上的查找失败: %s\n"
+
+#: fe-connect.c:4868
+msgid "more than one entry found on LDAP lookup\n"
+msgstr "在LDAP搜索上找到多个入口\n"
+
+#: fe-connect.c:4869 fe-connect.c:4881
+msgid "no entry found on LDAP lookup\n"
+msgstr "在LDAP查找上没有发现入口\n"
+
+#: fe-connect.c:4892 fe-connect.c:4905
+msgid "attribute has no values on LDAP lookup\n"
+msgstr "在LDAP查找上的属性没有值\n"
+
+#: fe-connect.c:4957 fe-connect.c:4976 fe-connect.c:5508
+#, c-format
+msgid "missing \"=\" after \"%s\" in connection info string\n"
+msgstr "在联接信息字串里的 \"%s\" 后面缺少 \"=\"\n"
+
+#: fe-connect.c:5049 fe-connect.c:5693 fe-connect.c:6469
+#, c-format
+msgid "invalid connection option \"%s\"\n"
+msgstr "非法联接选项 \"%s\"\n"
+
+#: fe-connect.c:5065 fe-connect.c:5557
+msgid "unterminated quoted string in connection info string\n"
+msgstr "联接信息字串中未结束的引号字串\n"
+
+#: fe-connect.c:5146
+#, c-format
+msgid "definition of service \"%s\" not found\n"
+msgstr "错误:没有找到服务\"%s\"的定义\n"
+
+#: fe-connect.c:5172
+#, c-format
+msgid "service file \"%s\" not found\n"
+msgstr "错误:没有找到服务文件\"%s\"\n"
+
+#: fe-connect.c:5186
+#, c-format
+msgid "line %d too long in service file \"%s\"\n"
+msgstr "在服务文件\"%2$s\"中的第%1$d行的长度太长\n"
+
+#: fe-connect.c:5257 fe-connect.c:5301
+#, c-format
+msgid "syntax error in service file \"%s\", line %d\n"
+msgstr "在服务文件\"%s\"的第%d行出现语法错误\n"
+
+#: fe-connect.c:5268
+#, c-format
+msgid "nested service specifications not supported in service file \"%s\", line %d\n"
+msgstr "在服务文件\"%s\"的第%d行出现不支持的嵌套服务说明\n"
+
+#: fe-connect.c:5989
+#, c-format
+msgid "invalid URI propagated to internal parser routine: \"%s\"\n"
+msgstr "无效的URI传入内部解析器处理程序: \"%s\"\n"
+
+#: fe-connect.c:6066
+#, 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:6073
+#, c-format
+msgid "IPv6 host address may not be empty in URI: \"%s\"\n"
+msgstr "URI:\"%s\"中的IPv6主机地址可能不为空\n"
+
+#: fe-connect.c:6088
+#, 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:6218
+#, c-format
+msgid "extra key/value separator \"=\" in URI query parameter: \"%s\"\n"
+msgstr "遇到多余的键/值分隔符\"=\"在URI查询参数里: \"%s\"\n"
+
+#: fe-connect.c:6238
+#, c-format
+msgid "missing key/value separator \"=\" in URI query parameter: \"%s\"\n"
+msgstr "缺少相应的键/值分隔符\"=\"在URI查询参数里: \"%s\"\n"
+
+#: fe-connect.c:6290
+#, c-format
+msgid "invalid URI query parameter: \"%s\"\n"
+msgstr "无效的URI查询参数: \"%s\"\n"
+
+#: fe-connect.c:6364
+#, c-format
+msgid "invalid percent-encoded token: \"%s\"\n"
+msgstr "无效的百分号编码令牌: \"%s\"\n"
+
+#: fe-connect.c:6374
+#, c-format
+msgid "forbidden value %%00 in percent-encoded value: \"%s\"\n"
+msgstr "在百分值编码的值: \"%s\"里禁止使用 %%00\n"
+
+#: fe-connect.c:6744
+msgid "connection pointer is NULL\n"
+msgstr "联接指针是 NULL\n"
+
+#: fe-connect.c:7032
+#, c-format
+msgid "WARNING: password file \"%s\" is not a plain file\n"
+msgstr "警告: 口令文件\"%s\"不是普通文本文件\n"
+
+#: fe-connect.c:7041
+#, 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:7149
+#, c-format
+msgid "password retrieved from file \"%s\"\n"
+msgstr "从文件\"%s\"中获取口令\n"
+
+#: fe-exec.c:449 fe-exec.c:3300
+#, c-format
+msgid "row number %d is out of range 0..%d"
+msgstr "行号码 %d 超出了范围 0..%d"
+
+#: fe-exec.c:510 fe-protocol3.c:219 fe-protocol3.c:244 fe-protocol3.c:273
+#: fe-protocol3.c:291 fe-protocol3.c:371 fe-protocol3.c:743
+msgid "out of memory"
+msgstr "内存不足"
+
+#: fe-exec.c:511 fe-protocol3.c:1939
+#, c-format
+msgid "%s"
+msgstr "%s"
+
+#: fe-exec.c:792
+msgid "write to server failed\n"
+msgstr "写入服务器失败\n"
+
+#: fe-exec.c:864
+msgid "NOTICE"
+msgstr "注意"
+
+#: fe-exec.c:922
+msgid "PGresult cannot support more than INT_MAX tuples"
+msgstr "PGresult不能支持超过INT_MAX元组"
+
+#: fe-exec.c:934
+msgid "size_t overflow"
+msgstr "size_t溢出"
+
+#: fe-exec.c:1349 fe-exec.c:1454 fe-exec.c:1503
+msgid "command string is a null pointer\n"
+msgstr "命令字串是一个空指针\n"
+
+#: fe-exec.c:1460 fe-exec.c:1509 fe-exec.c:1605
+msgid "number of parameters must be between 0 and %d\n"
+msgstr "参数的个数必须介于0到%d之间\n"
+
+#: fe-exec.c:1497 fe-exec.c:1599
+msgid "statement name is a null pointer\n"
+msgstr "声明名字是一个空指针\n"
+
+#: fe-exec.c:1641 fe-exec.c:3153
+msgid "no connection to the server\n"
+msgstr "没有到服务器的联接\n"
+
+#: fe-exec.c:1650 fe-exec.c:3162
+msgid "another command is already in progress\n"
+msgstr "已经有另外一条命令在处理\n"
+
+#: fe-exec.c:1679
+msgid "cannot queue commands during COPY\n"
+msgstr "复制期间无法对命令排队\n"
+
+#: fe-exec.c:1797
+msgid "length must be given for binary parameter\n"
+msgstr "对于2进制参数必须指定长度\n"
+
+#: fe-exec.c:2117
+#, c-format
+msgid "unexpected asyncStatus: %d\n"
+msgstr "意外的 asyncStatus(异步状态): %d\n"
+
+#: fe-exec.c:2137
+#, c-format
+msgid "PGEventProc \"%s\" failed during PGEVT_RESULTCREATE event\n"
+msgstr "在PGEVT_CONNRESET事件触发期间执行PGEventProc \"%s\"错误\n"
+
+#: fe-exec.c:2285
+msgid "synchronous command execution functions are not allowed in pipeline mode\n"
+msgstr "管道模式下不允许使用同步命令执行函数\n"
+
+#: fe-exec.c:2307
+msgid "COPY terminated by new PQexec"
+msgstr "COPY 被一个新的 PQexec 终止"
+
+#: fe-exec.c:2324
+msgid "PQexec not allowed during COPY BOTH\n"
+msgstr "在 COPY BOTH时不允许调用PQexec\n"
+
+#: fe-exec.c:2552 fe-exec.c:2608 fe-exec.c:2677 fe-protocol3.c:1870
+msgid "no COPY in progress\n"
+msgstr "没有正在处理的 COPY\n"
+
+#: fe-exec.c:2854
+msgid "PQfn not allowed in pipeline mode\n"
+msgstr "管道模式下不允许使用PQfn\n"
+
+#: fe-exec.c:2862
+msgid "connection in wrong state\n"
+msgstr "联接处于错误状态\n"
+
+#: fe-exec.c:2906
+msgid "cannot enter pipeline mode, connection not idle\n"
+msgstr "无法进入管道模式,连接未空闲\n"
+
+#: fe-exec.c:2940 fe-exec.c:2957
+msgid "cannot exit pipeline mode with uncollected results\n"
+msgstr "无法退出具有未收集结果的管道模式\n"
+
+#: fe-exec.c:2945
+msgid "cannot exit pipeline mode while busy\n"
+msgstr "忙时无法退出管道模式\n"
+
+#: fe-exec.c:3087
+msgid "cannot send pipeline when not in pipeline mode\n"
+msgstr "不处于管道模式时无法发送管道\n"
+
+#: fe-exec.c:3189
+msgid "invalid ExecStatusType code"
+msgstr "非法 ExecStatusType 代码"
+
+#: fe-exec.c:3216
+msgid "PGresult is not an error result\n"
+msgstr "PGresult不是错误的结果\n"
+
+#: fe-exec.c:3284 fe-exec.c:3307
+#, c-format
+msgid "column number %d is out of range 0..%d"
+msgstr "列号码 %d 超出了范围 0..%d"
+
+#: fe-exec.c:3322
+#, c-format
+msgid "parameter number %d is out of range 0..%d"
+msgstr "参数号%d超出了范围 0..%d"
+
+#: fe-exec.c:3632
+#, c-format
+msgid "could not interpret result from server: %s"
+msgstr "无法解释来自服务器的结果: %s"
+
+#: fe-exec.c:3892 fe-exec.c:3981
+msgid "incomplete multibyte character\n"
+msgstr "无效的多字节字符\n"
+
+#: fe-gssapi-common.c:124
+msgid "GSSAPI name import error"
+msgstr "GSSAPI名称导入错误"
+
+#: fe-lobj.c:145 fe-lobj.c:210 fe-lobj.c:403 fe-lobj.c:494 fe-lobj.c:568
+#: fe-lobj.c:969 fe-lobj.c:977 fe-lobj.c:985 fe-lobj.c:993 fe-lobj.c:1001
+#: fe-lobj.c:1009 fe-lobj.c:1017 fe-lobj.c:1025
+msgid "cannot determine OID of function %s\n"
+msgstr "无法判断函数%s的 OID\n"
+
+#: fe-lobj.c:162
+msgid "argument of lo_truncate exceeds integer range\n"
+msgstr "lo_truncate的参数超出整数范围\n"
+
+#: fe-lobj.c:266
+msgid "argument of lo_read exceeds integer range\n"
+msgstr "lo_read的参数值已超出整数范围\n"
+
+#: fe-lobj.c:318
+msgid "argument of lo_write exceeds integer range\n"
+msgstr "lo_write的参数值已超出整数范围\n"
+
+#: fe-lobj.c:678 fe-lobj.c:789
+#, c-format
+msgid "could not open file \"%s\": %s\n"
+msgstr "无法打开文件 \"%s\": %s\n"
+
+#: fe-lobj.c:734
+#, c-format
+msgid "could not read from file \"%s\": %s\n"
+msgstr "无法读取文件 \"%s\": %s\n"
+
+#: fe-lobj.c:810 fe-lobj.c:834
+#, c-format
+msgid "could not write to file \"%s\": %s\n"
+msgstr "无法写入文件 \"%s\": %s\n"
+
+#: fe-lobj.c:920
+msgid "query to initialize large object functions did not return data\n"
+msgstr "初始化大对象函数的查询没有返回数据\n"
+
+#: fe-misc.c:242
+#, c-format
+msgid "integer of size %lu not supported by pqGetInt"
+msgstr "pqGetInt 不支持大小为 %lu 的整数"
+
+#: fe-misc.c:275
+#, c-format
+msgid "integer of size %lu not supported by pqPutInt"
+msgstr "pqPutInt 不支持大小为 %lu 的整数"
+
+#: fe-misc.c:576 fe-misc.c:822
+msgid "connection not open\n"
+msgstr "联接未打开\n"
+
+#: fe-misc.c:755 fe-secure-openssl.c:209 fe-secure-openssl.c:316
+#: fe-secure.c:260 fe-secure.c:373
+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:1015
+msgid "timeout expired\n"
+msgstr "超时满\n"
+
+#: fe-misc.c:1060
+msgid "invalid socket\n"
+msgstr "无效套接字\n"
+
+#: fe-misc.c:1083
+msgid "%s() failed: %s\n"
+msgstr "%s()失败: %s\n"
+
+#: fe-protocol3.c:196
+#, c-format
+msgid "message type 0x%02x arrived from server while idle"
+msgstr "当空闲时收到服务起发送过来的消息类型 0x%02x"
+
+#: fe-protocol3.c:403
+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:446
+#, c-format
+msgid "unexpected response from server; first received character was \"%c\"\n"
+msgstr "来自服务器意外的回执, 第一个收到的字符是 \"%c\"\n"
+
+#: fe-protocol3.c:471
+#, c-format
+msgid "message contents do not agree with length in message type \"%c\"\n"
+msgstr "在消息类型 \"%c\" 中, 消息内容与长度不匹配\n"
+
+#: fe-protocol3.c:491
+#, c-format
+msgid "lost synchronization with server: got message type \"%c\", length %d\n"
+msgstr "失去与服务器同步: 获取到消息类型 \"%c\", 长度 %d\n"
+
+#: fe-protocol3.c:543 fe-protocol3.c:583
+msgid "insufficient data in \"T\" message"
+msgstr "\"T\"消息中剩下的数据不够"
+
+#: fe-protocol3.c:654 fe-protocol3.c:860
+msgid "out of memory for query result"
+msgstr "查询结果时内存耗尽"
+
+#: fe-protocol3.c:723
+msgid "insufficient data in \"t\" message"
+msgstr "\"t\"消息中剩下的数据不够"
+
+#: fe-protocol3.c:782 fe-protocol3.c:814 fe-protocol3.c:832
+msgid "insufficient data in \"D\" message"
+msgstr "\"D\"消息中剩下的数据不够"
+
+#: fe-protocol3.c:788
+msgid "unexpected field count in \"D\" message"
+msgstr "在 \"D\" 消息中, 意外的字段个数"
+
+#: fe-protocol3.c:1036
+msgid "no error message available\n"
+msgstr "没有可用的错误消息\n"
+
+#. translator: %s represents a digit string
+#: fe-protocol3.c:1084 fe-protocol3.c:1103
+#, c-format
+msgid " at character %s"
+msgstr " 在字符 %s"
+
+#: fe-protocol3.c:1116
+#, c-format
+msgid "DETAIL: %s\n"
+msgstr "描述: %s\n"
+
+#: fe-protocol3.c:1119
+#, c-format
+msgid "HINT: %s\n"
+msgstr "提示: %s\n"
+
+#: fe-protocol3.c:1122
+#, c-format
+msgid "QUERY: %s\n"
+msgstr "查询: %s\n"
+
+#: fe-protocol3.c:1129
+#, c-format
+msgid "CONTEXT: %s\n"
+msgstr "背景: %s\n"
+
+#: fe-protocol3.c:1138
+#, c-format
+msgid "SCHEMA NAME: %s\n"
+msgstr "方案名: %s\n"
+
+#: fe-protocol3.c:1142
+#, c-format
+msgid "TABLE NAME: %s\n"
+msgstr "表名: %s\n"
+
+#: fe-protocol3.c:1146
+#, c-format
+msgid "COLUMN NAME: %s\n"
+msgstr "列名: %s\n"
+
+#: fe-protocol3.c:1150
+#, c-format
+msgid "DATATYPE NAME: %s\n"
+msgstr "数据类型名: %s\n"
+
+#: fe-protocol3.c:1154
+#, c-format
+msgid "CONSTRAINT NAME: %s\n"
+msgstr "约束名: %s\n"
+
+#: fe-protocol3.c:1166
+msgid "LOCATION: "
+msgstr "位置: "
+
+#: fe-protocol3.c:1168
+#, c-format
+msgid "%s, "
+msgstr "%s, "
+
+#: fe-protocol3.c:1170
+#, c-format
+msgid "%s:%s"
+msgstr "%s:%s"
+
+#: fe-protocol3.c:1365
+#, c-format
+msgid "LINE %d: "
+msgstr "第%d行"
+
+#: fe-protocol3.c:1764
+msgid "PQgetline: not doing text COPY OUT\n"
+msgstr "PQgetline: not doing text COPY OUT\n"
+
+#: fe-protocol3.c:2130
+#, c-format
+msgid "protocol error: id=0x%x\n"
+msgstr "协议错误: id=0x%x\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:1333
+#, 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:1337
+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:1346
+#, 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:1396
+#, 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:822
+#, c-format
+msgid "could not create SSL context: %s\n"
+msgstr "无法创建 SSL 环境: %s\n"
+
+#: fe-secure-openssl.c:861
+#, c-format
+msgid "invalid value \"%s\" for minimum SSL protocol version\n"
+msgstr "最小SSL协议版本的值\"%s\"无效\n"
+
+#: fe-secure-openssl.c:872
+#, c-format
+msgid "could not set minimum SSL protocol version: %s\n"
+msgstr "无法设置最低SSL协议版本: %s\n"
+
+#: fe-secure-openssl.c:890
+#, c-format
+msgid "invalid value \"%s\" for maximum SSL protocol version\n"
+msgstr "最大SSL协议版本的值\"%s\"无效\n"
+
+#: fe-secure-openssl.c:901
+#, c-format
+msgid "could not set maximum SSL protocol version: %s\n"
+msgstr "无法设置最大SSL协议版本: %s\n"
+
+#: fe-secure-openssl.c:937
+#, c-format
+msgid "could not read root certificate file \"%s\": %s\n"
+msgstr "无法读取根证书文件 \"%s\": %s\n"
+
+#: fe-secure-openssl.c:990
+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:994
+#, 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:1025
+#, c-format
+msgid "could not open certificate file \"%s\": %s\n"
+msgstr "无法打开证书文件 \"%s\": %s\n"
+
+#: fe-secure-openssl.c:1044
+#, c-format
+msgid "could not read certificate file \"%s\": %s\n"
+msgstr "无法读取证书文件 \"%s\": %s\n"
+
+#: fe-secure-openssl.c:1069
+#, c-format
+msgid "could not establish SSL connection: %s\n"
+msgstr "无法建立 SSL 联接: %s\n"
+
+#: fe-secure-openssl.c:1103
+msgid "could not set SSL Server Name Indication (SNI): %s\n"
+msgstr "无法设置SSL服务器名称指示(SNI): %s\n"
+
+#: fe-secure-openssl.c:1149
+#, c-format
+msgid "could not load SSL engine \"%s\": %s\n"
+msgstr "无法加载SSL引擎 \"%s\": %s\n"
+
+#: fe-secure-openssl.c:1161
+#, c-format
+msgid "could not initialize SSL engine \"%s\": %s\n"
+msgstr "无法初始化SSL引擎\"%s\": %s\n"
+
+#: fe-secure-openssl.c:1177
+#, 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:1191
+#, 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:1228
+#, c-format
+msgid "certificate present, but not private key file \"%s\"\n"
+msgstr "有证书, 但不是私钥文件 \"%s\"\n"
+
+#: fe-secure-openssl.c:1236
+#, 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:1261
+#, c-format
+msgid "could not load private key file \"%s\": %s\n"
+msgstr "无法装载私钥文件 \"%s\": %s\n"
+
+#: fe-secure-openssl.c:1279
+#, c-format
+msgid "certificate does not match private key file \"%s\": %s\n"
+msgstr "证书不匹配私钥文件 \"%s\": %s\n"
+
+#: fe-secure-openssl.c:1379
+#, c-format
+msgid "This may indicate that the server does not support any SSL protocol version between %s and %s.\n"
+msgstr "这可能表示服务器不支持%s和%s之间的任何SSL协议版本.\n"
+
+#: fe-secure-openssl.c:1415
+#, c-format
+msgid "certificate could not be obtained: %s\n"
+msgstr "无法获得证书: %s\n"
+
+#: fe-secure-openssl.c:1521
+#, c-format
+msgid "no SSL error reported"
+msgstr "没有报告SSL错误"
+
+#: fe-secure-openssl.c:1530
+#, c-format
+msgid "SSL error code %lu"
+msgstr "SSL错误代码 %lu"
+
+#: fe-secure-openssl.c:1777
+#, c-format
+msgid "WARNING: sslpassword truncated\n"
+msgstr "警告:ssl密码被截断\n"
+
+#: fe-secure.c:267
+#, c-format
+msgid "could not receive data from server: %s\n"
+msgstr "无法从服务器接收数据: %s\n"
+
+#: fe-secure.c:380
+#, 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..a57f753
--- /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-2021, 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..0251095
--- /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-2021, 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..6dbb0a3
--- /dev/null
+++ b/src/interfaces/libpq/pthread-win32.c
@@ -0,0 +1,60 @@
+/*-------------------------------------------------------------------------
+*
+* pthread-win32.c
+* partial pthread implementation for win32
+*
+* Copyright (c) 2004-2021, 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..de705cf
--- /dev/null
+++ b/src/interfaces/libpq/test/regress.pl
@@ -0,0 +1,65 @@
+#!/usr/bin/perl
+
+# Copyright (c) 2021, PostgreSQL Global Development Group
+
+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..84fc52a
--- /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-2021, 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..f6cf9dc
--- /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-2021, 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..fcce1e0
--- /dev/null
+++ b/src/interfaces/libpq/win32.h
@@ -0,0 +1,23 @@
+/*
+ * 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 */
+
+/*
+ * support for handling Windows Socket errors
+ */
+extern const char *winsock_strerror(int err, char *strerrbuf, size_t buflen);
+
+#endif