diff options
| author | Marc G. Fournier <scrappy@hub.org> | 1996-07-09 06:22:35 +0000 |
|---|---|---|
| committer | Marc G. Fournier <scrappy@hub.org> | 1996-07-09 06:22:35 +0000 |
| commit | d31084e9d1118b25fd16580d9d8c2924b5740dff (patch) | |
| tree | 3179e66307d54df9c7b966543550e601eb55e668 /src/backend/libpq | |
| download | postgresql-d31084e9d1118b25fd16580d9d8c2924b5740dff.tar.gz | |
Postgres95 1.01 Distribution - Virgin SourcesPG95-1_01
Diffstat (limited to 'src/backend/libpq')
| -rw-r--r-- | src/backend/libpq/Makefile.inc | 26 | ||||
| -rw-r--r-- | src/backend/libpq/auth.c | 668 | ||||
| -rw-r--r-- | src/backend/libpq/auth.h | 49 | ||||
| -rw-r--r-- | src/backend/libpq/be-dumpdata.c | 323 | ||||
| -rw-r--r-- | src/backend/libpq/be-fsstubs.c | 351 | ||||
| -rw-r--r-- | src/backend/libpq/be-fsstubs.h | 32 | ||||
| -rw-r--r-- | src/backend/libpq/be-pqexec.c | 382 | ||||
| -rw-r--r-- | src/backend/libpq/libpq-be.h | 51 | ||||
| -rw-r--r-- | src/backend/libpq/libpq-fs.h | 119 | ||||
| -rw-r--r-- | src/backend/libpq/libpq.h | 261 | ||||
| -rw-r--r-- | src/backend/libpq/portal.c | 783 | ||||
| -rw-r--r-- | src/backend/libpq/portalbuf.c | 511 | ||||
| -rw-r--r-- | src/backend/libpq/pqcomm.c | 724 | ||||
| -rw-r--r-- | src/backend/libpq/pqcomm.h | 124 | ||||
| -rw-r--r-- | src/backend/libpq/pqpacket.c | 283 | ||||
| -rw-r--r-- | src/backend/libpq/pqsignal.c | 40 | ||||
| -rw-r--r-- | src/backend/libpq/pqsignal.h | 32 |
17 files changed, 4759 insertions, 0 deletions
diff --git a/src/backend/libpq/Makefile.inc b/src/backend/libpq/Makefile.inc new file mode 100644 index 0000000000..67052518d3 --- /dev/null +++ b/src/backend/libpq/Makefile.inc @@ -0,0 +1,26 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for the (backend side) libpq module +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/backend/libpq/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:21:30 scrappy Exp $ +# +#------------------------------------------------------------------------- + +# +# The frontend libpq interfaces to the backend through these files. +# +VPATH:= $(VPATH):$(CURDIR)/libpq + +SRCS_LIBPQ= be-dumpdata.c be-fsstubs.c be-pqexec.c + +# +# These files are shared with the frontend library. +# +SRCS_LIBPQ+= auth.c pqcomm.c portal.c portalbuf.c pqpacket.c pqsignal.c + +HEADERS+= auth.h be-fsstubs.h libpq-be.h libpq-fs.h libpq.h pqcomm.h pqsignal.h diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c new file mode 100644 index 0000000000..7b4437736f --- /dev/null +++ b/src/backend/libpq/auth.c @@ -0,0 +1,668 @@ +/*------------------------------------------------------------------------- + * + * auth.c-- + * Routines to handle network authentication + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/libpq/auth.c,v 1.1.1.1 1996/07/09 06:21:30 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +/* + * INTERFACE ROUTINES + * + * backend (postmaster) routines: + * be_recvauth receive authentication information + * be_setauthsvc do/do not permit an authentication service + * be_getauthsvc is an authentication service permitted? + * + * NOTES + * To add a new authentication system: + * 0. If you can't do your authentication over an existing socket, + * you lose -- get ready to hack around this framework instead of + * using it. Otherwise, you can assume you have an initialized + * and empty connection to work with. (Please don't leave leftover + * gunk in the connection after the authentication transactions, or + * the POSTGRES routines that follow will be very unhappy.) + * 1. Write a set of routines that: + * let a client figure out what user/principal name to use + * send authentication information (client side) + * receive authentication information (server side) + * You can include both routines in this file, using #ifdef FRONTEND + * to separate them. + * 2. Edit libpq/pqcomm.h and assign a MsgType for your protocol. + * 3. Edit the static "struct authsvc" array and the generic + * {be,fe}_{get,set}auth{name,svc} routines in this file to reflect + * the new service. You may have to change the arguments of these + * routines; they basically just reflect what Kerberos v4 needs. + * 4. Hack on src/{,bin}/Makefile.global and src/{backend,libpq}/Makefile + * to add library and CFLAGS hooks -- basically, grep the Makefile + * hierarchy for KRBVERS to see where you need to add things. + * + * Send mail to post_hackers@postgres.Berkeley.EDU if you have to make + * any changes to arguments, etc. Context diffs would be nice, too. + * + * Someday, this cruft will go away and magically be replaced by a + * nice interface based on the GSS API or something. For now, though, + * there's no (stable) UNIX security API to work with... + * + */ +#include <stdio.h> +#include <string.h> +#include <sys/param.h> /* for MAX{HOSTNAME,PATH}LEN, NOFILE */ +#include <pwd.h> +#include <ctype.h> /* isspace() declaration */ + +#include <netinet/in.h> +#include <arpa/inet.h> +#include "libpq/auth.h" +#include "libpq/libpq.h" +#include "libpq/pqcomm.h" +#include "libpq/libpq-be.h" + +/*---------------------------------------------------------------- + * common definitions for generic fe/be routines + *---------------------------------------------------------------- + */ + +struct authsvc { + char name[16]; /* service nickname (for command line) */ + MsgType msgtype; /* startup packet header type */ + int allowed; /* initially allowed (before command line + * option parsing)? + */ +}; + +/* + * Command-line parsing routines use this structure to map nicknames + * onto service types (and the startup packets to use with them). + * + * Programs receiving an authentication request use this structure to + * decide which authentication service types are currently permitted. + * By default, all authentication systems compiled into the system are + * allowed. Unauthenticated connections are disallowed unless there + * isn't any authentication system. + */ +static struct authsvc authsvcs[] = { +#ifdef KRB4 + { "krb4", STARTUP_KRB4_MSG, 1 }, + { "kerberos", STARTUP_KRB4_MSG, 1 }, +#endif /* KRB4 */ +#ifdef KRB5 + { "krb5", STARTUP_KRB5_MSG, 1 }, + { "kerberos", STARTUP_KRB5_MSG, 1 }, +#endif /* KRB5 */ + { UNAUTHNAME, STARTUP_MSG, +#if defined(KRB4) || defined(KRB5) + 0 +#else /* !(KRB4 || KRB5) */ + 1 +#endif /* !(KRB4 || KRB5) */ + } +}; + +static n_authsvcs = sizeof(authsvcs) / sizeof(struct authsvc); + +#ifdef KRB4 +/*---------------------------------------------------------------- + * MIT Kerberos authentication system - protocol version 4 + *---------------------------------------------------------------- + */ + +#include "krb.h" + +#ifdef FRONTEND +/* moves to src/libpq/fe-auth.c */ +#else /* !FRONTEND */ + +/* + * pg_krb4_recvauth -- server routine to receive authentication information + * from the client + * + * Nothing unusual here, except that we compare the username obtained from + * the client's setup packet to the authenticated name. (We have to retain + * the name in the setup packet since we have to retain the ability to handle + * unauthenticated connections.) + */ +static int +pg_krb4_recvauth(int sock, + struct sockaddr_in *laddr, + struct sockaddr_in *raddr, + char *username) +{ + long krbopts = 0; /* one-way authentication */ + KTEXT_ST clttkt; + char instance[INST_SZ]; + AUTH_DAT auth_data; + Key_schedule key_sched; + char version[KRB_SENDAUTH_VLEN]; + int status; + + strcpy(instance, "*"); /* don't care, but arg gets expanded anyway */ + status = krb_recvauth(krbopts, + sock, + &clttkt, + PG_KRB_SRVNAM, + instance, + raddr, + laddr, + &auth_data, + PG_KRB_SRVTAB, + key_sched, + version); + if (status != KSUCCESS) { + (void) sprintf(PQerrormsg, + "pg_krb4_recvauth: kerberos error: %s\n", + krb_err_txt[status]); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + return(STATUS_ERROR); + } + if (strncmp(version, PG_KRB4_VERSION, KRB_SENDAUTH_VLEN)) { + (void) sprintf(PQerrormsg, + "pg_krb4_recvauth: protocol version != \"%s\"\n", + PG_KRB4_VERSION); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + return(STATUS_ERROR); + } + if (username && *username && + strncmp(username, auth_data.pname, NAMEDATALEN)) { + (void) sprintf(PQerrormsg, + "pg_krb4_recvauth: name \"%s\" != \"%s\"\n", + username, + auth_data.pname); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + return(STATUS_ERROR); + } + return(STATUS_OK); +} + +#endif /* !FRONTEND */ + +#endif /* KRB4 */ + +#ifdef KRB5 +/*---------------------------------------------------------------- + * MIT Kerberos authentication system - protocol version 5 + *---------------------------------------------------------------- + */ + +#include "krb5/krb5.h" + +/* + * pg_an_to_ln -- return the local name corresponding to an authentication + * name + * + * XXX Assumes that the first aname component is the user name. This is NOT + * necessarily so, since an aname can actually be something out of your + * worst X.400 nightmare, like + * ORGANIZATION=U. C. Berkeley/NAME=Paul M. Aoki@CS.BERKELEY.EDU + * Note that the MIT an_to_ln code does the same thing if you don't + * provide an aname mapping database...it may be a better idea to use + * krb5_an_to_ln, except that it punts if multiple components are found, + * and we can't afford to punt. + */ +static char * +pg_an_to_ln(char *aname) +{ + char *p; + + if ((p = strchr(aname, '/')) || (p = strchr(aname, '@'))) + *p = '\0'; + return(aname); +} + +#ifdef FRONTEND +/* moves to src/libpq/fe-auth.c */ +#else /* !FRONTEND */ + +/* + * pg_krb4_recvauth -- server routine to receive authentication information + * from the client + * + * We still need to compare the username obtained from the client's setup + * packet to the authenticated name, as described in pg_krb4_recvauth. This + * is a bit more problematic in v5, as described above in pg_an_to_ln. + * + * In addition, as described above in pg_krb5_sendauth, we still need to + * canonicalize the server name v4-style before constructing a principal + * from it. Again, this is kind of iffy. + * + * Finally, we need to tangle with the fact that v5 doesn't let you explicitly + * set server keytab file names -- you have to feed lower-level routines a + * function to retrieve the contents of a keytab, along with a single argument + * that allows them to open the keytab. We assume that a server keytab is + * always a real file so we can allow people to specify their own filenames. + * (This is important because the POSTGRES keytab needs to be readable by + * non-root users/groups; the v4 tools used to force you do dump a whole + * host's worth of keys into a file, effectively forcing you to use one file, + * but kdb5_edit allows you to select which principals to dump. Yay!) + */ +static int +pg_krb5_recvauth(int sock, + struct sockaddr_in *laddr, + struct sockaddr_in *raddr, + char *username) +{ + char servbuf[MAXHOSTNAMELEN + 1 + + sizeof(PG_KRB_SRVNAM)]; + char *hostp, *kusername = (char *) NULL; + krb5_error_code code; + krb5_principal client, server; + krb5_address sender_addr; + krb5_rdreq_key_proc keyproc = (krb5_rdreq_key_proc) NULL; + krb5_pointer keyprocarg = (krb5_pointer) NULL; + + /* + * Set up server side -- since we have no ticket file to make this + * easy, we construct our own name and parse it. See note on + * canonicalization above. + */ + (void) strcpy(servbuf, PG_KRB_SRVNAM); + *(hostp = servbuf + (sizeof(PG_KRB_SRVNAM) - 1)) = '/'; + if (gethostname(++hostp, MAXHOSTNAMELEN) < 0) + (void) strcpy(hostp, "localhost"); + if (hostp = strchr(hostp, '.')) + *hostp = '\0'; + if (code = krb5_parse_name(servbuf, &server)) { + (void) sprintf(PQerrormsg, + "pg_krb5_recvauth: Kerberos error %d in krb5_parse_name\n", + code); + com_err("pg_krb5_recvauth", code, "in krb5_parse_name"); + return(STATUS_ERROR); + } + + /* + * krb5_sendauth needs this to verify the address in the client + * authenticator. + */ + sender_addr.addrtype = raddr->sin_family; + sender_addr.length = sizeof(raddr->sin_addr); + sender_addr.contents = (krb5_octet *) &(raddr->sin_addr); + + if (strcmp(PG_KRB_SRVTAB, "")) { + keyproc = krb5_kt_read_service_key; + keyprocarg = PG_KRB_SRVTAB; + } + + if (code = krb5_recvauth((krb5_pointer) &sock, + PG_KRB5_VERSION, + server, + &sender_addr, + (krb5_pointer) NULL, + keyproc, + keyprocarg, + (char *) NULL, + (krb5_int32 *) NULL, + &client, + (krb5_ticket **) NULL, + (krb5_authenticator **) NULL)) { + (void) sprintf(PQerrormsg, + "pg_krb5_recvauth: Kerberos error %d in krb5_recvauth\n", + code); + com_err("pg_krb5_recvauth", code, "in krb5_recvauth"); + krb5_free_principal(server); + return(STATUS_ERROR); + } + krb5_free_principal(server); + + /* + * The "client" structure comes out of the ticket and is therefore + * authenticated. Use it to check the username obtained from the + * postmaster startup packet. + */ + if ((code = krb5_unparse_name(client, &kusername))) { + (void) sprintf(PQerrormsg, + "pg_krb5_recvauth: Kerberos error %d in krb5_unparse_name\n", + code); + com_err("pg_krb5_recvauth", code, "in krb5_unparse_name"); + krb5_free_principal(client); + return(STATUS_ERROR); + } + krb5_free_principal(client); + if (!kusername) { + (void) sprintf(PQerrormsg, + "pg_krb5_recvauth: could not decode username\n"); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + return(STATUS_ERROR); + } + kusername = pg_an_to_ln(kusername); + if (username && strncmp(username, kusername, NAMEDATALEN)) { + (void) sprintf(PQerrormsg, + "pg_krb5_recvauth: name \"%s\" != \"%s\"\n", + username, kusername); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + free(kusername); + return(STATUS_ERROR); + } + free(kusername); + return(STATUS_OK); +} + +#endif /* !FRONTEND */ + +#endif /* KRB5 */ + + +/*---------------------------------------------------------------- + * host based authentication + *---------------------------------------------------------------- + * based on the securelib package originally written by William + * LeFebvre, EECS Department, Northwestern University + * (phil@eecs.nwu.edu) - orginal configuration file code handling + * by Sam Horrocks (sam@ics.uci.edu) + * + * modified and adapted for use with Postgres95 by Paul Fisher + * (pnfisher@unity.ncsu.edu) + */ + +#define CONF_FILE "pg_hba" /* Name of the config file */ + +#define MAX_LINES 255 /* Maximum number of config lines * + * that can apply to one database */ + +#define ALL_NAME "all" /* Name used in config file for * + * lines that apply to all databases */ + +#define MAX_TOKEN 80 /* Maximum size of one token in the * + * configuration file */ + +struct conf_line { /* Info about config file line */ + u_long adr, mask; +}; + +static int next_token(FILE *, char *, int); + +/* hba_recvauth */ +/* check for host-based authentication */ +/* + * hba_recvauth - check the sockaddr_in "addr" to see if it corresponds + * to an acceptable host for the database that's being + * connected to. Return STATUS_OK if acceptable, + * otherwise return STATUS_ERROR. + */ + +static int +hba_recvauth(struct sockaddr_in *addr, PacketBuf *pbuf, StartupInfo *sp) +{ + u_long ip_addr; + static struct conf_line conf[MAX_LINES]; + static int nconf; + int i; + + char buf[MAX_TOKEN]; + FILE *file; + + char *conf_file; + + /* put together the full pathname to the config file */ + conf_file = (char *) malloc((strlen(GetPGData())+strlen(CONF_FILE)+2)*sizeof(char)); + strcpy(conf_file, GetPGData()); + strcat(conf_file, "/"); + strcat(conf_file, CONF_FILE); + + + /* Open the config file. */ + file = fopen(conf_file, "r"); + if (file) + { + free(conf_file); + nconf = 0; + + /* Grab the "name" */ + while ((i = next_token(file, buf, sizeof(buf))) != EOF) + { + /* If only token on the line, ignore */ + if (i == '\n') continue; + + /* Comment -- read until end of line then next line */ + if (buf[0] == '#') + { + while (next_token(file, buf, sizeof(buf)) == 0) ; + continue; + } + + /* + * Check to make sure this says "all" or that it matches + * the database name. + */ + + if (strcmp(buf, ALL_NAME) == 0 || (strcmp(buf, sp->database) == 0)) + { + /* Get next token, if last on line, ignore */ + if (next_token(file, buf, sizeof(buf)) != 0) + continue; + + /* Got address */ + conf[nconf].adr = inet_addr(buf); + + /* Get next token (mask) */ + i = next_token(file, buf, sizeof(buf)); + + /* Only ignore if we got no text at all */ + if (i != EOF) + { + /* Add to list, quit if array is full */ + conf[nconf++].mask = inet_addr(buf); + if (nconf == MAX_LINES) break; + } + + /* If not at end-of-line, keep reading til we are */ + while (i == 0) + i = next_token(file, buf, sizeof(buf)); + } + } + fclose(file); + } + else + { (void) sprintf(PQerrormsg, + "hba_recvauth: config file does not exist or permissions are not setup correctly!\n"); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + free(conf_file); + return(STATUS_ERROR); + } + + + /* Config lines now in memory so start checking address */ + /* grab just the address */ + ip_addr = addr->sin_addr.s_addr; + + /* + * Go through the conf array, turn off the bits given by the mask + * and then compare the result with the address. A match means + * that this address is ok. + */ + for (i = 0; i < nconf; ++i) + if ((ip_addr & ~conf[i].mask) == conf[i].adr) return(STATUS_OK); + + /* no match, so we can't approve the address */ + return(STATUS_ERROR); +} + +/* + * Grab one token out of fp. Defined as the next string of non-whitespace + * in the file. After we get the token, continue reading until EOF, end of + * line or the next token. If it's the last token on the line, return '\n' + * for the value. If we get EOF before reading a token, return EOF. In all + * other cases return 0. + */ +static int +next_token(FILE *fp, char *buf, int bufsz) +{ + int c; + char *eb = buf+(bufsz-1); + + /* Discard inital whitespace */ + while (isspace(c = getc(fp))) ; + + /* EOF seen before any token so return EOF */ + if (c == EOF) return -1; + + /* Form a token in buf */ + do { + if (buf < eb) *buf++ = c; + c = getc(fp); + } while (!isspace(c) && c != EOF); + *buf = '\0'; + + /* Discard trailing tabs and spaces */ + while (c == ' ' || c == '\t') c = getc(fp); + + /* Put back the char that was non-whitespace (putting back EOF is ok) */ + (void) ungetc(c, fp); + + /* If we ended with a newline, return that, otherwise return 0 */ + return (c == '\n' ? '\n' : 0); +} + +/* + * be_recvauth -- server demux routine for incoming authentication information + */ +int +be_recvauth(MsgType msgtype, Port *port, char *username, StartupInfo* sp) +{ + if (!username) { + (void) sprintf(PQerrormsg, + "be_recvauth: no user name passed\n"); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + return(STATUS_ERROR); + } + if (!port) { + (void) sprintf(PQerrormsg, + "be_recvauth: no port structure passed\n"); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + return(STATUS_ERROR); + } + + switch (msgtype) { +#ifdef KRB4 + case STARTUP_KRB4_MSG: + if (!be_getauthsvc(msgtype)) { + (void) sprintf(PQerrormsg, + "be_recvauth: krb4 authentication disallowed\n"); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + return(STATUS_ERROR); + } + if (pg_krb4_recvauth(port->sock, &port->laddr, &port->raddr, + username) != STATUS_OK) { + (void) sprintf(PQerrormsg, + "be_recvauth: krb4 authentication failed\n"); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + return(STATUS_ERROR); + } + break; +#endif +#ifdef KRB5 + case STARTUP_KRB5_MSG: + if (!be_getauthsvc(msgtype)) { + (void) sprintf(PQerrormsg, + "be_recvauth: krb5 authentication disallowed\n"); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + return(STATUS_ERROR); + } + if (pg_krb5_recvauth(port->sock, &port->laddr, &port->raddr, + username) != STATUS_OK) { + (void) sprintf(PQerrormsg, + "be_recvauth: krb5 authentication failed\n"); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + return(STATUS_ERROR); + } + break; +#endif + case STARTUP_MSG: + if (!be_getauthsvc(msgtype)) { + (void) sprintf(PQerrormsg, + "be_recvauth: unauthenticated connections disallowed failed\n"); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + return(STATUS_ERROR); + } + break; + case STARTUP_HBA_MSG: + if (hba_recvauth(&port->raddr, &port->buf, sp) != STATUS_OK) { + (void) sprintf(PQerrormsg, + "be_recvauth: host-based authentication failed\n"); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + return(STATUS_ERROR); + } + break; + default: + (void) sprintf(PQerrormsg, + "be_recvauth: unrecognized message type: %d\n", + msgtype); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + return(STATUS_ERROR); + } + return(STATUS_OK); +} + +/* + * be_setauthsvc -- enable/disable the authentication services currently + * selected for use by the backend + * be_getauthsvc -- returns whether a particular authentication system + * (indicated by its message type) is permitted by the + * current selections + * + * be_setauthsvc encodes the command-line syntax that + * -a "<service-name>" + * enables a service, whereas + * -a "no<service-name>" + * disables it. + */ +void +be_setauthsvc(char *name) +{ + int i, j; + int turnon = 1; + + if (!name) + return; + if (!strncmp("no", name, 2)) { + turnon = 0; + name += 2; + } + if (name[0] == '\0') + return; + for (i = 0; i < n_authsvcs; ++i) + if (!strcmp(name, authsvcs[i].name)) { + for (j = 0; j < n_authsvcs; ++j) + if (authsvcs[j].msgtype == authsvcs[i].msgtype) + authsvcs[j].allowed = turnon; + break; + } + if (i == n_authsvcs) { + (void) sprintf(PQerrormsg, + "be_setauthsvc: invalid name %s, ignoring...\n", + name); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + } + return; +} + +int +be_getauthsvc(MsgType msgtype) +{ + int i; + + for (i = 0; i < n_authsvcs; ++i) + if (msgtype == authsvcs[i].msgtype) + return(authsvcs[i].allowed); + return(0); +} diff --git a/src/backend/libpq/auth.h b/src/backend/libpq/auth.h new file mode 100644 index 0000000000..adda8dc13c --- /dev/null +++ b/src/backend/libpq/auth.h @@ -0,0 +1,49 @@ +/*------------------------------------------------------------------------- + * + * auth.h-- + * Definitions for network authentication routines + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: auth.h,v 1.1.1.1 1996/07/09 06:21:30 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef AUTH_H +#define AUTH_H + +#include "c.h" +#include "libpq/pqcomm.h" + +/*---------------------------------------------------------------- + * Common routines and definitions + *---------------------------------------------------------------- + */ + +/* what we call "no authentication system" */ +#define UNAUTHNAME "unauth" + +/* what a frontend uses by default */ +#if !defined(KRB4) && !defined(KRB5) +#define DEFAULT_CLIENT_AUTHSVC UNAUTHNAME +#else /* KRB4 || KRB5 */ +#define DEFAULT_CLIENT_AUTHSVC "kerberos" +#endif /* KRB4 || KRB5 */ + +extern int fe_sendauth(MsgType msgtype, Port *port, char *hostname); +extern void fe_setauthsvc(char *name); +extern MsgType fe_getauthsvc(); +extern char *fe_getauthname(void); +extern int be_recvauth(MsgType msgtype, Port *port, char *username, StartupInfo* sp); +extern void be_setauthsvc(char *name); +extern int be_getauthsvc(MsgType msgtype); + +/* the value that matches any dbName value when doing + host based authentication*/ +#define ALL_DBNAME "*" + +#define PG_KRB4_VERSION "PGVER4.1" /* at most KRB_SENDAUTH_VLEN chars */ +#define PG_KRB5_VERSION "PGVER5.1" + +#endif /* AUTH_H */ diff --git a/src/backend/libpq/be-dumpdata.c b/src/backend/libpq/be-dumpdata.c new file mode 100644 index 0000000000..fb6b90c149 --- /dev/null +++ b/src/backend/libpq/be-dumpdata.c @@ -0,0 +1,323 @@ +/*------------------------------------------------------------------------- + * + * be-dumpdata.c-- + * support for collection of returned tuples from an internal + * PQ call into a backend buffer. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/libpq/Attic/be-dumpdata.c,v 1.1.1.1 1996/07/09 06:21:30 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +/* + * INTERFACE ROUTINES + * be_portalinit - initialize backend portal administration + * be_portalpush - add a portal to the top of the portal stack + * be_portalpop - remove portal on the top of the stack & return it + * be_currentportal - return the top portal on the portal stack + * be_newportal - return a new portal. + * be_portalinit - initialize backend portal expected to hold results. + * be_printtup - add a tuple to a backend portal + * + * NOTES + * Since backend user-defined operators can call queries + * which in turn call user-defined operators can call queries... + * we have to keep track of portals on a stack. BeginCommand() + * puts portals on the stack and the PQ functions remove them. + * + */ +#include "postgres.h" + +#include "lib/dllist.h" +#include "libpq/libpq-be.h" + +#include "access/heapam.h" +#include "access/htup.h" +#include "storage/buf.h" +#include "utils/memutils.h" +#include "utils/palloc.h" +#include "fmgr.h" +#include "utils/mcxt.h" +#include "utils/elog.h" +#include "utils/exc.h" + +#include "utils/syscache.h" +#include "catalog/pg_type.h" +#include "catalog/catalog.h" +#include "access/printtup.h" + +/* ---------------- + * backend portal stack for recursive PQexec calls + * ---------------- + */ +static Dllist *be_portalstack; + +/* ---------------- + * be_portalinit - initialize backend portal administration + * + * This is called once from InitPostgres() to initialize + * the portal stack. + * ---------------- + */ +void +be_portalinit() +{ + be_portalstack = DLNewList(); +} + +/* ---------------- + * be_portalpush - add a portal to the top of the portal stack + * + * used by BeginCommand() + * ---------------- + */ +void +be_portalpush(PortalEntry *entry) +{ + DLAddTail(be_portalstack, DLNewElem(entry)); +} + +/* ---------------- + * be_portalpop - remove the portal on the top of the stack & return it + * + * used by PQexec() + * ---------------- + */ +PortalEntry * +be_portalpop() +{ + PortalEntry *p; + Dlelem* elt; + elt = DLRemTail(be_portalstack); + + p = (elt ? (PortalEntry*)DLE_VAL(elt) : NULL); + DLFreeElem(elt); + return p; + + +} + +/* ---------------- + * be_currentportal - return the top portal on the portal stack + * + * used by be_printtup() + * ---------------- + */ +PortalEntry * +be_currentportal() +{ + Dlelem* elt; + elt = DLGetTail(be_portalstack); + return (elt ? (PortalEntry*)DLE_VAL(elt) : NULL); +} + +/* ---------------- + * be_newportal - return a new portal. + * + * If the user-defined function does not specify a portal name, + * we generate a unique one. Names are generated from a combination + * of a postgres oid and an integer counter which is incremented + * every time we ask for a local portal. + * + * used by BeginCommand() + * ---------------- + */ + +static Oid be_portaloid; +static u_int be_portalcnt = 0; + +PortalEntry * +be_newportal() +{ + PortalEntry *entry; + char buf[PortalNameLength]; + + /* ---------------- + * generate a new name + * ---------------- + */ + if (be_portalcnt == 0) + be_portaloid = newoid(); + be_portalcnt++; + sprintf(buf, "be_%d_%d", be_portaloid, be_portalcnt); + + /* ---------------- + * initialize the new portal entry and keep track + * of the current memory context for be_printtup(). + * This is important - otherwise whatever we allocate + * will go away and the contents of the portal after + * PQexec() returns will be meaningless. + * ---------------- + */ + entry = pbuf_setup(buf); + entry->portalcxt = (Pointer) CurrentMemoryContext; + + return entry; +} + +/* ---------------- + * be_typeinit - initialize backend portal expected to hold + * query results. + * + * used by BeginCommand() + * ---------------- + */ +void +be_typeinit(PortalEntry *entry, + TupleDesc tupDesc, + int natts) +{ + PortalBuffer *portal; + GroupBuffer *group; + int i; + AttributeTupleForm *attrs = tupDesc->attrs; + + /* ---------------- + * add a new portal group to the portal + * ---------------- + */ + portal = entry->portal; + portal->no_groups++; + portal->groups = group = pbuf_addGroup(portal); + group->no_fields = natts; + + /* ---------------- + * initialize portal group type info + * ---------------- + */ + if (natts > 0) { + group->types = pbuf_addTypes(natts); + for (i = 0; i < natts; ++i) { + strncpy(group->types[i].name, attrs[i]->attname.data, NAMEDATALEN); + group->types[i].adtid = attrs[i]->atttypid; + group->types[i].adtsize = attrs[i]->attlen; + } + } +} + +/* ---------------- + * be_printtup - add a tuple to a backend portal + * + * used indirectly by ExecRetrieve() + * + * This code is pretty much copied from printtup(), dump_type() + * and dump_data(). -cim 2/12/91 + * ---------------- + */ +void +be_printtup(HeapTuple tuple, TupleDesc typeinfo) +{ + int i; + char *attr; + bool isnull; + Oid typoutput; + + PortalEntry *entry = NULL; + PortalBuffer *portal = NULL; + GroupBuffer *group = NULL ; + TupleBlock *tuples = NULL; + char **values; + int *lengths; + + MemoryContext savecxt; + + /* ---------------- + * get the current portal and group + * ---------------- + */ + entry = be_currentportal(); + portal = entry->portal; + group = portal->groups; + + /* ---------------- + * switch to the portal's memory context so that + * the tuples we allocate are returned to the user. + * ---------------- + */ + savecxt = MemoryContextSwitchTo((MemoryContext)entry->portalcxt); + + /* ---------------- + * If no tuple block yet, allocate one. + * If the current block is full, allocate another one. + * ---------------- + */ + if (group->tuples == NULL) { + tuples = group->tuples = pbuf_addTuples(); + tuples->tuple_index = 0; + } else { + tuples = group->tuples; + /* walk to the end of the linked list of TupleBlocks */ + while (tuples->next) + tuples = tuples->next; + /* now, tuples is the last TupleBlock, check to see if it is full. + If so, allocate a new TupleBlock and add it to the end of + the chain */ + + if (tuples->tuple_index == TupleBlockSize) { + tuples->next = pbuf_addTuples(); + tuples = tuples->next; + tuples->tuple_index = 0; + } + } + + /* ---------------- + * Allocate space for a tuple. + * ---------------- + */ + tuples->values[tuples->tuple_index] = pbuf_addTuple(tuple->t_natts); + tuples->lengths[tuples->tuple_index] = pbuf_addTupleValueLengths(tuple->t_natts); + /* ---------------- + * copy printable representations of the tuple's attributes + * to the portal. + * + * This seems silly, because the user's function which is calling + * PQexec() or PQfn() will probably just convert this back into the + * internal form anyways, but the point here is to provide a uniform + * libpq interface and this is how the fe libpq interface currently + * works. Pretty soon we'll have to add code to let the fe or be + * select the desired data representation and then deal with that. + * This should not be too hard, as there already exist typrecieve() + * and typsend() procedures for user-defined types (see pg_type.h) + * -cim 2/11/91 + * ---------------- + */ + + values = tuples->values[tuples->tuple_index]; + lengths = tuples->lengths[tuples->tuple_index]; + + for (i = 0; i < tuple->t_natts; i++) { + attr = heap_getattr(tuple, InvalidBuffer, i+1, typeinfo, &isnull); + typoutput = typtoout((Oid) typeinfo->attrs[i]->atttypid); + + lengths[i] = typeinfo->attrs[i]->attlen; + + if (lengths[i] == -1) /* variable length attribute */ + if (!isnull) + lengths[i] = VARSIZE(attr)-VARHDRSZ; + else + lengths[i] = 0; + + if (!isnull && OidIsValid(typoutput)) { + values[i] = fmgr(typoutput, attr, gettypelem(typeinfo->attrs[i]->atttypid)); + } else + values[i] = NULL; + + } + + /* ---------------- + * increment tuple group counters + * ---------------- + */ + portal->no_tuples++; + group->no_tuples++; + tuples->tuple_index++; + + /* ---------------- + * return to the original memory context + * ---------------- + */ + MemoryContextSwitchTo(savecxt); +} diff --git a/src/backend/libpq/be-fsstubs.c b/src/backend/libpq/be-fsstubs.c new file mode 100644 index 0000000000..e32cd3b474 --- /dev/null +++ b/src/backend/libpq/be-fsstubs.c @@ -0,0 +1,351 @@ +/*------------------------------------------------------------------------- + * + * be-fsstubs.c-- + * support for filesystem operations on large objects + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/libpq/be-fsstubs.c,v 1.1.1.1 1996/07/09 06:21:30 scrappy Exp $ + * + * NOTES + * This should be moved to a more appropriate place. It is here + * for lack of a better place. + * + * Builtin functions for open/close/read/write operations on large objects. + * + * These functions operate in the current portal variable context, which + * means the large object descriptors hang around between transactions and + * are not deallocated until explicitly closed, or until the portal is + * closed. + *------------------------------------------------------------------------- + */ +#include "postgres.h" +#include "lib/dllist.h" +#include "libpq/libpq.h" +#include "libpq/libpq-fs.h" +#include "utils/mcxt.h" +#include "utils/palloc.h" + +#include "storage/fd.h" /* for O_ */ +#include "storage/large_object.h" + +#include "utils/elog.h" +#include "libpq/be-fsstubs.h" + +/*#define FSDB 1*/ +#define MAX_LOBJ_FDS 256 + +static LargeObjectDesc *cookies[MAX_LOBJ_FDS]; + +static GlobalMemory fscxt = NULL; + + +static int newLOfd(LargeObjectDesc *lobjCookie); +static void deleteLOfd(int fd); + + +/***************************************************************************** + * File Interfaces for Large Objects + *****************************************************************************/ + +int +lo_open(Oid lobjId, int mode) +{ + LargeObjectDesc *lobjDesc; + int fd; + MemoryContext currentContext; + +#if FSDB + elog(NOTICE,"LOopen(%d,%d)",lobjId,mode); +#endif + + if (fscxt == NULL) { + fscxt = CreateGlobalMemory("Filesystem"); + } + currentContext = MemoryContextSwitchTo((MemoryContext)fscxt); + + lobjDesc = inv_open(lobjId, mode); + + if (lobjDesc == NULL) { /* lookup failed */ + MemoryContextSwitchTo(currentContext); +#if FSDB + elog(NOTICE,"cannot open large object %d", lobjId); +#endif + return -1; + } + + fd = newLOfd(lobjDesc); + + /* switch context back to orig. */ + MemoryContextSwitchTo(currentContext); + + return fd; +} + +int +lo_close(int fd) +{ + MemoryContext currentContext; + + if (fd >= MAX_LOBJ_FDS) { + elog(WARN,"lo_close: large obj descriptor (%d) out of range", fd); + return -2; + } + if (cookies[fd] == NULL) { + elog(WARN,"lo_close: invalid large obj descriptor (%d)", fd); + return -3; + } +#if FSDB + elog(NOTICE,"LOclose(%d)",fd); +#endif + + Assert(fscxt != NULL); + currentContext = MemoryContextSwitchTo((MemoryContext)fscxt); + + inv_close(cookies[fd]); + + MemoryContextSwitchTo(currentContext); + + deleteLOfd(fd); + return 0; +} + +/* + * We assume the large object supports byte oriented reads and seeks so + * that our work is easier. + */ +int +lo_read(int fd, char *buf, int len) +{ + Assert(cookies[fd]!=NULL); + return inv_read(cookies[fd], buf, len); +} + +int +lo_write(int fd, char *buf, int len) +{ + Assert(cookies[fd]!=NULL); + return inv_write(cookies[fd], buf, len); +} + + +int +lo_lseek(int fd, int offset, int whence) +{ + if (fd >= MAX_LOBJ_FDS) { + elog(WARN,"lo_seek: large obj descriptor (%d) out of range", fd); + return -2; + } + return inv_seek(cookies[fd], offset, whence); +} + +Oid +lo_creat(int mode) +{ + LargeObjectDesc *lobjDesc; + MemoryContext currentContext; + Oid lobjId; + + if (fscxt == NULL) { + fscxt = CreateGlobalMemory("Filesystem"); + } + + currentContext = MemoryContextSwitchTo((MemoryContext)fscxt); + + lobjDesc = inv_create(mode); + + if (lobjDesc == NULL) { + MemoryContextSwitchTo(currentContext); + return InvalidOid; + } + + lobjId = lobjDesc->heap_r->rd_id; + + inv_close(lobjDesc); + + /* switch context back to original memory context */ + MemoryContextSwitchTo(currentContext); + + return lobjId; +} + +int +lo_tell(int fd) +{ + if (fd >= MAX_LOBJ_FDS) { + elog(WARN,"lo_tell: large object descriptor (%d) out of range",fd); + return -2; + } + if (cookies[fd] == NULL) { + elog(WARN,"lo_tell: invalid large object descriptor (%d)",fd); + return -3; + } + return inv_tell(cookies[fd]); +} + +int +lo_unlink(Oid lobjId) +{ + return (inv_destroy(lobjId)); +} + +/***************************************************************************** + * Read/Write using varlena + *****************************************************************************/ + +struct varlena * +LOread(int fd, int len) +{ + struct varlena *retval; + int totalread = 0; + + retval = (struct varlena *)palloc(sizeof(int32) + len); + totalread = lo_read(fd, VARDATA(retval), len); + VARSIZE(retval) = totalread + sizeof(int32); + + return retval; +} + +int LOwrite(int fd, struct varlena *wbuf) +{ + int totalwritten; + int bytestowrite; + + bytestowrite = VARSIZE(wbuf) - sizeof(int32); + totalwritten = lo_write(fd, VARDATA(wbuf), bytestowrite); + return totalwritten; +} + +/***************************************************************************** + * Import/Export of Large Object + *****************************************************************************/ + +/* + * lo_import - + * imports a file as an (inversion) large object. + */ +Oid +lo_import(text *filename) +{ + int fd; + int nbytes, tmp; +#define BUFSIZE 1024 + char buf[BUFSIZE]; + LargeObjectDesc *lobj; + Oid lobjOid; + + /* + * open the file to be read in + */ + fd = open(VARDATA(filename), O_RDONLY, 0666); + if (fd < 0) { /* error */ + elog(WARN, "lo_import: can't open unix file\"%s\"\n", filename); + } + + /* + * create an inversion "object" + */ + lobj = inv_create(INV_READ|INV_WRITE); + if (lobj == NULL) { + elog(WARN, "lo_import: can't create inv object for \"%s\"", + VARDATA(filename)); + } + + /* + * the oid for the large object is just the oid of the relation + * XInv??? which contains the data. + */ + lobjOid = lobj->heap_r->rd_id; + + /* + * read in from the Unix file and write to the inversion file + */ + while ((nbytes = read(fd, buf, BUFSIZE)) > 0) { + tmp = inv_write(lobj, buf, nbytes); + if (tmp < nbytes) { + elog(WARN, "lo_import: error while reading \"%s\"", + VARDATA(filename)); + } + } + + (void) close(fd); + (void) inv_close(lobj); + + return lobjOid; +} + +/* + * lo_export - + * exports an (inversion) large object. + */ +int4 +lo_export(Oid lobjId, text *filename) +{ + int fd; + int nbytes, tmp; +#define BUFSIZE 1024 + char buf[BUFSIZE]; + LargeObjectDesc *lobj; + + /* + * create an inversion "object" + */ + lobj = inv_open(lobjId, INV_READ); + if (lobj == NULL) { + elog(WARN, "lo_export: can't open inv object %d", + lobjId); + } + + /* + * open the file to be written to + */ + fd = open(VARDATA(filename), O_CREAT|O_WRONLY, 0666); + if (fd < 0) { /* error */ + elog(WARN, "lo_export: can't open unix file\"%s\"", + VARDATA(filename)); + } + + /* + * read in from the Unix file and write to the inversion file + */ + while ((nbytes = inv_read(lobj, buf, BUFSIZE)) > 0) { + tmp = write(fd, buf, nbytes); + if (tmp < nbytes) { + elog(WARN, "lo_export: error while writing \"%s\"", + VARDATA(filename)); + } + } + + (void) inv_close(lobj); + (void) close(fd); + + return 1; +} + + +/***************************************************************************** + * Support routines for this file + *****************************************************************************/ + +static int +newLOfd(LargeObjectDesc *lobjCookie) +{ + int i; + + for (i = 0; i < MAX_LOBJ_FDS; i++) { + + if (cookies[i] == NULL) { + cookies[i] = lobjCookie; + return i; + } + } + return -1; +} + +static void +deleteLOfd(int fd) +{ + cookies[fd] = NULL; +} diff --git a/src/backend/libpq/be-fsstubs.h b/src/backend/libpq/be-fsstubs.h new file mode 100644 index 0000000000..3929f42a69 --- /dev/null +++ b/src/backend/libpq/be-fsstubs.h @@ -0,0 +1,32 @@ +/*------------------------------------------------------------------------- + * + * be-fsstubs.h-- + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: be-fsstubs.h,v 1.1.1.1 1996/07/09 06:21:30 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef BE_FSSTUBS_H +#define BE_FSSTUBS_H + +extern Oid lo_import(text *filename); +extern int4 lo_export(Oid lobjId, text *filename); + +extern Oid lo_creat(int mode); + +extern int lo_open(Oid lobjId, int mode); +extern int lo_close(int fd); +extern int lo_read(int fd, char *buf, int len); +extern int lo_write(int fd, char *buf, int len); +extern int lo_lseek(int fd, int offset, int whence); +extern int lo_tell(int fd); +extern int lo_unlink(Oid lobjId); + +extern struct varlena *LOread(int fd, int len); +extern int LOwrite(int fd, struct varlena *wbuf); + +#endif /* BE_FSSTUBS_H */ diff --git a/src/backend/libpq/be-pqexec.c b/src/backend/libpq/be-pqexec.c new file mode 100644 index 0000000000..1b1738d4fc --- /dev/null +++ b/src/backend/libpq/be-pqexec.c @@ -0,0 +1,382 @@ +/*------------------------------------------------------------------------- + * + * be-pqexec.c-- + * support for executing POSTGRES commands and functions from a + * user-defined function in a backend. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/libpq/Attic/be-pqexec.c,v 1.1.1.1 1996/07/09 06:21:30 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +/* + * INTERFACE ROUTINES + * PQfn - call a POSTGRES function + * PQexec - execute a POSTGRES query + * + * NOTES + * These routines are compiled into the postgres backend. + */ +#include "postgres.h" + +#include "nodes/pg_list.h" +#include "tcop/dest.h" +#include "tcop/fastpath.h" +#include "tcop/tcopprot.h" +#include "lib/dllist.h" +#include "libpq/libpq-be.h" +#include "fmgr.h" +#include "utils/exc.h" +#include "utils/builtins.h" +#include "utils/elog.h" +#include "utils/palloc.h" + +/* ---------------------------------------------------------------- + * PQ interface routines + * ---------------------------------------------------------------- + */ + +/* ---------------- + * PQfn - Send a function call to the POSTGRES backend. + * + * fnid : function id + * result_buf : pointer to result buffer (&int if integer) + * result_len : length of return value. + * result_is_int : If the result is an integer, this must be non-zero + * args : pointer to a NULL terminated arg array. + * (length, if integer, and result-pointer) + * nargs : # of arguments in args array. + * + * This code scavanged from HandleFunctionRequest() in tcop/fastpath.h + * ---------------- + */ +char * +PQfn(int fnid, + int *result_buf, /* can't use void, dec compiler barfs */ + int result_len, + int result_is_int, + PQArgBlock *args, + int nargs) +{ + char *retval; /* XXX - should be datum, maybe ? */ + char *arg[8]; + int i; + + /* ---------------- + * fill args[] array + * ---------------- + */ + for (i = 0; i < nargs; i++) { + if (args[i].len == VAR_LENGTH_ARG) { + arg[i] = (char*) args[i].u.ptr; + } else if (args[i].len > 4) { + elog(WARN,"arg_length of argument %d too long",i); + } else { + arg[i] = (char*)args[i].u.integer; + } + } + + /* ---------------- + * call the postgres function manager + * ---------------- + */ + retval = (char *) + fmgr(fnid, arg[0], arg[1], arg[2], arg[3], + arg[4], arg[5], arg[6], arg[7]); + + /* ---------------- + * put the result in the buffer the user specified and + * return the proper code. + * ---------------- + */ + if (retval == (char *) NULL) /* void retval */ + return "0"; + + if (result_is_int) { + *result_buf = (int) retval; + } else { + memmove(result_buf, retval, result_len); + } + return "G"; +} + +/* ---------------- + * PQexec - Send a query to the POSTGRES backend + * + * The return value is a string. + * If 0 or more tuples fetched from the backend, return "P portal-name". + * If a query is does not return tuples, return "C query-command". + * If there is an error: return "E error-message". + * + * Note: if we get a serious error or an elog(WARN), then PQexec never + * returns because the system longjmp's back to the main loop. + * ---------------- + */ +char * +PQexec(char *query) +{ + PortalEntry *entry = NULL; + char *result = NULL; + + /* ---------------- + * create a new portal and put it on top of the portal stack. + * ---------------- + */ + entry = (PortalEntry *) be_newportal(); + be_portalpush(entry); + + /* ---------------- + * pg_eval_dest will put the query results in a portal which will + * end up on the top of the portal stack. + * ---------------- + */ + pg_eval_dest(query, (char **) NULL, (Oid *) NULL, 0, Local); + + /* ---------------- + * pop the portal off the portal stack and return the + * result. Note if result is null, we return C. + * ---------------- + */ + entry = (PortalEntry *) be_portalpop(); + result = entry->result; + if (result == NULL) { + char *PQE = "Cnull PQexec result"; + result = pstrdup(PQE); + } + + if (result[0] != 'P') + { + /* some successful command was executed, + but it's not one where we return the portal name so + here we should be sure to clear out the portal + (since the caller has no handle on it) + */ + pbuf_close(entry->name); + + } + return result; +} + +/* ---------------------------------------------------------------- + * pqtest support + * ---------------------------------------------------------------- + */ + +/* ---------------- + * pqtest_PQexec takes a text query and returns the number of + * tuples it returns. Note: there is no need to PQclear() + * here - the memory will go away at end transaction. + * ---------------- + */ +int +pqtest_PQexec(char *q) +{ + PortalBuffer *a; + char *res; + int t; + + /* ---------------- + * execute the postgres query + * ---------------- + */ + res = PQexec(q); + + /* ---------------- + * return number of tuples in portal or 0 if command returns no tuples. + * ---------------- + */ + t = 0; + switch(res[0]) { + case 'P': + a = PQparray(&res[1]); + if (a == NULL) + elog(WARN, "pqtest_PQexec: PQparray could not find portal %s", + res); + + t = PQntuples(a); + break; + case 'C': + break; + default: + elog(NOTICE, "pqtest_PQexec: PQexec(%s) returns %s", q, res); + break; + } + + return t; +} + +/* ---------------- + * utilities for pqtest_PQfn() + * ---------------- + */ +char * +strmake(char *str, int len) +{ + char *newstr; + if (str == NULL) return NULL; + if (len <= 0) len = strlen(str); + + newstr = (char *) palloc((unsigned) len+1); + (void) strncpy(newstr, str, len); + newstr[len] = (char) 0; + return newstr; +} + +#define SKIP 0 +#define SCAN 1 + +static char spacestr[] = " "; + +static int +strparse(char *s, char **fields, int *offsets, int maxfields) +{ + int len = strlen(s); + char *cp = s, *end = cp + len, *ep; + int parsed = 0; + int mode = SKIP, i = 0; + + if (*(end - 1) == '\n') end--; + + for (i=0; i<maxfields; i++) + fields[i] = spacestr; + + i = 0; + while (!parsed) { + if (mode == SKIP) { + + while ((cp < end) && + (*cp == ' ' || *cp == '\t')) + cp++; + if (cp < end) mode = SCAN; + else parsed = 1; + + } else { + + ep = cp; + while ((ep < end) && (*ep != ' ' && *ep != '\t')) + ep++; + + if (ep < end) mode = SKIP; + else parsed = 1; + + fields[i] = strmake(cp, ep - cp); + if (offsets != NULL) + offsets[i] = cp - s; + + i++; + cp = ep; + if (i > maxfields) + parsed = 1; + + } + } + return i; +} + +/* ---------------- + * pqtest_PQfn converts it's string into a PQArgBlock and + * calls the specified function, which is assumed to return + * an integer value. + * ---------------- + */ +int +pqtest_PQfn(char *q) +{ + int k, j, i, v, f, offsets; + char *fields[8]; + PQArgBlock pqargs[7]; + int res; + char *pqres; + + /* ---------------- + * parse q into fields + * ---------------- + */ + i = strparse(q, fields, &offsets, 8); + printf("pqtest_PQfn: strparse returns %d fields\n", i); /* debug */ + if (i == 0) + return -1; + + /* ---------------- + * get the function id + * ---------------- + */ + f = atoi(fields[0]); + printf("pqtest_PQfn: func is %d\n", f); /* debug */ + if (f == 0) + return -1; + + /* ---------------- + * build a PQArgBlock + * ---------------- + */ + for (j=1; j<i && j<8; j++) { + k = j-1; + v = atoi(fields[j]); + if (v != 0 || (v == 0 && fields[j][0] == '0')) { + pqargs[k].len = 4; + pqargs[k].u.integer = v; + printf("pqtest_PQfn: arg %d is int %d\n", k, v); /* debug */ + } else { + pqargs[k].len = VAR_LENGTH_ARG; + pqargs[k].u.ptr = (int *) textin(fields[j]); + printf("pqtest_PQfn: arg %d is text %s\n", k, fields[j]); /*debug*/ + } + } + + /* ---------------- + * call PQfn + * ---------------- + */ + pqres = PQfn(f, &res, 4, 1, pqargs, i-1); + printf("pqtest_PQfn: pqres is %s\n", pqres); /* debug */ + + /* ---------------- + * free memory used + * ---------------- + */ + for (j=0; j<i; j++) { + pfree(fields[j]); + if (pqargs[j].len == VAR_LENGTH_ARG) + pfree(pqargs[j].u.ptr); + } + + /* ---------------- + * return result + * ---------------- + */ + printf("pqtest_PQfn: res is %d\n", res); /* debugg */ + return res; +} + +/* ---------------- + * pqtest looks at the first character of it's test argument + * and decides which of pqtest_PQexec or pqtest_PQfn to call. + * ---------------- + */ +int32 +pqtest(struct varlena *vlena) +{ + char *q; + + /* ---------------- + * get the query + * ---------------- + */ + q = textout(vlena); + if (q == NULL) + return -1; + + switch(q[0]) { + case '%': + return pqtest_PQfn(&q[1]); + break; + default: + return pqtest_PQexec(q); + break; + } + return(0); +} diff --git a/src/backend/libpq/libpq-be.h b/src/backend/libpq/libpq-be.h new file mode 100644 index 0000000000..b73ca59455 --- /dev/null +++ b/src/backend/libpq/libpq-be.h @@ -0,0 +1,51 @@ +/*------------------------------------------------------------------------- + * + * libpq-be.h-- + * This file contains definitions for structures and + * externs for functions used by the POSTGRES backend. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: libpq-be.h,v 1.1.1.1 1996/07/09 06:21:30 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef LIBPQ_BE_H +#define LIBPQ_BE_H + +/* ---------------- + * include stuff common to fe and be + * ---------------- + */ +#include "libpq/libpq.h" +#include "access/htup.h" + +#include "access/tupdesc.h" + +/* ---------------- + * declarations for backend libpq support routines + * ---------------- + */ + +/* in be-dumpdata.c */ +extern void be_portalinit(void); +extern void be_portalpush(PortalEntry *entry); +extern PortalEntry *be_portalpop(void); +extern PortalEntry *be_currentportal(); +extern PortalEntry *be_newportal(void); +extern void be_typeinit(PortalEntry *entry, TupleDesc attrs, + int natts); +extern void be_printtup(HeapTuple tuple, TupleDesc typeinfo); + + +/* in be-pqexec.c */ +extern char *PQfn(int fnid, int *result_buf, int result_len, int result_is_int, + PQArgBlock *args, int nargs); +extern char *PQexec(char *query); +extern int pqtest_PQexec(char *q); +extern char *strmake(char *str, int len); +extern int pqtest_PQfn(char *q); +extern int32 pqtest(struct varlena *vlena); + +#endif /* LIBPQ_BE_H */ diff --git a/src/backend/libpq/libpq-fs.h b/src/backend/libpq/libpq-fs.h new file mode 100644 index 0000000000..76f1f84c30 --- /dev/null +++ b/src/backend/libpq/libpq-fs.h @@ -0,0 +1,119 @@ +/*------------------------------------------------------------------------- + * + * libpq-fs.h-- + * definitions for using Inversion file system routines + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: libpq-fs.h,v 1.1.1.1 1996/07/09 06:21:30 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef LIBPQ_FS_H +#define LIBPQ_FS_H + +#include "lib/dllist.h" +#include <sys/file.h> +#include <sys/stat.h> + +#include <fcntl.h> /* for O_ on some */ +#ifndef WIN32 +#include <unistd.h> /* for SEEK_ on most */ +#endif /* WIN32 */ +#ifndef SEEK_SET +#include <stdio.h> /* for SEEK_ on others */ +#endif /* SEEK_SET */ + +/* UNIX compatibility junk. This should be in all systems' include files, + but this is not always the case. */ + +#ifndef MAXNAMLEN +#define MAXNAMLEN 255 +#endif /* MAXNAMLEN */ + +struct pgdirent { + unsigned long d_ino; + unsigned short d_namlen; + char d_name[MAXNAMLEN+1]; +}; + +/* + * SysV struct dirent doesn't have d_namlen. + * This counts on d_name being last, which is moderately safe (ha) since + * it's the variable-length part of the structure. + */ +#ifdef SYSV_DIRENT +#define D_NAMLEN(dp) \ + ((dp)->d_reclen - offsetof(struct dirent, d_name[0])) +#else /* SYSV_DIRENT */ +#define D_NAMLEN(dp) \ + ((dp)->d_namlen) +#endif /* SYSV_DIRENT */ + +/* for stat(2) */ +#ifndef S_IRUSR +/* file modes */ + +#define S_IRWXU 00700 /* read, write, execute: owner */ +#define S_IRUSR 00400 /* read permission: owner */ +#define S_IWUSR 00200 /* write permission: owner */ +#define S_IXUSR 00100 /* execute permission: owner */ + +#define S_IRWXG 00070 /* read, write, execute: group */ +#define S_IRGRP 00040 /* read permission: group */ +#define S_IWGRP 00020 /* write permission: group */ +#define S_IXGRP 00010 /* execute permission: group */ + +#define S_IRWXO 00007 /* read, write, execute: other */ +#define S_IROTH 00004 /* read permission: other */ +#define S_IWOTH 00002 /* write permission: other */ +#define S_IXOTH 00001 /* execute permission: other */ + +#define _S_IFMT 0170000 /* type of file; sync with S_IFMT */ +#define _S_IFBLK 0060000 /* block special; sync with S_IFBLK */ +#define _S_IFCHR 0020000 /* character special sync with S_IFCHR */ +#define _S_IFDIR 0040000 /* directory; sync with S_IFDIR */ +#define _S_IFIFO 0010000 /* FIFO - named pipe; sync with S_IFIFO */ +#define _S_IFREG 0100000 /* regular; sync with S_IFREG */ + +#define S_IFDIR _S_IFDIR +#define S_IFREG _S_IFREG + +#define S_ISDIR( mode ) (((mode) & _S_IFMT) == _S_IFDIR) + +#endif /* S_IRUSR */ + +/* + * Inversion doesn't have links. + */ +#ifndef S_ISLNK +#define S_ISLNK(x) 0 +#endif + +/* + * Flags for inversion file system large objects. Normally, creat() + * takes mode arguments, but we don't use them in inversion, since + * you get postgres protections. Instead, we use the low sixteen bits + * of the integer mode argument to store the number of the storage + * manager to be used, and the high sixteen bits for flags. + */ + +#define INV_SMGRMASK 0x0000ffff +#define INV_ARCHIVE 0x00010000 +#define INV_WRITE 0x00020000 +#define INV_READ 0x00040000 + +/* Error values for p_errno */ +#define PEPERM 1 /* Not owner */ +#define PENOENT 2 /* No such file or directory */ +#define PEACCES 13 /* Permission denied */ +#define PEEXIST 17 /* File exists */ +#define PENOTDIR 20 /* Not a directory*/ +#define PEISDIR 21 /* Is a directory */ +#define PEINVAL 22 /* Invalid argument */ +#define PENAMETOOLONG 63 /* File name too long */ +#define PENOTEMPTY 66 /* Directory not empty */ +#define PEPGIO 99 /* postgres backend had problems */ + +#endif /* LIBPQ_FS_H */ diff --git a/src/backend/libpq/libpq.h b/src/backend/libpq/libpq.h new file mode 100644 index 0000000000..5fafbb148d --- /dev/null +++ b/src/backend/libpq/libpq.h @@ -0,0 +1,261 @@ +/*------------------------------------------------------------------------- + * + * libpq.h-- + * POSTGRES LIBPQ buffer structure definitions. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: libpq.h,v 1.1.1.1 1996/07/09 06:21:30 scrappy Exp $ + * + * NOTES + * This file contains definitions for structures and + * externs for functions used by both frontend applications + * and the POSTGRES backend. See the files libpq-fe.h and + * libpq-be.h for frontend/backend specific information + * + *------------------------------------------------------------------------- + */ +#ifndef LIBPQ_H +#define LIBPQ_H + +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#ifdef WIN32 +#include <winsock.h> +#else +#include <netinet/in.h> +#endif /* WIN32 */ + +#include "lib/dllist.h" +#include "utils/exc.h" +#include "postgres.h" + +#include "libpq/pqcomm.h" + +/* ---------------- + * PQArgBlock -- + * Information (pointer to array of this structure) required + * for the PQfn() call. + * ---------------- + */ +typedef struct { + int len; + int isint; + union { + int *ptr; /* can't use void (dec compiler barfs) */ + int integer; + } u; +} PQArgBlock; + +/* ---------------- + * TypeBlock -- + * Information about an attribute. + * ---------------- + */ +#define NameLength 16 + +typedef struct TypeBlock { + char name[NAMEDATALEN]; /* name of the attribute */ + int adtid; /* adtid of the type */ + int adtsize; /* adtsize of the type */ +} TypeBlock; + +/* ---------------- + * TupleBlock -- + * Data of a tuple. + * ---------------- + */ +#define TupleBlockSize 100 + +typedef struct TupleBlock { + char **values[TupleBlockSize]; /* an array of tuples */ + int *lengths[TupleBlockSize]; /* an array of length vec. foreach + tuple */ + struct TupleBlock *next; /* next tuple block */ + int tuple_index; /* current tuple index */ +} TupleBlock; + +/* ---------------- + * GroupBuffer -- + * A group of tuples with the same attributes. + * ---------------- + */ +typedef struct GroupBuffer { + int no_tuples; /* number of tuples in this group */ + int no_fields; /* number of attributes */ + TypeBlock *types; /* types of the attributes */ + TupleBlock *tuples; /* tuples in this group */ + struct GroupBuffer *next; /* next group */ +} GroupBuffer; + +/* ---------------- + * PortalBuffer -- + * Data structure of a portal buffer. + * ---------------- + */ +typedef struct PortalBuffer { + int rule_p; /* 1 if this is an asynchronized portal. */ + int no_tuples; /* number of tuples in this portal buffer */ + int no_groups; /* number of tuple groups */ + GroupBuffer *groups; /* linked list of tuple groups */ +} PortalBuffer; + +/* ---------------- + * PortalEntry -- + * an entry in the global portal table + * + * Note: the portalcxt is only meaningful for PQcalls made from + * within a postgres backend. frontend apps should ignore it. + * ---------------- + */ +#define PortalNameLength 32 + +typedef struct PortalEntry { + char name[PortalNameLength]; /* name of this portal */ + PortalBuffer *portal; /* tuples contained in this portal */ + Pointer portalcxt; /* memory context (for backend) */ + Pointer result; /* result for PQexec */ +} PortalEntry; + +#define PORTALS_INITIAL_SIZE 32 +#define PORTALS_GROW_BY 32 + +/* in portalbuf.c */ +extern PortalEntry** portals; +extern size_t portals_array_size; + +/* + * Asynchronous notification + */ +typedef struct PQNotifyList { + char relname[NAMEDATALEN]; /* name of relation containing data */ + int be_pid; /* process id of backend */ + int valid; /* has this already been handled by user. */ +/* SLNode Node; */ +} PQNotifyList; + +/* + * Exceptions. + */ + +#define libpq_raise(X, Y) ExcRaise((Exception *)(X), (ExcDetail) (Y),\ + (ExcData)0, (ExcMessage) 0) + +/* in portal.c */ +extern Exception MemoryError, PortalError, PostquelError, ProtocolError; + +/* + * POSTGRES backend dependent Constants. + */ + +/* ERROR_MSG_LENGTH should really be the same as ELOG_MAXLEN in utils/elog.h*/ +#define ERROR_MSG_LENGTH 4096 +#define COMMAND_LENGTH 20 +#define REMARK_LENGTH 80 + +extern char PQerrormsg[ERROR_MSG_LENGTH]; /* in portal.c */ + +/* + * External functions. + */ + +/* + * prototypes for functions in portal.c + */ +extern void pqdebug(char *target, char *msg); +extern void pqdebug2(char *target, char *msg1, char *msg2); +extern void PQtrace(void); +extern void PQuntrace(void); +extern int PQnportals(int rule_p); +extern void PQpnames(char **pnames, int rule_p); +extern PortalBuffer *PQparray(char *pname); +extern int PQrulep(PortalBuffer *portal); +extern int PQntuples(PortalBuffer *portal); +extern int PQninstances(PortalBuffer *portal); +extern int PQngroups(PortalBuffer *portal); +extern int PQntuplesGroup(PortalBuffer *portal, int group_index); +extern int PQninstancesGroup(PortalBuffer *portal, int group_index); +extern int PQnfieldsGroup(PortalBuffer *portal, int group_index); +extern int PQfnumberGroup(PortalBuffer *portal, int group_index, char *field_name); +extern char *PQfnameGroup(PortalBuffer *portal, int group_index, int field_number); +extern int PQftypeGroup(PortalBuffer *portal, int group_index, + int field_number); +extern int PQfsizeGroup(PortalBuffer *portal, int group_index, + int field_number); +extern GroupBuffer *PQgroup(PortalBuffer *portal, int tuple_index); +extern int PQgetgroup(PortalBuffer *portal, int tuple_index); +extern int PQnfields(PortalBuffer *portal, int tuple_index); +extern int PQfnumber(PortalBuffer *portal, int tuple_index, char *field_name); + extern char *PQfname(PortalBuffer *portal, int tuple_index, int field_number); +extern int PQftype(PortalBuffer *portal, int tuple_index, int field_number); +extern int PQfsize(PortalBuffer *portal, int tuple_index, int field_number); +extern int PQsametype(PortalBuffer *portal, int tuple_index1, int tuple_index2); +extern char *PQgetvalue(PortalBuffer *portal, int tuple_index, int field_number); +extern char *PQgetAttr(PortalBuffer *portal, int tuple_index, int field_number); +extern int PQgetlength(PortalBuffer *portal, int tuple_index, int field_number); +extern void PQclear(char *pname); +extern void PQcleanNotify(void); +extern void PQnotifies_init(void); +extern PQNotifyList *PQnotifies(void); +extern void PQremoveNotify(PQNotifyList *nPtr); +extern void PQappendNotify(char *relname, int pid); +/* + * prototypes for functions in portalbuf.c + */ +extern caddr_t pbuf_alloc(size_t size); +extern void pbuf_free(caddr_t pointer); +extern PortalBuffer *pbuf_addPortal(void); +extern GroupBuffer *pbuf_addGroup(PortalBuffer *portal); +extern TypeBlock *pbuf_addTypes(int n); +extern TupleBlock *pbuf_addTuples(void); +extern char **pbuf_addTuple(int n); +extern int *pbuf_addTupleValueLengths(int n); +extern char *pbuf_addValues(int n); +extern PortalEntry *pbuf_addEntry(void); +extern void pbuf_freeEntry(int i); +extern void pbuf_freeTypes(TypeBlock *types); +extern void pbuf_freeTuples(TupleBlock *tuples, int no_tuples, int no_fields); +extern void pbuf_freeGroup(GroupBuffer *group); +extern void pbuf_freePortal(PortalBuffer *portal); +extern int pbuf_getIndex(char *pname); +extern void pbuf_setportalinfo(PortalEntry *entry, char *pname); +extern PortalEntry *pbuf_setup(char *pname); +extern void pbuf_close(char *pname); +extern GroupBuffer *pbuf_findGroup(PortalBuffer *portal, int group_index); +extern int pbuf_findFnumber(GroupBuffer *group, char *field_name); +extern void pbuf_checkFnumber(GroupBuffer *group, int field_number); +extern char *pbuf_findFname(GroupBuffer *group, int field_number); + +/* + * prototypes for functions in pqcomm.c + */ +extern void pq_init(int fd); +extern void pq_gettty(char *tp); +extern int pq_getport(void); +extern void pq_close(void); +extern void pq_flush(void); +extern int pq_getstr(char *s, int maxlen); +extern int PQgetline(char *s, int maxlen); +extern int PQputline(char *s); +extern int pq_getnchar(char *s, int off, int maxlen); +extern int pq_getint(int b); +extern void pq_putstr(char *s); +extern void pq_putnchar(char *s, int n); +extern void pq_putint(int i, int b); +extern int pq_sendoob(char *msg, int len); +extern int pq_recvoob(char *msgPtr, int *lenPtr); +extern int pq_getinaddr(struct sockaddr_in *sin, char *host, int port); +extern int pq_getinserv(struct sockaddr_in *sin, char *host, char *serv); +extern int pq_connect(char *dbname, char *user, char *args, char *hostName, + char *debugTty, char *execFile, short portName); +extern int StreamOpen(char *hostName, short portName, Port *port); +extern void pq_regoob(void (*fptr)()); +extern void pq_unregoob(void); +extern void pq_async_notify(void); +extern int StreamServerPort(char *hostName, short portName, int *fdP); +extern int StreamConnection(int server_fd, Port *port); +extern void StreamClose(int sock); + +#endif /* LIBPQ_H */ diff --git a/src/backend/libpq/portal.c b/src/backend/libpq/portal.c new file mode 100644 index 0000000000..ca27fd8308 --- /dev/null +++ b/src/backend/libpq/portal.c @@ -0,0 +1,783 @@ +/*------------------------------------------------------------------------- + * + * portal.c-- + * generalized portal support routines + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/libpq/Attic/portal.c,v 1.1.1.1 1996/07/09 06:21:30 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +/* + * UTILITY ROUTINES + * pqdebug - send a string to the debugging output port + * pqdebug2 - send two strings to stdout + * PQtrace - turn on pqdebug() tracing + * PQuntrace - turn off pqdebug() tracing + * + * INTERFACE ROUTINES + * PQnportals - Return the number of open portals. + * PQpnames - Return all the portal names + * PQparray - Return the portal buffer given a portal name + * PQrulep - Return 1 if an asynchronous portal + * PQntuples - Return the number of tuples in a portal buffer + * PQninstances - same as PQntuples using object terminology + * PQngroups - Return the number of tuple groups in a portal buffer + * PQntuplesGroup - Return the number of tuples in a tuple group + * PQninstancesGroup - same as PQntuplesGroup using object terminology + * PQnfieldsGroup - Return the number of fields in a tuple group + * PQfnumberGroup - Return field number given (group index, field name) + * PQftypeGroup - Return field type given (group index, field index) + * PQfsizeGroup - Return field size given (group index, field index) + * PQfnameGroup - Return field name given (group index, field index) + * PQgroup - Return the tuple group that a particular tuple is in + * PQgetgroup - Return the index of the group that a tuple is in + * PQnfields - Return the number of fields in a tuple + * PQfnumber - Return the field index of a field name in a tuple + * PQfname - Return the name of a field + * PQftype - Return the type of a field + * PQfsize - Return the size of a field + * PQftype - Return the type of a field + * PQsametype - Return 1 if the two tuples have the same type + * PQgetvalue - Return an attribute (field) value + * PQgetlength - Return an attribute (field) length + * PQclear - free storage claimed by named portal + * PQnotifies - Return a list of relations on which notification + * has occurred. + * PQremoveNotify - Remove this notification from the list. + * + * NOTES + * These functions may be used by both frontend routines which + * communicate with a backend or by user-defined functions which + * are compiled or dynamically loaded into a backend. + * + * the portals[] array should be organized as a hash table for + * quick portal-by-name lookup. + * + * Do not confuse "PortalEntry" (or "PortalBuffer") with "Portal" + * see utils/mmgr/portalmem.c for why. -cim 2/22/91 + * + */ +#include <stdio.h> /* for sprintf() */ +#include <string.h> + +#include "c.h" +#include "lib/dllist.h" +#include "libpq/libpq.h" /* where the declarations go */ +#include "utils/exc.h" +#include "utils/palloc.h" + +/* ---------------- + * exceptions + * ---------------- + */ +Exception MemoryError = {"Memory Allocation Error"}; +Exception PortalError = {"Invalid arguments to portal functions"}; +Exception PostquelError = {"Sql Error"}; +Exception ProtocolError = {"Protocol Error"}; +char PQerrormsg[ERROR_MSG_LENGTH]; + +int PQtracep = 0; /* 1 to print out debugging messages */ +FILE *debug_port = (FILE *) NULL; + +static int +in_range(char *msg, int value, int min, int max) +{ + if (value < min || value >= max) { + (void) sprintf(PQerrormsg, "FATAL: %s, %d is not in range [%d,%d)\n", + msg, value, min, max); + pqdebug("%s", PQerrormsg); + fputs(PQerrormsg, stderr); + return(0); + } + return(1); +} + +static int +valid_pointer(char *msg, void *ptr) +{ + if (!ptr) { + (void) sprintf(PQerrormsg, "FATAL: %s\n", msg); + pqdebug("%s", PQerrormsg); + fputs(PQerrormsg, stderr); + return(0); + } + return(1); +} + +/* ---------------------------------------------------------------- + * PQ utility routines + * ---------------------------------------------------------------- + */ +void +pqdebug(char *target, char *msg) +{ + if (!target) + return; + + if (PQtracep) { + /* + * if nothing else was suggested default to stdout + */ + if (!debug_port) + debug_port = stdout; + fprintf(debug_port, target, msg); + fprintf(debug_port, "\n"); + } +} + +void +pqdebug2(char *target, char *msg1, char *msg2) +{ + if (!target) + return; + + if (PQtracep) { + /* + * if nothing else was suggested default to stdout + */ + if (!debug_port) + debug_port = stdout; + fprintf(debug_port, target, msg1, msg2); + fprintf(debug_port, "\n"); + } +} + +/* -------------------------------- + * PQtrace() / PQuntrace() + * -------------------------------- + */ +void +PQtrace() +{ + PQtracep = 1; +} + +void +PQuntrace() +{ + PQtracep = 0; +} + +/* ---------------------------------------------------------------- + * PQ portal interface routines + * ---------------------------------------------------------------- + */ + +/* -------------------------------- + * PQnportals - Return the number of open portals. + * If rule_p, only return asynchronous portals. + * -------------------------------- + */ +int +PQnportals(int rule_p) +{ + int i, n = 0; + + for (i = 0; i < portals_array_size; ++i) { + if (portals[i] && portals[i]->portal) { + if (!rule_p || portals[i]->portal->rule_p) { + ++n; + } + } + } + return(n); +} + +/* -------------------------------- + * PQpnames - Return all the portal names + * If rule_p, only return asynchronous portals. + * + * the caller must have allocated sufficient memory for char** pnames + * (an array of PQnportals strings of length PortalNameLength). + * + * notice that this assumes that the user is calling PQnportals and + * PQpnames with the same rule_p argument, and with no intervening + * portal closures. if not, you can get in heap big trouble.. + * -------------------------------- + */ +void +PQpnames(char **pnames, int rule_p) +{ + int i, cur_pname = 0; + + if (!valid_pointer("PQpnames: invalid name buffer", pnames)) + return; + + for (i = 0; i < portals_array_size; ++i) { + if (portals[i] && portals[i]->portal) { + if (!rule_p || portals[i]->portal->rule_p) { + (void) strncpy(pnames[cur_pname], portals[i]->name, PortalNameLength); + ++cur_pname; + } + } + } +} + +/* -------------------------------- + * PQparray - Return the portal buffer given a portal name + * -------------------------------- + */ +PortalBuffer * +PQparray(char *pname) +{ + int i; + + if (!valid_pointer("PQparray: invalid name buffer", pname)) + return NULL; + + if ((i = pbuf_getIndex(pname)) < 0) + return((PortalBuffer *) NULL); + return(portals[i]->portal); +} + +/* -------------------------------- + * PQrulep - Return 1 if an asynchronous portal + * -------------------------------- + */ +int +PQrulep(PortalBuffer *portal) +{ + if (!valid_pointer("PQrulep: invalid portal pointer", portal)) + return(-1); + + return(portal->rule_p); +} + +/* -------------------------------- + * PQntuples - Return the number of tuples in a portal buffer + * -------------------------------- + */ +int +PQntuples(PortalBuffer *portal) +{ + if (!valid_pointer("PQntuples: invalid portal pointer", portal)) + return(-1); + + return(portal->no_tuples); +} + +int +PQninstances(PortalBuffer *portal) +{ + return(PQntuples(portal)); +} + +/* -------------------------------- + * PQngroups - Return the number of tuple groups in a portal buffer + * -------------------------------- + */ +int +PQngroups(PortalBuffer *portal) +{ + if (!valid_pointer("PQngroups: invalid portal pointer", portal)) + return(-1); + + return(portal->no_groups); +} + +/* -------------------------------- + * PQntuplesGroup - Return the number of tuples in a tuple group + * -------------------------------- + */ +int +PQntuplesGroup(PortalBuffer *portal, int group_index) +{ + GroupBuffer *gbp; + + if (!valid_pointer("PQntuplesGroup: invalid portal pointer", portal) || + !in_range("PQntuplesGroup: group index", + group_index, 0, portal->no_groups)) + return(-1); + + gbp = pbuf_findGroup(portal, group_index); + if (gbp) + return(gbp->no_tuples); + return(-1); +} + +int +PQninstancesGroup(PortalBuffer *portal, int group_index) +{ + return(PQntuplesGroup(portal, group_index)); +} + +/* -------------------------------- + * PQnfieldsGroup - Return the number of fields in a tuple group + * -------------------------------- + */ +int +PQnfieldsGroup(PortalBuffer *portal, int group_index) +{ + GroupBuffer *gbp; + + if (!valid_pointer("PQnfieldsGroup: invalid portal pointer", portal) || + !in_range("PQnfieldsGroup: group index", + group_index, 0, portal->no_groups)) + return(-1); + gbp = pbuf_findGroup(portal, group_index); + if (gbp) + return(gbp->no_fields); + return(-1); +} + +/* -------------------------------- + * PQfnumberGroup - Return the field number (index) given + * the group index and the field name + * -------------------------------- + */ +int +PQfnumberGroup(PortalBuffer *portal, int group_index, char *field_name) +{ + GroupBuffer *gbp; + + if (!valid_pointer("PQfnumberGroup: invalid portal pointer", portal) || + !valid_pointer("PQfnumberGroup: invalid field name pointer", + field_name) || + !in_range("PQfnumberGroup: group index", + group_index, 0, portal->no_groups)) + return(-1); + gbp = pbuf_findGroup(portal, group_index); + if (gbp) + return(pbuf_findFnumber(gbp, field_name)); + return(-1); +} + +/* -------------------------------- + * PQfnameGroup - Return the field (attribute) name given + * the group index and field index. + * -------------------------------- + */ +char * +PQfnameGroup(PortalBuffer *portal, int group_index, int field_number) +{ + GroupBuffer *gbp; + + if (!valid_pointer("PQfnameGroup: invalid portal pointer", portal) || + !in_range("PQfnameGroup: group index", + group_index, 0, portal->no_groups)) + return((char *) NULL); + + if ((gbp = pbuf_findGroup(portal, group_index)) && + in_range("PQfnameGroup: field number", + field_number, 0, gbp->no_fields)) + return(pbuf_findFname(gbp, field_number)); + return((char *) NULL); +} + +/* -------------------------------- + * PQftypeGroup - Return the type of a field given + * the group index and field index + * -------------------------------- + */ +int +PQftypeGroup(PortalBuffer *portal, int group_index, int field_number) +{ + GroupBuffer *gbp; + + if (!valid_pointer("PQftypeGroup: invalid portal pointer", portal) || + !in_range("PQftypeGroup: group index", + group_index, 0, portal->no_groups)) + return(-1); + + if ((gbp = pbuf_findGroup(portal, group_index)) && + in_range("PQftypeGroup: field number", field_number, 0, gbp->no_fields)) + return(gbp->types[field_number].adtid); + return(-1); +} + +/* -------------------------------- + * PQfsizeGroup - Return the size of a field given + * the group index and field index + * -------------------------------- + */ +int +PQfsizeGroup(PortalBuffer *portal, int group_index, int field_number) +{ + GroupBuffer *gbp; + + if (!valid_pointer("PQfsizeGroup: invalid portal pointer", portal) || + !in_range("PQfsizeGroup: tuple index", + group_index, 0, portal->no_groups)) + return(-1); + + if ((gbp = pbuf_findGroup(portal, group_index)) && + in_range("PQfsizeGroup: field number", field_number, 0, gbp->no_fields)) + return(gbp->types[field_number].adtsize); + return(-1); +} + + +/* -------------------------------- + * PQgroup - Return the tuple group that a particular tuple is in + * -------------------------------- + */ +GroupBuffer * +PQgroup(PortalBuffer *portal, int tuple_index) +{ + GroupBuffer *gbp; + int tuple_count = 0; + + if (!valid_pointer("PQgroup: invalid portal pointer", portal) || + !in_range("PQgroup: tuple index", + tuple_index, 0, portal->no_tuples)) + return((GroupBuffer *) NULL); + + for (gbp = portal->groups; + gbp && tuple_index >= (tuple_count += gbp->no_tuples); + gbp = gbp->next) + ; + if (!in_range("PQgroup: tuple not found: tuple index", + tuple_index, 0, tuple_count)) + return((GroupBuffer *) NULL); + return(gbp); +} + +/* -------------------------------- + * PQgetgroup - Return the index of the group that a + * particular tuple is in + * -------------------------------- + */ +int +PQgetgroup(PortalBuffer *portal, int tuple_index) +{ + GroupBuffer *gbp; + int tuple_count = 0, group_count = 0; + + if (!valid_pointer("PQgetgroup: invalid portal pointer", portal) || + !in_range("PQgetgroup: tuple index", + tuple_index, 0, portal->no_tuples)) + return(-1); + + for (gbp = portal->groups; + gbp && tuple_index >= (tuple_count += gbp->no_tuples); + gbp = gbp->next) + ++group_count; + if (!gbp || !in_range("PQgetgroup: tuple not found: tuple index", + tuple_index, 0, tuple_count)) + return(-1); + return(group_count); +} + +/* -------------------------------- + * PQnfields - Return the number of fields in a tuple + * -------------------------------- + */ +int +PQnfields(PortalBuffer *portal, int tuple_index) +{ + GroupBuffer *gbp; + + if (!valid_pointer("PQnfields: invalid portal pointer", portal) || + !in_range("PQnfields: tuple index", + tuple_index, 0, portal->no_tuples)) + return(-1); + gbp = PQgroup(portal, tuple_index); + if (gbp) + return(gbp->no_fields); + return(-1); +} + +/* -------------------------------- + * PQfnumber - Return the field index of a given + * field name within a tuple. + * -------------------------------- + */ +int +PQfnumber(PortalBuffer *portal, int tuple_index, char *field_name) +{ + GroupBuffer *gbp; + + if (!valid_pointer("PQfnumber: invalid portal pointer", portal) || + !valid_pointer("PQfnumber: invalid field name pointer", field_name) || + !in_range("PQfnumber: tuple index", + tuple_index, 0, portal->no_tuples)) + return(-1); + gbp = PQgroup(portal, tuple_index); + if (gbp) + return(pbuf_findFnumber(gbp, field_name)); + return(-1); +} + +/* -------------------------------- + * PQfname - Return the name of a field + * -------------------------------- + */ +char * +PQfname(PortalBuffer *portal, int tuple_index, int field_number) +{ + GroupBuffer *gbp; + + if (!valid_pointer("PQfname: invalid portal pointer", portal) || + !in_range("PQfname: tuple index", + tuple_index, 0, portal->no_tuples)) + return((char *) NULL); + + if ((gbp = PQgroup(portal, tuple_index)) && + in_range("PQfname: field number", + field_number, 0, gbp->no_fields)) + return(pbuf_findFname(gbp, field_number)); + return((char *) NULL); +} + +/* -------------------------------- + * PQftype - Return the type of a field + * -------------------------------- + */ +int +PQftype(PortalBuffer *portal, int tuple_index, int field_number) +{ + GroupBuffer *gbp; + + if (!valid_pointer("PQftype: invalid portal pointer", portal) || + !in_range("PQfname: tuple index", + tuple_index, 0, portal->no_tuples)) + return(-1); + + if ((gbp = PQgroup(portal, tuple_index)) && + in_range("PQftype: field number", field_number, 0, gbp->no_fields)) + return(gbp->types[field_number].adtid); + return(-1); +} + +/* -------------------------------- + * PQfsize - Return the size of a field + * -------------------------------- + */ +int +PQfsize(PortalBuffer *portal, int tuple_index, int field_number) +{ + GroupBuffer *gbp; + + if (!valid_pointer("PQfsize: invalid portal pointer", portal) || + !in_range("PQfsize: tuple index", + tuple_index, 0, portal->no_tuples)) + return(-1); + + if ((gbp = PQgroup(portal, tuple_index)) && + in_range("PQfsize: field number", field_number, 0, gbp->no_fields)) + return(gbp->types[field_number].adtsize); + return(-1); +} + + + +/* -------------------------------- + * PQsametype - Return 1 if the two tuples have the same type + * (in the same group) + * -------------------------------- + */ +int +PQsametype(PortalBuffer *portal, int tuple_index1, int tuple_index2) +{ + GroupBuffer *gbp1, *gbp2; + + if (!valid_pointer("PQsametype: invalid portal pointer", portal) || + !in_range("PQsametype: tuple index 1", + tuple_index1, 0, portal->no_tuples) || + !in_range("PQsametype: tuple index 2", + tuple_index2, 0, portal->no_tuples)) + return(-1); + + gbp1 = PQgroup(portal, tuple_index1); + gbp2 = PQgroup(portal, tuple_index2); + if (gbp1 && gbp2) + return(gbp1 == gbp2); + return(-1); +} + +static TupleBlock * +PQGetTupleBlock(PortalBuffer *portal, + int tuple_index, + int *tuple_offset) +{ + GroupBuffer *gbp; + TupleBlock *tbp; + int tuple_count = 0; + + if (!valid_pointer("PQGetTupleBlock: invalid portal pointer", portal) || + !valid_pointer("PQGetTupleBlock: invalid offset pointer", + tuple_offset) || + !in_range("PQGetTupleBlock: tuple index", + tuple_index, 0, portal->no_tuples)) + return((TupleBlock *) NULL); + + for (gbp = portal->groups; + gbp && tuple_index >= (tuple_count += gbp->no_tuples); + gbp = gbp->next) + ; + if (!gbp || + !in_range("PQGetTupleBlock: tuple not found: tuple index", + tuple_index, 0, tuple_count)) + return((TupleBlock *) NULL); + tuple_count -= gbp->no_tuples; + for (tbp = gbp->tuples; + tbp && tuple_index >= (tuple_count += TupleBlockSize); + tbp = tbp->next) + ; + if (!tbp || + !in_range("PQGetTupleBlock: tuple not found: tuple index", + tuple_index, 0, tuple_count)) + return((TupleBlock *) NULL); + tuple_count -= TupleBlockSize; + + *tuple_offset = tuple_index - tuple_count; + return(tbp); +} + +/* -------------------------------- + * PQgetvalue - Return an attribute (field) value + * -------------------------------- + */ +char * +PQgetvalue(PortalBuffer *portal, + int tuple_index, + int field_number) +{ + TupleBlock *tbp; + int tuple_offset; + + tbp = PQGetTupleBlock(portal, tuple_index, &tuple_offset); + if (tbp) + return(tbp->values[tuple_offset][field_number]); + return((char *) NULL); +} + +/* -------------------------------- + * PQgetAttr - Return an attribute (field) value + * this differs from PQgetvalue in that the value returned is + * a copy. The CALLER is responsible for free'ing the data returned. + * -------------------------------- + */ +char * +PQgetAttr(PortalBuffer *portal, + int tuple_index, + int field_number) +{ + TupleBlock *tbp; + int tuple_offset; + int len; + char* result = NULL; + + tbp = PQGetTupleBlock(portal, tuple_index, &tuple_offset); + if (tbp) { + len = tbp->lengths[tuple_offset][field_number]; + result = malloc(len + 1); + memcpy(result, + tbp->values[tuple_offset][field_number], + len); + result[len] = '\0'; + } + return result; +} + + +/* -------------------------------- + * PQgetlength - Return an attribute (field) length + * -------------------------------- + */ +int +PQgetlength(PortalBuffer *portal, + int tuple_index, + int field_number) +{ + TupleBlock *tbp; + int tuple_offset; + + tbp = PQGetTupleBlock(portal, tuple_index, &tuple_offset); + if (tbp) + return(tbp->lengths[tuple_offset][field_number]); + return(-1); +} + +/* ---------------- + * PQclear - free storage claimed by named portal + * ---------------- + */ +void +PQclear(char *pname) +{ + if (!valid_pointer("PQclear: invalid portal name pointer", pname)) + return; + pbuf_close(pname); +} + +/* + * async notification. + * This is going away with pending rewrite of comm. code... + */ +/* static SLList pqNotifyList;*/ +static Dllist *pqNotifyList = NULL; + +/* remove invalid notifies before returning */ +void +PQcleanNotify() +{ + Dlelem *e, *next; + PQNotifyList *p; + + e = DLGetHead(pqNotifyList); + + while (e) { + next = DLGetSucc(e); + p = (PQNotifyList*)DLE_VAL(e); + if (p->valid == 0) { + DLRemove(e); + DLFreeElem(e); + pfree(p); + } + e = next; + } +} + +void +PQnotifies_init() +{ + Dlelem *e; + PQNotifyList *p; + + if (pqNotifyList == NULL) { + pqNotifyList = DLNewList(); + } + else { + /* clean all notifies */ + for (e = DLGetHead(pqNotifyList); e != NULL; e = DLGetSucc(e)) { + p = (PQNotifyList*)DLE_VAL(e); + p->valid = 0; + } + PQcleanNotify(); + } +} + +PQNotifyList * +PQnotifies() +{ + Dlelem *e; + PQcleanNotify(); + e = DLGetHead(pqNotifyList); + return (e ? (PQNotifyList*)DLE_VAL(e) : NULL); +} + +void +PQremoveNotify(PQNotifyList *nPtr) +{ + nPtr->valid = 0; /* remove later */ +} + +void +PQappendNotify(char *relname, int pid) +{ + PQNotifyList *p; + + if (pqNotifyList == NULL) + pqNotifyList = DLNewList(); + + p = (PQNotifyList*)pbuf_alloc(sizeof(PQNotifyList)); + strncpy(p->relname, relname, NAMEDATALEN); + p->be_pid = pid; + p->valid = 1; + DLAddTail(pqNotifyList, DLNewElem(p)); +} diff --git a/src/backend/libpq/portalbuf.c b/src/backend/libpq/portalbuf.c new file mode 100644 index 0000000000..f927e268ed --- /dev/null +++ b/src/backend/libpq/portalbuf.c @@ -0,0 +1,511 @@ +/*------------------------------------------------------------------------- + * + * portalbuf.c-- + * portal buffer support routines for src/libpq/portal.c + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/libpq/Attic/portalbuf.c,v 1.1.1.1 1996/07/09 06:21:30 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +/* + * INTERFACE ROUTINES + * pbuf_alloc - allocate memory for libpq routines + * pbuf_free - free memory for libpq routines + * pbuf_addPortal - Allocate a new portal buffer + * pbuf_addGroup - Add a new tuple group to the portal + * pbuf_addTypes - Allocate n type blocks + * pbuf_addTuples - Allocate a tuple block + * pbuf_addTuple - Allocate a tuple of n fields (attributes) + * pbuf_addValues - Allocate n bytes for a value + * pbuf_addEntry - Allocate a portal entry + * pbuf_freeEntry - Free a portal entry in the portal table + * pbuf_freeTypes - Free up the space used by a portal + * pbuf_freeTuples - free space used by tuple block + * pbuf_freeGroup - free space used by group, types and tuples + * pbuf_freePortal - free space used by portal and portal's group + * pbuf_getIndex - Return the index of the portal entry + * pbuf_setup - Set up a portal for dumping data + * pbuf_close - Close a portal, remove it from the portal table + * pbuf_findGroup - Return group given the group_index + * pbuf_findFnumber - Return field index of a given field within a group + * pbuf_findFname - Find the field name given the field index + * pbuf_checkFnumber - signal an error if field number is out of bounds + * + * NOTES + * These functions may be used by both frontend routines which + * communicate with a backend or by user-defined functions which + * are compiled or dynamically loaded into a backend. + * + * the portals[] array should be organized as a hash table for + * quick portal-by-name lookup. + * + * Do not confuse "PortalEntry" (or "PortalBuffer") with "Portal" + * see utils/mmgr/portalmem.c for why. -cim 2/22/91 + * + */ +#include <sys/types.h> +#include "c.h" + +#include "libpq/libpq.h" /* where the declarations go */ +#include "utils/exc.h" +#include "utils/palloc.h" + +PortalEntry** portals = (PortalEntry**) NULL; +size_t portals_array_size = 0; + +/* portals array memory is malloc'd instead of using MemoryContexts */ +/* since it will be used by both front and backend programs*/ +/* GlobalMemory portals_mmcxt = (GlobalMemory) NULL; */ + +/* ------------------------------- + * portals_realloc -- + * grow the size of the portals array by size + * + * also ensures that elements are initially NULL + */ + +static void +portals_realloc(size_t size) +{ + size_t oldsize; + int i; + PortalEntry** newp; + + oldsize = portals_array_size; + + portals_array_size += size; + if (portals) + newp= (PortalEntry**)realloc(portals, + portals_array_size*sizeof(PortalEntry*)); + else + newp= (PortalEntry**)malloc(portals_array_size*sizeof(PortalEntry*)); + + if (newp) + portals = newp; + else + libpq_raise(&PortalError, + form("Cannot alloc more memory in portals_realloc")); + + for (i=oldsize;i<portals_array_size;i++) + portals[i]=(PortalEntry*)NULL; + +} + +/* -------------------------------- + * pbuf_alloc - allocate memory for portal buffers + * + * remember: palloc() in the backend uses the postgres MemoryContext + * library and palloc() in the frontend (fe-pqstubs.c) calls malloc(). + * -------------------------------- + */ +caddr_t +pbuf_alloc(size_t size) +{ + caddr_t addr; + + if (size <= 0) + libpq_raise(&MemoryError, form("Invalid argument to pbuf_alloc().")); + + addr = (caddr_t) palloc(size); + if (addr == (caddr_t) NULL) + libpq_raise(&MemoryError, form("Cannot Allocate space.")); + + return (addr); +} + +/* -------------------------------- + * pbuf_free - free memory for portal buffers + * + * remember: pfree() in the backend uses the postgres MemoryContext + * library and pfree() in the frontend (fe-pqstubs.c) calls free(). + * -------------------------------- + */ +void +pbuf_free(caddr_t pointer) +{ + if (pointer) + pfree(pointer); + else + libpq_raise(&MemoryError, form("Tried to free NULL memory pointer")); + +} + +/* -------------------------------- + * pbuf_addPortal - Allocate a new portal buffer + * -------------------------------- + */ +PortalBuffer * +pbuf_addPortal() +{ + PortalBuffer *portal; + + portal = (PortalBuffer *) + pbuf_alloc(sizeof (PortalBuffer)); + + portal->rule_p = 0; + portal->no_tuples = 0; + portal->no_groups = 0; + portal->groups = NULL; + + return (portal); +} + +/* -------------------------------- + * pbuf_addGroup - Add a new tuple group to the portal + * -------------------------------- + */ +GroupBuffer * +pbuf_addGroup(PortalBuffer *portal) +{ + GroupBuffer *group, *group1; + + group = (GroupBuffer *) + pbuf_alloc(sizeof (GroupBuffer)); + + /* Initialize the new group buffer. */ + group->no_tuples = 0; + group->no_fields = 0; + group->types = NULL; + group->tuples = NULL; + group->next = NULL; + + if ((group1 = portal->groups) == NULL) + portal->groups = group; + else { + while (group1->next != NULL) + group1 = group1->next; + group1->next = group; + } + + return (group); +} + +/* -------------------------------- + * pbuf_addTypes - Allocate n type blocks + * -------------------------------- + */ +TypeBlock * +pbuf_addTypes(int n) +{ + TypeBlock *types; + + types = (TypeBlock *) + pbuf_alloc(n * sizeof (TypeBlock)); + + return (types); +} + +/* -------------------------------- + * pbuf_addTuples - Allocate a tuple block + * -------------------------------- + */ +TupleBlock * +pbuf_addTuples() +{ + TupleBlock *tuples; + + tuples = (TupleBlock *) + pbuf_alloc(sizeof (TupleBlock)); + + tuples->next = NULL; + tuples->tuple_index = 0; + + return (tuples); +} + +/* -------------------------------- + * pbuf_addTuple - Allocate a tuple of n fields (attributes) + * -------------------------------- + */ +char ** +pbuf_addTuple(int n) +{ + return (char **) + pbuf_alloc(n * sizeof (char *)); +} + +/* -------------------------------- + * pbuf_addTupleValueLengths - Allocate a tuple of n lengths (attributes) + * -------------------------------- + */ +int * +pbuf_addTupleValueLengths(int n) +{ + return (int *) + pbuf_alloc(n * sizeof(int)); +} + +/* -------------------------------- + * pbuf_addValues - Allocate n bytes for a value + * -------------------------------- + */ +char * +pbuf_addValues(int n) +{ + return + pbuf_alloc(n); +} + +/* -------------------------------- + * pbuf_addEntry - Allocate a portal entry + * -------------------------------- + */ +PortalEntry *pbuf_addEntry() +{ + return (PortalEntry *) + pbuf_alloc (sizeof (PortalEntry)); +} + +/* -------------------------------- + * pbuf_freeEntry - Free a portal entry in the portal table + * the portal is freed separately. + * -------------------------------- + */ +void +pbuf_freeEntry(int i) +{ + if (portals) + { + pbuf_free ((caddr_t)portals[i]); + portals[i] = NULL; + } +} + + +/* -------------------------------- + * pbuf_freeTypes - Free up the space used by a portal + * -------------------------------- + */ +void +pbuf_freeTypes(TypeBlock *types) +{ + pbuf_free((caddr_t)types); +} + +/* -------------------------------- + * pbuf_freeTuples - free space used by tuple block + * -------------------------------- + */ +void +pbuf_freeTuples(TupleBlock *tuples, + int no_tuples, + int no_fields) +{ + int i, j; + + if (no_tuples > TupleBlockSize) { + pbuf_freeTuples (tuples->next, no_tuples - TupleBlockSize, no_fields); + no_tuples = TupleBlockSize; + } + + /* For each tuple, free all its attribute values. */ + for (i = 0; i < no_tuples; i++) { + for (j = 0; j < no_fields; j++) + if (tuples->values[i][j] != NULL) + pbuf_free((caddr_t)tuples->values[i][j]); + if (tuples->lengths[i]) + pbuf_free((caddr_t)tuples->lengths[i]); + if (tuples->values[i]) + pbuf_free((caddr_t)tuples->values[i]); + } + + pbuf_free((caddr_t)tuples); +} + +/* -------------------------------- + * pbuf_freeGroup - free space used by group, types and tuples + * -------------------------------- + */ +void +pbuf_freeGroup(GroupBuffer *group) +{ + if (group->next != NULL) + pbuf_freeGroup(group->next); + + if (group->types != NULL) + pbuf_freeTypes(group->types); + + if (group->tuples != NULL) + pbuf_freeTuples(group->tuples, group->no_tuples,group->no_fields); + + pbuf_free((caddr_t)group); +} + +/* -------------------------------- + * pbuf_freePortal - free space used by portal and portal's group + * -------------------------------- + */ +void +pbuf_freePortal(PortalBuffer *portal) +{ + if (portal->groups != NULL) + pbuf_freeGroup(portal->groups); + + pbuf_free((caddr_t)portal); +} + +/* -------------------------------- + * pbuf_getIndex - Return the index of the portal entry + * note: portals[] maps portal names to portal buffers. + * -------------------------------- + */ +int +pbuf_getIndex(char *pname) +{ + int i; + + if (portals) { + for (i = 0; i < portals_array_size; i++) + if (portals[i] != NULL && + strncmp(portals[i]->name, pname, PortalNameLength) == 0) + return i; + } + + return (-1); +} + +/* -------------------------------- + * pbuf_setportalname - assign a user given name to a portal + * -------------------------------- + */ +void +pbuf_setportalinfo(PortalEntry *entry, char *pname) +{ + if (entry) + strncpy(entry->name, pname, PortalNameLength-1); + entry->name[PortalNameLength-1] = '\0'; +} + +/* -------------------------------- + * pbuf_setup - Set up a portal for dumping data + * -------------------------------- + */ +PortalEntry * +pbuf_setup(char *pname) +{ + int i; + + if (!portals) /* the portals array has not been allocated yet */ + { + /* allocate portals[] array here */ + portals_realloc(PORTALS_INITIAL_SIZE); + } + + /* If a portal with the same name already exists, close it. */ + /* else look for an empty entry in the portal table. */ + if ((i = pbuf_getIndex(pname)) != -1) + pbuf_freePortal(portals[i]->portal); + else { + for (i = 0; i < portals_array_size; i++) + if (portals[i] == NULL) + break; + + /* If the portal table is full, enlarge it */ + if (i >= portals_array_size) + portals_realloc(PORTALS_GROW_BY); + + portals[i] = pbuf_addEntry(); + strncpy(portals[i]->name, pname, PortalNameLength); + } + portals[i]->portal = pbuf_addPortal(); + portals[i]->portalcxt = NULL; + portals[i]->result = NULL; + + return portals[i]; +} + +/* -------------------------------- + * pbuf_close - Close a portal, remove it from the portal table + * and free up the space + * -------------------------------- + */ +void +pbuf_close(char *pname) +{ + int i; + + if ((i = pbuf_getIndex(pname)) == -1) + libpq_raise(&PortalError, form("Portal %s does not exist.", pname)); + + pbuf_freePortal(portals[i]->portal); + pbuf_freeEntry(i); +} + +/* -------------------------------- + * pbuf_findGroup - Return the group given the group_index + * -------------------------------- + */ +GroupBuffer * +pbuf_findGroup(PortalBuffer *portal, + int group_index) +{ + GroupBuffer *group; + + group = portal->groups; + while (group_index > 0 && group != NULL) { + group = group->next; + group_index--; + } + + if (group == NULL) + libpq_raise(&PortalError, + form("Group index %d out of bound.", group_index)); + + return (group); +} + +/* -------------------------------- + * pbuf_findFnumber - Return the field index of a given field within a group + * -------------------------------- + */ +int +pbuf_findFnumber(GroupBuffer *group, + char *field_name) +{ + TypeBlock *types; + int i; + + types = group->types; + + for (i = 0; i < group->no_fields; i++) + if (strncmp(types[i].name, field_name, NAMEDATALEN) == 0) + return (i); + + libpq_raise(&PortalError, + form("Field-name %s does not exist.", field_name)); + + /* not reached, here to make compiler happy */ + return 0; + +} + +/* -------------------------------- + * pbuf_checkFnumber - signal an error if field number is out of bounds + * -------------------------------- + */ +void +pbuf_checkFnumber(GroupBuffer *group, + int field_number) +{ + if (field_number < 0 || field_number >= group->no_fields) + libpq_raise(&PortalError, + form("Field number %d out of bound.", field_number)); +} + +/* -------------------------------- + * pbuf_findFname - Find the field name given the field index + * -------------------------------- + */ +char * +pbuf_findFname(GroupBuffer *group, + int field_number) +{ + pbuf_checkFnumber(group, field_number); + return + (group->types[field_number]).name; +} + diff --git a/src/backend/libpq/pqcomm.c b/src/backend/libpq/pqcomm.c new file mode 100644 index 0000000000..7fc4a85f20 --- /dev/null +++ b/src/backend/libpq/pqcomm.c @@ -0,0 +1,724 @@ +/*------------------------------------------------------------------------- + * + * pqcomm.c-- + * Communication functions between the Frontend and the Backend + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/libpq/pqcomm.c,v 1.1.1.1 1996/07/09 06:21:31 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +/* + * INTERFACE ROUTINES + * pq_gettty - return the name of the tty in the given buffer + * pq_getport - return the PGPORT setting + * pq_close - close input / output connections + * pq_flush - flush pending output + * pq_getstr - get a null terminated string from connection + * pq_getnchar - get n characters from connection + * pq_getint - get an integer from connection + * pq_putstr - send a null terminated string to connection + * pq_putnchar - send n characters to connection + * pq_putint - send an integer to connection + * pq_getinaddr - initialize address from host and port number + * pq_getinserv - initialize address from host and service name + * pq_connect - create remote input / output connection + * pq_accept - accept remote input / output connection + * pq_async_notify - receive notification from backend. + * + * NOTES + * These functions are used by both frontend applications and + * the postgres backend. + * + */ +#include "libpq/pqsignal.h" /* substitute for <signal.h> */ +#include <stdio.h> +#include <string.h> +#ifndef WIN32 +#include <unistd.h> /* for ttyname() */ +#include <sys/types.h> +#include <sys/socket.h> +#include <netdb.h> +#include <netinet/in.h> +#else +#include <winsock.h> +#endif /* WIN32 */ +#include <errno.h> +#include <fcntl.h> + +#ifdef PORTNAME_linux +#ifndef SOMAXCONN +#define SOMAXCONN 5 /* from Linux listen(2) man page */ +#endif /* SOMAXCONN */ +#endif /* PORTNAME_linux */ + +#include "c.h" +#include "libpq/auth.h" +#include "libpq/libpq.h" /* where the declarations go */ +#include "libpq/pqcomm.h" +#include "utils/elog.h" + +/* ---------------- + * declarations + * ---------------- + */ +FILE *Pfout, *Pfin; +FILE *Pfdebug; /* debugging libpq */ +int PQAsyncNotifyWaiting; /* for async. notification */ + +/* -------------------------------- + * pq_init - open portal file descriptors + * -------------------------------- + */ +void +pq_init(int fd) +{ +#ifdef WIN32 + int in, out; + + in = _open_osfhandle(fd, _O_RDONLY); + out = _open_osfhandle(fd, _O_APPEND); + Pfin = fdopen(in, "rb"); + Pfout = fdopen(out, "wb"); +#else + Pfin = fdopen(fd, "r"); + Pfout = fdopen(dup(fd), "w"); +#endif /* WIN32 */ + if (!Pfin || !Pfout) + elog(FATAL, "pq_init: Couldn't initialize socket connection"); + PQnotifies_init(); + if (getenv("LIBPQ_DEBUG")) { + Pfdebug = stderr; + }else { + Pfdebug = NULL; + } +} + +/* ------------------------- + * pq_getc(File* fin) + * + * get a character from the input file, + * + * if Pfdebug is set, also echo the character fetched into Pfdebug + * + * used for debugging libpq + */ +static int +pq_getc(FILE* fin) +{ + int c; + + c = getc(fin); + if (Pfdebug && c != EOF) + putc(c,Pfdebug); + return c; +} + +/* -------------------------------- + * pq_gettty - return the name of the tty in the given buffer + * -------------------------------- + */ +void +pq_gettty(char *tp) +{ + (void) strncpy(tp, ttyname(0), 19); +} + +/* -------------------------------- + * pq_getport - return the PGPORT setting + * -------------------------------- + */ +int +pq_getport() +{ + char *envport = getenv("PGPORT"); + + if (envport) + return(atoi(envport)); + return(atoi(POSTPORT)); +} + +/* -------------------------------- + * pq_close - close input / output connections + * -------------------------------- + */ +void +pq_close() +{ + if (Pfin) { + fclose(Pfin); + Pfin = NULL; + } + if (Pfout) { + fclose(Pfout); + Pfout = NULL; + } + PQAsyncNotifyWaiting = 0; + PQnotifies_init(); + pq_unregoob(); +} + +/* -------------------------------- + * pq_flush - flush pending output + * -------------------------------- + */ +void +pq_flush() +{ + if (Pfout) + fflush(Pfout); +} + +/* -------------------------------- + * pq_getstr - get a null terminated string from connection + * -------------------------------- + */ +int +pq_getstr(char *s, int maxlen) +{ + int c; + + if (Pfin == (FILE *) NULL) { +/* elog(DEBUG, "Input descriptor is null"); */ + return(EOF); + } + + while (maxlen-- && (c = pq_getc(Pfin)) != EOF && c) + *s++ = c; + *s = '\0'; + + /* ----------------- + * If EOF reached let caller know. + * (This will only happen if we hit EOF before the string + * delimiter is reached.) + * ----------------- + */ + if (c == EOF) + return(EOF); + return(!EOF); +} + +/* + * USER FUNCTION - 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. + * + * PQgetline reads up to maxlen-1 characters (like fgets(3)) but strips + * the terminating \n (like gets(3)). + * + * RETURNS: + * EOF if it is detected or 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 + */ +int +PQgetline(char *s, int maxlen) +{ + int c = '\0'; + + if (!Pfin || !s || maxlen <= 1) + return(EOF); + + for (; maxlen > 1 && (c = pq_getc(Pfin)) != '\n' && c != EOF; --maxlen) { + *s++ = c; + } + *s = '\0'; + + if (c == EOF) { + return(EOF); /* error -- reached EOF before \n */ + } else if (c == '\n') { + return(0); /* done with this line */ + } + return(1); /* returning a full buffer */ +} + +/* + * USER FUNCTION - sends a string to the backend. + * + * Chiefly here so that applications can use "COPY <rel> from stdin". + * + * RETURNS: + * 0 in all cases. + */ +int +PQputline(char *s) +{ + if (Pfout) { + (void) fputs(s, Pfout); + fflush(Pfout); + } + return(0); +} + +/* -------------------------------- + * pq_getnchar - get n characters from connection + * -------------------------------- + */ +int +pq_getnchar(char *s, int off, int maxlen) +{ + int c; + + if (Pfin == (FILE *) NULL) { +/* elog(DEBUG, "Input descriptor is null"); */ + return(EOF); + } + + s += off; + while (maxlen-- && (c = pq_getc(Pfin)) != EOF) + *s++ = c; + + /* ----------------- + * If EOF reached let caller know + * ----------------- + */ + if (c == EOF) + return(EOF); + return(!EOF); +} + +/* -------------------------------- + * pq_getint - get an integer from connection + * we receive an integer a byte at a type and reconstruct it so that + * machines with different ENDIAN representations can talk to each + * other + * -------------------------------- + */ +int +pq_getint(int b) +{ + int n, c, p; + + if (Pfin == (FILE *) NULL) { +/* elog(DEBUG, "pq_getint: Input descriptor is null"); */ + return(EOF); + } + + n = p = 0; + while (b-- && (c = pq_getc(Pfin)) != EOF && p < 32) { + n |= (c & 0xff) << p; + p += 8; + } + + return(n); +} + +/* -------------------------------- + * pq_putstr - send a null terminated string to connection + * -------------------------------- + */ +void +pq_putstr(char *s) +{ + int status; + + if (Pfout) { + status = fputs(s, Pfout); + if (status == EOF) { + (void) sprintf(PQerrormsg, + "FATAL: pq_putstr: fputs() failed: errno=%d\n", + errno); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + } + status = fputc('\0', Pfout); + if (status == EOF) { + (void) sprintf(PQerrormsg, + "FATAL: pq_putstr: fputc() failed: errno=%d\n", + errno); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + } + } +} + +/* -------------------------------- + * pq_putnchar - send n characters to connection + * -------------------------------- + */ +void +pq_putnchar(char *s, int n) +{ + int status; + + if (Pfout) { + while (n--) { + status = fputc(*s++, Pfout); + if (status == EOF) { + (void) sprintf(PQerrormsg, + "FATAL: pq_putnchar: fputc() failed: errno=%d\n", + errno); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + } + } + } +} + +/* -------------------------------- + * pq_putint - send an integer to connection + * we chop an integer into bytes and send individual bytes + * machines with different ENDIAN representations can still talk to each + * other + * -------------------------------- + */ +void +pq_putint(int i, int b) +{ + int status; + + if (b > 4) + b = 4; + + if (Pfout) { + while (b--) { + status = fputc(i & 0xff, Pfout); + i >>= 8; + if (status == EOF) { + (void) sprintf(PQerrormsg, + "FATAL: pq_putint: fputc() failed: errno=%d\n", + errno); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + } + } + } +} + +/* --- + * pq_sendoob - send a string over the out-of-band channel + * pq_recvoob - receive a string over the oob channel + * NB: Fortunately, the out-of-band channel doesn't conflict with + * buffered I/O because it is separate from regular com. channel. + * --- + */ +int +pq_sendoob(char *msg, int len) +{ + int fd = fileno(Pfout); + + return(send(fd,msg,len,MSG_OOB)); +} + +int +pq_recvoob(char *msgPtr, int *lenPtr) +{ + int fd = fileno(Pfout); + int len = 0; + + len = recv(fd,msgPtr+len,*lenPtr,MSG_OOB); + *lenPtr = len; + return(len); +} + +/* -------------------------------- + * pq_getinaddr - initialize address from host and port number + * -------------------------------- + */ +int +pq_getinaddr(struct sockaddr_in *sin, + char *host, + int port) +{ + struct hostent *hs; + + memset((char *) sin, 0, sizeof(*sin)); + + if (host) { + if (*host >= '0' && *host <= '9') + sin->sin_addr.s_addr = inet_addr(host); + else { + if (!(hs = gethostbyname(host))) { + perror(host); + return(1); + } + if (hs->h_addrtype != AF_INET) { + (void) sprintf(PQerrormsg, + "FATAL: pq_getinaddr: %s not on Internet\n", + host); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + return(1); + } + memmove((char *) &sin->sin_addr, + hs->h_addr, + hs->h_length); + } + } + sin->sin_family = AF_INET; + sin->sin_port = htons(port); + return(0); +} + +/* -------------------------------- + * pq_getinserv - initialize address from host and servive name + * -------------------------------- + */ +int +pq_getinserv(struct sockaddr_in *sin, char *host, char *serv) +{ + struct servent *ss; + + if (*serv >= '0' && *serv <= '9') + return(pq_getinaddr(sin, host, atoi(serv))); + if (!(ss = getservbyname(serv, NULL))) { + (void) sprintf(PQerrormsg, + "FATAL: pq_getinserv: unknown service: %s\n", + serv); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + return(1); + } + return(pq_getinaddr(sin, host, ntohs(ss->s_port))); +} + +/* + * register an out-of-band listener proc--at most one allowed. + * This is used for receiving async. notification from the backend. + */ +void +pq_regoob(void (*fptr)()) +{ +#ifdef WIN32 + /* Who knows what to do here? */ + return; +#else + int fd = fileno(Pfout); +#ifdef PORTNAME_hpux + ioctl(fd, FIOSSAIOOWN, getpid()); +#else /* PORTNAME_hpux */ + fcntl(fd, F_SETOWN, getpid()); +#endif /* PORTNAME_hpux */ + (void) signal(SIGURG,fptr); +#endif /* WIN32 */ +} + +void +pq_unregoob() +{ +#ifndef WIN32 + signal(SIGURG,SIG_DFL); +#endif /* WIN32 */ +} + + +void +pq_async_notify() +{ + char msg[20]; + /* int len = sizeof(msg);*/ + int len = 20; + + if (pq_recvoob(msg,&len) >= 0) { + /* debugging */ + printf("received notification: %s\n",msg); + PQAsyncNotifyWaiting = 1; + /* PQappendNotify(msg+1);*/ + } else { + extern int errno; + printf("SIGURG but no data: len = %d, err=%d\n",len,errno); + } +} + +/* + * Streams -- wrapper around Unix socket system calls + * + * + * Stream functions are used for vanilla TCP connection protocol. + */ + +/* + * StreamServerPort -- open a sock stream "listening" port. + * + * This initializes the Postmaster's connection + * accepting port. + * + * ASSUME: that this doesn't need to be non-blocking because + * the Postmaster uses select() to tell when the socket + * is ready. + * + * RETURNS: STATUS_OK or STATUS_ERROR + */ +int +StreamServerPort(char *hostName, short portName, int *fdP) +{ + struct sockaddr_in sin; + int fd; + +#ifdef WIN32 + /* This is necessary to make it possible for a backend to use + ** stdio to read from the socket. + */ + int optionvalue = SO_SYNCHRONOUS_NONALERT; + + setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE, (char *)&optionvalue, + sizeof(optionvalue)); +#endif /* WIN32 */ + + if (! hostName) + hostName = "localhost"; + + memset((char *)&sin, 0, sizeof sin); + + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + (void) sprintf(PQerrormsg, + "FATAL: StreamServerPort: socket() failed: errno=%d\n", + errno); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + return(STATUS_ERROR); + } + + sin.sin_family = AF_INET; + sin.sin_port = htons(portName); + + if (bind(fd, (struct sockaddr *)&sin, sizeof sin) < 0) { + (void) sprintf(PQerrormsg, + "FATAL: StreamServerPort: bind() failed: errno=%d\n", + errno); + pqdebug("%s", PQerrormsg); + (void) strcat(PQerrormsg, "\tIs another postmaster already running on that port?\n"); + (void) strcat(PQerrormsg, "\tIf not, wait a few seconds and retry.\n"); + fputs(PQerrormsg, stderr); + return(STATUS_ERROR); + } + + listen(fd, SOMAXCONN); + + /* MS: I took this code from Dillon's version. It makes the + * listening port non-blocking. That is not necessary (and + * may tickle kernel bugs). + + (void) fcntl(fd, F_SETFD, 1); + (void) fcntl(fd, F_SETFL, FNDELAY); + */ + + *fdP = fd; + return(STATUS_OK); +} + +/* + * StreamConnection -- create a new connection with client using + * server port. + * + * This one should be non-blocking. + * + * RETURNS: STATUS_OK or STATUS_ERROR + */ +int +StreamConnection(int server_fd, Port *port) +{ + int addrlen; + + /* accept connection (and fill in the client (remote) address) */ + addrlen = sizeof(struct sockaddr_in); + if ((port->sock = accept(server_fd, + (struct sockaddr *) &port->raddr, + &addrlen)) < 0) { + elog(WARN, "postmaster: StreamConnection: accept: %m"); + return(STATUS_ERROR); + } + + /* fill in the server (local) address */ + addrlen = sizeof(struct sockaddr_in); + if (getsockname(port->sock, (struct sockaddr *) &port->laddr, + &addrlen) < 0) { + elog(WARN, "postmaster: StreamConnection: getsockname: %m"); + return(STATUS_ERROR); + } + + port->mask = 1 << port->sock; + +#ifndef WIN32 + /* reset to non-blocking */ + fcntl(port->sock, F_SETFL, 1); +#endif /* WIN32 */ + + return(STATUS_OK); +} + +/* + * StreamClose -- close a client/backend connection + */ +void +StreamClose(int sock) +{ + (void) close(sock); +} + +/* --------------------------- + * StreamOpen -- From client, initiate a connection with the + * server (Postmaster). + * + * RETURNS: STATUS_OK or STATUS_ERROR + * + * NOTE: connection is NOT established just because this + * routine exits. Local state is ok, but we haven't + * spoken to the postmaster yet. + * --------------------------- + */ +int +StreamOpen(char *hostName, short portName, Port *port) +{ + struct hostent *hp; + int laddrlen = sizeof(struct sockaddr_in); + extern int errno; + + if (!hostName) + hostName = "localhost"; + + /* set up the server (remote) address */ + if (!(hp = gethostbyname(hostName)) || hp->h_addrtype != AF_INET) { + (void) sprintf(PQerrormsg, + "FATAL: StreamOpen: unknown hostname: %s\n", + hostName); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + return(STATUS_ERROR); + } + memset((char *) &port->raddr, 0, sizeof(port->raddr)); + memmove((char *) &(port->raddr.sin_addr), + (char *) hp->h_addr, + hp->h_length); + port->raddr.sin_family = AF_INET; + port->raddr.sin_port = htons(portName); + + /* connect to the server */ + if ((port->sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + (void) sprintf(PQerrormsg, + "FATAL: StreamOpen: socket() failed: errno=%d\n", + errno); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + return(STATUS_ERROR); + } + if (connect(port->sock, (struct sockaddr *)&port->raddr, + sizeof(port->raddr)) < 0) { + (void) sprintf(PQerrormsg, + "FATAL: StreamOpen: connect() failed: errno=%d\n", + errno); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + return(STATUS_ERROR); + } + + /* fill in the client address */ + if (getsockname(port->sock, (struct sockaddr *) &port->laddr, + &laddrlen) < 0) { + (void) sprintf(PQerrormsg, + "FATAL: StreamOpen: getsockname() failed: errno=%d\n", + errno); + fputs(PQerrormsg, stderr); + pqdebug("%s", PQerrormsg); + return(STATUS_ERROR); + } + + return(STATUS_OK); +} diff --git a/src/backend/libpq/pqcomm.h b/src/backend/libpq/pqcomm.h new file mode 100644 index 0000000000..a7870871ea --- /dev/null +++ b/src/backend/libpq/pqcomm.h @@ -0,0 +1,124 @@ +/*------------------------------------------------------------------------- + * + * pqcomm.h-- + * Parameters for the communication module + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: pqcomm.h,v 1.1.1.1 1996/07/09 06:21:31 scrappy Exp $ + * + * NOTES + * Some of this should move to libpq.h + * + *------------------------------------------------------------------------- + */ +#ifndef PQCOMM_H +#define PQCOMM_H + +#include <sys/types.h> +#ifdef WIN32 +#include <winsock.h> +#else +#include <netinet/in.h> +#endif /* WIN32 */ + +#include "postgres.h" + +/* + * startup msg parameters: path length, argument string length + */ +#define PATH_SIZE 64 +#define ARGV_SIZE 64 + + +typedef enum _MsgType { + ACK_MSG = 0, /* acknowledge a message */ + ERROR_MSG=1, /* error response to client from server */ + RESET_MSG=2, /* client must reset connection */ + PRINT_MSG=3, /* tuples for client from server */ + NET_ERROR=4, /* error in net system call */ + FUNCTION_MSG=5, /* fastpath call (unused) */ + QUERY_MSG=6, /* client query to server */ + STARTUP_MSG=7, /* initialize a connection with a backend */ + DUPLICATE_MSG=8, /* duplicate msg arrived (errors msg only) */ + INVALID_MSG=9, /* for some control functions */ + STARTUP_KRB4_MSG=10, /* krb4 session follows startup packet */ + STARTUP_KRB5_MSG=11, /* krb5 session follows startup packet */ + STARTUP_HBA_MSG=12 /* use host-based authentication */ + /* insert new values here -- DO NOT REORDER OR DELETE ENTRIES */ +} MsgType; + +typedef char *Addr; +typedef int PacketLen; /* packet length */ + + +typedef struct StartupInfo { +/* PacketHdr hdr; */ + char database[PATH_SIZE]; /* database name */ + char user[NAMEDATALEN]; /* user name */ + char options[ARGV_SIZE]; /* possible additional args */ + char execFile[ARGV_SIZE]; /* possible backend to use */ + char tty[PATH_SIZE]; /* possible tty for debug output*/ +} StartupInfo; + +/* amount of available data in a packet buffer */ +#define MESSAGE_SIZE sizeof(StartupInfo) + 5 + +/* I/O can be blocking or non-blocking */ +#define BLOCKING (FALSE) +#define NON_BLOCKING (TRUE) + +/* a PacketBuf gets shipped from client to server so be careful + of differences in representation. + Be sure to use htonl() and ntohl() on the len and msgtype fields! */ +typedef struct PacketBuf { + int len; + MsgType msgtype; + char data[MESSAGE_SIZE]; +} PacketBuf; + +/* update the conversion routines + StartupInfo2PacketBuf() and PacketBuf2StartupInfo() (decl. below) + if StartupInfo or PacketBuf structs ever change */ + +/* + * socket descriptor port + * we need addresses of both sides to do authentication calls + */ +typedef struct Port { + int sock; /* file descriptor */ + int mask; /* select mask */ + int nBytes; /* nBytes read in so far */ + struct sockaddr_in laddr; /* local addr (us) */ + struct sockaddr_in raddr; /* remote addr (them) */ +/* PacketBufId id;*/ /* id of packet buf currently in use */ + PacketBuf buf; /* stream implementation (curr pack buf) */ +} Port; + +/* invalid socket descriptor */ +#define INVALID_SOCK (-1) + +#define INVALID_ID (-1) +#define MAX_CONNECTIONS 10 +#define N_PACK_BUFS 20 + +/* no multi-packet messages yet */ +#define MAX_PACKET_BACKLOG 1 + +#define DEFAULT_STRING "" + +extern FILE *Pfout, *Pfin; +extern int PQAsyncNotifyWaiting; + +/* + * prototypes for functions in pqpacket.c + */ +extern int PacketReceive(Port *port, PacketBuf *buf, bool nonBlocking); +extern int PacketSend(Port *port, PacketBuf *buf, + PacketLen len, bool nonBlocking); +/* extern PacketBuf* StartupInfo2PacketBuf(StartupInfo*); */ +/* extern StartupInfo* PacketBuf2StartupInfo(PacketBuf*); */ + + +#endif /* PQCOMM_H */ diff --git a/src/backend/libpq/pqpacket.c b/src/backend/libpq/pqpacket.c new file mode 100644 index 0000000000..edf373d1af --- /dev/null +++ b/src/backend/libpq/pqpacket.c @@ -0,0 +1,283 @@ +/*------------------------------------------------------------------------- + * + * pqpacket.c-- + * routines for reading and writing data packets sent/received by + * POSTGRES clients and servers + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/libpq/Attic/pqpacket.c,v 1.1.1.1 1996/07/09 06:21:31 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +/* NOTES + * This is the module that understands the lowest-level part + * of the communication protocol. All of the trickiness in + * this module is for making sure that non-blocking I/O in + * the Postmaster works correctly. Check the notes in PacketRecv + * on non-blocking I/O. + * + * Data Structures: + * Port has two important functions. (1) It records the + * sock/addr used in communication. (2) It holds partially + * read in messages. This is especially important when + * we haven't seen enough to construct a complete packet + * header. + * + * PacketBuf -- None of the clients of this module should know + * what goes into a packet hdr (although they know how big + * it is). This routine is in charge of host to net order + * conversion for headers. Data conversion is someone elses + * responsibility. + * + * IMPORTANT: these routines are called by backends, clients, and + * the Postmaster. + * + */ +#include <stdio.h> +#include <sys/types.h> +#ifndef WIN32 +#include <sys/socket.h> +#include <netdb.h> +#include <netinet/in.h> +#else +#include <winsock.h> +#endif /*WIN32 */ +#include <fcntl.h> +#include <errno.h> + +#include "postgres.h" +#include "miscadmin.h" +#include "utils/elog.h" +#include "storage/ipc.h" +#include "libpq/pqcomm.h" /* where the declarations go */ +#include "libpq/libpq.h" + +/* + * PacketReceive -- receive a packet on a port. + * + * RETURNS: connection id of the packet sender, if one + * is available. + * + */ +int +PacketReceive(Port *port, /* receive port */ + PacketBuf *buf, /* MAX_PACKET_SIZE-worth of buffer space */ + bool nonBlocking) /* NON_BLOCKING or BLOCKING i/o */ +{ + PacketLen max_size = sizeof(PacketBuf); + PacketLen cc; /* character count -- bytes recvd */ + PacketLen packetLen; /* remaining packet chars to read */ + Addr tmp; /* curr recv buf pointer */ + int addrLen = sizeof(struct sockaddr_in); + int hdrLen; + int flag; + int decr; + + hdrLen = sizeof(buf->len); + + if (nonBlocking == NON_BLOCKING) { + flag = MSG_PEEK; + decr = 0; + } else { + flag = 0; + decr = hdrLen; + } + /* + * Assume port->nBytes is zero unless we were interrupted during + * non-blocking I/O. This first recvfrom() is to get the hdr + * information so we know how many bytes to read. Life would + * be very complicated if we read too much data (buffering). + */ + tmp = ((Addr)buf) + port->nBytes; + + if (port->nBytes >= hdrLen) { + packetLen = ntohl(buf->len) - port->nBytes; + } + else { + /* peeking into the incoming message */ + cc = recvfrom(port->sock, (char *)&(buf->len), hdrLen, flag, + (struct sockaddr*) &(port->raddr), &addrLen); + if (cc < hdrLen) { + /* if cc is negative, the system call failed */ + if (cc < 0) { + return(STATUS_ERROR); + } + /* + * cc == 0 means the connection was broken at the + * other end. + */ + else if (! cc) { + return(STATUS_INVALID); + + } else { + /* + * Worst case. We didn't even read in enough data to + * get the header length. + * since we are using a data stream, + * this happens only if the client is mallicious. + * + * Don't save the number of bytes we've read so far. + * Since we only peeked at the incoming message, the + * kernel is going to keep it for us. + */ + return(STATUS_NOT_DONE); + } + } else { + /* + * great. got the header. now get the true length (including + * header size). + */ + packetLen = ntohl(buf->len); + /* + * if someone is sending us junk, close the connection + */ + if (packetLen > max_size) { + port->nBytes = packetLen; + return(STATUS_BAD_PACKET); + } + packetLen -= decr; + tmp += decr - port->nBytes; + } + } + + /* + * Now that we know how big it is, read the packet. We read + * the entire packet, since the last call was just a peek. + */ + while (packetLen) { + cc = recvfrom(port->sock, tmp, packetLen, 0, + (struct sockaddr*) &(port->raddr), &addrLen); + if (cc < 0) + return(STATUS_ERROR); + /* + * cc == 0 means the connection was broken at the + * other end. + */ + else if (! cc) + return(STATUS_INVALID); + +/* + fprintf(stderr,"expected packet of %d bytes, got %d bytes\n", + packetLen, cc); +*/ + tmp += cc; + packetLen -= cc; + + /* if non-blocking, we're done. */ + if (nonBlocking && packetLen) { + port->nBytes += cc; + return(STATUS_NOT_DONE); + } + } + + port->nBytes = 0; + return(STATUS_OK); +} + +/* + * PacketSend -- send a single-packet message. + * + * RETURNS: STATUS_ERROR if the write fails, STATUS_OK otherwise. + * SIDE_EFFECTS: may block. + * NOTES: Non-blocking writes would significantly complicate + * buffer management. For now, we're not going to do it. + * + */ +int +PacketSend(Port *port, + PacketBuf *buf, + PacketLen len, + bool nonBlocking) +{ + PacketLen totalLen; + int addrLen = sizeof(struct sockaddr_in); + + Assert(!nonBlocking); + Assert(buf); + + totalLen = len; + + len = sendto(port->sock, (Addr) buf, totalLen, /* flags */ 0, + (struct sockaddr *)&(port->raddr), addrLen); + + if (len < totalLen) { + (void) sprintf(PQerrormsg, + "FATAL: PacketSend: couldn't send complete packet: errno=%d\n", + errno); + fputs(PQerrormsg, stderr); + return(STATUS_ERROR); + } + + return(STATUS_OK); +} + +/* + * StartupInfo2PacketBuf - + * convert the fields of the StartupInfo to a PacketBuf + * + */ +/* moved to src/libpq/fe-connect.c */ +/* +PacketBuf* +StartupInfo2PacketBuf(StartupInfo* s) +{ + PacketBuf* res; + char* tmp; + + res = (PacketBuf*)malloc(sizeof(PacketBuf)); + res->len = htonl(sizeof(PacketBuf)); + res->data[0] = '\0'; + + tmp= res->data; + + strncpy(tmp, s->database, sizeof(s->database)); + tmp += sizeof(s->database); + strncpy(tmp, s->user, sizeof(s->user)); + tmp += sizeof(s->user); + strncpy(tmp, s->options, sizeof(s->options)); + tmp += sizeof(s->options); + strncpy(tmp, s->execFile, sizeof(s->execFile)); + tmp += sizeof(s->execFile); + strncpy(tmp, s->tty, sizeof(s->execFile)); + + return res; +} +*/ + +/* + * PacketBuf2StartupInfo - + * convert the fields of the StartupInfo to a PacketBuf + * + */ +/* moved to postmaster.c +StartupInfo* +PacketBuf2StartupInfo(PacketBuf* p) +{ + StartupInfo* res; + char* tmp; + + res = (StartupInfo*)malloc(sizeof(StartupInfo)); + + res->database[0]='\0'; + res->user[0]='\0'; + res->options[0]='\0'; + res->execFile[0]='\0'; + res->tty[0]='\0'; + + tmp= p->data; + strncpy(res->database,tmp,sizeof(res->database)); + tmp += sizeof(res->database); + strncpy(res->user,tmp, sizeof(res->user)); + tmp += sizeof(res->user); + strncpy(res->options,tmp, sizeof(res->options)); + tmp += sizeof(res->options); + strncpy(res->execFile,tmp, sizeof(res->execFile)); + tmp += sizeof(res->execFile); + strncpy(res->tty,tmp, sizeof(res->tty)); + + return res; +} +*/ diff --git a/src/backend/libpq/pqsignal.c b/src/backend/libpq/pqsignal.c new file mode 100644 index 0000000000..b60a5659cc --- /dev/null +++ b/src/backend/libpq/pqsignal.c @@ -0,0 +1,40 @@ +/*------------------------------------------------------------------------- + * + * pqsignal.c-- + * reliable BSD-style signal(2) routine stolen from RWW who stole it + * from Stevens... + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/libpq/pqsignal.c,v 1.1.1.1 1996/07/09 06:21:31 scrappy Exp $ + * + * NOTES + * This shouldn't be in libpq, but the monitor and some other + * things need it... + * + *------------------------------------------------------------------------- + */ +#include "libpq/pqsignal.h" + +pqsigfunc +pqsignal(int signo, pqsigfunc func) +{ +#if defined(USE_POSIX_SIGNALS) + struct sigaction act, oact; + + act.sa_handler = func; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + if (signo != SIGALRM) { + act.sa_flags |= SA_RESTART; + } + if (sigaction(signo, &act, &oact) < 0) + return(SIG_ERR); + return(oact.sa_handler); +#else /* !USE_POSIX_SIGNALS */ + Assert(0); + return 0; +#endif /* !USE_POSIX_SIGNALS */ +} diff --git a/src/backend/libpq/pqsignal.h b/src/backend/libpq/pqsignal.h new file mode 100644 index 0000000000..44f10882f2 --- /dev/null +++ b/src/backend/libpq/pqsignal.h @@ -0,0 +1,32 @@ +/*------------------------------------------------------------------------- + * + * pqsignal.h-- + * prototypes for the reliable BSD-style signal(2) routine. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: pqsignal.h,v 1.1.1.1 1996/07/09 06:21:31 scrappy Exp $ + * + * NOTES + * This shouldn't be in libpq, but the monitor and some other + * things need it... + * + *------------------------------------------------------------------------- + */ +#ifndef PQSIGNAL_H +#define PQSIGNAL_H + +#include <signal.h> + +#include "c.h" + +typedef void (*pqsigfunc)(int); + +extern pqsigfunc pqsignal(int signo, pqsigfunc func); + +#if defined(USE_POSIX_SIGNALS) +#define signal(signo, handler) pqsignal(signo, (pqsigfunc)(handler)) +#endif /* USE_POSIX_SIGNALS */ + +#endif /* PQSIGNAL_H */ |
