[libgda/gtk3] Virtual connection: corrections for the INSERT, DELETE and UPDATE operations
- From: Vivien Malerba <vivien src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [libgda/gtk3] Virtual connection: corrections for the INSERT, DELETE and UPDATE operations
- Date: Wed, 9 Feb 2011 20:35:42 +0000 (UTC)
commit 79c44a02e31f94e5911119580a8254bbf0b8e1ee
Author: Vivien Malerba <malerba gnome-db org>
Date: Mon Jan 31 19:29:36 2011 +0100
Virtual connection: corrections for the INSERT, DELETE and UPDATE operations
also added a new test
libgda/sqlite/gda-sqlite-recordset.c | 3 +-
.../virtual/gda-vconnection-data-model-private.h | 2 +-
libgda/sqlite/virtual/gda-vprovider-data-model.c | 388 +++++++++++++++---
tests/data-models/.gitignore | 1 +
tests/data-models/Makefile.am | 16 +-
tests/data-models/check_vcnc.c | 449 ++++++++++++++++++++
tests/data-models/cities1.xml | 83 ++++
tests/data-models/cities2.xml | 33 ++
tests/data-models/cities3.xml | 28 ++
tests/data-models/countriesA.xml | 75 ++++
tests/data-models/countriesB.xml | 31 ++
11 files changed, 1048 insertions(+), 61 deletions(-)
---
diff --git a/libgda/sqlite/gda-sqlite-recordset.c b/libgda/sqlite/gda-sqlite-recordset.c
index 9f06d47..c6f2c61 100644
--- a/libgda/sqlite/gda-sqlite-recordset.c
+++ b/libgda/sqlite/gda-sqlite-recordset.c
@@ -1,5 +1,5 @@
/* GDA SQLite provider
- * Copyright (C) 1998 - 2010 The GNOME Foundation.
+ * Copyright (C) 1998 - 2011 The GNOME Foundation.
*
* AUTHORS:
* Rodrigo Moya <rodrigo gnome-db org>
@@ -539,6 +539,7 @@ fetch_next_sqlite_row (GdaSqliteRecordset *model, gboolean do_store, GError **er
GDA_DATA_SELECT (model)->advertized_nrows = model->priv->next_row_num;
SQLITE3_CALL (sqlite3_reset) (ps->sqlite_stmt);
break;
+ case SQLITE_READONLY:
case SQLITE_MISUSE:
g_set_error (error, GDA_SERVER_PROVIDER_ERROR,
GDA_SERVER_PROVIDER_INTERNAL_ERROR,
diff --git a/libgda/sqlite/virtual/gda-vconnection-data-model-private.h b/libgda/sqlite/virtual/gda-vconnection-data-model-private.h
index a07e392..271b4fd 100644
--- a/libgda/sqlite/virtual/gda-vconnection-data-model-private.h
+++ b/libgda/sqlite/virtual/gda-vconnection-data-model-private.h
@@ -29,7 +29,7 @@ typedef struct {
GdaVconnectionDataModelSpec *spec;
GDestroyNotify spec_free_func;
- GdaDataModel *real_model; /* data model really being used, a reference count is kept on it */
+ GdaDataModel *real_model; /* data model really being used, a reference is kept on it */
GList *columns;
gchar *table_name;
gchar *unique_name;
diff --git a/libgda/sqlite/virtual/gda-vprovider-data-model.c b/libgda/sqlite/virtual/gda-vprovider-data-model.c
index 7a0c051..eab1afa 100644
--- a/libgda/sqlite/virtual/gda-vprovider-data-model.c
+++ b/libgda/sqlite/virtual/gda-vprovider-data-model.c
@@ -1,6 +1,6 @@
/*
* GDA common library
- * Copyright (C) 2007 - 2010 The GNOME Foundation.
+ * Copyright (C) 2007 - 2011 The GNOME Foundation.
*
* AUTHORS:
* Vivien Malerba <malerba gnome-db org>
@@ -147,6 +147,54 @@ gda_vprovider_data_model_new (void)
return provider;
}
+/*
+ * Note about RowIDs and how SQLite uses them:
+ *
+ * SQLite considers that each virtual table has unique row IDs, absolute value available all the time.
+ * When an UPDATE or DELETE statement is executed, SQLite does the following:
+ * - xBegin (table): request a transaction start
+ * - xOpen (table): create a new cursor
+ * - xFilter (cursor): initialize the cursor
+ * - moves the cursor one step at a time up to the end (xEof, xColumn and xNext). If it finds a
+ * row needing to be updated or deleted, it calls xRowid (cursor) to get the RowID
+ * - xClose (cursor): free the now useless cursor
+ * - calls xUpdate (table) as many times as needed (one time for each row where xRowid was called)
+ * - xSync (table)
+ * - xCommit (table)
+ *
+ * This does not work well with Libgda because it does not know how to define a unique RowID for
+ * a table: it defines the RowID as being the position of the row in the data model, which changes
+ * each time the data model used for the virtuel table changes.
+ *
+ * Moreover, when the cursor has reached the end of the data model, it may not be possible to move
+ * it backwards and so the data at any given row is not accessible anymore. The solution to this
+ * problem is, each time the xRowid() is called, to copy the row in memory, using the table->rowid_hash
+ * hash table.
+ */
+
+#ifdef GDA_DEBUG_VIRTUAL
+#define TRACE(table,cursor) g_print ("== %s (table=>%p cursor=>%p)\n", __FUNCTION__, (table), (cursor))
+#else
+#define TRACE(table,cursor)
+#endif
+
+typedef struct {
+ sqlite3_vtab base;
+ GdaVconnectionDataModel *cnc;
+ GdaVConnectionTableData *td;
+
+ GdaDataModel *rowid_hash_model; /* data model used to build the rowid_hash's
+ * contents. No ref held there as it's never
+ * dereferenced */
+ GHashTable *rowid_hash; /* key = a gint64 rowId, value = a GdaRow */
+} VirtualTable;
+
+typedef struct {
+ sqlite3_vtab_cursor base; /* base.pVtab is a pointer to the sqlite3_vtab virtual table */
+ GdaDataModelIter *iter;
+ gint ncols;
+} VirtualCursor;
+
/* module creation */
static int virtualCreate (sqlite3 *db, void *pAux, int argc, const char *const *argv, sqlite3_vtab **ppVtab, char **pzErr);
static int virtualConnect (sqlite3 *db, void *pAux, int argc, const char *const *argv, sqlite3_vtab **ppVtab, char **pzErr);
@@ -272,25 +320,6 @@ gda_vprovider_data_model_get_name (G_GNUC_UNUSED GdaServerProvider *provider)
return "Virtual";
}
-/* module implementation */
-#ifdef GDA_DEBUG_VIRTUAL
- #define TRACE(table) g_print ("== %s (table=>%p)\n", __FUNCTION__, (table))
-#else
- #define TRACE(table)
-#endif
-
-typedef struct {
- sqlite3_vtab base;
- GdaVconnectionDataModel *cnc;
- GdaVConnectionTableData *td;
-} VirtualTable;
-
-typedef struct {
- sqlite3_vtab_cursor base; /* base.pVtab is a pointer to the sqlite3_vtab virtual table */
- GdaDataModelIter *iter;
- gint ncols;
-} VirtualCursor;
-
static int
virtualCreate (sqlite3 *db, void *pAux, int argc, const char *const *argv, sqlite3_vtab **ppVtab, char **pzErr)
{
@@ -300,7 +329,7 @@ virtualCreate (sqlite3 *db, void *pAux, int argc, const char *const *argv, sqlit
gchar *spec_name, *tmp;
GdaVConnectionTableData *td;
- TRACE (NULL);
+ TRACE (NULL, NULL);
/* find Spec */
g_assert (argc == 4);
@@ -429,7 +458,7 @@ virtualCreate (sqlite3 *db, void *pAux, int argc, const char *const *argv, sqlit
return SQLITE_ERROR;
}
- /*g_print ("VIRTUAL TABLE: %s\n", sql->str);*/
+ /*g_print ("VIRTUAL TABLE [%p]: %s\n", vtable, sql->str);*/
g_string_free (sql, TRUE);
return SQLITE_OK;
@@ -438,7 +467,7 @@ virtualCreate (sqlite3 *db, void *pAux, int argc, const char *const *argv, sqlit
static int
virtualConnect (sqlite3 *db, void *pAux, int argc, const char *const *argv, sqlite3_vtab **ppVtab, char **pzErr)
{
- TRACE (NULL);
+ TRACE (NULL, NULL);
return virtualCreate (db, pAux, argc, argv, ppVtab, pzErr);
}
@@ -448,8 +477,10 @@ virtualDisconnect (sqlite3_vtab *pVtab)
{
VirtualTable *vtable = (VirtualTable *) pVtab;
- TRACE (pVtab);
+ TRACE (pVtab, NULL);
+ if (vtable->rowid_hash)
+ g_hash_table_destroy (vtable->rowid_hash);
g_free (vtable);
return SQLITE_OK;
}
@@ -457,7 +488,7 @@ virtualDisconnect (sqlite3_vtab *pVtab)
static int
virtualDestroy (sqlite3_vtab *pVtab)
{
- TRACE (pVtab);
+ TRACE (pVtab, NULL);
return virtualDisconnect (pVtab);
}
@@ -467,7 +498,7 @@ virtualOpen (sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor)
{
VirtualCursor *cursor;
- TRACE (pVTab);
+ TRACE (pVTab, NULL);
/* create empty cursor */
cursor = g_new0 (VirtualCursor, 1);
@@ -481,11 +512,11 @@ virtualClose (sqlite3_vtab_cursor *cur)
{
VirtualCursor *cursor = (VirtualCursor*) cur;
- TRACE (cur->pVtab);
+ TRACE (cur->pVtab, cur);
if (cursor->iter)
g_object_unref (cursor->iter);
- /* FIXME: destroy table->spec->model and table->wrapper */
+
g_free (cur);
return SQLITE_OK;
}
@@ -495,7 +526,7 @@ virtualEof (sqlite3_vtab_cursor *cur)
{
VirtualCursor *cursor = (VirtualCursor*) cur;
- TRACE (cur->pVtab);
+ TRACE (cur->pVtab, cur);
if (gda_data_model_iter_is_valid (cursor->iter))
return FALSE;
@@ -509,7 +540,7 @@ virtualNext (sqlite3_vtab_cursor *cur)
VirtualCursor *cursor = (VirtualCursor*) cur;
/*VirtualTable *vtable = (VirtualTable*) cur->pVtab;*/
- TRACE (cur->pVtab);
+ TRACE (cur->pVtab, cur);
if (!gda_data_model_iter_move_next (cursor->iter)) {
if (gda_data_model_iter_is_valid (cursor->iter))
@@ -525,7 +556,7 @@ virtualColumn (sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i)
{
VirtualCursor *cursor = (VirtualCursor*) cur;
- TRACE (cur->pVtab);
+ TRACE (cur->pVtab, cur);
GdaHolder *param;
@@ -580,10 +611,41 @@ static int
virtualRowid (sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid)
{
VirtualCursor *cursor = (VirtualCursor*) cur;
+ VirtualTable *vtable = (VirtualTable*) cur->pVtab;
- TRACE (cur->pVtab);
+ TRACE (vtable, cur);
*pRowid = gda_data_model_iter_get_row (cursor->iter);
+ if (! vtable->rowid_hash || (vtable->rowid_hash_model == vtable->td->real_model)) {
+ if (! vtable->rowid_hash) {
+ vtable->rowid_hash = g_hash_table_new_full (g_int_hash, g_int_equal,
+ g_free,
+ (GDestroyNotify) g_object_unref);
+ vtable->rowid_hash_model = vtable->td->real_model;
+ }
+
+ GdaRow *grow;
+ gint i, ncols;
+ gint64 *hid;
+ ncols = g_list_length (vtable->td->columns);
+
+ grow = gda_row_new (ncols);
+ for (i = 0; i < ncols; i++) {
+ const GValue *cvalue;
+ GValue *gvalue;
+ cvalue = gda_data_model_iter_get_value_at (cursor->iter, i);
+ gvalue = gda_row_get_value (grow, i);
+ if (G_VALUE_TYPE (cvalue) != GDA_TYPE_NULL) {
+ g_value_init (gvalue, G_VALUE_TYPE (cvalue));
+ g_value_copy (cvalue, gvalue);
+ }
+ }
+
+ hid = g_new (gint64, 1);
+ *hid = *pRowid;
+ g_hash_table_insert (vtable->rowid_hash, hid, grow);
+ }
+
return SQLITE_OK;
}
@@ -705,7 +767,7 @@ virtualFilter (sqlite3_vtab_cursor *pVtabCursor, int idxNum, const char *idxStr,
VirtualCursor *cursor = (VirtualCursor*) pVtabCursor;
VirtualTable *vtable = (VirtualTable*) pVtabCursor->pVtab;
- TRACE (pVtabCursor->pVtab);
+ TRACE (pVtabCursor->pVtab, pVtabCursor);
virtual_table_manage_real_data_model (vtable, idxNum, idxStr, argc, argv);
if (! vtable->td->real_model)
@@ -850,7 +912,7 @@ virtualBestIndex (sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo)
{
VirtualTable *vtable = (VirtualTable *) tab;
- TRACE (tab);
+ TRACE (tab, NULL);
#ifdef GDA_DEBUG_VIRTUAL
index_info_dump (pIdxInfo, FALSE);
#endif
@@ -870,6 +932,22 @@ virtualBestIndex (sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo)
}
/*
+ *
+ * Returns: >= 0 if Ok, -1 on error
+ */
+static gint
+param_name_to_number (gint maxrows, const gchar *str)
+{
+ gchar *endptr [1];
+ long int i;
+ i = strtol (str, endptr, 10);
+ if ((**endptr == '\0') && (i < maxrows) && (i >= 0))
+ return i;
+ else
+ return -1;
+}
+
+/*
* apData[0] apData[1] apData[2..]
*
* INTEGER DELETE
@@ -885,35 +963,227 @@ virtualUpdate (sqlite3_vtab *tab, int nData, sqlite3_value **apData, sqlite_int6
{
VirtualTable *vtable = (VirtualTable *) tab;
const gchar *api_misuse_error = NULL;
+ gint optype; /* 1 => DELETE
+ * 2 => INSERT
+ * 3 => UPDATE
+ */
+
+ TRACE (tab, NULL);
- TRACE (tab);
+ /* determine operation type */
+ if (nData == 1)
+ optype = 1;
+ else if ((nData > 1) && (SQLITE3_CALL (sqlite3_value_type) (apData[0]) == SQLITE_NULL)) {
+ optype = 2;
+ if (SQLITE3_CALL (sqlite3_value_type) (apData[1]) != SQLITE_NULL) {
+ /* argc>1 and argv[0] is not NULL: rowid is imposed by SQLite
+ * which is not supported */
+ return SQLITE_READONLY;
+ }
+ }
+ else if ((nData > 1) && (SQLITE3_CALL (sqlite3_value_type) (apData[0]) == SQLITE_INTEGER)) {
+ optype = 3;
+ if (SQLITE3_CALL (sqlite3_value_int) (apData[0]) !=
+ SQLITE3_CALL (sqlite3_value_int) (apData[1])) {
+ /* argc>1 and argv[0]==argv[1]: rowid is imposed by SQLite
+ * which is not supported */
+ return SQLITE_READONLY;
+ }
+ }
+ else
+ return SQLITE_MISUSE;
+
+ /* handle data model */
+ if (! vtable->td->real_model) {
+ virtual_table_manage_real_data_model (vtable, -1, NULL, 0, NULL);
+ if (! vtable->td->real_model)
+ return SQLITE_ERROR;
+ }
+
+ GdaDataModelAccessFlags access_flags;
+ access_flags = gda_data_model_get_access_flags (vtable->td->real_model);
+ if (((optype == 1) && ! (access_flags & GDA_DATA_MODEL_ACCESS_DELETE)) ||
+ ((optype == 2) && ! (access_flags & GDA_DATA_MODEL_ACCESS_INSERT)) ||
+ ((optype == 3) && ! (access_flags & GDA_DATA_MODEL_ACCESS_UPDATE))) {
+ /* we can't use vtable->td->real_model because it can't be accessed correctly */
+ if (! GDA_IS_DATA_SELECT (vtable->td->real_model)) {
+ tab->zErrMsg = SQLITE3_CALL (sqlite3_mprintf)
+ (_("Data model representing the table is read only"));
+ return SQLITE_READONLY;
+ }
+
+ GdaStatement *stmt;
+ switch (optype) {
+ case 1:
+ g_object_get (vtable->td->real_model, "delete-stmt", &stmt, NULL);
+ break;
+ case 2:
+ g_object_get (vtable->td->real_model, "insert-stmt", &stmt, NULL);
+ break;
+ case 3:
+ g_object_get (vtable->td->real_model, "update-stmt", &stmt, NULL);
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ if (! stmt) {
+ tab->zErrMsg = SQLITE3_CALL (sqlite3_mprintf)
+ (_("No statement provided to modify the data model representing the table"));
+ return SQLITE_READONLY;
+ }
+
+ GdaSet *params;
+ if (! gda_statement_get_parameters (stmt, ¶ms, NULL) || !params) {
+ tab->zErrMsg = SQLITE3_CALL (sqlite3_mprintf)
+ (_("Invalid statement provided to modify the data model representing the table"));
+ g_object_unref (stmt);
+ return SQLITE_READONLY;
+ }
+
+ GSList *list;
+ for (list = params->holders; list; list = list->next) {
+ const gchar *id;
+ GdaHolder *holder = GDA_HOLDER (list->data);
+ gboolean holder_value_set = FALSE;
+
+ id = gda_holder_get_id (holder);
+ if (!id) {
+ g_object_unref (params);
+ g_object_unref (stmt);
+ tab->zErrMsg = SQLITE3_CALL (sqlite3_mprintf)
+ (_("Invalid parameter in statement provided to modify "
+ "the data model representing the table"));
+ return SQLITE_READONLY;
+ }
+ if (*id == '+' && id[1]) {
+ long int i;
+ i = param_name_to_number (gda_data_model_get_n_columns (vtable->td->real_model),
+ id+1);
+ if (i >= 0) {
+ GType type;
+ GValue *value;
+ type = gda_column_get_g_type (gda_data_model_describe_column (vtable->td->real_model, i));
+ if ((type != GDA_TYPE_NULL) && SQLITE3_CALL (sqlite3_value_text) (apData [i+2]))
+ value = gda_value_new_from_string ((const gchar*) SQLITE3_CALL (sqlite3_value_text) (apData [i+2]), type);
+ else
+ value = gda_value_new_null ();
+ if (gda_holder_take_value (holder, value, NULL))
+ holder_value_set = TRUE;
+ }
+ }
+ else if (*id == '-') {
+ if (! vtable->rowid_hash) {
+ tab->zErrMsg = SQLITE3_CALL (sqlite3_mprintf)
+ (_("Could not retreive row to delete"));
+ return SQLITE_READONLY;
+ }
+
+ gint64 rowid = SQLITE3_CALL (sqlite3_value_int64) (apData [0]);
+ GdaRow *grow = NULL;
+ if (vtable->rowid_hash_model == vtable->td->real_model)
+ grow = g_hash_table_lookup (vtable->rowid_hash, &rowid);
+ if (!grow) {
+ tab->zErrMsg = SQLITE3_CALL (sqlite3_mprintf)
+ (_("Could not retreive row to delete"));
+ return SQLITE_READONLY;
+ }
+
+ long int i;
+ i = param_name_to_number (gda_data_model_get_n_columns (vtable->td->real_model),
+ id+1);
+ if (i >= 0) {
+ GValue *value;
+ value = gda_row_get_value (grow, i);
+ if (gda_holder_set_value (holder, value, NULL))
+ holder_value_set = TRUE;
+ }
+ }
+
+ if (! holder_value_set) {
+ GdaSet *exec_set;
+ GdaHolder *eh;
+ g_object_get (vtable->td->real_model, "exec-params",
+ &exec_set, NULL);
+ if (! exec_set) {
+ /* can't give value to param named @id */
+ g_object_unref (params);
+ g_object_unref (stmt);
+ tab->zErrMsg = SQLITE3_CALL (sqlite3_mprintf)
+ (_("Invalid parameter in statement provided to modify "
+ "the data model representing the table"));
+ return SQLITE_READONLY;
+ }
+ eh = gda_set_get_holder (exec_set, id);
+ if (! eh ||
+ ! gda_holder_set_bind (holder, eh, NULL)) {
+ /* can't give value to param named @id */
+ g_object_unref (params);
+ g_object_unref (stmt);
+ tab->zErrMsg = SQLITE3_CALL (sqlite3_mprintf)
+ (_("Invalid parameter in statement provided to modify "
+ "the data model representing the table"));
+ return SQLITE_READONLY;
+ }
+ }
+ }
+
+ GdaConnection *cnc;
+ cnc = gda_data_select_get_connection (GDA_DATA_SELECT (vtable->td->real_model));
+
+#ifdef GDA_DEBUG_NO
+ gchar *sql;
+ GError *lerror = NULL;
+ sql = gda_statement_to_sql (stmt, NULL, NULL);
+ g_print ("SQL: [%s] ", sql);
+ g_free (sql);
+ sql = gda_statement_to_sql_extended (stmt, cnc, params, GDA_STATEMENT_SQL_PRETTY, NULL, &lerror);
+ if (sql) {
+ g_print ("With params: [%s]\n", sql);
+ g_free (sql);
+ }
+ else {
+ g_print ("params ERROR [%s]\n", lerror && lerror->message ? lerror->message : "No detail");
+ }
+ g_clear_error (&lerror);
+#endif
+
+ if (!cnc ||
+ (gda_connection_statement_execute_non_select (cnc, stmt, params,
+ NULL, NULL) == -1)) {
+ /* failed to execute */
+ g_object_unref (params);
+ g_object_unref (stmt);
+ tab->zErrMsg = SQLITE3_CALL (sqlite3_mprintf)
+ (_("Failed to execute the statement provided to modify "
+ "the data model representing the table"));
+ return SQLITE_READONLY;
+ }
+ return SQLITE_OK;
+ }
/* REM: when using the values of apData[], the limit is
* (nData -1 ) and not nData because the last column of the corresponding CREATE TABLE ...
* is an internal hidden field which does not correspond to any column of the real data model
*/
- if (nData == 1) {
+ if (optype == 1) {
/* DELETE */
if (SQLITE3_CALL (sqlite3_value_type) (apData[0]) == SQLITE_INTEGER) {
gint rowid = SQLITE3_CALL (sqlite3_value_int) (apData [0]);
- return gda_data_model_remove_row (vtable->td->real_model, rowid, NULL) ? SQLITE_OK : SQLITE_READONLY;
+ return gda_data_model_remove_row (vtable->td->real_model, rowid, NULL) ?
+ SQLITE_OK : SQLITE_READONLY;
}
else {
api_misuse_error = "argc==1 and argv[0] is not an integer";
goto api_misuse;
}
}
- else if ((nData > 1) && (SQLITE3_CALL (sqlite3_value_type) (apData[0]) == SQLITE_NULL)) {
+ else if (optype == 2) {
/* INSERT */
gint newrow, i;
GList *values = NULL;
- if (SQLITE3_CALL (sqlite3_value_type) (apData[1]) != SQLITE_NULL) {
- /* argc>1 and argv[0] is not NULL: rowid is imposed by SQLite which is not supported */
- return SQLITE_READONLY;
- }
-
for (i = 2; i < (nData - 1); i++) {
GType type;
GValue *value;
@@ -922,7 +1192,8 @@ virtualUpdate (sqlite3_vtab *tab, int nData, sqlite3_value **apData, sqlite_int6
value = gda_value_new_from_string ((const gchar*) SQLITE3_CALL (sqlite3_value_text) (apData [i]), type);
else
value = gda_value_new_null ();
- /*g_print ("TXT #%s# => value %p (type=%s) apData[]=%p\n", SQLITE3_CALL (sqlite3_value_text) (apData [i]), value,
+ /*g_print ("TXT #%s# => value %p (type=%s) apData[]=%p\n",
+ SQLITE3_CALL (sqlite3_value_text) (apData [i]), value,
g_type_name (type), apData[i]);*/
values = g_list_prepend (values, value);
}
@@ -936,14 +1207,11 @@ virtualUpdate (sqlite3_vtab *tab, int nData, sqlite3_value **apData, sqlite_int6
*pRowid = newrow;
}
- else if ((nData > 1) && (SQLITE3_CALL (sqlite3_value_type) (apData[0])==SQLITE_INTEGER)) {
+ else if (optype == 3) {
/* UPDATE */
gint i;
- if (SQLITE3_CALL (sqlite3_value_int) (apData[0]) != SQLITE3_CALL (sqlite3_value_int) (apData[1])) {
- /* argc>1 and argv[0]==argv[1]: rowid is imposed by SQLite which is not supported */
- return SQLITE_READONLY;
- }
+
for (i = 2; i < (nData - 1); i++) {
GValue *value;
GType type;
@@ -954,7 +1222,8 @@ virtualUpdate (sqlite3_vtab *tab, int nData, sqlite3_value **apData, sqlite_int6
/*g_print ("%d => %s\n", i, SQLITE3_CALL (sqlite3_value_text) (apData [i]));*/
type = gda_column_get_g_type (gda_data_model_describe_column (vtable->td->real_model, i - 2));
value = gda_value_new_from_string ((const gchar*) SQLITE3_CALL (sqlite3_value_text) (apData [i]), type);
- res = gda_data_model_set_value_at (vtable->td->real_model, i - 2, rowid, value, &error);
+ res = gda_data_model_set_value_at (vtable->td->real_model, i - 2, rowid,
+ value, &error);
gda_value_free (value);
if (!res) {
g_print ("Error: %s\n", error && error->message ? error->message : "???");
@@ -979,7 +1248,7 @@ virtualUpdate (sqlite3_vtab *tab, int nData, sqlite3_value **apData, sqlite_int6
static int
virtualBegin (sqlite3_vtab *tab)
{
- TRACE (tab);
+ TRACE (tab, NULL);
/* no documentation currently available, don't do anything */
return SQLITE_OK;
}
@@ -987,7 +1256,7 @@ virtualBegin (sqlite3_vtab *tab)
static int
virtualSync (G_GNUC_UNUSED sqlite3_vtab *tab)
{
- TRACE (tab);
+ TRACE (tab, NULL);
/* no documentation currently available, don't do anything */
return SQLITE_OK;
}
@@ -995,15 +1264,22 @@ virtualSync (G_GNUC_UNUSED sqlite3_vtab *tab)
static int
virtualCommit (G_GNUC_UNUSED sqlite3_vtab *tab)
{
- TRACE (tab);
- /* no documentation currently available, don't do anything */
+ VirtualTable *vtable = (VirtualTable *) tab;
+ TRACE (tab, NULL);
+
+ if (vtable->rowid_hash) {
+ g_hash_table_destroy (vtable->rowid_hash);
+ vtable->rowid_hash = NULL;
+ vtable->rowid_hash_model = NULL;
+ }
+
return SQLITE_OK;
}
static int
virtualRollback (G_GNUC_UNUSED sqlite3_vtab *tab)
{
- TRACE (tab);
+ TRACE (tab, NULL);
/* no documentation currently available, don't do anything */
return SQLITE_OK;
}
@@ -1011,7 +1287,7 @@ virtualRollback (G_GNUC_UNUSED sqlite3_vtab *tab)
static int
virtualRename(sqlite3_vtab *pVtab, const char *zNew)
{
- TRACE (pVtab);
+ TRACE (pVtab, NULL);
/* not yet analysed and implemented */
return SQLITE_OK;
}
diff --git a/tests/data-models/.gitignore b/tests/data-models/.gitignore
index f21f8d3..1629cb3 100644
--- a/tests/data-models/.gitignore
+++ b/tests/data-models/.gitignore
@@ -5,4 +5,5 @@ check_model_copy
check_pmodel
check_empty_rs
check_model_errors
+check_vcnc
*.db
diff --git a/tests/data-models/Makefile.am b/tests/data-models/Makefile.am
index 1cd0291..7024556 100644
--- a/tests/data-models/Makefile.am
+++ b/tests/data-models/Makefile.am
@@ -8,8 +8,8 @@ AM_CPPFLAGS = \
-DTOP_SRC_DIR=\""$(top_srcdir)"\" \
-DTOP_BUILD_DIR=\""$(top_builddir)"\"
-check_PROGRAMS = check_model_import check_virtual check_data_proxy check_model_copy check_pmodel check_empty_rs check_model_errors
-TESTS = check_model_import check_virtual check_data_proxy check_model_copy check_pmodel check_empty_rs check_model_errors
+check_PROGRAMS = check_model_import check_virtual check_data_proxy check_model_copy check_pmodel check_empty_rs check_model_errors check_vcnc
+TESTS = check_model_import check_virtual check_data_proxy check_model_copy check_pmodel check_empty_rs check_model_errors check_vcnc
common_sources =
@@ -57,6 +57,15 @@ check_model_errors_LDADD = \
$(top_builddir)/tests/libgda-test-5.0.la \
$(LIBGDA_LIBS)
+check_vcnc_SOURCES = $(common_sources) check_vcnc.c
+check_vcnc_CFLAGS = \
+ -I$(top_srcdir)/libgda/sqlite \
+ -I$(top_srcdir)/libgda/sqlite/vcnc
+check_vcnc_LDADD = \
+ $(top_builddir)/libgda/libgda-4.0.la \
+ $(LIBGDA_LIBS)
+
+
EXTRA_DIST = \
check_virtual.csv \
city.csv \
@@ -67,4 +76,5 @@ EXTRA_DIST = \
pmodel_data_locations.xml
CLEANFILES = \
- pmodel.db pmodel.db-journal
+ pmodel.db pmodel.db-journal \
+ vcnc.db vcnc.db-journal
diff --git a/tests/data-models/check_vcnc.c b/tests/data-models/check_vcnc.c
new file mode 100644
index 0000000..813e21e
--- /dev/null
+++ b/tests/data-models/check_vcnc.c
@@ -0,0 +1,449 @@
+#include <libgda/libgda.h>
+#include <virtual/libgda-virtual.h>
+#include <sql-parser/gda-sql-parser.h>
+#include <string.h>
+
+typedef enum {
+ ACTION_COMPARE,
+ ACTION_EXPORT
+} Action;
+
+static GdaDataModel *run_sql_select (GdaConnection *cnc, const gchar *sql,
+ gboolean iter_only, GError **error);
+static gboolean run_sql_non_select (GdaConnection *cnc, const gchar *sql, GError **error);
+static GdaDataModel *assert_run_sql_select (GdaConnection *cnc, const gchar *sql,
+ Action action, const gchar *compare_file);
+static void assert_run_sql_non_select (GdaConnection *cnc, const gchar *sql);
+GdaDataModel *load_from_file (const gchar *filename);
+static void assert_data_model_equel (GdaDataModel *model, GdaDataModel *ref);
+
+static GdaConnection *open_destination_connection (void);
+static void check_update_delete (GdaConnection *virtual);
+static void check_simultanous_select_random (GdaConnection *virtual);
+static void check_simultanous_select_forward (GdaConnection *virtual);
+
+int
+main (int argc, char *argv[])
+{
+ GError *error = NULL;
+ GdaConnection *virtual, *out_cnc;
+ GdaVirtualProvider *provider;
+ gchar *file;
+
+ /* set up test environment */
+ g_setenv ("GDA_TOP_BUILD_DIR", TOP_BUILD_DIR, 0);
+ g_setenv ("GDA_TOP_SRC_DIR", TOP_SRC_DIR, TRUE);
+ gda_init ();
+
+ provider = gda_vprovider_hub_new ();
+ virtual = gda_virtual_connection_open (provider, NULL);
+ g_assert (virtual);
+
+ /* load CSV data models */
+ GdaDataModel *country_model, *city_model;
+ GdaSet *options = gda_set_new_inline (2, "TITLE_AS_FIRST_LINE", G_TYPE_BOOLEAN, TRUE,
+ "G_TYPE_2", G_TYPE_GTYPE, G_TYPE_INT);
+ file = g_build_filename (CHECK_FILES, "tests", "data-models", "city.csv", NULL);
+ city_model = gda_data_model_import_new_file (file, TRUE, options);
+ g_free (file);
+ file = g_build_filename (CHECK_FILES, "tests", "data-models", "country.csv", NULL);
+ country_model = gda_data_model_import_new_file (file, TRUE, options);
+ g_free (file);
+ g_object_unref (options);
+
+ /* Add data models to connection */
+ if (!gda_vconnection_data_model_add_model (GDA_VCONNECTION_DATA_MODEL (virtual), city_model, "city", &error))
+ g_error ("Add city model error: %s\n", error && error->message ? error->message : "no detail");
+ if (!gda_vconnection_data_model_add_model (GDA_VCONNECTION_DATA_MODEL (virtual), country_model, "country", &error))
+ g_error ("Add country model error: %s\n", error && error->message ? error->message : "no detail");
+
+ /* SQLite connection for outputs */
+ out_cnc = open_destination_connection ();
+
+ /* adding connections to the virtual connection */
+ if (!gda_vconnection_hub_add (GDA_VCONNECTION_HUB (virtual), out_cnc, "out", &error)) {
+ g_print ("Could not add connection to virtual connection: %s\n",
+ error && error->message ? error->message : "No detail");
+ exit (1);
+ }
+
+ check_update_delete (virtual);
+
+ g_print ("*** Copying data into 'countries' virtual table...\n");
+ assert_run_sql_non_select (virtual, "INSERT INTO out.countries SELECT * FROM country");
+
+ check_simultanous_select_random (virtual);
+ check_simultanous_select_forward (virtual);
+
+ gda_connection_close (virtual);
+ gda_connection_close (out_cnc);
+ return 0;
+}
+
+GdaConnection *
+open_destination_connection (void)
+{
+ /* create connection */
+ GdaConnection *cnc;
+ GError *error = NULL;
+ cnc = gda_connection_open_from_string ("SQLite", "DB_DIR=.;DB_NAME=vcnc",
+ NULL,
+ GDA_CONNECTION_OPTIONS_NONE,
+ &error);
+ if (!cnc) {
+ g_print ("Could not open connection to local SQLite database: %s\n",
+ error && error->message ? error->message : "No detail");
+ exit (1);
+ }
+
+ /* table "cities" */
+ assert_run_sql_non_select (cnc, "DROP table IF EXISTS cities");
+ assert_run_sql_non_select (cnc, "CREATE table cities (name string not NULL primary key, countrycode string not null, population int)");
+
+ /* table "countries" */
+ assert_run_sql_non_select (cnc, "DROP table IF EXISTS countries");
+ assert_run_sql_non_select (cnc, "CREATE table countries (code string not null primary key, name string not null)");
+
+ return cnc;
+}
+
+static GdaDataModel *
+run_sql_select (GdaConnection *cnc, const gchar *sql, gboolean iter_only, GError **error)
+{
+ GdaStatement *stmt;
+ GdaDataModel *res;
+ GdaSqlParser *parser;
+
+ parser = gda_connection_create_parser (cnc);
+ stmt = gda_sql_parser_parse_string (parser, sql, NULL, NULL);
+ g_object_unref (parser);
+
+ res = gda_connection_statement_execute_select_full (cnc, stmt, NULL,
+ iter_only ? GDA_STATEMENT_MODEL_CURSOR_FORWARD :
+ GDA_STATEMENT_MODEL_RANDOM_ACCESS,
+ NULL, error);
+ g_object_unref (stmt);
+ return res;
+}
+
+
+static gboolean
+run_sql_non_select (GdaConnection *cnc, const gchar *sql, GError **error)
+{
+ GdaStatement *stmt;
+ gint nrows;
+ GdaSqlParser *parser;
+
+ parser = gda_connection_create_parser (cnc);
+ stmt = gda_sql_parser_parse_string (parser, sql, NULL, NULL);
+ g_object_unref (parser);
+
+ nrows = gda_connection_statement_execute_non_select (cnc, stmt, NULL, NULL, error);
+ g_object_unref (stmt);
+ if (nrows == -1)
+ return FALSE;
+ else
+ return TRUE;
+}
+
+static void
+assert_data_model_equel (GdaDataModel *model, GdaDataModel *ref)
+{
+ GdaDataComparator *comp;
+ GError *error = NULL;
+ comp = GDA_DATA_COMPARATOR (gda_data_comparator_new (ref, model));
+ if (! gda_data_comparator_compute_diff (comp, &error)) {
+ g_print ("Error comparing data models: %s\n",
+ error && error->message ? error->message : "No detail");
+ exit (1);
+ }
+ if (gda_data_comparator_get_n_diffs (comp) > 0) {
+ g_print ("Data models differ!\n");
+ g_print ("Expected:\n");
+ gda_data_model_dump (ref, NULL);
+ g_print ("Got:\n");
+ gda_data_model_dump (model, NULL);
+ exit (1);
+ }
+ g_object_unref (comp);
+}
+
+static GdaDataModel *
+assert_run_sql_select (GdaConnection *cnc, const gchar *sql, Action action, const gchar *compare_file)
+{
+ GError *error = NULL;
+ GdaDataModel *model = run_sql_select (cnc, sql, FALSE, &error);
+ if (!model) {
+ g_print ("Error executing [%s]: %s\n",
+ sql,
+ error && error->message ? error->message : "No detail");
+ exit (1);
+ }
+
+ gda_data_model_dump (model, NULL);
+ if (compare_file) {
+ gchar *file;
+ file = g_build_filename (CHECK_FILES, "tests", "data-models", compare_file, NULL);
+ if (action == ACTION_EXPORT) {
+ if (! gda_data_model_export_to_file (model, GDA_DATA_MODEL_IO_DATA_ARRAY_XML,
+ file,
+ NULL, 0, NULL, 0, NULL, &error)) {
+ g_print ("Error exporting to file '%s': %s\n",
+ file,
+ error && error->message ? error->message : "No detail");
+ exit (1);
+ }
+ g_print ("Generated '%s'\n", file);
+ }
+ else if (action == ACTION_COMPARE) {
+ GdaDataModel *ref;
+ ref = load_from_file (compare_file);
+ assert_data_model_equel (model, ref);
+ g_object_unref (ref);
+ }
+ else
+ g_assert_not_reached ();
+ g_free (file);
+ }
+
+ return model;
+}
+
+static void
+assert_run_sql_non_select (GdaConnection *cnc, const gchar *sql)
+{
+ GError *error = NULL;
+ if (! run_sql_non_select (cnc, sql, &error)) {
+ g_print ("Error executing [%s]: %s\n",
+ sql,
+ error && error->message ? error->message : "No detail");
+ exit (1);
+ }
+}
+
+GdaDataModel *
+load_from_file (const gchar *filename)
+{
+ GdaDataModel *model;
+ gchar *file;
+
+ file = g_build_filename (CHECK_FILES, "tests", "data-models", filename, NULL);
+ model = gda_data_model_import_new_file (file, TRUE, NULL);
+ if (gda_data_model_import_get_errors (GDA_DATA_MODEL_IMPORT (model))) {
+ g_print ("Error loading file '%s'\n", file);
+ exit (1);
+ }
+ g_free (file);
+ return model;
+}
+
+static void
+move_iter_forward (GdaDataModelIter *iter, const gchar *iter_name, gint nb, GdaDataModel *ref, gint start_row)
+{
+ gint i;
+ for (i = 0; i < nb; i++) {
+ g_print ("*** moving iter %s forward... ", iter_name);
+ if (! gda_data_model_iter_move_next (iter)) {
+ g_print ("Could not move forward at step %d\n", i);
+ exit (1);
+ }
+ else {
+ const GValue *cvalue;
+ gchar *str;
+ cvalue = gda_data_model_iter_get_value_at (iter, 0);
+ str = gda_value_stringify (cvalue);
+ g_print ("Col0=[%s]", str);
+ g_free (str);
+
+ if (ref) {
+ if (gda_data_model_iter_get_row (iter) != (start_row + i)) {
+ g_print (" Wrong reported row %d instead of %d\n",
+ gda_data_model_iter_get_row (iter), start_row + i);
+ exit (1);
+ }
+ const GValue *rvalue;
+ rvalue = gda_data_model_get_value_at (ref, 0, start_row + i, NULL);
+ g_assert (rvalue);
+ gchar *str1, *str2;
+ str1 = gda_value_stringify (cvalue);
+ str2 = gda_value_stringify (rvalue);
+ if (strcmp (str1, str2)) {
+ g_print (" Wrong reported value [%s] instead of [%s]\n",
+ str1, str2);
+ exit (1);
+ }
+ else
+ g_print (" Value Ok.");
+ g_free (str1);
+ g_free (str2);
+ }
+ g_print ("\n");
+ }
+ }
+}
+
+static void
+check_simultanous_select_random (GdaConnection *virtual)
+{
+ GdaDataModel *m1, *m2;
+ GdaDataModel *refA = NULL, *refB = NULL;
+ GdaDataModelIter *iter1, *iter2;
+ GError *error = NULL;
+
+ g_print ("*** simultaneous SELECT RANDOM 1\n");
+ m1 = run_sql_select (virtual, "SELECT * FROM countries WHERE code LIKE 'A%' ORDER BY code",
+ FALSE, &error);
+ if (!m1) {
+ g_print ("Could not execute SELECT with forward iter only (1): %s\n",
+ error && error->message ? error->message : "No detail");
+ exit (1);
+ }
+
+ g_print ("*** simultaneous SELECT RANDOM 2\n");
+ m2 = run_sql_select (virtual, "SELECT * FROM countries WHERE code LIKE 'B%' ORDER BY code",
+ FALSE, &error);
+ if (!m2) {
+ g_print ("Could not execute SELECT with forward iter only (2): %s\n",
+ error && error->message ? error->message : "No detail");
+ exit (1);
+ }
+
+ gda_data_model_dump (m2, NULL);
+ gda_data_model_dump (m1, NULL);
+
+/*#define EXPORT*/
+#ifdef EXPORT
+ gchar *file;
+ file = g_build_filename (CHECK_FILES, "tests", "data-models", "countriesA.xml", NULL);
+ if (! gda_data_model_export_to_file (m1, GDA_DATA_MODEL_IO_DATA_ARRAY_XML,
+ file,
+ NULL, 0, NULL, 0, NULL, &error)) {
+ g_print ("Error exporting to file '%s': %s\n",
+ file,
+ error && error->message ? error->message : "No detail");
+ exit (1);
+ }
+ g_print ("Generated '%s'\n", file);
+ g_free (file);
+
+ file = g_build_filename (CHECK_FILES, "tests", "data-models", "countriesB.xml", NULL);
+ if (! gda_data_model_export_to_file (m2, GDA_DATA_MODEL_IO_DATA_ARRAY_XML,
+ file,
+ NULL, 0, NULL, 0, NULL, &error)) {
+ g_print ("Error exporting to file '%s': %s\n",
+ file,
+ error && error->message ? error->message : "No detail");
+ exit (1);
+ }
+ g_print ("Generated '%s'\n", file);
+ g_free (file);
+#else
+ refA = load_from_file ("countriesA.xml");
+ assert_data_model_equel (m1, refA);
+
+ refB = load_from_file ("countriesB.xml");
+ assert_data_model_equel (m2, refB);
+#endif
+
+ iter1 = gda_data_model_create_iter (m1);
+ g_print ("*** simultaneous iter 1 %p\n", iter1);
+
+ iter2 = gda_data_model_create_iter (m2);
+ g_print ("*** simultaneous iter 2 %p\n", iter2);
+
+ move_iter_forward (iter1, "iter1", 10, refA, 0);
+ move_iter_forward (iter2, "iter2", 3, refB, 0);
+ move_iter_forward (iter1, "iter1", 3, refA, 10);
+ move_iter_forward (iter2, "iter2", 2, refB, 3);
+
+ g_object_unref (iter1);
+ g_object_unref (iter2);
+
+ g_object_unref (m1);
+ g_object_unref (m2);
+#ifndef EXPORT
+ g_object_unref (refA);
+ g_object_unref (refB);
+#endif
+}
+
+static void
+check_simultanous_select_forward (GdaConnection *virtual)
+{
+ GdaDataModel *m1, *m2;
+ GdaDataModel *refA, *refB;
+ GdaDataModelIter *iter1, *iter2;
+ GError *error = NULL;
+
+ g_print ("*** simultaneous SELECT FORWARD 1\n");
+ m1 = run_sql_select (virtual, "SELECT * FROM countries WHERE code LIKE 'A%' ORDER BY code",
+ TRUE, &error);
+ if (!m1) {
+ g_print ("Could not execute SELECT with forward iter only (1): %s\n",
+ error && error->message ? error->message : "No detail");
+ exit (1);
+ }
+
+ g_print ("*** simultaneous SELECT FORWARD 2\n");
+ m2 = run_sql_select (virtual, "SELECT * FROM countries WHERE code LIKE 'B%' ORDER BY code",
+ TRUE, &error);
+ if (!m2) {
+ g_print ("Could not execute SELECT with forward iter only (2): %s\n",
+ error && error->message ? error->message : "No detail");
+ exit (1);
+ }
+
+ refA = load_from_file ("countriesA.xml");
+ refB = load_from_file ("countriesB.xml");
+
+ iter1 = gda_data_model_create_iter (m1);
+ g_print ("*** simultaneous iter 1 %p\n", iter1);
+
+ iter2 = gda_data_model_create_iter (m2);
+ g_print ("*** simultaneous iter 2 %p\n", iter2);
+
+ move_iter_forward (iter1, "iter1", 10, refA, 0);
+ if (gda_data_model_iter_move_prev (iter1)) {
+ g_print ("Iter should not be allowed to move backward!\n");
+ exit (1);
+ }
+ move_iter_forward (iter2, "iter2", 3, refB, 0);
+ move_iter_forward (iter1, "iter1", 3, refA, 10);
+ move_iter_forward (iter2, "iter2", 2, refB, 3);
+
+ g_object_unref (iter1);
+ g_object_unref (iter2);
+
+ g_object_unref (refA);
+ g_object_unref (refB);
+
+ g_object_unref (m1);
+ g_object_unref (m2);
+}
+
+
+static void
+check_update_delete (GdaConnection *virtual)
+{
+ /* Check DELETE and UPDATE */
+ g_print ("*** Copying data into virtual 'cities' table...\n");
+ assert_run_sql_non_select (virtual, "INSERT INTO out.cities SELECT * FROM city WHERE population >= 500000");
+ g_print ("*** Showing list of cities WHERE population >= 1000000\n");
+ assert_run_sql_select (virtual, "SELECT * FROM out.cities WHERE population >= 1000000 "
+ "ORDER BY name", ACTION_COMPARE, "cities1.xml");
+
+ g_print ("*** Deleting data where population < 2000000...\n");
+ assert_run_sql_non_select (virtual, "DELETE FROM out.cities WHERE population < 2000000");
+
+ g_print ("*** Showing (shorter) list of cities WHERE population >= 1000000\n");
+ assert_run_sql_select (virtual, "SELECT * FROM out.cities WHERE population >= 1000000 "
+ "ORDER BY name", ACTION_COMPARE, "cities2.xml");
+
+ g_print ("*** Updating data where population > 3000000...\n");
+ assert_run_sql_non_select (virtual, "UPDATE out.cities SET population = 3000000 WHERE "
+ "population >= 3000000");
+
+ g_print ("*** Showing list of cities WHERE population >= 2100000\n");
+ assert_run_sql_select (virtual, "SELECT * FROM out.cities WHERE population >= 2100000 "
+ "ORDER BY name", ACTION_COMPARE, "cities3.xml");
+}
diff --git a/tests/data-models/cities1.xml b/tests/data-models/cities1.xml
new file mode 100644
index 0000000..1a8abf0
--- /dev/null
+++ b/tests/data-models/cities1.xml
@@ -0,0 +1,83 @@
+<?xml version="1.0"?>
+<gda_array id="EXPORT" name="Données exportées">
+ <gda_array_field id="FI0" name="name" title="name" dbms_type="string" gdatype="string" nullok="TRUE"/>
+ <gda_array_field id="FI1" name="countrycode" title="countrycode" dbms_type="string" gdatype="string" nullok="TRUE"/>
+ <gda_array_field id="FI2" name="population" title="population" dbms_type="integer" gdatype="int" nullok="TRUE"/>
+ <gda_array_data>
+ <gda_array_row>
+ <gda_value>Baku</gda_value>
+ <gda_value>AZE</gda_value>
+ <gda_value>1787800</gda_value>
+ </gda_array_row>
+ <gda_array_row>
+ <gda_value>Brisbane</gda_value>
+ <gda_value>AUS</gda_value>
+ <gda_value>1291117</gda_value>
+ </gda_array_row>
+ <gda_array_row>
+ <gda_value>Buenos Aires</gda_value>
+ <gda_value>ARG</gda_value>
+ <gda_value>2982146</gda_value>
+ </gda_array_row>
+ <gda_array_row>
+ <gda_value>Chittagong</gda_value>
+ <gda_value>BGD</gda_value>
+ <gda_value>1392860</gda_value>
+ </gda_array_row>
+ <gda_array_row>
+ <gda_value>Córdoba</gda_value>
+ <gda_value>ARG</gda_value>
+ <gda_value>1157507</gda_value>
+ </gda_array_row>
+ <gda_array_row>
+ <gda_value>Dhaka</gda_value>
+ <gda_value>BGD</gda_value>
+ <gda_value>3612850</gda_value>
+ </gda_array_row>
+ <gda_array_row>
+ <gda_value>Kabul</gda_value>
+ <gda_value>AFG</gda_value>
+ <gda_value>1780000</gda_value>
+ </gda_array_row>
+ <gda_array_row>
+ <gda_value>La Matanza</gda_value>
+ <gda_value>ARG</gda_value>
+ <gda_value>1266461</gda_value>
+ </gda_array_row>
+ <gda_array_row>
+ <gda_value>Luanda</gda_value>
+ <gda_value>AGO</gda_value>
+ <gda_value>2022000</gda_value>
+ </gda_array_row>
+ <gda_array_row>
+ <gda_value>Melbourne</gda_value>
+ <gda_value>AUS</gda_value>
+ <gda_value>2865329</gda_value>
+ </gda_array_row>
+ <gda_array_row>
+ <gda_value>Perth</gda_value>
+ <gda_value>AUS</gda_value>
+ <gda_value>1096829</gda_value>
+ </gda_array_row>
+ <gda_array_row>
+ <gda_value>Sofija</gda_value>
+ <gda_value>BGR</gda_value>
+ <gda_value>1122302</gda_value>
+ </gda_array_row>
+ <gda_array_row>
+ <gda_value>Sydney</gda_value>
+ <gda_value>AUS</gda_value>
+ <gda_value>3276207</gda_value>
+ </gda_array_row>
+ <gda_array_row>
+ <gda_value>Wien</gda_value>
+ <gda_value>AUT</gda_value>
+ <gda_value>1608144</gda_value>
+ </gda_array_row>
+ <gda_array_row>
+ <gda_value>Yerevan</gda_value>
+ <gda_value>ARM</gda_value>
+ <gda_value>1248700</gda_value>
+ </gda_array_row>
+ </gda_array_data>
+</gda_array>
diff --git a/tests/data-models/cities2.xml b/tests/data-models/cities2.xml
new file mode 100644
index 0000000..afc1112
--- /dev/null
+++ b/tests/data-models/cities2.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0"?>
+<gda_array id="EXPORT" name="Données exportées">
+ <gda_array_field id="FI0" name="name" title="name" dbms_type="string" gdatype="string" nullok="TRUE"/>
+ <gda_array_field id="FI1" name="countrycode" title="countrycode" dbms_type="string" gdatype="string" nullok="TRUE"/>
+ <gda_array_field id="FI2" name="population" title="population" dbms_type="integer" gdatype="int" nullok="TRUE"/>
+ <gda_array_data>
+ <gda_array_row>
+ <gda_value>Buenos Aires</gda_value>
+ <gda_value>ARG</gda_value>
+ <gda_value>2982146</gda_value>
+ </gda_array_row>
+ <gda_array_row>
+ <gda_value>Dhaka</gda_value>
+ <gda_value>BGD</gda_value>
+ <gda_value>3612850</gda_value>
+ </gda_array_row>
+ <gda_array_row>
+ <gda_value>Luanda</gda_value>
+ <gda_value>AGO</gda_value>
+ <gda_value>2022000</gda_value>
+ </gda_array_row>
+ <gda_array_row>
+ <gda_value>Melbourne</gda_value>
+ <gda_value>AUS</gda_value>
+ <gda_value>2865329</gda_value>
+ </gda_array_row>
+ <gda_array_row>
+ <gda_value>Sydney</gda_value>
+ <gda_value>AUS</gda_value>
+ <gda_value>3276207</gda_value>
+ </gda_array_row>
+ </gda_array_data>
+</gda_array>
diff --git a/tests/data-models/cities3.xml b/tests/data-models/cities3.xml
new file mode 100644
index 0000000..7c45c8e
--- /dev/null
+++ b/tests/data-models/cities3.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0"?>
+<gda_array id="EXPORT" name="Données exportées">
+ <gda_array_field id="FI0" name="name" title="name" dbms_type="string" gdatype="string" nullok="TRUE"/>
+ <gda_array_field id="FI1" name="countrycode" title="countrycode" dbms_type="string" gdatype="string" nullok="TRUE"/>
+ <gda_array_field id="FI2" name="population" title="population" dbms_type="integer" gdatype="int" nullok="TRUE"/>
+ <gda_array_data>
+ <gda_array_row>
+ <gda_value>Buenos Aires</gda_value>
+ <gda_value>ARG</gda_value>
+ <gda_value>2982146</gda_value>
+ </gda_array_row>
+ <gda_array_row>
+ <gda_value>Dhaka</gda_value>
+ <gda_value>BGD</gda_value>
+ <gda_value>3000000</gda_value>
+ </gda_array_row>
+ <gda_array_row>
+ <gda_value>Melbourne</gda_value>
+ <gda_value>AUS</gda_value>
+ <gda_value>2865329</gda_value>
+ </gda_array_row>
+ <gda_array_row>
+ <gda_value>Sydney</gda_value>
+ <gda_value>AUS</gda_value>
+ <gda_value>3000000</gda_value>
+ </gda_array_row>
+ </gda_array_data>
+</gda_array>
diff --git a/tests/data-models/countriesA.xml b/tests/data-models/countriesA.xml
new file mode 100644
index 0000000..743fc93
--- /dev/null
+++ b/tests/data-models/countriesA.xml
@@ -0,0 +1,75 @@
+<?xml version="1.0"?>
+<gda_array id="EXPORT" name="Exported Data">
+ <gda_array_field id="FI0" name="code" title="code" dbms_type="string" gdatype="string" nullok="TRUE"/>
+ <gda_array_field id="FI1" name="name" title="name" dbms_type="string" gdatype="string" nullok="TRUE"/>
+ <gda_array_data>
+ <gda_array_row>
+ <gda_value>ABW</gda_value>
+ <gda_value>Aruba</gda_value>
+ </gda_array_row>
+ <gda_array_row>
+ <gda_value>AFG</gda_value>
+ <gda_value>Afghanistan</gda_value>
+ </gda_array_row>
+ <gda_array_row>
+ <gda_value>AGO</gda_value>
+ <gda_value>Angola</gda_value>
+ </gda_array_row>
+ <gda_array_row>
+ <gda_value>AIA</gda_value>
+ <gda_value>Anguilla</gda_value>
+ </gda_array_row>
+ <gda_array_row>
+ <gda_value>ALB</gda_value>
+ <gda_value>Albania</gda_value>
+ </gda_array_row>
+ <gda_array_row>
+ <gda_value>AND</gda_value>
+ <gda_value>Andorra</gda_value>
+ </gda_array_row>
+ <gda_array_row>
+ <gda_value>ANT</gda_value>
+ <gda_value>Netherlands Antilles</gda_value>
+ </gda_array_row>
+ <gda_array_row>
+ <gda_value>ARE</gda_value>
+ <gda_value>United Arab Emirates</gda_value>
+ </gda_array_row>
+ <gda_array_row>
+ <gda_value>ARG</gda_value>
+ <gda_value>Argentina</gda_value>
+ </gda_array_row>
+ <gda_array_row>
+ <gda_value>ARM</gda_value>
+ <gda_value>Armenia</gda_value>
+ </gda_array_row>
+ <gda_array_row>
+ <gda_value>ASM</gda_value>
+ <gda_value>American Samoa</gda_value>
+ </gda_array_row>
+ <gda_array_row>
+ <gda_value>ATA</gda_value>
+ <gda_value>Antarctica</gda_value>
+ </gda_array_row>
+ <gda_array_row>
+ <gda_value>ATF</gda_value>
+ <gda_value>French Southern territories</gda_value>
+ </gda_array_row>
+ <gda_array_row>
+ <gda_value>ATG</gda_value>
+ <gda_value>Antigua and Barbuda</gda_value>
+ </gda_array_row>
+ <gda_array_row>
+ <gda_value>AUS</gda_value>
+ <gda_value>Australia</gda_value>
+ </gda_array_row>
+ <gda_array_row>
+ <gda_value>AUT</gda_value>
+ <gda_value>Austria</gda_value>
+ </gda_array_row>
+ <gda_array_row>
+ <gda_value>AZE</gda_value>
+ <gda_value>Azerbaijan</gda_value>
+ </gda_array_row>
+ </gda_array_data>
+</gda_array>
diff --git a/tests/data-models/countriesB.xml b/tests/data-models/countriesB.xml
new file mode 100644
index 0000000..834ea06
--- /dev/null
+++ b/tests/data-models/countriesB.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0"?>
+<gda_array id="EXPORT" name="Exported Data">
+ <gda_array_field id="FI0" name="code" title="code" dbms_type="string" gdatype="string" nullok="TRUE"/>
+ <gda_array_field id="FI1" name="name" title="name" dbms_type="string" gdatype="string" nullok="TRUE"/>
+ <gda_array_data>
+ <gda_array_row>
+ <gda_value>BDI</gda_value>
+ <gda_value>Burundi</gda_value>
+ </gda_array_row>
+ <gda_array_row>
+ <gda_value>BEL</gda_value>
+ <gda_value>Belgium</gda_value>
+ </gda_array_row>
+ <gda_array_row>
+ <gda_value>BEN</gda_value>
+ <gda_value>Benin</gda_value>
+ </gda_array_row>
+ <gda_array_row>
+ <gda_value>BFA</gda_value>
+ <gda_value>Burkina Faso</gda_value>
+ </gda_array_row>
+ <gda_array_row>
+ <gda_value>BGD</gda_value>
+ <gda_value>Bangladesh</gda_value>
+ </gda_array_row>
+ <gda_array_row>
+ <gda_value>BGR</gda_value>
+ <gda_value>Bulgaria</gda_value>
+ </gda_array_row>
+ </gda_array_data>
+</gda_array>
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]