mirror of
https://github.com/postgres/postgres.git
synced 2025-05-01 01:04:50 +03:00
985 lines
25 KiB
C
985 lines
25 KiB
C
/* 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@postgresql.org>
|
|
on Feb. 5th, 1998 */
|
|
|
|
#include <stdio.h>
|
|
#include <locale.h>
|
|
|
|
#include <ecpgtype.h>
|
|
#include <ecpglib.h>
|
|
#include <ecpgerrno.h>
|
|
#include "extern.h"
|
|
#include <sqlca.h>
|
|
#include <sql3types.h>
|
|
|
|
/* variables visible to the programs */
|
|
struct sqlca sqlca =
|
|
{
|
|
{'S', 'Q', 'L', 'C', 'A', ' ', ' ', ' '},
|
|
sizeof(struct sqlca),
|
|
0,
|
|
{0, {0}},
|
|
{'N', 'O', 'T', ' ', 'S', 'E', 'T', ' '},
|
|
{0, 0, 0, 0, 0, 0},
|
|
{0, 0, 0, 0, 0, 0, 0, 0},
|
|
{0, 0, 0, 0, 0, 0, 0, 0}
|
|
};
|
|
|
|
struct variable
|
|
{
|
|
enum ECPGttype type;
|
|
void *value;
|
|
void *pointer;
|
|
long varcharsize;
|
|
long arrsize;
|
|
long offset;
|
|
enum ECPGttype ind_type;
|
|
void *ind_value;
|
|
long ind_varcharsize;
|
|
long ind_arrsize;
|
|
long ind_offset;
|
|
struct variable *next;
|
|
};
|
|
|
|
/* keep a list of memory we allocated for the user */
|
|
static struct auto_mem
|
|
{
|
|
void *pointer;
|
|
struct auto_mem *next;
|
|
} *auto_allocs = NULL;
|
|
|
|
static void
|
|
add_mem(void *ptr, int lineno)
|
|
{
|
|
struct auto_mem *am = (struct auto_mem *) ecpg_alloc(sizeof(struct auto_mem), lineno);
|
|
|
|
am->next = auto_allocs;
|
|
auto_allocs = am;
|
|
}
|
|
|
|
void free_auto_mem(void)
|
|
{
|
|
struct auto_mem *am;
|
|
|
|
/* free all memory we have allocated for the user */
|
|
for (am = auto_allocs; am;)
|
|
{
|
|
struct auto_mem *act = am;
|
|
|
|
am = am->next;
|
|
free(act->pointer);
|
|
free(act);
|
|
}
|
|
|
|
auto_allocs = NULL;
|
|
}
|
|
|
|
/* This function returns a newly malloced string that has the \
|
|
in the argument quoted with \ and the ' quote with ' as SQL92 says.
|
|
*/
|
|
static
|
|
char *
|
|
quote_postgres(char *arg, int lineno)
|
|
{
|
|
char *res = (char *) ecpg_alloc(2 * strlen(arg) + 3, lineno);
|
|
int i,
|
|
ri = 0;
|
|
|
|
if (!res)
|
|
return (res);
|
|
|
|
res[ri++] = '\'';
|
|
for (i = 0; arg[i]; i++, ri++)
|
|
{
|
|
switch (arg[i])
|
|
{
|
|
case '\'':
|
|
res[ri++] = '\'';
|
|
break;
|
|
case '\\':
|
|
res[ri++] = '\\';
|
|
break;
|
|
default:
|
|
;
|
|
}
|
|
|
|
res[ri] = arg[i];
|
|
}
|
|
res[ri++] = '\'';
|
|
res[ri] = '\0';
|
|
|
|
return res;
|
|
}
|
|
|
|
/*
|
|
* create a list of variables
|
|
* The variables are listed with input variables preceeding outputvariables
|
|
* The end of each group is marked by an end marker.
|
|
* per variable we list:
|
|
* type - as defined in ecpgtype.h
|
|
* value - where to store the data
|
|
* varcharsize - length of string in case we have a stringvariable, else 0
|
|
* arraysize - 0 for pointer (we don't know the size of the array),
|
|
* 1 for simple variable, size for arrays
|
|
* offset - offset between ith and (i+1)th entry in an array,
|
|
* normally that means sizeof(type)
|
|
* ind_type - type of indicator variable
|
|
* ind_value - pointer to indicator variable
|
|
* ind_varcharsize - empty
|
|
* ind_arraysize - arraysize of indicator array
|
|
* ind_offset - indicator offset
|
|
*/
|
|
static bool
|
|
create_statement(int lineno, struct connection * connection, struct statement ** stmt, char *query, va_list ap)
|
|
{
|
|
struct variable **list = &((*stmt)->inlist);
|
|
enum ECPGttype type;
|
|
|
|
if (!(*stmt = (struct statement *) ecpg_alloc(sizeof(struct statement), lineno)))
|
|
return false;
|
|
|
|
(*stmt)->command = query;
|
|
(*stmt)->connection = connection;
|
|
(*stmt)->lineno = lineno;
|
|
|
|
list = &((*stmt)->inlist);
|
|
|
|
type = va_arg(ap, enum ECPGttype);
|
|
|
|
while (type != ECPGt_EORT)
|
|
{
|
|
if (type == ECPGt_EOIT)
|
|
list = &((*stmt)->outlist);
|
|
else
|
|
{
|
|
struct variable *var,
|
|
*ptr;
|
|
|
|
if (!(var = (struct variable *) ecpg_alloc(sizeof(struct variable), lineno)))
|
|
return false;
|
|
|
|
var->type = type;
|
|
var->pointer = va_arg(ap, void *);
|
|
|
|
/* if variable is NULL, the statement hasn't been prepared */
|
|
if (var->pointer == NULL)
|
|
{
|
|
ECPGlog("create_statement: invalid statement name\n");
|
|
ECPGraise(lineno, ECPG_INVALID_STMT, NULL);
|
|
free(var);
|
|
return false;
|
|
}
|
|
|
|
var->varcharsize = va_arg(ap, long);
|
|
var->arrsize = va_arg(ap, long);
|
|
var->offset = va_arg(ap, long);
|
|
|
|
if (var->arrsize == 0 || var->varcharsize == 0)
|
|
var->value = *((void **) (var->pointer));
|
|
else
|
|
var->value = var->pointer;
|
|
|
|
var->ind_type = va_arg(ap, enum ECPGttype);
|
|
var->ind_value = va_arg(ap, void *);
|
|
var->ind_varcharsize = va_arg(ap, long);
|
|
var->ind_arrsize = va_arg(ap, long);
|
|
var->ind_offset = va_arg(ap, long);
|
|
var->next = NULL;
|
|
|
|
for (ptr = *list; ptr && ptr->next; ptr = ptr->next);
|
|
|
|
if (ptr == NULL)
|
|
*list = var;
|
|
else
|
|
ptr->next = var;
|
|
}
|
|
|
|
type = va_arg(ap, enum ECPGttype);
|
|
}
|
|
|
|
return (true);
|
|
}
|
|
|
|
static void
|
|
free_variable(struct variable * var)
|
|
{
|
|
struct variable *var_next;
|
|
|
|
if (var == (struct variable *) NULL)
|
|
return;
|
|
var_next = var->next;
|
|
free(var);
|
|
|
|
while (var_next)
|
|
{
|
|
var = var_next;
|
|
var_next = var->next;
|
|
free(var);
|
|
}
|
|
}
|
|
|
|
static void
|
|
free_statement(struct statement * stmt)
|
|
{
|
|
if (stmt == (struct statement *) NULL)
|
|
return;
|
|
free_variable(stmt->inlist);
|
|
free_variable(stmt->outlist);
|
|
free(stmt);
|
|
}
|
|
|
|
static char *
|
|
next_insert(char *text)
|
|
{
|
|
char *ptr = text;
|
|
bool string = false;
|
|
|
|
for (; *ptr != '\0' && (*ptr != '?' || string); ptr++)
|
|
if (*ptr == '\'' && *(ptr-1) != '\\')
|
|
string = string ? false : true;
|
|
|
|
return (*ptr == '\0') ? NULL : ptr;
|
|
}
|
|
|
|
static bool
|
|
ECPGexecute(struct statement * stmt)
|
|
{
|
|
bool status = false;
|
|
char *copiedquery;
|
|
PGresult *results, *query;
|
|
PGnotify *notify;
|
|
struct variable *var;
|
|
|
|
copiedquery = ecpg_strdup(stmt->command, stmt->lineno);
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
var = stmt->inlist;
|
|
while (var)
|
|
{
|
|
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).
|
|
*/
|
|
|
|
buff[0] = '\0';
|
|
|
|
/* check for null value and set input buffer accordingly */
|
|
switch (var->ind_type)
|
|
{
|
|
case ECPGt_short:
|
|
case ECPGt_unsigned_short:
|
|
if (*(short *) var->ind_value < 0)
|
|
strcpy(buff, "null");
|
|
break;
|
|
case ECPGt_int:
|
|
case ECPGt_unsigned_int:
|
|
if (*(int *) var->ind_value < 0)
|
|
strcpy(buff, "null");
|
|
break;
|
|
case ECPGt_long:
|
|
case ECPGt_unsigned_long:
|
|
if (*(long *) var->ind_value < 0L)
|
|
strcpy(buff, "null");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (*buff == '\0')
|
|
{
|
|
switch (var->type)
|
|
{
|
|
int element;
|
|
|
|
case ECPGt_short:
|
|
if (!(mallocedval = ecpg_alloc(var->arrsize * 20, stmt->lineno)))
|
|
return false;
|
|
|
|
sprintf(mallocedval, "%s", (var->arrsize > 1) ? "'{" : "");
|
|
|
|
for (element = 0; element < var->arrsize; element++)
|
|
sprintf(mallocedval + strlen(mallocedval), "%d,", ((short *) var->value)[element]);
|
|
|
|
sprintf(mallocedval + strlen(mallocedval) - 1, "%s", (var->arrsize > 1) ? "}'" : "");
|
|
|
|
tobeinserted = mallocedval;
|
|
break;
|
|
|
|
case ECPGt_int:
|
|
if (!(mallocedval = ecpg_alloc(var->arrsize * 20, stmt->lineno)))
|
|
return false;
|
|
|
|
sprintf(mallocedval, "%s", (var->arrsize > 1) ? "'{" : "");
|
|
|
|
for (element = 0; element < var->arrsize; element++)
|
|
sprintf(mallocedval + strlen(mallocedval), "%d,", ((int *) var->value)[element]);
|
|
|
|
sprintf(mallocedval + strlen(mallocedval) - 1, "%s", (var->arrsize > 1) ? "}'" : "");
|
|
|
|
tobeinserted = mallocedval;
|
|
break;
|
|
|
|
case ECPGt_unsigned_short:
|
|
if (!(mallocedval = ecpg_alloc(var->arrsize * 20, stmt->lineno)))
|
|
return false;
|
|
|
|
sprintf(mallocedval, "%s", (var->arrsize > 1) ? "'{" : "");
|
|
|
|
for (element = 0; element < var->arrsize; element++)
|
|
sprintf(mallocedval + strlen(mallocedval), "%d,", ((unsigned short *) var->value)[element]);
|
|
|
|
sprintf(mallocedval + strlen(mallocedval) - 1, "%s", (var->arrsize > 1) ? "}'" : "");
|
|
|
|
tobeinserted = mallocedval;
|
|
break;
|
|
|
|
case ECPGt_unsigned_int:
|
|
if (!(mallocedval = ecpg_alloc(var->arrsize * 20, stmt->lineno)))
|
|
return false;
|
|
|
|
sprintf(mallocedval, "%s", (var->arrsize > 1) ? "'{" : "");
|
|
|
|
for (element = 0; element < var->arrsize; element++)
|
|
sprintf(mallocedval + strlen(mallocedval), "%d,", ((unsigned int *) var->value)[element]);
|
|
|
|
sprintf(mallocedval + strlen(mallocedval) - 1, "%s", (var->arrsize > 1) ? "}'" : "");
|
|
|
|
tobeinserted = mallocedval;
|
|
break;
|
|
|
|
case ECPGt_long:
|
|
if (!(mallocedval = ecpg_alloc(var->arrsize * 20, stmt->lineno)))
|
|
return false;
|
|
|
|
sprintf(mallocedval, "%s", (var->arrsize > 1) ? "'{" : "");
|
|
|
|
for (element = 0; element < var->arrsize; element++)
|
|
sprintf(mallocedval + strlen(mallocedval), "%ld,", ((long *) var->value)[element]);
|
|
|
|
sprintf(mallocedval + strlen(mallocedval) - 1, "%s", (var->arrsize > 1) ? "}'" : "");
|
|
|
|
tobeinserted = mallocedval;
|
|
break;
|
|
|
|
case ECPGt_unsigned_long:
|
|
if (!(mallocedval = ecpg_alloc(var->arrsize * 20, stmt->lineno)))
|
|
return false;
|
|
|
|
sprintf(mallocedval, "%s", (var->arrsize > 1) ? "'{" : "");
|
|
|
|
for (element = 0; element < var->arrsize; element++)
|
|
sprintf(mallocedval + strlen(mallocedval), "%ld,", ((unsigned long *) var->value)[element]);
|
|
|
|
sprintf(mallocedval + strlen(mallocedval) - 1, "%s", (var->arrsize > 1) ? "}'" : "");
|
|
|
|
tobeinserted = mallocedval;
|
|
break;
|
|
|
|
case ECPGt_float:
|
|
if (!(mallocedval = ecpg_alloc(var->arrsize * 20, stmt->lineno)))
|
|
return false;
|
|
|
|
sprintf(mallocedval, "%s", (var->arrsize > 1) ? "'{" : "");
|
|
|
|
for (element = 0; element < var->arrsize; element++)
|
|
sprintf(mallocedval + strlen(mallocedval), "%.14g,", ((float *) var->value)[element]);
|
|
|
|
sprintf(mallocedval + strlen(mallocedval) - 1, "%s", (var->arrsize > 1) ? "}'" : "");
|
|
|
|
tobeinserted = mallocedval;
|
|
break;
|
|
|
|
case ECPGt_double:
|
|
if (!(mallocedval = ecpg_alloc(var->arrsize * 20, stmt->lineno)))
|
|
return false;
|
|
|
|
sprintf(mallocedval, "%s", (var->arrsize > 1) ? "'{" : "");
|
|
|
|
for (element = 0; element < var->arrsize; element++)
|
|
sprintf(mallocedval + strlen(mallocedval), "%.14g,", ((double *) var->value)[element]);
|
|
|
|
sprintf(mallocedval + strlen(mallocedval) - 1, "%s", (var->arrsize > 1) ? "}'" : "");
|
|
|
|
tobeinserted = mallocedval;
|
|
break;
|
|
|
|
case ECPGt_bool:
|
|
if (!(mallocedval = ecpg_alloc(var->arrsize * 20, stmt->lineno)))
|
|
return false;
|
|
|
|
sprintf(mallocedval, "%s", (var->arrsize > 1) ? "'{" : "");
|
|
|
|
for (element = 0; element < var->arrsize; element++)
|
|
sprintf(mallocedval + strlen(mallocedval), "%c,", (((char *) var->value)[element]) ? 't' : 'f');
|
|
|
|
sprintf(mallocedval + strlen(mallocedval) - 1, "%s", (var->arrsize > 1) ? "}'" : "");
|
|
|
|
tobeinserted = mallocedval;
|
|
break;
|
|
|
|
case ECPGt_char:
|
|
case ECPGt_unsigned_char:
|
|
{
|
|
/* set slen to string length if type is char * */
|
|
int slen = (var->varcharsize == 0) ? strlen((char *) var->value) : var->varcharsize;
|
|
|
|
if (!(newcopy = ecpg_alloc(slen + 1, stmt->lineno)))
|
|
return false;
|
|
|
|
strncpy(newcopy, (char *) var->value, slen);
|
|
newcopy[slen] = '\0';
|
|
|
|
mallocedval = quote_postgres(newcopy, stmt->lineno);
|
|
if (!mallocedval)
|
|
return false;
|
|
|
|
free(newcopy);
|
|
|
|
tobeinserted = mallocedval;
|
|
}
|
|
break;
|
|
case ECPGt_char_variable:
|
|
{
|
|
int slen = strlen((char *) var->value);
|
|
|
|
if (!(mallocedval = ecpg_alloc(slen + 1, stmt->lineno)))
|
|
return false;
|
|
|
|
strncpy(mallocedval, (char *) var->value, slen);
|
|
mallocedval[slen] = '\0';
|
|
|
|
tobeinserted = mallocedval;
|
|
}
|
|
break;
|
|
case ECPGt_varchar:
|
|
{
|
|
struct ECPGgeneric_varchar *variable =
|
|
(struct ECPGgeneric_varchar *) (var->value);
|
|
|
|
if (!(newcopy = (char *) ecpg_alloc(variable->len + 1, stmt->lineno)))
|
|
return false;
|
|
|
|
strncpy(newcopy, variable->arr, variable->len);
|
|
newcopy[variable->len] = '\0';
|
|
|
|
mallocedval = quote_postgres(newcopy, stmt->lineno);
|
|
if (!mallocedval)
|
|
return false;
|
|
|
|
free(newcopy);
|
|
|
|
tobeinserted = mallocedval;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
/* Not implemented yet */
|
|
ECPGraise(stmt->lineno, ECPG_UNSUPPORTED, ECPGtype_name(var->type));
|
|
return false;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
tobeinserted = buff;
|
|
|
|
/*
|
|
* Now tobeinserted points to an area that is to be inserted at
|
|
* the first %s
|
|
*/
|
|
if (!(newcopy = (char *) ecpg_alloc(strlen(copiedquery) + strlen(tobeinserted) + 1, stmt->lineno)))
|
|
return false;
|
|
|
|
strcpy(newcopy, copiedquery);
|
|
if ((p = next_insert(newcopy)) == NULL)
|
|
{
|
|
|
|
/*
|
|
* We have an argument but we dont have the matched up string
|
|
* in the string
|
|
*/
|
|
ECPGraise(stmt->lineno, ECPG_TOO_MANY_ARGUMENTS, NULL);
|
|
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)
|
|
+ sizeof("?") - 1 /* don't count the '\0' */ );
|
|
}
|
|
|
|
/*
|
|
* Now everything is safely copied to the newcopy. Lets free the
|
|
* oldcopy and let the copiedquery get the var->value from the
|
|
* newcopy.
|
|
*/
|
|
if (mallocedval != NULL)
|
|
{
|
|
free(mallocedval);
|
|
mallocedval = NULL;
|
|
}
|
|
|
|
free(copiedquery);
|
|
copiedquery = newcopy;
|
|
|
|
var = var->next;
|
|
}
|
|
|
|
/* Check if there are unmatched things left. */
|
|
if (next_insert(copiedquery) != NULL)
|
|
{
|
|
ECPGraise(stmt->lineno, ECPG_TOO_FEW_ARGUMENTS, NULL);
|
|
return false;
|
|
}
|
|
|
|
/* Now the request is built. */
|
|
|
|
if (stmt->connection->committed && !stmt->connection->autocommit)
|
|
{
|
|
if ((results = PQexec(stmt->connection->connection, "begin transaction")) == NULL)
|
|
{
|
|
ECPGraise(stmt->lineno, ECPG_TRANS, NULL);
|
|
return false;
|
|
}
|
|
PQclear(results);
|
|
stmt->connection->committed = false;
|
|
}
|
|
|
|
ECPGlog("ECPGexecute line %d: QUERY: %s on connection %s\n", stmt->lineno, copiedquery, stmt->connection->name);
|
|
results = PQexec(stmt->connection->connection, copiedquery);
|
|
free(copiedquery);
|
|
|
|
if (results == NULL)
|
|
{
|
|
ECPGlog("ECPGexecute line %d: error: %s", stmt->lineno,
|
|
PQerrorMessage(stmt->connection->connection));
|
|
ECPGraise(stmt->lineno, ECPG_PGSQL, PQerrorMessage(stmt->connection->connection));
|
|
}
|
|
else
|
|
{
|
|
var = stmt->outlist;
|
|
switch (PQresultStatus(results))
|
|
{
|
|
int nfields,
|
|
ntuples,
|
|
act_tuple,
|
|
act_field,
|
|
isarray;
|
|
|
|
case PGRES_TUPLES_OK:
|
|
nfields = PQnfields(results);
|
|
sqlca.sqlerrd[2] = ntuples = PQntuples(results);
|
|
status = true;
|
|
|
|
if (ntuples < 1)
|
|
{
|
|
ECPGlog("ECPGexecute line %d: Incorrect number of matches: %d\n",
|
|
stmt->lineno, ntuples);
|
|
ECPGraise(stmt->lineno, ECPG_NOT_FOUND, NULL);
|
|
status = false;
|
|
break;
|
|
}
|
|
|
|
for (act_field = 0; act_field < nfields && status; act_field++)
|
|
{
|
|
char *array_query;
|
|
|
|
if (var == NULL)
|
|
{
|
|
ECPGlog("ECPGexecute line %d: Too few arguments.\n", stmt->lineno);
|
|
ECPGraise(stmt->lineno, ECPG_TOO_FEW_ARGUMENTS, NULL);
|
|
return (false);
|
|
}
|
|
|
|
array_query = (char *)ecpg_alloc(strlen("select typelem from pg_type where oid=") + 11, stmt -> lineno);
|
|
sprintf(array_query, "select typelem from pg_type where oid=%d", PQftype(results, act_field));
|
|
query = PQexec(stmt->connection->connection, array_query);
|
|
isarray = 0;
|
|
if (PQresultStatus(query) == PGRES_TUPLES_OK) {
|
|
isarray = atol((char *)PQgetvalue(query, 0, 0));
|
|
if (ECPGDynamicType(PQftype(results, act_field)) == SQL3_CHARACTER ||
|
|
ECPGDynamicType(PQftype(results, act_field)) == SQL3_CHARACTER_VARYING)
|
|
{
|
|
/* arrays of character strings are not yet implemented */
|
|
isarray = false;
|
|
}
|
|
ECPGlog("ECPGexecute line %d: TYPE database: %d C: %d array: %s\n", stmt->lineno, PQftype(results, act_field), var->type, isarray ? "yes" : "no");
|
|
}
|
|
PQclear(query);
|
|
|
|
if (!isarray)
|
|
{
|
|
/*
|
|
* if we don't have enough space, we cannot read all
|
|
* tuples
|
|
*/
|
|
if ((var->arrsize > 0 && ntuples > var->arrsize) || (var->ind_arrsize > 0 && ntuples > var->ind_arrsize))
|
|
{
|
|
ECPGlog("ECPGexecute line %d: Incorrect number of matches: %d don't fit into array of %d\n",
|
|
stmt->lineno, ntuples, var->arrsize);
|
|
ECPGraise(stmt->lineno, ECPG_TOO_MANY_MATCHES, NULL);
|
|
status = false;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* since we read an array, the variable has to be
|
|
* an array too
|
|
*/
|
|
if (var->arrsize == 0)
|
|
{
|
|
ECPGlog("ECPGexecute line %d: variable is not an array\n");
|
|
ECPGraise(stmt->lineno, ECPG_NO_ARRAY, NULL);
|
|
status = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* allocate memory for NULL pointers
|
|
*/
|
|
if ((var->arrsize == 0 || var->varcharsize == 0) && var->value == NULL)
|
|
{
|
|
int len = 0;
|
|
|
|
switch (var->type)
|
|
{
|
|
case ECPGt_char:
|
|
case ECPGt_unsigned_char:
|
|
var->varcharsize = 0;
|
|
/* check strlen for each tuple */
|
|
for (act_tuple = 0; act_tuple < ntuples; act_tuple++)
|
|
{
|
|
int len = strlen(PQgetvalue(results, act_tuple, act_field)) + 1;
|
|
|
|
if (len > var->varcharsize)
|
|
var->varcharsize = len;
|
|
}
|
|
var->offset *= var->varcharsize;
|
|
len = var->offset * ntuples;
|
|
break;
|
|
case ECPGt_varchar:
|
|
len = ntuples * (var->varcharsize + sizeof(int));
|
|
break;
|
|
default:
|
|
len = var->offset * ntuples;
|
|
break;
|
|
}
|
|
var->value = (void *) ecpg_alloc(len, stmt->lineno);
|
|
*((void **) var->pointer) = var->value;
|
|
add_mem(var->value, stmt->lineno);
|
|
}
|
|
|
|
for (act_tuple = 0; act_tuple < ntuples && status; act_tuple++)
|
|
{
|
|
if (!get_data(results, act_tuple, act_field, stmt->lineno,
|
|
var->type, var->ind_type, var->value,
|
|
var->ind_value, var->varcharsize, var->offset, isarray))
|
|
status = false;
|
|
}
|
|
var = var->next;
|
|
}
|
|
|
|
if (status && var != NULL)
|
|
{
|
|
ECPGraise(stmt->lineno, ECPG_TOO_MANY_ARGUMENTS, NULL);
|
|
status = false;
|
|
}
|
|
|
|
break;
|
|
case PGRES_EMPTY_QUERY:
|
|
/* do nothing */
|
|
ECPGraise(stmt->lineno, ECPG_EMPTY, NULL);
|
|
break;
|
|
case PGRES_COMMAND_OK:
|
|
status = true;
|
|
sqlca.sqlerrd[1] = atol(PQoidStatus(results));
|
|
sqlca.sqlerrd[2] = atol(PQcmdTuples(results));
|
|
ECPGlog("ECPGexecute line %d Ok: %s\n", stmt->lineno, PQcmdStatus(results));
|
|
break;
|
|
case PGRES_NONFATAL_ERROR:
|
|
case PGRES_FATAL_ERROR:
|
|
case PGRES_BAD_RESPONSE:
|
|
ECPGlog("ECPGexecute line %d: Error: %s",
|
|
stmt->lineno, PQerrorMessage(stmt->connection->connection));
|
|
ECPGraise(stmt->lineno, ECPG_PGSQL, PQerrorMessage(stmt->connection->connection));
|
|
status = false;
|
|
break;
|
|
case PGRES_COPY_OUT:
|
|
ECPGlog("ECPGexecute line %d: Got PGRES_COPY_OUT ... tossing.\n", stmt->lineno);
|
|
PQendcopy(stmt->connection->connection);
|
|
break;
|
|
case PGRES_COPY_IN:
|
|
ECPGlog("ECPGexecute line %d: Got PGRES_COPY_IN ... tossing.\n", stmt->lineno);
|
|
PQendcopy(stmt->connection->connection);
|
|
break;
|
|
default:
|
|
ECPGlog("ECPGexecute line %d: Got something else, postgres error.\n",
|
|
stmt->lineno);
|
|
ECPGraise(stmt->lineno, ECPG_PGSQL, PQerrorMessage(stmt->connection->connection));
|
|
status = false;
|
|
break;
|
|
}
|
|
PQclear(results);
|
|
}
|
|
|
|
/* check for asynchronous returns */
|
|
notify = PQnotifies(stmt->connection->connection);
|
|
if (notify)
|
|
{
|
|
ECPGlog("ECPGexecute line %d: ASYNC NOTIFY of '%s' from backend pid '%d' received\n",
|
|
stmt->lineno, notify->relname, notify->be_pid);
|
|
free(notify);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
bool
|
|
ECPGdo(int lineno, const char *connection_name, char *query, ...)
|
|
{
|
|
va_list args;
|
|
struct statement *stmt;
|
|
struct connection *con = get_connection(connection_name);
|
|
bool status=true;
|
|
char *locale = setlocale(LC_NUMERIC, NULL);
|
|
|
|
/* Make sure we do NOT honor the locale for numeric input/output */
|
|
/* since the database wants teh standard decimal point */
|
|
setlocale(LC_NUMERIC, "C");
|
|
|
|
if (!ecpg_init(con, connection_name, lineno))
|
|
{
|
|
setlocale(LC_NUMERIC, locale);
|
|
return(false);
|
|
}
|
|
|
|
va_start(args, query);
|
|
if (create_statement(lineno, con, &stmt, query, args) == false)
|
|
{
|
|
setlocale(LC_NUMERIC, locale);
|
|
return (false);
|
|
}
|
|
va_end(args);
|
|
|
|
/* are we connected? */
|
|
if (con == NULL || con->connection == NULL)
|
|
{
|
|
free_statement(stmt);
|
|
ECPGlog("ECPGdo: not connected to %s\n", con->name);
|
|
ECPGraise(lineno, ECPG_NOT_CONN, NULL);
|
|
setlocale(LC_NUMERIC, locale);
|
|
return false;
|
|
}
|
|
|
|
status = ECPGexecute(stmt);
|
|
free_statement(stmt);
|
|
|
|
/* and reset locale value so our application is not affected */
|
|
setlocale(LC_NUMERIC, locale);
|
|
return (status);
|
|
}
|
|
|
|
/* dynamic SQL support routines
|
|
*
|
|
* Copyright (c) 2000, Christof Petig <christof.petig@wtal.de>
|
|
*
|
|
* $Header: /cvsroot/pgsql/src/interfaces/ecpg/lib/Attic/execute.c,v 1.2 2000/03/17 23:26:31 tgl Exp $
|
|
*/
|
|
|
|
PGconn *ECPG_internal_get_connection(char *name);
|
|
|
|
extern struct descriptor
|
|
{
|
|
char *name;
|
|
PGresult *result;
|
|
struct descriptor *next;
|
|
} *all_descriptors;
|
|
|
|
/* like ECPGexecute */
|
|
static bool execute_descriptor(int lineno,const char *query
|
|
,struct connection *con,PGresult **resultptr)
|
|
{
|
|
bool status = false;
|
|
PGresult *results;
|
|
PGnotify *notify;
|
|
|
|
/* Now the request is built. */
|
|
|
|
if (con->committed && !con->autocommit)
|
|
{
|
|
if ((results = PQexec(con->connection, "begin transaction")) == NULL)
|
|
{
|
|
ECPGraise(lineno, ECPG_TRANS, NULL);
|
|
return false;
|
|
}
|
|
PQclear(results);
|
|
con->committed = false;
|
|
}
|
|
|
|
ECPGlog("execute_descriptor line %d: QUERY: %s on connection %s\n", lineno, query, con->name);
|
|
results = PQexec(con->connection, query);
|
|
|
|
if (results == NULL)
|
|
{
|
|
ECPGlog("ECPGexecute line %d: error: %s", lineno,
|
|
PQerrorMessage(con->connection));
|
|
ECPGraise(lineno, ECPG_PGSQL, PQerrorMessage(con->connection));
|
|
}
|
|
else
|
|
{ *resultptr=results;
|
|
switch (PQresultStatus(results))
|
|
{ int ntuples;
|
|
case PGRES_TUPLES_OK:
|
|
status = true;
|
|
sqlca.sqlerrd[2] = ntuples = PQntuples(results);
|
|
if (ntuples < 1)
|
|
{
|
|
ECPGlog("execute_descriptor line %d: Incorrect number of matches: %d\n",
|
|
lineno, ntuples);
|
|
ECPGraise(lineno, ECPG_NOT_FOUND, NULL);
|
|
status = false;
|
|
break;
|
|
}
|
|
break;
|
|
#if 1 /* strictly these are not needed (yet) */
|
|
case PGRES_EMPTY_QUERY:
|
|
/* do nothing */
|
|
ECPGraise(lineno, ECPG_EMPTY, NULL);
|
|
break;
|
|
case PGRES_COMMAND_OK:
|
|
status = true;
|
|
sqlca.sqlerrd[1] = atol(PQoidStatus(results));
|
|
sqlca.sqlerrd[2] = atol(PQcmdTuples(results));
|
|
ECPGlog("ECPGexecute line %d Ok: %s\n", lineno, PQcmdStatus(results));
|
|
break;
|
|
case PGRES_COPY_OUT:
|
|
ECPGlog("ECPGexecute line %d: Got PGRES_COPY_OUT ... tossing.\n", lineno);
|
|
PQendcopy(con->connection);
|
|
break;
|
|
case PGRES_COPY_IN:
|
|
ECPGlog("ECPGexecute line %d: Got PGRES_COPY_IN ... tossing.\n", lineno);
|
|
PQendcopy(con->connection);
|
|
break;
|
|
#else
|
|
case PGRES_EMPTY_QUERY:
|
|
case PGRES_COMMAND_OK:
|
|
case PGRES_COPY_OUT:
|
|
case PGRES_COPY_IN:
|
|
break;
|
|
#endif
|
|
case PGRES_NONFATAL_ERROR:
|
|
case PGRES_FATAL_ERROR:
|
|
case PGRES_BAD_RESPONSE:
|
|
ECPGlog("ECPGexecute line %d: Error: %s",
|
|
lineno, PQerrorMessage(con->connection));
|
|
ECPGraise(lineno, ECPG_PGSQL, PQerrorMessage(con->connection));
|
|
status = false;
|
|
break;
|
|
default:
|
|
ECPGlog("ECPGexecute line %d: Got something else, postgres error.\n",
|
|
lineno);
|
|
ECPGraise(lineno, ECPG_PGSQL, PQerrorMessage(con->connection));
|
|
status = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* check for asynchronous returns */
|
|
notify = PQnotifies(con->connection);
|
|
if (notify)
|
|
{
|
|
ECPGlog("ECPGexecute line %d: ASYNC NOTIFY of '%s' from backend pid '%d' received\n",
|
|
lineno, notify->relname, notify->be_pid);
|
|
free(notify);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
/* like ECPGdo */
|
|
static bool do_descriptor2(int lineno,const char *connection_name,
|
|
PGresult **resultptr, const char *query)
|
|
{
|
|
struct connection *con = get_connection(connection_name);
|
|
bool status=true;
|
|
char *locale = setlocale(LC_NUMERIC, NULL);
|
|
|
|
/* Make sure we do NOT honor the locale for numeric input/output */
|
|
/* since the database wants teh standard decimal point */
|
|
setlocale(LC_NUMERIC, "C");
|
|
|
|
if (!ecpg_init(con, connection_name, lineno))
|
|
{ setlocale(LC_NUMERIC, locale);
|
|
return(false);
|
|
}
|
|
|
|
/* are we connected? */
|
|
if (con == NULL || con->connection == NULL)
|
|
{
|
|
ECPGlog("do_descriptor2: not connected to %s\n", con->name);
|
|
ECPGraise(lineno, ECPG_NOT_CONN, NULL);
|
|
setlocale(LC_NUMERIC, locale);
|
|
return false;
|
|
}
|
|
|
|
status = execute_descriptor(lineno,query,con,resultptr);
|
|
|
|
/* and reset locale value so our application is not affected */
|
|
setlocale(LC_NUMERIC, locale);
|
|
return (status);
|
|
}
|
|
|
|
bool ECPGdo_descriptor(int line,const char *connection,
|
|
const char *descriptor,const char *query)
|
|
{
|
|
struct descriptor *i;
|
|
for (i=all_descriptors;i!=NULL;i=i->next)
|
|
{ if (!strcmp(descriptor,i->name))
|
|
{
|
|
bool status;
|
|
|
|
/* free previous result */
|
|
if (i->result) PQclear(i->result);
|
|
i->result=NULL;
|
|
|
|
status=do_descriptor2(line,connection,&i->result,query);
|
|
|
|
if (!i->result) PQmakeEmptyPGresult(NULL, 0);
|
|
return (status);
|
|
}
|
|
}
|
|
|
|
ECPGraise(line, ECPG_UNKNOWN_DESCRIPTOR, descriptor);
|
|
return false;
|
|
}
|