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 (®istering);
135 if (type == 0)
136 type = g_type_register_static (GDA_TYPE_DATA_SELECT, "GdaJdbcRecordset", &info, 0);
137 g_mutex_unlock (®istering);
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