1 /*
2  * Copyright (C) 2008 - 2011 Vivien Malerba <malerba@gnome-db.org>
3  * Copyright (C) 2010 David King <davidk@openismus.com>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18  * Boston, MA  02110-1301, USA.
19  */
20 
21 #include <glib/gi18n-lib.h>
22 #include <string.h>
23 #include <libgda/libgda.h>
24 #include "gda-jdbc.h"
25 #include "gda-jdbc-util.h"
26 #include "gda-jdbc-blob-op.h"
27 #include "jni-globals.h"
28 
29 struct _GdaJdbcBlobOpPrivate {
30 	GdaConnection *cnc;
31 	GValue        *blob_obj; /* JAVA GdaJBlobOp object */
32 };
33 
34 static void gda_jdbc_blob_op_class_init (GdaJdbcBlobOpClass *klass);
35 static void gda_jdbc_blob_op_init       (GdaJdbcBlobOp *blob,
36 					 GdaJdbcBlobOpClass *klass);
37 static void gda_jdbc_blob_op_finalize   (GObject *object);
38 
39 static glong gda_jdbc_blob_op_get_length (GdaBlobOp *op);
40 static glong gda_jdbc_blob_op_read       (GdaBlobOp *op, GdaBlob *blob, glong offset, glong size);
41 static glong gda_jdbc_blob_op_write      (GdaBlobOp *op, GdaBlob *blob, glong offset);
42 
43 static GObjectClass *parent_class = NULL;
44 
45 /*
46  * Object init and finalize
47  */
48 GType
49 gda_jdbc_blob_op_get_type (void)
50 {
51 	static GType type = 0;
52 
53 	if (G_UNLIKELY (type == 0)) {
54 		static GMutex registering;
55 		static const GTypeInfo info = {
56 			sizeof (GdaJdbcBlobOpClass),
57 			(GBaseInitFunc) NULL,
58 			(GBaseFinalizeFunc) NULL,
59 			(GClassInitFunc) gda_jdbc_blob_op_class_init,
60 			NULL,
61 			NULL,
62 			sizeof (GdaJdbcBlobOp),
63 			0,
64 			(GInstanceInitFunc) gda_jdbc_blob_op_init,
65 			0
66 		};
67 		g_mutex_lock (&registering);
68 		if (type == 0)
69 			type = g_type_register_static (GDA_TYPE_BLOB_OP, "GdaJdbcBlobOp", &info, 0);
70 		g_mutex_unlock (&registering);
71 	}
72 	return type;
73 }
74 
75 static void
76 gda_jdbc_blob_op_init (GdaJdbcBlobOp *op,
77 			   G_GNUC_UNUSED GdaJdbcBlobOpClass *klass)
78 {
79 	g_return_if_fail (GDA_IS_JDBC_BLOB_OP (op));
80 
81 	op->priv = g_new0 (GdaJdbcBlobOpPrivate, 1);
82 	op->priv->blob_obj = NULL;
83 }
84 
85 static void
86 gda_jdbc_blob_op_class_init (GdaJdbcBlobOpClass *klass)
87 {
88 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
89 	GdaBlobOpClass *blob_class = GDA_BLOB_OP_CLASS (klass);
90 
91 	parent_class = g_type_class_peek_parent (klass);
92 
93 	object_class->finalize = gda_jdbc_blob_op_finalize;
94 	blob_class->get_length = gda_jdbc_blob_op_get_length;
95 	blob_class->read = gda_jdbc_blob_op_read;
96 	blob_class->write = gda_jdbc_blob_op_write;
97 }
98 
99 static void
100 gda_jdbc_blob_op_finalize (GObject * object)
101 {
102 	GdaJdbcBlobOp *bop = (GdaJdbcBlobOp *) object;
103 
104 	g_return_if_fail (GDA_IS_JDBC_BLOB_OP (bop));
105 
106 	/* free specific information */
107 	if (bop->priv->blob_obj)
108 		gda_value_free (bop->priv->blob_obj);
109 	g_free (bop->priv);
110 	bop->priv = NULL;
111 
112 	parent_class->finalize (object);
113 }
114 
115 /**
116  * gda_jdbc_blob_op_new_with_jblob
117  *
118  * Steals @blob_op.
119  */
120 GdaBlobOp *
121 gda_jdbc_blob_op_new_with_jblob (GdaConnection *cnc, JNIEnv *jenv, jobject blob_obj)
122 {
123 	GdaJdbcBlobOp *bop;
124 	JavaVM *jvm;
125 
126 	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), NULL);
127 	g_return_val_if_fail (blob_obj, NULL);
128 
129 	if ((*jenv)->GetJavaVM (jenv, &jvm))
130 		g_error ("Could not attach JAVA virtual machine's current thread");
131 
132 	bop = g_object_new (GDA_TYPE_JDBC_BLOB_OP, NULL);
133 	bop->priv->cnc = cnc;
134 	bop->priv->blob_obj = gda_value_new_jni_object (jvm, jenv, blob_obj);
135 
136 	return GDA_BLOB_OP (bop);
137 }
138 
139 /*
140  * Get length request
141  */
142 static glong
143 gda_jdbc_blob_op_get_length (GdaBlobOp *op)
144 {
145 	GdaJdbcBlobOp *bop;
146 	GValue *jexec_res;
147 	gint error_code;
148 	gchar *sql_state;
149 	glong retval;
150 	JNIEnv *jenv = NULL;
151 	gboolean jni_detach;
152 	GError *error = NULL;
153 
154 	g_return_val_if_fail (GDA_IS_JDBC_BLOB_OP (op), -1);
155 	bop = GDA_JDBC_BLOB_OP (op);
156 	g_return_val_if_fail (bop->priv, -1);
157 	g_return_val_if_fail (GDA_IS_CONNECTION (bop->priv->cnc), -1);
158 
159 	/* fetch JNIEnv */
160 	jenv = _gda_jdbc_get_jenv (&jni_detach, &error);
161 	if (!jenv)
162 		return -1;
163 
164 	/* get length */
165 	jexec_res = jni_wrapper_method_call (jenv, GdaJBlobOp__length,
166 					     bop->priv->blob_obj, &error_code, &sql_state, &error);
167 	if (!jexec_res) {
168 		_gda_jdbc_make_error (bop->priv->cnc, error_code, sql_state, error);
169 		return -1;
170 	}
171 
172 	_gda_jdbc_release_jenv (jni_detach);
173 
174 	retval = g_value_get_int64 (jexec_res);
175 	gda_value_free (jexec_res);
176 	return retval;
177 }
178 
179 /*
180  * Blob read request
181  */
182 static glong
183 gda_jdbc_blob_op_read (GdaBlobOp *op, GdaBlob *blob, glong offset, glong size)
184 {
185 	GdaJdbcBlobOp *bop;
186 	GdaBinary *bin;
187 	gint error_code;
188 	gchar *sql_state;
189 	GValue *jexec_res;
190 	JNIEnv *jenv = NULL;
191 	gboolean jni_detach;
192 	GError *error = NULL;
193 	jbyteArray bytes;
194 
195 	g_return_val_if_fail (GDA_IS_JDBC_BLOB_OP (op), -1);
196 	bop = GDA_JDBC_BLOB_OP (op);
197 	g_return_val_if_fail (bop->priv, -1);
198 	g_return_val_if_fail (GDA_IS_CONNECTION (bop->priv->cnc), -1);
199 	if (offset >= G_MAXINT)
200 		return -1;
201 	g_return_val_if_fail (blob, -1);
202 
203 	/* fetch JNIEnv */
204 	jenv = _gda_jdbc_get_jenv (&jni_detach, &error);
205 	if (!jenv)
206 		return -1;
207 
208 	/* get data */
209 	jexec_res = jni_wrapper_method_call (jenv, GdaJBlobOp__read,
210 					     bop->priv->blob_obj, &error_code, &sql_state, &error,
211 					     (jlong) offset, (jint) size);
212 	if (!jexec_res) {
213 		_gda_jdbc_make_error (bop->priv->cnc, error_code, sql_state, error);
214 		return -1;
215 	}
216 
217 	/* copy data */
218 	bin = (GdaBinary *) blob;
219 	if (bin->data)
220 		g_free (bin->data);
221 	bytes = (jbyteArray) gda_value_get_jni_object (jexec_res);
222 	bin->binary_length = (*jenv)->GetArrayLength (jenv, bytes);
223 	bin->data = g_new (guchar, bin->binary_length);
224 	(*jenv)->GetByteArrayRegion(jenv, bytes, 0, bin->binary_length, (jbyte *) bin->data);
225 
226 	_gda_jdbc_release_jenv (jni_detach);
227 	gda_value_free (jexec_res);
228 
229 	return bin->binary_length;
230 }
231 
232 /*
233  * Blob write request
234  */
235 static glong
236 gda_jdbc_blob_op_write (GdaBlobOp *op, GdaBlob *blob, glong offset)
237 {
238 	GdaJdbcBlobOp *bop;
239 	GdaBinary *bin;
240 	gint error_code;
241 	gchar *sql_state;
242 	GValue *jexec_res;
243 	JNIEnv *jenv = NULL;
244 	gboolean jni_detach;
245 	GError *error = NULL;
246 	jbyteArray bytes;
247 	glong nbwritten;
248 
249 	g_return_val_if_fail (GDA_IS_JDBC_BLOB_OP (op), -1);
250 	bop = GDA_JDBC_BLOB_OP (op);
251 	g_return_val_if_fail (bop->priv, -1);
252 	g_return_val_if_fail (GDA_IS_CONNECTION (bop->priv->cnc), -1);
253 	g_return_val_if_fail (blob, -1);
254 
255 	/* fetch JNIEnv */
256 	jenv = _gda_jdbc_get_jenv (&jni_detach, &error);
257 	if (!jenv)
258 		return -1;
259 
260 	if (blob->op && (blob->op != op)) {
261 		/* use data through blob->op */
262 		#define buf_size 16384
263 		gint nread = 0;
264 		GdaBlob *tmpblob;
265 
266 		nbwritten = 0;
267 		tmpblob = g_new0 (GdaBlob, 1);
268 		gda_blob_set_op (tmpblob, blob->op);
269 
270 		for (nread = gda_blob_op_read (tmpblob->op, tmpblob, 0, buf_size);
271 		     nread > 0;
272 		     nread = gda_blob_op_read (tmpblob->op, tmpblob, nbwritten, buf_size)) {
273 			GdaBinary *bin = (GdaBinary *) tmpblob;
274 			glong tmp_written;
275 
276 			bytes = (*jenv)->NewByteArray (jenv, nread);
277 			if (jni_wrapper_handle_exception (jenv, &error_code, &sql_state, &error)) {
278 				_gda_jdbc_make_error (bop->priv->cnc, error_code, sql_state, error);
279 				_gda_jdbc_release_jenv (jni_detach);
280 				gda_blob_free ((gpointer) tmpblob);
281 				return -1;
282 			}
283 
284 			(*jenv)->SetByteArrayRegion (jenv, bytes, 0, nread, (jbyte*) bin->data);
285 			if (jni_wrapper_handle_exception (jenv, &error_code, &sql_state, &error)) {
286 				_gda_jdbc_make_error (bop->priv->cnc, error_code, sql_state, error);
287 				(*jenv)->DeleteLocalRef (jenv, bytes);
288 				_gda_jdbc_release_jenv (jni_detach);
289 				gda_blob_free ((gpointer) tmpblob);
290 				return -1;
291 			}
292 
293 			/* write data */
294 			jexec_res = jni_wrapper_method_call (jenv, GdaJBlobOp__write,
295 							     bop->priv->blob_obj, &error_code,
296 							     &sql_state, &error, (jlong) offset, bytes);
297 			(*jenv)->DeleteLocalRef (jenv, bytes);
298 			if (!jexec_res) {
299 				_gda_jdbc_make_error (bop->priv->cnc, error_code, sql_state, error);
300 				_gda_jdbc_release_jenv (jni_detach);
301 				gda_blob_free ((gpointer) tmpblob);
302 				return -1;
303 			}
304 
305 			tmp_written = g_value_get_int64 (jexec_res);
306 			gda_value_free (jexec_res);
307 
308 			g_assert (tmp_written == nread);
309 
310 			nbwritten += tmp_written;
311 			if (nread < buf_size)
312 				/* nothing more to read */
313 				break;
314 		}
315 		gda_blob_free ((gpointer) tmpblob);
316 	}
317 	else {
318 		/* prepare data to be written using bin->data and bin->binary_length */
319 		bin = (GdaBinary *) blob;
320 		bytes = (*jenv)->NewByteArray (jenv, bin->binary_length);
321 		if (jni_wrapper_handle_exception (jenv, &error_code, &sql_state, &error)) {
322 			_gda_jdbc_make_error (bop->priv->cnc, error_code, sql_state, error);
323 			_gda_jdbc_release_jenv (jni_detach);
324 			return -1;
325 		}
326 
327 		(*jenv)->SetByteArrayRegion (jenv, bytes, 0, bin->binary_length, (jbyte*) bin->data);
328 		if (jni_wrapper_handle_exception (jenv, &error_code, &sql_state, &error)) {
329 			_gda_jdbc_make_error (bop->priv->cnc, error_code, sql_state, error);
330 			(*jenv)->DeleteLocalRef (jenv, bytes);
331 			_gda_jdbc_release_jenv (jni_detach);
332 			return -1;
333 		}
334 
335 		/* write data */
336 		jexec_res = jni_wrapper_method_call (jenv, GdaJBlobOp__write,
337 						     bop->priv->blob_obj, &error_code, &sql_state, &error,
338 						     (jlong) offset, bytes);
339 		(*jenv)->DeleteLocalRef (jenv, bytes);
340 		if (!jexec_res) {
341 			_gda_jdbc_make_error (bop->priv->cnc, error_code, sql_state, error);
342 			return -1;
343 		}
344 		nbwritten = g_value_get_int64 (jexec_res);
345 		gda_value_free (jexec_res);
346 	}
347 	_gda_jdbc_release_jenv (jni_detach);
348 
349 	return nbwritten;
350 }
351