Exemple #1
0
/**
 * gda_xa_transaction_begin:
 * @xa_trans: a #GdaXaTransaction object
 * @error: (nullable): a place to store errors, or %NULL
 *
 * Begins a distributed transaction (managed by @xa_trans). Please note that this phase may fail
 * for some connections if a (normal) transaction is already started (this depends on the database
 * provider being used), so it's better to avoid starting any (normal) transaction on any of the
 * connections registered with @xa_trans.
 *
 * Returns: TRUE if no error occurred
 */
gboolean
gda_xa_transaction_begin  (GdaXaTransaction *xa_trans, GError **error)
{
	GList *list;
	g_return_val_if_fail (GDA_IS_XA_TRANSACTION (xa_trans), FALSE);
	GdaXaTransactionPrivate *priv = gda_xa_transaction_get_instance_private (xa_trans);
	
	for (list = priv->cnc_list; list; list = list->next) {
		GdaConnection *cnc;
		GdaServerProvider *prov;
		
		cnc = GDA_CONNECTION (list->data);
		prov = gda_connection_get_provider (cnc);
		if (cnc != priv->non_xa_cnc) {
			GdaBinary *branch;
			branch = g_hash_table_lookup (priv->cnc_hash, cnc);
			memcpy (priv->xid.data + priv->xid.gtrid_length, /* Flawfinder: ignore */
				gda_binary_get_data (branch), gda_binary_get_size (branch));
			if (! _gda_server_provider_xa_start (prov, cnc, &(priv->xid), error))
				break;
		}
		else {
			/* do a simple BEGIN */
			if (! gda_connection_begin_transaction (cnc, NULL, GDA_TRANSACTION_ISOLATION_UNKNOWN,
								error))
				break;
		}
	}

	if (list) {
		/* something went wrong */
		for (; list; list = list->prev) {
			GdaConnection *cnc;
			GdaServerProvider *prov;
			
			cnc = GDA_CONNECTION (list->data);
			prov = gda_connection_get_provider (cnc);
			if (cnc != priv->non_xa_cnc) {
				GdaBinary *branch;
				branch = g_hash_table_lookup (priv->cnc_hash, cnc);
				memcpy (priv->xid.data + priv->xid.gtrid_length, /* Flawfinder: ignore */
					gda_binary_get_data (branch), gda_binary_get_size (branch));
				_gda_server_provider_xa_rollback (prov, cnc, &(priv->xid), NULL);
			}
			else {
				/* do a simple ROLLBACK */
				gda_connection_rollback_transaction (cnc, NULL, NULL);
			}
		}
		return FALSE;
	}
	return TRUE;
}
Exemple #2
0
int
main (int argc, char** argv)
{
	gchar *file;
	GdaBinary* bin;
	gchar *bin_data;
	gsize bin_length;
	GError *error = NULL;

	/* load binary data */
	file = g_build_filename (CHECK_FILES, "data", "sales_test.db", NULL);
	if (! g_file_get_contents (file, &bin_data, &bin_length, &error)) {
		g_print ("Error reading binary file: %s\n", error->message);
		return EXIT_FAILURE;
	}		
	g_free (file);
	bin = gda_binary_new ();
	gda_binary_set_data (bin, (guchar*) bin_data, bin_length);

	/* convert to string */
	gchar *conv1;
	conv1 = gda_binary_to_string (bin, 0);
	
	/* convert back to binary */
	GdaBinary *bin2;
	bin2 = gda_string_to_binary (conv1);
	
	/* compare bin */
	if (gda_binary_get_size (bin) != gda_binary_get_size (bin2)) {
		g_print ("Error: binary length differ: from %ld to %ld\n",
			 gda_binary_get_size (bin), gda_binary_get_size (bin2));
		return EXIT_FAILURE;
	}
	gint i;
	for (i = 0; i < gda_binary_get_size (bin); i++) {
	  guchar *buffer = gda_binary_get_data (bin);
	  guchar *buffer2 = gda_binary_get_data (bin2);
		if (buffer[i] != buffer2[i]) {
			g_print ("Error: binary differ orig[%d]=%d and copy[%d]=%d\n",
				 i, buffer[i], i, buffer2[i]);
			return EXIT_FAILURE;
		}
	}
	
	g_free (conv1);
	gda_binary_free (bin);
	gda_binary_free (bin2);

	g_print ("Ok (file size: %d)\n", bin_length);
	return EXIT_SUCCESS;
}
Exemple #3
0
/**
 * gda_xa_transaction_rollback:
 * @xa_trans: a #GdaXaTransaction object
 * @error: (nullable): a place to store errors, or %NULL
 *
 * Cancels a distributed transaction (managed by @xa_trans). 
 *
 * Returns: %TRUE if no error occurred
 */
gboolean
gda_xa_transaction_rollback (GdaXaTransaction *xa_trans, GError **error)
{
	GList *list;
	g_return_val_if_fail (GDA_IS_XA_TRANSACTION (xa_trans), FALSE);
	GdaXaTransactionPrivate *priv = gda_xa_transaction_get_instance_private (xa_trans);
	
	for (list = priv->cnc_list; list; list = list->next) {
		GdaConnection *cnc;
		GdaServerProvider *prov;

		cnc = GDA_CONNECTION (list->data);
		prov = gda_connection_get_provider (cnc);
		
		if (cnc == priv->non_xa_cnc)
			gda_connection_rollback_transaction (cnc, NULL, NULL);
		else {
			GdaBinary *branch;
			branch = g_hash_table_lookup (priv->cnc_hash, cnc);
			memcpy (priv->xid.data + priv->xid.gtrid_length, /* Flawfinder: ignore */
				gda_binary_get_data (branch), gda_binary_get_size (branch));
			GError *lerror = NULL;
			_gda_server_provider_xa_rollback (prov, cnc, &(priv->xid), &lerror);
			if (error && !*error)
				g_propagate_error (error, lerror);
			else
				g_clear_error (&lerror);
		}
	}
	return TRUE;
}
Exemple #4
0
/*
 * Blob read request
 */
static glong
gda_sqlite_blob_op_read (GdaBlobOp *op, GdaBlob *blob, glong offset, glong size)
{
	GdaSqliteBlobOp *bop;
	GdaBinary *bin;
	gpointer buffer = NULL;
	int rc;

	g_return_val_if_fail (GDA_IS_SQLITE_BLOB_OP (op), -1);
	bop = GDA_SQLITE_BLOB_OP (op);
  GdaSqliteBlobOpPrivate *priv = gda_sqlite_blob_op_get_instance_private (bop);
	g_return_val_if_fail (priv->sblob, -1);
	if (offset >= G_MAXINT)
		return -1;
	g_return_val_if_fail (blob, -1);

	if (offset > G_MAXINT)
		return -1;
	if (size > G_MAXINT)
		return -1;

	GdaSqliteProvider *prov = g_weak_ref_get (&priv->provider);
	g_return_val_if_fail (prov != NULL, -1);

	bin = gda_blob_get_binary (blob);
	gda_binary_set_data (bin, (guchar*) "", 0);

	/* fetch blob data using C API into bin->data, and set bin->binary_length */
	int rsize;
	int len;

	len = SQLITE3_CALL (prov, sqlite3_blob_bytes) (priv->sblob);
	if (len < 0){
		g_object_unref (prov);
		return -1;
	} else if (len == 0) {
		g_object_unref (prov);
		return 0;
	}
		
	rsize = (int) size;
	if (offset >= len) {
		g_object_unref (prov);
		return -1;
	}

	if (len - offset < rsize)
		rsize = len - offset;
  
	rc = SQLITE3_CALL (prov, sqlite3_blob_read) (priv->sblob, buffer, rsize, offset);
	if (rc != SQLITE_OK) {
		gda_binary_reset_data (bin);
		g_object_unref (prov);
		return -1;
	}
	gda_binary_set_data (bin, buffer, rsize);
	g_object_unref (prov);

	return gda_binary_get_size (bin);
}
Exemple #5
0
static glong
gda_dir_blob_op_write (GdaBlobOp *op, GdaBlob *blob, glong offset)
{
	GdaDirBlobOp *dirop;
	GdaBinary *bin;
	FILE *file;
	glong nbwritten;

	g_return_val_if_fail (GDA_IS_DIR_BLOB_OP (op), -1);
	dirop = GDA_DIR_BLOB_OP (op);
	GdaDirBlobOpPrivate *priv = gda_dir_blob_op_get_instance_private (dirop);
	if (offset >= G_MAXINT)
		return -1;
	g_return_val_if_fail (blob, -1);

	/* open file */
	file = fopen (priv->complete_filename, "w+b"); /* Flawfinder: ignore */
	if (!file)
		return -1;
	
	/* go to offset */
	if (offset > 0) {
		if (fseek (file, offset, SEEK_SET) != 0) {
			fclose (file);
			return -1;
		}
	}

	if (gda_blob_get_op (blob) && (gda_blob_get_op (blob) != op)) {
		/* use data through blob->op */
#define buf_size 16384
		gint nread = 0;
		GdaBlob *tmpblob = gda_blob_new ();
		gda_blob_set_op (tmpblob, gda_blob_get_op (blob));

		nbwritten = 0;

		for (nread = gda_blob_op_read (gda_blob_get_op (tmpblob), tmpblob, 0, buf_size);
		     nread > 0;
		     nread = gda_blob_op_read (gda_blob_get_op (tmpblob), tmpblob, nbwritten, buf_size)) {
			GdaBinary *bin = gda_blob_get_binary (tmpblob);
			glong tmp_written;
			tmp_written = fwrite ((char *) gda_binary_get_data (bin), sizeof (guchar), gda_binary_get_size (bin), file);
			if (tmp_written < gda_binary_get_size (bin)) {
				/* error writing stream */
				fclose (file);
				gda_blob_free (tmpblob);
				return -1;
			}
			nbwritten += tmp_written;
			if (nread < buf_size)
				/* nothing more to read */
				break;
		}

		fclose (file);
		gda_blob_free (tmpblob);
	}
	else {
		bin = (GdaBinary *) blob;
		nbwritten = fwrite ((char *) (gda_binary_get_data (bin)), 1, gda_binary_get_size (bin), file);
		fclose (file);
	}

	return (nbwritten >= 0) ? nbwritten : -1;
}
Exemple #6
0
/**
 * gda_xa_transaction_commit_recovered:
 * @xa_trans: a #GdaXaTransaction object
 * @cnc_to_recover: (nullable) (element-type Gda.Connection) (out callee-allocates): a place to store the list of connections for which the there were data to recover and which failed to be actually committed, or %NULL
 * @error: (nullable): a place to store errors, or %NULL
 *
 * Tries to commit the data prepared but which failed to commit (see gda_xa_transaction_commit()). This
 * method allows one to terminate a distributed transaction which succeeded but for which some
 * connections needed to be recovered.
 *
 * Returns: %TRUE if all the data which was still uncommitted has been committed
 */
gboolean
gda_xa_transaction_commit_recovered (GdaXaTransaction *xa_trans, GSList **cnc_to_recover, GError **error)
{
	GList *list;
	gboolean retval = TRUE;
	
	if (cnc_to_recover)
		*cnc_to_recover = NULL;
	g_return_val_if_fail (GDA_IS_XA_TRANSACTION (xa_trans), FALSE);
	GdaXaTransactionPrivate *priv = gda_xa_transaction_get_instance_private (xa_trans);
	
	for (list = priv->cnc_list; list; list = list->next) {
		GdaConnection *cnc;
		GdaServerProvider *prov;

		cnc = GDA_CONNECTION (list->data);
		prov = gda_connection_get_provider (cnc);
		
		if (cnc == priv->non_xa_cnc)
			continue;
		else {
			GList *recov_xid_list;
			
			GdaBinary *branch;
			GList *xlist;
			gboolean commit_needed = FALSE;
				
			recov_xid_list = _gda_server_provider_xa_recover (prov, cnc, error);
			if (!recov_xid_list)
				continue;

			branch = g_hash_table_lookup (priv->cnc_hash, cnc);
			memcpy (priv->xid.data + priv->xid.gtrid_length, /* Flawfinder: ignore */
				gda_binary_get_data (branch), gda_binary_get_size (branch));
			for (xlist = recov_xid_list; xlist; xlist = xlist->next) {
				GdaXaTransactionId *xid = (GdaXaTransactionId*) xlist->data;
				if (!xid)
					/* ignore unknown XID format */
					continue;

				if (!commit_needed &&
				    (xid->format == priv->xid.format) &&
				    (xid->gtrid_length == priv->xid.gtrid_length) &&
				    (xid->bqual_length == priv->xid.bqual_length) &&
				    ! memcmp (priv->xid.data, xid->data, xid->bqual_length + xid->bqual_length))
					/* found a transaction to commit */
					commit_needed = TRUE;
				
				g_free (xid);
			}
			g_list_free (recov_xid_list);

			if (commit_needed) {
				retval = _gda_server_provider_xa_commit (prov, cnc, &(priv->xid), error);
				if (!retval)
					if (cnc_to_recover)
						*cnc_to_recover = g_slist_prepend (*cnc_to_recover, cnc);
			}
		}
	}
	return retval;	
}
Exemple #7
0
/**
 * gda_xa_transaction_commit:
 * @xa_trans: a #GdaXaTransaction object
 * @cnc_to_recover: (nullable) (element-type Gda.Connection) (out callee-allocates): a place to store the list of connections for which the commit phase failed, or %NULL
 * @error: a place to store errors, or %NULL
 *
 * Commits a distributed transaction (managed by @xa_trans). The commit is composed of two phases:
 * <itemizedlist>
 *   <listitem><para>a PREPARE phase where all the connections are required to store their transaction data to a 
 *     permanent place (to be able to complete the commit should a problem occur afterwards)</para></listitem>
 *   <listitem><para>a COMMIT phase where the transaction data is actually written to the database</para></listitem>
 * </itemizedlist>
 *
 * If the PREPARE phase fails for any of the connection registered with @xa_trans, then the distributed commit
 * fails and FALSE is returned. During the COMMIT phase, some commit may actually fail but the transaction can
 * still be completed because the PREPARE phase succeeded (through the recover method).
 *
 * Returns: TRUE if no error occurred (there may be some connections to recover, though)
 */
gboolean
gda_xa_transaction_commit (GdaXaTransaction *xa_trans, GSList **cnc_to_recover, GError **error)
{
	GList *list;
	if (cnc_to_recover)
		*cnc_to_recover = NULL;

	g_return_val_if_fail (GDA_IS_XA_TRANSACTION (xa_trans), FALSE);
	GdaXaTransactionPrivate *priv = gda_xa_transaction_get_instance_private (xa_trans);
	
	/* 
	 * PREPARE phase 
	 */
	for (list = priv->cnc_list; list; list = list->next) {
		GdaConnection *cnc = NULL;
		GdaServerProvider *prov;
		GdaBinary *branch;
		
		if (cnc == priv->non_xa_cnc)
			continue;

		cnc = GDA_CONNECTION (list->data);
		prov = gda_connection_get_provider (cnc);

		branch = g_hash_table_lookup (priv->cnc_hash, cnc);
		memcpy (priv->xid.data + priv->xid.gtrid_length, /* Flawfinder: ignore */
			gda_binary_get_data (branch), gda_binary_get_size (branch));

		if (!_gda_server_provider_xa_end (prov, cnc, &(priv->xid), error))
			break;

		if (!_gda_server_provider_xa_prepare (prov, cnc, &(priv->xid), error))
			break;
	}
	if (list) {
		/* something went wrong during the PREPARE phase => rollback everything */
		for (; list; list = list->prev) {
			GdaConnection *cnc = NULL;
			GdaServerProvider *prov;
			
			if (cnc == priv->non_xa_cnc)
				gda_connection_rollback_transaction (cnc, NULL, NULL);
			else {
				GdaBinary *branch;
				cnc = GDA_CONNECTION (list->data);
				prov = gda_connection_get_provider (cnc);
				branch = g_hash_table_lookup (priv->cnc_hash, cnc);
				memcpy (priv->xid.data + priv->xid.gtrid_length, /* Flawfinder: ignore */
					gda_binary_get_data (branch), gda_binary_get_size (branch));
				_gda_server_provider_xa_rollback (prov, cnc, &(priv->xid), NULL);
			}
		}
		return FALSE;
	}

	/*
	 * COMMIT phase 
	 */
	if (priv->non_xa_cnc &&
	    ! gda_connection_commit_transaction (priv->non_xa_cnc, NULL, error)) {
		/* something went wrong => rollback everything */
		for (list = priv->cnc_list; list; list = list->next) {
			GdaConnection *cnc = NULL;
			GdaServerProvider *prov;
			
			if (cnc == priv->non_xa_cnc)
				gda_connection_rollback_transaction (cnc, NULL, NULL);
			else {
				GdaBinary *branch;
				cnc = GDA_CONNECTION (list->data);
				prov = gda_connection_get_provider (cnc);
				branch = g_hash_table_lookup (priv->cnc_hash, cnc);
				memcpy (priv->xid.data + priv->xid.gtrid_length, /* Flawfinder: ignore */
					gda_binary_get_data (branch), gda_binary_get_size (branch));
				_gda_server_provider_xa_rollback (prov, cnc, &(priv->xid), NULL);
			}
		}
		return FALSE;
	}

	for (list = priv->cnc_list; list; list = list->next) {
		GdaConnection *cnc = NULL;
		GdaServerProvider *prov;
		GdaBinary *branch;
		
		if (cnc == priv->non_xa_cnc)
			continue;

		cnc = GDA_CONNECTION (list->data);
		prov = gda_connection_get_provider (cnc);
		branch = g_hash_table_lookup (priv->cnc_hash, cnc);
		memcpy (priv->xid.data + priv->xid.gtrid_length, /* Flawfinder: ignore */
			gda_binary_get_data (branch), gda_binary_get_size (branch));
		if (! _gda_server_provider_xa_commit (prov, cnc, &(priv->xid), error) &&
		    cnc_to_recover)
			*cnc_to_recover = g_slist_prepend (*cnc_to_recover, cnc);
	}
	
	return TRUE;
}
Exemple #8
0
/*
 * Blob write request
 */
static glong
gda_sqlite_blob_op_write (GdaBlobOp *op, GdaBlob *blob, glong offset)
{
	GdaSqliteBlobOp *bop;
	GdaBinary *bin;
	glong nbwritten = -1;
	int len;

	g_return_val_if_fail (GDA_IS_SQLITE_BLOB_OP (op), -1);
	bop = GDA_SQLITE_BLOB_OP (op);
  GdaSqliteBlobOpPrivate *priv = gda_sqlite_blob_op_get_instance_private (bop);
	g_return_val_if_fail (priv->sblob, -1);
	g_return_val_if_fail (blob, -1);

	GdaSqliteProvider *prov = g_weak_ref_get (&priv->provider);
	g_return_val_if_fail (prov != NULL, -1);

	len = SQLITE3_CALL (prov, sqlite3_blob_bytes) (priv->sblob);
	if (len < 0) {
		g_object_unref (prov);
		return -1;
	}

	if (gda_blob_get_op (blob) && (gda_blob_get_op (blob) != op)) {
		/* use data through blob->op */
		#define buf_size 16384
		gint nread = 0;
		GdaBlob *tmpblob = gda_blob_new ();
		gda_blob_set_op (tmpblob, gda_blob_get_op (blob));

		nbwritten = 0;

		for (nread = gda_blob_op_read (gda_blob_get_op (tmpblob), tmpblob, nbwritten, buf_size);
		     nread > 0;
		     nread = gda_blob_op_read (gda_blob_get_op (tmpblob), tmpblob, nbwritten, buf_size)) {
			int tmp_written;
			int rc;
			int wlen;
			
			if (nread + offset + nbwritten > len)
				wlen = 	len - offset - nbwritten;
			else
				wlen = nread;

			rc = SQLITE3_CALL (prov, sqlite3_blob_write) (priv->sblob,
								gda_binary_get_data (gda_blob_get_binary (tmpblob)), wlen, offset + nbwritten);
			if (rc != SQLITE_OK)
				tmp_written = -1;
			else
				tmp_written = wlen;
			
			if (tmp_written < 0) {
				/* treat error */
				gda_blob_free ((gpointer) tmpblob);
				g_object_unref (prov);
				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 */
		int rc;
		int wlen;
		bin = gda_blob_get_binary (blob);
		if (gda_binary_get_size (bin) + offset > len)
			wlen = 	len - offset;
		else
			wlen = gda_binary_get_size (bin);

		rc = SQLITE3_CALL (prov, sqlite3_blob_write) (priv->sblob, gda_binary_get_data (bin), wlen, offset);
		if (rc != SQLITE_OK)
			nbwritten = -1;
		else
			nbwritten = wlen;
	}
	g_object_unref (prov);

	return nbwritten;
}