4 EXEC SQL INCLUDE SQLCA;
5 EXEC SQL INCLUDE SQLDA;
14 * ---Implementations---
18 return (sqlca.sqlcode < 0) ? sqlca.sqlcode : 0;
21 short INGgetCols(const char *query)
23 EXEC SQL BEGIN DECLARE SECTION;
25 EXEC SQL END DECLARE SECTION;
30 sqlda = (IISQLDA *)malloc(IISQDA_HEAD_SIZE + (number * IISQDA_VAR_SIZE));
31 memset(sqlda, 0, (IISQDA_HEAD_SIZE + (number * IISQDA_VAR_SIZE)));
35 stmt = (char*)malloc(strlen(stmt)+1);
36 bstrncpy(stmt,stmt,strlen(stmt)+1);
38 EXEC SQL PREPARE s1 from :stmt;
44 EXEC SQL DESCRIBE s1 into :sqlda;
57 static IISQLDA *INGgetDescriptor(short numCols, const char *query)
59 EXEC SQL BEGIN DECLARE SECTION;
61 EXEC SQL END DECLARE SECTION;
66 sqlda = (IISQLDA *)malloc(IISQDA_HEAD_SIZE + (numCols * IISQDA_VAR_SIZE));
67 memset(sqlda, 0, (IISQDA_HEAD_SIZE + (numCols * IISQDA_VAR_SIZE)));
69 sqlda->sqln = numCols;
71 stmt = (char *)malloc(strlen(stmt)+1);
72 bstrncpy(stmt,stmt,strlen(stmt)+1);
74 EXEC SQL PREPARE s2 INTO :sqlda FROM :stmt;
78 for (i = 0; i < sqlda->sqld; ++i) {
80 * Negative type indicates nullable coulumns, so an indicator
81 * is allocated, otherwise it's null
83 if (sqlda->sqlvar[i].sqltype > 0) {
84 sqlda->sqlvar[i].sqlind = NULL;
86 sqlda->sqlvar[i].sqlind = (short *)malloc(sizeof(short));
89 * Alloc space for variable like indicated in sqllen
90 * for date types sqllen is always 0 -> allocate by type
92 switch (abs(sqlda->sqlvar[i].sqltype)) {
94 sqlda->sqlvar[i].sqldata = (char *)malloc(IISQ_TSW_LEN);
97 sqlda->sqlvar[i].sqldata = (char *)malloc(IISQ_TSWO_LEN);
100 sqlda->sqlvar[i].sqldata = (char *)malloc(IISQ_TSTMP_LEN);
104 * plus one to avoid zero mem allocs
106 sqlda->sqlvar[i].sqldata = (char *)malloc(sqlda->sqlvar[i].sqllen+1);
114 static void INGfreeDescriptor(IISQLDA *sqlda)
122 for (i = 0; i < sqlda->sqld; ++i) {
123 if (sqlda->sqlvar[i].sqldata) {
124 free(sqlda->sqlvar[i].sqldata);
126 if (sqlda->sqlvar[i].sqlind) {
127 free(sqlda->sqlvar[i].sqlind);
134 static int INGgetTypeSize(IISQLVAR *ingvar)
139 * TODO: add date types (at least TSTMP,TSW TSWO)
141 switch (ingvar->sqltype) {
149 inglength = ingvar->sqllen;
156 static INGresult *INGgetINGresult(IISQLDA *sqlda)
163 INGresult *result = NULL;
165 result = (INGresult *)malloc(sizeof(INGresult));
166 memset(result, 0, sizeof(INGresult));
168 result->sqlda = sqlda;
169 result->num_fields = sqlda->sqld;
170 result->num_rows = 0;
171 result->first_row = NULL;
172 result->status = ING_EMPTY_RESULT;
173 result->act_row = NULL;
174 memset(result->numrowstring, 0, sizeof(result->numrowstring));
176 if (result->num_fields) {
177 result->fields = (INGRES_FIELD *)malloc(sizeof(INGRES_FIELD) * result->num_fields);
178 memset(result->fields, 0, sizeof(INGRES_FIELD) * result->num_fields);
180 for (i = 0; i < result->num_fields; ++i) {
181 memset(result->fields[i].name, 0, 34);
182 bstrncpy(result->fields[i].name, sqlda->sqlvar[i].sqlname.sqlnamec, sqlda->sqlvar[i].sqlname.sqlnamel);
183 result->fields[i].max_length = INGgetTypeSize(&sqlda->sqlvar[i]);
184 result->fields[i].type = abs(sqlda->sqlvar[i].sqltype);
185 result->fields[i].flags = (sqlda->sqlvar[i].sqltype < 0) ? 1 : 0;
192 static void INGfreeINGresult(INGresult *ing_res)
202 * Free all rows and fields, then res, not descriptor!
204 if (ing_res != NULL) {
206 * Use of rows is a nasty workaround til I find the reason,
207 * why aggregates like max() don't work
209 rows = ing_res->num_rows;
210 ing_res->act_row = ing_res->first_row;
211 while (ing_res->act_row != NULL && rows > 0) {
212 rowtemp = ing_res->act_row->next;
213 INGfreeRowSpace(ing_res->act_row, ing_res->sqlda);
214 ing_res->act_row = rowtemp;
217 if (ing_res->fields) {
218 free(ing_res->fields);
225 static ING_ROW *INGgetRowSpace(INGresult *ing_res)
228 unsigned short len; /* used for VARCHAR type length */
229 IISQLDA *sqlda = ing_res->sqlda;
231 IISQLVAR *vars = NULL;
233 row = (ING_ROW *)malloc(sizeof(ING_ROW));
234 memset(row, 0, sizeof(ING_ROW));
236 vars = (IISQLVAR *)malloc(sizeof(IISQLVAR) * sqlda->sqld);
237 memset(vars, 0, sizeof(IISQLVAR) * sqlda->sqld);
242 for (i = 0; i < sqlda->sqld; ++i) {
244 * Make strings out of the data, then the space and assign
245 * (why string? at least it seems that way, looking into the sources)
247 vars[i].sqlind = (short *)malloc(sizeof(short));
248 if (sqlda->sqlvar[i].sqlind) {
249 memcpy(vars[i].sqlind,sqlda->sqlvar[i].sqlind,sizeof(short));
251 *vars[i].sqlind = NULL;
254 * if sqlind pointer exists AND points to -1 -> column is 'null'
256 if ( *vars[i].sqlind && (*vars[i].sqlind == -1)) {
257 vars[i].sqldata = NULL;
259 switch (ing_res->fields[i].type) {
261 len = ((ING_VARCHAR *)sqlda->sqlvar[i].sqldata)->len;
262 vars[i].sqldata = (char *)malloc(len+1);
263 memcpy(vars[i].sqldata,sqlda->sqlvar[i].sqldata+2,len);
264 vars[i].sqldata[len] = '\0';
267 vars[i].sqldata = (char *)malloc(ing_res->fields[i].max_length+1);
268 memcpy(vars[i].sqldata,sqlda->sqlvar[i].sqldata,sqlda->sqlvar[i].sqllen);
269 vars[i].sqldata[ing_res->fields[i].max_length] = '\0';
272 vars[i].sqldata = (char *)malloc(20);
273 memset(vars[i].sqldata, 0, 20);
274 switch (sqlda->sqlvar[i].sqllen) {
276 bsnprintf(vars[i].sqldata, 20, "%d",*(short*)sqlda->sqlvar[i].sqldata);
279 bsnprintf(vars[i].sqldata, 20, "%ld",*(int*)sqlda->sqlvar[i].sqldata);
282 bsnprintf(vars[i].sqldata, 20, "%lld",*(long*)sqlda->sqlvar[i].sqldata);
286 case IISQ_TSTMP_TYPE:
287 vars[i].sqldata = (char *)malloc(IISQ_TSTMP_LEN+1);
288 vars[i].sqldata[IISQ_TSTMP_LEN] = '\0';
291 vars[i].sqldata = (char *)malloc(IISQ_TSWO_LEN+1);
292 vars[i].sqldata[IISQ_TSWO_LEN] = '\0';
295 vars[i].sqldata = (char *)malloc(IISQ_TSW_LEN+1);
296 vars[i].sqldata[IISQ_TSW_LEN] = '\0';
304 static void INGfreeRowSpace(ING_ROW *row, IISQLDA *sqlda)
308 if (row == NULL || sqlda == NULL) {
312 for (i = 0; i < sqlda->sqld; ++i) {
313 if (row->sqlvar[i].sqldata) {
314 free(row->sqlvar[i].sqldata);
316 if (row->sqlvar[i].sqlind) {
317 free(row->sqlvar[i].sqlind);
324 static int INGfetchAll(const char *query, INGresult *ing_res)
331 desc = ing_res->sqlda;
333 EXEC SQL DECLARE c2 CURSOR FOR s2;
334 if ((check = INGcheck()) < 0) {
339 if ((check = INGcheck()) < 0) {
343 /* for (linecount = 0; sqlca.sqlcode == 0; ++linecount) */
345 EXEC SQL FETCH c2 USING DESCRIPTOR :desc;
347 if ( (sqlca.sqlcode == 0) || (sqlca.sqlcode == -40202) ) {
348 row = INGgetRowSpace(ing_res); /* alloc space for fetched row */
351 * Initialize list when encountered first time
353 if (ing_res->first_row == 0) {
354 ing_res->first_row = row; /* head of the list */
355 ing_res->first_row->next = NULL;
356 ing_res->act_row = ing_res->first_row;
358 ing_res->act_row->next = row; /* append row to old act_row */
359 ing_res->act_row = row; /* set row as act_row */
360 row->row_number = linecount;
363 } while ( (sqlca.sqlcode == 0) || (sqlca.sqlcode == -40202) );
367 ing_res->status = ING_COMMAND_OK;
368 ing_res->num_rows = linecount;
372 static ING_STATUS INGresultStatus(INGresult *res)
374 if (res == NULL) {return ING_NO_RESULT;}
378 static void INGrowSeek(INGresult *res, int row_number)
380 ING_ROW *trow = NULL;
381 if (res->act_row->row_number == row_number) {
386 * TODO: real error handling
388 if (row_number<0 || row_number>res->num_rows) {
392 for (trow = res->first_row ; trow->row_number != row_number ; trow = trow->next );
395 * Note - can be null - if row_number not found, right?
399 char *INGgetvalue(INGresult *res, int row_number, int column_number)
401 if (row_number != res->act_row->row_number) {
402 INGrowSeek(res, row_number);
404 return res->act_row->sqlvar[column_number].sqldata;
407 int INGgetisnull(INGresult *res, int row_number, int column_number)
409 if (row_number != res->act_row->row_number) {
410 INGrowSeek(res, row_number);
412 return (*res->act_row->sqlvar[column_number].sqlind == -1) 1 : 0;
415 int INGntuples(const INGresult *res)
417 return res->num_rows;
420 int INGnfields(const INGresult *res)
422 return res->num_fields;
425 char *INGfname(const INGresult *res, int column_number)
427 if ((column_number > res->num_fields) || (column_number < 0)) {
430 return res->fields[column_number].name;
434 short INGftype(const INGresult *res, int column_number)
436 return res->fields[column_number].type;
439 int INGexec(INGconn *conn, const char *query)
442 EXEC SQL BEGIN DECLARE SECTION;
445 EXEC SQL END DECLARE SECTION;
447 stmt = (char *)malloc(strlen(query)+1);
448 bstrncpy(stmt,query,strlen(query)+1);
451 EXEC SQL EXECUTE IMMEDIATE :stmt;
453 if ((check = INGcheck()) < 0) {
457 EXEC SQL INQUIRE_INGRES(:rowcount = ROWCOUNT);
458 if ((check = INGcheck()) < 0) {
465 INGresult *INGquery(INGconn *conn, const char *query)
468 * TODO: error handling
470 IISQLDA *desc = NULL;
471 INGresult *res = NULL;
473 int cols = INGgetCols(query);
475 desc = INGgetDescriptor(cols, query);
479 res = INGgetINGresult(desc);
483 rows = INGfetchAll(query, res);
486 INGfreeINGresult(res);
487 INGfreeDescriptor(desc);
493 void INGclear(INGresult *res)
498 IISQLDA *desc = res->sqlda;
499 INGfreeINGresult(res);
500 INGfreeDescriptor(desc);
503 INGconn *INGconnectDB(char *dbname, char *user, char *passwd)
505 if (dbname == NULL || strlen(dbname) == 0) {
509 INGconn *dbconn = (INGconn *)malloc(sizeof(INGconn));
510 memset(dbconn, 0, sizeof(INGconn));
512 EXEC SQL BEGIN DECLARE SECTION;
518 EXEC SQL END DECLARE SECTION;
520 bstrncpy(ingdbname, dbname, sizeof(ingdbname));
523 bstrncpy(ingdbuser, user, sizeof(ingdbuser));
524 if (passwd != NULL) {
525 bstrncpy(ingdbpasw, passwd, sizeof(ingdbpasw));
527 memset(ingdbpasw, 0, sizeof(ingdbpasw));
531 identified by :ingdbuser
532 dbms_password = :ingdbpasw;
534 EXEC SQL CONNECT :ingdbname;
537 EXEC SQL INQUIRE_SQL(:conn_name = connection_name);
538 EXEC SQL INQUIRE_SQL(:sess_id = session);
540 bstrncpy(dbconn->dbname, ingdbname, sizeof(dbconn->dbname));
541 bstrncpy(dbconn->user, ingdbuser, sizeof(dbconn->user));
542 bstrncpy(dbconn->password, ingdbpasw, sizeof(dbconn->password));
543 bstrncpy(dbconn->connection_name, conn_name, sizeof(dbconn->connection_name));
544 dbconn->session_id = sess_id;
545 dbconn->msg = (char*)malloc(257);
546 memset(dbconn->msg, 0, 257);
551 void INGdisconnectDB(INGconn *dbconn)
554 * TODO: check for any real use of dbconn: maybe whenn multithreaded?
557 if (dbconn != NULL) {
563 char *INGerrorMessage(const INGconn *conn)
565 EXEC SQL BEGIN DECLARE SECTION;
567 EXEC SQL END DECLARE SECTION;
569 EXEC SQL INQUIRE_INGRES(:errbuf = ERRORTEXT);
570 memcpy(conn->msg,&errbuf,256);
574 char *INGcmdTuples(INGresult *res)
576 return res->numrowstring;
580 int INGputCopyEnd(INGconn *conn, const char *errormsg);
581 int INGputCopyData(INGconn *conn, const char *buffer, int nbytes);