1 /*
2  * Copyright (C) 2001 - 2002 Gonzalo Paniagua Javier <gonzalo@gnome-db.org>
3  * Copyright (C) 2001 - 2004 Rodrigo Moya <rodrigo@gnome-db.org>
4  * Copyright (C) 2004 Jeronimo Albi <jeronimoalbi@yahoo.com.ar>
5  * Copyright (C) 2004 - 2012 Vivien Malerba <malerba@gnome-db.org>
6  * Copyright (C) 2005 Christopher Taylor <christophth@tiscali.it>
7  * Copyright (C) 2005 Álvaro Peña <alvaropg@telefonica.net>
8  * Copyright (C) 2009 Bas Driessen <bas.driessen@xobas.com>
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public
12  * License as published by the Free Software Foundation; either
13  * version 2 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with this library; if not, write to the
22  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
23  * Boston, MA  02110-1301, USA.
24  */
25 
26 #include <stdarg.h>
27 #include <string.h>
28 #include <glib/gi18n-lib.h>
29 #include <libgda/gda-util.h>
30 #include <libgda/gda-connection-private.h>
31 #include "gda-firebird.h"
32 #include "gda-firebird-recordset.h"
33 #include "gda-firebird-provider.h"
34 #include <libgda/libgda-global-variables.h>
35 #include <libgda/gda-debug-macros.h>
36 
37 #define _GDA_PSTMT(x) ((GdaPStmt*)(x))
38 
39 #ifndef ISC_INT64_FORMAT
40 
41 /* Define a format string for printf.  Printing of 64-bit integers
42    is not standard between platforms */
43 
44 #if (defined(_MSC_VER) && defined(WIN32))
45 #define	ISC_INT64_FORMAT	"I64"
46 #else
47 #define	ISC_INT64_FORMAT	"ll"
48 #endif
49 #endif
50 
51 typedef PARAMVARY VARY2;
52 
53 static void gda_firebird_recordset_class_init (GdaFirebirdRecordsetClass *klass);
54 static void gda_firebird_recordset_init       (GdaFirebirdRecordset *recset,
55 					     GdaFirebirdRecordsetClass *klass);
56 static void gda_firebird_recordset_dispose   (GObject *object);
57 
58 /* virtual methods */
59 static gint     gda_firebird_recordset_fetch_nb_rows (GdaDataSelect *model);
60 static gboolean gda_firebird_recordset_fetch_random (GdaDataSelect *model, GdaRow **prow, gint rownum, GError **error);
61 static gboolean gda_firebird_recordset_fetch_next (GdaDataSelect *model, GdaRow **prow, gint rownum, GError **error);
62 static gboolean gda_firebird_recordset_fetch_prev (GdaDataSelect *model, GdaRow **prow, gint rownum, GError **error);
63 static gboolean gda_firebird_recordset_fetch_at (GdaDataSelect *model, GdaRow **prow, gint rownum, GError **error);
64 
65 
66 struct _GdaFirebirdRecordsetPrivate {
67 
68 	gint n_columns;
69 };
70 static GObjectClass *parent_class = NULL;
71 
72 
73 /*
74  * Object init and finalize
75  */
76 static void
gda_firebird_recordset_init(GdaFirebirdRecordset * recset,GdaFirebirdRecordsetClass * klass)77 gda_firebird_recordset_init (GdaFirebirdRecordset *recset,
78 			   GdaFirebirdRecordsetClass *klass)
79 {
80 //WHERE_AM_I;
81 	g_return_if_fail (GDA_IS_FIREBIRD_RECORDSET (recset));
82 
83 	recset->priv = g_new0 (GdaFirebirdRecordsetPrivate, 1);
84 
85 
86 	/* initialize specific information */
87 	//TO_IMPLEMENT;
88 
89 }
90 
91 static void
gda_firebird_recordset_class_init(GdaFirebirdRecordsetClass * klass)92 gda_firebird_recordset_class_init (GdaFirebirdRecordsetClass *klass)
93 {
94 //WHERE_AM_I;
95 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
96 	GdaDataSelectClass *pmodel_class = GDA_DATA_SELECT_CLASS (klass);
97 
98 	parent_class = g_type_class_peek_parent (klass);
99 
100 	object_class->dispose = gda_firebird_recordset_dispose;
101 	pmodel_class->fetch_nb_rows = gda_firebird_recordset_fetch_nb_rows;
102 	pmodel_class->fetch_random = gda_firebird_recordset_fetch_random;
103 
104 	pmodel_class->fetch_next = gda_firebird_recordset_fetch_next;
105 	pmodel_class->fetch_prev = gda_firebird_recordset_fetch_prev;
106 	pmodel_class->fetch_at = gda_firebird_recordset_fetch_at;
107 }
108 
109 static void
gda_firebird_recordset_dispose(GObject * object)110 gda_firebird_recordset_dispose (GObject *object)
111 {
112 	GdaFirebirdRecordset *recset = (GdaFirebirdRecordset *) object;
113 //WHERE_AM_I;
114 	g_return_if_fail (GDA_IS_FIREBIRD_RECORDSET (recset));
115 
116 	if (recset->priv) {
117 		//if (recset->priv->cnc)
118 		//	g_object_unref (recset->priv->cnc);
119 
120 		/* free specific information */
121 		//TO_IMPLEMENT;
122 
123 		g_free (recset->priv);
124 		recset->priv = NULL;
125 	}
126 
127 	parent_class->dispose (object);
128 }
129 
130 /*
131  * Public functions
132  */
133 
134 GType
gda_firebird_recordset_get_type(void)135 gda_firebird_recordset_get_type (void)
136 {
137 	static GType type = 0;
138 
139 	if (G_UNLIKELY (type == 0)) {
140 		static GMutex registering;
141 		static const GTypeInfo info = {
142 			sizeof (GdaFirebirdRecordsetClass),
143 			(GBaseInitFunc) NULL,
144 			(GBaseFinalizeFunc) NULL,
145 			(GClassInitFunc) gda_firebird_recordset_class_init,
146 			NULL,
147 			NULL,
148 			sizeof (GdaFirebirdRecordset),
149 			0,
150 			(GInstanceInitFunc) gda_firebird_recordset_init,
151 			0
152 		};
153 		g_mutex_lock (&registering);
154 		if (type == 0) {
155 #ifdef FIREBIRD_EMBED
156 			type = g_type_register_static (GDA_TYPE_DATA_SELECT, "GdaFirebirdRecordsetEmbed", &info, 0);
157 #else
158 			type = g_type_register_static (GDA_TYPE_DATA_SELECT, "GdaFirebirdRecordset", &info, 0);
159 #endif
160 		}
161 		g_mutex_unlock (&registering);
162 	}
163 
164 	return type;
165 }
166 
167 static GType
_gda_firebird_type_to_gda(XSQLVAR * var)168 _gda_firebird_type_to_gda (XSQLVAR *var){
169 	short       dtype;
170 	GType gtype;
171 	dtype = var->sqltype & ~1;
172 	/*
173 	if ((var->sqltype & 1) && (*var->sqlind < 0)){
174 		return GDA_TYPE_NULL;
175 	}
176 	*/
177 	switch (dtype){
178 		case SQL_TEXT:
179 		case SQL_VARYING:
180 			gtype = G_TYPE_STRING;
181 			//g_print("SQL_TEXT\n");
182 			break;
183 		case SQL_LONG:
184 			gtype = G_TYPE_ULONG;
185 			//g_print("SQL_TYPE_LONG\n");
186 			break;
187 		case SQL_SHORT:
188 		case SQL_INT64:
189 			gtype = G_TYPE_INT;
190 			//g_print("SQL_TYPE_INT\n");
191 			break;
192 		case SQL_FLOAT:
193 			gtype = G_TYPE_FLOAT;
194 			//g_print("SQL_TYPE_FLOAT\n");
195 			break;
196 		case SQL_DOUBLE:
197 			gtype = G_TYPE_DOUBLE;
198 			//g_print("SQL_TYPE_DOUBLE\n");
199 			break;
200 		case SQL_TIMESTAMP:
201 			//gtype = G_TYPE_DATE_TIME;
202 			gtype = GDA_TYPE_TIMESTAMP;
203 			//g_print("SQL_TIMESTAMP\n");
204 			break;
205 		case SQL_TYPE_DATE:
206 			gtype = G_TYPE_DATE;
207 			//g_print("SQL_TYPE_DATE\n");
208 			break;
209 		case SQL_TYPE_TIME:
210 			gtype = GDA_TYPE_TIME;
211 			//g_print("SQL_TYPE_TIME\n");
212 			break;
213 		case SQL_BLOB:
214 		case SQL_ARRAY:
215 		default:
216 			gtype = GDA_TYPE_BLOB;
217 			//g_print("SQL_BLOB\n");
218 			break;
219 	}
220 
221 	return gtype;
222 }
223 
224 /*
225  *    Print column's data.
226  */
227 void
_fb_set_row_data(XSQLVAR * var,GValue * value,GdaRow * row,GType req_col_type)228 _fb_set_row_data (XSQLVAR *var, GValue *value, GdaRow *row, GType req_col_type){
229 	short       dtype;
230 	char        data[2048], *p;
231 	char        blob_s[20], date_s[25];
232 	VARY2        *vary2;
233 	short       len;
234 	struct tm   times;
235 	ISC_QUAD    bid;
236 
237 	dtype = var->sqltype & ~1;
238 	p = data;
239 
240 	/* Null handling.  If the column is nullable and null */
241 	if ((var->sqltype & 1) && (*var->sqlind < 0))
242 	{
243 		switch (dtype)
244 		{
245 			case SQL_TEXT:
246 			case SQL_VARYING:
247 				len = var->sqllen;
248 				break;
249 			case SQL_SHORT:
250 				len = 6;
251 				if (var->sqlscale > 0) len += var->sqlscale;
252 				break;
253 			case SQL_LONG:
254 				len = 11;
255 				if (var->sqlscale > 0) len += var->sqlscale;
256 				break;
257 			case SQL_INT64:
258 				len = 21;
259 				if (var->sqlscale > 0) len += var->sqlscale;
260 				break;
261 			case SQL_FLOAT:
262 				len = 15;
263 				break;
264 			case SQL_DOUBLE:
265 				len = 24;
266 				break;
267 			case SQL_TIMESTAMP:
268 				len = 24;
269 				break;
270 			case SQL_TYPE_DATE:
271 				len = 10;
272 				break;
273 			case SQL_TYPE_TIME:
274 				len = 13;
275 				break;
276 				case SQL_BLOB:
277 				case SQL_ARRAY:
278 				default:
279 					len = 17;
280 					break;
281 		}
282 		//if (req_col_type != G_TYPE_BOOLEAN)
283 		//	g_value_set_string (value, "NULL");
284 		//else
285 		//	g_value_set_boolean(value, FALSE);
286 	}
287 	else
288 	{
289 		switch (dtype)
290 		{
291 			case SQL_TEXT:
292 				sprintf (p, "%.*s", var->sqllen, var->sqldata);
293 				if (req_col_type != G_TYPE_BOOLEAN)
294 					g_value_set_string (value, p);
295 				else
296 					g_value_set_boolean(value, g_ascii_strcasecmp("1", p) == 0 ? TRUE : FALSE);
297 				break;
298 			case SQL_VARYING:
299 				vary2 = (VARY2*) var->sqldata;
300 				vary2->vary_string[vary2->vary_length] = '\0';
301 				g_value_set_string (value, (char *)vary2->vary_string);
302 				break;
303 
304 			case SQL_SHORT:
305 			case SQL_LONG:
306 			case SQL_INT64: {
307 				ISC_INT64	fb_value;
308 				short		field_width;
309 				short		dscale;
310 				switch (dtype) {
311 					case SQL_SHORT:
312 						fb_value = (ISC_INT64)*(short *) var->sqldata;
313 						field_width = 6;
314 						break;
315 					case SQL_LONG:
316 						fb_value = (ISC_INT64)*(int *) var->sqldata;
317 						field_width = 11;
318 						break;
319 					case SQL_INT64:
320 						fb_value = (ISC_INT64)*(ISC_INT64 *) var->sqldata;
321 						field_width = 21;
322 						break;
323 					default:
324 						field_width = 11;
325 						fb_value = 0;
326 						break;
327 				}
328 
329 				dscale = var->sqlscale;
330 				if (dscale < 0) {
331 					ISC_INT64	tens;
332 					short	i;
333 
334 					tens = 1;
335 					for (i = 0; i > dscale; i--)
336 						tens *= 10;
337 
338 					if (fb_value >= 0)
339 						sprintf (p, "%*" ISC_INT64_FORMAT "d.%0*" ISC_INT64_FORMAT "d",
340 							 field_width - 1 + dscale,
341 							 (ISC_INT64) fb_value / tens,
342 							 -dscale,
343 							 (ISC_INT64) fb_value % tens);
344 					else if ((fb_value / tens) != 0)
345 						sprintf (p, "%*" ISC_INT64_FORMAT "d.%0*" ISC_INT64_FORMAT "d",
346 							 field_width - 1 + dscale,
347 							 (ISC_INT64) (fb_value / tens),
348 							 -dscale,
349 							 (ISC_INT64) -(fb_value % tens));
350 					else
351 						sprintf (p, "%*s.%0*" ISC_INT64_FORMAT "d",
352 							 field_width - 1 + dscale,
353 							 "-0",
354 							 -dscale,
355 							 (ISC_INT64) -(fb_value % tens));
356 				}
357 				else if (dscale){
358 					sprintf (p, "%*" ISC_INT64_FORMAT "d%0*d",
359 						 field_width,
360 						 (ISC_INT64) fb_value,
361 						 dscale, 0);
362 				}
363 				else{
364 					sprintf (p, "%*" ISC_INT64_FORMAT "d",
365 						 field_width,
366 						 (ISC_INT64) fb_value);
367 				}
368 
369 				if (req_col_type != G_TYPE_BOOLEAN){
370 					switch (req_col_type) {
371 						case G_TYPE_INT:
372 							g_value_set_int (value, atoi (p));
373 							break;
374 						case G_TYPE_UINT:
375 							g_value_set_uint (value, atoi (p));
376 							break;
377 						case G_TYPE_INT64:
378 							g_value_set_int64(value, atoll(p));
379 							break;
380 						case G_TYPE_UINT64:
381 							g_value_set_uint64(value, atoll(p));
382 							break;
383 						case G_TYPE_LONG:
384 							g_value_set_long (value, atoll (p));
385 							break;
386 						case G_TYPE_ULONG:
387 							g_value_set_ulong (value, atoll (p));
388 							break;
389 						default:
390 							g_print("Oh flip....did not cater for this\n");
391 							g_value_set_int64 (value, atoll (p));
392 							break;
393 					}
394 					/*switch (dtype) {
395 						case SQL_SHORT:
396 							gda_value_set_short (value, atoi (p));
397 							break;
398 						case SQL_LONG:
399 							g_value_set_ulong (value, atoll (p));
400 							break;
401 						case SQL_INT64:
402 						default:
403 							g_value_set_int64 (value, atoll (p));
404 							break;
405 					}*/
406 				}
407 				else
408 					g_value_set_boolean(value, atoll(p) == 1 ? TRUE : FALSE);
409 				}
410 				break;
411 
412 			case SQL_FLOAT:
413 				g_value_set_float (value, *(float *) (var->sqldata));
414 				break;
415 
416 			case SQL_DOUBLE:
417 				g_value_set_double (value, *(double *) (var->sqldata));
418 				break;
419 
420 			case SQL_TIMESTAMP:
421 				isc_decode_timestamp((ISC_TIMESTAMP *)var->sqldata, &times);
422 				sprintf(date_s, "%04d-%02d-%02d %02d:%02d:%02d",
423 						times.tm_year + 1900,
424 						times.tm_mon+1,
425 						times.tm_mday,
426 						times.tm_hour,
427 						times.tm_min,
428 						times.tm_sec);
429 				sprintf(p, "%*s", 22, date_s);
430 				//g_value_set_string (value, p);
431 
432 				GdaTimestamp timestamp;
433 				if (! gda_parse_iso8601_timestamp (&timestamp, date_s)) {
434 					g_print("****ERROR CONVERTING TO TIMESTAMP: %s\n", date_s);
435 
436 					//gda_row_invalidate_value (row, value);
437 					/*
438 					g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
439 							GDA_SERVER_PROVIDER_DATA_ERROR,
440 							_("Invalid timestamp '%s' (format should be YYYY-MM-DD HH:MM:SS[.ms])"),
441 							p);
442 					*/
443 				}
444 				else
445 					gda_value_set_timestamp (value, &timestamp);
446 				break;
447 
448 			case SQL_TYPE_DATE:
449 				isc_decode_sql_date((ISC_DATE *)var->sqldata, &times);
450 				sprintf(date_s, "%04d-%02d-%02d",
451 						times.tm_year + 1900,
452 						times.tm_mon+1,
453 						times.tm_mday);
454 				sprintf(p, "%*s ", 10, date_s);
455 				GDate date;
456 				if (!gda_parse_iso8601_date (&date, date_s)) {
457 					//gda_row_invalidate_value (row, value);
458 					/*
459 					g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
460 							 GDA_SERVER_PROVIDER_DATA_ERROR,
461 							 _("Invalid date '%s' (date format should be YYYY-MM-DD)"), p);
462 					*/
463 				}
464 				else
465 					g_value_set_boxed (value, &date);
466 				break;
467 
468 			case SQL_TYPE_TIME:
469 				isc_decode_sql_time((ISC_TIME *)var->sqldata, &times);
470 				sprintf(date_s, "%02d:%02d:%02d",
471 						times.tm_hour,
472 						times.tm_min,
473 						times.tm_sec);
474 				sprintf(p, "%*s ", 11, date_s);
475 				GdaTime timegda;
476 				if (!gda_parse_iso8601_time (&timegda, date_s)) {
477 					//gda_row_invalidate_value (row, value);
478 					/*
479 					g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
480 							 GDA_SERVER_PROVIDER_DATA_ERROR,
481 							 _("Invalid time '%s' (time format should be HH:MM:SS[.ms])"), p);
482 					*/
483 				}
484 				else
485 					gda_value_set_time (value, &timegda);
486 				break;
487 
488 			case SQL_BLOB:
489 			case SQL_ARRAY:
490 				/* Print the blob id on blobs or arrays */
491 				bid = *(ISC_QUAD *) var->sqldata;
492 				sprintf(blob_s, "%08x:%08x", (unsigned int)bid.gds_quad_high, (unsigned int)bid.gds_quad_low);
493 				sprintf(p, "%17s ", blob_s);
494 				g_value_set_string (value, p);
495 				break;
496 
497 			default:
498 				g_value_set_string(value, "TYPE NOT FOUND");
499 				break;
500 		}
501 
502 		//g_print("\t\t%s\n", p);
503 	}
504 }
505 
506 static GdaRow *
new_row_from_firebird_stmt(GdaFirebirdRecordset * imodel,G_GNUC_UNUSED gint rownum,GType * col_types,GError ** error)507 new_row_from_firebird_stmt (GdaFirebirdRecordset *imodel, G_GNUC_UNUSED gint rownum, GType *col_types, GError **error)
508 {
509 	gint				i;
510 	gint 				fetch_stat;
511 	ISC_STATUS			status[20];
512 	GdaRow*				row = NULL;
513 	GdaFirebirdPStmt*	ps = (GdaFirebirdPStmt *) ((GdaDataSelect *) imodel)->prep_stmt;
514 	//WHERE_AM_I;
515 	if ((fetch_stat = isc_dsql_fetch(status, &(ps->stmt_h), SQL_DIALECT_V6, ps->sqlda)) == 0) {
516 		row = gda_row_new (ps->sqlda->sqld);
517 		for (i = 0; i < ps->sqlda->sqld; i++) {
518 			GValue *value = gda_row_get_value (row, i);
519 			GType type = _gda_firebird_type_to_gda((XSQLVAR *) &(ps->sqlda->sqlvar[i]));
520 
521 			//IF COLUMN TYPES IS NOT EXPLICITLY SPECIFIED THEN WE
522 			//SET THE COLUMN TYPES TO WHAT FIREBIRD RETURNS
523 			if (col_types)
524 				type = col_types[i];
525 			//g_print("col#: %d\n", i);
526 			gda_value_reset_with_type (value, type);
527 			_fb_set_row_data ((XSQLVAR *) &(ps->sqlda->sqlvar[i]), value, row, type);
528 		}
529 		rownum++;
530 	}
531 
532 	return row;
533 }
534 
535 /*
536  * the @ps struct is modified and transferred to the new data model created in
537  * this function
538  */
539 GdaDataModel *
gda_firebird_recordset_new(GdaConnection * cnc,GdaFirebirdPStmt * ps,GdaSet * exec_params,GdaDataModelAccessFlags flags,GType * col_types)540 gda_firebird_recordset_new (GdaConnection            *cnc,
541 			    GdaFirebirdPStmt            *ps,
542 			    GdaSet                   *exec_params,
543 			    GdaDataModelAccessFlags   flags,
544 			    GType                    *col_types)
545 
546 //(GdaConnection *cnc, GdaFirebirdPStmt *ps, GdaDataModelAccessFlags flags, GType *col_types)
547 {
548 	GdaFirebirdRecordset *model;
549 	FirebirdConnectionData *cdata;
550 	gint i;
551 	GdaDataModelAccessFlags rflags;
552 //WHERE_AM_I;
553 	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), NULL);
554 	g_return_val_if_fail (ps, NULL);
555 
556 	cdata = (FirebirdConnectionData*) gda_connection_internal_get_provider_data (cnc);
557 	if (!cdata)
558 		return NULL;
559 
560 	//g_print("RST-SQL>> %s\n", _GDA_PSTMT (ps)->sql);
561 
562 	if (ps->sqlda == NULL){
563 		g_print("ERROR: ps->sqlda seems to be NULL\n");
564 	}
565 	/* make sure @ps reports the correct number of columns using the API*/
566         //if (_GDA_PSTMT (ps)->ncols < 0)
567                 /*_GDA_PSTMT (ps)->ncols = ...;*/
568 		//TO_IMPLEMENT;
569 
570 	/* completing @ps if not yet done */
571 	if (_GDA_PSTMT (ps)->ncols < 0)
572 		_GDA_PSTMT (ps)->ncols = ps->sqlda->sqld;
573 
574 	/* completing @ps if not yet done */
575 	if (!_GDA_PSTMT (ps)->types && (_GDA_PSTMT (ps)->ncols > 0)) {
576 		/* create prepared statement's columns */
577 		GSList *list;
578 		for (i = 0; i < _GDA_PSTMT (ps)->ncols; i++)
579 			_GDA_PSTMT (ps)->tmpl_columns = g_slist_prepend (_GDA_PSTMT (ps)->tmpl_columns,
580 									 gda_column_new ());
581 		_GDA_PSTMT (ps)->tmpl_columns = g_slist_reverse (_GDA_PSTMT (ps)->tmpl_columns);
582 
583 		/* create prepared statement's types, all types are initialized to GDA_TYPE_NULL */
584 		_GDA_PSTMT (ps)->types = g_new (GType, _GDA_PSTMT (ps)->ncols);
585 		for (i = 0; i < _GDA_PSTMT (ps)->ncols; i++)
586 			_GDA_PSTMT (ps)->types [i] = GDA_TYPE_NULL;
587 
588 		if (col_types) {
589 			for (i = 0; ; i++) {
590 				if (col_types [i] > 0) {
591 					if (col_types [i] == G_TYPE_NONE)
592 						break;
593 					if (i >= _GDA_PSTMT (ps)->ncols)
594 						g_warning (_("Column %d out of range (0-%d), ignoring its specified type"), i,
595 							   _GDA_PSTMT (ps)->ncols - 1);
596 					else
597 						_GDA_PSTMT (ps)->types [i] = col_types [i];
598 				}
599 			}
600 		}
601 
602 		/* fill GdaColumn's data */
603 		g_print("FB reported %d columns. Gda col-cnt: %d\n", ps->sqlda->sqld, GDA_PSTMT (ps)->ncols);
604 		for (i=0, list = _GDA_PSTMT (ps)->tmpl_columns;
605 			i < GDA_PSTMT (ps)->ncols;
606 			i++, list = list->next) {
607 			GdaColumn*		column;
608 			GType			gtype;
609 			XSQLVAR*		var;
610 			/*
611 			g_print("\t\tField:%d/%d (fb-cnt=d)\n\t\tSQL-NAME:%s\n\t\tREL-NAME: %s\n\t\tOWN-NAME: %s\n\t\tALIAS: %s\n**************************\n\n"
612 					, i
613 					, GDA_PSTMT (ps)->ncols
614 					//, ps->sqlda->sqld
615 					, var->sqlname
616 					, var->relname
617 					, var->ownname
618 					, var->aliasname);
619 			*/
620 			var = (XSQLVAR *) &(ps->sqlda->sqlvar[i]);
621 			column = GDA_COLUMN (list->data);
622 			/* use C API to set columns' information using gda_column_set_*() */
623 			gtype = _gda_firebird_type_to_gda(var);
624 			_GDA_PSTMT (ps)->types [i] = gtype;
625 			if (col_types)
626 				gda_column_set_g_type (column, col_types[i]);
627 			else
628 				gda_column_set_g_type (column, gtype);
629 
630 			gda_column_set_name (column, var->aliasname);
631 			gda_column_set_description (column, var->aliasname);
632 		}
633 	}
634 
635 
636 	if (ps->input_sqlda != NULL){
637 		g_print("\n\nPRINTING THE INPUT PARAMETERS\n--------------------------\n");
638 		for(i =0; i < ps->input_sqlda->sqld; i++){
639 			g_print("input-paramater #%d: %s\n", i, (ps->input_sqlda->sqlvar[i].sqldata));
640 			g_print("input-len       #%d: %d\n", i, ps->input_sqlda->sqlvar[i].sqllen);
641 		}
642 	}
643 
644 	//RUN ISC_DSQL_DESCRIBE TO GET OUTPUT FIELDS
645 	//isc_dsql_describe(cdata->status, cdata->ftr, &(ps->stmt_h), ps->sqlda);
646 
647 
648 	g_print("isc_dsql_execute\n");
649 	/* post init specific code */
650 	//if (isc_dsql_execute (cdata->status, cdata->ftr, &(ps->stmt_h), SQL_DIALECT_V6, NULL)) {
651 	if (isc_dsql_execute2 (cdata->status, cdata->ftr, &(ps->stmt_h), SQL_DIALECT_V6, ps->input_sqlda, NULL)) {
652 		g_print("\nisc error occured: \n");
653 		isc_print_status(cdata->status);
654 		g_print("\n");
655 	}
656 
657 	isc_dsql_set_cursor_name(cdata->status, &(ps->stmt_h), "dyn_cursor", NULL);
658 
659 	/* determine access mode: RANDOM or CURSOR FORWARD are the only supported */
660 	if (flags & GDA_DATA_MODEL_ACCESS_RANDOM){
661 		rflags = GDA_DATA_MODEL_ACCESS_RANDOM;
662 		g_print("\nRANDOM ACCESS\n");
663 	}
664 	else{
665 		rflags = GDA_DATA_MODEL_ACCESS_CURSOR_FORWARD;
666 		g_print("CURSOR FORWARD ACCESS\n");
667 	}
668 	/* create data model */
669 	g_print("Creating the data-model\n");
670 	model = g_object_new (GDA_TYPE_FIREBIRD_RECORDSET
671 			, "connection", cnc
672 			, "prepared-stmt", ps
673 			, "model-usage", rflags
674 			, "exec-params", exec_params
675 			, NULL);
676 	g_print("point to the connection\n");
677 	//model->priv->cnc = cnc;
678 	g_print("set the number of columns\n");
679 	//model->priv->n_columns	= ps->sqlda->sqld;
680 	g_print("add reference to connection\n");
681 	//g_object_ref (model->priv->cnc);
682 	gda_data_select_set_columns (GDA_DATA_SELECT (model), _GDA_PSTMT (ps)->tmpl_columns);
683 
684 	gint rownum = 0;
685 	g_print("populate the model\n");
686 	GdaRow *row = NULL;
687 	while ((row = new_row_from_firebird_stmt (model, rownum, col_types, NULL)) != NULL) {
688 		gda_data_select_take_row ((GdaDataSelect*) model, row, rownum);
689 		rownum++;
690 	}
691 
692 	isc_dsql_free_statement(cdata->status, &(ps->stmt_h), DSQL_close);
693 
694 	g_print("SQL-ROWS >> %d\n", rownum);
695 	((GdaDataSelect *) model)->advertized_nrows = rownum;
696 	/*
697 	g_print("\n\ncreating a dump of the table\n\n");
698 	g_print("%s", gda_data_model_dump_as_string(GDA_DATA_MODEL (model)));
699 	g_print("\n\nreturning the data model\n\n");
700 	*/
701 	return GDA_DATA_MODEL (model);
702 }
703 
704 
705 /*
706  * Get the number of rows in @model, if possible
707  */
708 static gint
gda_firebird_recordset_fetch_nb_rows(GdaDataSelect * model)709 gda_firebird_recordset_fetch_nb_rows (GdaDataSelect *model)
710 {
711 	//GdaFirebirdRecordset *imodel;
712 	//WHERE_AM_I;
713 	//imodel = GDA_FIREBIRD_RECORDSET (model);
714 	if (model->advertized_nrows >= 0)
715 		return model->advertized_nrows;
716 
717 	/* use C API to determine number of rows,if possible */
718 	TO_IMPLEMENT;
719 
720 	return model->advertized_nrows;
721 }
722 
723 /*
724  * Create a new filled #GdaRow object for the row at position @rownum, and put it into *prow.
725  *
726  * NOTES:
727  * - @prow will NOT be NULL, but *prow WILL be NULL.
728  * - a new #GdaRow object has to be created, corresponding to the @rownum row
729  * - memory management for that new GdaRow object is left to the implementation, which
730  *   can use gda_data_select_take_row() to "give" the GdaRow to @model (in this case
731  *   this method won't be called anymore for the same @rownum), or may decide to
732  *   keep a cache of GdaRow object and "recycle" them.
733  * - implementing this method is MANDATORY if the data model supports random access
734  * - this method is only called when data model is used in random access mode
735  */
736 static gboolean
gda_firebird_recordset_fetch_random(GdaDataSelect * model,GdaRow ** prow,gint rownum,GError ** error)737 gda_firebird_recordset_fetch_random (GdaDataSelect *model, GdaRow **prow, gint rownum, GError **error)
738 {
739 	gboolean				result = FALSE;
740 	GdaFirebirdRecordset*	imodel;
741 	//WHERE_AM_I;
742 	imodel = GDA_FIREBIRD_RECORDSET (model);
743 
744 	if ((*prow = new_row_from_firebird_stmt (imodel, rownum, NULL, NULL)) != NULL) {
745 		result = TRUE;
746 		gda_data_select_take_row (model, *prow, rownum);
747 	}
748 
749 	return result;
750 }
751 
752 /*
753  * Create and "give" filled #GdaRow object for all the rows in the model
754  */
755 static gboolean
gda_firebird_recordset_store_all(GdaDataSelect * model,GError ** error)756 gda_firebird_recordset_store_all (GdaDataSelect *model, GError **error)
757 {
758 	//GdaFirebirdRecordset *imodel;
759 	gint i;
760 	//WHERE_AM_I;
761 	//imodel = GDA_FIREBIRD_RECORDSET (model);
762 
763 	/* default implementation */
764 	for (i = 0; i < model->advertized_nrows; i++) {
765 		GdaRow *prow;
766 		if (! gda_firebird_recordset_fetch_random (model, &prow, i, error))
767 			return FALSE;
768 	}
769 	return TRUE;
770 }
771 
772 /*
773  * Create a new filled #GdaRow object for the next cursor row, and put it into *prow.
774  *
775  * NOTES:
776  * - @prow will NOT be NULL, but *prow WILL be NULL.
777  * - a new #GdaRow object has to be created, corresponding to the @rownum row
778  * - memory management for that new GdaRow object is left to the implementation, which
779  *   can use gda_data_select_take_row() to "give" the GdaRow to @model (in this case
780  *   this method won't be called anymore for the same @rownum), or may decide to
781  *   keep a cache of GdaRow object and "recycle" them.
782  * - implementing this method is MANDATORY
783  * - this method is only called when data model is used in cursor access mode
784  */
785 static gboolean
gda_firebird_recordset_fetch_next(GdaDataSelect * model,GdaRow ** prow,gint rownum,GError ** error)786 gda_firebird_recordset_fetch_next (GdaDataSelect *model, GdaRow **prow, gint rownum, GError **error)
787 {
788 	gboolean				result = FALSE;
789 	GdaFirebirdRecordset*	imodel;
790 	//WHERE_AM_I;
791 	imodel = GDA_FIREBIRD_RECORDSET (model);
792 
793 	if ((*prow = new_row_from_firebird_stmt (imodel, rownum, NULL, NULL)) != NULL) {
794 		result = TRUE;
795 		gda_data_select_take_row (model, *prow, rownum);
796 	}
797 
798 	return result;
799 }
800 
801 /*
802  * Create a new filled #GdaRow object for the previous cursor row, and put it into *prow.
803  *
804  * NOTES:
805  * - @prow will NOT be NULL, but *prow WILL be NULL.
806  * - a new #GdaRow object has to be created, corresponding to the @rownum row
807  * - memory management for that new GdaRow object is left to the implementation, which
808  *   can use gda_data_select_take_row() to "give" the GdaRow to @model (in this case
809  *   this method won't be called anymore for the same @rownum), or may decide to
810  *   keep a cache of GdaRow object and "recycle" them.
811  * - implementing this method is OPTIONAL (in this case the data model is assumed not to
812  *   support moving iterators backward)
813  * - this method is only called when data model is used in cursor access mode
814  */
815 static gboolean
gda_firebird_recordset_fetch_prev(GdaDataSelect * model,GdaRow ** prow,gint rownum,GError ** error)816 gda_firebird_recordset_fetch_prev (GdaDataSelect *model, GdaRow **prow, gint rownum, GError **error)
817 {
818 	gboolean				result = FALSE;
819 	GdaFirebirdRecordset*	imodel;
820 	//WHERE_AM_I;
821 	imodel = GDA_FIREBIRD_RECORDSET (model);
822 
823 	if ((*prow = new_row_from_firebird_stmt (imodel, rownum, NULL, NULL)) != NULL) {
824 		result = TRUE;
825 		gda_data_select_take_row (model, *prow, rownum);
826 	}
827 
828 	return result;
829 
830 }
831 
832 /*
833  * Create a new filled #GdaRow object for the cursor row at position @rownum, and put it into *prow.
834  *
835  * NOTES:
836  * - @prow will NOT be NULL, but *prow WILL be NULL.
837  * - a new #GdaRow object has to be created, corresponding to the @rownum row
838  * - memory management for that new GdaRow object is left to the implementation, which
839  *   can use gda_data_select_take_row() to "give" the GdaRow to @model (in this case
840  *   this method won't be called anymore for the same @rownum), or may decide to
841  *   keep a cache of GdaRow object and "recycle" them.
842  * - implementing this method is OPTIONAL and usefull only if there is a method quicker
843  *   than iterating one step at a time to the correct position.
844  * - this method is only called when data model is used in cursor access mode
845  */
846 static gboolean
gda_firebird_recordset_fetch_at(GdaDataSelect * model,GdaRow ** prow,gint rownum,GError ** error)847 gda_firebird_recordset_fetch_at (GdaDataSelect *model, GdaRow **prow, gint rownum, GError **error)
848 {
849 	gboolean				result = FALSE;
850 	GdaFirebirdRecordset*	imodel;
851 	//WHERE_AM_I;
852 	imodel = GDA_FIREBIRD_RECORDSET (model);
853 
854 	if ((*prow = new_row_from_firebird_stmt (imodel, rownum, NULL, NULL)) != NULL) {
855 		result = TRUE;
856 		gda_data_select_take_row (model, *prow, rownum);
857 	}
858 
859 	return result;
860 
861 }
862 
863