libgda r3117 - in trunk: . doc/C doc/C/tmpl libgda libgda/sql-parser providers/postgres providers/skel-implementation/capi tests/parser
- From: vivien svn gnome org
- To: svn-commits-list gnome org
- Subject: libgda r3117 - in trunk: . doc/C doc/C/tmpl libgda libgda/sql-parser providers/postgres providers/skel-implementation/capi tests/parser
- Date: Sun, 6 Apr 2008 11:47:58 +0100 (BST)
Author: vivien
Date: Sun Apr 6 11:47:57 2008
New Revision: 3117
URL: http://svn.gnome.org/viewvc/libgda?rev=3117&view=rev
Log:
2008-04-06 Vivien Malerba <malerba gnome-db org>
* libgda/gda-server-operation.c:
* libgda/gda-easy.[ch]: modifications for bug #525877
* tests/parser/Makefile.am:
* tests/parser/check_validation.c:
* tests/parser/check_normalization.c:
* tests/parser/check_dml_comp.c: separated the validation check into a strict validation
check, a DML command computation from a SELECT command and a "normalization" check
* tests/parser/testvalid.xml: more test cases
* libgda/sql-parser/: improved GsaSqlStatement structure check, validation, and added
gda_sql_statement_normalize() which for example will expand all the '*' fields into the actual
list of fields in the table.
* libgda/gda-util.[ch]: added gda_identifier_equal() to compare SQL identifiers in
a safe way (case insensitive comparison unless double quoted) and gda_identifier_hash()
which computes a hash for an identifier (both can be used as base functions for a GHashTable)
* providers/skel-implementation/capi/parser.y:
* providers/postgres/parser.y:
* libgda/sql-parser/parser.y: modified parser to allow the "customers"."id" style syntax
* doc/C: misc doc updates
Added:
trunk/tests/parser/check_dml_comp.c
trunk/tests/parser/check_normalization.c
Modified:
trunk/ChangeLog
trunk/doc/C/libgda-4.0-sections.txt
trunk/doc/C/tmpl/gda-sql-statement.sgml
trunk/doc/C/tmpl/gda-statement.sgml
trunk/libgda/gda-data-model-query.c
trunk/libgda/gda-easy.c
trunk/libgda/gda-easy.h
trunk/libgda/gda-meta-struct.c
trunk/libgda/gda-server-operation.c
trunk/libgda/gda-statement.c
trunk/libgda/gda-statement.h
trunk/libgda/gda-util.c
trunk/libgda/gda-util.h
trunk/libgda/sql-parser/gda-statement-struct-decl.h
trunk/libgda/sql-parser/gda-statement-struct-parts.c
trunk/libgda/sql-parser/gda-statement-struct-parts.h
trunk/libgda/sql-parser/gda-statement-struct-util.c
trunk/libgda/sql-parser/gda-statement-struct-util.h
trunk/libgda/sql-parser/gda-statement-struct.c
trunk/libgda/sql-parser/gda-statement-struct.h
trunk/libgda/sql-parser/parser.y
trunk/providers/postgres/parser.y
trunk/providers/skel-implementation/capi/parser.y
trunk/tests/parser/ (props changed)
trunk/tests/parser/Makefile.am
trunk/tests/parser/check_validation.c
trunk/tests/parser/testvalid.xml
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 Sun Apr 6 11:47:57 2008
@@ -1083,6 +1083,7 @@
gda_statement_is_useless
gda_statement_check_structure
gda_statement_check_validity
+gda_statement_normalize
<SUBSECTION Standard>
GDA_IS_STATEMENT
GDA_STATEMENT
@@ -1144,6 +1145,7 @@
gda_sql_statement_check_structure
gda_sql_statement_check_validity
gda_sql_statement_check_clean
+gda_sql_statement_normalize
<SUBSECTION>
GdaSqlAnyPart
GdaSqlAnyPartType
Modified: trunk/doc/C/tmpl/gda-sql-statement.sgml
==============================================================================
--- trunk/doc/C/tmpl/gda-sql-statement.sgml (original)
+++ trunk/doc/C/tmpl/gda-sql-statement.sgml Sun Apr 6 11:47:57 2008
@@ -146,6 +146,17 @@
@stmt:
+<!-- ##### FUNCTION gda_sql_statement_normalize ##### -->
+<para>
+
+</para>
+
+ stmt:
+ cnc:
+ error:
+ Returns:
+
+
<!-- ##### STRUCT GdaSqlAnyPart ##### -->
<para>
Base structure of which all structures (except #GdaSqlStatement) "inherit". It identifies, for each structure, its type and
@@ -307,12 +318,12 @@
@any: inheritance structure
@distinct: TRUE if a DISTINCT clause applies
@distinct_expr: expression on which the distinct applies, or %NULL
- expr_list: list of expressions, one for each column in the data set returned by the execution of the statement
+ expr_list: list of expressions (as #GdaSqlSelectField pointers), one for each column in the data set returned by the execution of the statement
@from: target(s) of the SELECT statement
@where_cond: condition expression filtering the resulting data set (WHERE condition)
- group_by: grouping expressions
+ group_by: grouping expressions (as #GdaSqlExpr pointers)
@having_cond: condition expression filtering the groupped expressions (HAVING condition)
- order_by: ordering expressions
+ order_by: ordering expressions (as #GdaSqlSelectOrder pointers)
@limit_count: size limiting expression (LIMIT clause)
@limit_offset: when @limit_count is defined, the start offset for the limit
@@ -1078,7 +1089,9 @@
<!-- ##### STRUCT GdaSqlSelectField ##### -->
<para>
This structure represents a selected item in a SELECT statement (when executed, the returned data set
- will have one column per selected item).
+ will have one column per selected item). Note that the @table_name and
+ @table field parts <emphasis>will be</emphasis> overwritten by &LIBGDA;,
+ set the value of @expr->value instead.
</para>
@any: inheritance structure
@@ -1154,7 +1167,9 @@
<!-- ##### STRUCT GdaSqlSelectTarget ##### -->
<para>
This structure represents a target used to fetch data from in a SELECT statement; it can represent a table or
- a sub select.
+ a sub select. Note that the @table_name
+ part <emphasis>will be</emphasis> overwritten by &LIBGDA;,
+ set the value of @expr->value instead.
</para>
@any: inheritance structure
Modified: trunk/doc/C/tmpl/gda-statement.sgml
==============================================================================
--- trunk/doc/C/tmpl/gda-statement.sgml (original)
+++ trunk/doc/C/tmpl/gda-statement.sgml Sun Apr 6 11:47:57 2008
@@ -206,3 +206,14 @@
@Returns:
+<!-- ##### FUNCTION gda_statement_normalize ##### -->
+<para>
+
+</para>
+
+ stmt:
+ cnc:
+ error:
+ Returns:
+
+
Modified: trunk/libgda/gda-data-model-query.c
==============================================================================
--- trunk/libgda/gda-data-model-query.c (original)
+++ trunk/libgda/gda-data-model-query.c Sun Apr 6 11:47:57 2008
@@ -29,8 +29,10 @@
#include <libgda/gda-data-model-extra.h>
#include <libgda/gda-data-model-iter.h>
#include <libgda/gda-statement.h>
+#include <libgda/gda-meta-struct.h>
#include <libgda/gda-set.h>
#include <libgda/gda-holder.h>
+#include <libgda/gda-util.h>
#include <sql-parser/gda-sql-statement.h>
struct _GdaDataModelQueryPrivate {
@@ -427,10 +429,6 @@
}
if (qindex == SEL_QUERY) {
/* SELECT statement */
-
- /* expand any target.* field => add a gda_sql_statement_select_expand_all () method */
- TO_IMPLEMENT;
-
if (model->priv->params[qindex])
g_signal_connect (model->priv->params[qindex], "holder_changed",
G_CALLBACK (holder_changed_cb), model);
@@ -777,8 +775,6 @@
static void
create_columns (GdaDataModelQuery *model)
{
- GSList *fields = NULL;
-
if (model->priv->columns)
return;
if (! model->priv->statements[SEL_QUERY])
@@ -788,30 +784,30 @@
* statement's GdaSqlSelectFields and if a name and GType can be determined, then use them, otherwise
* use "column%d" and G_TYPE_STRING, and let the user modify that directly using gda_data_model_describe ()
*/
- TO_IMPLEMENT;
- if (0) {
+ GdaSqlStatement *sqlst;
+ g_object_get (G_OBJECT (model->priv->statements[SEL_QUERY]), "structure", &sqlst, NULL);
+
+ if (gda_sql_statement_normalize (sqlst, model->priv->cnc, NULL)) {
+ GdaSqlStatementSelect *selst = (GdaSqlStatementSelect*) sqlst->contents;
GSList *list;
- gboolean allok = TRUE;
- for (list = fields; list; list = list->next) {
- }
- if (! allok)
- return;
-
- list = fields;
- while (list) {
+ for (list = selst->expr_list; list; list = list->next) {
+ GdaSqlSelectField *field = (GdaSqlSelectField*) list->data;
GdaColumn *col;
col = gda_column_new ();
- gda_column_set_name (col, "col");
- gda_column_set_title (col, "col");
- gda_column_set_g_type (col, G_TYPE_STRING);
+ if (field->validity_meta_table_column) {
+ gda_column_set_name (col, field->validity_meta_table_column->column_name);
+ gda_column_set_title (col, field->validity_meta_table_column->column_name);
+ gda_column_set_g_type (col, field->validity_meta_table_column->gtype);
+ }
+ else {
+ gda_column_set_name (col, "col");
+ gda_column_set_title (col, "col");
+ gda_column_set_g_type (col, G_TYPE_STRING);
+ }
model->priv->columns = g_slist_append (model->priv->columns, col);
-
- list = g_slist_next (list);
}
-
- g_slist_free (fields);
}
else {
if (model->priv->data) {
@@ -835,6 +831,7 @@
model->priv->emit_reset = TRUE;
}
}
+ gda_sql_statement_free (sqlst);
if (model->priv->columns && model->priv->emit_reset) {
model->priv->emit_reset = FALSE;
@@ -1474,12 +1471,15 @@
* If @target is %NULL, then an error will be returned if @model's SELECT query has more than
* one target.
*
- * Returns: TRUE if the INSERT, DELETE and UPDATE statements have been computed.
+ * Returns: TRUE at least one of the INSERT, DELETE and UPDATE statements has been computed
*/
gboolean
gda_data_model_query_compute_modification_queries (GdaDataModelQuery *model, const gchar *target,
GdaDataModelQueryOptions options, GError **error)
{
+ gint i;
+ gboolean require_pk = TRUE;
+ GdaStatement *ins, *upd, *del;
g_return_val_if_fail (GDA_IS_DATA_MODEL_QUERY (model), FALSE);
g_return_val_if_fail (model->priv, FALSE);
@@ -1490,6 +1490,23 @@
return FALSE;
}
- TO_IMPLEMENT;
+ for (i = SEL_QUERY; i <= DEL_QUERY; i++) {
+ if (model->priv->statements[i])
+ forget_statement (model, model->priv->statements[i]);
+ }
+
+ if (options & GDA_DATA_MODEL_QUERY_OPTION_USE_ALL_FIELDS_IF_NO_PK)
+ require_pk = FALSE;
+ if (gda_compute_dml_statements (model->priv->cnc, model->priv->statements[SEL_QUERY], require_pk,
+ &ins, &upd, &del, error)) {
+
+ g_object_set (G_OBJECT (model), "insert_query", ins,
+ "update-query", upd, "delete-query", del, NULL);
+ if (ins) g_object_unref (ins);
+ if (upd) g_object_unref (upd);
+ if (del) g_object_unref (del);
+ return TRUE;
+ }
+
return FALSE;
}
Modified: trunk/libgda/gda-easy.c
==============================================================================
--- trunk/libgda/gda-easy.c (original)
+++ trunk/libgda/gda-easy.c Sun Apr 6 11:47:57 2008
@@ -64,7 +64,7 @@
op = gda_server_provider_create_operation (prov, NULL, GDA_SERVER_OPERATION_CREATE_DB,
NULL, error);
if (op) {
- g_object_set_data (G_OBJECT (op), "_gda_provider_obj", prov);
+ g_object_set_data_full (G_OBJECT (op), "_gda_provider_obj", g_object_ref (prov), g_object_unref);
if (db_name)
gda_server_operation_set_value_at (op, db_name, NULL, "/DB_DEF_P/DB_NAME");
}
@@ -95,9 +95,8 @@
if (provider)
return gda_server_provider_perform_operation (provider, NULL, op, error);
else {
- g_set_error (error, GDA_CONNECTION_ERROR, GDA_CONNECTION_PROVIDER_NOT_FOUND_ERROR,
- _("Could not find operation's associated provider, "
- "did you use gda_prepare_create_database() ?"));
+ g_warning ("Could not find operation's associated provider, "
+ "did you use gda_prepare_create_database() ?");
return FALSE;
}
}
@@ -132,7 +131,7 @@
op = gda_server_provider_create_operation (prov, NULL, GDA_SERVER_OPERATION_DROP_DB,
NULL, error);
if (op) {
- g_object_set_data (G_OBJECT (op), "_gda_provider_obj", prov);
+ g_object_set_data_full (G_OBJECT (op), "_gda_provider_obj", g_object_ref (prov), g_object_unref);
if (db_name)
gda_server_operation_set_value_at (op, db_name, NULL, "/DB_DESC_P/DB_NAME");
}
@@ -163,9 +162,8 @@
if (provider)
return gda_server_provider_perform_operation (provider, NULL, op, error);
else {
- g_set_error (error, GDA_CONNECTION_ERROR, GDA_CONNECTION_PROVIDER_NOT_FOUND_ERROR,
- _("Could not find operation's associated provider, "
- "did you use gda_prepare_drop_database() ?"));
+ g_warning ("Could not find operation's associated provider, "
+ "did you use gda_prepare_drop_database() ?");
return FALSE;
}
}
@@ -264,7 +262,7 @@
g_return_val_if_fail (gda_connection_is_opened (cnc), FALSE);
- server = gda_connection_get_provider_obj(cnc);
+ server = gda_connection_get_provider_obj (cnc);
if (!table_name) {
g_set_error (error, GDA_EASY_ERROR, GDA_EASY_OBJECT_NAME_ERROR,
@@ -318,8 +316,7 @@
va_end (args);
- g_object_set_data (G_OBJECT (op), "_gda_connection", cnc);
- g_object_set_data (G_OBJECT (op), "_gda_provider_obj", server);
+ g_object_set_data_full (G_OBJECT (op), "_gda_connection", g_object_ref (cnc), g_object_unref);
return op;
}
@@ -345,16 +342,18 @@
gda_perform_create_table (GdaServerOperation *op, GError **error)
{
GdaConnection *cnc;
- GdaServerProvider *server;
g_return_val_if_fail (GDA_IS_SERVER_OPERATION (op), FALSE);
- g_return_val_if_fail (gda_server_operation_get_op_type (op) ==
- GDA_SERVER_OPERATION_CREATE_TABLE, FALSE);
+ g_return_val_if_fail (gda_server_operation_get_op_type (op) == GDA_SERVER_OPERATION_CREATE_TABLE, FALSE);
- server = GDA_SERVER_PROVIDER (g_object_get_data (G_OBJECT (op), "_gda_provider_obj"));
- cnc = GDA_CONNECTION (g_object_get_data (G_OBJECT (op), "_gda_connection"));
-
- return gda_server_provider_perform_operation (server, cnc, op, error);
+ cnc = g_object_get_data (G_OBJECT (op), "_gda_connection");
+ if (cnc)
+ return gda_server_provider_perform_operation (gda_connection_get_provider_obj (cnc), cnc, op, error);
+ else {
+ g_warning ("Could not find operation's associated connection, "
+ "did you use gda_prepare_create_table() ?");
+ return FALSE;
+ }
}
/**
@@ -386,8 +385,7 @@
if (gda_server_operation_set_value_at (op, table_name,
error, "/TABLE_DESC_P/TABLE_NAME")) {
- g_object_set_data (G_OBJECT (op), "_gda_connection", cnc);
- g_object_set_data (G_OBJECT (op), "_gda_provider_obj", server);
+ g_object_set_data_full (G_OBJECT (op), "_gda_connection", g_object_ref (cnc), g_object_unref);
return op;
}
else
@@ -410,16 +408,18 @@
gda_perform_drop_table (GdaServerOperation *op, GError **error)
{
GdaConnection *cnc;
- GdaServerProvider *server;
-
- g_return_val_if_fail (GDA_IS_SERVER_OPERATION (op), FALSE);
- g_return_val_if_fail (gda_server_operation_get_op_type (op) ==
- GDA_SERVER_OPERATION_DROP_TABLE, FALSE);
- server = GDA_SERVER_PROVIDER (g_object_get_data (G_OBJECT (op), "_gda_provider_obj"));
- cnc = GDA_CONNECTION (g_object_get_data (G_OBJECT (op), "_gda_connection"));
+ g_return_val_if_fail (GDA_IS_SERVER_OPERATION (op), FALSE);
+ g_return_val_if_fail (gda_server_operation_get_op_type (op) == GDA_SERVER_OPERATION_DROP_TABLE, FALSE);
- return gda_server_provider_perform_operation (server, cnc, op, error);
+ cnc = g_object_get_data (G_OBJECT (op), "_gda_connection");
+ if (cnc)
+ return gda_server_provider_perform_operation (gda_connection_get_provider_obj (cnc), cnc, op, error);
+ else {
+ g_warning ("Could not find operation's associated connection, "
+ "did you use gda_prepare_drop_table() ?");
+ return FALSE;
+ }
}
static guint
Modified: trunk/libgda/gda-easy.h
==============================================================================
--- trunk/libgda/gda-easy.h (original)
+++ trunk/libgda/gda-easy.h Sun Apr 6 11:47:57 2008
@@ -77,7 +77,7 @@
/*
* Tables creation and destruction
*/
-GdaServerOperation *gda_prepare_create_table (GdaConnection *cnc, const gchar *table_name, GError **error, ...);
+GdaServerOperation *gda_prepare_create_table (GdaConnection *cnc, const gchar *table_name, GError **error, ...);
gboolean gda_perform_create_table (GdaServerOperation *op, GError **error);
GdaServerOperation *gda_prepare_drop_table (GdaConnection *cnc, const gchar *table_name, GError **error);
gboolean gda_perform_drop_table (GdaServerOperation *op, GError **error);
Modified: trunk/libgda/gda-meta-struct.c
==============================================================================
--- trunk/libgda/gda-meta-struct.c (original)
+++ trunk/libgda/gda-meta-struct.c Sun Apr 6 11:47:57 2008
@@ -793,9 +793,9 @@
for (list = mstruct->db_objects; list; list = list->next) {
GdaMetaDbObject *dbo;
dbo = GDA_META_DB_OBJECT (list->data);
- if (!strcmp (dbo->obj_name, obj_name) &&
- (!obj_schema || !strcmp (dbo->obj_schema, obj_schema)) &&
- (!obj_catalog || !strcmp (dbo->obj_catalog, obj_catalog)))
+ if (gda_identifier_equal (dbo->obj_name, obj_name) &&
+ (!obj_schema || gda_identifier_equal (dbo->obj_schema, obj_schema)) &&
+ (!obj_catalog || gda_identifier_equal (dbo->obj_catalog, obj_catalog)))
matching = g_slist_prepend (matching, dbo);
}
@@ -828,6 +828,7 @@
{
GSList *list;
const gchar *cname;
+
g_return_val_if_fail (GDA_IS_META_STRUCT (mstruct), NULL);
g_return_val_if_fail (table, NULL);
g_return_val_if_fail (col_name && (G_VALUE_TYPE (col_name) == G_TYPE_STRING), NULL);
@@ -835,7 +836,7 @@
for (list = table->columns; list; list = list->next) {
GdaMetaTableColumn *tcol = GDA_META_TABLE_COLUMN (list->data);
- if (!strcmp (tcol->column_name, cname))
+ if (gda_identifier_equal (tcol->column_name, cname))
return tcol;
}
return NULL;
Modified: trunk/libgda/gda-server-operation.c
==============================================================================
--- trunk/libgda/gda-server-operation.c (original)
+++ trunk/libgda/gda-server-operation.c Sun Apr 6 11:47:57 2008
@@ -166,11 +166,11 @@
g_object_class_install_property (object_class, PROP_CNC,
g_param_spec_object ("connection", NULL, NULL,
GDA_TYPE_CONNECTION,
- G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
+ G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property (object_class, PROP_PROV,
g_param_spec_object ("provider_obj", NULL, NULL,
- GDA_TYPE_SERVER_PROVIDER,
- G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
+ GDA_TYPE_SERVER_PROVIDER,
+ G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property (object_class, PROP_SPEC_FILE,
g_param_spec_string ("spec_file", NULL, NULL,
NULL, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
@@ -511,7 +511,7 @@
if (op->priv->cnc)
g_object_unref (op->priv->cnc);
- op->priv->cnc = GDA_CONNECTION( g_value_get_object (value));
+ op->priv->cnc = GDA_CONNECTION (g_value_get_object (value));
op->priv->cnc_set = TRUE;
if (op->priv->cnc) {
@@ -641,6 +641,12 @@
op = GDA_SERVER_OPERATION (object);
if (op->priv) {
switch (param_id) {
+ case PROP_CNC:
+ g_value_set_object (value, op->priv->cnc);
+ break;
+ case PROP_PROV:
+ g_value_set_object (value, op->priv->prov);
+ break;
case PROP_OP_TYPE:
g_value_set_int (value, op->priv->op_type);
break;
Modified: trunk/libgda/gda-statement.c
==============================================================================
--- trunk/libgda/gda-statement.c (original)
+++ trunk/libgda/gda-statement.c Sun Apr 6 11:47:57 2008
@@ -393,6 +393,27 @@
}
/**
+ * gda_statement_normalize
+ * @stmt: a #GdaStatement object
+ * @cnc: a #GdaConnection object
+ * @error: a place to store errors, or %NULL
+ *
+ * "Normalizes" some parts of @stmt, see gda_sql_statement_normalize() for more
+ * information.
+ *
+ * Returns: TRUE if no error occurred
+ */
+gboolean
+gda_statement_normalize (GdaStatement *stmt, GdaConnection *cnc, GError **error)
+{
+ g_return_val_if_fail (GDA_IS_STATEMENT (stmt), FALSE);
+ g_return_val_if_fail (stmt->priv, FALSE);
+ g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
+
+ return gda_sql_statement_normalize (stmt->priv->internal_struct, cnc, error);
+}
+
+/**
* gda_statement_serialize
* @stmt: a #GdaStatement object
*
Modified: trunk/libgda/gda-statement.h
==============================================================================
--- trunk/libgda/gda-statement.h (original)
+++ trunk/libgda/gda-statement.h Sun Apr 6 11:47:57 2008
@@ -97,6 +97,7 @@
gboolean gda_statement_is_useless (GdaStatement *stmt);
gboolean gda_statement_check_structure (GdaStatement *stmt, GError **error);
gboolean gda_statement_check_validity (GdaStatement *stmt, GdaConnection *cnc, GError **error);
+gboolean gda_statement_normalize (GdaStatement *stmt, GdaConnection *cnc, GError **error);
G_END_DECLS
Modified: trunk/libgda/gda-util.c
==============================================================================
--- trunk/libgda/gda-util.c (original)
+++ trunk/libgda/gda-util.c Sun Apr 6 11:47:57 2008
@@ -607,6 +607,128 @@
}
/*
+ * Checks related to the structure of the SELECT statement before computing the INSERT, UPDATE and DELETE
+ * corresponding statements.
+ */
+static gboolean
+dml_statements_check_select_structure (GdaConnection *cnc, GdaSqlStatement *sel_struct, GError **error)
+{
+ GdaSqlStatementSelect *stsel;
+ stsel = (GdaSqlStatementSelect*) sel_struct->contents;
+ if (!stsel->from) {
+ g_set_error (error, GDA_SQL_ERROR, GDA_SQL_STRUCTURE_CONTENTS_ERROR,
+ _("SELECT statement has no FROM part"));
+ return FALSE;
+ }
+ if (stsel->from->targets && stsel->from->targets->next) {
+ g_set_error (error, GDA_SQL_ERROR, GDA_SQL_STRUCTURE_CONTENTS_ERROR,
+ _("SELECT statement involves more than one table or expression"));
+ return FALSE;
+ }
+ GdaSqlSelectTarget *target;
+ target = (GdaSqlSelectTarget*) stsel->from->targets->data;
+ if (!target->table_name) {
+ g_set_error (error, GDA_SQL_ERROR, GDA_SQL_STRUCTURE_CONTENTS_ERROR,
+ _("SELECT statement involves more than one table or expression"));
+ return FALSE;
+ }
+ if (!gda_sql_statement_check_validity (sel_struct, cnc, error))
+ return FALSE;
+
+ /* check that we want to modify a table */
+ g_assert (target->validity_meta_object); /* because gda_sql_statement_check_validity() returned TRUE */
+ if (target->validity_meta_object->obj_type != GDA_META_DB_TABLE) {
+ g_set_error (error, GDA_SQL_ERROR, GDA_SQL_STRUCTURE_CONTENTS_ERROR,
+ _("Can only build modification statement for tables"));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/*
+ * Builds the WHERE condition of the computed DML statement
+ */
+static GdaSqlExpr*
+dml_statements_build_condition (GdaSqlStatementSelect *stsel, GdaMetaTable *mtable, gboolean require_pk, GError **error)
+{
+ gint i;
+ GdaSqlExpr *expr;
+ GdaSqlOperation *and_cond = NULL;
+
+ if (mtable->pk_cols_nb == 0) {
+ g_set_error (error, GDA_SQL_ERROR, GDA_SQL_STRUCTURE_CONTENTS_ERROR,
+ _("Table does not have any primary key"));
+ return NULL;
+ }
+
+ expr = gda_sql_expr_new (NULL); /* no parent */
+ if (require_pk) {
+ if (mtable->pk_cols_nb == 0) {
+ g_set_error (error, GDA_SQL_ERROR, GDA_SQL_STRUCTURE_CONTENTS_ERROR,
+ _("Table does not have any primary key"));
+ goto onerror;
+ }
+ else if (mtable->pk_cols_nb > 1) {
+ and_cond = gda_sql_operation_new (GDA_SQL_ANY_PART (expr));
+ and_cond->operator = GDA_SQL_OPERATOR_AND;
+ expr->cond = and_cond;
+ }
+ for (i = 0; i < mtable->pk_cols_nb; i++) {
+ GdaSqlSelectField *sfield = NULL;
+ GdaMetaTableColumn *tcol;
+ GSList *list;
+
+ tcol = (GdaMetaTableColumn *) g_slist_nth_data (mtable->columns, mtable->pk_cols_array[i]);
+ for (list = stsel->expr_list; list; list = list->next) {
+ sfield = (GdaSqlSelectField *) list->data;
+ if (sfield->validity_meta_table_column == tcol)
+ break;
+ else
+ sfield = NULL;
+ }
+ if (!sfield) {
+ g_set_error (error, GDA_SQL_ERROR, GDA_SQL_STRUCTURE_CONTENTS_ERROR,
+ _("Table's primary key is not selected"));
+ goto onerror;
+ }
+ else {
+ GdaSqlOperation *op;
+ GdaSqlExpr *opexpr;
+ GdaSqlParamSpec *pspec;
+
+ /* equal condition */
+ op = gda_sql_operation_new (GDA_SQL_ANY_PART (and_cond ? (gpointer)and_cond : (gpointer)expr));
+ op->operator = GDA_SQL_OPERATOR_EQ;
+ if (and_cond)
+ and_cond->operands = g_slist_append (and_cond->operands, op);
+ else
+ expr->cond = op;
+ /* left operand */
+ opexpr = gda_sql_expr_new (GDA_SQL_ANY_PART (op));
+ g_value_set_string (opexpr->value = gda_value_new (G_TYPE_STRING), tcol->column_name);
+ op->operands = g_slist_append (op->operands, opexpr);
+
+ /* right operand */
+ opexpr = gda_sql_expr_new (GDA_SQL_ANY_PART (op));
+ pspec = g_new0 (GdaSqlParamSpec, 1);
+ pspec->name = g_strdup_printf ("-%d", mtable->pk_cols_array[i]);
+ pspec->g_type = tcol->gtype != G_TYPE_INVALID ? tcol->gtype: G_TYPE_STRING;
+ pspec->type = g_strdup (gda_g_type_to_string (pspec->g_type));
+ pspec->nullok = tcol->nullok;
+ opexpr->param_spec = pspec;
+ op->operands = g_slist_append (op->operands, opexpr);
+ }
+ }
+ }
+ return expr;
+
+ onerror:
+ gda_sql_expr_free (expr);
+ return NULL;
+}
+
+/*
* Statement computation from meta store
* @select_stmt: must be a SELECT statement (compound statements not handled)
*/
@@ -621,7 +743,11 @@
GdaStatement *ret_delete = NULL;
GdaMetaStruct *mstruct;
gboolean retval = TRUE;
- GdaMetaTable *mtable;
+ GdaSqlSelectTarget *target;
+
+ GdaSqlStatementInsert *ist = NULL;
+ GdaSqlStatementUpdate *ust = NULL;
+ GdaSqlStatementDelete *dst = NULL;
g_return_val_if_fail (GDA_IS_CONNECTION (cnc), FALSE);
g_return_val_if_fail (GDA_IS_STATEMENT (select_stmt), FALSE);
@@ -630,28 +756,13 @@
/*g_print ("*** %s\n", gda_statement_serialize (select_stmt));*/
g_object_get (G_OBJECT (select_stmt), "structure", &sel_struct, NULL);
- stsel = (GdaSqlStatementSelect*) sel_struct->contents;
- if (!stsel->from) {
- g_set_error (error, GDA_SQL_ERROR, GDA_SQL_STRUCTURE_CONTENTS_ERROR,
- _("SELECT statement has no FROM part"));
- retval = FALSE;
- goto cleanup;
- }
- if (stsel->from->targets && stsel->from->targets->next) {
- g_set_error (error, GDA_SQL_ERROR, GDA_SQL_STRUCTURE_CONTENTS_ERROR,
- _("SELECT statement involves more than one table or expression"));
- retval = FALSE;
- goto cleanup;
- }
- GdaSqlSelectTarget *target;
- target = (GdaSqlSelectTarget*) stsel->from->targets->data;
- if (!target->table_name) {
- g_set_error (error, GDA_SQL_ERROR, GDA_SQL_STRUCTURE_CONTENTS_ERROR,
- _("SELECT statement involves more than one table or expression"));
+ if (!dml_statements_check_select_structure (cnc, sel_struct, error)) {
retval = FALSE;
goto cleanup;
}
- if (!gda_sql_statement_check_validity (sel_struct, cnc, error)) {
+
+ /* normalize the statement */
+ if (!gda_sql_statement_normalize (sel_struct, cnc, error)) {
retval = FALSE;
goto cleanup;
}
@@ -659,34 +770,11 @@
mstruct = sel_struct->validity_meta_struct;
g_assert (mstruct); /* because gda_sql_statement_check_validity() returned TRUE */
- /* check that we want to modify a table */
- if (target->validity_meta_object->obj_type != GDA_META_DB_TABLE) {
- g_set_error (error, GDA_SQL_ERROR, GDA_SQL_STRUCTURE_CONTENTS_ERROR,
- _("Can only build modification statement for tables"));
- retval = FALSE;
- goto cleanup;
- }
-
/* check that the condition will be buildable */
- mtable = GDA_META_DB_OBJECT_GET_TABLE (target->validity_meta_object);
- if ((update || delete) && require_pk) {
- gint i;
- if (mtable->pk_cols_nb == 0) {
- g_set_error (error, GDA_SQL_ERROR, GDA_SQL_STRUCTURE_CONTENTS_ERROR,
- _("Table does not have any primary key"));
- retval = FALSE;
- goto cleanup;
- }
- for (i = 0; i < mtable->pk_cols_nb; i++) {
- TO_IMPLEMENT;
- }
- }
+ stsel = (GdaSqlStatementSelect*) sel_struct->contents;
+ target = (GdaSqlSelectTarget*) stsel->from->targets->data;
- /* actual statement structure's computation */
- GdaSqlStatementInsert *ist;
- GdaSqlStatementUpdate *ust;
- GdaSqlStatementDelete *dst;
-
+ /* actual statement structure's computation */
if (insert) {
ist = g_new0 (GdaSqlStatementInsert, 1);
GDA_SQL_ANY_PART (ist)->type = GDA_SQL_ANY_STMT_INSERT;
@@ -699,6 +787,14 @@
GDA_SQL_ANY_PART (ust)->type = GDA_SQL_ANY_STMT_UPDATE;
ust->table = gda_sql_table_new (GDA_SQL_ANY_PART (ust));
ust->table->table_name = g_strdup ((gchar *) target->table_name);
+ ust->cond = dml_statements_build_condition (stsel,
+ GDA_META_DB_OBJECT_GET_TABLE (target->validity_meta_object),
+ require_pk, error);
+ if (!ust->cond) {
+ retval = FALSE;
+ goto cleanup;
+ }
+ GDA_SQL_ANY_PART (ust->cond)->parent = GDA_SQL_ANY_PART (ust);
}
if (delete) {
@@ -706,27 +802,28 @@
GDA_SQL_ANY_PART (dst)->type = GDA_SQL_ANY_STMT_DELETE;
dst->table = gda_sql_table_new (GDA_SQL_ANY_PART (dst));
dst->table->table_name = g_strdup ((gchar *) target->table_name);
+ dst->cond = dml_statements_build_condition (stsel,
+ GDA_META_DB_OBJECT_GET_TABLE (target->validity_meta_object),
+ require_pk, error);
+ if (!dst->cond) {
+ retval = FALSE;
+ goto cleanup;
+ }
+ GDA_SQL_ANY_PART (dst->cond)->parent = GDA_SQL_ANY_PART (dst);
}
GSList *expr_list;
gint colindex;
GSList *insert_values_list = NULL;
GHashTable *fields_hash; /* key = a table's field's name, value = we don't care */
- fields_hash = g_hash_table_new (g_str_hash, g_str_equal);
+ fields_hash = g_hash_table_new ((GHashFunc) gda_identifier_hash, (GEqualFunc) gda_identifier_equal);
for (expr_list = stsel->expr_list, colindex = 0;
expr_list;
expr_list = expr_list->next, colindex++) {
GdaSqlSelectField *selfield = (GdaSqlSelectField *) expr_list->data;
- if (selfield->expr && selfield->expr->value &&
- !strcmp (g_value_get_string (selfield->expr->value), "*")) {
- TO_IMPLEMENT;
+ if ((selfield->validity_meta_object != target->validity_meta_object) ||
+ !selfield->validity_meta_table_column)
continue;
- }
- else {
- if ((selfield->validity_meta_object != target->validity_meta_object) ||
- !selfield->validity_meta_table_column)
- continue;
- }
/* field to insert into */
if (g_hash_table_lookup (fields_hash, selfield->field_name))
@@ -775,9 +872,18 @@
/* finish the statements */
if (insert) {
GdaSqlStatement *st;
+
+ if (!ist->fields_list) {
+ /* nothing to insert => don't create statement */
+ g_set_error (error, GDA_SQL_ERROR, GDA_SQL_STRUCTURE_CONTENTS_ERROR,
+ _("Could not compute any field to insert into"));
+ retval = FALSE;
+ goto cleanup;
+ }
ist->values_list = g_slist_append (NULL, insert_values_list);
st = gda_sql_statement_new (GDA_SQL_STATEMENT_INSERT);
st->contents = ist;
+ ist = NULL;
ret_insert = g_object_new (GDA_TYPE_STATEMENT, "structure", st, NULL);
gda_sql_statement_free (st);
}
@@ -785,6 +891,7 @@
GdaSqlStatement *st;
st = gda_sql_statement_new (GDA_SQL_STATEMENT_UPDATE);
st->contents = ust;
+ ust = NULL;
ret_update = g_object_new (GDA_TYPE_STATEMENT, "structure", st, NULL);
gda_sql_statement_free (st);
}
@@ -792,12 +899,29 @@
GdaSqlStatement *st;
st = gda_sql_statement_new (GDA_SQL_STATEMENT_DELETE);
st->contents = dst;
+ dst = NULL;
ret_delete = g_object_new (GDA_TYPE_STATEMENT, "structure", st, NULL);
gda_sql_statement_free (st);
}
cleanup:
gda_sql_statement_free (sel_struct);
+ if (ist) {
+ GdaSqlStatementContentsInfo *cinfo;
+ cinfo = gda_sql_statement_get_contents_infos (GDA_SQL_STATEMENT_INSERT);
+ cinfo->free (ist);
+ }
+ if (ust) {
+ GdaSqlStatementContentsInfo *cinfo;
+ cinfo = gda_sql_statement_get_contents_infos (GDA_SQL_STATEMENT_UPDATE);
+ cinfo->free (ust);
+ }
+ if (dst) {
+ GdaSqlStatementContentsInfo *cinfo;
+ cinfo = gda_sql_statement_get_contents_infos (GDA_SQL_STATEMENT_DELETE);
+ cinfo->free (dst);
+ }
+
if (insert)
*insert = ret_insert;
if (update)
@@ -806,3 +930,71 @@
*delete = ret_delete;
return retval;
}
+
+/*
+ * computes a hash string from @is, to be used in hash tables as a GHashFunc
+ */
+guint
+gda_identifier_hash (const gchar *id)
+{
+ const signed char *p = (signed char *) id;
+ guint32 h = 0;
+ gboolean lower = FALSE;
+
+ if (*p != '"') {
+ lower = TRUE;
+ h = g_ascii_tolower (*p);
+ }
+
+ for (p += 1; *p && *p != '"'; p++) {
+ if (lower)
+ h = (h << 5) - h + g_ascii_tolower (*p);
+ else
+ h = (h << 5) - h + *p;
+ }
+ if (*p == '"' && *(p+1))
+ g_warning ("Argument passed to %s() is not an SQL identifier", __FUNCTION__);
+
+ return h;
+}
+
+/*
+ * Does the same as strcmp(id1, id2), but handles the case where id1 and/or id2 are enclosed in double quotes,
+ * can also be used in hash tables as a GEqualFunc
+ */
+gboolean
+gda_identifier_equal (const gchar *id1, const gchar *id2)
+{
+ const gchar *ptr1, *ptr2;
+ gboolean dq1 = FALSE, dq2 = FALSE;
+
+ ptr1 = id1;
+ if (*ptr1 == '"') {
+ ptr1++;
+ dq1 = TRUE;
+ }
+ ptr2 = id2;
+ if (*ptr2 == '"') {
+ ptr2++;
+ dq2 = TRUE;
+ }
+ for (; *ptr1 && *ptr2; ptr1++, ptr2++) {
+ gchar c1, c2;
+ c1 = *ptr1;
+ c2 = *ptr2;
+ if (!dq1)
+ c1 = g_ascii_tolower (c1);
+ if (!dq2)
+ c2 = g_ascii_tolower (c2);
+ if (c1 != c2)
+ return FALSE;
+ }
+ if (*ptr1 || *ptr2) {
+ if (*ptr1 && (*ptr1 == '"'))
+ return TRUE;
+ if (*ptr2 && (*ptr2 == '"'))
+ return TRUE;
+ return FALSE;
+ }
+ return TRUE;
+}
Modified: trunk/libgda/gda-util.h
==============================================================================
--- trunk/libgda/gda-util.h (original)
+++ trunk/libgda/gda-util.h Sun Apr 6 11:47:57 2008
@@ -43,6 +43,8 @@
*/
gchar *gda_default_escape_string (const gchar *string);
gchar *gda_default_unescape_string (const gchar *string);
+guint gda_identifier_hash (const gchar *id);
+gboolean gda_identifier_equal (const gchar *id1, const gchar *id2);
/*
* Param & model utilities
Modified: trunk/libgda/sql-parser/gda-statement-struct-decl.h
==============================================================================
--- trunk/libgda/sql-parser/gda-statement-struct-decl.h (original)
+++ trunk/libgda/sql-parser/gda-statement-struct-decl.h Sun Apr 6 11:47:57 2008
@@ -33,6 +33,7 @@
typedef enum {
GDA_SQL_STRUCTURE_CONTENTS_ERROR,
GDA_SQL_MALFORMED_IDENTIFIER_ERROR,
+ GDA_SQL_MISSING_IDENTIFIER_ERROR,
GDA_SQL_VALIDATION_ERROR
} GdaSqlErrorType;
Modified: trunk/libgda/sql-parser/gda-statement-struct-parts.c
==============================================================================
--- trunk/libgda/sql-parser/gda-statement-struct-parts.c (original)
+++ trunk/libgda/sql-parser/gda-statement-struct-parts.c Sun Apr 6 11:47:57 2008
@@ -840,24 +840,8 @@
field->expr = expr;
gda_sql_any_part_set_parent (field->expr, field);
- if (expr && expr->value) {
- const gchar *str;
- gchar *ptr;
-
- str = g_value_get_string (expr->value);
- if (_string_is_identifier (str)) {
- for (ptr = (gchar *) str; *ptr; ptr++);
- for (; (ptr > str) && (*ptr != '.'); ptr --);
- if (ptr != str) {
- field->field_name = g_strdup (ptr + 1);
- *ptr = 0;
- field->table_name = g_strdup (str);
- *ptr = '.';
- }
- else
- field->field_name = g_strdup (ptr);
- }
- }
+ if (expr && expr->value)
+ _split_identifier_string (g_value_dup_string (expr->value), &(field->table_name), &(field->field_name));
}
void
Modified: trunk/libgda/sql-parser/gda-statement-struct-parts.h
==============================================================================
--- trunk/libgda/sql-parser/gda-statement-struct-parts.h (original)
+++ trunk/libgda/sql-parser/gda-statement-struct-parts.h Sun Apr 6 11:47:57 2008
@@ -189,7 +189,7 @@
gchar *gda_sql_case_serialize (GdaSqlCase *scase);
/*
- * Any expression in a SELECT ... before the FROM
+ * Any expression in a SELECT ... before the FROM clause
*/
struct _GdaSqlSelectField
{
Modified: trunk/libgda/sql-parser/gda-statement-struct-util.c
==============================================================================
--- trunk/libgda/sql-parser/gda-statement-struct-util.c (original)
+++ trunk/libgda/sql-parser/gda-statement-struct-util.c Sun Apr 6 11:47:57 2008
@@ -249,9 +249,14 @@
if (!str || !(*str))
return FALSE;
- for (ptr = (gchar *) str; IdChar(*ptr) || (*ptr == '*') || (*ptr == '.'); ptr++);
+ for (ptr = *str == '"' ? str + 1 : str;
+ IdChar(*ptr) || (*ptr == '*') || (*ptr == '.') || ((*ptr == '"') && ptr[1] == 0);
+ ptr++);
if (*ptr)
return FALSE;
+ if ((*str == '"') && (ptr[-1] == '"'))
+ return TRUE;
+
/* @str is composed only of character that can be used in an identifier */
d = g_ascii_strtod (str, &endptr);
if (!*endptr)
@@ -259,3 +264,78 @@
return FALSE;
return TRUE;
}
+
+/*
+ * Reuses @str and fills in @remain and @last
+ *
+ * @str "swallowed" by the function and must be allocated memory, and @remain and @last are
+ * also allocated memory.
+ *
+ * Accepted notations for each individual part:
+ * - aaa
+ * - "aaa"
+ *
+ * So "aaa".bbb, aaa.bbb, "aaa"."bbb", aaa."bbb" are Ok.
+ *
+ * Returns TRUE:
+ * if @str has the <part1>.<part2> form, then @last will contain <part2> and @remain will contain <part1>
+ * if @str has the <part1> form, then @last will contain <part1> and @remain will contain NULL
+ *
+ * Returns FALSE:
+ * if @str is NULL:
+ * if @str is "":
+ * if @str is malformed:
+ * @last and @remain will contain NULL
+ */
+gboolean
+_split_identifier_string (gchar *str, gchar **remain, gchar **last)
+{
+ gchar *ptr;
+ gboolean inq = FALSE;
+ gint len;
+
+ *remain = NULL;
+ *last = NULL;
+ if (!str)
+ return FALSE;
+ g_strchomp (str);
+ if (!*str) {
+ g_free (str);
+ return FALSE;
+ }
+
+ len = strlen (str) - 1;
+ if (((str[len] == '"') && (str[len-1] == '.')) ||
+ (str[len] == '.')) {
+ g_free (str);
+ return FALSE;
+ }
+
+ if (((str[0] == '"') && (str[1] == '.')) ||
+ (str[0] == '.')) {
+ g_free (str);
+ return FALSE;
+ }
+
+ for (ptr = str + strlen (str) - 1; ptr >= str; ptr--) {
+ if (*ptr == '"')
+ inq = !inq;
+ else if ((*ptr == '.') && !inq) {
+ *ptr = 0;
+ *remain = str;
+ *last = g_strdup (ptr+1);
+ break;
+ }
+ }
+ if (!(*last) && !(*remain))
+ *last = str;
+
+ if (*last && !_string_is_identifier (*last)) {
+ g_free (*last);
+ *last = NULL;
+ g_free (*remain);
+ *remain = NULL;
+ return FALSE;
+ }
+ return TRUE;
+}
Modified: trunk/libgda/sql-parser/gda-statement-struct-util.h
==============================================================================
--- trunk/libgda/sql-parser/gda-statement-struct-util.h (original)
+++ trunk/libgda/sql-parser/gda-statement-struct-util.h Sun Apr 6 11:47:57 2008
@@ -27,8 +27,9 @@
gchar *_remove_quotes (gchar *str);
gchar *_add_quotes (const gchar *str);
-gchar *_json_quote_string (const gchar *str);
-gboolean _string_is_identifier (const gchar *str);
+gchar *_json_quote_string (const gchar *str);
+gboolean _string_is_identifier (const gchar *str);
+gboolean _split_identifier_string (gchar *str, gchar **remain, gchar **last);
/* to be removed, only here for debug */
gchar *gda_sql_value_stringify (const GValue *value);
Modified: trunk/libgda/sql-parser/gda-statement-struct.c
==============================================================================
--- trunk/libgda/sql-parser/gda-statement-struct.c (original)
+++ trunk/libgda/sql-parser/gda-statement-struct.c Sun Apr 6 11:47:57 2008
@@ -23,6 +23,7 @@
#include <libgda/gda-debug-macros.h>
#include <libgda/gda-connection.h>
#include <libgda/gda-meta-struct.h>
+#include <libgda/gda-util.h>
#include <libgda/sql-parser/gda-statement-struct-util.h>
#include <libgda/sql-parser/gda-statement-struct-unknown.h>
#include <libgda/sql-parser/gda-statement-struct-trans.h>
@@ -254,6 +255,9 @@
* If @cnc is %NULL, then remove any information from a previous call to this method stored in @stmt. In this case,
* the @stmt->validity_meta_struct attribute is cleared.
*
+ * Also note that some parts of @stmt may be modified: for example leading ad trailing spaces in aliases or
+ * objects names will be removed.
+ *
* Returns: TRUE if no error occurred
*/
gboolean
@@ -312,18 +316,104 @@
return cinfo->check_validity_func (node, data, error);
break;
}
- case GDA_SQL_ANY_EXPR:
- return gda_sql_expr_check_validity ((GdaSqlExpr*) node, data, error);
- case GDA_SQL_ANY_SQL_FIELD:
- return gda_sql_field_check_validity ((GdaSqlField*) node, data, error);
- case GDA_SQL_ANY_SQL_TABLE:
- return gda_sql_table_check_validity ((GdaSqlTable*) node, data, error);
- case GDA_SQL_ANY_SQL_FUNCTION:
- return gda_sql_function_check_validity ((GdaSqlFunction*) node, data, error);
- case GDA_SQL_ANY_SQL_SELECT_FIELD:
- return gda_sql_select_field_check_validity ((GdaSqlSelectField*) node, data, error);
- case GDA_SQL_ANY_SQL_SELECT_TARGET:
- return gda_sql_select_target_check_validity ((GdaSqlSelectTarget*) node, data, error);
+ case GDA_SQL_ANY_EXPR: {
+ GdaSqlExpr *expr = (GdaSqlExpr *) node;
+ if (expr->cast_as) {
+ g_strchomp (expr->cast_as);
+ if (! *(expr->cast_as)) {
+ g_free (expr->cast_as);
+ expr->cast_as = NULL;
+ }
+ }
+ return gda_sql_expr_check_validity (expr, data, error);
+ }
+ case GDA_SQL_ANY_SQL_FIELD: {
+ GdaSqlField *field = (GdaSqlField*) node;
+ if (field->field_name) {
+ g_strchomp (field->field_name);
+ if (! *(field->field_name)) {
+ g_free (field->field_name);
+ field->field_name = NULL;
+ }
+ }
+ return gda_sql_field_check_validity (field, data, error);
+ }
+ case GDA_SQL_ANY_SQL_TABLE: {
+ GdaSqlTable *table = (GdaSqlTable*) node;
+ if (table->table_name) {
+ g_strchomp (table->table_name);
+ if (! *(table->table_name)) {
+ g_free (table->table_name);
+ table->table_name = NULL;
+ }
+ }
+ return gda_sql_table_check_validity (table, data, error);
+ }
+ case GDA_SQL_ANY_SQL_FUNCTION: {
+ GdaSqlFunction *function = (GdaSqlFunction*) node;
+ if (function->function_name) {
+ g_strchomp (function->function_name);
+ if (! *(function->function_name)) {
+ g_free (function->function_name);
+ function->function_name = NULL;
+ }
+ }
+ return gda_sql_function_check_validity (function, data, error);
+ }
+ case GDA_SQL_ANY_SQL_SELECT_FIELD: {
+ GdaSqlSelectField *field = (GdaSqlSelectField*) node;
+ if (field->as) {
+ g_strchomp (field->as);
+ if (! *(field->as)) {
+ g_free (field->as);
+ field->as = NULL;
+ }
+ }
+
+ if (field->expr && field->expr->value && (G_VALUE_TYPE (field->expr->value) == G_TYPE_STRING)) {
+ g_free (field->field_name);
+ g_free (field->table_name);
+ _split_identifier_string (g_value_dup_string (field->expr->value), &(field->table_name),
+ &(field->field_name));
+ }
+ if (field->table_name) {
+ g_strchomp (field->table_name);
+ if (! *(field->table_name)) {
+ g_free (field->table_name);
+ field->table_name = NULL;
+ }
+ }
+ if (field->field_name) {
+ g_strchomp (field->field_name);
+ if (! *(field->field_name)) {
+ g_free (field->field_name);
+ field->field_name = NULL;
+ }
+ }
+ return gda_sql_select_field_check_validity (field, data, error);
+ }
+ case GDA_SQL_ANY_SQL_SELECT_TARGET: {
+ GdaSqlSelectTarget *target = (GdaSqlSelectTarget*) node;
+ if (target->as) {
+ g_strchomp (target->as);
+ if (! *(target->as)) {
+ g_free (target->as);
+ target->as = NULL;
+ }
+ }
+ if (target->expr && target->expr->value && (G_VALUE_TYPE (target->expr->value) == G_TYPE_STRING)) {
+ g_free (target->table_name);
+ target->table_name = g_value_dup_string (target->expr->value);
+ }
+ if (target->table_name) {
+ g_strchomp (target->table_name);
+ if (! *(target->table_name)) {
+ g_free (target->table_name);
+ target->table_name = NULL;
+ }
+ }
+ return gda_sql_select_target_check_validity (target, data, error);
+ }
default:
break;
}
@@ -551,7 +641,7 @@
return TRUE;
memset (&value, 0, sizeof (GValue));
- if (!strcmp (field->field_name, "*"))
+ if (gda_identifier_equal (field->field_name, "*"))
starred_field = TRUE;
if (!field->table_name) {
@@ -591,9 +681,15 @@
}
}
if (!dbo) {
- g_set_error (error, GDA_SQL_ERROR, GDA_SQL_VALIDATION_ERROR,
- _("Could not identify table for field '%s'"), field->field_name);
- return FALSE;
+ targets = ((GdaSqlStatementSelect *)any)->from->targets;
+ if (starred_field && targets && !targets->next)
+ /* only one target => it's the one */
+ dbo = ((GdaSqlSelectTarget*) targets->data)->validity_meta_object;
+ else {
+ g_set_error (error, GDA_SQL_ERROR, GDA_SQL_VALIDATION_ERROR,
+ _("Could not identify table for field '%s'"), field->field_name);
+ return FALSE;
+ }
}
field->validity_meta_object = dbo;
field->validity_meta_table_column = tcol;
@@ -612,7 +708,7 @@
return FALSE;
/* field part */
- if (strcmp (field->field_name, "*")) {
+ if (!gda_identifier_equal (field->field_name, "*")) {
GdaMetaTableColumn *tcol;
g_value_set_string (g_value_init (&value, G_TYPE_STRING), field->field_name);
tcol = gda_meta_struct_get_table_column (data->mstruct,
@@ -1160,3 +1256,98 @@
/* finally call @func for this node */
return func (node, data, error);
}
+
+
+static gboolean foreach_normalize (GdaSqlAnyPart *node, GdaConnection *cnc, GError **error);
+
+/**
+ * gda_sql_statement_normalize
+ * @stmt: a pointer to a #GdaSqlStatement structure
+ * @cnc: a #GdaConnection object, or %NULL
+ *
+ * "Normalizes" (in place) some parts of @stmt, which means @stmt may be modified.
+ * At the moment any "*" field in a SELECT statement will be replaced by one
+ * #GdaSqlSelectField structure for each field in the referenced table.
+ *
+ * Returns: TRUE if no error occurred
+ */
+gboolean
+gda_sql_statement_normalize (GdaSqlStatement *stmt, GdaConnection *cnc, GError **error)
+{
+ gboolean retval;
+ g_return_val_if_fail (stmt, FALSE);
+
+ if (!stmt->validity_meta_struct && !gda_sql_statement_check_validity (stmt, cnc, error))
+ return FALSE;
+
+ retval = gda_sql_any_part_foreach (GDA_SQL_ANY_PART (stmt->contents),
+ (GdaSqlForeachFunc) foreach_normalize, cnc, error);
+#ifdef GDA_DEBUG
+ GError *lerror = NULL;
+ if (retval && !gda_sql_statement_check_validity (stmt, cnc, &lerror)) {
+ g_warning ("Internal error in %s(): statement is not valid anymore after: %s", __FUNCTION__,
+ lerror && lerror->message ? lerror->message : "No detail");
+ if (lerror)
+ g_error_free (lerror);
+ }
+#endif
+ return retval;
+}
+
+static gboolean
+foreach_normalize (GdaSqlAnyPart *node, GdaConnection *cnc, GError **error)
+{
+ if (!node) return TRUE;
+
+ if (node->type == GDA_SQL_ANY_SQL_SELECT_FIELD) {
+ GdaSqlSelectField *field = (GdaSqlSelectField*) node;
+ if (((field->field_name && gda_identifier_equal (field->field_name, "*")) ||
+ (field->expr && field->expr->value && (G_VALUE_TYPE (field->expr->value) == G_TYPE_STRING) &&
+ gda_identifier_equal (g_value_get_string (field->expr->value), "*"))) &&
+ field->validity_meta_object) {
+ /* expand * to all the fields */
+ GdaMetaTable *mtable = GDA_META_DB_OBJECT_GET_TABLE (field->validity_meta_object);
+ GSList *list;
+ GdaSqlAnyPart *parent_node = ((GdaSqlAnyPart*) field)->parent;
+ gint nodepos = g_slist_index (((GdaSqlStatementSelect*) parent_node)->expr_list, node);
+ if (parent_node->type != GDA_SQL_ANY_STMT_SELECT) {
+ g_set_error (error, GDA_SQL_ERROR, GDA_SQL_STRUCTURE_CONTENTS_ERROR,
+ _("Select field is not in a SELECT statement"));
+ return FALSE;
+ }
+ for (list = mtable->columns; list; list = list->next) {
+ GdaSqlSelectField *nfield;
+ GdaMetaTableColumn *tcol = (GdaMetaTableColumn *) list->data;
+
+ nfield = gda_sql_select_field_new (parent_node);
+ nfield->field_name = g_strdup (tcol->column_name);
+ if (field->table_name)
+ nfield->table_name = g_strdup (field->table_name);
+ nfield->validity_meta_object = field->validity_meta_object;
+ nfield->validity_meta_table_column = tcol;
+ nfield->expr = gda_sql_expr_new ((GdaSqlAnyPart*) nfield);
+ nfield->expr->value = gda_value_new (G_TYPE_STRING);
+ if (field->table_name)
+ g_value_take_string (nfield->expr->value, g_strdup_printf ("%s.%s",
+ nfield->table_name,
+ nfield->field_name));
+ else
+ g_value_set_string (nfield->expr->value, nfield->field_name);
+
+ /* insert nfield into expr_list */
+ GSList *expr_list = ((GdaSqlStatementSelect*) parent_node)->expr_list;
+ if (list == mtable->columns) {
+ GSList *lnode = g_slist_nth (expr_list, nodepos);
+ lnode->data = nfield;
+ }
+ else
+ ((GdaSqlStatementSelect*) parent_node)->expr_list =
+ g_slist_insert (expr_list, nfield, ++nodepos);
+ }
+ /* get rid of @field */
+ gda_sql_select_field_free (field);
+ }
+ }
+
+ return TRUE;
+}
Modified: trunk/libgda/sql-parser/gda-statement-struct.h
==============================================================================
--- trunk/libgda/sql-parser/gda-statement-struct.h (original)
+++ trunk/libgda/sql-parser/gda-statement-struct.h Sun Apr 6 11:47:57 2008
@@ -43,6 +43,7 @@
gboolean gda_sql_statement_check_structure (GdaSqlStatement *stmt, GError **error);
gboolean gda_sql_statement_check_validity (GdaSqlStatement *stmt, GdaConnection *cnc, GError **error);
void gda_sql_statement_check_clean (GdaSqlStatement *stmt);
+gboolean gda_sql_statement_normalize (GdaSqlStatement *stmt, GdaConnection *cnc, GError **error);
GdaSqlStatementContentsInfo *gda_sql_statement_get_contents_infos (GdaSqlStatementType type) ;
Modified: trunk/libgda/sql-parser/parser.y
==============================================================================
--- trunk/libgda/sql-parser/parser.y (original)
+++ trunk/libgda/sql-parser/parser.y Sun Apr 6 11:47:57 2008
@@ -986,9 +986,9 @@
//
nm(A) ::= JOIN(X). {A = X;}
nm(A) ::= ID(X). {A = X;}
+nm(A) ::= TEXTUAL(X). {A = X;}
// Fully qualified name
-fullname(A) ::= TEXTUAL(X). {A = X;}
fullname(A) ::= nm(X). {A = X;}
fullname(A) ::= nm(S) DOT nm(X). {gchar *str;
str = g_strdup_printf ("%s.%s", g_value_get_string (S), g_value_get_string (X));
Modified: trunk/providers/postgres/parser.y
==============================================================================
--- trunk/providers/postgres/parser.y (original)
+++ trunk/providers/postgres/parser.y Sun Apr 6 11:47:57 2008
@@ -987,9 +987,9 @@
//
nm(A) ::= JOIN(X). {A = X;}
nm(A) ::= ID(X). {A = X;}
+nm(A) ::= TEXTUAL(X). {A = X;}
// Fully qualified name
-fullname(A) ::= TEXTUAL(X). {A = X;}
fullname(A) ::= nm(X). {A = X;}
fullname(A) ::= nm(S) DOT nm(X). {gchar *str;
str = g_strdup_printf ("%s.%s", g_value_get_string (S), g_value_get_string (X));
Modified: trunk/providers/skel-implementation/capi/parser.y
==============================================================================
--- trunk/providers/skel-implementation/capi/parser.y (original)
+++ trunk/providers/skel-implementation/capi/parser.y Sun Apr 6 11:47:57 2008
@@ -1001,9 +1001,9 @@
//
nm(A) ::= JOIN(X). {A = X;}
nm(A) ::= ID(X). {A = X;}
+nm(A) ::= TEXTUAL(X). {A = X;}
// Fully qualified name
-fullname(A) ::= nm(X). {A = X;}
fullname(A) ::= nm(S) DOT nm(X). {gchar *str;
str = g_strdup_printf ("%s.%s", g_value_get_string (S), g_value_get_string (X));
A = g_new0 (GValue, 1);
Modified: trunk/tests/parser/Makefile.am
==============================================================================
--- trunk/tests/parser/Makefile.am (original)
+++ trunk/tests/parser/Makefile.am Sun Apr 6 11:47:57 2008
@@ -5,8 +5,8 @@
$(LIBGDA_CFLAGS) \
-DROOT_DIR=\""$(top_srcdir)"\"
-TESTS = check_parser check_validation
-check_PROGRAMS = check_parser check_validation
+TESTS = check_parser check_validation check_normalization check_dml_comp
+check_PROGRAMS = check_parser check_validation check_normalization check_dml_comp
check_parser_SOURCES = check_parser.c
check_parser_LDADD = \
@@ -18,4 +18,14 @@
$(top_builddir)/libgda/libgda-4.0.la \
$(LIBGDA_LIBS)
+check_normalization_SOURCES = check_normalization.c
+check_normalization_LDADD = \
+ $(top_builddir)/libgda/libgda-4.0.la \
+ $(LIBGDA_LIBS)
+
+check_dml_comp_SOURCES = check_dml_comp.c
+check_dml_comp_LDADD = \
+ $(top_builddir)/libgda/libgda-4.0.la \
+ $(LIBGDA_LIBS)
+
EXTRA_DIST = testdata.xml testvalid.xml
Added: trunk/tests/parser/check_dml_comp.c
==============================================================================
--- (empty file)
+++ trunk/tests/parser/check_dml_comp.c Sun Apr 6 11:47:57 2008
@@ -0,0 +1,192 @@
+#include <stdio.h>
+#include <glib.h>
+#include <glib-object.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <string.h>
+#include <gmodule.h>
+#include <libgda/libgda.h>
+#include <libgda/gda-util.h>
+#include <sql-parser/gda-sql-parser.h>
+
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+
+GdaConnection *cnc;
+static gint do_test (const xmlChar *id, const xmlChar *sql,
+ const gchar *computed_type, const xmlChar *computed_exp, gboolean require_pk);
+
+int
+main (int argc, char** argv)
+{
+ xmlDocPtr doc;
+ xmlNodePtr root, node;
+ gint failures = 0;
+ gint ntests = 0;
+ gchar *fname;
+
+ gda_init ("Parser validation", ".1", argc, argv);
+
+ /* open connection */
+ gchar *cnc_string;
+ fname = g_build_filename (ROOT_DIR, "data", NULL);
+ cnc_string = g_strdup_printf ("DB_DIR=%s;DB_NAME=sales_test", fname);
+ g_free (fname);
+ cnc = gda_connection_open_from_string ("SQLite", cnc_string, NULL,
+ GDA_CONNECTION_OPTIONS_READ_ONLY, NULL);
+ if (!cnc) {
+ g_print ("Failed to open connection, cnc_string = %s\n", cnc_string);
+ exit (1);
+ }
+ if (!gda_connection_update_meta_store (cnc, NULL, NULL)) {
+ g_print ("Failed to update meta store, cnc_string = %s\n", cnc_string);
+ exit (1);
+ }
+ g_free (cnc_string);
+
+ /* load file */
+ fname = g_build_filename (ROOT_DIR, "tests", "parser", "testvalid.xml", NULL);
+ if (! g_file_test (fname, G_FILE_TEST_EXISTS)) {
+ g_print ("File '%s' does not exist\n", fname);
+ exit (1);
+ }
+
+ /* use test data */
+ doc = xmlParseFile (fname);
+ g_free (fname);
+ g_assert (doc);
+ root = xmlDocGetRootElement (doc);
+ g_assert (!strcmp ((gchar*) root->name, "testdata"));
+ for (node = root->children; node; node = node->next) {
+ if (strcmp ((gchar*) node->name, "test"))
+ continue;
+ xmlNodePtr snode;
+ xmlChar *sql = NULL;
+ xmlChar *id;
+
+ id = xmlGetProp (node, BAD_CAST "id");
+ for (snode = node->children; snode; snode = snode->next) {
+ if (!strcmp ((gchar*) snode->name, "sql"))
+ sql = xmlNodeGetContent (snode);
+ else if (!strcmp ((gchar*) snode->name, "insert") ||
+ !strcmp ((gchar*) snode->name, "update") ||
+ !strcmp ((gchar*) snode->name, "delete")) {
+ xmlChar *comp_exp;
+ xmlChar *prop;
+ gboolean require_pk = TRUE;
+ comp_exp = xmlNodeGetContent (snode);
+ prop = xmlGetProp (snode, "need_pk");
+ if (prop) {
+ if ((*prop == 'f') || (*prop == 'F') || (*prop == '0'))
+ require_pk = FALSE;
+ xmlFree (prop);
+ }
+ if (sql) {
+ if (!do_test (id, sql, snode->name, comp_exp, require_pk))
+ failures++;
+ ntests++;
+ }
+ }
+ }
+
+ /* mem free */
+ if (sql) xmlFree (sql);
+ if (id) xmlFree (id);
+ }
+ xmlFreeDoc (doc);
+
+ g_print ("TESTS COUNT: %d\n", ntests);
+ g_print ("FAILURES: %d\n", failures);
+
+ return failures != 0 ? 1 : 0;
+}
+
+/*
+ * Returns: the number of failures
+ */
+static gint
+do_test (const xmlChar *id, const xmlChar *sql,
+ const gchar *computed_type, const xmlChar *computed_exp, gboolean require_pk)
+{
+ static GdaSqlParser *parser = NULL;
+ GdaStatement *stmt;
+ GError *error = NULL;
+
+ if (!parser) {
+ parser = gda_connection_create_parser (cnc);
+ if (!parser)
+ parser = gda_sql_parser_new ();
+ }
+
+#ifdef GDA_DEBUG
+ g_print ("===== TEST %s COMPUTING %s (%s), SQL: @%s \n", id, computed_type,
+ require_pk ? "PK fields" : "All fields", sql);
+#endif
+
+ stmt = gda_sql_parser_parse_string (parser, sql, NULL, NULL);
+ if (!stmt) {
+ g_print ("ERROR for test '%s': could not parse statement\n", id);
+ return FALSE;
+ }
+ /*g_print ("EXP %d, got %d\n", valid_expected, is_valid);*/
+ /*g_print ("PARSED: %s\n", gda_statement_serialize (stmt));*/
+
+ if (computed_exp) {
+ GdaStatement *cstmt = NULL;
+ switch (*computed_type) {
+ case 'i':
+ case 'I':
+ gda_compute_dml_statements (cnc, stmt, require_pk, &cstmt, NULL, NULL, &error);
+ break;
+ case 'u':
+ case 'U':
+ gda_compute_dml_statements (cnc, stmt, require_pk, NULL, &cstmt, NULL, &error);
+ break;
+ case 'd':
+ case 'D':
+ gda_compute_dml_statements (cnc, stmt, require_pk, NULL, NULL, &cstmt, &error);
+ break;
+ default:
+ TO_IMPLEMENT;
+ }
+
+ if (!*computed_exp && cstmt) {
+ g_object_unref (stmt);
+ gchar *serial, *rend;
+ serial = gda_statement_serialize (cstmt);
+ rend = gda_statement_to_sql (cstmt, NULL, NULL);
+ g_print ("ERROR for test '%s': %s statement created but none expected\n"
+ "\tgot: %s\n\tSQL: %s\n", id, computed_type, serial, rend);
+ g_free (serial);
+ g_free (rend);
+ g_object_unref (cstmt);
+ return FALSE;
+ }
+ if (*computed_exp && !cstmt) {
+ g_print ("ERROR for test '%s': %s statement not created but expected: %s\n", id,
+ computed_type,
+ error && error->message ? error->message : "No detail");
+ g_object_unref (stmt);
+ return FALSE;
+ }
+ if (*computed_exp && cstmt) {
+ gchar *serial;
+ serial = gda_statement_serialize (cstmt);
+ if (strcmp (serial, computed_exp)) {
+ gchar *rend;
+ rend = gda_statement_to_sql (cstmt, NULL, NULL);
+ g_print ("ERROR for test '%s': computed %s statement is incorrect:\n"
+ "\texp: %s\n\tgot: %s\n\tSQL: %s\n", id, computed_type, computed_exp, serial,
+ rend);
+ g_free (rend);
+ g_object_unref (stmt);
+ g_object_unref (cstmt);
+ g_free (serial);
+ return FALSE;
+ }
+ g_free (serial);
+ }
+ }
+ g_object_unref (stmt);
+ return TRUE;
+}
Added: trunk/tests/parser/check_normalization.c
==============================================================================
--- (empty file)
+++ trunk/tests/parser/check_normalization.c Sun Apr 6 11:47:57 2008
@@ -0,0 +1,139 @@
+#include <stdio.h>
+#include <glib.h>
+#include <glib-object.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <string.h>
+#include <gmodule.h>
+#include <libgda/libgda.h>
+#include <libgda/gda-util.h>
+#include <sql-parser/gda-sql-parser.h>
+
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+
+GdaConnection *cnc;
+static gint do_test (const xmlChar *id, const xmlChar *sql, const xmlChar *norm);
+
+int
+main (int argc, char** argv)
+{
+ xmlDocPtr doc;
+ xmlNodePtr root, node;
+ gint failures = 0;
+ gint ntests = 0;
+ gchar *fname;
+
+ gda_init ("Parser validation", ".1", argc, argv);
+
+ /* open connection */
+ gchar *cnc_string;
+ fname = g_build_filename (ROOT_DIR, "data", NULL);
+ cnc_string = g_strdup_printf ("DB_DIR=%s;DB_NAME=sales_test", fname);
+ g_free (fname);
+ cnc = gda_connection_open_from_string ("SQLite", cnc_string, NULL,
+ GDA_CONNECTION_OPTIONS_READ_ONLY, NULL);
+ if (!cnc) {
+ g_print ("Failed to open connection, cnc_string = %s\n", cnc_string);
+ exit (1);
+ }
+ if (!gda_connection_update_meta_store (cnc, NULL, NULL)) {
+ g_print ("Failed to update meta store, cnc_string = %s\n", cnc_string);
+ exit (1);
+ }
+ g_free (cnc_string);
+
+ /* load file */
+ fname = g_build_filename (ROOT_DIR, "tests", "parser", "testvalid.xml", NULL);
+ if (! g_file_test (fname, G_FILE_TEST_EXISTS)) {
+ g_print ("File '%s' does not exist\n", fname);
+ exit (1);
+ }
+
+ /* use test data */
+ doc = xmlParseFile (fname);
+ g_free (fname);
+ g_assert (doc);
+ root = xmlDocGetRootElement (doc);
+ g_assert (!strcmp ((gchar*) root->name, "testdata"));
+ for (node = root->children; node; node = node->next) {
+ if (strcmp ((gchar*) node->name, "test"))
+ continue;
+ xmlNodePtr snode;
+ xmlChar *sql = NULL;
+ xmlChar *id;
+ xmlChar *norm = NULL;
+
+ id = xmlGetProp (node, BAD_CAST "id");
+ for (snode = node->children; snode; snode = snode->next) {
+ if (!strcmp ((gchar*) snode->name, "sql"))
+ sql = xmlNodeGetContent (snode);
+ if (!strcmp ((gchar*) snode->name, "normalized"))
+ norm = xmlNodeGetContent (snode);
+ }
+ if (sql && norm) {
+ if (!do_test (id, sql, norm))
+ failures++;
+ ntests++;
+ }
+
+ /* mem free */
+ if (sql) xmlFree (sql);
+ if (norm) xmlFree (norm);
+ if (id) xmlFree (id);
+ }
+ xmlFreeDoc (doc);
+
+ g_print ("TESTS COUNT: %d\n", ntests);
+ g_print ("FAILURES: %d\n", failures);
+
+ return failures != 0 ? 1 : 0;
+}
+
+/*
+ * Returns: the number of failures
+ */
+static gint
+do_test (const xmlChar *id, const xmlChar *sql, const xmlChar *norm)
+{
+ static GdaSqlParser *parser = NULL;
+ GdaStatement *stmt;
+ GError *error = NULL;
+ gchar *str;
+
+ if (!parser) {
+ parser = gda_connection_create_parser (cnc);
+ if (!parser)
+ parser = gda_sql_parser_new ();
+ }
+
+#ifdef GDA_DEBUG
+ g_print ("===== TEST %s SQL: @%s \n", id, sql);
+#endif
+
+ stmt = gda_sql_parser_parse_string (parser, sql, NULL, NULL);
+ if (!stmt) {
+ g_print ("ERROR for test '%s': could not parse statement\n", id);
+ return FALSE;
+ }
+ if (!gda_statement_normalize (stmt, cnc, &error)) {
+ g_print ("ERROR for test '%s': statement can't be normalized: %s\n", id,
+ error && error->message ? error->message : "No detail");
+ g_object_unref (stmt);
+ return FALSE;
+ }
+
+ str = gda_statement_serialize (stmt);
+ if (strcmp (str, norm)) {
+ gchar *sql;
+ sql = gda_statement_to_sql (stmt, NULL, NULL);
+ g_print ("ERROR for test '%s': \n\tEXP: %s\n\tGOT: %s\n\tSQL: %s\n", id, norm, str, sql);
+ g_free (sql);
+ g_free (str);
+ return FALSE;
+ }
+
+ g_free (str);
+ g_object_unref (stmt);
+ return TRUE;
+}
Modified: trunk/tests/parser/check_validation.c
==============================================================================
--- trunk/tests/parser/check_validation.c (original)
+++ trunk/tests/parser/check_validation.c Sun Apr 6 11:47:57 2008
@@ -13,8 +13,7 @@
#include <libxml/tree.h>
GdaConnection *cnc;
-static gint do_test (const xmlChar *id, const xmlChar *sql, gboolean valid_expected,
- const gchar *computed_type, const xmlChar *computed_exp, gboolean require_pk);
+static gint do_test (const xmlChar *id, const xmlChar *sql, gboolean valid_expected);
int
main (int argc, char** argv)
@@ -77,28 +76,9 @@
xmlFree (prop);
}
}
- else if (!strcmp ((gchar*) snode->name, "insert") ||
- !strcmp ((gchar*) snode->name, "update") ||
- !strcmp ((gchar*) snode->name, "delete")) {
- xmlChar *comp_exp;
- xmlChar *prop;
- gboolean require_pk = TRUE;
- comp_exp = xmlNodeGetContent (snode);
- prop = xmlGetProp (snode, "need_pk");
- if (prop) {
- if ((*prop == 'f') || (*prop == 'F') || (*prop == '0'))
- require_pk = FALSE;
- xmlFree (prop);
- }
- if (sql) {
- if (!do_test (id, sql, valid, snode->name, comp_exp, require_pk))
- failures++;
- ntests++;
- }
- }
}
if (sql) {
- if (!do_test (id, sql, valid, NULL, NULL, FALSE))
+ if (!do_test (id, sql, valid))
failures++;
ntests++;
}
@@ -119,8 +99,7 @@
* Returns: the number of failures
*/
static gint
-do_test (const xmlChar *id, const xmlChar *sql, gboolean valid_expected,
- const gchar *computed_type, const xmlChar *computed_exp, gboolean require_pk)
+do_test (const xmlChar *id, const xmlChar *sql, gboolean valid_expected)
{
static GdaSqlParser *parser = NULL;
GdaStatement *stmt;
@@ -134,11 +113,7 @@
}
#ifdef GDA_DEBUG
- if (computed_type)
- g_print ("===== TEST %s COMPUTING %s (%s), SQL: @%s \n", id, computed_type,
- require_pk ? "PK fields" : "All fields", sql);
- else
- g_print ("===== TEST %s SQL: @%s \n", id, sql);
+ g_print ("===== TEST %s SQL: @%s \n", id, sql);
#endif
stmt = gda_sql_parser_parse_string (parser, sql, NULL, NULL);
@@ -161,62 +136,6 @@
/*g_print ("EXP %d, got %d\n", valid_expected, is_valid);*/
/*g_print ("PARSED: %s\n", gda_statement_serialize (stmt));*/
- if (computed_exp) {
- GdaStatement *cstmt = NULL;
- switch (*computed_type) {
- case 'i':
- case 'I':
- gda_compute_dml_statements (cnc, stmt, require_pk, &cstmt, NULL, NULL, &error);
- break;
- case 'u':
- case 'U':
- gda_compute_dml_statements (cnc, stmt, require_pk, NULL, &cstmt, NULL, &error);
- break;
- case 'd':
- case 'D':
- gda_compute_dml_statements (cnc, stmt, require_pk, NULL, NULL, &cstmt, &error);
- break;
- default:
- TO_IMPLEMENT;
- }
-
- if (!*computed_exp && cstmt) {
- g_object_unref (stmt);
- gchar *serial, *rend;
- serial = gda_statement_serialize (cstmt);
- rend = gda_statement_to_sql (cstmt, NULL, NULL);
- g_print ("ERROR for test '%s': %s statement created but none expected\n"
- "\tgot: %s\n\tSQL: %s\n", id, computed_type, serial, rend);
- g_free (serial);
- g_free (rend);
- g_object_unref (cstmt);
- return FALSE;
- }
- if (*computed_exp && !cstmt) {
- g_print ("ERROR for test '%s': %s statement not created but expected: %s\n", id,
- computed_type,
- error && error->message ? error->message : "No detail");
- g_object_unref (stmt);
- return FALSE;
- }
- if (*computed_exp && cstmt) {
- gchar *serial;
- serial = gda_statement_serialize (cstmt);
- if (strcmp (serial, computed_exp)) {
- gchar *rend;
- rend = gda_statement_to_sql (cstmt, NULL, NULL);
- g_print ("ERROR for test '%s': computed %s statement is incorrect:\n"
- "\texp: %s\n\tgot: %s\n\tSQL: %s\n", id, computed_type, computed_exp, serial,
- rend);
- g_free (rend);
- g_object_unref (stmt);
- g_object_unref (cstmt);
- g_free (serial);
- return FALSE;
- }
- g_free (serial);
- }
- }
g_object_unref (stmt);
return TRUE;
}
Modified: trunk/tests/parser/testvalid.xml
==============================================================================
--- trunk/tests/parser/testvalid.xml (original)
+++ trunk/tests/parser/testvalid.xml Sun Apr 6 11:47:57 2008
@@ -57,7 +57,8 @@
<!-- target has no primary key -->
<test id="pre3">
<sql valid="t">SELECT * FROM sales_orga</sql>
- <insert>unknown</insert>
+ <normalized>{"statement":{"sql":"SELECT * FROM sales_orga","stmt_type":"SELECT","contents":{"distinct":"false","fields":[{"expr":{"value":"id_salesrep"},"field_name":"id_salesrep"},{"expr":{"value":"id_role"},"field_name":"id_role"},{"expr":{"value":"note"},"field_name":"note"}],"from":{"targets":[{"expr":{"value":"sales_orga"},"table_name":"sales_orga"}]}}}}</normalized>
+ <insert>{"statement":{"sql":null,"stmt_type":"INSERT","contents":{"table":"sales_orga","fields":["id_salesrep","id_role","note"],"values":[[{"value":null,"param_spec":{"name":"+0","descr":null,"type":"gint","is_param":false,"nullok":false}},{"value":null,"param_spec":{"name":"+1","descr":null,"type":"gint","is_param":false,"nullok":false}},{"value":null,"param_spec":{"name":"+2","descr":null,"type":"gchararray","is_param":false,"nullok":true}}]]}}}</insert>
<update/>
<delete/>
</test>
@@ -65,8 +66,8 @@
<test id="0">
<sql valid="t">SELECT id, name FROM customers</sql>
<insert>{"statement":{"sql":null,"stmt_type":"INSERT","contents":{"table":"customers","fields":["id","name"],"values":[[{"value":null,"param_spec":{"name":"+0","descr":null,"type":"gint","is_param":false,"nullok":false}},{"value":null,"param_spec":{"name":"+1","descr":null,"type":"gchararray","is_param":false,"nullok":false}}]]}}}</insert>
- <update>unknown</update>
- <delete>unknown</delete>
+ <update>{"statement":{"sql":null,"stmt_type":"UPDATE","contents":{"table":"customers","fields":["id","name"],"expressions":[{"value":null,"param_spec":{"name":"+0","descr":null,"type":"gint","is_param":false,"nullok":false}},{"value":null,"param_spec":{"name":"+1","descr":null,"type":"gchararray","is_param":false,"nullok":false}}],"condition":{"operation":{"operator":"=","operand0":{"value":"id"},"operand1":{"value":null,"param_spec":{"name":"-0","descr":null,"type":"gint","is_param":false,"nullok":false}}}}}}}</update>
+ <delete>{"statement":{"sql":null,"stmt_type":"DELETE","contents":{"table":"customers","condition":{"operation":{"operator":"=","operand0":{"value":"id"},"operand1":{"value":null,"param_spec":{"name":"-0","descr":null,"type":"gint","is_param":false,"nullok":false}}}}}}}</delete>
</test>
<test id="0.1">
@@ -79,4 +80,62 @@
<insert>{"statement":{"sql":null,"stmt_type":"INSERT","contents":{"table":"customers","fields":["id","name"],"values":[[{"value":null,"param_spec":{"name":"+0","descr":null,"type":"gint","is_param":false,"nullok":false}},{"value":null,"param_spec":{"name":"+2","descr":null,"type":"gchararray","is_param":false,"nullok":false}}]]}}}</insert>
</test>
+ <test id="0.3">
+ <sql valid="t">SELECT id || name FROM customers</sql>
+ <insert/>
+ <update/>
+ <delete/>
+ </test>
+
+ <!-- normalization test -->
+ <test id="N0">
+ <sql valid="t">SELECT count (*) FROM sales_orga</sql>
+ <normalized>{"statement":{"sql":"SELECT count (*) FROM sales_orga","stmt_type":"SELECT","contents":{"distinct":"false","fields":[{"expr":{"func":{"function_name":"count","function_args":[{"value":"*"}]}}}],"from":{"targets":[{"expr":{"value":"sales_orga"},"table_name":"sales_orga"}]}}}}</normalized>
+ <insert/>
+ <update/>
+ <delete/>
+ </test>
+
+ <test id="N1">
+ <sql valid="t">SELECT name, *, city FROM customers</sql>
+ <normalized>{"statement":{"sql":"SELECT name, *, city FROM customers","stmt_type":"SELECT","contents":{"distinct":"false","fields":[{"expr":{"value":"name"},"field_name":"name"},{"expr":{"value":"id"},"field_name":"id"},{"expr":{"value":"name"},"field_name":"name"},{"expr":{"value":"default_served_by"},"field_name":"default_served_by"},{"expr":{"value":"country"},"field_name":"country"},{"expr":{"value":"city"},"field_name":"city"},{"expr":{"value":"city"},"field_name":"city"}],"from":{"targets":[{"expr":{"value":"customers"},"table_name":"customers"}]}}}}</normalized>
+ <insert>{"statement":{"sql":null,"stmt_type":"INSERT","contents":{"table":"customers","fields":["name","id","default_served_by","country","city"],"values":[[{"value":null,"param_spec":{"name":"+0","descr":null,"type":"gchararray","is_param":false,"nullok":false}},{"value":null,"param_spec":{"name":"+1","descr":null,"type":"gint","is_param":false,"nullok":false}},{"value":null,"param_spec":{"name":"+3","descr":null,"type":"gint","is_param":false,"nullok":true}},{"value":null,"param_spec":{"name":"+4","descr":null,"type":"gchararray","is_param":false,"nullok":true}},{"value":null,"param_spec":{"name":"+5","descr":null,"type":"gchararray","is_param":false,"nullok":true}}]]}}}</insert>
+ <update>{"statement":{"sql":null,"stmt_type":"UPDATE","contents":{"table":"customers","fields":["name","id","default_served_by","country","city"],"expressions":[{"value":null,"param_spec":{"name":"+0","descr":null,"type":"gchararray","is_param":false,"nullok":false}},{"value":null,"param_spec":{"name":"+1","descr":null,"type":"gint","is_param":false,"nullok":false}},{"value":null,"param_spec":{"name":"+3","descr":null,"type":"gint","is_param":false,"nullok":true}},{"value":null,"param_spec":{"name":"+4","descr":null,"type":"gchararray","is_param":false,"nullok":true}},{"value":null,"param_spec":{"name":"+5","descr":null,"type":"gchararray","is_param":false,"nullok":true}}],"condition":{"operation":{"operator":"=","operand0":{"value":"id"},"operand1":{"value":null,"param_spec":{"name":"-0","descr":null,"type":"gint","is_param":false,"nullok":false}}}}}}}</update>
+ <delete>{"statement":{"sql":null,"stmt_type":"DELETE","contents":{"table":"customers","condition":{"operation":{"operator":"=","operand0":{"value":"id"},"operand1":{"value":null,"param_spec":{"name":"-0","descr":null,"type":"gint","is_param":false,"nullok":false}}}}}}}</delete>
+ </test>
+
+ <test id="N2">
+ <sql valid="t">SELECT s.* FROM sales_orga as s</sql>
+ <normalized>{"statement":{"sql":"SELECT s.* FROM sales_orga as s","stmt_type":"SELECT","contents":{"distinct":"false","fields":[{"expr":{"value":"s.id_salesrep"},"field_name":"id_salesrep","table_name":"s"},{"expr":{"value":"s.id_role"},"field_name":"id_role","table_name":"s"},{"expr":{"value":"s.note"},"field_name":"note","table_name":"s"}],"from":{"targets":[{"expr":{"value":"sales_orga"},"table_name":"sales_orga","as":"s"}]}}}}</normalized>
+ <insert>{"statement":{"sql":null,"stmt_type":"INSERT","contents":{"table":"sales_orga","fields":["id_salesrep","id_role","note"],"values":[[{"value":null,"param_spec":{"name":"+0","descr":null,"type":"gint","is_param":false,"nullok":false}},{"value":null,"param_spec":{"name":"+1","descr":null,"type":"gint","is_param":false,"nullok":false}},{"value":null,"param_spec":{"name":"+2","descr":null,"type":"gchararray","is_param":false,"nullok":true}}]]}}}</insert>
+ <update></update>
+ <delete></delete>
+ <update needpk="f"></update>
+ <delete needpk="f"></delete>
+ </test>
+
+ <test id="N3">
+ <sql valid="t">SELECT "name", c.*, "id" FROM customers as c</sql>
+ <normalized>{"statement":{"sql":"SELECT \"name\", c.*, \"id\" FROM customers as c","stmt_type":"SELECT","contents":{"distinct":"false","fields":[{"expr":{"value":"\"name\""},"field_name":"\"name\""},{"expr":{"value":"c.id"},"field_name":"id","table_name":"c"},{"expr":{"value":"c.name"},"field_name":"name","table_name":"c"},{"expr":{"value":"c.default_served_by"},"field_name":"default_served_by","table_name":"c"},{"expr":{"value":"c.country"},"field_name":"country","table_name":"c"},{"expr":{"value":"c.city"},"field_name":"city","table_name":"c"},{"expr":{"value":"\"id\""},"field_name":"\"id\""}],"from":{"targets":[{"expr":{"value":"customers"},"table_name":"customers","as":"c"}]}}}}</normalized>
+ <insert>{"statement":{"sql":null,"stmt_type":"INSERT","contents":{"table":"customers","fields":["\"name\"","id","default_served_by","country","city"],"values":[[{"value":null,"param_spec":{"name":"+0","descr":null,"type":"gchararray","is_param":false,"nullok":false}},{"value":null,"param_spec":{"name":"+1","descr":null,"type":"gint","is_param":false,"nullok":false}},{"value":null,"param_spec":{"name":"+3","descr":null,"type":"gint","is_param":false,"nullok":true}},{"value":null,"param_spec":{"name":"+4","descr":null,"type":"gchararray","is_param":false,"nullok":true}},{"value":null,"param_spec":{"name":"+5","descr":null,"type":"gchararray","is_param":false,"nullok":true}}]]}}}</insert>
+ <update>{"statement":{"sql":null,"stmt_type":"UPDATE","contents":{"table":"customers","fields":["\"name\"","id","default_served_by","country","city"],"expressions":[{"value":null,"param_spec":{"name":"+0","descr":null,"type":"gchararray","is_param":false,"nullok":false}},{"value":null,"param_spec":{"name":"+1","descr":null,"type":"gint","is_param":false,"nullok":false}},{"value":null,"param_spec":{"name":"+3","descr":null,"type":"gint","is_param":false,"nullok":true}},{"value":null,"param_spec":{"name":"+4","descr":null,"type":"gchararray","is_param":false,"nullok":true}},{"value":null,"param_spec":{"name":"+5","descr":null,"type":"gchararray","is_param":false,"nullok":true}}],"condition":{"operation":{"operator":"=","operand0":{"value":"id"},"operand1":{"value":null,"param_spec":{"name":"-0","descr":null,"type":"gint","is_param":false,"nullok":false}}}}}}}</update>
+ <delete>{"statement":{"sql":null,"stmt_type":"DELETE","contents":{"table":"customers","condition":{"operation":{"operator":"=","operand0":{"value":"id"},"operand1":{"value":null,"param_spec":{"name":"-0","descr":null,"type":"gint","is_param":false,"nullok":false}}}}}}}</delete>
+ </test>
+
+ <test id="N4">
+ <sql valid="t">SELECT "name", "customers".*, "id" FROM customers as c</sql>
+ <normalized>{"statement":{"sql":"SELECT \"name\", \"customers\".*, \"id\" FROM customers as c","stmt_type":"SELECT","contents":{"distinct":"false","fields":[{"expr":{"value":"\"name\""},"field_name":"\"name\""},{"expr":{"value":"\"customers\".id"},"field_name":"id","table_name":"\"customers\""},{"expr":{"value":"\"customers\".name"},"field_name":"name","table_name":"\"customers\""},{"expr":{"value":"\"customers\".default_served_by"},"field_name":"default_served_by","table_name":"\"customers\""},{"expr":{"value":"\"customers\".country"},"field_name":"country","table_name":"\"customers\""},{"expr":{"value":"\"customers\".city"},"field_name":"city","table_name":"\"customers\""},{"expr":{"value":"\"id\""},"field_name":"\"id\""}],"from":{"targets":[{"expr":{"value":"customers"},"table_name":"customers","as":"c"}]}}}}</normalized>
+ <insert>{"statement":{"sql":null,"stmt_type":"INSERT","contents":{"table":"customers","fields":["\"name\"","id","default_served_by","country","city"],"values":[[{"value":null,"param_spec":{"name":"+0","descr":null,"type":"gchararray","is_param":false,"nullok":false}},{"value":null,"param_spec":{"name":"+1","descr":null,"type":"gint","is_param":false,"nullok":false}},{"value":null,"param_spec":{"name":"+3","descr":null,"type":"gint","is_param":false,"nullok":true}},{"value":null,"param_spec":{"name":"+4","descr":null,"type":"gchararray","is_param":false,"nullok":true}},{"value":null,"param_spec":{"name":"+5","descr":null,"type":"gchararray","is_param":false,"nullok":true}}]]}}}</insert>
+ <update>{"statement":{"sql":null,"stmt_type":"UPDATE","contents":{"table":"customers","fields":["\"name\"","id","default_served_by","country","city"],"expressions":[{"value":null,"param_spec":{"name":"+0","descr":null,"type":"gchararray","is_param":false,"nullok":false}},{"value":null,"param_spec":{"name":"+1","descr":null,"type":"gint","is_param":false,"nullok":false}},{"value":null,"param_spec":{"name":"+3","descr":null,"type":"gint","is_param":false,"nullok":true}},{"value":null,"param_spec":{"name":"+4","descr":null,"type":"gchararray","is_param":false,"nullok":true}},{"value":null,"param_spec":{"name":"+5","descr":null,"type":"gchararray","is_param":false,"nullok":true}}],"condition":{"operation":{"operator":"=","operand0":{"value":"id"},"operand1":{"value":null,"param_spec":{"name":"-0","descr":null,"type":"gint","is_param":false,"nullok":false}}}}}}}</update>
+ <delete>{"statement":{"sql":null,"stmt_type":"DELETE","contents":{"table":"customers","condition":{"operation":{"operator":"=","operand0":{"value":"id"},"operand1":{"value":null,"param_spec":{"name":"-0","descr":null,"type":"gint","is_param":false,"nullok":false}}}}}}}</delete>
+ </test>
+
+ <test id="N5">
+ <sql valid="t">SELECT NaMe, c."*", ID FROM customers as c</sql>
+ <normalized>{"statement":{"sql":"SELECT NaMe, c.\"*\", ID FROM customers as c","stmt_type":"SELECT","contents":{"distinct":"false","fields":[{"expr":{"value":"NaMe"},"field_name":"NaMe"},{"expr":{"value":"c.id"},"field_name":"id","table_name":"c"},{"expr":{"value":"c.name"},"field_name":"name","table_name":"c"},{"expr":{"value":"c.default_served_by"},"field_name":"default_served_by","table_name":"c"},{"expr":{"value":"c.country"},"field_name":"country","table_name":"c"},{"expr":{"value":"c.city"},"field_name":"city","table_name":"c"},{"expr":{"value":"ID"},"field_name":"ID"}],"from":{"targets":[{"expr":{"value":"customers"},"table_name":"customers","as":"c"}]}}}}</normalized>
+ <insert>{"statement":{"sql":null,"stmt_type":"INSERT","contents":{"table":"customers","fields":["NaMe","id","default_served_by","country","city"],"values":[[{"value":null,"param_spec":{"name":"+0","descr":null,"type":"gchararray","is_param":false,"nullok":false}},{"value":null,"param_spec":{"name":"+1","descr":null,"type":"gint","is_param":false,"nullok":false}},{"value":null,"param_spec":{"name":"+3","descr":null,"type":"gint","is_param":false,"nullok":true}},{"value":null,"param_spec":{"name":"+4","descr":null,"type":"gchararray","is_param":false,"nullok":true}},{"value":null,"param_spec":{"name":"+5","descr":null,"type":"gchararray","is_param":false,"nullok":true}}]]}}}</insert>
+ <update>{"statement":{"sql":null,"stmt_type":"UPDATE","contents":{"table":"customers","fields":["NaMe","id","default_served_by","country","city"],"expressions":[{"value":null,"param_spec":{"name":"+0","descr":null,"type":"gchararray","is_param":false,"nullok":false}},{"value":null,"param_spec":{"name":"+1","descr":null,"type":"gint","is_param":false,"nullok":false}},{"value":null,"param_spec":{"name":"+3","descr":null,"type":"gint","is_param":false,"nullok":true}},{"value":null,"param_spec":{"name":"+4","descr":null,"type":"gchararray","is_param":false,"nullok":true}},{"value":null,"param_spec":{"name":"+5","descr":null,"type":"gchararray","is_param":false,"nullok":true}}],"condition":{"operation":{"operator":"=","operand0":{"value":"id"},"operand1":{"value":null,"param_spec":{"name":"-0","descr":null,"type":"gint","is_param":false,"nullok":false}}}}}}}</update>
+ <delete>{"statement":{"sql":null,"stmt_type":"DELETE","contents":{"table":"customers","condition":{"operation":{"operator":"=","operand0":{"value":"id"},"operand1":{"value":null,"param_spec":{"name":"-0","descr":null,"type":"gint","is_param":false,"nullok":false}}}}}}}</delete>
+ </test>
+
</testdata>
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]