1
0
mirror of https://github.com/postgres/postgres.git synced 2025-11-03 09:13:20 +03:00

Postgres95 1.01 Distribution - Virgin Sources

This commit is contained in:
Marc G. Fournier
1996-07-09 06:22:35 +00:00
commit d31084e9d1
868 changed files with 242656 additions and 0 deletions

View File

@@ -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

668
src/backend/libpq/auth.c Normal file
View File

@@ -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);
}

49
src/backend/libpq/auth.h Normal file
View File

@@ -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 */

View File

@@ -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);
}

View File

@@ -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;
}

View File

@@ -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 */

View File

@@ -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);
}

View File

@@ -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 */

View File

@@ -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 */

261
src/backend/libpq/libpq.h Normal file
View File

@@ -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 */

783
src/backend/libpq/portal.c Normal file
View File

@@ -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));
}

View File

@@ -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;
}

724
src/backend/libpq/pqcomm.c Normal file
View File

@@ -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);
}

124
src/backend/libpq/pqcomm.h Normal file
View File

@@ -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 */

View File

@@ -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;
}
*/

View File

@@ -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 */
}

View File

@@ -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 */