*** empty log message ***

This commit is contained in:
Michael Meskes 2000-02-16 16:18:29 +00:00
parent 988d53e5ea
commit 35ba9de276
16 changed files with 1002 additions and 22 deletions

View File

@ -809,5 +809,10 @@ Tue Feb 15 17:39:19 CET 2000
Wed Feb 16 11:57:02 CET 2000
- Fixed library to be able to input complete arrays.
Wed Feb 16 17:04:41 CET 2000
- Apply patch by Christof Petig <christof.petig@wtal.de> that adds
descriptors.
- Set library version to 3.1.0.
- Set ecpg version to 2.7.0.

View File

@ -0,0 +1,20 @@
descriptor statements have the following shortcomings
- up to now the only reasonable statement is
FETCH ... INTO SQL DESCRIPTOR <name>
no input variables allowed!
Reason: to fully support dynamic SQL the frontend/backend communication
should change to recognize input parameters.
Since this is not likely to happen in the near future and you
can cover the same functionality with the existing infrastructure
I'll leave the work to someone else.
- string buffer overflow does not always generate warnings
(beware: terminating 0 may be missing because strncpy is used)
:var=data sets sqlwarn accordingly (but not indicator)
- char variables pointing to NULL are not allocated on demand
- string truncation does not show up in indicator

View File

@ -18,8 +18,6 @@ cvariable for an array var
How can one insert arrays from c variables?
support for dynamic SQL with unknown number of variables with DESCRIPTORS
What happens to the output variable during read if there was an
indicator-error?

View File

@ -10,11 +10,13 @@ install::
$(INSTALL) $(INSTLOPTS) ecpglib.h $(HEADERDIR)
$(INSTALL) $(INSTLOPTS) ecpgtype.h $(HEADERDIR)
$(INSTALL) $(INSTLOPTS) sqlca.h $(HEADERDIR)
$(INSTALL) $(INSTLOPTS) sql3types.h $(HEADERDIR)
uninstall::
rm -f $(HEADERDIR)/ecpgerrno.h
rm -f $(HEADERDIR)/ecpglib.h
rm -f $(HEADERDIR)/ecpgtype.h
rm -f $(HEADERDIR)/sqlca.h
rm -f $(HEADERDIR)/sql3types.h
dep depend:

View File

@ -29,6 +29,10 @@
#define ECPG_INVALID_STMT -230
/* dynamic SQL related */
#define ECPG_UNKNOWN_DESCRIPTOR -240
#define ECPG_INVALID_DESCRIPTOR_INDEX -241
/* finally the backend error messages, they start at 400 */
#define ECPG_PGSQL -400
#define ECPG_TRANS -401

View File

@ -1,4 +1,5 @@
#include <postgres.h>
#include <libpq-fe.h>
#ifdef __cplusplus
extern "C"
@ -49,6 +50,17 @@ extern "C"
#define SQLCODE sqlca.sqlcode
/* dynamic SQL */
unsigned int ECPGDynamicType(Oid type);
unsigned int ECPGDynamicType_DDT(Oid type);
PGresult * ECPGresultByDescriptor(int line,const char *name);
bool ECPGdo_descriptor(int line,const char *connection,
const char *descriptor,const char *query);
bool ECPGdeallocate_desc(int line,const char *name);
bool ECPGallocate_desc(int line,const char *name);
void ECPGraise(int line,int code);
#ifdef __cplusplus
}

View File

@ -0,0 +1,38 @@
/* SQL3 dynamic type codes
*
* Copyright (c) 2000, Christof Petig <christof.petig@wtal.de>
*
* $Header: /cvsroot/pgsql/src/interfaces/ecpg/include/sql3types.h,v 1.1 2000/02/16 16:18:03 meskes Exp $
*/
/* chapter 13.1 table 2: Codes used for SQL data types in Dynamic SQL */
enum { SQL3_CHARACTER=1,
SQL3_NUMERIC,
SQL3_DECIMAL,
SQL3_INTEGER,
SQL3_SMALLINT,
SQL3_FLOAT,
SQL3_REAL,
SQL3_DOUBLE_PRECISION,
SQL3_DATE_TIME_TIMESTAMP,
SQL3_INTERVAL, //10
SQL3_CHARACTER_VARYING=12,
SQL3_ENUMERATED,
SQL3_BIT,
SQL3_BIT_VARYING,
SQL3_BOOLEAN,
SQL3_abstract
// the rest is xLOB stuff
};
/* chapter 13.1 table 3: Codes associated with datetime data types in Dynamic SQL */
enum { SQL3_DDT_DATE=1,
SQL3_DDT_TIME,
SQL3_DDT_TIMESTAMP,
SQL3_DDT_TIME_WITH_TIME_ZONE,
SQL3_DDT_TIMESTAMP_WITH_TIME_ZONE,
SQL3_DDT_ILLEGAL /* not a datetime data type (not part of standard) */
};

View File

@ -6,7 +6,7 @@
# Copyright (c) 1994, Regents of the University of California
#
# IDENTIFICATION
# $Header: /cvsroot/pgsql/src/interfaces/ecpg/lib/Attic/Makefile.in,v 1.57 2000/02/16 11:52:24 meskes Exp $
# $Header: /cvsroot/pgsql/src/interfaces/ecpg/lib/Attic/Makefile.in,v 1.58 2000/02/16 16:18:05 meskes Exp $
#
#-------------------------------------------------------------------------
@ -36,7 +36,7 @@ include $(SRCDIR)/Makefile.shlib
install: install-lib $(install-shlib-dep)
# Handmade dependencies in case make depend not done
ecpglib.o : ecpglib.c ../include/ecpglib.h ../include/ecpgtype.h
ecpglib.o : ecpglib.c ../include/ecpglib.h ../include/ecpgtype.h dynamic.c
typename.o : typename.c ../include/ecpgtype.h

View File

@ -0,0 +1,290 @@
/* dynamic SQL support routines
*
* Copyright (c) 2000, Christof Petig <christof.petig@wtal.de>
*
* $Header: /cvsroot/pgsql/src/interfaces/ecpg/lib/Attic/dynamic.c,v 1.1 2000/02/16 16:18:12 meskes Exp $
*/
/* I borrowed the include files from ecpglib.c, maybe we don't need all of them */
#if 0
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include <locale.h>
#include <libpq-fe.h>
#include <libpq/pqcomm.h>
#include <ecpgtype.h>
#include <ecpglib.h>
#include <sqlca.h>
#endif
#include <sql3types.h>
static struct descriptor
{ char *name;
PGresult *result;
struct descriptor *next;
} *all_descriptors=NULL;
PGconn *ECPG_internal_get_connection(char *name);
unsigned int ECPGDynamicType(Oid type)
{ switch(type)
{ case 16: return SQL3_BOOLEAN; /* bool */
case 21: return SQL3_SMALLINT; /* int2 */
case 23: return SQL3_INTEGER; /* int4 */
case 25: return SQL3_CHARACTER; /* text */
case 700: return SQL3_REAL; /* float4 */
case 701: return SQL3_DOUBLE_PRECISION; /* float8 */
case 1042: return SQL3_CHARACTER; /* bpchar */
case 1043: return SQL3_CHARACTER_VARYING; /* varchar */
case 1082: return SQL3_DATE_TIME_TIMESTAMP; /* date */
case 1083: return SQL3_DATE_TIME_TIMESTAMP; /* time */
case 1184: return SQL3_DATE_TIME_TIMESTAMP; /* datetime */
case 1296: return SQL3_DATE_TIME_TIMESTAMP; /* timestamp */
case 1700: return SQL3_NUMERIC; /* numeric */
default:
return -type;
}
}
unsigned int ECPGDynamicType_DDT(Oid type)
{ switch(type)
{
case 1082: return SQL3_DDT_DATE; /* date */
case 1083: return SQL3_DDT_TIME; /* time */
case 1184: return SQL3_DDT_TIMESTAMP_WITH_TIME_ZONE; /* datetime */
case 1296: return SQL3_DDT_TIMESTAMP_WITH_TIME_ZONE; /* timestamp */
default:
return SQL3_DDT_ILLEGAL;
}
}
// 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)
{
register_error(ECPG_TRANS, "Error in transaction processing line %d.", lineno);
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));
register_error(ECPG_PGSQL, "Postgres error: %s line %d.",
PQerrorMessage(con->connection), lineno);
}
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);
register_error(ECPG_NOT_FOUND, "No data found line %d.", lineno);
status = false;
break;
}
break;
#if 1 /* strictly these are not needed (yet) */
case PGRES_EMPTY_QUERY:
/* do nothing */
register_error(ECPG_EMPTY, "Empty query line %d.", lineno);
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));
register_error(ECPG_PGSQL, "Postgres error: %s line %d.",
PQerrorMessage(con->connection), lineno);
status = false;
break;
default:
ECPGlog("ECPGexecute line %d: Got something else, postgres error.\n",
lineno);
register_error(ECPG_PGSQL, "Postgres error: %s line %d.",
PQerrorMessage(con->connection), lineno);
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("ECPGdo: not connected to %s\n", con->name);
register_error(ECPG_NOT_CONN, "Not connected in line %d.", lineno);
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);
return false;
}
PGresult *ECPGresultByDescriptor(int line,const char *name)
{
struct descriptor *i;
for (i=all_descriptors;i!=NULL;i=i->next)
{ if (!strcmp(name,i->name)) return i->result;
}
ECPGraise(line,ECPG_UNKNOWN_DESCRIPTOR);
return 0;
}
bool ECPGdeallocate_desc(int line,const char *name)
{
struct descriptor *i;
struct descriptor **lastptr=&all_descriptors;
for (i=all_descriptors;i;lastptr=&i->next,i=i->next)
{ if (!strcmp(name,i->name))
{ *lastptr=i->next;
free(i->name);
PQclear(i->result);
free(i);
return true;
}
}
ECPGraise(line,ECPG_UNKNOWN_DESCRIPTOR);
return false;
}
bool ECPGallocate_desc(int line,const char *name)
{
struct descriptor *new=(struct descriptor *)malloc(sizeof(struct descriptor));
new->next=all_descriptors;
new->name=malloc(strlen(name)+1);
new->result=PQmakeEmptyPGresult(NULL, 0);
strcpy(new->name,name);
all_descriptors=new;
return true;
}
void ECPGraise(int line,int code)
{ sqlca.sqlcode=code;
switch (code)
{ case ECPG_NOT_FOUND:
snprintf(sqlca.sqlerrm.sqlerrmc,sizeof(sqlca.sqlerrm.sqlerrmc),
"No data found line %d.",line);
break;
case ECPG_MISSING_INDICATOR:
snprintf(sqlca.sqlerrm.sqlerrmc,sizeof(sqlca.sqlerrm.sqlerrmc),
"NULL value without indicator, line %d.",line);
break;
case ECPG_UNKNOWN_DESCRIPTOR:
snprintf(sqlca.sqlerrm.sqlerrmc,sizeof(sqlca.sqlerrm.sqlerrmc),
"descriptor not found, line %d.",line);
break;
case ECPG_INVALID_DESCRIPTOR_INDEX:
snprintf(sqlca.sqlerrm.sqlerrmc,sizeof(sqlca.sqlerrm.sqlerrmc),
"descriptor index out of range, line %d.",line);
break;
default:
snprintf(sqlca.sqlerrm.sqlerrmc,sizeof(sqlca.sqlerrm.sqlerrmc),
"SQL error #%d, line %d.",code,line);
break;
}
}

View File

@ -20,7 +20,6 @@
#include <ctype.h>
#include <locale.h>
#include <libpq-fe.h>
#include <libpq/pqcomm.h>
#include <ecpgtype.h>
#include <ecpglib.h>
@ -753,7 +752,7 @@ ECPGexecute(struct statement * stmt)
{
char *pval;
char *scan_length;
char *array_query;
char *array_query;
if (var == NULL)
{
@ -1127,36 +1126,44 @@ ECPGexecute(struct statement * stmt)
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;
char *locale = setlocale(LC_NUMERIC, NULL);
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);
register_error(ECPG_NOT_CONN, "Not connected in line %d.", lineno);
setlocale(LC_NUMERIC, locale);
return false;
}
status = ECPGexecute(stmt);
free_statement(stmt);
/* and reser value so our application is not affected */
/* and reset locale value so our application is not affected */
setlocale(LC_NUMERIC, locale);
return (status);
}
@ -1508,3 +1515,5 @@ ECPGprepared_statement(char *name)
for (this = prep_stmts; this != NULL && strcmp(this->name, name) != 0; this = this->next);
return (this) ? this->stmt->command : NULL;
}
#include "dynamic.c"

View File

@ -19,6 +19,7 @@
*/
static ScanKeyword ScanKeywords[] = {
/* name value */
{"allocate", SQL_ALLOCATE},
{"at", SQL_AT},
{"autocommit", SQL_AUTOCOMMIT},
{"bool", SQL_BOOL},
@ -28,10 +29,12 @@ static ScanKeyword ScanKeywords[] = {
{"connection", SQL_CONNECTION},
{"continue", SQL_CONTINUE},
{"deallocate", SQL_DEALLOCATE},
{"descriptor", SQL_DESCRIPTOR},
{"disconnect", SQL_DISCONNECT},
{"enum", SQL_ENUM},
{"found", SQL_FOUND},
{"free", SQL_FREE},
{"get", SQL_GET},
{"go", SQL_GO},
{"goto", SQL_GOTO},
{"identified", SQL_IDENTIFIED},
@ -46,12 +49,14 @@ static ScanKeyword ScanKeywords[] = {
{"section", SQL_SECTION},
{"short", SQL_SHORT},
{"signed", SQL_SIGNED},
{"sql",SQL_SQL}, // strange thing, used for into sql descriptor MYDESC;
{"sqlerror", SQL_SQLERROR},
{"sqlprint", SQL_SQLPRINT},
{"sqlwarning", SQL_SQLWARNING},
{"stop", SQL_STOP},
{"struct", SQL_STRUCT},
{"unsigned", SQL_UNSIGNED},
{"value", SQL_VALUE},
{"var", SQL_VAR},
{"whenever", SQL_WHENEVER},
};

View File

@ -29,6 +29,7 @@ extern struct arguments *argsinsert;
extern struct arguments *argsresult;
extern struct when when_error, when_nf, when_warn;
extern struct ECPGstruct_member *struct_member_list[STRUCT_DEPTH];
extern struct descriptor *descriptors;
/* functions */

View File

@ -23,6 +23,8 @@
int struct_level = 0;
char errortext[128];
static char *connection = NULL;
static char *descriptor_name = NULL;
static char *descriptor_index= NULL;
static int QueryIsRule = 0, ForUpdateNotAllowed = 0, FoundInto = 0;
static int FoundSort = 0;
static int initializer = 0;
@ -38,6 +40,11 @@ struct variable no_indicator = {"no_indicator", &ecpg_no_indicator, 0, NULL};
struct ECPGtype ecpg_query = {ECPGt_char_variable, 0L, {NULL}};
/* variable lookup */
static struct variable * find_variable(char * name);
static void whenever_action(int mode);
/*
* Handle parsing errors and warnings
*/
@ -80,6 +87,275 @@ output_simple_statement(char *cmd)
free(cmd);
}
/*
* assignment handling function (descriptor)
*/
static struct assignment *assignments;
static void push_assignment(char *var,char *value)
{
struct assignment *new=(struct assignment *)mm_alloc(sizeof(struct assignment));
new->next=assignments;
new->variable=mm_alloc(strlen(var)+1);
strcpy(new->variable,var);
new->value=mm_alloc(strlen(value)+1);
strcpy(new->value,value);
assignments=new;
}
static void drop_assignments(void)
{ while (assignments)
{ struct assignment *old_head=assignments;
assignments=old_head->next;
free(old_head->variable);
free(old_head->value);
free(old_head);
}
}
/* XXX: these should be more accurate (consider ECPGdump_a_* ) */
static void ECPGnumeric_lvalue(FILE *f,char *name)
{ const struct variable *v=find_variable(name);
switch(v->type->typ)
{ case ECPGt_short:
case ECPGt_int:
case ECPGt_long:
case ECPGt_unsigned_short:
case ECPGt_unsigned_int:
case ECPGt_unsigned_long:
fputs(name,yyout);
break;
default:
snprintf(errortext,sizeof errortext,"variable %s: numeric type needed"
,name);
mmerror(ET_ERROR,errortext);
break;
}
}
static void ECPGstring_buffer(FILE *f,char *name)
{ const struct variable *v=find_variable(name);
switch(v->type->typ)
{ case ECPGt_varchar:
fprintf(yyout,"%s.arr",name);
break;
case ECPGt_char:
case ECPGt_unsigned_char:
fputs(name,yyout);
break;
default:
snprintf(errortext,sizeof errortext,"variable %s: character type needed"
,name);
mmerror(ET_ERROR,errortext);
break;
}
}
static void ECPGstring_length(FILE *f,char *name)
{ const struct variable *v=find_variable(name);
switch(v->type->typ)
{ case ECPGt_varchar:
case ECPGt_char:
case ECPGt_unsigned_char:
if (!v->type->size)
{ snprintf(errortext,sizeof errortext,"zero length char variable %s for assignment",
v->name);
mmerror(ET_ERROR,errortext);
}
fprintf(yyout,"%ld",v->type->size);
break;
default:
snprintf(errortext,sizeof errortext,"variable %s: character type needed"
,name);
mmerror(ET_ERROR,errortext);
break;
}
}
static void ECPGdata_assignment(char *variable,char *index_plus_1)
{ const struct variable *v=find_variable(variable);
fprintf(yyout,"\t\t\tif (!PQgetisnull(ECPGresult,0,(%s)-1))\n",index_plus_1);
switch(v->type->typ)
{ case ECPGt_short:
case ECPGt_int: /* use the same conversion as ecpglib does */
case ECPGt_long:
fprintf(yyout,"\t\t\t\t%s=strtol(PQgetvalue(ECPGresult,0,(%s)-1),NULL,10);\n"
,variable,index_plus_1);
break;
case ECPGt_unsigned_short:
case ECPGt_unsigned_int:
case ECPGt_unsigned_long:
fprintf(yyout,"\t\t\t\t%s=strtoul(PQgetvalue(ECPGresult,0,(%s)-1),NULL,10);\n"
,variable,index_plus_1);
break;
case ECPGt_float:
case ECPGt_double:
fprintf(yyout,"\t\t\t\t%s=strtod(PQgetvalue(ECPGresult,0,(%s)-1),NULL);\n"
,variable,index_plus_1);
break;
case ECPGt_bool:
fprintf(yyout,"\t\t\t\t%s=PQgetvalue(ECPGresult,0,(%s)-1)[0]=='t';\n"
,variable,index_plus_1);
break;
case ECPGt_varchar:
fprintf(yyout,"\t\t\t{\tstrncpy(%s.arr,PQgetvalue(ECPGresult,0,(%s)-1),%ld);\n"
,variable,index_plus_1,v->type->size);
fprintf(yyout,"\t\t\t\t%s.len=strlen(PQgetvalue(ECPGresult,0,(%s)-1)\n"
,variable,index_plus_1);
fprintf(yyout,"\t\t\t\tif (%s.len>%ld) { %s.len=%ld; sqlca.sqlwarn[0]=sqlca.sqlwarn[1]='W'; }\n"
,variable,v->type->size,variable,v->type->size);
fputs("\t\t\t}\n",yyout);
break;
case ECPGt_char:
case ECPGt_unsigned_char:
if (!v->type->size)
{ snprintf(errortext,sizeof errortext,"zero length char variable %s for DATA assignment",
v->name);
mmerror(ET_ERROR,errortext);
}
fprintf(yyout,"\t\t\t{\tstrncpy(%s,PQgetvalue(ECPGresult,0,(%s)-1),%ld);\n"
,variable,index_plus_1,v->type->size);
fprintf(yyout,"\t\t\t\tif (strlen(PQgetvalue(ECPGresult,0,(%s)-1))>=%ld)\n"
"\t\t\t\t{ %s[%ld]=0; sqlca.sqlwarn[0]=sqlca.sqlwarn[1]='W'; }\n"
,index_plus_1,v->type->size,variable,v->type->size-1);
fputs("\t\t\t}\n",yyout);
break;
default:
snprintf(errortext,sizeof errortext,"unknown variable type %d for DATA assignment"
,v->type->typ);
mmerror(ET_ERROR,errortext);
break;
}
}
static void
output_get_descr_header(char *desc_name)
{ struct assignment *results;
fprintf(yyout,"{\tPGresult *ECPGresult=ECPGresultByDescriptor(%d, \"%s\");\n"
,yylineno,desc_name);
fputs("\tif (ECPGresult)\n\t{",yyout);
for (results=assignments;results!=NULL;results=results->next)
{ if (!strcasecmp(results->value,"count"))
{ fputs("\t\t",yyout);
ECPGnumeric_lvalue(yyout,results->variable);
fputs("=PQnfields(ECPGresult);\n",yyout);
}
else
{ snprintf(errortext,sizeof errortext,"unknown descriptor header item '%s'",results->value);
mmerror(ET_WARN,errortext);
}
}
drop_assignments();
fputs("}",yyout);
whenever_action(2|1);
}
static void
output_get_descr(char *desc_name)
{ struct assignment *results;
int flags=0;
const int DATA_SEEN=1;
const int INDICATOR_SEEN=2;
fprintf(yyout,"{\tPGresult *ECPGresult=ECPGresultByDescriptor(%d, \"%s\");\n"
,yylineno,desc_name);
fputs("\tif (ECPGresult)\n\t{",yyout);
fprintf(yyout,"\tif (PQntuples(ECPGresult)<1) ECPGraise(%d,ECPG_NOT_FOUND);\n",yylineno);
fprintf(yyout,"\t\telse if (%s<1 || %s>PQnfields(ECPGresult))\n"
"\t\t\tECPGraise(%d,ECPG_INVALID_DESCRIPTOR_INDEX);\n"
,descriptor_index,descriptor_index,yylineno);
fputs("\t\telse\n\t\t{\n",yyout);
for (results=assignments;results!=NULL;results=results->next)
{ if (!strcasecmp(results->value,"type"))
{ fputs("\t\t\t",yyout);
ECPGnumeric_lvalue(yyout,results->variable);
fprintf(yyout,"=ECPGDynamicType(PQftype(ECPGresult,(%s)-1));\n",descriptor_index);
}
else if (!strcasecmp(results->value,"datetime_interval_code"))
{ fputs("\t\t\t",yyout);
ECPGnumeric_lvalue(yyout,results->variable);
fprintf(yyout,"=ECPGDynamicType_DDT(PQftype(ECPGresult,(%s)-1));\n",descriptor_index);
}
else if (!strcasecmp(results->value,"length"))
{ fputs("\t\t\t",yyout);
ECPGnumeric_lvalue(yyout,results->variable);
fprintf(yyout,"=PQfmod(ECPGresult,(%s)-1)-VARHDRSZ;\n",descriptor_index);
}
else if (!strcasecmp(results->value,"octet_length"))
{ fputs("\t\t\t",yyout);
ECPGnumeric_lvalue(yyout,results->variable);
fprintf(yyout,"=PQfsize(ECPGresult,(%s)-1);\n",descriptor_index);
}
else if (!strcasecmp(results->value,"returned_length")
|| !strcasecmp(results->value,"returned_octet_length"))
{ fputs("\t\t\t",yyout);
ECPGnumeric_lvalue(yyout,results->variable);
fprintf(yyout,"=PQgetlength(ECPGresult,0,(%s)-1);\n",descriptor_index);
}
else if (!strcasecmp(results->value,"precision"))
{ fputs("\t\t\t",yyout);
ECPGnumeric_lvalue(yyout,results->variable);
fprintf(yyout,"=PQfmod(ECPGresult,(%s)-1)>>16;\n",descriptor_index);
}
else if (!strcasecmp(results->value,"scale"))
{ fputs("\t\t\t",yyout);
ECPGnumeric_lvalue(yyout,results->variable);
fprintf(yyout,"=(PQfmod(ECPGresult,(%s)-1)-VARHDRSZ)&0xffff;\n",descriptor_index);
}
else if (!strcasecmp(results->value,"nullable"))
{ mmerror(ET_WARN,"nullable is always 1");
fputs("\t\t\t",yyout);
ECPGnumeric_lvalue(yyout,results->variable);
fprintf(yyout,"=1;\n");
}
else if (!strcasecmp(results->value,"key_member"))
{ mmerror(ET_WARN,"key_member is always 0");
fputs("\t\t\t",yyout);
ECPGnumeric_lvalue(yyout,results->variable);
fprintf(yyout,"=0;\n");
}
else if (!strcasecmp(results->value,"name"))
{ fputs("\t\t\tstrncpy(",yyout);
ECPGstring_buffer(yyout,results->variable);
fprintf(yyout,",PQfname(ECPGresult,(%s)-1),",descriptor_index);
ECPGstring_length(yyout,results->variable);
fputs(");\n",yyout);
}
else if (!strcasecmp(results->value,"indicator"))
{ flags|=INDICATOR_SEEN;
fputs("\t\t\t",yyout);
ECPGnumeric_lvalue(yyout,results->variable);
fprintf(yyout,"=-PQgetisnull(ECPGresult,0,(%s)-1);\n",descriptor_index);
}
else if (!strcasecmp(results->value,"data"))
{ flags|=DATA_SEEN;
ECPGdata_assignment(results->variable,descriptor_index);
}
else
{ snprintf(errortext,sizeof errortext,"unknown descriptor header item '%s'",results->value);
mmerror(ET_WARN,errortext);
}
}
if (flags==DATA_SEEN) /* no indicator */
{ fprintf(yyout,"\t\t\tif (PQgetisnull(ECPGresult,0,(%s)-1))\n"
"\t\t\t\tECPGraise(%d,ECPG_MISSING_INDICATOR);\n"
,descriptor_index,yylineno);
}
drop_assignments();
fputs("\t\t}\n\t}\n",yyout);
whenever_action(2|1);
}
/*
* store the whenever action here
*/
@ -157,8 +433,6 @@ new_variable(const char * name, struct ECPGtype * type)
return(p);
}
static struct variable * find_variable(char * name);
static struct variable *
find_struct_member(char *name, char *str, struct ECPGstruct_member *members)
{
@ -401,6 +675,67 @@ check_indicator(struct ECPGtype *var)
}
}
/*
* descriptor name lookup
*/
static struct descriptor *descriptors;
static void add_descriptor(char *name,char *connection)
{
struct descriptor *new=(struct descriptor *)mm_alloc(sizeof(struct descriptor));
new->next=descriptors;
new->name=mm_alloc(strlen(name)+1);
strcpy(new->name,name);
if (connection)
{ new->connection=mm_alloc(strlen(connection)+1);
strcpy(new->connection,connection);
}
else new->connection=connection;
descriptors=new;
}
static void drop_descriptor(char *name,char *connection)
{ struct descriptor *i;
struct descriptor **lastptr=&descriptors;
for (i=descriptors;i;lastptr=&i->next,i=i->next)
{ if (!strcmp(name,i->name))
{ if ((!connection && !i->connection)
|| (connection && i->connection
&& !strcmp(connection,i->connection)))
{ *lastptr=i->next;
if (i->connection) free(i->connection);
free(i->name);
free(i);
return;
}
}
}
snprintf(errortext,sizeof errortext,"unknown descriptor %s",name);
mmerror(ET_WARN,errortext);
}
static struct descriptor *lookup_descriptor(char *name,char *connection)
{ struct descriptor *i;
for (i=descriptors;i;i=i->next)
{ if (!strcmp(name,i->name))
{ if ((!connection && !i->connection)
|| (connection && i->connection
&& !strcmp(connection,i->connection)))
{ return i;
}
}
}
snprintf(errortext,sizeof errortext,"unknown descriptor %s",name);
mmerror(ET_WARN,errortext);
return NULL;
}
/*
* string concatenation
*/
static char *
cat2_str(char *str1, char *str2)
{
@ -522,6 +857,32 @@ output_statement(char * stmt, int mode)
free(connection);
}
static void
output_statement_desc(char * stmt, int mode)
{
int i, j=strlen(stmt);
fprintf(yyout, "{ ECPGdo_descriptor(__LINE__, %s, \"%s\", \"",
connection ? connection : "NULL", descriptor_name);
/* do this char by char as we have to filter '\"' */
for (i = 0;i < j; i++) {
if (stmt[i] != '\"')
fputc(stmt[i], yyout);
else
fputs("\\\"", yyout);
}
fputs("\");", yyout);
mode |= 2;
whenever_action(mode);
free(stmt);
if (connection != NULL)
free(connection);
free(descriptor_name);
}
static struct typedefs *
get_typedef(char *name)
{
@ -632,15 +993,16 @@ adjust_array(enum ECPGttype type_enum, int *dimension, int *length, int type_dim
}
/* special embedded SQL token */
%token SQL_AT SQL_AUTOCOMMIT SQL_BOOL SQL_BREAK
%token SQL_ALLOCATE SQL_AT SQL_AUTOCOMMIT SQL_BOOL SQL_BREAK
%token SQL_CALL SQL_CONNECT SQL_CONNECTION SQL_CONTINUE
%token SQL_DEALLOCATE SQL_DISCONNECT SQL_ENUM
%token SQL_FOUND SQL_FREE SQL_GO SQL_GOTO
%token SQL_DEALLOCATE SQL_DESCRIPTOR SQL_DISCONNECT SQL_ENUM
%token SQL_FOUND SQL_FREE SQL_GET SQL_GO SQL_GOTO
%token SQL_IDENTIFIED SQL_INDICATOR SQL_INT SQL_LONG
%token SQL_OFF SQL_OPEN SQL_PREPARE SQL_RELEASE SQL_REFERENCE
%token SQL_SECTION SQL_SHORT SQL_SIGNED SQL_SQLERROR SQL_SQLPRINT
%token SQL_SECTION SQL_SHORT SQL_SIGNED SQL_SQL
%token SQL_SQLERROR SQL_SQLPRINT
%token SQL_SQLWARNING SQL_START SQL_STOP SQL_STRUCT SQL_UNSIGNED
%token SQL_VAR SQL_WHENEVER
%token SQL_VALUE SQL_VAR SQL_WHENEVER
/* C token */
%token S_ANYTHING S_AUTO S_CONST S_EXTERN
@ -829,6 +1191,9 @@ adjust_array(enum ECPGttype type_enum, int *dimension, int *length, int type_dim
%type <str> ECPGFree ECPGDeclare ECPGVar opt_at enum_definition
%type <str> struct_type s_struct declaration declarations variable_declarations
%type <str> s_struct s_union union_type ECPGSetAutocommit on_off
%type <str> ECPGAllocateDescr ECPGDeallocateDescr
%type <str> ECPGGetDescriptor ECPGGetDescriptorHeader
%type <str> FetchDescriptorStmt
%type <type_enum> simple_type signed_type unsigned_type varchar_type
@ -879,6 +1244,7 @@ stmt: AlterTableStmt { output_statement($1, 0); }
| ExtendStmt { output_statement($1, 0); }
| ExplainStmt { output_statement($1, 0); }
| FetchStmt { output_statement($1, 1); }
| FetchDescriptorStmt { output_statement_desc($1, 1); }
| GrantStmt { output_statement($1, 0); }
| IndexStmt { output_statement($1, 0); }
| ListenStmt { output_statement($1, 0); }
@ -912,6 +1278,10 @@ stmt: AlterTableStmt { output_statement($1, 0); }
| VariableShowStmt { output_statement($1, 0); }
| VariableResetStmt { output_statement($1, 0); }
| ConstraintsSetStmt { output_statement($1, 0); }
| ECPGAllocateDescr { fprintf(yyout,"ECPGallocate_desc(__LINE__, \"%s\");",$1);
whenever_action(0);
free($1);
}
| ECPGConnect {
if (connection)
mmerror(ET_ERROR, "no at option for connect statement.\n");
@ -932,6 +1302,10 @@ stmt: AlterTableStmt { output_statement($1, 0); }
whenever_action(2);
free($1);
}
| ECPGDeallocateDescr { fprintf(yyout,"ECPGdeallocate_desc(__LINE__, \"%s\");",$1);
whenever_action(0);
free($1);
}
| ECPGDeclare {
output_simple_statement($1);
}
@ -952,6 +1326,14 @@ stmt: AlterTableStmt { output_statement($1, 0); }
whenever_action(2);
free($1);
}
| ECPGGetDescriptor {
lookup_descriptor($1,connection);
output_get_descr($1);
}
| ECPGGetDescriptorHeader {
lookup_descriptor($1,connection);
output_get_descr_header($1);
}
| ECPGOpen {
struct cursor *ptr;
@ -5014,6 +5396,78 @@ ECPGPrepare: SQL_PREPARE ident FROM execstring
$$ = cat2_str(make3_str(make_str("\""), $2, make_str("\",")), $4);
}
/*
* dynamic SQL: descriptor based access
* written by Christof Petig <christof.petig@wtal.de>
*/
/*
* deallocate a descriptor
*/
ECPGDeallocateDescr: SQL_DEALLOCATE SQL_DESCRIPTOR ident
{ drop_descriptor($3,connection);
$$ = $3;
}
/*
* allocate a descriptor
*/
ECPGAllocateDescr: SQL_ALLOCATE SQL_DESCRIPTOR ident
{ add_descriptor($3,connection);
$$ = $3;
}
/*
* read from descriptor
*/
ECPGGetDescHeaderItem: cvariable '=' ident {
push_assignment($1,$3);
}
ECPGGetDescItem: cvariable '=' ident {
push_assignment($1,$3);
}
| cvariable '=' TYPE_P {
push_assignment($1,"type");
}
| cvariable '=' PRECISION {
push_assignment($1,"precision");
}
| cvariable '=' SQL_INDICATOR {
push_assignment($1,"indicator");
}
ECPGGetDescHeaderItems: ECPGGetDescHeaderItem
| ECPGGetDescHeaderItems ',' ECPGGetDescHeaderItem;
ECPGGetDescItems: ECPGGetDescItem
| ECPGGetDescItems ',' ECPGGetDescItem;
ECPGGetDescriptorHeader: SQL_GET SQL_DESCRIPTOR ident ECPGGetDescHeaderItems
{ $$ = $3; }
ECPGGetDescriptor: SQL_GET SQL_DESCRIPTOR ident SQL_VALUE cvariable ECPGGetDescItems
{ $$ = $3; descriptor_index=$5; }
| SQL_GET SQL_DESCRIPTOR ident SQL_VALUE Iconst ECPGGetDescItems
{ $$ = $3; descriptor_index=$5; }
/*
* fetch [ in | from ] <portalname> into sql descriptor <name>
*/
FetchDescriptorStmt: FETCH from_in name INTO SQL_SQL SQL_DESCRIPTOR ident
{
$$ = cat_str(3, make_str("fetch"), $2, $3);
descriptor_name=$7;
}
| FETCH name INTO SQL_SQL SQL_DESCRIPTOR ident
{
$$ = cat2_str(make_str("fetch"), $2);
descriptor_name=$6;
}
;
/*
* for compatibility with ORACLE we will also allow the keyword RELEASE
* after a transaction statement to disconnect from the database.

View File

@ -139,4 +139,18 @@ struct arguments
struct arguments *next;
};
struct descriptor
{
char *name;
char *connection;
struct descriptor *next;
};
struct assignment
{
char *variable;
char *value;
struct assignment *next;
};
enum errortype {ET_WARN, ET_ERROR, ET_FATAL};

View File

@ -1,8 +1,8 @@
all: stp.so test1 test2 test3 test4 test5 perftest
all: stp.so test1 test2 test3 test4 test5 perftest dyntest
#LDFLAGS=-g -I /usr/local/pgsql/include -L/usr/local/pgsql/lib -lecpg -lpq -lcrypt
#LDFLAGS=-g -I../include -I/usr/include/postgresql -L/usr/lib/postgresql -L../lib -lecpg -lpq -lcrypt
LDFLAGS=-g -I/usr/include/postgresql -lecpg -lpq -lcrypt
LDFLAGS=-g -I../include -I/usr/include/postgresql -L/usr/lib/postgresql -L../lib -lecpg -lpq -lcrypt
#LDFLAGS=-g -I/usr/include/postgresql -lecpg -lpq -lcrypt
#ECPG=/usr/local/pgsql/bin/ecpg
ECPG=../preproc/ecpg -I../include
@ -16,6 +16,7 @@ test3: test3.c
test4: test4.c
test5: test5.c
perftest: perftest.c
dyntest: dyntest.c
.pgc.c:
$(ECPG) $?

View File

@ -0,0 +1,127 @@
/* dynamic SQL test program
*
* Copyright (c) 2000, Christof Petig <christof.petig@wtal.de>
*
* $Header: /cvsroot/pgsql/src/interfaces/ecpg/test/Attic/dyntest.pgc,v 1.1 2000/02/16 16:18:29 meskes Exp $
*/
#include <stdio.h>
exec sql include sql3types;
exec sql include sqlca;
void error()
{ printf("#%d:%s\n",sqlca.sqlcode,sqlca.sqlerrm.sqlerrmc);
exit(1);
}
int main(int argc,char **argv)
{ exec sql begin declare section;
int COUNT;
int INTVAR;
int INDEX;
int INDICATOR;
int TYPE,LENGTH,OCTET_LENGTH,PRECISION,SCALE,NULLABLE,RETURNED_OCTET_LENGTH;
int DATETIME_INTERVAL_CODE;
char NAME[120];
char STRINGVAR[1024];
float FLOATVAR;
double DOUBLEVAR;
char QUERY[1024];
exec sql end declare section;
int done=0;
snprintf(QUERY,sizeof QUERY,"select * from %s",argc>1?argv[1]:"pg_tables");
exec sql whenever sqlerror do error();
exec sql allocate descriptor MYDESC;
exec sql connect to test;
exec sql prepare MYQUERY from :QUERY;
exec sql declare MYCURS cursor for MYQUERY;
exec sql open MYCURS;
while (1)
{ exec sql fetch in MYCURS into sql descriptor MYDESC;
if (sqlca.sqlcode) break;
exec sql get descriptor MYDESC :COUNT = count;
if (!done)
{ printf("Count %d\n",COUNT);
done=1;
}
for (INDEX=1;INDEX<=COUNT;++INDEX)
{ exec sql get descriptor MYDESC value :INDEX
:TYPE = type,
:LENGTH = length, :OCTET_LENGTH=octet_length,
:RETURNED_OCTET_LENGTH=returned_octet_length,
:PRECISION = precision, :SCALE=scale,
:NULLABLE=nullable, :NAME=name,
:INDICATOR=indicator;
printf("%2d %s %d(%d)(%d,%d) %d,%d %d = "
,INDEX,NAME,TYPE,LENGTH,PRECISION,SCALE
,OCTET_LENGTH,RETURNED_OCTET_LENGTH,NULLABLE);
if (INDICATOR==-1) printf("NULL\n");
else switch (TYPE)
{ case SQL3_BOOLEAN:
exec sql get descriptor MYDESC value :INDEX :INTVAR=data;
printf("%s\n",INTVAR?"true":"false");
break;
case SQL3_NUMERIC:
case SQL3_DECIMAL:
if (SCALE==0)
{ exec sql get descriptor MYDESC value :INDEX :INTVAR=data;
printf("%d\n",INTVAR);
}
else
{ exec sql get descriptor MYDESC value :INDEX :FLOATVAR=data;
printf("%.*f\n",SCALE,FLOATVAR);
}
break;
case SQL3_INTEGER:
case SQL3_SMALLINT:
exec sql get descriptor MYDESC value :INDEX :INTVAR=data;
printf("%d\n",INTVAR);
break;
case SQL3_FLOAT:
case SQL3_REAL:
exec sql get descriptor MYDESC value :INDEX :FLOATVAR=data;
printf("%.*f\n",PRECISION,FLOATVAR);
break;
case SQL3_DOUBLE_PRECISION:
exec sql get descriptor MYDESC value :INDEX :DOUBLEVAR=data;
printf("%.*f\n",PRECISION,DOUBLEVAR);
break;
case SQL3_DATE_TIME_TIMESTAMP:
exec sql get descriptor MYDESC value :INDEX
:DATETIME_INTERVAL_CODE=datetime_interval_code,
:STRINGVAR=data;
printf("%d \"%s\"\n",DATETIME_INTERVAL_CODE,STRINGVAR);
break;
case SQL3_INTERVAL:
exec sql get descriptor MYDESC value :INDEX :STRINGVAR=data;
printf("\"%s\"\n",STRINGVAR);
break;
case SQL3_CHARACTER:
case SQL3_CHARACTER_VARYING:
exec sql get descriptor MYDESC value :INDEX :STRINGVAR=data;
printf("\"%s\"\n",STRINGVAR);
break;
default:
exec sql get descriptor MYDESC value :INDEX :STRINGVAR=data;
printf("<\"%s\">\n",STRINGVAR);
break;
}
}
}
exec sql close MYCURS;
exec sql deallocate descriptor MYDESC;
return 0;
}