4 EXEC SQL INCLUDE SQLCA;
5 EXEC SQL INCLUDE SQLDA;
14 * ---Implementations---
18 return (sqlca.sqlcode < 0) ? sqlca.sqlcode : 0;
21 short INGgetCols(B_DB *mdb, const char *query)
23 bool stmt_free = false;
24 EXEC SQL BEGIN DECLARE SECTION;
26 EXEC SQL END DECLARE SECTION;
31 sqlda = (IISQLDA *)malloc(IISQDA_HEAD_SIZE + (number * IISQDA_VAR_SIZE));
32 memset(sqlda, 0, (IISQDA_HEAD_SIZE + (number * IISQDA_VAR_SIZE)));
37 * See if we need to run this through the limit_filter.
39 if (strstr(query, "LIMIT") != NULL) {
40 stmt = mdb->limit_filter->replace(query);
42 stmt = bstrdup(query);
46 EXEC SQL PREPARE s1 from :stmt;
52 EXEC SQL DESCRIBE s1 into :sqlda;
68 static inline IISQLDA *INGgetDescriptor(B_DB *mdb, short numCols, const char *query)
70 bool stmt_free = false;
71 EXEC SQL BEGIN DECLARE SECTION;
73 EXEC SQL END DECLARE SECTION;
78 sqlda = (IISQLDA *)malloc(IISQDA_HEAD_SIZE + (numCols * IISQDA_VAR_SIZE));
79 memset(sqlda, 0, (IISQDA_HEAD_SIZE + (numCols * IISQDA_VAR_SIZE)));
81 sqlda->sqln = numCols;
84 * See if we need to run this through the limit_filter.
86 if (strstr(query, "LIMIT") != NULL) {
87 stmt = mdb->limit_filter->replace(query);
89 stmt = bstrdup(query);
93 EXEC SQL PREPARE s2 INTO :sqlda FROM :stmt;
99 for (i = 0; i < sqlda->sqld; ++i) {
101 * Negative type indicates nullable coulumns, so an indicator
102 * is allocated, otherwise it's null
104 if (sqlda->sqlvar[i].sqltype > 0) {
105 sqlda->sqlvar[i].sqlind = NULL;
107 sqlda->sqlvar[i].sqlind = (short *)malloc(sizeof(short));
110 * Alloc space for variable like indicated in sqllen
111 * for date types sqllen is always 0 -> allocate by type
113 switch (abs(sqlda->sqlvar[i].sqltype)) {
115 sqlda->sqlvar[i].sqldata = (char *)malloc(IISQ_TSW_LEN);
118 sqlda->sqlvar[i].sqldata = (char *)malloc(IISQ_TSWO_LEN);
120 case IISQ_TSTMP_TYPE:
121 sqlda->sqlvar[i].sqldata = (char *)malloc(IISQ_TSTMP_LEN);
125 * plus one to avoid zero mem allocs
127 sqlda->sqlvar[i].sqldata = (char *)malloc(sqlda->sqlvar[i].sqllen+1);
135 static void INGfreeDescriptor(IISQLDA *sqlda)
143 for (i = 0; i < sqlda->sqld; ++i) {
144 if (sqlda->sqlvar[i].sqldata) {
145 free(sqlda->sqlvar[i].sqldata);
147 if (sqlda->sqlvar[i].sqlind) {
148 free(sqlda->sqlvar[i].sqlind);
155 static inline int INGgetTypeSize(IISQLVAR *ingvar)
160 * TODO: add date types (at least TSTMP,TSW TSWO)
162 switch (ingvar->sqltype) {
170 inglength = ingvar->sqllen;
177 static inline INGresult *INGgetINGresult(IISQLDA *sqlda)
180 INGresult *result = NULL;
186 result = (INGresult *)malloc(sizeof(INGresult));
187 memset(result, 0, sizeof(INGresult));
189 result->sqlda = sqlda;
190 result->num_fields = sqlda->sqld;
191 result->num_rows = 0;
192 result->first_row = NULL;
193 result->status = ING_EMPTY_RESULT;
194 result->act_row = NULL;
195 memset(result->numrowstring, 0, sizeof(result->numrowstring));
197 if (result->num_fields) {
198 result->fields = (INGRES_FIELD *)malloc(sizeof(INGRES_FIELD) * result->num_fields);
199 memset(result->fields, 0, sizeof(INGRES_FIELD) * result->num_fields);
201 for (i = 0; i < result->num_fields; ++i) {
202 memset(result->fields[i].name, 0, 34);
203 bstrncpy(result->fields[i].name, sqlda->sqlvar[i].sqlname.sqlnamec, sqlda->sqlvar[i].sqlname.sqlnamel);
204 result->fields[i].max_length = INGgetTypeSize(&sqlda->sqlvar[i]);
205 result->fields[i].type = abs(sqlda->sqlvar[i].sqltype);
206 result->fields[i].flags = (sqlda->sqlvar[i].sqltype < 0) ? 1 : 0;
213 static void INGfreeINGresult(INGresult *ing_res)
223 * Free all rows and fields, then res, not descriptor!
225 * Use of rows is a nasty workaround til I find the reason,
226 * why aggregates like max() don't work
228 rows = ing_res->num_rows;
229 ing_res->act_row = ing_res->first_row;
230 while (ing_res->act_row != NULL && rows > 0) {
231 rowtemp = ing_res->act_row->next;
232 INGfreeRowSpace(ing_res->act_row, ing_res->sqlda);
233 ing_res->act_row = rowtemp;
236 if (ing_res->fields) {
237 free(ing_res->fields);
243 static inline ING_ROW *INGgetRowSpace(INGresult *ing_res)
246 unsigned short len; /* used for VARCHAR type length */
247 IISQLDA *sqlda = ing_res->sqlda;
249 IISQLVAR *vars = NULL;
251 row = (ING_ROW *)malloc(sizeof(ING_ROW));
252 memset(row, 0, sizeof(ING_ROW));
254 vars = (IISQLVAR *)malloc(sizeof(IISQLVAR) * sqlda->sqld);
255 memset(vars, 0, sizeof(IISQLVAR) * sqlda->sqld);
260 for (i = 0; i < sqlda->sqld; ++i) {
262 * Make strings out of the data, then the space and assign
263 * (why string? at least it seems that way, looking into the sources)
265 vars[i].sqlind = (short *)malloc(sizeof(short));
266 if (sqlda->sqlvar[i].sqlind) {
267 memcpy(vars[i].sqlind,sqlda->sqlvar[i].sqlind,sizeof(short));
269 *vars[i].sqlind = NULL;
272 * if sqlind pointer exists AND points to -1 -> column is 'null'
274 if ( *vars[i].sqlind && (*vars[i].sqlind == -1)) {
275 vars[i].sqldata = NULL;
277 switch (ing_res->fields[i].type) {
279 len = ((ING_VARCHAR *)sqlda->sqlvar[i].sqldata)->len;
280 vars[i].sqldata = (char *)malloc(len+1);
281 memcpy(vars[i].sqldata,sqlda->sqlvar[i].sqldata+2,len);
282 vars[i].sqldata[len] = '\0';
285 vars[i].sqldata = (char *)malloc(ing_res->fields[i].max_length+1);
286 memcpy(vars[i].sqldata,sqlda->sqlvar[i].sqldata,sqlda->sqlvar[i].sqllen);
287 vars[i].sqldata[ing_res->fields[i].max_length] = '\0';
290 vars[i].sqldata = (char *)malloc(20);
291 memset(vars[i].sqldata, 0, 20);
292 switch (sqlda->sqlvar[i].sqllen) {
294 bsnprintf(vars[i].sqldata, 20, "%d",*(short*)sqlda->sqlvar[i].sqldata);
297 bsnprintf(vars[i].sqldata, 20, "%ld",*(int*)sqlda->sqlvar[i].sqldata);
300 bsnprintf(vars[i].sqldata, 20, "%lld",*(long*)sqlda->sqlvar[i].sqldata);
304 case IISQ_TSTMP_TYPE:
305 vars[i].sqldata = (char *)malloc(IISQ_TSTMP_LEN+1);
306 vars[i].sqldata[IISQ_TSTMP_LEN] = '\0';
309 vars[i].sqldata = (char *)malloc(IISQ_TSWO_LEN+1);
310 vars[i].sqldata[IISQ_TSWO_LEN] = '\0';
313 vars[i].sqldata = (char *)malloc(IISQ_TSW_LEN+1);
314 vars[i].sqldata[IISQ_TSW_LEN] = '\0';
322 static inline void INGfreeRowSpace(ING_ROW *row, IISQLDA *sqlda)
326 if (row == NULL || sqlda == NULL) {
330 for (i = 0; i < sqlda->sqld; ++i) {
331 if (row->sqlvar[i].sqldata) {
332 free(row->sqlvar[i].sqldata);
334 if (row->sqlvar[i].sqlind) {
335 free(row->sqlvar[i].sqlind);
342 static inline int INGfetchAll(const char *query, INGresult *ing_res)
349 desc = ing_res->sqlda;
351 EXEC SQL DECLARE c2 CURSOR FOR s2;
352 if ((check = INGcheck()) < 0) {
357 if ((check = INGcheck()) < 0) {
361 /* for (linecount = 0; sqlca.sqlcode == 0; ++linecount) */
363 EXEC SQL FETCH c2 USING DESCRIPTOR :desc;
365 if ( (sqlca.sqlcode == 0) || (sqlca.sqlcode == -40202) ) {
366 row = INGgetRowSpace(ing_res); /* alloc space for fetched row */
369 * Initialize list when encountered first time
371 if (ing_res->first_row == 0) {
372 ing_res->first_row = row; /* head of the list */
373 ing_res->first_row->next = NULL;
374 ing_res->act_row = ing_res->first_row;
376 ing_res->act_row->next = row; /* append row to old act_row */
377 ing_res->act_row = row; /* set row as act_row */
378 row->row_number = linecount;
381 } while ( (sqlca.sqlcode == 0) || (sqlca.sqlcode == -40202) );
385 ing_res->status = ING_COMMAND_OK;
386 ing_res->num_rows = linecount;
390 static inline ING_STATUS INGresultStatus(INGresult *res)
393 return ING_NO_RESULT;
399 static void INGrowSeek(INGresult *res, int row_number)
401 ING_ROW *trow = NULL;
403 if (res->act_row->row_number == row_number) {
408 * TODO: real error handling
410 if (row_number<0 || row_number>res->num_rows) {
414 for (trow = res->first_row; trow->row_number != row_number; trow = trow->next) ;
417 * Note - can be null - if row_number not found, right?
421 char *INGgetvalue(INGresult *res, int row_number, int column_number)
423 if (row_number != res->act_row->row_number) {
424 INGrowSeek(res, row_number);
427 return res->act_row->sqlvar[column_number].sqldata;
430 int INGgetisnull(INGresult *res, int row_number, int column_number)
432 if (row_number != res->act_row->row_number) {
433 INGrowSeek(res, row_number);
436 return (*res->act_row->sqlvar[column_number].sqlind == -1) 1 : 0;
439 int INGntuples(const INGresult *res)
441 return res->num_rows;
444 int INGnfields(const INGresult *res)
446 return res->num_fields;
449 char *INGfname(const INGresult *res, int column_number)
451 if ((column_number > res->num_fields) || (column_number < 0)) {
454 return res->fields[column_number].name;
458 short INGftype(const INGresult *res, int column_number)
460 return res->fields[column_number].type;
463 int INGexec(B_DB *mdb, INGconn *conn, const char *query)
465 bool stmt_free = false;
467 EXEC SQL BEGIN DECLARE SECTION;
470 EXEC SQL END DECLARE SECTION;
473 * See if we need to run this through the limit_filter.
475 if (strstr(query, "LIMIT") != NULL) {
476 stmt = mdb->limit_filter->replace(query);
478 stmt = bstrdup(query);
483 EXEC SQL EXECUTE IMMEDIATE :stmt;
489 if ((check = INGcheck()) < 0) {
493 EXEC SQL INQUIRE_INGRES(:rowcount = ROWCOUNT);
494 if ((check = INGcheck()) < 0) {
501 INGresult *INGquery(B_DB *mdb, INGconn *conn, const char *query)
504 * TODO: error handling
506 IISQLDA *desc = NULL;
507 INGresult *res = NULL;
509 int cols = INGgetCols(mdb, query);
511 desc = INGgetDescriptor(cols, query);
515 res = INGgetINGresult(desc);
519 rows = INGfetchAll(query, res);
522 INGfreeINGresult(res);
523 INGfreeDescriptor(desc);
529 void INGclear(INGresult *res)
535 INGfreeINGresult(res);
536 INGfreeDescriptor(res->sqlda);
539 INGconn *INGconnectDB(char *dbname, char *user, char *passwd)
543 if (dbname == NULL || strlen(dbname) == 0) {
547 dbconn = (INGconn *)malloc(sizeof(INGconn));
548 memset(dbconn, 0, sizeof(INGconn));
550 EXEC SQL BEGIN DECLARE SECTION;
556 EXEC SQL END DECLARE SECTION;
558 bstrncpy(ingdbname, dbname, sizeof(ingdbname));
561 bstrncpy(ingdbuser, user, sizeof(ingdbuser));
562 if (passwd != NULL) {
563 bstrncpy(ingdbpasw, passwd, sizeof(ingdbpasw));
565 memset(ingdbpasw, 0, sizeof(ingdbpasw));
569 identified by :ingdbuser
570 dbms_password = :ingdbpasw;
572 EXEC SQL CONNECT :ingdbname;
575 EXEC SQL INQUIRE_SQL(:conn_name = connection_name);
576 EXEC SQL INQUIRE_SQL(:sess_id = session);
578 bstrncpy(dbconn->dbname, ingdbname, sizeof(dbconn->dbname));
579 bstrncpy(dbconn->user, ingdbuser, sizeof(dbconn->user));
580 bstrncpy(dbconn->password, ingdbpasw, sizeof(dbconn->password));
581 bstrncpy(dbconn->connection_name, conn_name, sizeof(dbconn->connection_name));
582 dbconn->session_id = sess_id;
583 dbconn->msg = (char*)malloc(257);
584 memset(dbconn->msg, 0, 257);
589 void INGdisconnectDB(INGconn *dbconn)
592 * TODO: check for any real use of dbconn: maybe whenn multithreaded?
595 if (dbconn != NULL) {
601 char *INGerrorMessage(const INGconn *conn)
603 EXEC SQL BEGIN DECLARE SECTION;
605 EXEC SQL END DECLARE SECTION;
607 EXEC SQL INQUIRE_INGRES(:errbuf = ERRORTEXT);
608 memcpy(conn->msg, &errbuf, 256);
612 char *INGcmdTuples(INGresult *res)
614 return res->numrowstring;
618 int INGputCopyEnd(INGconn *conn, const char *errormsg);
619 int INGputCopyData(INGconn *conn, const char *buffer, int nbytes);