[libgda/wip/xml-ddl-tool: 1/2] A new object GdaDdlFkey was added



commit ca3b4d256304ec608fcb94bb714a0ec61f383e29
Author: Pavlo Solntsev <p sun fun gmail com>
Date:   Sun Apr 22 20:04:51 2018 -0500

    A new object GdaDdlFkey was added
    
    Object and corresponding test was added.

 doc/C/libgda-sections.txt  |  26 +++
 doc/C/libgda.types.in      |   4 +
 libgda/Makefile.am         |   6 +-
 libgda/gda-ddl-fkey.c      | 528 +++++++++++++++++++++++++++++++++++++++++++++
 libgda/gda-ddl-fkey.h      | 100 +++++++++
 tests/ddl/Makefile.am      |   9 +-
 tests/ddl/check-ddl-fkey.c | 219 +++++++++++++++++++
 tests/ddl/fkey_test.xml    |   7 +
 8 files changed, 896 insertions(+), 3 deletions(-)
---
diff --git a/doc/C/libgda-sections.txt b/doc/C/libgda-sections.txt
index 75bd426b0..8dc011d68 100644
--- a/doc/C/libgda-sections.txt
+++ b/doc/C/libgda-sections.txt
@@ -1,4 +1,30 @@
 <SECTION>
+<FILE>gda-ddl-fkey</FILE>
+<TITLE>GdaDdlFkey</TITLE>
+GDA_TYPE_DDL_FKEY
+GdaDdlFkeyClass
+GdaDdlFkeyReferenceAction
+GdaDdlFkeyError
+GDA_DDL_FKEY_ERROR
+gda_ddl_fkey_error_quark
+gda_ddl_fkey_new
+gda_ddl_fkey_get_field_name
+gda_ddl_fkey_get_ref_field
+gda_ddl_fkey_set_field
+gda_ddl_fkey_get_ref_table
+gda_ddl_fkey_set_ref_table
+gda_ddl_fkey_get_ondelete
+gda_ddl_fkey_get_ondelete_id
+gda_ddl_fkey_set_ondelete
+gda_ddl_fkey_get_onupdate
+gda_ddl_fkey_get_onupdate_id
+gda_ddl_fkey_set_onupdate
+gda_ddl_fkey_parse_node
+gda_ddl_fkey_write_xml
+gda_ddl_fkey_free
+GdaDdlFkey
+</SECTION>
+<SECTION>
 <FILE>gda-column</FILE>
 <TITLE>GdaColumn</TITLE>
 GdaColumn
diff --git a/doc/C/libgda.types.in b/doc/C/libgda.types.in
index 42f5faead..92f4604e4 100644
--- a/doc/C/libgda.types.in
+++ b/doc/C/libgda.types.in
@@ -7,6 +7,8 @@
 #include <gda-report-document.h>
 #include <RML/gda-report-rml-document.h>
 #include <DocBook/gda-report-docbook-document.h>
+#include <libgda/gda-ddl-fkey.h>
+#include <libgda/gda-ddl-base.h>
 gda_blob_op_get_type
 gda_column_get_type
 gda_config_get_type
@@ -67,3 +69,5 @@ gda_tree_mgr_select_get_type
 gda_tree_mgr_tables_get_type
 gda_tree_node_get_type
 gda_sql_builder_get_type
+gda_ddl_fkey_get_type
+gda_ddl_base_get_type
diff --git a/libgda/Makefile.am b/libgda/Makefile.am
index c0695597d..9b7cf4e4c 100644
--- a/libgda/Makefile.am
+++ b/libgda/Makefile.am
@@ -113,7 +113,8 @@ gda_headers = \
        gda-xa-transaction.h \
        libgda-global-variables.h \
        gda-data-pivot.h \
-       gda-ddl-base.h
+       gda-ddl-base.h \
+       gda-ddl-fkey.h
 
 gda_built_sources= \
        $(builddir)/libgda.h
@@ -167,7 +168,8 @@ gda_sources= \
        gda-value.c \
        gda-xa-transaction.c \
        gda-data-pivot.c \
-       gda-ddl-base.c
+       gda-ddl-base.c \
+       gda-ddl-fkey.c
 
 libgda_sources =  \
        csv.h \
diff --git a/libgda/gda-ddl-fkey.c b/libgda/gda-ddl-fkey.c
new file mode 100644
index 000000000..be4bed42d
--- /dev/null
+++ b/libgda/gda-ddl-fkey.c
@@ -0,0 +1,528 @@
+/* gda-ddl-fkey.c
+*
+* Copyright (C) 2018 Pavlo Solntsev <p sun fun gmail com>
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#include "gda-ddl-fkey.h"
+#include <glib/gi18n-lib.h>
+#include <glib.h>
+
+G_DEFINE_QUARK (gda-ddl-fkey-error, gda_ddl_fkey_error)
+
+typedef struct
+{
+       /* /FKEY_S/%d/FKEY_REF_TABLE */
+       gchar *mp_ref_table;
+       /* /FKEY_S/%d/FKEY_FIELDS_A/@FK_FIELD */
+
+       GList  *mp_field;
+       /* /FKEY_S/FKEY_FIELDS_A/@FK_REF_PK_FIELD */
+       GList  *mp_ref_field;
+       /* /FKEY_S/FKEY_ONUPDATE This action is reserved for ONUPDATE */
+       GdaDdlFkeyReferenceAction m_onupdate;
+       /* /FKEY_S/FKEY_ONDELETE This action is reserved for ONDELETE */
+       GdaDdlFkeyReferenceAction m_ondelete;
+}GdaDdlFkeyPrivate;
+
+G_DEFINE_TYPE_WITH_PRIVATE (GdaDdlFkey, gda_ddl_fkey, G_TYPE_OBJECT)
+
+static const gchar *OnAction[] = {"NO ACTION", "SET NULL", "RESTRICT",
+       "SET DEFAULT", "CASCADE"};
+
+GdaDdlFkey *
+gda_ddl_fkey_new (void)
+{
+    return g_object_new (GDA_TYPE_DDL_FKEY, NULL);
+}
+
+static void
+gda_ddl_fkey_finalize (GObject *object)
+{
+    GdaDdlFkey *self = GDA_DDL_FKEY(object);
+    GdaDdlFkeyPrivate *priv = gda_ddl_fkey_get_instance_private (self);
+
+    if (priv->mp_field)     g_list_free_full (priv->mp_field,     g_free);
+    if (priv->mp_ref_field) g_list_free_full (priv->mp_ref_field, g_free);
+
+    g_free(priv->mp_ref_table);
+
+    G_OBJECT_CLASS (gda_ddl_fkey_parent_class)->finalize (object);
+}
+
+static void
+gda_ddl_fkey_class_init (GdaDdlFkeyClass *klass)
+{
+    GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+    object_class->finalize = gda_ddl_fkey_finalize;
+}
+
+static void
+gda_ddl_fkey_init (GdaDdlFkey *self)
+{
+       GdaDdlFkeyPrivate *priv = gda_ddl_fkey_get_instance_private (self);
+
+       priv->mp_field = NULL;
+       priv->mp_ref_field = NULL;
+       priv->mp_ref_table = NULL;
+
+       priv->m_onupdate = GDA_DDL_FKEY_NO_ACTION;
+       priv->m_ondelete = GDA_DDL_FKEY_NO_ACTION;
+}
+
+/**
+ * gda_ddl_fkey_get_ondelete:
+ * @self: An object #GdaDdlFkey
+ *
+ * Return: ON DELETE action as a string
+ *
+ * Since: 6.0
+ */
+const gchar *
+gda_ddl_fkey_get_ondelete (GdaDdlFkey *self)
+{
+    g_return_val_if_fail (self,NULL);
+
+    GdaDdlFkeyPrivate *priv = gda_ddl_fkey_get_instance_private (self);
+
+    return OnAction[priv->m_ondelete];
+}
+
+/**
+ * gda_ddl_fkey_get_ondelete_id:
+ *
+ * Return: ON DELETE action as a #GdaDdlFkeyReferenceAction
+ *
+ * Since: 6.0
+ */
+GdaDdlFkeyReferenceAction
+gda_ddl_fkey_get_ondelete_id (GdaDdlFkey *self)
+{
+    GdaDdlFkeyPrivate *priv = gda_ddl_fkey_get_instance_private (self);
+
+    return priv->m_ondelete;
+}
+
+/**
+ * gda_ddl_fkey_set_onupdate:
+ * @sefl: An object #GdaDdlFkey
+ * @id: Action to set
+ *
+ * Set action for ON UPDATE
+ *
+ */
+void
+gda_ddl_fkey_set_onupdate (GdaDdlFkey *self,
+                          GdaDdlFkeyReferenceAction id)
+{
+       g_return_if_fail (self);
+
+       GdaDdlFkeyPrivate *priv = gda_ddl_fkey_get_instance_private (self);
+
+       priv->m_onupdate = id;
+}
+
+/**
+ * gda_ddl_fkey_set_ondelete:
+ * @sefl: An object #GdaDdlFkey
+ * @id: Action to set
+ *
+ * Set action for ON DELETE
+ *
+ */
+void
+gda_ddl_fkey_set_ondelete (GdaDdlFkey *self,
+                          GdaDdlFkeyReferenceAction id)
+{
+       g_return_if_fail (self);
+
+       GdaDdlFkeyPrivate *priv = gda_ddl_fkey_get_instance_private (self);
+
+       priv->m_ondelete = id;
+}
+
+
+/**
+ * gda_ddl_fkey_get_onupdate:
+ *
+ * Return: ON UPDATE action as a string
+ *
+ * Since: 6.0
+ */
+const gchar *
+gda_ddl_fkey_get_onupdate (GdaDdlFkey *self)
+{
+    g_return_val_if_fail (self,NULL);
+
+    GdaDdlFkeyPrivate *priv = gda_ddl_fkey_get_instance_private (self);
+
+    return OnAction[priv->m_onupdate];
+}
+
+/**
+ * gda_ddl_fkey_get_onupdate_id:
+ *
+ * Return: ON UPDATE action as a #GdaDdlFkeyReferenceAction
+ *
+ * Since: 6.0
+ */
+GdaDdlFkeyReferenceAction
+gda_ddl_fkey_get_onupdate_id (GdaDdlFkey *self)
+{
+    GdaDdlFkeyPrivate *priv = gda_ddl_fkey_get_instance_private (self);
+
+    return priv->m_onupdate;
+}
+
+
+/**
+ * gda_ddl_fkey_get_ref_table:
+ *
+ * Return: Returns reference table name as a string
+ *
+ * Since: 6.0
+ */
+const gchar *
+gda_ddl_fkey_get_ref_table (GdaDdlFkey *self)
+{
+    g_return_val_if_fail (self, NULL);
+
+    GdaDdlFkeyPrivate *priv = gda_ddl_fkey_get_instance_private (self);
+
+    return priv->mp_ref_table;
+}
+
+/**
+ * gda_ddl_fkey_set_ref_table:
+ * @self: #GdaDdlFkey object
+ * @rtable: reference table name
+ *
+ * Set reference table
+ *
+ * Since: 6.0
+ */
+void
+gda_ddl_fkey_set_ref_table (GdaDdlFkey *self,
+                           const gchar *rtable)
+{
+       g_return_if_fail (self);
+
+       GdaDdlFkeyPrivate *priv = gda_ddl_fkey_get_instance_private (self);
+       g_free (priv->mp_ref_table);
+
+       priv->mp_ref_table = g_strdup (rtable);
+}
+
+/**
+ * gda_ddl_fkey_get_field_name:
+ *
+ * Returns: A list of strings where each string corresponds to a foregin key field.
+ *
+ * Since: 6.0
+ */
+const GList *
+gda_ddl_fkey_get_field_name (GdaDdlFkey *self)
+{
+    g_return_val_if_fail (self, NULL);
+
+    GdaDdlFkeyPrivate *priv = gda_ddl_fkey_get_instance_private (self);
+
+    return priv->mp_field;
+}
+
+/**
+ * gda_ddl_fkey_get_ref_field:
+ *
+ * Returns: A list of strings where each string corresponds to a foregin key reference field.
+ *
+ * Since: 6.0
+ */
+const GList *
+gda_ddl_fkey_get_ref_field (GdaDdlFkey *self)
+{
+    g_return_val_if_fail (self,NULL);
+
+    GdaDdlFkeyPrivate *priv = gda_ddl_fkey_get_instance_private (self);
+
+    return priv->mp_ref_field;
+}
+
+/**
+ * gda_ddl_fkey_parse_node:
+ *
+ * @self: #GdaDdlFkey object
+ * @node: xml node to parse as #xmlNodePtr
+ * @error: #GError object to store error
+ *
+ * Use this method to populate corresponding #GdaDdlFkey object from xml node. Usually,
+ * this method is called from #GdaDdlCreator suring parsing the imput xml file.
+ *
+ * The corresponding DTD section suitable for parsing by this method should correspond
+ * th the following code:
+ *
+ * |[<!-- language="dtd" -->
+ * <!ELEMENT fkey (fk_field?)>
+ * <!ATTLIST fkey reftable IDREF #IMPLIED>
+ * <!ATTLIST fkey onupdate (RESTRICT|CASCADE|SET_NULL|NO_ACTION|SET_DEFAULT) #IMPLIED>
+ * <!ATTLIST fkey ondelete (RESTRICT|CASCADE|SET_NULL|NO_ACTION|SET_DEFAULT) #IMPLIED>
+ *
+ * <!ELEMENT fk_field (#PCDATA)>
+ * <!ATTLIST fk_field name     IDREF #REQUIRED>
+ * <!ATTLIST fk_field reffield IDREF #REQUIRED>
+ * ]|
+ *
+ * Returns: %FALSE if an error occures, %TRUE otherwise
+ *
+ * Since: 6.0
+ */
+gboolean
+gda_ddl_fkey_parse_node (GdaDdlFkey    *self,
+                         xmlNodePtr    node,
+                         GError     **error)
+{
+       g_return_val_if_fail (self, FALSE);
+       g_return_val_if_fail (node, FALSE);
+//    g_return_val_if_fail(!*error, FALSE);
+ /*
+       *       <fkey reftable="products" onupdate="NO_ACTION" ondelete="NO_ACTION">
+       *           <fk_field name="column_name" reffield="column_name"/>
+       *           <fk_field name="column_id" reffield="column_"/>
+       *       </fkey>
+       * */
+       GdaDdlFkeyPrivate *priv = gda_ddl_fkey_get_instance_private (self);
+       xmlChar *prop = NULL;
+
+       prop = xmlGetProp (node,(xmlChar *)"reftable");
+
+       g_assert (prop); /* Bug with xml valdation */
+
+       priv->mp_ref_table = g_strdup ((gchar *)prop);
+       xmlFree (prop);
+       prop = NULL;
+
+       prop = xmlGetProp (node,(xmlChar *)"onupdate");
+
+       g_assert(prop);
+
+       priv->m_onupdate = GDA_DDL_FKEY_NO_ACTION;
+
+       for (guint i = 0; i < G_N_ELEMENTS(OnAction);i++) {
+               if (!g_strcmp0 (prop,OnAction[i]))
+                       priv->m_onupdate = (GdaDdlFkeyReferenceAction)i;
+       }
+
+       xmlFree (prop);
+       prop = NULL;
+
+       prop = xmlGetProp (node,(xmlChar *)"ondelete");
+
+       g_assert(prop);
+
+       for (guint i = 0; i < G_N_ELEMENTS(OnAction);i++) {
+               if (!g_strcmp0 (prop,OnAction[i]))
+                       priv->m_ondelete = (GdaDdlFkeyReferenceAction)i;
+       }
+
+       xmlFree (prop);
+       prop = NULL;
+
+       xmlChar *name = NULL;
+       xmlChar *reffield = NULL;
+
+       for (xmlNodePtr it = node->children; it; it = it->next) {
+               if (!g_strcmp0 ((gchar *)it->name, "fk_field")) {
+                       name = xmlGetProp (it,(xmlChar *)"name");
+
+                       g_assert(name);
+                       priv->mp_field = g_list_append (priv->mp_field,
+                                                       g_strdup ((const gchar *)name));
+                       xmlFree (name);
+
+                       reffield = xmlGetProp (it, (xmlChar *)"reffield");
+                       g_assert(reffield);
+                       priv->mp_ref_field = g_list_append (priv->mp_ref_field,
+                                                           g_strdup ((const gchar *)reffield));
+                       xmlFree (reffield);
+                       } /* end of if */
+                                                                       } /* end of for loop */
+       return TRUE;
+}
+
+/**
+ * gda_ddl_fkey_free:
+ *
+ * Convenient method to free the object. It is a wrap around g_clear_object()
+ *
+ * Since: 6.0
+ */
+void
+gda_ddl_fkey_free (GdaDdlFkey *self)
+{
+    g_clear_object (&self);
+}
+
+
+/**
+ * gda_ddl_fkey_set_field:
+ *
+ * @self: An object #GdaDdlFkey
+ * @field: Field name as a string
+ * @reffield: A reference field name as a string
+ *
+ * Returns:
+ */
+void
+gda_ddl_fkey_set_field (GdaDdlFkey  *self,
+                       const gchar *field,
+                       const gchar *reffield)
+{
+       g_return_if_fail (self);
+       g_return_if_fail (field);
+       g_return_if_fail (reffield);
+
+       GdaDdlFkeyPrivate *priv = gda_ddl_fkey_get_instance_private (self);
+
+       priv->mp_field = g_list_append (priv->mp_field,(gpointer)field);
+       priv->mp_ref_field = g_list_append(priv->mp_ref_field,(gpointer)reffield);
+}
+
+/**
+ * gda_ddl_fkey_write_xml:
+ * @self: An object #GdaDdlFkey
+ * @writer: An object to #xmlTextWriterPtr instance
+ * @error: A place to store error
+ *
+ * An appropriate encoding and version should be placed to the writer before
+ * it is used. This method will not add any header, just populate appropriate
+ * fields in the xml tree. The new xml block should be something like:
+ *
+ * |[<!-- language="xml" -->
+ * <fkey reftable="products" onupdate="NO_ACTION" ondelete="NO_ACTION">
+ *     <fk_field name="column_namefk1" reffield="column_name1"/>
+ *     <fk_field name="column_namefk2" reffield="column_name2"/>
+ *     <fk_field name="column_namefk3" reffield="column_name3"/>
+ * </fkey>
+ *]|
+ * Returns: TRUE if no error, FALSE otherwise
+ */
+gboolean
+gda_ddl_fkey_write_xml (GdaDdlFkey  *self,
+                       xmlTextWriterPtr writer,
+                       GError     **error)
+{
+       g_return_val_if_fail (self, FALSE);
+       g_return_val_if_fail (writer, FALSE);
+
+       GdaDdlFkeyPrivate *priv = gda_ddl_fkey_get_instance_private (self);
+
+       int res = 0;
+
+       res = xmlTextWriterStartElement(writer, BAD_CAST "fkey");
+       if (res < 0) {
+               g_set_error (error,
+                            GDA_DDL_FKEY_ERROR,
+                            GDA_DDL_FKEY_ERROR_START_ELEMENT,
+                            _("Can't set start element <fkey> in xml tree\n"));
+               return FALSE;
+       }
+
+       res = xmlTextWriterWriteAttribute (writer,"reftable",(xmlChar*)priv->mp_ref_table);
+
+       if (res < 0) {
+               g_set_error (error,
+                            GDA_DDL_FKEY_ERROR,
+                            GDA_DDL_FKEY_ERROR_ATTRIBUTE,
+                            _("Can't set reftable attribute to element <fkey>\n"));
+               return FALSE;
+       }
+
+       res = xmlTextWriterWriteAttribute (writer,"onupdate",(xmlChar*)OnAction[priv->m_onupdate]);
+
+       if (res < 0) {
+               g_set_error (error,
+                            GDA_DDL_FKEY_ERROR,
+                            GDA_DDL_FKEY_ERROR_ATTRIBUTE,
+                            _("Can't set onupdate attribute to element <fkey>\n"));
+               return FALSE;
+       }
+
+       res = xmlTextWriterWriteAttribute (writer,"ondelete",(xmlChar*)OnAction[priv->m_ondelete]);
+
+       if (res < 0) {
+               g_set_error (error,
+                            GDA_DDL_FKEY_ERROR,
+                            GDA_DDL_FKEY_ERROR_ATTRIBUTE,
+                            _("Can't set ondelete attribute to element <fkey>\n"));
+               return FALSE;
+       }
+
+       GList *it = priv->mp_field;
+       GList *jt = priv->mp_ref_field;
+
+       for (; it && jt; it = it->next, jt=jt->next ) {
+               res = xmlTextWriterStartElement(writer, BAD_CAST "fk_field");
+               if (res < 0) {
+                       g_set_error (error,
+                                    GDA_DDL_FKEY_ERROR,
+                                    GDA_DDL_FKEY_ERROR_START_ELEMENT,
+                                    _("Can't set start element <fk_field> in xml tree\n"));
+                       return FALSE;
+               }
+
+               res = xmlTextWriterWriteAttribute (writer,"name",(xmlChar*)it->data);
+
+               if (res < 0) {
+                       g_set_error (error,
+                                    GDA_DDL_FKEY_ERROR,
+                                    GDA_DDL_FKEY_ERROR_ATTRIBUTE,
+                                    _("Can't set ondelete attribute to element <fk_field>\n"));
+                       return FALSE;
+               }
+
+               res = xmlTextWriterWriteAttribute (writer,"reffield",(xmlChar*)jt->data);
+
+               if (res < 0) {
+                       g_set_error (error,
+                                    GDA_DDL_FKEY_ERROR,
+                                    GDA_DDL_FKEY_ERROR_ATTRIBUTE,
+                                    _("Can't set ondelete attribute to element <fk_field>\n"));
+                       return FALSE;
+               }
+
+               res = xmlTextWriterEndElement (writer);
+
+               if (res < 0) {
+                       g_set_error (error,
+                                    GDA_DDL_FKEY_ERROR,
+                                    GDA_DDL_FKEY_ERROR_END_ELEMENT,
+                                    _("Can't close element <fk_field>\n"));
+                       return FALSE;
+               }
+       }
+
+       res = xmlTextWriterEndElement (writer);
+
+       if (res < 0) {
+               g_set_error (error,
+                            GDA_DDL_FKEY_ERROR,
+                            GDA_DDL_FKEY_ERROR_END_ELEMENT,
+                            _("Can't close element <fkey>\n"));
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+
diff --git a/libgda/gda-ddl-fkey.h b/libgda/gda-ddl-fkey.h
new file mode 100644
index 000000000..71d1258c9
--- /dev/null
+++ b/libgda/gda-ddl-fkey.h
@@ -0,0 +1,100 @@
+/* gda-ddl-fkey.h
+ *
+ * Copyright © 2018 Pavlo Solntsev <pavlo solntsev gmail com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef GDA_DDL_FKEY_H
+#define GDA_DDL_FKEY_H
+
+#include <glib-object.h>
+#include <gmodule.h>
+#include <glib.h>
+#include <libxml/parser.h>
+#include <libxml/xmlwriter.h>
+
+G_BEGIN_DECLS
+
+#define GDA_TYPE_DDL_FKEY (gda_ddl_fkey_get_type())
+
+G_DECLARE_DERIVABLE_TYPE (GdaDdlFkey, gda_ddl_fkey, GDA, DDL_FKEY, GObject)
+
+struct _GdaDdlFkeyClass {
+       GObjectClass parent_class;
+};
+
+typedef enum {
+       GDA_DDL_FKEY_NO_ACTION,
+       GDA_DDL_FKEY_SET_NULL,
+       GDA_DDL_FKEY_RESTRICT,
+       GDA_DDL_FKEY_SET_DEFAULT,
+       GDA_DDL_FKEY_CASCADE
+} GdaDdlFkeyReferenceAction;
+
+typedef enum {
+       GDA_DDL_FKEY_ERROR_START_ELEMENT,
+       GDA_DDL_FKEY_ERROR_ATTRIBUTE,
+       GDA_DDL_FKEY_ERROR_END_ELEMENT
+}GdaDdlFkeyError;
+
+#define GDA_DDL_FKEY_ERROR gda_ddl_fkey_error_quark()
+GQuark gda_ddl_fkey_error_quark (void);
+
+/**
+ * SECTION:gda-ddl-fkey
+ * @short_description: Object to hold information for foregn key.
+ * @stability: Stable
+ * @include: libgda.h
+ *
+ * For generating database from xml file or for mapping
+ * database to an xml file #GdaDdlFkey holds information about
+ * foregn keys with a convenient set of methods to manipulate them.
+ */
+
+GdaDdlFkey*            gda_ddl_fkey_new                                (void);
+
+const GList*   gda_ddl_fkey_get_field_name     (GdaDdlFkey *self);
+const GList*   gda_ddl_fkey_get_ref_field      (GdaDdlFkey *self);
+
+void                   gda_ddl_fkey_set_field                  (GdaDdlFkey  *self,
+                                                                                                const gchar 
*field,
+                                                                                                const gchar 
*reffield);
+
+const gchar*   gda_ddl_fkey_get_ref_table      (GdaDdlFkey *self);
+void                   gda_ddl_fkey_set_ref_table              (GdaDdlFkey  *self,
+                                                                                                const gchar 
*rtable);
+
+const gchar*   gda_ddl_fkey_get_ondelete       (GdaDdlFkey *self);
+GdaDdlFkeyReferenceAction gda_ddl_fkey_get_ondelete_id (GdaDdlFkey *self);
+void                   gda_ddl_fkey_set_ondelete               (GdaDdlFkey *self,
+                                                                                                
GdaDdlFkeyReferenceAction id);
+
+const gchar*   gda_ddl_fkey_get_onupdate       (GdaDdlFkey *self);
+GdaDdlFkeyReferenceAction gda_ddl_fkey_get_onupdate_id (GdaDdlFkey *self);
+void                   gda_ddl_fkey_set_onupdate               (GdaDdlFkey *self,
+                                                                                                
GdaDdlFkeyReferenceAction id);
+gboolean               gda_ddl_fkey_parse_node         (GdaDdlFkey  *self,
+                                                                                                xmlNodePtr 
node,
+                                                                                                GError 
**error);
+
+gboolean               gda_ddl_fkey_write_xml                  (GdaDdlFkey  *self,
+                                                                                                
xmlTextWriterPtr writer,
+                                                                                                GError     
**error);
+
+void                   gda_ddl_fkey_free                       (GdaDdlFkey *self);
+
+G_END_DECLS
+
+#endif /* GDA_DDL_FKEY_H */
+
diff --git a/tests/ddl/Makefile.am b/tests/ddl/Makefile.am
index d80b69b9d..8233e0e11 100644
--- a/tests/ddl/Makefile.am
+++ b/tests/ddl/Makefile.am
@@ -9,9 +9,16 @@ AM_CPPFLAGS = \
        -DROOT_DIR=\""$(top_srcdir)"\"
 
 test_programs = \
-    check_ddl_base
+    check_ddl_base \
+    check_ddl_fkey
 
 check_ddl_base_SOURCES = check-ddl-base.c
 check_ddl_base_LDADD = \
        $(top_builddir)/libgda/libgda-6.0.la \
        $(COREDEPS_LIBS)
+
+check_ddl_fkey_SOURCES = check-ddl-fkey.c
+check_ddl_fkey_LDADD = \
+       $(top_builddir)/libgda/libgda-6.0.la \
+       $(COREDEPS_LIBS)
+
diff --git a/tests/ddl/check-ddl-fkey.c b/tests/ddl/check-ddl-fkey.c
new file mode 100644
index 000000000..56efdd6c0
--- /dev/null
+++ b/tests/ddl/check-ddl-fkey.c
@@ -0,0 +1,219 @@
+/* check-ddl-creator.c
+ *
+ * Copyright 2018 Pavlo Solntsev <p sun fun gmail com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <locale.h>
+#include <libgda/libgda.h>
+#include <libgda/gda-ddl-fkey.h>
+
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+#include <libxml/xmlwriter.h>
+
+typedef struct {
+       GdaDdlFkey *fkey;
+
+       GList *fkfield;
+       GList *reffield;
+
+       gchar *xmlfile;
+       gchar *reftable;
+       gchar *onupdate;
+       gchar *ondelete;
+
+       xmlDocPtr doc;
+       xmlTextWriterPtr writer;
+       xmlBufferPtr buffer;
+} CheckDdlObject;
+
+static void
+test_ddl_fkey_run2 (CheckDdlObject *self,
+                   gconstpointer user_data)
+{
+       const gchar *reftable = NULL;
+
+       reftable = gda_ddl_fkey_get_ref_table (self->fkey);
+
+       g_assert_cmpstr (reftable, ==, self->reftable);
+
+       const GList *fkfield = NULL;
+
+       fkfield = gda_ddl_fkey_get_field_name (self->fkey);
+
+       g_assert_nonnull (fkfield);
+
+       const GList *it = NULL;
+       GList *jt = NULL;
+
+       for (it = fkfield, jt = self->fkfield; it && jt; it = it->next, jt = jt->next)
+               g_assert_cmpstr (it->data, ==, jt->data);
+
+       const GList *reffield = NULL;
+
+       reffield = gda_ddl_fkey_get_ref_field(self->fkey);
+
+       g_assert_nonnull (reffield);
+
+       for (it = reffield, jt = self->reffield; it && jt; it = it->next, jt = jt->next)
+               g_assert_cmpstr(it->data, ==, jt->data);
+
+       const gchar *onupdate = gda_ddl_fkey_get_onupdate (self->fkey);
+
+       g_assert_cmpstr(onupdate, ==, self->onupdate);
+
+       const gchar *ondelete = gda_ddl_fkey_get_ondelete (self->fkey);
+
+       g_assert_cmpstr(ondelete, ==, self->ondelete);
+}
+
+static void
+test_ddl_fkey_run1 (void)
+{
+       GdaDdlFkey *self = gda_ddl_fkey_new ();
+
+       gda_ddl_fkey_free (self);
+}
+
+static void
+test_ddl_fkey_run3 (CheckDdlObject *self,
+                   gconstpointer user_data)
+{
+       int res = gda_ddl_fkey_write_xml (self->fkey,self->writer,NULL);
+
+       g_assert_true (res >= 0);
+
+//     res = xmlTextWriterEndDocument (self->writer);
+
+//     g_assert_true (res >= 0);
+       xmlFreeTextWriter (self->writer);
+
+       g_print ("%s\n",(gchar*)self->buffer->content);
+}
+
+static void
+test_ddl_fkey_start (CheckDdlObject *self,
+                    gconstpointer user_data)
+{
+       self->doc = NULL;
+       self->xmlfile = NULL;
+       self->fkey = NULL;
+       self->reffield = NULL;
+       self->fkfield = NULL;
+       self->reftable = NULL;
+       self->onupdate = NULL;
+       self->ondelete = NULL;
+
+       self->reffield = g_list_append (self->reffield,"column_name1");
+       self->reffield = g_list_append (self->reffield,"column_name2");
+       self->reffield = g_list_append (self->reffield,"column_name3");
+
+       self->fkfield = g_list_append(self->fkfield,"column_namefk1");
+       self->fkfield = g_list_append(self->fkfield,"column_namefk2");
+       self->fkfield = g_list_append(self->fkfield,"column_namefk3");
+
+       self->reftable = g_strdup ("products");
+       self->onupdate = g_strdup("NO ACTION");
+       self->ondelete = g_strdup("NO ACTION");
+
+       const gchar *topsrcdir = g_getenv ("GDA_TOP_SRC_DIR");
+
+       g_print ("ENV: %s\n",topsrcdir);
+       g_assert_nonnull (topsrcdir);
+
+       self->xmlfile = g_build_filename(topsrcdir,
+                                        "tests",
+                                        "ddl",
+                                        "fkey_test.xml",NULL);
+
+       g_assert_nonnull (self->xmlfile);
+
+       self->doc = xmlParseFile(self->xmlfile);
+       g_assert_nonnull (self->doc);
+
+       xmlNodePtr node = xmlDocGetRootElement (self->doc);
+       g_assert_nonnull (node);
+
+       self->fkey = gda_ddl_fkey_new ();
+
+       g_assert_nonnull(self->fkey);
+
+       gboolean res = gda_ddl_fkey_parse_node (self->fkey,
+                                               node,
+                                               NULL);
+       g_assert_true (res);
+
+       self->buffer = xmlBufferCreate ();
+
+       g_assert_nonnull (self->buffer);
+
+       self->writer = xmlNewTextWriterMemory (self->buffer,0);
+
+       g_assert_nonnull (self->writer);
+
+       res = xmlTextWriterStartDocument (self->writer, NULL, NULL, NULL);
+
+       g_assert_true (res >= 0);
+}
+
+static void
+test_ddl_fkey_finish (CheckDdlObject *self,
+                     gconstpointer user_data)
+{
+       g_free (self->xmlfile);
+       gda_ddl_fkey_free (self->fkey);
+       xmlFreeDoc (self->doc);
+       g_list_free (self->reffield);
+       g_list_free (self->fkfield);
+       g_free (self->reftable);
+       g_free (self->ondelete);
+       g_free (self->onupdate);
+
+
+       xmlBufferFree (self->buffer);
+}
+
+gint
+main (gint   argc,
+      gchar *argv[])
+{
+       setlocale (LC_ALL,"");
+
+       g_test_init (&argc,&argv,NULL);
+
+       g_test_add_func ("/test-ddl/fkey-basic",
+                        test_ddl_fkey_run1);
+
+       g_test_add ("/test-ddl/fkey-parse",
+                   CheckDdlObject,
+                   NULL,
+                   test_ddl_fkey_start,
+                   test_ddl_fkey_run2,
+                   test_ddl_fkey_finish);
+
+       g_test_add ("/test-ddl/fkey-write",
+                   CheckDdlObject,
+                   NULL,
+                   test_ddl_fkey_start,
+                   test_ddl_fkey_run3,
+                   test_ddl_fkey_finish);
+
+       return g_test_run();
+}
diff --git a/tests/ddl/fkey_test.xml b/tests/ddl/fkey_test.xml
new file mode 100644
index 000000000..2c1b8d5fb
--- /dev/null
+++ b/tests/ddl/fkey_test.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<fkey reftable="products" onupdate="NO_ACTION" ondelete="NO_ACTION">
+    <fk_field name="column_namefk1" reffield="column_name1"/>
+    <fk_field name="column_namefk2" reffield="column_name2"/>
+    <fk_field name="column_namefk3" reffield="column_name3"/>
+</fkey>
+


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