diff options
Diffstat (limited to 'src/interfaces/libpq')
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, ®evt, passThrough)) + { + conn->nEvents--; + free(conn->events[conn->nEvents].name); + return false; + } + + return true; +} + +/* + * Set some "instance data" for an event within a PGconn. + * Returns nonzero on success, zero on failure. + */ +int +PQsetInstanceData(PGconn *conn, PGEventProc proc, void *data) +{ + int i; + + if (!conn || !proc) + return false; + + for (i = 0; i < conn->nEvents; i++) + { + if (conn->events[i].proc == proc) + { + conn->events[i].data = data; + return true; + } + } + + return false; +} + +/* + * Obtain the "instance data", if any, for the event. + */ +void * +PQinstanceData(const PGconn *conn, PGEventProc proc) +{ + int i; + + if (!conn || !proc) + return NULL; + + for (i = 0; i < conn->nEvents; i++) + { + if (conn->events[i].proc == proc) + return conn->events[i].data; + } + + return NULL; +} + +/* + * Set some "instance data" for an event within a PGresult. + * Returns nonzero on success, zero on failure. + */ +int +PQresultSetInstanceData(PGresult *result, PGEventProc proc, void *data) +{ + int i; + + if (!result || !proc) + return false; + + for (i = 0; i < result->nEvents; i++) + { + if (result->events[i].proc == proc) + { + result->events[i].data = data; + return true; + } + } + + return false; +} + +/* + * Obtain the "instance data", if any, for the event. + */ +void * +PQresultInstanceData(const PGresult *result, PGEventProc proc) +{ + int i; + + if (!result || !proc) + return NULL; + + for (i = 0; i < result->nEvents; i++) + if (result->events[i].proc == proc) + return result->events[i].data; + + return NULL; +} + +/* + * Fire RESULTCREATE events for an application-created PGresult. + * + * The conn argument can be NULL if event procedures won't use it. + */ +int +PQfireResultCreateEvents(PGconn *conn, PGresult *res) +{ + int i; + + if (!res) + return false; + + for (i = 0; i < res->nEvents; i++) + { + if (!res->events[i].resultInitialized) + { + PGEventResultCreate evt; + + evt.conn = conn; + evt.result = res; + if (!res->events[i].proc(PGEVT_RESULTCREATE, &evt, + res->events[i].passThrough)) + return false; + + res->events[i].resultInitialized = true; + } + } + + return true; +} diff --git a/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 |