1 /*
2  * Copyright (C) 2008 - 2013 Vivien Malerba <malerba@gnome-db.org>
3  * Copyright (C) 2009 Bas Driessen <bas.driessen@xobas.com>
4  * Copyright (C) 2010 David King <davidk@openismus.com>
5  * Copyright (C) 2011 Murray Cumming <murrayc@murrayc.com>
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20  * Boston, MA  02110-1301, USA.
21  */
22 
23 #include <stdarg.h>
24 #include <string.h>
25 #include <glib/gi18n-lib.h>
26 #include <libgda/gda-util.h>
27 #include <libgda/gda-connection-private.h>
28 #include "gda-jdbc.h"
29 #include "gda-jdbc-recordset.h"
30 #include "gda-jdbc-provider.h"
31 #include "jni-wrapper.h"
32 #include "jni-globals.h"
33 #include "gda-jdbc-util.h"
34 #include "gda-jdbc-blob-op.h"
35 
36 #define _GDA_PSTMT(x) ((GdaPStmt*)(x))
37 
38 static void gda_jdbc_recordset_class_init (GdaJdbcRecordsetClass *klass);
39 static void gda_jdbc_recordset_init       (GdaJdbcRecordset *recset,
40 					     GdaJdbcRecordsetClass *klass);
41 static void gda_jdbc_recordset_dispose   (GObject *object);
42 
43 /* virtual methods */
44 static gint     gda_jdbc_recordset_fetch_nb_rows (GdaDataSelect *model);
45 static gboolean gda_jdbc_recordset_fetch_random (GdaDataSelect *model, GdaRow **prow, gint rownum, GError **error);
46 static gboolean gda_jdbc_recordset_fetch_next (GdaDataSelect *model, GdaRow **prow, gint rownum, GError **error);
47 
48 
49 struct _GdaJdbcRecordsetPrivate {
50 	GdaConnection *cnc;
51 	GValue        *rs_value; /* JAVA GdaJResultSet object */
52 
53 	gint           next_row_num;
54         GdaRow        *tmp_row; /* used in cursor mode */
55 };
56 static GObjectClass *parent_class = NULL;
57 
58 /*
59  * Object init and finalize
60  */
61 static void
gda_jdbc_recordset_init(GdaJdbcRecordset * recset,G_GNUC_UNUSED GdaJdbcRecordsetClass * klass)62 gda_jdbc_recordset_init (GdaJdbcRecordset *recset,
63 			 G_GNUC_UNUSED GdaJdbcRecordsetClass *klass)
64 {
65 	g_return_if_fail (GDA_IS_JDBC_RECORDSET (recset));
66 	recset->priv = g_new0 (GdaJdbcRecordsetPrivate, 1);
67 	recset->priv->cnc = NULL;
68 	recset->priv->rs_value = NULL;
69 }
70 
71 static void
gda_jdbc_recordset_class_init(GdaJdbcRecordsetClass * klass)72 gda_jdbc_recordset_class_init (GdaJdbcRecordsetClass *klass)
73 {
74 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
75 	GdaDataSelectClass *pmodel_class = GDA_DATA_SELECT_CLASS (klass);
76 
77 	parent_class = g_type_class_peek_parent (klass);
78 
79 	object_class->dispose = gda_jdbc_recordset_dispose;
80 	pmodel_class->fetch_nb_rows = gda_jdbc_recordset_fetch_nb_rows;
81 	pmodel_class->fetch_random = gda_jdbc_recordset_fetch_random;
82 	pmodel_class->fetch_next = gda_jdbc_recordset_fetch_next;
83 	pmodel_class->fetch_prev = NULL;
84         pmodel_class->fetch_at = NULL;
85 }
86 
87 static void
gda_jdbc_recordset_dispose(GObject * object)88 gda_jdbc_recordset_dispose (GObject *object)
89 {
90 	GdaJdbcRecordset *recset = (GdaJdbcRecordset *) object;
91 
92 	g_return_if_fail (GDA_IS_JDBC_RECORDSET (recset));
93 
94 	if (recset->priv) {
95 		if (recset->priv->cnc)
96 			g_object_unref (recset->priv->cnc);
97 
98 		if (recset->priv->rs_value)
99 			gda_value_free (recset->priv->rs_value);
100 
101 		if (recset->priv->tmp_row)
102                         g_object_unref (recset->priv->tmp_row);
103 
104 		g_free (recset->priv);
105 		recset->priv = NULL;
106 	}
107 
108 	parent_class->dispose (object);
109 }
110 
111 /*
112  * Public functions
113  */
114 
115 GType
gda_jdbc_recordset_get_type(void)116 gda_jdbc_recordset_get_type (void)
117 {
118 	static GType type = 0;
119 
120 	if (G_UNLIKELY (type == 0)) {
121 		static GMutex registering;
122 		static const GTypeInfo info = {
123 			sizeof (GdaJdbcRecordsetClass),
124 			(GBaseInitFunc) NULL,
125 			(GBaseFinalizeFunc) NULL,
126 			(GClassInitFunc) gda_jdbc_recordset_class_init,
127 			NULL,
128 			NULL,
129 			sizeof (GdaJdbcRecordset),
130 			0,
131 			(GInstanceInitFunc) gda_jdbc_recordset_init,
132 			0
133 		};
134 		g_mutex_lock (&registering);
135 		if (type == 0)
136 			type = g_type_register_static (GDA_TYPE_DATA_SELECT, "GdaJdbcRecordset", &info, 0);
137 		g_mutex_unlock (&registering);
138 	}
139 
140 	return type;
141 }
142 
143 /* Same as GdaJValue::jdbc_type_to_g_type
144  * See http://docs.oracle.com/javase/6/docs/api/constant-values.html#java.sql.Types.ARRAY for reference
145  */
146 static GType
jdbc_type_to_g_type(gint jdbc_type)147 jdbc_type_to_g_type (gint jdbc_type)
148 {
149 	switch (jdbc_type) {
150 	case 12: /* VARCHAR */
151 		return G_TYPE_STRING;
152 	case 2003: /* ARRAY */
153 		return GDA_TYPE_BINARY;
154  	case -5: /* BIGINT */
155 		return G_TYPE_INT64;
156  	case -2: /* BINARY */
157 		return GDA_TYPE_BINARY;
158  	case -7: /* BIT */
159 		return G_TYPE_BOOLEAN;
160  	case 2004: /* BLOB */
161 		return GDA_TYPE_BLOB;
162  	case 16: /* BOOLEAN */
163 		return G_TYPE_BOOLEAN;
164  	case 1: /* CHAR */
165 		return G_TYPE_STRING;
166  	case 2005: /* CLOB */
167  	case 70: /* DATALINK */
168 		return GDA_TYPE_BINARY;
169  	case 91: /* DATE */
170 		return G_TYPE_DATE;
171  	case 3: /* DECIMAL */
172 		return GDA_TYPE_NUMERIC;
173  	case 2001: /* DISTINCT */
174 		return GDA_TYPE_BINARY;
175  	case 8: /* DOUBLE */
176 		return G_TYPE_DOUBLE;
177  	case 6: /* FLOAT */
178 		return G_TYPE_FLOAT;
179  	case 4: /* INTEGER */
180 		return G_TYPE_INT;
181  	case 2000: /* JAVA_OBJECT */
182 		return GDA_TYPE_BINARY;
183 	case -16: /* LONGNVARCHAR */
184 		return G_TYPE_STRING;
185  	case -4: /* LONGVARBINARY */
186 		return GDA_TYPE_BINARY;
187  	case -1: /* LONGVARCHAR */
188 		return G_TYPE_STRING;
189 	case -15: /* NCHAR */
190 		return G_TYPE_STRING;
191 	case 2011: /* NCLOB */
192 		return GDA_TYPE_BINARY;
193  	case 0: /* NULL */
194 		return GDA_TYPE_NULL;
195 	case 2: /* NUMERIC */
196 		return GDA_TYPE_NUMERIC;
197 	case -9: /* NVARCHAR */
198 		return G_TYPE_STRING;
199 	case 1111: /* OTHER */
200 		return GDA_TYPE_BINARY;
201  	case 7: /* REAL */
202 		return G_TYPE_FLOAT;
203  	case 2006: /* REF */
204 		return GDA_TYPE_BINARY;
205 	case -8: /* ROWID */
206 		return G_TYPE_STRING;
207  	case 5: /* SMALLINT */
208 		return GDA_TYPE_SHORT;
209 	case 2009: /* SQLXML */
210 		return G_TYPE_STRING;
211  	case 2002: /* STRUCT */
212 		return GDA_TYPE_BINARY;
213  	case 92: /* TIME */
214 		return GDA_TYPE_TIME;
215  	case 93: /* TIMESTAMP */
216 		return GDA_TYPE_TIMESTAMP;
217  	case -6: /* TINYINT */
218 		return G_TYPE_CHAR;
219  	case -3: /* VARBINARY */
220 		return GDA_TYPE_BINARY;
221 	default:
222 		return GDA_TYPE_BINARY;
223 	}
224 }
225 
226 /*
227  * the @ps struct is modified and transferred to the new data model created in
228  * this function
229  */
230 GdaDataModel *
gda_jdbc_recordset_new(GdaConnection * cnc,GdaJdbcPStmt * ps,GdaSet * exec_params,JNIEnv * jenv,GValue * rs_value,GdaDataModelAccessFlags flags,GType * col_types)231 gda_jdbc_recordset_new (GdaConnection *cnc, GdaJdbcPStmt *ps, GdaSet *exec_params,
232 			JNIEnv *jenv, GValue *rs_value, GdaDataModelAccessFlags flags, GType *col_types)
233 {
234 	GdaJdbcRecordset *model;
235         JdbcConnectionData *cdata;
236         gint i;
237 	GdaDataModelAccessFlags rflags;
238 	GValue *jexec_res;
239 
240 	GError *error = NULL;
241 	gint error_code;
242 	gchar *sql_state;
243 
244         g_return_val_if_fail (GDA_IS_CONNECTION (cnc), NULL);
245 	if (ps)
246 		g_return_val_if_fail (GDA_IS_JDBC_PSTMT (ps), NULL);
247 	else
248 		ps = gda_jdbc_pstmt_new (NULL);
249 
250 	cdata = (JdbcConnectionData*) gda_connection_internal_get_provider_data (cnc);
251 	if (!cdata)
252 		return NULL;
253 
254 	/* make sure @ps reports the correct number of columns using the API*/
255 	jexec_res = jni_wrapper_method_call (jenv, GdaJResultSet__getInfos,
256 					     rs_value, &error_code, &sql_state, &error);
257 	if (!jexec_res) {
258 		_gda_jdbc_make_error (cnc, error_code, sql_state, error);
259 		gda_value_free (rs_value);
260 		return NULL;
261 	}
262 
263         if (_GDA_PSTMT (ps)->ncols < 0) {
264 		GValue *jfield_v;
265 		jfield_v = jni_wrapper_field_get (jenv, GdaJResultSetInfos__ncols, jexec_res, &error);
266 		if (! jfield_v) {
267 			gda_value_free (jexec_res);
268 			gda_value_free (rs_value);
269 			return NULL;
270 		}
271                 _GDA_PSTMT (ps)->ncols = g_value_get_int (jfield_v);
272 		gda_value_free (jfield_v);
273 	}
274 
275         /* completing @ps if not yet done */
276         if (!_GDA_PSTMT (ps)->types && (_GDA_PSTMT (ps)->ncols > 0)) {
277 		/* create prepared statement's columns */
278 		GSList *list;
279 		gboolean allok = TRUE;
280 
281 		for (i = 0; i < _GDA_PSTMT (ps)->ncols; i++)
282 			_GDA_PSTMT (ps)->tmpl_columns = g_slist_prepend (_GDA_PSTMT (ps)->tmpl_columns,
283 									 gda_column_new ());
284 		_GDA_PSTMT (ps)->tmpl_columns = g_slist_reverse (_GDA_PSTMT (ps)->tmpl_columns);
285 
286 		/* create prepared statement's types, all types are initialized to GDA_TYPE_NULL */
287 		_GDA_PSTMT (ps)->types = g_new (GType, _GDA_PSTMT (ps)->ncols);
288 		for (i = 0; i < _GDA_PSTMT (ps)->ncols; i++)
289 			_GDA_PSTMT (ps)->types [i] = GDA_TYPE_NULL;
290 
291 		if (col_types) {
292 			for (i = 0; ; i++) {
293 				if (col_types [i] > 0) {
294 					if (col_types [i] == G_TYPE_NONE)
295 						break;
296 					if (i >= _GDA_PSTMT (ps)->ncols) {
297 						g_warning (_("Column %d out of range (0-%d), ignoring its specified type"), i,
298 							   _GDA_PSTMT (ps)->ncols - 1);
299 						break;
300 					}
301 					else
302 						_GDA_PSTMT (ps)->types [i] = col_types [i];
303 				}
304 			}
305 		}
306 
307 		/* fill GdaColumn's data */
308 		for (i = 0, list = _GDA_PSTMT (ps)->tmpl_columns;
309 		     allok && (i < GDA_PSTMT (ps)->ncols);
310 		     i++, list = list->next) {
311 			GdaColumn *column;
312 			GValue *jcol_v;
313 			GValue *jcol_a;
314 
315 			jcol_v = jni_wrapper_method_call (jenv, GdaJResultSetInfos__describeColumn, jexec_res,
316 							  NULL, NULL, NULL, i);
317 			if (!jcol_v) {
318 				allok = FALSE;
319 				break;
320 			}
321 			column = GDA_COLUMN (list->data);
322 			jcol_a = jni_wrapper_field_get (jenv, GdaJColumnInfos__col_name, jcol_v, NULL);
323 			if (jcol_a) {
324 				if (G_VALUE_TYPE (jcol_a) != GDA_TYPE_NULL)
325 					gda_column_set_name (column, g_value_get_string (jcol_a));
326 				gda_value_free (jcol_a);
327 			}
328 			else
329 				allok = FALSE;
330 			jcol_a = jni_wrapper_field_get (jenv, GdaJColumnInfos__col_descr, jcol_v, NULL);
331 			if (jcol_a) {
332 				if (G_VALUE_TYPE (jcol_a) != GDA_TYPE_NULL)
333 					gda_column_set_description (column, g_value_get_string (jcol_a));
334 				gda_value_free (jcol_a);
335 			}
336 			else
337 				allok = FALSE;
338 			jcol_a = jni_wrapper_field_get (jenv, GdaJColumnInfos__col_type, jcol_v, NULL);
339 			if (jcol_a) {
340 				_GDA_PSTMT (ps)->types [i] = jdbc_type_to_g_type (g_value_get_int (jcol_a));
341 				gda_column_set_g_type (column, _GDA_PSTMT (ps)->types [i]);
342 				gda_value_free (jcol_a);
343 			}
344 			else
345 				allok = FALSE;
346 			gda_value_free (jcol_v);
347 		}
348 		if (!allok) {
349 			g_free (_GDA_PSTMT (ps)->types);
350 			_GDA_PSTMT (ps)->types = NULL;
351 			g_slist_foreach (_GDA_PSTMT (ps)->tmpl_columns, (GFunc) g_object_unref, NULL);
352 			g_slist_free (_GDA_PSTMT (ps)->tmpl_columns);
353 			_GDA_PSTMT (ps)->tmpl_columns = NULL;
354 
355 			gda_value_free (jexec_res);
356 			gda_value_free (rs_value);
357 			return NULL;
358 		}
359         }
360 	gda_value_free (jexec_res);
361 
362 	/* declare the requested types (and connection pointer) to the used resultset */
363 	jbyte *ctypes;
364 	jbyteArray jtypes;
365 
366 	ctypes = g_new (jbyte, GDA_PSTMT (ps)->ncols);
367 	for (i = 0; i < GDA_PSTMT (ps)->ncols; i++)
368 		ctypes [i] = _gda_jdbc_gtype_to_proto_type (_GDA_PSTMT (ps)->types [i]);
369 
370 	jtypes = (*jenv)->NewByteArray (jenv, GDA_PSTMT (ps)->ncols);
371 	if (jni_wrapper_handle_exception (jenv, &error_code, &sql_state, &error)) {
372 		g_free (ctypes);
373 		_gda_jdbc_make_error (cnc, error_code, sql_state, error);
374 		gda_value_free (rs_value);
375 		return NULL;
376 	}
377 
378 	(*jenv)->SetByteArrayRegion (jenv, jtypes, 0, GDA_PSTMT (ps)->ncols, ctypes);
379 	if (jni_wrapper_handle_exception (jenv, &error_code, &sql_state, &error)) {
380 		g_free (ctypes);
381 		_gda_jdbc_make_error (cnc, error_code, sql_state, error);
382 		(*jenv)->DeleteLocalRef (jenv, jtypes);
383 		gda_value_free (rs_value);
384 		return NULL;
385 	}
386 
387 	jexec_res = jni_wrapper_method_call (jenv, GdaJResultSet__declareColumnTypes,
388 					     rs_value, &error_code, &sql_state, &error, jni_cpointer_to_jlong (cnc), jtypes);
389 	(*jenv)->DeleteLocalRef (jenv, jtypes);
390 	g_free (ctypes);
391 
392 	if (!jexec_res) {
393 		_gda_jdbc_make_error (cnc, error_code, sql_state, error);
394 		gda_value_free (rs_value);
395 		return NULL;
396 	}
397 
398 	/* determine access mode: RANDOM or CURSOR FORWARD are the only supported */
399 	if (flags & GDA_DATA_MODEL_ACCESS_RANDOM)
400 		rflags = GDA_DATA_MODEL_ACCESS_RANDOM;
401 	else
402 		rflags = GDA_DATA_MODEL_ACCESS_CURSOR_FORWARD;
403 
404 	/* create data model */
405         model = g_object_new (GDA_TYPE_JDBC_RECORDSET,
406 			      "prepared-stmt", ps,
407 			      "model-usage", rflags,
408 			      "exec-params", exec_params, NULL);
409         model->priv->cnc = cnc;
410 	model->priv->rs_value = rs_value;
411 	g_object_ref (cnc);
412 
413         return GDA_DATA_MODEL (model);
414 }
415 
416 static GdaRow *
fetch_next_jdbc_row(GdaJdbcRecordset * model,JNIEnv * jenv,gboolean do_store,GError ** error)417 fetch_next_jdbc_row (GdaJdbcRecordset *model, JNIEnv *jenv, gboolean do_store, GError **error)
418 {
419 	GValue *jexec_res;
420 	gint error_code;
421 	gchar *sql_state;
422 	GError *lerror = NULL;
423 	gboolean row_found;
424 
425 	/* ask JDBC to fetch the next row and store the values */
426 	GdaRow *prow = NULL;
427 	GdaJdbcPStmt *ps;
428 	ps = GDA_JDBC_PSTMT (GDA_DATA_SELECT (model)->prep_stmt);
429 	prow = gda_row_new (_GDA_PSTMT (ps)->ncols);
430 
431 	jexec_res = jni_wrapper_method_call (jenv, GdaJResultSet__fillNextRow,
432 					     model->priv->rs_value, &error_code, &sql_state, &lerror,
433 					     jni_cpointer_to_jlong (prow));
434 	if (!jexec_res) {
435 		if (error && lerror)
436 			*error = g_error_copy (lerror);
437 		_gda_jdbc_make_error (model->priv->cnc, error_code, sql_state, lerror);
438 		g_object_unref ((GObject*) prow);
439 		return NULL;
440 	}
441 
442 	row_found = g_value_get_boolean (jexec_res);
443 	gda_value_free (jexec_res);
444 	if (! row_found) {
445 		GDA_DATA_SELECT (model)->advertized_nrows = model->priv->next_row_num;
446 		g_object_unref ((GObject*) prow);
447 		return NULL;
448 	}
449 
450 	if (do_store) {
451 		/* insert row */
452 		gda_data_select_take_row (GDA_DATA_SELECT (model), prow, model->priv->next_row_num);
453 	}
454 	model->priv->next_row_num ++;
455 
456 	return prow;
457 }
458 
459 
460 /*
461  * Get the number of rows in @model, if possible
462  */
463 static gint
gda_jdbc_recordset_fetch_nb_rows(GdaDataSelect * model)464 gda_jdbc_recordset_fetch_nb_rows (GdaDataSelect *model)
465 {
466 	GdaJdbcRecordset *imodel;
467 	GdaRow *prow = NULL;
468 	JNIEnv *jenv = NULL;
469 	gboolean jni_detach;
470 
471 	imodel = GDA_JDBC_RECORDSET (model);
472 	if (model->advertized_nrows >= 0)
473 		return model->advertized_nrows;
474 
475 	jenv = _gda_jdbc_get_jenv (&jni_detach, NULL);
476 	if (!jenv)
477 		return model->advertized_nrows;
478 
479 	for (prow = fetch_next_jdbc_row (imodel, jenv, TRUE, NULL);
480              prow;
481              prow = fetch_next_jdbc_row (imodel, jenv, TRUE, NULL));
482 
483 	_gda_jdbc_release_jenv (jni_detach);
484 	return model->advertized_nrows;
485 }
486 
487 /*
488  * Create a new filled #GdaRow object for the row at position @rownum.
489  *
490  * Each new #GdaRow created is "given" to the #GdaDataSelect implementation
491  * using gda_data_select_take_row ().
492  */
493 static gboolean
gda_jdbc_recordset_fetch_random(GdaDataSelect * model,GdaRow ** prow,gint rownum,GError ** error)494 gda_jdbc_recordset_fetch_random (GdaDataSelect *model, GdaRow **prow, gint rownum, GError **error)
495 {
496 	GdaJdbcRecordset *imodel;
497 	JNIEnv *jenv = NULL;
498 	gboolean jni_detach;
499 
500 	jenv = _gda_jdbc_get_jenv (&jni_detach, NULL);
501 	if (!jenv)
502 		return TRUE;
503 
504 	imodel = GDA_JDBC_RECORDSET (model);
505 	if (imodel->priv->next_row_num >= rownum) {
506 		g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
507 			     GDA_SERVER_PROVIDER_INTERNAL_ERROR,
508 			     "%s", _("Requested row could not be found"));
509 		return TRUE;
510 	}
511 	for (*prow = fetch_next_jdbc_row (imodel, jenv, TRUE, error);
512 	     *prow && (imodel->priv->next_row_num < rownum);
513 	     *prow = fetch_next_jdbc_row (imodel, jenv, TRUE, error));
514 
515 	_gda_jdbc_release_jenv (jni_detach);
516         return TRUE;
517 }
518 
519 /*
520  * Create a new filled #GdaRow object for the next cursor row, and put it into *prow.
521  *
522  * Each new #GdaRow created is referenced only by imodel->priv->tmp_row (the #GdaDataSelect implementation
523  * never keeps a reference to it). Before a new #GdaRow gets created, the previous one,
524  * if set, is discarded.
525  */
526 static gboolean
gda_jdbc_recordset_fetch_next(GdaDataSelect * model,GdaRow ** prow,gint rownum,GError ** error)527 gda_jdbc_recordset_fetch_next (GdaDataSelect *model, GdaRow **prow, gint rownum, GError **error)
528 {
529 	GdaJdbcRecordset *imodel = (GdaJdbcRecordset*) model;
530 	JNIEnv *jenv = NULL;
531 	gboolean jni_detach;
532 
533 	jenv = _gda_jdbc_get_jenv (&jni_detach, NULL);
534 	if (!jenv)
535 		return FALSE;
536 
537 	if (imodel->priv->tmp_row) {
538                 g_object_unref (imodel->priv->tmp_row);
539 		imodel->priv->tmp_row = NULL;
540 	}
541 	if (imodel->priv->next_row_num != rownum) {
542 		GError *lerror = NULL;
543 		*prow = NULL;
544 		g_set_error (&lerror, GDA_DATA_MODEL_ERROR,
545 			     GDA_DATA_MODEL_ROW_NOT_FOUND_ERROR,
546 			     "%s", _("Can't set iterator on requested row"));
547 		gda_data_select_add_exception (GDA_DATA_SELECT (model), lerror);
548 		if (error)
549 			g_propagate_error (error, g_error_copy (lerror));
550 		_gda_jdbc_release_jenv (jni_detach);
551 
552 		return TRUE;
553 	}
554         *prow = fetch_next_jdbc_row (imodel, jenv, FALSE, error);
555         imodel->priv->tmp_row = *prow;
556 
557 	_gda_jdbc_release_jenv (jni_detach);
558 
559 	return TRUE;
560 }
561 
562