diff options
author | Marc G. Fournier <scrappy@hub.org> | 1998-02-10 16:44:17 +0000 |
---|---|---|
committer | Marc G. Fournier <scrappy@hub.org> | 1998-02-10 16:44:17 +0000 |
commit | 38201e21d0849a469e165085e4d12ac0969f5018 (patch) | |
tree | b9e0e9db026ea6b3838f57025aebbca014f998f4 /src/interfaces/ecpg/lib | |
parent | a8313f9671c621852dbdf16b6f47e19ceda489ea (diff) | |
download | postgresql-38201e21d0849a469e165085e4d12ac0969f5018.tar.gz postgresql-38201e21d0849a469e165085e4d12ac0969f5018.zip |
Erk, the whole directory structure changed on us here...
Diffstat (limited to 'src/interfaces/ecpg/lib')
-rw-r--r-- | src/interfaces/ecpg/lib/Makefile | 26 | ||||
-rw-r--r-- | src/interfaces/ecpg/lib/Makefile.in | 25 | ||||
-rw-r--r-- | src/interfaces/ecpg/lib/ecpglib.c | 648 | ||||
-rw-r--r-- | src/interfaces/ecpg/lib/typename.c | 24 |
4 files changed, 723 insertions, 0 deletions
diff --git a/src/interfaces/ecpg/lib/Makefile b/src/interfaces/ecpg/lib/Makefile new file mode 100644 index 00000000000..e3722e8aec1 --- /dev/null +++ b/src/interfaces/ecpg/lib/Makefile @@ -0,0 +1,26 @@ +# Generated automatically from Makefile.in by configure. +SRCDIR= ../../.. +include $(SRCDIR)/Makefile.global + +PQ_INCLUDE=-I$(SRCDIR)/include -I$(SRCDIR)/interfaces/libpq + +all: lib + +lib: libecpg.a + +clean: + rm -f *.o *.a core a.out *~ + +install: libecpg.a + install -m 644 libecpg.a $(LIBDIR) + +uninstall:: + rm -f $(LIBDIR)/libecpg.a + +# Rules that do something +libecpg.a : libecpg.a(ecpglib.o) libecpg.a(typename.o) + +ecpglib.o : ecpglib.c ../include/ecpglib.h ../include/ecpgtype.h + $(CC) -O2 -g -Wall -I../include $(PQ_INCLUDE) -c ecpglib.c +typename.o : typename.c ../include/ecpgtype.h + $(CC) -g -O2 -Wall -I../include $(PQ_INCLUDE) -c typename.c diff --git a/src/interfaces/ecpg/lib/Makefile.in b/src/interfaces/ecpg/lib/Makefile.in new file mode 100644 index 00000000000..7ed351ab7a9 --- /dev/null +++ b/src/interfaces/ecpg/lib/Makefile.in @@ -0,0 +1,25 @@ +SRCDIR= ../../.. +include $(SRCDIR)/Makefile.global + +PQ_INCLUDE=-I$(SRCDIR)/include -I$(SRCDIR)/interfaces/libpq + +all: lib + +lib: libecpg.a + +clean: + rm -f *.o *.a core a.out *~ + +install: libecpg.a + install -m 644 libecpg.a $(LIBDIR) + +uninstall:: + rm -f $(LIBDIR)/libecpg.a + +# Rules that do something +libecpg.a : libecpg.a(ecpglib.o) libecpg.a(typename.o) + +ecpglib.o : ecpglib.c ../include/ecpglib.h ../include/ecpgtype.h + $(CC) -O2 -g -Wall -I../include $(PQ_INCLUDE) -c ecpglib.c +typename.o : typename.c ../include/ecpgtype.h + $(CC) -g -O2 -Wall -I../include $(PQ_INCLUDE) -c typename.c diff --git a/src/interfaces/ecpg/lib/ecpglib.c b/src/interfaces/ecpg/lib/ecpglib.c new file mode 100644 index 00000000000..80e9b0fd9c4 --- /dev/null +++ b/src/interfaces/ecpg/lib/ecpglib.c @@ -0,0 +1,648 @@ +/* Copyright comment */ +/* + * The aim is to get a simpler inteface to the database routines. + * All the tidieous messing around with tuples is supposed to be hidden + * by this function. + */ +/* Author: Linus Tolke + (actually most if the code is "borrowed" from the distribution and just + slightly modified) + */ + +/* Taken over as part of PostgreSQL by Michael Meskes <meskes@debian.org> + on Feb. 5th, 1998 */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdarg.h> +#include <string.h> + +#include <ecpgtype.h> +#include <ecpglib.h> +#include <sqlca.h> +#include <libpq-fe.h> +#include <libpq/pqcomm.h> + +static PGconn *simple_connection; +static int simple_debug = 0; +static FILE *debugstream = NULL; +static int committed = true; + +static void +register_error(int code, char *fmt,...) +{ + va_list args; + + sqlca.sqlcode = code; + va_start(args, fmt); + vsprintf(sqlca.sqlerrm.sqlerrmc, fmt, args); + va_end(args); + sqlca.sqlerrm.sqlerrml = strlen(sqlca.sqlerrm.sqlerrmc); +} + +/* This function returns a newly malloced string that has the ' and \ + in the argument quoted with \. + */ +static +char * +quote_postgres(char *arg) +{ + char *res = (char *) malloc(2 * strlen(arg) + 1); + int i, + ri; + + for (i = 0, ri = 0; arg[i]; i++, ri++) + { + switch (arg[i]) + { + case '\'': + case '\\': + res[ri++] = '\\'; + default: + ; + } + + res[ri] = arg[i]; + } + res[ri] = '\0'; + + return res; +} + + +bool +ECPGdo(int lineno, char *query,...) +{ + va_list ap; + bool status = false; + char *copiedquery; + PGresult *results; + PGnotify *notify; + enum ECPGttype type; + + va_start(ap, query); + + sqlca.sqlcode = 0; + copiedquery = strdup(query); + + type = va_arg(ap, enum ECPGttype); + + /* + * Now, if the type is one of the fill in types then we take the argument + * and enter that in the string at the first %s position. Then if there + * are any more fill in types we fill in at the next and so on. + */ + while (type != ECPGt_EOIT) + { + void *value = NULL; + short varcharsize; + short size; + short arrsize; + + char *newcopy; + char *mallocedval = NULL; + char *tobeinserted = NULL; + char *p; + char buff[20]; + + /* Some special treatment is needed for records since we want their + contents to arrive in a comma-separated list on insert (I think). */ + + value = va_arg(ap, void *); + varcharsize = va_arg(ap, short); + size = va_arg(ap, short); + arrsize = va_arg(ap, short); + + switch (type) + { + case ECPGt_char: + case ECPGt_short: + case ECPGt_int: + sprintf(buff, "%d", *(int *) value); + tobeinserted = buff; + break; + + case ECPGt_unsigned_char: + case ECPGt_unsigned_short: + case ECPGt_unsigned_int: + sprintf(buff, "%d", *(unsigned int *) value); + tobeinserted = buff; + break; + + case ECPGt_long: + sprintf(buff, "%ld", *(long *) value); + tobeinserted = buff; + break; + + case ECPGt_unsigned_long: + sprintf(buff, "%ld", *(unsigned long *) value); + tobeinserted = buff; + break; + + case ECPGt_float: + sprintf(buff, "%.14g", *(float *) value); + tobeinserted = buff; + break; + + case ECPGt_double: + sprintf(buff, "%.14g", *(double *) value); + tobeinserted = buff; + break; + + case ECPGt_bool: + sprintf(buff, "'%c'", (*(char *) value ? 't' : 'f')); + tobeinserted = buff; + break; + + case ECPGt_varchar: + case ECPGt_varchar2: + { + struct ECPGgeneric_varchar *var = + (struct ECPGgeneric_varchar *) value; + + newcopy = (char *) malloc(var->len + 1); + strncpy(newcopy, var->arr, var->len); + newcopy[var->len] = '\0'; + + mallocedval = (char *) malloc(2 * strlen(newcopy) + 3); + strcpy(mallocedval, "'"); + strcat(mallocedval, quote_postgres(newcopy)); + strcat(mallocedval, "'"); + + free(newcopy); + + tobeinserted = mallocedval; + } + break; + + default: + /* Not implemented yet */ + register_error(-1, "Unsupported type %s on line %d.", + ECPGtype_name(type), lineno); + return false; + break; + } + + /* Now tobeinserted points to an area that is to be inserted at + the first %s + */ + newcopy = (char *) malloc(strlen(copiedquery) + + strlen(tobeinserted) + + 1); + strcpy(newcopy, copiedquery); + if ((p = strstr(newcopy, ";;")) == NULL) + { + /* We have an argument but we dont have the matched up string + in the string + */ + register_error(-1, "Too many arguments line %d.", lineno); + return false; + } + else + { + strcpy(p, tobeinserted); + /* The strange thing in the second argument is the rest of the + string from the old string */ + strcat(newcopy, + copiedquery + + (p - newcopy) + + 2 /* Length of ;; */ ); + } + + /* Now everything is safely copied to the newcopy. Lets free the + oldcopy and let the copiedquery get the value from the newcopy. + */ + if (mallocedval != NULL) + { + free(mallocedval); + mallocedval = NULL; + } + + free(copiedquery); + copiedquery = newcopy; + + type = va_arg(ap, enum ECPGttype); + } + + /* Check if there are unmatched things left. */ + if (strstr(copiedquery, ";;") != NULL) + { + register_error(-1, "Too few arguments line %d.", lineno); + return false; + } + + /* Now then request is built. */ + + if (committed) + { + if ((results = PQexec(simple_connection, "begin")) == NULL) + { + register_error(-1, "Error starting transaction line %d.", lineno); + return false; + } + PQclear(results); + committed = 0; + } + + ECPGlog("ECPGdo line %d: QUERY: %s\n", lineno, copiedquery); + results = PQexec(simple_connection, copiedquery); + free(copiedquery); + + if (results == NULL) + { + ECPGlog("ECPGdo line %d: error: %s", lineno, + PQerrorMessage(simple_connection)); + register_error(-1, "Postgres error: %s line %d.", + PQerrorMessage(simple_connection), lineno); + } + else + switch (PQresultStatus(results)) + { + int m, + n, + x; + + case PGRES_TUPLES_OK: + /* XXX Cheap Hack. For now, we see only the last group + * of tuples. This is clearly not the right + * way to do things !! + */ + + m = PQnfields(results); + n = PQntuples(results); + + if (n < 1) + { + ECPGlog("ECPGdo lineno %d: Incorrect number of matches: %d\n", + lineno, n); + register_error(1, "Data not found line %d.", lineno); + break; + } + + if (n > 1) + { + ECPGlog("ECPGdo line %d: Incorrect number of matches: %d\n", + lineno, n); + register_error(-1, "To many matches line %d.", lineno); + break; + } + + status = true; + + for (x = 0; x < m && status; x++) + { + void *value = NULL; + short varcharsize; + short size; + short arrsize; + + char *pval = PQgetvalue(results, 0, x); + + /*long int * res_int; + char ** res_charstar; + char * res_char; + int res_len; */ + char *scan_length; + + ECPGlog("ECPGdo line %d: RESULT: %s\n", lineno, pval ? pval : ""); + + /* No the pval is a pointer to the value. */ + /* We will have to decode the value */ + type = va_arg(ap, enum ECPGttype); + value = va_arg(ap, void *); + varcharsize = va_arg(ap, short); + size = va_arg(ap, short); + arrsize = va_arg(ap, short); + + switch (type) + { + long res; + unsigned long ures; + double dres; + + case ECPGt_char: + case ECPGt_short: + case ECPGt_int: + case ECPGt_long: + if (pval) + { + res = strtol(pval, &scan_length, 10); + if (*scan_length != '\0') /* Garbage left */ + { + register_error(-1, "Not correctly formatted int type: %s line %d.", + pval, lineno); + status = false; + res = 0L; + } + } + else + res = 0L; + + /* Again?! Yes */ + switch (type) + { + case ECPGt_char: + *(char *) value = (char) res; + break; + case ECPGt_short: + *(short *) value = (short) res; + break; + case ECPGt_int: + *(int *) value = (int) res; + break; + case ECPGt_long: + *(long *) value = res; + break; + default: + /* Cannot happen */ + break; + } + break; + + case ECPGt_unsigned_char: + case ECPGt_unsigned_short: + case ECPGt_unsigned_int: + case ECPGt_unsigned_long: + if (pval) + { + ures = strtoul(pval, &scan_length, 10); + if (*scan_length != '\0') /* Garbage left */ + { + register_error(-1, "Not correctly formatted unsigned type: %s line %d.", + pval, lineno); + status = false; + ures = 0L; + } + } + else + ures = 0L; + + /* Again?! Yes */ + switch (type) + { + case ECPGt_unsigned_char: + *(unsigned char *) value = (unsigned char) ures; + break; + case ECPGt_unsigned_short: + *(unsigned short *) value = (unsigned short) ures; + break; + case ECPGt_unsigned_int: + *(unsigned int *) value = (unsigned int) ures; + break; + case ECPGt_unsigned_long: + *(unsigned long *) value = ures; + break; + default: + /* Cannot happen */ + break; + } + break; + + + case ECPGt_float: + case ECPGt_double: + if (pval) + { + dres = strtod(pval, &scan_length); + if (*scan_length != '\0') /* Garbage left */ + { + register_error(-1, "Not correctly formatted floating point type: %s line %d.", + pval, lineno); + status = false; + dres = 0.0; + } + } + else + dres = 0.0; + + /* Again?! Yes */ + switch (type) + { + case ECPGt_float: + *(float *) value = dres; + break; + case ECPGt_double: + *(double *) value = dres; + break; + default: + /* Cannot happen */ + break; + } + break; + + case ECPGt_bool: + if (pval) + { + if (pval[0] == 'f' && pval[1] == '\0') + { + *(char *) value = false; + break; + } + else if (pval[0] == 't' && pval[1] == '\0') + { + *(char *) value = true; + break; + } + } + + register_error(-1, "Unable to convert %s to bool on line %d.", + (pval ? pval : "NULL"), + lineno); + return false; + break; + + case ECPGt_varchar: + { + struct ECPGgeneric_varchar *var = + (struct ECPGgeneric_varchar *) value; + + strncpy(var->arr, pval, varcharsize); + var->len = strlen(pval); + if (var->len > varcharsize) + var->len = varcharsize; + } + break; + + case ECPGt_EORT: + ECPGlog("ECPGdo line %d: Too few arguments.\n", lineno); + register_error(-1, "Too few arguments line %d.", lineno); + status = false; + break; + + default: + register_error(-1, "Unsupported type %s on line %d.", + ECPGtype_name(type), lineno); + return false; + break; + } + } + + type = va_arg(ap, enum ECPGttype); + + if (status && type != ECPGt_EORT) + { + register_error(-1, "Too many arguments line %d.", lineno); + return false; + } + + PQclear(results); + break; + case PGRES_EMPTY_QUERY: + /* do nothing */ + register_error(-1, "Empty query line %d.", lineno); + break; + case PGRES_COMMAND_OK: + status = true; + ECPGlog("ECPGdo line %d Ok: %s\n", lineno, PQcmdStatus(results)); + break; + case PGRES_NONFATAL_ERROR: + case PGRES_FATAL_ERROR: + case PGRES_BAD_RESPONSE: + ECPGlog("ECPGdo line %d: Error: %s", + lineno, PQerrorMessage(simple_connection)); + register_error(-1, "Error: %s line %d.", + PQerrorMessage(simple_connection), lineno); + break; + case PGRES_COPY_OUT: + ECPGlog("ECPGdo line %d: Got PGRES_COPY_OUT ... tossing.\n", lineno); + PQendcopy(results->conn); + break; + case PGRES_COPY_IN: + ECPGlog("ECPGdo line %d: Got PGRES_COPY_IN ... tossing.\n", lineno); + PQendcopy(results->conn); + break; + default: + ECPGlog("ECPGdo line %d: Got something else, postgres error.\n", + lineno); + register_error(-1, "Postgres error line %d.", lineno); + break; + } + + /* check for asynchronous returns */ + notify = PQnotifies(simple_connection); + if (notify) + { + ECPGlog("ECPGdo line %d: ASYNC NOTIFY of '%s' from backend pid '%d' received\n", + lineno, notify->relname, notify->be_pid); + free(notify); + } + + va_end(ap); + return status; +} + + +bool +ECPGcommit(int lineno) +{ + PGresult *res; + + ECPGlog("ECPGcommit line %d\n", lineno); + if ((res = PQexec(simple_connection, "end")) == NULL) + { + register_error(-1, "Error committing line %d.", lineno); + return (FALSE); + } + PQclear(res); + committed = 1; + return (TRUE); +} + +bool +ECPGrollback(int lineno) +{ + PGresult *res; + + ECPGlog("ECPGrollback line %d\n", lineno); + if ((res = PQexec(simple_connection, "abort")) == NULL) + { + register_error(-1, "Error rolling back line %d.", lineno); + return (FALSE); + } + PQclear(res); + committed = 1; + return (TRUE); +} + + + +bool +ECPGsetdb(PGconn *newcon) +{ + ECPGfinish(); + simple_connection = newcon; + return true; +} + +bool +ECPGconnect(const char *dbname) +{ + char *name = strdup(dbname); + + ECPGlog("ECPGconnect: opening database %s\n", name); + + sqlca.sqlcode = 0; + + ECPGsetdb(PQsetdb(NULL, NULL, NULL, NULL, name)); + + free(name); + name = NULL; + + if (PQstatus(simple_connection) == CONNECTION_BAD) + { + ECPGfinish(); + ECPGlog("ECPGconnect: could not open database %s\n", dbname); + register_error(-1, "ECPGconnect: could not open database %s.", dbname); + return false; + } + return true; +} + + +bool +ECPGstatus() +{ + return PQstatus(simple_connection) != CONNECTION_BAD; +} + + +bool +ECPGfinish() +{ + if (simple_connection != NULL) + { + ECPGlog("ECPGfinish: finishing.\n"); + PQfinish(simple_connection); + } + else + ECPGlog("ECPGfinish: called an extra time.\n"); + return true; +} + +void +ECPGdebug(int n, FILE *dbgs) +{ + simple_debug = n; + debugstream = dbgs; + ECPGlog("ECPGdebug: set to %d\n", simple_debug); +} + +void +ECPGlog(const char *format,...) +{ + va_list ap; + + if (simple_debug) + { + char *f = (char *) malloc(strlen(format) + 100); + + sprintf(f, "[%d]: %s", getpid(), format); + + va_start(ap, format); + vfprintf(debugstream, f, ap); + va_end(ap); + + free(f); + } +} diff --git a/src/interfaces/ecpg/lib/typename.c b/src/interfaces/ecpg/lib/typename.c new file mode 100644 index 00000000000..55756037b30 --- /dev/null +++ b/src/interfaces/ecpg/lib/typename.c @@ -0,0 +1,24 @@ +#include <ecpgtype.h> +/* + * This function is used to generate the correct type names. + */ +const char * +ECPGtype_name(enum ECPGttype typ) +{ + switch (typ) + { + case ECPGt_char: return "char"; + case ECPGt_unsigned_char: return "unsigned char"; + case ECPGt_short: return "short"; + case ECPGt_unsigned_short: return "unsigned short"; + case ECPGt_int: return "int"; + case ECPGt_unsigned_int: return "unsigned int"; + case ECPGt_long: return "long"; + case ECPGt_unsigned_long: return "unsigned long"; + case ECPGt_float: return "float"; + case ECPGt_double: return "double"; + case ECPGt_bool: return "bool"; + default: + abort(); + } +} |