libgda r3255 - in trunk: . doc/C doc/C/tmpl libgda libgda/sqlite providers/postgres providers/skel-implementation/capi tools



Author: vivien
Date: Fri Nov 21 20:40:14 2008
New Revision: 3255
URL: http://svn.gnome.org/viewvc/libgda?rev=3255&view=rev

Log:
2008-11-21  Vivien Malerba <malerba gnome-db org>

	* libgda/dir-blob-op.c: fixed the "write" virtual method
	* tools/gda-sql.c:
	  - added the "setex" command which sets a parameter either from a table's
	    value or from the contents of a file (as a blob)
	  - added the "export" command to export a parameter or a table's value to
	    a file
	  - lines starting with a # are considered as a comment and ignored
	  - added the '-i' option to keep the console after a script has been executed
	* libgda/gda-value.[ch]: added gda_value_new_blob_from_file() as a convenience
	function
	* libgda/gda-column: doc. improvements
	* libgda/gda-blob-op.c:
	* doc/C:
	  - improved documentation for provider's developpers about blob operations
	  - moved the gda_column_set*() to the provider's developpers section
	  - updated the gda-sql console commands description (.setex end .export commands)
	* providers/postgres/gda-postgres-provider.c:
	  - fixed prepared statement names using static counters to avoid naming collisions
	  - remove savepoint correction
	  - better transaction control when using blobs
	* providers/postgres/gda-postgres-blob-op.c:
	  - don't keep the blob "opened" at all times
	  - correctly handle transaction states when accesing the blob
	* providers/postgres/gda-postgres-recordset.h: fixed indentation
	* libgda/sqlite/gda-sqlite-provider.c: note for future implementation of
	BLOBS
	* providers/skel-implementation/capi/gda-capi-blob-op.c:
	* providers/skel-implementation/capi/gda-capi-provider.c: corrections to the
	default provider's skeleton implementation


Modified:
   trunk/ChangeLog
   trunk/doc/C/gda-sql-manual.xml
   trunk/doc/C/libgda-4.0-sections.txt
   trunk/doc/C/prov-writing.xml
   trunk/doc/C/tmpl/gda-column.sgml
   trunk/doc/C/tmpl/gda-data-select-priv.sgml
   trunk/doc/C/tmpl/gda-value.sgml
   trunk/libgda/dir-blob-op.c
   trunk/libgda/gda-blob-op.c
   trunk/libgda/gda-column.c
   trunk/libgda/gda-value.c
   trunk/libgda/gda-value.h
   trunk/libgda/sqlite/gda-sqlite-provider.c
   trunk/providers/postgres/gda-postgres-blob-op.c
   trunk/providers/postgres/gda-postgres-provider.c
   trunk/providers/postgres/gda-postgres-recordset.h
   trunk/providers/skel-implementation/capi/gda-capi-blob-op.c
   trunk/providers/skel-implementation/capi/gda-capi-provider.c
   trunk/tools/gda-sql.c

Modified: trunk/doc/C/gda-sql-manual.xml
==============================================================================
--- trunk/doc/C/gda-sql-manual.xml	(original)
+++ trunk/doc/C/gda-sql-manual.xml	Fri Nov 21 20:40:14 2008
@@ -20,20 +20,21 @@
     <para>
       The &LIBGDA;'s console tool' main features are:
       <itemizedlist>
-	<listitem><para>Can handle more than one connection at the same time</para></listitem>
-	<listitem><para>Reports meta data in a similar way, whatever the real type of database
+	<listitem><para>Handle more than one connection at the same time</para></listitem>
+	<listitem><para>Report meta data in a similar way, whatever the real type of database
 	    accessed</para></listitem>
-	<listitem><para>Can create <emphasis>virtual</emphasis> connections binding 
+	<listitem><para>Create <emphasis>virtual</emphasis> connections binding 
 	    several already opened connections (to run SQL commands across multiple 
 	    connections)</para></listitem>
-	<listitem><para>Allows one to define variables which can be referenced in any
+	<listitem><para>Allow one to define variables which can be referenced in any
 	    SQL statement (using a common syntax)</para></listitem>
-	<listitem><para>Has one query buffer per connection to edit complex queries (uses a
+	<listitem><para>One query buffer per connection to edit complex queries (uses a
 	    parametrable external editor)</para></listitem>
 	<listitem><para>Easy command line editing and output using the Readline library and a
 	    (parametrable) pager</para></listitem>
-	<listitem><para>Works on any platform on which &LIBGDA; has been ported to.</para></listitem>
-	<listitem><para>Can list, define or remove named data sources (DSN)</para></listitem>
+	<listitem><para>Work on any platform on which &LIBGDA; has been ported to.</para></listitem>
+	<listitem><para>List, define or remove named data sources (DSN)</para></listitem>
+	<listitem><para>Import and export BLOBS (binary large objects) from and to local files</para></listitem>
       </itemizedlist>
     </para>
   </sect1>
@@ -638,11 +639,21 @@
   <sect1>
     <title>Internal parameters</title>
     <para>
-      Variables can be defined using the <command>.set</command> command. Variables are then automatically looked for
+      Variables can be defined using the <command>.set</command> and <command>.setex</command> commands.
+      Variables are then automatically looked for
       when executing SQL statements for which a variable is required; they are not typed and are converted to 
       the correct type when needed. Note that variables are shared by all the opened connections.
     </para>
     <para>
+      When setting values, the textual representation must respect the following format:
+      <itemizedlist>
+	<listitem><para>for booleans: "true" or "false" (case insensitive)</para></listitem>
+	<listitem><para>for numerical types: the dot as a fraction separator</para></listitem>
+	<listitem><para>for dates, time and timestamp: the ISO 8601 format (dates as "YYYY-MM-DD", time as "HH:MM:SS")
+	</para></listitem>
+      </itemizedlist>
+    </para>
+    <para>
       Use the <command>.set &lt;variablename&gt; &lt;variable value&gt;</command> command to define a variable,
       and the <command>.set</command> command to list all defined variables. The following example illustrates
       variables usage:
@@ -669,6 +680,38 @@
 SalesTest>
     </programlisting>
     </para>
+    <para>
+      The <command>.setex</command> command also sets an internal parameter, it has two usages:
+      <itemizedlist>
+	<listitem><para>The <command>.set &lt;variablename&gt; &lt;filename&gt;</command> usage loads the contents
+	    of the named filename into the named variable (usually to be used as a BLOB)</para></listitem>
+	<listitem><para>The <command>.set &lt;variablename&gt; &lt;table&gt; &lt;column&gt; &lt;row condition&gt;</command>
+	    usage creates a named variable which contents is the value of the &lt;table&gt; table and
+	  &lt;column&gt; column for the row identified by &lt;row condition&gt;. Note that this command will fail
+	  if the &lt;row condition&gt; condition does not return exactly one value.</para>
+	  <para>
+	    The following example defined a "bl10" variable containing the value of the "blob" column in the "blobs" table
+	    for the "id=10" condition:
+	    <programlisting>
+.setex bl10 blobs blob "id=10"
+	    </programlisting>
+	  </para>
+	</listitem>
+      </itemizedlist>
+    </para>
+    <para>
+      The <command>.export</command> command exports to a file the contents of an internal variable or the
+      the contents of a table's value:
+      <itemizedlist>
+	<listitem><para>The <command>.export &lt;variablename&gt; &lt;filename&gt;</command> usage exports the contents
+	    of the named variable into the named filename</para></listitem>
+	<listitem><para>The <command>.set &lt;table&gt; &lt;column&gt; &lt;row condition&gt; &lt;filename&gt;</command>
+	    usage exports the value of the &lt;table&gt; table and
+	  &lt;column&gt; column for the row identified by &lt;row condition&gt;. Note that this command will fail
+	  if the &lt;row condition&gt; condition does not return exactly one value.</para>
+	</listitem>
+      </itemizedlist>
+    </para>
   </sect1>
 
   <sect1>

Modified: trunk/doc/C/libgda-4.0-sections.txt
==============================================================================
--- trunk/doc/C/libgda-4.0-sections.txt	(original)
+++ trunk/doc/C/libgda-4.0-sections.txt	Fri Nov 21 20:40:14 2008
@@ -5,21 +5,13 @@
 gda_column_new
 gda_column_copy
 gda_column_get_name
-gda_column_set_name
 gda_column_get_description
-gda_column_set_description
 gda_column_get_dbms_type
-gda_column_set_dbms_type
 gda_column_get_g_type
-gda_column_set_g_type
 gda_column_get_allow_null
-gda_column_set_allow_null
 gda_column_get_auto_increment
-gda_column_set_auto_increment
 gda_column_get_position
-gda_column_set_position
 gda_column_get_default_value
-gda_column_set_default_value
 gda_column_get_attribute
 gda_column_set_attribute
 <SUBSECTION Standard>
@@ -697,6 +689,7 @@
 <SECTION>
 <FILE>gda-blob-op</FILE>
 <TITLE>GdaBlobOp</TITLE>
+<INCLUDE>libgda/gda-blob-op.h</INCLUDE>
 GdaBlobOp
 gda_blob_op_get_length
 gda_blob_op_read
@@ -746,6 +739,7 @@
 <SUBSECTION>
 GdaBlob
 gda_value_new_blob
+gda_value_new_blob_from_file
 gda_blob_copy
 gda_blob_free
 gda_value_get_blob
@@ -1396,6 +1390,15 @@
 gda_data_select_take_row
 gda_data_select_get_stored_row
 gda_data_select_get_connection
+<SUBSECTION>
+gda_column_set_name
+gda_column_set_description
+gda_column_set_dbms_type
+gda_column_set_g_type
+gda_column_set_allow_null
+gda_column_set_auto_increment
+gda_column_set_position
+gda_column_set_default_value
 </SECTION>
 
 <SECTION>

Modified: trunk/doc/C/prov-writing.xml
==============================================================================
--- trunk/doc/C/prov-writing.xml	(original)
+++ trunk/doc/C/prov-writing.xml	Fri Nov 21 20:40:14 2008
@@ -540,7 +540,8 @@
       This method is called when the user calls <link linkend="gda-data-model-get-n-rows">gda_data_model_get_n_rows ()</link>.
     </para>
     <para>
-      Note that the number of rows of the data model may not be known until the cursor has reached the
+      Note that the number of rows of the data model may not be known until the cursor has reached the last row of
+      the recordset.
       Once known, the number of rows can be stored in the <structfield>advertized_nrows</structfield>'s member of the
       <link linkend="GdaDataSelect">GdaDataSelect</link> object.
     </para>
@@ -604,31 +605,73 @@
     blobs; otherwise binary data may still be used if supported by the database, but the whole binary data is transfered in
     the SQL statement which is not suitable for large data.
   </para>
+  <para>
+    &LIBGDA; defines <link linkend="GdaBlop">GdaBlob</link> structure which is an extension of the
+    <link linkend="GdaBinary">GdaBinary</link> structure (which contains a pointer to some data and the size of the pointed
+    data). The extension simply adds a pointer to a <link linkend="GdaBlopOp">GdaBlobOp</link> object which has
+    to be implemented by each provider which supports blobs. The following documents the
+    <link linkend="GdaBlopOp">GdaBlobOp</link>'s virtual methods which actually implement the reading from and
+    writing to a blob contained in the database.
+  </para>
+  <para>
+    When reading from a blob in the database or writing to a blob in the database, data read or written is the stored in
+    the <link linkend="GdaBinary">GdaBinary</link> part of the <link linkend="GdaBlopOp">GdaBlobOp</link>.
+  </para>
   <sect2>
     <title>get_length()</title>
     <para>
-      To write.
+      This method returns the total length of a blob in bytes. In case of error, -1 is returned and the
+      provider should have added an error (a <link linkend="GdaConnectionEvent">GdaConnectionEvent</link>) to the connection.
     </para>
   </sect2>
 
   <sect2>
     <title>read()</title>
     <para>
-      To write.
+      This method requests that some data be read from the blob. The data read must be stored in the
+      <link linkend="GdaBinary">GdaBinary</link> part of the <parameter>blob</parameter> parameter. The data to read is
+      the data starting at the <parameter>offset</parameter> offset from the beginning of the blob, and
+      of the <parameter>size</parameter> length.
+    </para>
+    <para>
+      Note that in this method, the <structfield>op</structfield> attribute of the <parameter>blob</parameter>
+      parameter is not used.
+    </para>
+    <para>
+      The returned value is the number of bytes read, or -1 if an error
+      occured (then the provider should have added an error to the connection).
     </para>
   </sect2>
 
   <sect2>
     <title>write()</title>
     <para>
-      To write.
+      This method requests the some data be written to the blob. The data has to be written
+      in the blob starting at the <parameter>offset</parameter> offset from the beginning of the blob.
+    </para>
+    <para>
+      If the <structfield>op</structfield> attribute of the <parameter>blob</parameter> parameter is not NULL and is different
+      than the <parameter>op</parameter>, then the data to be written is the complete contents of the data stored in the
+      blob represented by the <structfield>op</structfield> attribute of the <parameter>blob</parameter> parameter. Otherwise
+      The data to be written is stored in the
+      <link linkend="GdaBinary">GdaBinary</link> part of the <parameter>blob</parameter>.
+    </para>
+    <para>
+      The returned value is the number of bytes written, or -1 if an error
+      occured (then the provider should have added an error to the connection).
     </para>
   </sect2>
 
   <sect2>
     <title>write_all()</title>
     <para>
-      To write.
+      This method requests that all the contents of the blob be replaced by some data (if necessary the
+      blob is truncated from its previous length). The data to be written is the same as for the write() method, and
+      the returned value is also the same.
+    </para>
+    <para>
+      If this virtual method is not implemented, then the write() virtual method is used with an <parameter>offset</parameter>
+      parameter of 0.
     </para>
   </sect2>
 </chapter>

Modified: trunk/doc/C/tmpl/gda-column.sgml
==============================================================================
--- trunk/doc/C/tmpl/gda-column.sgml	(original)
+++ trunk/doc/C/tmpl/gda-column.sgml	Fri Nov 21 20:40:14 2008
@@ -71,15 +71,6 @@
 @Returns: 
 
 
-<!-- ##### FUNCTION gda_column_set_name ##### -->
-<para>
-
-</para>
-
- column: 
- name: 
-
-
 <!-- ##### FUNCTION gda_column_get_description ##### -->
 <para>
 
@@ -89,15 +80,6 @@
 @Returns: 
 
 
-<!-- ##### FUNCTION gda_column_set_description ##### -->
-<para>
-
-</para>
-
- column: 
- title: 
-
-
 <!-- ##### FUNCTION gda_column_get_dbms_type ##### -->
 <para>
 
@@ -107,15 +89,6 @@
 @Returns: 
 
 
-<!-- ##### FUNCTION gda_column_set_dbms_type ##### -->
-<para>
-
-</para>
-
- column: 
- dbms_type: 
-
-
 <!-- ##### FUNCTION gda_column_get_g_type ##### -->
 <para>
 
@@ -125,15 +98,6 @@
 @Returns: 
 
 
-<!-- ##### FUNCTION gda_column_set_g_type ##### -->
-<para>
-
-</para>
-
- column: 
- type: 
-
-
 <!-- ##### FUNCTION gda_column_get_allow_null ##### -->
 <para>
 
@@ -143,15 +107,6 @@
 @Returns: 
 
 
-<!-- ##### FUNCTION gda_column_set_allow_null ##### -->
-<para>
-
-</para>
-
- column: 
- allow: 
-
-
 <!-- ##### FUNCTION gda_column_get_auto_increment ##### -->
 <para>
 
@@ -161,15 +116,6 @@
 @Returns: 
 
 
-<!-- ##### FUNCTION gda_column_set_auto_increment ##### -->
-<para>
-
-</para>
-
- column: 
- is_auto: 
-
-
 <!-- ##### FUNCTION gda_column_get_position ##### -->
 <para>
 
@@ -179,15 +125,6 @@
 @Returns: 
 
 
-<!-- ##### FUNCTION gda_column_set_position ##### -->
-<para>
-
-</para>
-
- column: 
- position: 
-
-
 <!-- ##### FUNCTION gda_column_get_default_value ##### -->
 <para>
 
@@ -197,15 +134,6 @@
 @Returns: 
 
 
-<!-- ##### FUNCTION gda_column_set_default_value ##### -->
-<para>
-
-</para>
-
- column: 
- default_value: 
-
-
 <!-- ##### FUNCTION gda_column_get_attribute ##### -->
 <para>
 

Modified: trunk/doc/C/tmpl/gda-data-select-priv.sgml
==============================================================================
--- trunk/doc/C/tmpl/gda-data-select-priv.sgml	(original)
+++ trunk/doc/C/tmpl/gda-data-select-priv.sgml	Fri Nov 21 20:40:14 2008
@@ -73,3 +73,75 @@
 @Returns: 
 
 
+<!-- ##### FUNCTION gda_column_set_name ##### -->
+<para>
+
+</para>
+
+ column: 
+ name: 
+
+
+<!-- ##### FUNCTION gda_column_set_description ##### -->
+<para>
+
+</para>
+
+ column: 
+ title: 
+
+
+<!-- ##### FUNCTION gda_column_set_dbms_type ##### -->
+<para>
+
+</para>
+
+ column: 
+ dbms_type: 
+
+
+<!-- ##### FUNCTION gda_column_set_g_type ##### -->
+<para>
+
+</para>
+
+ column: 
+ type: 
+
+
+<!-- ##### FUNCTION gda_column_set_allow_null ##### -->
+<para>
+
+</para>
+
+ column: 
+ allow: 
+
+
+<!-- ##### FUNCTION gda_column_set_auto_increment ##### -->
+<para>
+
+</para>
+
+ column: 
+ is_auto: 
+
+
+<!-- ##### FUNCTION gda_column_set_position ##### -->
+<para>
+
+</para>
+
+ column: 
+ position: 
+
+
+<!-- ##### FUNCTION gda_column_set_default_value ##### -->
+<para>
+
+</para>
+
+ column: 
+ default_value: 
+
+

Modified: trunk/doc/C/tmpl/gda-value.sgml
==============================================================================
--- trunk/doc/C/tmpl/gda-value.sgml	(original)
+++ trunk/doc/C/tmpl/gda-value.sgml	Fri Nov 21 20:40:14 2008
@@ -300,6 +300,15 @@
 @Returns: 
 
 
+<!-- ##### FUNCTION gda_value_new_blob_from_file ##### -->
+<para>
+
+</para>
+
+ filename: 
+ Returns: 
+
+
 <!-- ##### FUNCTION gda_blob_copy ##### -->
 <para>
 

Modified: trunk/libgda/dir-blob-op.c
==============================================================================
--- trunk/libgda/dir-blob-op.c	(original)
+++ trunk/libgda/dir-blob-op.c	Fri Nov 21 20:40:14 2008
@@ -160,14 +160,14 @@
 static glong
 gda_dir_blob_op_get_length (GdaBlobOp *op)
 {
-	GdaDirBlobOp *pgop;
+	GdaDirBlobOp *dirop;
 	struct stat filestat;
 
 	g_return_val_if_fail (GDA_IS_DIR_BLOB_OP (op), -1);
-	pgop = GDA_DIR_BLOB_OP (op);
-	g_return_val_if_fail (pgop->priv, -1);
+	dirop = GDA_DIR_BLOB_OP (op);
+	g_return_val_if_fail (dirop->priv, -1);
 
-	if (! g_stat (pgop->priv->complete_filename, &filestat)) 
+	if (! g_stat (dirop->priv->complete_filename, &filestat)) 
 		return filestat.st_size;
 	else
 		return -1;
@@ -176,20 +176,20 @@
 static glong
 gda_dir_blob_op_read (GdaBlobOp *op, GdaBlob *blob, glong offset, glong size)
 {
-	GdaDirBlobOp *pgop;
+	GdaDirBlobOp *dirop;
 	GdaBinary *bin;
 	FILE *file;
 	size_t nread;
 
 	g_return_val_if_fail (GDA_IS_DIR_BLOB_OP (op), -1);
-	pgop = GDA_DIR_BLOB_OP (op);
-	g_return_val_if_fail (pgop->priv, -1);
+	dirop = GDA_DIR_BLOB_OP (op);
+	g_return_val_if_fail (dirop->priv, -1);
 	if (offset >= G_MAXINT)
 		return -1;
 	g_return_val_if_fail (blob, -1);
 
 	/* open file */
-	file = fopen (pgop->priv->complete_filename, "r");
+	file = fopen (dirop->priv->complete_filename, "r");
 	if (!file)
 		return -1;
 	
@@ -217,20 +217,20 @@
 static glong
 gda_dir_blob_op_write (GdaBlobOp *op, GdaBlob *blob, glong offset)
 {
-	GdaDirBlobOp *pgop;
+	GdaDirBlobOp *dirop;
 	GdaBinary *bin;
 	FILE *file;
 	glong nbwritten;
 
 	g_return_val_if_fail (GDA_IS_DIR_BLOB_OP (op), -1);
-	pgop = GDA_DIR_BLOB_OP (op);
-	g_return_val_if_fail (pgop->priv, -1);
+	dirop = GDA_DIR_BLOB_OP (op);
+	g_return_val_if_fail (dirop->priv, -1);
 	if (offset >= G_MAXINT)
 		return -1;
 	g_return_val_if_fail (blob, -1);
 
 	/* open file */
-	file = fopen (pgop->priv->complete_filename, "w+");
+	file = fopen (dirop->priv->complete_filename, "w+");
 	if (!file)
 		return -1;
 	
@@ -241,10 +241,39 @@
 			return -1;
 		}
 	}
-	
-	bin = (GdaBinary *) blob;
-	nbwritten = fwrite ((char *) (bin->data), 1, bin->binary_length, file);
-	fclose (file);
+
+	if (blob->op && (blob->op != op)) {
+		/* use data through blob->op */
+		#define buf_size 16384
+		gint nread = 0;
+		GdaBlob *tmpblob = g_new0 (GdaBlob, 1);
+		tmpblob->op = blob->op;
+
+		nbwritten = 0;
+
+		for (nread = gda_blob_op_read (tmpblob->op, tmpblob, nbwritten, buf_size);
+		     nread > 0;
+		     nread = gda_blob_op_read (tmpblob->op, tmpblob, nbwritten, buf_size)) {
+			GdaBinary *bin = (GdaBinary *) tmpblob;
+			glong tmp_written;
+			tmp_written = fwrite ((char *) (bin->data), 1, bin->binary_length, file);
+			if (tmp_written <= 0) {
+				gda_blob_free ((gpointer) tmpblob);
+				return -1;
+			}
+			nbwritten += tmp_written;
+			if (nread < buf_size)
+				/* nothing more to read */
+				break;
+		}
+		fclose (file);
+		gda_blob_free ((gpointer) tmpblob);
+	}
+	else {
+		bin = (GdaBinary *) blob;
+		nbwritten = fwrite ((char *) (bin->data), 1, bin->binary_length, file);
+		fclose (file);
+	}
 
 	return (nbwritten >= 0) ? nbwritten : -1;
 }

Modified: trunk/libgda/gda-blob-op.c
==============================================================================
--- trunk/libgda/gda-blob-op.c	(original)
+++ trunk/libgda/gda-blob-op.c	Fri Nov 21 20:40:14 2008
@@ -92,10 +92,6 @@
 /**
  * gda_blob_op_get_length
  * @op: an existing #GdaBlobOp
- *
- * Opens an existing BLOB. The BLOB must be initialized by
- * #gda_connection_create_blob or obtained from a #GValue.
- * FIXME: gda_connection_create_blob() no longer exists.
  * 
  * Returns: the length of the blob in bytes. In case of error, -1 is returned and the
  * provider should have added an error (a #GdaConnectionEvent) to the connection.
@@ -118,7 +114,7 @@
  * @offset: offset to read from the start of the blob (starts at 0)
  * @size: maximum number of bytes to read.
  *
- * Reads a chunk of bytes from the BLOB into @blob.
+ * Reads a chunk of bytes from the BLOB accessible through @op into @blob.
  *
  * Returns: the number of bytes actually read. In case of error, -1 is returned and the
  * provider should have added an error to the connection.
@@ -151,7 +147,10 @@
 	g_return_val_if_fail (blob, FALSE);
 
 	len = gda_blob_op_get_length (blob->op);
-	return (gda_blob_op_read (blob->op, blob, 0, len) < 0) ? FALSE : TRUE;
+	if (len >= 0)
+		return (gda_blob_op_read (blob->op, blob, 0, len) < 0) ? FALSE : TRUE;
+	else
+		return FALSE;
 }
 
 /**

Modified: trunk/libgda/gda-column.c
==============================================================================
--- trunk/libgda/gda-column.c	(original)
+++ trunk/libgda/gda-column.c	Fri Nov 21 20:40:14 2008
@@ -577,7 +577,11 @@
  * If there is already an attribute named @attribute set, then its value is replaced with the new @value, 
  * except if @value is %NULL, in which case the attribute is removed.
  *
- * Warning: @sttribute should be a static string (no copy of it is made), so the string should exist as long as the @column
+ * Note: this method does not modify in any way the contents of the data model for which @column is a column (nor
+ * does it modify the table definition of the tables used by a SELECT statement is the model was created from a
+ * SELECT statement).
+ *
+ * Warning: @attribute should be a static string (no copy of it is made), so the string should exist as long as the @column
  * object exists.
  */
 void

Modified: trunk/libgda/gda-value.c
==============================================================================
--- trunk/libgda/gda-value.c	(original)
+++ trunk/libgda/gda-value.c	Fri Nov 21 20:40:14 2008
@@ -38,6 +38,8 @@
 #include <libgda/gda-util.h>
 #include <libxml/parser.h>
 #include <libxml/tree.h>
+#define __GDA_INTERNAL__
+#include "dir-blob-op.h"
 
 #define l_g_value_unset(val) G_STMT_START{ if (G_IS_VALUE (val)) g_value_unset (val); }G_STMT_END
 #ifdef G_OS_WIN32
@@ -1114,7 +1116,7 @@
  * @val: value to set for the new #GValue.
  * @size: the size of the memory pool pointer to by @val.
  *
- * Makes a new #GValue of type #GDA_TYPE_BLOB with value @val.
+ * Makes a new #GValue of type #GDA_TYPE_BLOB with the data contained by @val.
  *
  * Returns: the newly created #GValue.
  */
@@ -1122,20 +1124,44 @@
 gda_value_new_blob (const guchar *val, glong size)
 {
 	GValue *value;
-	GdaBlob blob;
+	GdaBlob *blob;
 	GdaBinary *bin;
 
+	blob = g_new0 (GdaBlob, 1);
 	bin = (GdaBinary*)(&blob);
-
-        /* We use the const on the function parameter to make this clearer, 
-	 * but it would be awkward to keep the const in the struct.
-         */
-        bin->data = (guchar*)val;
+	bin->data = g_new (guchar, size);
+        memcpy ((gpointer) bin->data, (gpointer) val, size);
         bin->binary_length = size;
-	blob.op = NULL;
+	blob->op = NULL;
 
         value = g_new0 (GValue, 1);
-        gda_value_set_blob (value, &blob);
+	g_value_init (value, GDA_TYPE_BLOB);
+        g_value_take_boxed (value, blob);
+
+        return value;
+}
+
+/**
+ * gda_value_new_blob
+ * @val: value to set for the new #GValue.
+ * @size: the size of the memory pool pointer to by @val.
+ *
+ * Makes a new #GValue of type #GDA_TYPE_BLOB with the data contained by @val.
+ *
+ * Returns: the newly created #GValue.
+ */
+GValue *
+gda_value_new_blob_from_file (const gchar *filename)
+{
+	GValue *value;
+	GdaBlob *blob;
+
+	blob = g_new0 (GdaBlob, 1);
+	blob->op = gda_dir_blob_op_new (filename);
+
+        value = g_new0 (GValue, 1);
+	g_value_init (value, GDA_TYPE_BLOB);
+        g_value_take_boxed (value, blob);
 
         return value;
 }

Modified: trunk/libgda/gda-value.h
==============================================================================
--- trunk/libgda/gda-value.h	(original)
+++ trunk/libgda/gda-value.h	Fri Nov 21 20:40:14 2008
@@ -111,6 +111,7 @@
 
 GValue                           *gda_value_new_binary (const guchar *val, glong size);
 GValue                           *gda_value_new_blob (const guchar *val, glong size);
+GValue                           *gda_value_new_blob_from_file (const gchar *filename);
 GValue                           *gda_value_new_timestamp_from_timet (time_t val);
 
 GValue                           *gda_value_new_from_string (const gchar *as_string, GType type);

Modified: trunk/libgda/sqlite/gda-sqlite-provider.c
==============================================================================
--- trunk/libgda/sqlite/gda-sqlite-provider.c	(original)
+++ trunk/libgda/sqlite/gda-sqlite-provider.c	Fri Nov 21 20:40:14 2008
@@ -1653,7 +1653,7 @@
 	if (params)
 		g_object_unref (params);
 
-	/* create a prepared statement */
+	/* create a prepared statement object */
 	ps = gda_sqlite_pstmt_new (sqlite_stmt);
 	gda_pstmt_set_gda_statement (_GDA_PSTMT (ps), stmt);
 	_GDA_PSTMT (ps)->param_ids = param_ids;
@@ -2036,6 +2036,7 @@
 		else if (G_VALUE_TYPE (value) == G_TYPE_UCHAR)
 			sqlite3_bind_int (ps->sqlite_stmt, i, g_value_get_uchar (value));
 		else if (G_VALUE_TYPE (value) == GDA_TYPE_BLOB) {
+			TO_IMPLEMENT; /* use sqlite3_bind_zeroblob () */
 			GdaBinary *bin = (GdaBinary *) gda_value_get_blob (value);
 			sqlite3_bind_blob (ps->sqlite_stmt, i, 
 					   bin->data, bin->binary_length, SQLITE_TRANSIENT);

Modified: trunk/providers/postgres/gda-postgres-blob-op.c
==============================================================================
--- trunk/providers/postgres/gda-postgres-blob-op.c	(original)
+++ trunk/providers/postgres/gda-postgres-blob-op.c	Fri Nov 21 20:40:14 2008
@@ -124,10 +124,11 @@
 		use_svp = TRUE;
 
 	if (use_svp)
-		gda_connection_add_savepoint (pgop->priv->cnc, "__gda_blob_read_svp", NULL);
+		use_svp = gda_connection_add_savepoint (pgop->priv->cnc, "__gda_blob_read_svp", NULL);
 	
 	pgop->priv->fd = lo_open (get_pconn (pgop->priv->cnc), pgop->priv->blobid, INV_READ | INV_WRITE);
 	if (pgop->priv->fd < 0) {
+		_gda_postgres_make_error (pgop->priv->cnc, get_pconn (pgop->priv->cnc), NULL, NULL);
 		if (use_svp)
 			gda_connection_rollback_savepoint (pgop->priv->cnc, "__gda_blob_read_svp", NULL);
 		return FALSE;
@@ -138,6 +139,13 @@
 }
 
 static void
+blob_op_close (GdaPostgresBlobOp *pgop)
+{
+	lo_close (get_pconn (pgop->priv->cnc), pgop->priv->fd);
+	pgop->priv->fd = -1;
+}
+
+static void
 gda_postgres_blob_op_finalize (GObject * object)
 {
 	GdaPostgresBlobOp *pgop = (GdaPostgresBlobOp *) object;
@@ -178,7 +186,6 @@
 	pconn = get_pconn (cnc);
 	pgop->priv->blobid = atoi (sql_id);
 	pgop->priv->cnc = cnc;
-	blob_op_open (pgop);
 
 	return GDA_BLOB_OP (pgop);
 }
@@ -203,8 +210,6 @@
 		}
 	}
 	
-	if (!blob_op_open (pgop))
-		return FALSE;
 	return TRUE;
 }
 
@@ -235,12 +240,25 @@
 	g_return_if_fail (pgop->priv);
 	g_return_if_fail (sql_id);
 
-	if (pgop->priv->fd >= 0) {
-		lo_close (get_pconn (pgop->priv->cnc), pgop->priv->fd);
-		pgop->priv->fd = 0;
-	}
+	if (pgop->priv->fd >= 0)
+		blob_op_close (pgop);
 	pgop->priv->blobid = atoi (sql_id);
-	blob_op_open (pgop);
+}
+
+static gboolean
+check_transaction_started (GdaConnection *cnc, gboolean *out_started)
+{
+        GdaTransactionStatus *trans;
+
+        trans = gda_connection_get_transaction_status (cnc);
+        if (!trans) {
+		if (!gda_connection_begin_transaction (cnc, NULL,
+						       GDA_TRANSACTION_ISOLATION_UNKNOWN, NULL))
+			return FALSE;
+		else
+			*out_started = TRUE;
+	}
+	return TRUE;
 }
 
 /*
@@ -252,20 +270,39 @@
 	GdaPostgresBlobOp *pgop;
 	PGconn *pconn;
 	int pos;
+	gboolean transaction_started = FALSE;
 
 	g_return_val_if_fail (GDA_IS_POSTGRES_BLOB_OP (op), -1);
 	pgop = GDA_POSTGRES_BLOB_OP (op);
 	g_return_val_if_fail (pgop->priv, -1);
 	g_return_val_if_fail (GDA_IS_CONNECTION (pgop->priv->cnc), -1);
+
+	if (! check_transaction_started (pgop->priv->cnc, &transaction_started))
+		return -1;
 	
 	if (!blob_op_open (pgop))
-		return -1;
+		goto out_error;
+
 	pconn = get_pconn (pgop->priv->cnc);
 	pos = lo_lseek (pconn, pgop->priv->fd, 0, SEEK_END);
-        if (pos < 0)
-                return -1;
-        else
-                return pos;
+
+	if (pos < 0) {
+		_gda_postgres_make_error (pgop->priv->cnc, pconn, NULL, NULL);
+		goto out_error;
+	}
+
+	blob_op_close (pgop);
+	if (transaction_started)
+		gda_connection_rollback_transaction (pgop->priv->cnc, NULL, NULL);
+
+	return pos;
+
+ out_error:
+	blob_op_close (pgop);
+	if (transaction_started)
+		gda_connection_rollback_transaction (pgop->priv->cnc, NULL, NULL);
+
+	return -1;
 }
 
 static glong
@@ -274,6 +311,7 @@
 	GdaPostgresBlobOp *pgop;
 	PGconn *pconn;
 	GdaBinary *bin;
+	gboolean transaction_started = FALSE;
 
 	g_return_val_if_fail (GDA_IS_POSTGRES_BLOB_OP (op), -1);
 	pgop = GDA_POSTGRES_BLOB_OP (op);
@@ -283,13 +321,16 @@
 		return -1;
 	g_return_val_if_fail (blob, -1);
 
-	if (!blob_op_open (pgop))
+	if (! check_transaction_started (pgop->priv->cnc, &transaction_started))
 		return -1;
 
+	if (!blob_op_open (pgop))
+		goto out_error;
+
 	pconn = get_pconn (pgop->priv->cnc);
 	if (lo_lseek (pconn, pgop->priv->fd, offset, SEEK_SET) < 0) {
 		_gda_postgres_make_error (pgop->priv->cnc, pconn, NULL, NULL);
-		return -1;
+		goto out_error;
 	}
 
 	bin = (GdaBinary *) blob;
@@ -297,7 +338,19 @@
 		g_free (bin->data);
 	bin->data = g_new0 (guchar, size);
 	bin->binary_length = lo_read (pconn, pgop->priv->fd, (char *) (bin->data), size);
+
+	blob_op_close (pgop);
+	if (transaction_started)
+		gda_connection_rollback_transaction (pgop->priv->cnc, NULL, NULL);
+
 	return bin->binary_length;
+
+ out_error:
+	blob_op_close (pgop);
+	if (transaction_started)
+		gda_connection_rollback_transaction (pgop->priv->cnc, NULL, NULL);
+
+	return -1;
 }
 
 static glong
@@ -305,8 +358,8 @@
 {
 	GdaPostgresBlobOp *pgop;
 	PGconn *pconn;
-	GdaBinary *bin;
 	glong nbwritten;
+	gboolean transaction_started = FALSE;
 
 	g_return_val_if_fail (GDA_IS_POSTGRES_BLOB_OP (op), -1);
 	pgop = GDA_POSTGRES_BLOB_OP (op);
@@ -314,21 +367,66 @@
 	g_return_val_if_fail (GDA_IS_CONNECTION (pgop->priv->cnc), -1);
 	g_return_val_if_fail (blob, -1);
 
-	if (!blob_op_open (pgop))
+	if (! check_transaction_started (pgop->priv->cnc, &transaction_started))
 		return -1;
 
+	if (!blob_op_open (pgop))
+		goto out_error;
+
 	pconn = get_pconn (pgop->priv->cnc);
 	if (lo_lseek (pconn, pgop->priv->fd, offset, SEEK_SET) < 0) {
 		_gda_postgres_make_error (pgop->priv->cnc, pconn, NULL, NULL);
-		return -1;
+		goto out_error;
 	}
 
-	bin = (GdaBinary *) blob;
-	nbwritten = lo_write (pconn, pgop->priv->fd, (char*) bin->data, bin->binary_length);
-	if (nbwritten == -1) {
-		_gda_postgres_make_error (pgop->priv->cnc, pconn, NULL, NULL);
-		return -1;
+	if (blob->op && (blob->op != op)) {
+		/* use data through blob->op */
+		#define buf_size 16384
+		gint nread = 0;
+		GdaBlob *tmpblob = g_new0 (GdaBlob, 1);
+		tmpblob->op = blob->op;
+
+		nbwritten = 0;
+
+		for (nread = gda_blob_op_read (tmpblob->op, tmpblob, nbwritten, buf_size);
+		     nread > 0;
+		     nread = gda_blob_op_read (tmpblob->op, tmpblob, nbwritten, buf_size)) {
+			GdaBinary *bin = (GdaBinary *) tmpblob;
+			glong tmp_written;
+			tmp_written = lo_write (pconn, pgop->priv->fd, (char*) bin->data, 
+						bin->binary_length);
+			if (tmp_written < 0) {
+				_gda_postgres_make_error (pgop->priv->cnc, pconn, NULL, NULL);
+				gda_blob_free ((gpointer) tmpblob);
+				goto out_error;
+			}
+			nbwritten += tmp_written;
+			if (nread < buf_size)
+				/* nothing more to read */
+				break;
+		}
+		gda_blob_free ((gpointer) tmpblob);
+	}
+	else {
+		/* use data in (GdaBinary *) blob */
+		GdaBinary *bin = (GdaBinary *) blob;
+		nbwritten = lo_write (pconn, pgop->priv->fd, (char*) bin->data, bin->binary_length);
+		if (nbwritten == -1) {
+			_gda_postgres_make_error (pgop->priv->cnc, pconn, NULL, NULL);
+			goto out_error;
+		}
 	}
 
+	blob_op_close (pgop);
+	if (transaction_started)
+		if (!gda_connection_commit_transaction (pgop->priv->cnc, NULL, NULL))
+			return -1;
+
 	return nbwritten;
+
+ out_error:
+	blob_op_close (pgop);
+	if (transaction_started)
+		gda_connection_rollback_transaction (pgop->priv->cnc, NULL, NULL);
+	return -1;
 }

Modified: trunk/providers/postgres/gda-postgres-provider.c
==============================================================================
--- trunk/providers/postgres/gda-postgres-provider.c	(original)
+++ trunk/providers/postgres/gda-postgres-provider.c	Fri Nov 21 20:40:14 2008
@@ -822,7 +822,7 @@
 /*
  * Get database request
  *
- * Returns the server version as a string, which should be stored in @cnc's associated PostgresConnectionData structure
+ * Returns the database name as a string
  */
 static const gchar *
 gda_postgres_provider_get_database (GdaServerProvider *provider, GdaConnection *cnc)
@@ -1084,7 +1084,7 @@
                 g_string_free (string, TRUE);
 
 		if (PQstatus (pconn) != CONNECTION_OK) {
-                        g_set_error (error, 0, 0, PQerrorMessage (pconn));
+                        g_set_error (error, 0, 0, "%s", PQerrorMessage (pconn));
                         PQfinish(pconn);
 
                         return FALSE;
@@ -1098,7 +1098,7 @@
 			pg_res = _gda_postgres_PQexec_wrap (cnc, pconn, sql);
 			g_free (sql);
 			if (!pg_res || PQresultStatus (pg_res) != PGRES_COMMAND_OK) {
-				g_set_error (error, 0, 0, PQresultErrorMessage (pg_res));
+				g_set_error (error, 0, 0, "%s", PQresultErrorMessage (pg_res));
 				PQfinish (pconn);
 				return FALSE;
 			}
@@ -1256,6 +1256,7 @@
 
 	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
 	g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
+	g_return_val_if_fail (name && *name, FALSE);
 
 	cdata = (PostgresConnectionData*) gda_connection_internal_get_provider_data (cnc);
 	if (!cdata) 
@@ -1268,13 +1269,9 @@
 	const gchar *remain;
 
 	parser = gda_server_provider_internal_get_parser (provider);
-	if (name)
-		str = g_strdup_printf ("SAVEPOINT %s", name);
-	else
-		str = (gchar *) name;
+	str = g_strdup_printf ("SAVEPOINT %s", name);
 	stmt = gda_sql_parser_parse_string (parser, str, &remain, NULL);
-	if (name)
-		g_free (str);
+	g_free (str);
 
 	if (!stmt) {
 		g_set_error (error, GDA_SERVER_PROVIDER_ERROR, GDA_SERVER_PROVIDER_INTERNAL_ERROR,
@@ -1309,6 +1306,7 @@
 
 	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
 	g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
+	g_return_val_if_fail (name && *name, FALSE);
 
 	cdata = (PostgresConnectionData*) gda_connection_internal_get_provider_data (cnc);
 	if (!cdata) 
@@ -1321,13 +1319,9 @@
 	const gchar *remain;
 
 	parser = gda_server_provider_internal_get_parser (provider);
-	if (name)
-		str = g_strdup_printf ("ROLLBACK TO SAVEPOINT %s", name);
-	else
-		str = (gchar *) name;
+	str = g_strdup_printf ("ROLLBACK TO SAVEPOINT %s", name);
 	stmt = gda_sql_parser_parse_string (parser, str, &remain, NULL);
-	if (name)
-		g_free (str);
+	g_free (str);
 
 	if (!stmt) {
 		g_set_error (error, GDA_SERVER_PROVIDER_ERROR, GDA_SERVER_PROVIDER_INTERNAL_ERROR,
@@ -1362,6 +1356,7 @@
 
 	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
 	g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
+	g_return_val_if_fail (name && *name, FALSE);
 
 	cdata = (PostgresConnectionData*) gda_connection_internal_get_provider_data (cnc);
 	if (!cdata) 
@@ -1374,13 +1369,9 @@
 	const gchar *remain;
 
 	parser = gda_server_provider_internal_get_parser (provider);
-	if (name)
-		str = g_strdup_printf ("DELETE SAVEPOINT %s", name);
-	else
-		str = (gchar *) name;
+	str = g_strdup_printf ("RELEASE SAVEPOINT %s", name);
 	stmt = gda_sql_parser_parse_string (parser, str, &remain, NULL);
-	if (name)
-		g_free (str);
+	g_free (str);
 
 	if (!stmt) {
 		g_set_error (error, GDA_SERVER_PROVIDER_ERROR, GDA_SERVER_PROVIDER_INTERNAL_ERROR,
@@ -1497,8 +1488,8 @@
 		dh = gda_server_provider_handler_find (provider, NULL, type, NULL);
                 if (!dh) {
                         dh = gda_handler_time_new ();
-                        gda_handler_time_set_sql_spec   ((GdaHandlerTime *) dh, G_DATE_YEAR,
-                                                         G_DATE_MONTH, G_DATE_DAY, '-', FALSE);
+                        gda_handler_time_set_sql_spec ((GdaHandlerTime *) dh, G_DATE_YEAR,
+                                                       G_DATE_MONTH, G_DATE_DAY, '-', FALSE);
                         gda_server_provider_handler_declare (provider, dh, NULL, G_TYPE_DATE, NULL);
                         gda_server_provider_handler_declare (provider, dh, NULL, GDA_TYPE_TIME, NULL);
                         gda_server_provider_handler_declare (provider, dh, NULL, GDA_TYPE_TIMESTAMP, NULL);
@@ -1625,6 +1616,7 @@
 {
 	GdaPostgresPStmt *ps;
 	PostgresConnectionData *cdata;
+	static guint counter = 0; /* each prepared statement MUST have a unique name, ensured with this counter */
 
 	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
 	g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
@@ -1656,7 +1648,7 @@
 	gchar *prep_stm_name;
 	GdaConnectionEvent *event = NULL;
 
-	prep_stm_name = g_strdup_printf ("ps%p", stmt);
+	prep_stm_name = g_strdup_printf ("psc%d", counter ++);
 	pg_res = PQprepare (cdata->pconn, prep_stm_name, sql, 0, NULL);
 	if (!pg_res || (PQresultStatus (pg_res) != PGRES_COMMAND_OK)) {
 		event = _gda_postgres_make_error (cnc, cdata->pconn, pg_res, error);
@@ -1698,6 +1690,7 @@
 	
 	gda_connection_add_prepared_statement (cnc, stmt, (GdaPStmt *) ps);
 	g_object_unref (ps);
+
 	return TRUE;
 
  out_err:
@@ -1710,15 +1703,19 @@
 }
 
 static gboolean
-check_transaction_started (GdaConnection *cnc)
+check_transaction_started (GdaConnection *cnc, gboolean *out_started)
 {
         GdaTransactionStatus *trans;
+
         trans = gda_connection_get_transaction_status (cnc);
-        if (!trans && !gda_connection_begin_transaction (cnc, NULL,
-							 GDA_TRANSACTION_ISOLATION_UNKNOWN, NULL))
-                return FALSE;
-        else
-                return TRUE;
+        if (!trans) {
+		if (!gda_connection_begin_transaction (cnc, NULL,
+						       GDA_TRANSACTION_ISOLATION_UNKNOWN, NULL))
+			return FALSE;
+		else
+			*out_started = TRUE;
+	}
+	return TRUE;
 }
 
 /*
@@ -1733,7 +1730,7 @@
 	gchar *prep_stm_name;
 	GdaConnectionEvent *event = NULL;
 
-	prep_stm_name = g_strdup_printf ("ps%d", counter++);
+	prep_stm_name = g_strdup_printf ("pss%d", counter++);
 	pg_res = PQprepare (cdata->pconn, prep_stm_name, sql, 0, NULL);
 	if (!pg_res || (PQresultStatus (pg_res) != PGRES_COMMAND_OK)) {
 		event = _gda_postgres_make_error (cdata->cnc, cdata->pconn, pg_res, error);
@@ -2007,7 +2004,7 @@
 			sql = gda_postgres_provider_statement_to_sql (provider, cnc, stmt, params, 0, NULL, error);
 			if (!sql)
 				return NULL;
-			ps = prepare_stmt_simple (cdata, sql, error);
+			ps = prepare_stmt_simple (cdata, sql, error); // FIXME: this @ps is leaked!
 			g_free (sql);
 			if (!ps)
 				return NULL;
@@ -2025,6 +2022,7 @@
         int *param_lengths = NULL;
         int *param_formats = NULL;
 	gint nb_params;
+	gboolean transaction_started = FALSE;
 	
 	nb_params = g_slist_length (_GDA_PSTMT (ps)->param_ids);
 	param_values = g_new0 (char *, nb_params + 1);
@@ -2060,7 +2058,7 @@
 				event = gda_connection_event_new (GDA_CONNECTION_EVENT_ERROR);
 				gda_connection_event_set_description (event, str);
 				g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
-					     GDA_SERVER_PROVIDER_MISSING_PARAM_ERROR, str);
+					     GDA_SERVER_PROVIDER_MISSING_PARAM_ERROR, "%s", str);
 				g_free (str);
 				break;
 			}
@@ -2078,7 +2076,7 @@
 				event = gda_connection_event_new (GDA_CONNECTION_EVENT_ERROR);
 				gda_connection_event_set_description (event, str);
 				g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
-					     GDA_SERVER_PROVIDER_MISSING_PARAM_ERROR, str);
+					     GDA_SERVER_PROVIDER_MISSING_PARAM_ERROR, "%s", str);
 				g_free (str);
 				break;
 			}
@@ -2100,7 +2098,7 @@
 			GdaPostgresBlobOp *op;
 			
 			/* Postgres requires that a transaction be started for LOB operations */
-			if (!check_transaction_started (cnc)) {
+			if (!check_transaction_started (cnc, &transaction_started)) {
 				event = gda_connection_event_new (GDA_CONNECTION_EVENT_ERROR);
 				gda_connection_event_set_description (event, _("Cannot start transaction"));
 				g_set_error (error, GDA_SERVER_PROVIDER_ERROR, GDA_SERVER_PROVIDER_MISSING_PARAM_ERROR,
@@ -2152,6 +2150,8 @@
 		g_strfreev (param_values);
                 g_free (param_lengths);
                 g_free (param_formats);
+		if (transaction_started)
+			gda_connection_rollback_transaction (cnc, NULL, NULL);
 		return NULL;
 	}
 	
@@ -2168,12 +2168,18 @@
 		GdaStatement *estmt;
 		gchar *esql;
 		estmt = gda_select_alter_select_for_empty (stmt, error);
-		if (!estmt)
+		if (!estmt) {
+			if (transaction_started)
+				gda_connection_rollback_transaction (cnc, NULL, NULL);
 			return NULL;
+		}
 		esql = gda_statement_to_sql (estmt, NULL, error);
 		g_object_unref (estmt);
-		if (!esql) 
+		if (!esql) {
+			if (transaction_started)
+				gda_connection_rollback_transaction (cnc, NULL, NULL);
 			return NULL;
+		}
 
 		pg_res = PQexec (cdata->pconn, esql);
 		g_free (esql);
@@ -2226,6 +2232,9 @@
 	}
 
 	gda_connection_internal_statement_executed (cnc, stmt, params, NULL); /* required: help @cnc keep some stats */
+	if (transaction_started) 
+		gda_connection_commit_transaction (cnc, NULL, NULL);
+
 	return retval;
 }
 

Modified: trunk/providers/postgres/gda-postgres-recordset.h
==============================================================================
--- trunk/providers/postgres/gda-postgres-recordset.h	(original)
+++ trunk/providers/postgres/gda-postgres-recordset.h	Fri Nov 21 20:40:14 2008
@@ -41,7 +41,7 @@
 
 struct _GdaPostgresRecordset {
 	GdaDataSelect                    model;
-	GdaPostgresRecordsetPrivate *priv;
+	GdaPostgresRecordsetPrivate     *priv;
 };
 
 struct _GdaPostgresRecordsetClass {

Modified: trunk/providers/skel-implementation/capi/gda-capi-blob-op.c
==============================================================================
--- trunk/providers/skel-implementation/capi/gda-capi-blob-op.c	(original)
+++ trunk/providers/skel-implementation/capi/gda-capi-blob-op.c	Fri Nov 21 20:40:14 2008
@@ -100,15 +100,15 @@
 static void
 gda_capi_blob_op_finalize (GObject * object)
 {
-	GdaCapiBlobOp *pgop = (GdaCapiBlobOp *) object;
+	GdaCapiBlobOp *bop = (GdaCapiBlobOp *) object;
 
-	g_return_if_fail (GDA_IS_CAPI_BLOB_OP (pgop));
+	g_return_if_fail (GDA_IS_CAPI_BLOB_OP (bop));
 
 	/* free specific information */
 	TO_IMPLEMENT;
 
-	g_free (pgop->priv);
-	pgop->priv = NULL;
+	g_free (bop->priv);
+	bop->priv = NULL;
 
 	parent_class->finalize (object);
 }
@@ -116,14 +116,14 @@
 GdaBlobOp *
 gda_capi_blob_op_new (GdaConnection *cnc)
 {
-	GdaCapiBlobOp *pgop;
+	GdaCapiBlobOp *bop;
 
 	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), NULL);
 
-	pgop = g_object_new (GDA_TYPE_CAPI_BLOB_OP, NULL);
-	pgop->priv->cnc = cnc;
+	bop = g_object_new (GDA_TYPE_CAPI_BLOB_OP, NULL);
+	bop->priv->cnc = cnc;
 	
-	return GDA_BLOB_OP (pgop);
+	return GDA_BLOB_OP (bop);
 }
 
 /*
@@ -132,12 +132,12 @@
 static glong
 gda_capi_blob_op_get_length (GdaBlobOp *op)
 {
-	GdaCapiBlobOp *pgop;
+	GdaCapiBlobOp *bop;
 
 	g_return_val_if_fail (GDA_IS_CAPI_BLOB_OP (op), -1);
-	pgop = GDA_CAPI_BLOB_OP (op);
-	g_return_val_if_fail (pgop->priv, -1);
-	g_return_val_if_fail (GDA_IS_CONNECTION (pgop->priv->cnc), -1);
+	bop = GDA_CAPI_BLOB_OP (op);
+	g_return_val_if_fail (bop->priv, -1);
+	g_return_val_if_fail (GDA_IS_CONNECTION (bop->priv->cnc), -1);
 
 	TO_IMPLEMENT;
 	return -1;
@@ -149,13 +149,13 @@
 static glong
 gda_capi_blob_op_read (GdaBlobOp *op, GdaBlob *blob, glong offset, glong size)
 {
-	GdaCapiBlobOp *pgop;
+	GdaCapiBlobOp *bop;
 	GdaBinary *bin;
 
 	g_return_val_if_fail (GDA_IS_CAPI_BLOB_OP (op), -1);
-	pgop = GDA_CAPI_BLOB_OP (op);
-	g_return_val_if_fail (pgop->priv, -1);
-	g_return_val_if_fail (GDA_IS_CONNECTION (pgop->priv->cnc), -1);
+	bop = GDA_CAPI_BLOB_OP (op);
+	g_return_val_if_fail (bop->priv, -1);
+	g_return_val_if_fail (GDA_IS_CONNECTION (bop->priv->cnc), -1);
 	if (offset >= G_MAXINT)
 		return -1;
 	g_return_val_if_fail (blob, -1);
@@ -178,18 +178,49 @@
 static glong
 gda_capi_blob_op_write (GdaBlobOp *op, GdaBlob *blob, glong offset)
 {
-	GdaCapiBlobOp *pgop;
+	GdaCapiBlobOp *bop;
 	GdaBinary *bin;
+	glong nbwritten = -1;
 
 	g_return_val_if_fail (GDA_IS_CAPI_BLOB_OP (op), -1);
-	pgop = GDA_CAPI_BLOB_OP (op);
-	g_return_val_if_fail (pgop->priv, -1);
-	g_return_val_if_fail (GDA_IS_CONNECTION (pgop->priv->cnc), -1);
+	bop = GDA_CAPI_BLOB_OP (op);
+	g_return_val_if_fail (bop->priv, -1);
+	g_return_val_if_fail (GDA_IS_CONNECTION (bop->priv->cnc), -1);
 	g_return_val_if_fail (blob, -1);
 
-	/* write blob using bin->data and bin->binary_length */
-	bin = (GdaBinary *) blob;
-	TO_IMPLEMENT;
+	if (blob->op && (blob->op != op)) {
+		/* use data through blob->op */
+		#define buf_size 16384
+		gint nread = 0;
+		GdaBlob *tmpblob = g_new0 (GdaBlob, 1);
+		tmpblob->op = blob->op;
+
+		nbwritten = 0;
+
+		for (nread = gda_blob_op_read (tmpblob->op, tmpblob, nbwritten, buf_size);
+		     nread > 0;
+		     nread = gda_blob_op_read (tmpblob->op, tmpblob, nbwritten, buf_size)) {
+			glong tmp_written;
+
+			tmp_written = -1; TO_IMPLEMENT;
+			
+			if (tmp_written < 0) {
+				/* treat error */
+				gda_blob_free ((gpointer) tmpblob);
+				return -1;
+			}
+			nbwritten += tmp_written;
+			if (nread < buf_size)
+				/* nothing more to read */
+				break;
+		}
+		gda_blob_free ((gpointer) tmpblob);
+	}
+	else {
+		/* write blob using bin->data and bin->binary_length */
+		bin = (GdaBinary *) blob;
+		nbwritten = -1; TO_IMPLEMENT;
+	}
 
-	return -1;
+	return nbwritten;
 }

Modified: trunk/providers/skel-implementation/capi/gda-capi-provider.c
==============================================================================
--- trunk/providers/skel-implementation/capi/gda-capi-provider.c	(original)
+++ trunk/providers/skel-implementation/capi/gda-capi-provider.c	Fri Nov 21 20:40:14 2008
@@ -418,7 +418,7 @@
 /*
  * Get database request
  *
- * Returns the server version as a string, which should be stored in @cnc's associated CapiConnectionData structure
+ * Returns the database name as a string, which should be stored in @cnc's associated CapiConnectionData structure
  */
 static const gchar *
 gda_capi_provider_get_database (GdaServerProvider *provider, GdaConnection *cnc)
@@ -908,6 +908,7 @@
 				     GdaStatement *stmt, GError **error)
 {
 	GdaCapiPStmt *ps;
+	gboolean retval = FALSE;
 
 	g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
 	g_return_val_if_fail (gda_connection_get_provider (cnc) == provider, FALSE);
@@ -918,15 +919,58 @@
 	if (ps)
 		return TRUE;
 
+	/* render as SQL understood by the provider */
+	GdaSet *params = NULL;
+	gchar *sql;
+	GSList *used_params = NULL;
+	if (! gda_statement_get_parameters (stmt, &params, error))
+                return FALSE;
+        sql = gda_capi_provider_statement_to_sql (provider, cnc, stmt, params, GDA_STATEMENT_SQL_PARAMS_AS_UQMARK,
+						  &used_params, error);
+        if (!sql) 
+		goto out;
+
 	/* prepare @stmt using the C API, creates @ps */
 	TO_IMPLEMENT;
-	if (!ps)
-		return FALSE;
-	else {
-		gda_connection_add_prepared_statement (cnc, stmt, (GdaPStmt *) ps);
-		g_object_unref (ps);
-		return TRUE;
-	}
+
+	/* make a list of the parameter names used in the statement */
+	GSList *param_ids = NULL;
+        if (used_params) {
+                GSList *list;
+                for (list = used_params; list; list = list->next) {
+                        const gchar *cid;
+                        cid = gda_holder_get_id (GDA_HOLDER (list->data));
+                        if (cid) {
+                                param_ids = g_slist_append (param_ids, g_strdup (cid));
+                                /*g_print ("PREPARATION: param ID: %s\n", cid);*/
+                        }
+                        else {
+                                g_set_error (error, GDA_SERVER_PROVIDER_ERROR, GDA_SERVER_PROVIDER_PREPARE_STMT_ERROR,
+                                             _("Unnamed parameter is not allowed in prepared statements"));
+                                g_slist_foreach (param_ids, (GFunc) g_free, NULL);
+                                g_slist_free (param_ids);
+                                goto out;
+                        }
+                }
+        }
+	
+	/* create a prepared statement object */
+	/*ps = gda_capi_pstmt_new (...);*/
+	gda_pstmt_set_gda_statement (_GDA_PSTMT (ps), stmt);
+        _GDA_PSTMT (ps)->param_ids = param_ids;
+        _GDA_PSTMT (ps)->sql = sql;
+
+	gda_connection_add_prepared_statement (cnc, stmt, (GdaPStmt *) ps);
+	g_object_unref (ps);
+
+	retval = TRUE;
+
+ out:
+	if (used_params)
+                g_slist_free (used_params);
+        if (params)
+                g_object_unref (params);
+	return retval;
 }
 
 /*
@@ -964,7 +1008,7 @@
 	if (async_cb) {
 		g_set_error (error, GDA_SERVER_PROVIDER_ERROR, GDA_SERVER_PROVIDER_METHOD_NON_IMPLEMENTED_ERROR,
 			     _("Provider does not support asynchronous statement execution"));
-                return FALSE;
+                return NULL;
 	}
 
 	if (! (model_usage & GDA_STATEMENT_MODEL_RANDOM_ACCESS) &&
@@ -991,13 +1035,20 @@
 			 * where the C API cannot allow them (for example if the variable is the table name
 			 * in a SELECT statement). The action here is to get the actual SQL code for @stmt,
 			 * and use that SQL instead of @stmt to create another GdaCapiPStmt object.
+			 *
+			 * Don't call gda_connection_add_prepared_statement() with this new prepared statement
+			 * as it will be destroyed once used.
 			 */
 			TO_IMPLEMENT;
 			return NULL;
 		}
-		else
+		else {
 			ps = (GdaCapiPStmt *) gda_connection_get_prepared_statement (cnc, stmt);
+			g_object_ref (ps);
+		}
 	}
+	else
+		g_object_ref (ps);
 	g_assert (ps);
 
 	/* optionnally reset the prepared statement if required by the API */
@@ -1074,6 +1125,7 @@
 		
 	if (event) {
 		gda_connection_add_event (cnc, event);
+		g_object_unref (ps);
 		return NULL;
 	}
 	
@@ -1091,12 +1143,16 @@
 		GdaStatement *estmt;
                 gchar *esql;
                 estmt = gda_select_alter_select_for_empty (stmt, error);
-                if (!estmt)
+                if (!estmt) {
+			g_object_unref (ps);
                         return NULL;
+		}
                 esql = gda_statement_to_sql (estmt, NULL, error);
                 g_object_unref (estmt);
-                if (!esql)
+                if (!esql) {
+			g_object_unref (ps);
                         return NULL;
+		}
 
 		/* Execute the 'esql' SQL code */
                 g_free (esql);
@@ -1121,6 +1177,7 @@
 
                 data_model = (GObject *) gda_capi_recordset_new (cnc, ps, params, flags, col_types);
 		gda_connection_internal_statement_executed (cnc, stmt, params, NULL); /* required: help @cnc keep some stats */
+		g_object_unref (ps);
 		return data_model;
         }
 	else {
@@ -1131,6 +1188,7 @@
 		/* Create GdaConnectionEvent notice with the type of command and impacted rows */
 
 		gda_connection_internal_statement_executed (cnc, stmt, params, event); /* required: help @cnc keep some stats */
+		g_object_unref (ps);
 		return (GObject*) set;
 	}
 }

Modified: trunk/tools/gda-sql.c
==============================================================================
--- trunk/tools/gda-sql.c	(original)
+++ trunk/tools/gda-sql.c	Fri Nov 21 20:40:14 2008
@@ -35,6 +35,7 @@
 #include <sys/types.h>
 #include <libgda/gda-quark-list.h>
 #include <libgda/gda-meta-struct.h>
+#include <libgda/gda-blob-op.h>
 
 #ifndef G_OS_WIN32
 #include <signal.h>
@@ -53,6 +54,7 @@
 
 gchar *single_command = NULL;
 gchar *commandsfile = NULL;
+gboolean interractive = FALSE;
 
 gboolean list_configs = FALSE;
 gboolean list_providers = FALSE;
@@ -65,7 +67,8 @@
 
         { "output-file", 'o', 0, G_OPTION_ARG_STRING, &outfile, "Output file", "output file"},
         { "command", 'C', 0, G_OPTION_ARG_STRING, &single_command, "Run only single command (SQL or internal) and exit", "command" },
-        { "commands-file", 'f', 0, G_OPTION_ARG_STRING, &commandsfile, "Execute commands from file, then exit", "filename" },
+        { "commands-file", 'f', 0, G_OPTION_ARG_STRING, &commandsfile, "Execute commands from file, then exit (except if -i specified)", "filename" },
+	{ "interractive", 'i', 0, G_OPTION_ARG_NONE, &interractive, "Keep the console opened after executing a file (-f option)", NULL },
         { "list-dsn", 'l', 0, G_OPTION_ARG_NONE, &list_configs, "List configured data sources and exit", NULL },
         { "list-providers", 'L', 0, G_OPTION_ARG_NONE, &list_providers, "List installed database providers and exit", NULL },
         { NULL }
@@ -137,7 +140,7 @@
 /* commands manipulation */
 static GdaInternalCommandsList  *build_internal_commands_list (MainData *data);
 static gboolean                  command_is_complete (const gchar *command);
-static GdaInternalCommandResult *command_execute (MainData *data, gchar *command, GError **error);
+static GdaInternalCommandResult *command_execute (MainData *data, const gchar *command, GError **error);
 static void                      display_result (MainData *data, GdaInternalCommandResult *res);
 
 int
@@ -557,7 +560,7 @@
 	compute_prompt (data, prompt, data->partial_command == NULL ? FALSE : TRUE);
 	if (data->input_stream) {
 		cmde = input_from_stream (data->input_stream);
-		if (!cmde && isatty (fileno (stdin))) {
+		if (interractive && !cmde && isatty (fileno (stdin))) {
 			/* go back to console after file is over */
 			set_input_file (data, NULL, NULL);
 			cmde = input_from_console (prompt->str);
@@ -579,10 +582,16 @@
 {
 	if (!command || !(*command))
 		return FALSE;
+	if (single_command)
+		return TRUE;
 	if ((*command == '\\') || (*command == '.')) {
 		/* internal command */
 		return TRUE;
 	}
+	else if (*command == '#') {
+		/* comment, to be ignored */
+		return TRUE;
+	}
 	else {
 		if (command [strlen (command) - 1] == ';')
 			return TRUE;
@@ -597,31 +606,38 @@
  */
 static GdaInternalCommandResult *execute_external_command (MainData *data, const gchar *command, GError **error);
 static GdaInternalCommandResult *
-command_execute (MainData *data, gchar *command, GError **error)
+command_execute (MainData *data, const gchar *command, GError **error)
 {
 	if (!command || !(*command))
-		return NULL;
-	if ((*command == '\\') || (*command == '.')) {
-		if (data->current)
-			return gda_internal_command_execute (data->internal_commands, 
-							     data->current->cnc, command, error);
-		else
-			return gda_internal_command_execute (data->internal_commands, NULL, command, error);
-	}
-	else {
-		if (!data->current) {
-			g_set_error (error, 0, 0, 
-				     _("No connection specified"));
-			return NULL;
-		}
-		if (!gda_connection_is_opened (data->current->cnc)) {
-			g_set_error (error, 0, 0, 
-				     _("Connection closed"));
-			return NULL;
-		}
-			
-		return execute_external_command (data, command, error);
+                return NULL;
+        if ((*command == '\\') || (*command == '.')) {
+                if (data->current)
+                        return gda_internal_command_execute (data->internal_commands,
+                                                             data->current->cnc, command, error);
+                else
+                        return gda_internal_command_execute (data->internal_commands, NULL, command, error);
+        }
+	else if (*command == '#') {
+		/* nothing to do */
+		GdaInternalCommandResult *res;
+		res = g_new0 (GdaInternalCommandResult, 1);
+		res->type = GDA_INTERNAL_COMMAND_RESULT_EMPTY;
+		return res;
 	}
+        else {
+                if (!data->current) {
+                        g_set_error (error, 0, 0,
+                                     _("No connection specified"));
+                        return NULL;
+                }
+                if (!gda_connection_is_opened (data->current->cnc)) {
+                        g_set_error (error, 0, 0,
+                                     _("Connection closed"));
+                        return NULL;
+                }
+
+                return execute_external_command (data, command, error);
+        }
 }
 
 /*
@@ -677,26 +693,44 @@
 			GdaHolder *h = GDA_HOLDER (list->data);
 			GdaHolder *h_in_data = g_hash_table_lookup (data->parameters, gda_holder_get_id (h));
 			if (h_in_data) {
-				gchar *str;
 				const GValue *cvalue;
 				GValue *value;
-				GdaDataHandler *dh;
-				GdaServerProvider *prov;
 
-				prov = gda_connection_get_provider (data->current->cnc);
 				cvalue = gda_holder_get_value (h_in_data);
-				dh = gda_server_provider_get_data_handler_g_type (prov, data->current->cnc,
-										 gda_holder_get_g_type (h_in_data));
-				str = gda_data_handler_get_str_from_value (dh, cvalue);
-
-				dh = gda_server_provider_get_data_handler_g_type (prov, data->current->cnc,
-										 gda_holder_get_g_type (h));
-				value = gda_data_handler_get_value_from_str (dh, str, gda_holder_get_g_type (h));
-				g_free (str);
-				if (! gda_holder_take_value (h, value, error)) {
-					g_free (res);
-					res = NULL;
-					goto cleanup;
+				if (cvalue && (G_VALUE_TYPE (cvalue) == gda_holder_get_g_type (h))) {
+					if (!gda_holder_set_value (h, cvalue, error)) {
+						g_free (res);
+						res = NULL;
+						goto cleanup;
+					}
+				}
+				else if (cvalue && (G_VALUE_TYPE (cvalue) == GDA_TYPE_NULL)) {
+					if (!gda_holder_set_value (h, cvalue, error)) {
+						g_free (res);
+						res = NULL;
+						goto cleanup;
+					}
+				}
+				else {
+					gchar *str;
+					str = gda_value_stringify (cvalue);
+					value = gda_value_new_from_string (str, gda_holder_get_g_type (h));
+					g_free (str);
+					if (! value) {
+						g_set_error (error, 0, 0,
+							     _("Could not interpret the '%s' parameter's value"), 
+							     gda_holder_get_id (h));
+						g_free (res);
+						res = NULL;
+						goto cleanup;
+					}
+					else if (! gda_holder_take_value (h, value, error)) {
+						gda_value_free (value);
+						g_free (res);
+						res = NULL;
+						goto cleanup;
+					}
+					g_free (str);
 				}
 			}
 			else {
@@ -1021,6 +1055,8 @@
 			cs->name = g_strdup_printf ("c%d", cncindex);
 		cncindex++;
 		cs->parser = gda_connection_create_parser (newcnc);
+		if (!cs->parser)
+			cs->parser = gda_sql_parser_new ();
 		cs->cnc = newcnc;
 		cs->query_buffer = NULL;
 		cs->threader = NULL;
@@ -1409,6 +1445,13 @@
 static GdaInternalCommandResult *extra_command_graph (GdaConnection *cnc, const gchar **args,
 						      GError **error, MainData *data);
 
+static GdaInternalCommandResult *extra_command_lo_update (GdaConnection *cnc, const gchar **args,
+							  GError **error, MainData *data);
+static GdaInternalCommandResult *extra_command_export (GdaConnection *cnc, const gchar **args,
+						       GError **error, MainData *data);
+static GdaInternalCommandResult *extra_command_set2 (GdaConnection *cnc, const gchar **args,
+						     GError **error, MainData *data);
+
 static GdaInternalCommandsList *
 build_internal_commands_list (MainData *data)
 {
@@ -1759,8 +1802,8 @@
 
 	c = g_new0 (GdaInternalCommand, 1);
 	c->group = _("Query buffer");
-	c->name = g_strdup_printf (_("%s NAME"), "unset");
-	c->description = _("Unset (delete) internal parameter");
+	c->name = g_strdup_printf (_("%s [NAME]"), "unset");
+	c->description = _("Unset (delete) internal named parameter (or all parameters)");
 	c->args = NULL;
 	c->command_func = (GdaInternalCommandFunc) extra_command_unset;
 	c->user_data = data;
@@ -1779,6 +1822,41 @@
 	c->unquote_args = TRUE;
 	commands->commands = g_slist_prepend (commands->commands, c);
 
+	/*
+	c = g_new0 (GdaInternalCommand, 1);
+	c->group = _("Query buffer");
+	c->name = g_strdup_printf (_("%s FILE TABLE BLOB_COLUMN ROW_CONDITION"), "lo_update");
+	c->description = _("Import a blob into the database");
+	c->args = NULL;
+	c->command_func = (GdaInternalCommandFunc) extra_command_lo_update;
+	c->user_data = data;
+	c->arguments_delimiter_func = NULL;
+	c->unquote_args = TRUE;
+	commands->commands = g_slist_prepend (commands->commands, c);
+	*/
+
+	c = g_new0 (GdaInternalCommand, 1);
+	c->group = _("Query buffer");
+	c->name = g_strdup_printf (_("%s [NAME|TABLE COLUMN ROW_CONDITION] FILE"), "export");
+	c->description = _("Export internal parameter or table's value to the FILE file");
+	c->args = NULL;
+	c->command_func = (GdaInternalCommandFunc) extra_command_export;
+	c->user_data = data;
+	c->arguments_delimiter_func = NULL;
+	c->unquote_args = TRUE;
+	commands->commands = g_slist_prepend (commands->commands, c);
+
+	c = g_new0 (GdaInternalCommand, 1);
+	c->group = _("Query buffer");
+	c->name = g_strdup_printf (_("%s NAME [FILE|TABLE COLUMN ROW_CONDITION]"), "setex");
+	c->description = _("Set internal parameter as the contents of the FILE file or from an existing table's value");
+	c->args = NULL;
+	c->command_func = (GdaInternalCommandFunc) extra_command_set2;
+	c->user_data = data;
+	c->arguments_delimiter_func = NULL;
+	c->unquote_args = TRUE;
+	commands->commands = g_slist_prepend (commands->commands, c);
+
 	/* comes last */
 	c = g_new0 (GdaInternalCommand, 1);
 	c->group = _("General");
@@ -2285,6 +2363,8 @@
 					ncs->cnc = gda_meta_store_get_internal_connection (store);
 					g_object_ref (ncs->cnc);
 					ncs->parser = gda_connection_create_parser (ncs->cnc);
+					if (!cs->parser)
+						cs->parser = gda_sql_parser_new ();
 					ncs->query_buffer = NULL;
 					ncs->threader = NULL;
 					ncs->meta_job_id = 0;
@@ -3155,6 +3235,264 @@
 	gda_value_free (value);
 }
 
+static const GValue *
+get_table_value_at_cell (GdaConnection *cnc, GError **error, MainData *data,
+			 const gchar *table, const gchar *column, const gchar *row_cond,
+			 GdaDataModel **out_model_of_value)
+{
+	const GValue *retval = NULL;
+
+	*out_model_of_value = NULL;
+
+	/* prepare executed statement */
+	gchar *sql;
+	gchar *rtable, *rcolumn;
+	
+	if (gda_sql_identifier_needs_quotes (table))
+		rtable = gda_sql_identifier_add_quotes (table);
+	else
+		rtable = g_strdup (table);
+	if (gda_sql_identifier_needs_quotes (column))
+		rcolumn = gda_sql_identifier_add_quotes (column);
+	else
+		rcolumn = g_strdup (column);
+	sql = g_strdup_printf ("SELECT %s FROM %s WHERE %s", rcolumn, rtable, row_cond);
+	g_free (rtable);
+	g_free (rcolumn);
+
+	GdaStatement *stmt;
+	const gchar *remain;
+	stmt = gda_sql_parser_parse_string (data->current->parser, sql, &remain, error);
+	if (!stmt) {
+		g_free (sql);
+		return NULL;
+	}
+	if (remain) {
+		g_set_error (error, 0, 0,
+			     _("Wrong row condition"));
+		g_free (sql);
+		return NULL;
+	}
+	g_object_unref (stmt);
+
+	/* execute statement */
+	GdaInternalCommandResult *tmpres;
+	tmpres = execute_external_command (data, sql, error);
+	g_free (sql);
+	if (!tmpres)
+		return NULL;
+	gboolean errorset = FALSE;
+	if (tmpres->type == GDA_INTERNAL_COMMAND_RESULT_DATA_MODEL) {
+		GdaDataModel *model;
+		model = tmpres->u.model;
+		if (gda_data_model_get_n_rows (model) == 1) {
+			retval = gda_data_model_get_value_at (model, 0, 0, error);
+			if (!retval)
+				errorset = TRUE;
+			else
+				*out_model_of_value = g_object_ref (model);
+		}
+	}
+	gda_internal_command_exec_result_free (tmpres);
+
+	if (!retval && !errorset)
+		g_set_error (error, 0, 0,
+			     _("No unique row identified"));
+
+	return retval;
+}
+
+static GdaInternalCommandResult *
+extra_command_set2 (GdaConnection *cnc, const gchar **args,
+		    GError **error, MainData *data)
+{
+	GdaInternalCommandResult *res = NULL;
+	const gchar *pname = NULL;
+	const gchar *filename = NULL;
+	const gchar *table = NULL;
+        const gchar *column = NULL;
+        const gchar *row_cond = NULL;
+	gint whichargs = 0;
+
+	if (!cnc) {
+                g_set_error (error, 0, 0, _("No current connection"));
+                return NULL;
+        }
+
+        if (args[0] && *args[0]) {
+                pname = args[0];
+                if (args[1] && *args[1]) {
+			if (args[2] && *args[2]) {
+				table = args[1];
+				column = args[2];
+				if (args[3] && *args[3]) {
+					row_cond = args[3];
+					if (args [4]) {
+						g_set_error (error, 0, 0,
+							     _("Too many arguments"));
+						return NULL;
+					}
+					whichargs = 1;
+				}
+			}
+			else {
+				filename = args[1];
+				whichargs = 2;
+			}
+		}
+        }
+
+	if (whichargs == 1) {
+		/* param from an existing blob */
+		const GValue *value;
+		GdaDataModel *model = NULL;
+		value = get_table_value_at_cell (cnc, error, data, table, column, row_cond, &model);
+		if (value) {
+			GdaHolder *param = g_hash_table_lookup (data->parameters, pname);
+			if (param) 
+				g_hash_table_remove (data->parameters, pname);
+		
+			param = gda_holder_new (G_VALUE_TYPE (value));
+			g_assert (gda_holder_set_value (param, value, NULL));
+			g_hash_table_insert (data->parameters, g_strdup (pname), param);
+			res = g_new0 (GdaInternalCommandResult, 1);
+			res->type = GDA_INTERNAL_COMMAND_RESULT_EMPTY;
+		}
+		if (model)
+			g_object_unref (model);
+	}
+	else if (whichargs == 2) {
+		/* param from filename */
+		GdaHolder *param = g_hash_table_lookup (data->parameters, pname);
+		GValue *bvalue;
+		if (param) 
+			g_hash_table_remove (data->parameters, pname);
+		
+		param = gda_holder_new (GDA_TYPE_BLOB);
+		bvalue = gda_value_new_blob_from_file (filename);
+		g_assert (gda_holder_take_value (param, bvalue, NULL));
+		g_hash_table_insert (data->parameters, g_strdup (pname), param);
+		res = g_new0 (GdaInternalCommandResult, 1);
+		res->type = GDA_INTERNAL_COMMAND_RESULT_EMPTY;
+	}
+	else 
+		g_set_error (error, 0, 0,
+                             _("Wrong number of arguments"));
+
+	return res;
+}
+
+static GdaInternalCommandResult *
+extra_command_export (GdaConnection *cnc, const gchar **args,
+		      GError **error, MainData *data)
+{
+	GdaInternalCommandResult *res = NULL;
+
+	const gchar *pname = NULL;
+	const gchar *table = NULL;
+        const gchar *column = NULL;
+        const gchar *filename = NULL;
+        const gchar *row_cond = NULL;
+	gint whichargs = 0;
+
+	if (!cnc) {
+                g_set_error (error, 0, 0, _("No current connection"));
+                return NULL;
+        }
+
+        if (args[0] && *args[0]) {
+                table = args[0];
+                pname = args[0];
+                if (args[1] && *args[1]) {
+                        column = args[1];
+			filename = args[1];
+			if (args[2] && *args[2]) {
+				row_cond = args[2];
+				if (args[3] && *args[3]) {
+					filename = args[3];
+					if (args [4]) {
+						g_set_error (error, 0, 0,
+							     _("Too many arguments"));
+						return NULL;
+					}
+					else
+						whichargs = 1;
+				}
+			}
+			else {
+				whichargs = 2;
+			}
+		}
+        }
+
+	const GValue *value = NULL;
+	GdaDataModel *model = NULL;
+
+	if (whichargs == 1) 
+		value = get_table_value_at_cell (cnc, error, data, table, column, row_cond, &model);
+	else if (whichargs == 2) {
+		GdaHolder *param = g_hash_table_lookup (data->parameters, pname);
+		if (!pname) 
+			g_set_error (error, 0, 0,
+				     _("No parameter named '%s' defined"), pname);
+		else
+			value = gda_holder_get_value (param);
+	}
+	else 
+		g_set_error (error, 0, 0,
+                             _("Wrong number of arguments"));
+
+	if (value) {
+		/* to file through this blob */
+		gboolean done = FALSE;
+
+		if (G_VALUE_TYPE (value) == GDA_TYPE_BLOB) {
+			GValue *vblob = gda_value_new_blob_from_file (filename);
+			GdaBlob *tblob = (GdaBlob*) gda_value_get_blob (vblob);
+			const GdaBlob *fblob = gda_value_get_blob (value);
+			if (gda_blob_op_write (tblob->op, (GdaBlob*) fblob, 0) < 0)
+				g_set_error (error, 0, 0,
+					     _("Could not write file"));
+			else
+				done = TRUE;
+			gda_value_free (vblob);
+		}
+		else if (G_VALUE_TYPE (value) == GDA_TYPE_BINARY) {
+			GValue *vblob = gda_value_new_blob_from_file (filename);
+			GdaBlob *tblob = (GdaBlob*) gda_value_get_blob (vblob);
+			GdaBlob *fblob = g_new0 (GdaBlob, 1);
+			const GdaBinary *fbin = gda_value_get_binary (value);
+			((GdaBinary *) fblob)->data = fbin->data;
+			((GdaBinary *) fblob)->binary_length = fbin->binary_length;
+			if (gda_blob_op_write (tblob->op, (GdaBlob*) fblob, 0) < 0)
+				g_set_error (error, 0, 0,
+					     _("Could not write file"));
+			else
+				done = TRUE;
+			g_free (fblob);
+			gda_value_free (vblob);
+		}
+		else {
+			gchar *str;
+			str = gda_value_stringify (value);
+			if (g_file_set_contents (filename, str, -1, error))
+				done = TRUE;
+			g_free (str);
+		}
+		
+		if (done) {
+			res = g_new0 (GdaInternalCommandResult, 1);
+			res->type = GDA_INTERNAL_COMMAND_RESULT_EMPTY;
+		}
+	}
+	if (model)
+		g_object_unref (model);
+
+				
+	return res;
+}
+
+
 static GdaInternalCommandResult *
 extra_command_unset (GdaConnection *cnc, const gchar **args,
 		     GError **error, MainData *data)
@@ -3176,9 +3514,12 @@
 			g_set_error (error, 0, 0,
 				     _("No parameter named '%s' defined"), pname);
 	}
-	else 
-		g_set_error (error, 0, 0,
-			     _("Missing parameter's name"));
+	else {
+		g_hash_table_destroy (data->parameters);
+		data->parameters = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
+		res = g_new0 (GdaInternalCommandResult, 1);
+		res->type = GDA_INTERNAL_COMMAND_RESULT_EMPTY;
+	}
 		
 	return res;
 }
@@ -3293,6 +3634,114 @@
 		return NULL;
 }
 
+#ifdef NONE
+static GdaInternalCommandResult *
+extra_command_lo_update (GdaConnection *cnc, const gchar **args,
+			 GError **error, MainData *data)
+{
+	GdaInternalCommandResult *res;
+
+	const gchar *table = NULL;
+        const gchar *blob_col = NULL;
+        const gchar *filename = NULL;
+        const gchar *row_cond = NULL;
+
+	if (!cnc) {
+                g_set_error (error, 0, 0, _("No current connection"));
+                return NULL;
+        }
+
+        if (args[0] && *args[0]) {
+                filename = args[0];
+                if (args[1] && *args[1]) {
+                        table = args[1];
+			if (args[2] && *args[2]) {
+				blob_col = args[2];
+				if (args[3] && *args[3]) {
+					row_cond = args[3];
+					if (args [4]) {
+						g_set_error (error, 0, 0,
+							     _("Too many arguments"));
+						return NULL;
+					}
+				}
+			}
+		}
+        }
+	if (!row_cond) {
+		g_set_error (error, 0, 0,
+                             _("Missing arguments"));
+		return NULL;
+	}
+
+	g_print ("file: #%s#\n", filename);
+	g_print ("table: #%s#\n", table);
+	g_print ("col: #%s#\n", blob_col);
+	g_print ("cond: #%s#\n", row_cond);
+	TO_IMPLEMENT;
+
+	/* prepare executed statement */
+	gchar *sql;
+	gchar *rtable, *rblob_col;
+	
+	if (gda_sql_identifier_needs_quotes (table))
+		rtable = gda_sql_identifier_add_quotes (table);
+	else
+		rtable = g_strdup (table);
+	if (gda_sql_identifier_needs_quotes (blob_col))
+		rblob_col = gda_sql_identifier_add_quotes (blob_col);
+	else
+		rblob_col = g_strdup (blob_col);
+	sql = g_strdup_printf ("UPDATE %s SET %s = ##blob::GdaBlob WHERE %s", rtable, rblob_col, row_cond);
+	g_free (rtable);
+	g_free (rblob_col);
+
+	GdaStatement *stmt;
+	const gchar *remain;
+	stmt = gda_sql_parser_parse_string (data->current->parser, sql, &remain, error);
+	g_free (sql);
+	if (!stmt)
+		return NULL;
+	if (remain) {
+		g_set_error (error, 0, 0,
+			     _("Wrong row condition"));
+		return NULL;
+	}
+
+	/* prepare execution environment */
+	GdaSet *params;
+	GdaHolder *h;
+	GValue *blob;
+
+	if (!gda_statement_get_parameters (stmt, &params, error)) {
+		g_object_unref (stmt);
+		return NULL;
+	}
+
+	h = gda_set_get_holder (params, "blob");
+	g_assert (h);
+	blob = gda_value_new_blob_from_file (filename);
+	if (!gda_holder_take_value (h, blob, error)) {
+		gda_value_free (blob);
+		g_object_unref (params);
+		g_object_unref (stmt);
+		return NULL;
+	}
+
+	/* execute statement */
+	gint nrows;
+	nrows = gda_connection_statement_execute_non_select (cnc, stmt, params, NULL, error);
+	g_object_unref (params);
+	g_object_unref (stmt);
+	if (nrows == -1)
+		return NULL;
+
+	res = g_new0 (GdaInternalCommandResult, 1);
+	res->type = GDA_INTERNAL_COMMAND_RESULT_EMPTY;
+	return res;
+}
+#endif
+
 
 static gchar **
 args_as_string_func (const gchar *str)



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]