mirror of https://github.com/postgres/postgres
parent
988d53e5ea
commit
35ba9de276
@ -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 |
||||
|
@ -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) */ |
||||
}; |
@ -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; |
||||
} |
||||
} |
@ -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; |
||||
} |
Loading…
Reference in new issue