diff --git a/src/Makefile.global b/src/Makefile.global index d0356683c9..c4eb88b1d6 100644 --- a/src/Makefile.global +++ b/src/Makefile.global @@ -7,7 +7,7 @@ # # # IDENTIFICATION -# $Header: /cvsroot/pgsql/src/Attic/Makefile.global,v 1.56 1996/11/08 07:47:52 scrappy Exp $ +# $Header: /cvsroot/pgsql/src/Attic/Makefile.global,v 1.57 1996/11/09 10:39:02 scrappy Exp $ # # NOTES # This is seen by any Makefiles that include mk/postgres.mk. To @@ -59,7 +59,7 @@ # to change it in Makefile.custom. # make sure that you have no whitespaces after the PORTNAME setting # or the makefiles can get confused -PORTNAME= UNDEFINED +PORTNAME= BSD44_derived # Ignore LINUX_ELF if you're not using Linux. But if you are, and you're # compiling to a.out (which means you're using the dld dynamic loading @@ -860,7 +860,7 @@ includedir= $(HEADERDIR) # Flags for CC and LD. (depend on COPT and PROFILE) # # PostgreSQL should *always* compile with -Wall -Werror enabled -CFLAGS+= -Wall -Werror +CFLAGS+= -Wall #-Werror # Globally pass debugging/optimization/profiling flags based # on the options selected above. diff --git a/src/interfaces/libpgtcl/pgtcl.c b/src/interfaces/libpgtcl/pgtcl.c index d4021894e4..ad9b68b41d 100644 --- a/src/interfaces/libpgtcl/pgtcl.c +++ b/src/interfaces/libpgtcl/pgtcl.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/interfaces/libpgtcl/Attic/pgtcl.c,v 1.3 1996/10/30 06:18:38 scrappy Exp $ + * $Header: /cvsroot/pgsql/src/interfaces/libpgtcl/Attic/pgtcl.c,v 1.4 1996/11/09 10:39:40 scrappy Exp $ * *------------------------------------------------------------------------- */ @@ -75,6 +75,11 @@ Pgtcl_Init (Tcl_Interp *interp) Tcl_CreateExitHandler(Pgtcl_AtExit, (ClientData)cd); /* register all pgtcl commands */ + Tcl_CreateCommand(interp, + "pg_conndefaults", + Pg_conndefaults, + (ClientData)cd, (Tcl_CmdDeleteProc*)NULL); + Tcl_CreateCommand(interp, "pg_connect", Pg_connect, diff --git a/src/interfaces/libpgtcl/pgtclCmds.c b/src/interfaces/libpgtcl/pgtclCmds.c index 91be2bd37f..397bcd15f7 100644 --- a/src/interfaces/libpgtcl/pgtclCmds.c +++ b/src/interfaces/libpgtcl/pgtclCmds.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/interfaces/libpgtcl/Attic/pgtclCmds.c,v 1.5 1996/10/30 06:18:39 scrappy Exp $ + * $Header: /cvsroot/pgsql/src/interfaces/libpgtcl/Attic/pgtclCmds.c,v 1.6 1996/11/09 10:39:41 scrappy Exp $ * *------------------------------------------------------------------------- */ @@ -209,6 +209,44 @@ tcl_value (char *value) #endif +/********************************** + * pg_conndefaults + + syntax: + pg_conndefaults + + the return result is a list describing the possible options and their + current default values for a call to pg_connect with the new -conninfo + syntax. Each entry in the list is a sublist of the format: + + {optname label dispchar dispsize value} + + **********************************/ + +int +Pg_conndefaults(ClientData cData, Tcl_Interp *interp, int argc, char* argv[]) +{ + PQconninfoOption *option; + char buf[8192]; + + Tcl_ResetResult(interp); + for(option = PQconndefaults(); option->keyword != NULL; option++) { + if(option->val == NULL) { + option->val = ""; + } + sprintf(buf, "{%s} {%s} {%s} %d {%s}", + option->keyword, + option->label, + option->dispchar, + option->dispsize, + option->val); + Tcl_AppendElement(interp, buf); + } + + return TCL_OK; +} + + /********************************** * pg_connect make a connection to a backend. @@ -235,55 +273,73 @@ Pg_connect(ClientData cData, Tcl_Interp *interp, int argc, char* argv[]) if (argc == 1) { Tcl_AppendResult(interp, "pg_connect: database name missing\n", 0); - Tcl_AppendResult(interp, "pg_connect databaseName [-host hostName] [-port portNumber] [-tty pgtty]]", 0); + Tcl_AppendResult(interp, "pg_connect databaseName [-host hostName] [-port portNumber] [-tty pgtty]]\n", 0); + Tcl_AppendResult(interp, "pg_connect -conninfo ", 0); return TCL_ERROR; } - if (argc > 2) { - /* parse for pg environment settings */ - i = 2; - while (i+1 < argc) { - if (strcmp(argv[i], "-host") == 0) { - pghost = argv[i+1]; - i += 2; - } - else - if (strcmp(argv[i], "-port") == 0) { - pgport = argv[i+1]; + + if (!strcmp("-conninfo", argv[1])) { + /* + * Establish a connection using the new PQconnectdb() interface + */ + if (argc != 3) { + Tcl_AppendResult(interp, "pg_connect: syntax error\n", 0); + Tcl_AppendResult(interp, "pg_connect -conninfo ", 0); + return TCL_ERROR; + } + conn = PQconnectdb(argv[2]); + } else { + /* + * Establish a connection using the old PQsetdb() interface + */ + if (argc > 2) { + /* parse for pg environment settings */ + i = 2; + while (i+1 < argc) { + if (strcmp(argv[i], "-host") == 0) { + pghost = argv[i+1]; i += 2; } else - if (strcmp(argv[i], "-tty") == 0) { - pgtty = argv[i+1]; + if (strcmp(argv[i], "-port") == 0) { + pgport = argv[i+1]; i += 2; } - else if (strcmp(argv[i], "-options") == 0) { - pgoptions = argv[i+1]; - i += 2; - } - else { - Tcl_AppendResult(interp, "Bad option to pg_connect : \n", - argv[i], 0); - Tcl_AppendResult(interp, "pg_connect databaseName [-host hostName] [-port portNumber] [-tty pgtty]]",0); - return TCL_ERROR; - } - } /* while */ - if ((i % 2 != 0) || i != argc) { - Tcl_AppendResult(interp, "wrong # of arguments to pg_connect\n", argv[i],0); - Tcl_AppendResult(interp, "pg_connect databaseName [-host hostName] [-port portNumber] [-tty pgtty]]",0); - return TCL_ERROR; + else + if (strcmp(argv[i], "-tty") == 0) { + pgtty = argv[i+1]; + i += 2; + } + else if (strcmp(argv[i], "-options") == 0) { + pgoptions = argv[i+1]; + i += 2; + } + else { + Tcl_AppendResult(interp, "Bad option to pg_connect : \n", + argv[i], 0); + Tcl_AppendResult(interp, "pg_connect databaseName [-host hostName] [-port portNumber] [-tty pgtty]]",0); + return TCL_ERROR; + } + } /* while */ + if ((i % 2 != 0) || i != argc) { + Tcl_AppendResult(interp, "wrong # of arguments to pg_connect\n", argv[i],0); + Tcl_AppendResult(interp, "pg_connect databaseName [-host hostName] [-port portNumber] [-tty pgtty]]",0); + return TCL_ERROR; + } } + dbName = argv[1]; + conn = PQsetdb(pghost, pgport, pgoptions, pgtty, dbName); } - dbName = argv[1]; - conn = PQsetdb(pghost, pgport, pgoptions, pgtty, dbName); if (conn->status == CONNECTION_OK) { PgSetConnectionId(cd, interp->result, conn); return TCL_OK; } else { - Tcl_AppendResult(interp, "Connection to ", dbName, " failed\n", 0); + Tcl_AppendResult(interp, "Connection to database failed\n", 0); Tcl_AppendResult(interp, conn->errorMessage, 0); + PQfinish(conn); return TCL_ERROR; } } diff --git a/src/interfaces/libpgtcl/pgtclCmds.h b/src/interfaces/libpgtcl/pgtclCmds.h index ea3ea84225..4883b926ea 100644 --- a/src/interfaces/libpgtcl/pgtclCmds.h +++ b/src/interfaces/libpgtcl/pgtclCmds.h @@ -5,7 +5,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: pgtclCmds.h,v 1.3 1996/10/30 06:18:40 scrappy Exp $ + * $Id: pgtclCmds.h,v 1.4 1996/11/09 10:39:42 scrappy Exp $ * *------------------------------------------------------------------------- */ @@ -43,6 +43,8 @@ typedef struct Pg_ResultId_s { /* **************************/ /* registered Tcl functions */ /* **************************/ +extern int Pg_conndefaults( + ClientData cData, Tcl_Interp *interp, int argc, char* argv[]); extern int Pg_connect( ClientData cData, Tcl_Interp *interp, int argc, char* argv[]); extern int Pg_disconnect( diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c index 8e67399370..341f6728f6 100644 --- a/src/interfaces/libpq/fe-connect.c +++ b/src/interfaces/libpq/fe-connect.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.14 1996/11/04 04:00:54 momjian Exp $ + * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.15 1996/11/09 10:39:51 scrappy Exp $ * *------------------------------------------------------------------------- */ @@ -52,10 +52,197 @@ static int packetSend(Port *port, PacketBuf *buf, PacketLen len, static void startup2PacketBuf(StartupInfo* s, PacketBuf* res); static void freePGconn(PGconn *conn); static void closePGconn(PGconn *conn); +static int conninfo_parse(const char *conninfo, char *errorMessage); +static char *conninfo_getval(char *keyword); +static void conninfo_free(); #define NOTIFYLIST_INITIAL_SIZE 10 #define NOTIFYLIST_GROWBY 10 + +/* ---------- + * Definition of the conninfo parametes and their fallback resources. + * If Environment-Var and Compiled-in are specified as NULL, no + * fallback is available. If after all no value can be determined + * for an option, an error is returned. + * + * The values for dbname and user are treated special in conninfo_parse. + * If the Compiled-in resource is specified as a NULL value, the + * user is determined by fe_getauthname() and for dbname the user + * name is copied. + * + * The Label and Disp-Char entries are provided for applications that + * want to use PQconndefaults() to create a generic database connection + * dialog. Disp-Char is defined as follows: + * "" Normal input field + * ---------- + */ +static PQconninfoOption PQconninfoOptions[] = { +/* ----------------------------------------------------------------- */ +/* Option-name Environment-Var Compiled-in Current value */ +/* Label Disp-Char */ +/* ----------------- --------------- --------------- --------------- */ + { "user", "PGUSER", NULL, NULL, + "Database-User", "", 20 }, + + { "dbname", "PGDATABASE", NULL, NULL, + "Database-Name", "", 20 }, + + { "host", "PGHOST", DefaultHost, NULL, + "Database-Host", "", 40 }, + + { "port", "PGPORT", POSTPORT, NULL, + "Database-Port", "", 6 }, + + { "tty", "PGTTY", DefaultTty, NULL, + "Backend-Debug-TTY", "D", 40 }, + + { "options", "PGOPTIONS", DefaultOption, NULL, + "Backend-Debug-Options", "D", 40 }, +/* ----------------- --------------- --------------- --------------- */ + { NULL, NULL, NULL, NULL, + NULL, NULL, 0 } +}; + +/* ---------------- + * PQconnectdb + * + * establishes a connectin to a postgres backend through the postmaster + * using connection information in a string. + * + * The conninfo string is a list of + * + * option = value + * + * definitions. Value might be a single value containing no whitespaces + * or a single quoted string. If a single quote should appear everywhere + * in the value, it must be escaped with a backslash like \' + * + * Returns a PGconn* which is needed for all subsequent libpq calls + * if the status field of the connection returned is CONNECTION_BAD, + * then some fields may be null'ed out instead of having valid values + * ---------------- + */ +PGconn* +PQconnectdb(const char *conninfo) +{ + PGconn *conn; + PQconninfoOption *option; + char errorMessage[ERROR_MSG_LENGTH]; + + /* ---------- + * Allocate memory for the conn structure + * ---------- + */ + conn = (PGconn*)malloc(sizeof(PGconn)); + if (conn == NULL) { + fprintf(stderr, + "FATAL: PQsetdb() -- unable to allocate memory for a PGconn"); + return (PGconn*)NULL; + } + memset((char *)conn, 0, sizeof(PGconn)); + + /* ---------- + * Parse the conninfo string and get the fallback resources + * ---------- + */ + if(conninfo_parse(conninfo, errorMessage) < 0) { + conn->status = CONNECTION_BAD; + strcpy(conn->errorMessage, errorMessage); + conninfo_free(); + return conn; + } + + /* ---------- + * Check that we have all connection parameters + * ---------- + */ + for(option = PQconninfoOptions; option->keyword != NULL; option++) { + if(option->val != NULL) continue; /* Value was in conninfo */ + + /* ---------- + * No value was found for this option. Return an error. + * ---------- + */ + conn->status = CONNECTION_BAD; + sprintf(conn->errorMessage, + "ERROR: PQconnectdb(): Cannot determine a value for option '%s'.\n", + option->keyword); + strcat(conn->errorMessage, + "Option not specified in conninfo string"); + if(option->environ) { + strcat(conn->errorMessage, + ", environment variable "); + strcat(conn->errorMessage, option->environ); + strcat(conn->errorMessage, "\nnot set"); + } + strcat(conn->errorMessage, " and no compiled in default value.\n"); + conninfo_free(); + return conn; + } + + /* ---------- + * Setup the conn structure + * ---------- + */ + conn->Pfout = NULL; + conn->Pfin = NULL; + conn->Pfdebug = NULL; + conn->port = NULL; + conn->notifyList = DLNewList(); + + conn->pghost = strdup(conninfo_getval("host")); + conn->pgport = strdup(conninfo_getval("port")); + conn->pgtty = strdup(conninfo_getval("tty")); + conn->pgoptions = strdup(conninfo_getval("options")); + conn->pguser = strdup(conninfo_getval("user")); + conn->dbName = strdup(conninfo_getval("dbname")); + + /* ---------- + * Free the connection info - all is in conn now + * ---------- + */ + conninfo_free(); + + /* ---------- + * Connect to the database + * ---------- + */ + conn->status = connectDB(conn); + if (conn->status == CONNECTION_OK) { + PGresult *res; + /* Send a blank query to make sure everything works; in particular, that + the database exists. + */ + res = PQexec(conn," "); + if (res == NULL || res->resultStatus != PGRES_EMPTY_QUERY) { + /* PQexec has put error message in conn->errorMessage */ + closePGconn(conn); + } + PQclear(res); + } + + return conn; +} + +/* ---------------- + * PQconndefaults + * + * Parse an empty string like PQconnectdb() would do and return the + * address of the connection options structure. Using this function + * an application might determine all possible options and their + * current default values. + * ---------------- + */ +PQconninfoOption* +PQconndefaults() +{ + char errorMessage[ERROR_MSG_LENGTH]; + + conninfo_parse("", errorMessage); + return PQconninfoOptions; +} + /* ---------------- * PQsetdb * @@ -162,7 +349,7 @@ PQsetdb(const char *pghost, const char* pgport, const char* pgoptions, const cha if (((tmp = (char *)dbName) && (dbName[0] != '\0')) || ((tmp = getenv("PGDATABASE")))) { conn->dbName = strdup(tmp); - } else conn->dbName = conn->pguser; + } else conn->dbName = strdup(conn->pguser); } else conn->dbName = NULL; if (error) conn->status = CONNECTION_BAD; @@ -461,6 +648,230 @@ startup2PacketBuf(StartupInfo* s, PacketBuf* res) strncpy(tmp, s->tty, sizeof(s->execFile)); } +/* ---------------- + * Conninfo parser routine + * ---------------- + */ +static int conninfo_parse(const char *conninfo, char *errorMessage) +{ + char *pname; + char *pval; + char *buf; + char *tmp; + char *cp; + char *cp2; + PQconninfoOption *option; + char errortmp[ERROR_MSG_LENGTH]; + + conninfo_free(); + + if((buf = strdup(conninfo)) == NULL) { + strcpy(errorMessage, + "FATAL: cannot allocate memory for copy of conninfo string\n"); + return -1; + } + cp = buf; + + while(*cp) { + /* Skip blanks before the parameter name */ + if(isspace(*cp)) { + cp++; + continue; + } + + /* Get the parameter name */ + pname = cp; + while(*cp) { + if(*cp == '=') { + break; + } + if(isspace(*cp)) { + *cp++ = '\0'; + while(*cp) { + if(!isspace(*cp)) { + break; + } + cp++; + } + break; + } + cp++; + } + + /* Check that there is a following '=' */ + if(*cp != '=') { + sprintf(errorMessage, + "ERROR: PQconnectdb() - Missing '=' after '%s' in conninfo\n", + pname); + free(buf); + return -1; + } + *cp++ = '\0'; + + /* Skip blanks after the '=' */ + while(*cp) { + if(!isspace(*cp)) { + break; + } + cp++; + } + + pval = cp; + + if(*cp != '\'') { + cp2 = pval; + while(*cp) { + if(isspace(*cp)) { + *cp++ = '\0'; + break; + } + if(*cp == '\\') { + cp++; + if(*cp != '\0') { + *cp2++ = *cp++; + } + } else { + *cp2++ = *cp++; + } + } + *cp2 = '\0'; + } else { + cp2 = pval; + cp++; + for(;;) { + if(*cp == '\0') { + sprintf(errorMessage, + "ERROR: PQconnectdb() - unterminated quoted string in conninfo\n"); + free(buf); + return -1; + } + if(*cp == '\\') { + cp++; + if(*cp != '\0') { + *cp2++ = *cp++; + } + continue; + } + if(*cp == '\'') { + *cp2 = '\0'; + cp++; + break; + } + *cp2++ = *cp++; + } + } + + /* ---------- + * Now we have the name and the value. Search + * for the param record. + * ---------- + */ + for(option = PQconninfoOptions; option->keyword != NULL; option++) { + if(!strcmp(option->keyword, pname)) { + break; + } + } + if(option->keyword == NULL) { + sprintf(errorMessage, + "ERROR: PQconnectdb() - unknown option '%s'\n", + pname); + free(buf); + return -1; + } + + /* ---------- + * Store the value + * ---------- + */ + option->val = strdup(pval); + } + + free(buf); + + /* ---------- + * Get the fallback resources for parameters not specified + * in the conninfo string. + * ---------- + */ + for(option = PQconninfoOptions; option->keyword != NULL; option++) { + if(option->val != NULL) continue; /* Value was in conninfo */ + + /* ---------- + * Try to get the environment variable fallback + * ---------- + */ + if(option->environ != NULL) { + if((tmp = getenv(option->environ)) != NULL) { + option->val = strdup(tmp); + continue; + } + } + + /* ---------- + * No environment variable specified or this one isn't set - + * try compiled in + * ---------- + */ + if(option->compiled != NULL) { + option->val = strdup(option->compiled); + continue; + } + + /* ---------- + * Special handling for user + * ---------- + */ + if(!strcmp(option->keyword, "user")) { + tmp = fe_getauthname(errortmp); + if (tmp) { + option->val = strdup(tmp); + } + } + + /* ---------- + * Special handling for dbname + * ---------- + */ + if(!strcmp(option->keyword, "dbname")) { + tmp = conninfo_getval("user"); + if (tmp) { + option->val = strdup(tmp); + } + } + } + + return 0; +} + + +static char* +conninfo_getval(char *keyword) +{ + PQconninfoOption *option; + + for(option = PQconninfoOptions; option->keyword != NULL; option++) { + if (!strcmp(option->keyword, keyword)) { + return option->val; + } + } + + return NULL; +} + + +static void +conninfo_free() +{ + PQconninfoOption *option; + + for(option = PQconninfoOptions; option->keyword != NULL; option++) { + if(option->val != NULL) { + free(option->val); + option->val = NULL; + } + } +} + /* =========== accessor functions for PGconn ========= */ char* PQdb(PGconn* conn) @@ -472,6 +883,16 @@ PQdb(PGconn* conn) return conn->dbName; } +char* +PQuser(PGconn* conn) +{ + if (!conn) { + fprintf(stderr,"PQuser() -- pointer to PGconn is null"); + return (char *)NULL; + } + return conn->pguser; +} + char* PQhost(PGconn* conn) { diff --git a/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-fe.h index 073b2cf52f..bbf8021c59 100644 --- a/src/interfaces/libpq/libpq-fe.h +++ b/src/interfaces/libpq/libpq-fe.h @@ -6,7 +6,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: libpq-fe.h,v 1.9 1996/11/04 04:00:56 momjian Exp $ + * $Id: libpq-fe.h,v 1.10 1996/11/09 10:39:54 scrappy Exp $ * *------------------------------------------------------------------------- */ @@ -146,8 +146,31 @@ struct _PQprintOpt { typedef struct _PQprintOpt PQprintOpt; +/* ---------------- + * Structure for the conninfo parameter definitions of PQconnectdb() + * ---------------- + */ +struct _PQconninfoOption { + char *keyword; /* The keyword of the option */ + char *environ; /* Fallback environment variable name */ + char *compiled; /* Fallback compiled in default value */ + char *val; /* Options value */ + char *label; /* Label for field in connect dialog */ + char *dispchar; /* Character to display for this field */ + /* in a connect dialog. Values are: */ + /* "" Display entered value as is */ + /* "*" Password field - hide value */ + /* "D" Debug options - don't */ + /* create a field by default */ + int dispsize; /* Field size in characters for dialog */ +}; + +typedef struct _PQconninfoOption PQconninfoOption; + /* === in fe-connect.c === */ /* make a new client connection to the backend */ +extern PGconn* PQconnectdb(const char* conninfo); +extern PQconninfoOption *PQconndefaults(); extern PGconn* PQsetdb(const char* pghost, const char* pgport, const char* pgoptions, const char* pgtty, const char* dbName); /* close the current connection and free the PGconn data structure */ @@ -157,6 +180,7 @@ extern void PQfinish(PGconn* conn); extern void PQreset(PGconn* conn); extern char* PQdb(PGconn* conn); +extern char* PQuser(PGconn* conn); extern char* PQhost(PGconn* conn); extern char* PQoptions(PGconn* conn); extern char* PQport(PGconn* conn);