/* * Copyright (C) 2008 - 2010 Murray Cumming * Copyright (C) 2008 - 2012 Vivien Malerba * Copyright (C) 2009 Bas Driessen * Copyright (C) 2010 David King * Copyright (C) 2010 Jonh Wendell * Copyright (C) 2012 Daniel Espinosa * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include /* * Main static functions */ static void gda_meta_struct_class_init (GdaMetaStructClass *klass); static void gda_meta_struct_init (GdaMetaStruct *mstruct); static void gda_meta_struct_dispose (GObject *object); static void gda_meta_struct_finalize (GObject *object); static void gda_meta_struct_set_property (GObject *object, guint param_id, const GValue *value, GParamSpec *pspec); static void gda_meta_struct_get_property (GObject *object, guint param_id, GValue *value, GParamSpec *pspec); static void meta_store_changed_cb (GdaMetaStore *store, GSList *changes, GdaMetaStruct *mstruct); static void meta_store_reset_cb (GdaMetaStore *store, GdaMetaStruct *mstruct); struct _GdaMetaStructPrivate { GdaMetaStore *store; GSList *db_objects; /* list of GdaMetaDbObject structures */ GHashTable *index; /* key = [catalog].[schema].[name], value = a GdaMetaDbObject. Note: catalog, schema and name * are case sensitive (and don't have any double quote around them) */ guint features; }; static GdaMetaDbObject *_meta_struct_get_db_object (GdaMetaStruct *mstruct, const GValue *catalog, const GValue *schema, const GValue *name); static void gda_meta_db_object_free (GdaMetaDbObject *dbo); static void gda_meta_db_object_free_contents (GdaMetaDbObject *dbo); static void gda_meta_table_free_contents (GdaMetaTable *table); static void gda_meta_view_free_contents (GdaMetaView *view); static void gda_meta_table_column_free (GdaMetaTableColumn *tcol); static void gda_meta_table_foreign_key_free (GdaMetaTableForeignKey *tfk); /* get a pointer to the parents to be able to call their destructor */ static GObjectClass *parent_class = NULL; static GdaAttributesManager *att_mgr; /* properties */ enum { PROP_0, PROP_STORE, PROP_FEATURES }; /* module error */ GQuark gda_meta_struct_error_quark (void) { static GQuark quark; if (!quark) quark = g_quark_from_static_string ("gda_meta_struct_error"); return quark; } GType gda_meta_struct_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) { static GMutex registering; static const GTypeInfo info = { sizeof (GdaMetaStructClass), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, (GClassInitFunc) gda_meta_struct_class_init, NULL, NULL, sizeof (GdaMetaStruct), 0, (GInstanceInitFunc) gda_meta_struct_init, 0 }; g_mutex_lock (®istering); if (type == 0) type = g_type_register_static (G_TYPE_OBJECT, "GdaMetaStruct", &info, 0); g_mutex_unlock (®istering); } return type; } static void gda_meta_struct_class_init (GdaMetaStructClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); parent_class = g_type_class_peek_parent (klass); /* Properties */ object_class->set_property = gda_meta_struct_set_property; object_class->get_property = gda_meta_struct_get_property; g_object_class_install_property (object_class, PROP_STORE, /* To translators: GdaMetaStore is an object holding meta data information * which will be used as an information source */ g_param_spec_object ("meta-store", NULL, _ ("GdaMetaStore object to fetch information from"), GDA_TYPE_META_STORE, (G_PARAM_WRITABLE | G_PARAM_READABLE | G_PARAM_CONSTRUCT_ONLY))); g_object_class_install_property (object_class, PROP_FEATURES, /* To translators: The GdaMetaStruct object computes lists of tables, views, * and some other information, the "Features to compute" allows one to avoid * computing some features which won't be used */ g_param_spec_uint ("features", _ ("Features to compute"), NULL, GDA_META_STRUCT_FEATURE_NONE, G_MAXINT, GDA_META_STRUCT_FEATURE_ALL, (G_PARAM_WRITABLE | G_PARAM_READABLE | G_PARAM_CONSTRUCT_ONLY))); /* virtual methods */ object_class->dispose = gda_meta_struct_dispose; object_class->finalize = gda_meta_struct_finalize; /* extra */ att_mgr = gda_attributes_manager_new (FALSE, NULL, NULL); } static void gda_meta_struct_init (GdaMetaStruct *mstruct) { mstruct->priv = g_new0 (GdaMetaStructPrivate, 1); mstruct->priv->store = NULL; mstruct->priv->db_objects = NULL; mstruct->priv->index = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); } /** * gda_meta_struct_new: * @store: a #GdaMetaStore from which the new #GdaMetaStruct object will fetch information * @features: the kind of extra information the new #GdaMetaStruct object will compute * * Creates a new #GdaMetaStruct object. The @features specifies the extra features which will also be computed: * the more features, the more time it takes to run. Features such as table's columns, each column's attributes, etc * are not optional and will always be computed. * * Returns: (transfer full): the newly created #GdaMetaStruct object */ GdaMetaStruct * gda_meta_struct_new (GdaMetaStore *store, GdaMetaStructFeature features) { g_return_val_if_fail (GDA_IS_META_STORE (store), NULL); return (GdaMetaStruct*) g_object_new (GDA_TYPE_META_STRUCT, "meta-store", store, "features", features, NULL); } static void gda_meta_struct_dispose (GObject *object) { GdaMetaStruct *mstruct; g_return_if_fail (object != NULL); g_return_if_fail (GDA_IS_META_STRUCT (object)); mstruct = GDA_META_STRUCT (object); if (mstruct->priv->store) { g_signal_handlers_disconnect_by_func (G_OBJECT (mstruct->priv->store), G_CALLBACK (meta_store_changed_cb), mstruct); g_signal_handlers_disconnect_by_func (G_OBJECT (mstruct->priv->store), G_CALLBACK (meta_store_reset_cb), mstruct); g_object_ref (mstruct->priv->store); mstruct->priv->store = NULL; } /* parent class */ parent_class->finalize (object); } static void gda_meta_struct_finalize (GObject *object) { GdaMetaStruct *mstruct; g_return_if_fail (object != NULL); g_return_if_fail (GDA_IS_META_STRUCT (object)); mstruct = GDA_META_STRUCT (object); if (mstruct->priv) { g_slist_foreach (mstruct->priv->db_objects, (GFunc) gda_meta_db_object_free, NULL); g_slist_free (mstruct->priv->db_objects); g_hash_table_destroy (mstruct->priv->index); g_free (mstruct->priv); mstruct->priv = NULL; } /* parent class */ parent_class->finalize (object); } static void gda_meta_struct_set_property (GObject *object, guint param_id, const GValue *value, GParamSpec *pspec) { GdaMetaStruct *mstruct; mstruct = GDA_META_STRUCT (object); if (mstruct->priv) { switch (param_id) { case PROP_STORE: mstruct->priv->store = g_value_get_object (value); if (mstruct->priv->store) { g_object_ref (mstruct->priv->store); g_signal_connect (G_OBJECT (mstruct->priv->store), "meta-changed", G_CALLBACK (meta_store_changed_cb), mstruct); g_signal_connect (G_OBJECT (mstruct->priv->store), "meta-reset", G_CALLBACK (meta_store_reset_cb), mstruct); } break; case PROP_FEATURES: mstruct->priv->features = g_value_get_uint (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); break; } } } static void gda_meta_struct_get_property (GObject *object, guint param_id, GValue *value, GParamSpec *pspec) { GdaMetaStruct *mstruct; mstruct = GDA_META_STRUCT (object); if (mstruct->priv) { switch (param_id) { case PROP_STORE: g_value_set_object (value, mstruct->priv->store); break; case PROP_FEATURES: g_value_set_uint (value, mstruct->priv->features); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); break; } } } static void meta_store_changed_cb (GdaMetaStore *store, G_GNUC_UNUSED GSList *changes, GdaMetaStruct *mstruct) { /* for now we mark ALL the db objects as outdated */ meta_store_reset_cb (store, mstruct); } static void meta_store_reset_cb (G_GNUC_UNUSED GdaMetaStore *store, GdaMetaStruct *mstruct) { /* mark ALL the db objects as outdated */ GSList *list; for (list = mstruct->priv->db_objects; list; list = list->next) GDA_META_DB_OBJECT (list->data)->outdated = TRUE; } static void compute_view_dependencies (GdaMetaStruct *mstruct, GdaMetaDbObject *view_dbobj, GdaSqlStatement *sqlst) { if (sqlst->stmt_type == GDA_SQL_STATEMENT_SELECT) { GdaSqlStatementSelect *selst; selst = (GdaSqlStatementSelect*) (sqlst->contents); GSList *targets = NULL; if (selst->from) targets = selst->from->targets; for (; targets; targets = targets->next) { GdaSqlSelectTarget *t = (GdaSqlSelectTarget *) targets->data; GValue *catalog, *schema, *name; GdaMetaDbObject *ref_obj, *tmp_obj; GdaMetaStruct *m2; GValue *vname; if (!t->table_name) continue; m2 = gda_meta_struct_new (mstruct->priv->store, GDA_META_STRUCT_FEATURE_NONE); g_value_set_string ((vname = gda_value_new (G_TYPE_STRING)), t->table_name); if (! (tmp_obj = gda_meta_struct_complement (m2, GDA_META_DB_TABLE, NULL, NULL, vname, NULL))) tmp_obj = gda_meta_struct_complement (m2, GDA_META_DB_VIEW, NULL, NULL, vname, NULL); gda_value_free (vname); if (!tmp_obj) { /* could not find dependency */ g_object_unref (m2); continue; } /* the dependency exists, and is identified by tmp_obj->obj_catalog, tmp_obj->obj_schema * and tmp_obj->obj_name */ g_value_set_string ((catalog = gda_value_new (G_TYPE_STRING)), tmp_obj->obj_catalog); g_value_set_string ((schema = gda_value_new (G_TYPE_STRING)), tmp_obj->obj_schema); g_value_set_string ((name = gda_value_new (G_TYPE_STRING)), tmp_obj->obj_name); ref_obj = _meta_struct_get_db_object (mstruct, catalog, schema, name); if (!ref_obj) { gchar *str; ref_obj = g_new0 (GdaMetaDbObject, 1); ref_obj->obj_type = GDA_META_DB_UNKNOWN; ref_obj->obj_catalog = g_strdup (tmp_obj->obj_catalog); ref_obj->obj_schema = g_strdup (tmp_obj->obj_schema); ref_obj->obj_name = g_strdup (tmp_obj->obj_name); mstruct->priv->db_objects = g_slist_append (mstruct->priv->db_objects, ref_obj); str = g_strdup_printf ("%s.%s.%s", tmp_obj->obj_catalog, tmp_obj->obj_schema, tmp_obj->obj_name); g_hash_table_insert (mstruct->priv->index, str, ref_obj); } g_object_unref (m2); g_assert (ref_obj); gda_value_free (catalog); gda_value_free (schema); gda_value_free (name); view_dbobj->depend_list = g_slist_append (view_dbobj->depend_list, ref_obj); } } else if (sqlst->stmt_type == GDA_SQL_STATEMENT_COMPOUND) { GdaSqlStatementCompound *cst; GSList *list; cst = (GdaSqlStatementCompound*) (sqlst->contents); for (list = cst->stmt_list; list; list = list->next) compute_view_dependencies (mstruct, view_dbobj, (GdaSqlStatement*) list->data); } else g_assert_not_reached (); } static GdaMetaDbObject * _meta_struct_complement (GdaMetaStruct *mstruct, GdaMetaDbObjectType type, const GValue *icatalog, const GValue *ischema, const GValue *iname, const GValue *short_name, const GValue *full_name, const GValue *owner, GError **error); static gboolean determine_db_object_from_schema_and_name (GdaMetaStruct *mstruct, GdaMetaDbObjectType *int_out_type, GValue **out_catalog, GValue **out_short_name, GValue **out_full_name, GValue **out_owner, const GValue *schema, const GValue *name); static gboolean determine_db_object_from_short_name (GdaMetaStruct *mstruct, GdaMetaDbObjectType *int_out_type, GValue **out_catalog, GValue **out_schema, GValue **out_name, GValue **out_short_name, GValue **out_full_name, GValue **out_owner, const GValue *name); static gboolean determine_db_object_from_missing_type (GdaMetaStruct *mstruct, GdaMetaDbObjectType *out_type, GValue **out_short_name, GValue **out_full_name, GValue **out_owner, const GValue *catalog, const GValue *schema, const GValue *name); static gchar *array_type_to_sql (GdaMetaStore *store, const GValue *specific_name); static gchar * get_user_obj_name (const GValue *catalog, const GValue *schema, const GValue *name) { GString *string = NULL; gchar *ret; if (catalog && (G_VALUE_TYPE (catalog) != GDA_TYPE_NULL)) string = g_string_new (g_value_get_string (catalog)); if (schema && (G_VALUE_TYPE (schema) != GDA_TYPE_NULL)) { if (string) { g_string_append_c (string, '.'); g_string_append (string, g_value_get_string (schema)); } else string = g_string_new (g_value_get_string (schema)); } if (name && (G_VALUE_TYPE (name) != GDA_TYPE_NULL)) { if (string) { g_string_append_c (string, '.'); g_string_append (string, g_value_get_string (name)); } else string = g_string_new (g_value_get_string (name)); } ret = string->str; g_string_free (string, FALSE); return ret; } static gchar * prepare_sql_identifier_for_compare (gchar *str) { if (!str || (*str == '"')) return str; else { gchar *ptr; for (ptr = str; *ptr; ptr++) *ptr = g_ascii_tolower (*ptr); return str; } } /** * gda_meta_struct_complement: * @mstruct: a #GdaMetaStruct object * @type: the type of object to add (which can be GDA_META_DB_UNKNOWN) * @catalog: (allow-none): the catalog the object belongs to (as a G_TYPE_STRING GValue), or %NULL * @schema: (allow-none): the schema the object belongs to (as a G_TYPE_STRING GValue), or %NULL * @name: the object's name (as a G_TYPE_STRING GValue), not %NULL * @error: (allow-none): a place to store errors, or %NULL * * Creates a new #GdaMetaDbObject structure in @mstruct to represent the database object (of type @type) * which can be uniquely identified as @catalog.@schema.@name. * * If @catalog is not %NULL, then @schema should not be %NULL. * * If both @catalog and @schema are %NULL, then the database object will be the one which is * "visible" by default (that is which can be accessed only by its short @name name). * * If @catalog is %NULL and @schema is not %NULL, then the database object will be the one which * can be accessed by its @schema.@name name. * * Important note: @catalog, @schema and @name will be used using the following convention: * * be surrounded by double quotes for a case sensitive search * otherwise for case insensitive search * * * For more information, see the * meta data section about SQL identifiers. * * Returns: (transfer none): the #GdaMetaDbObject corresponding to the database object if no error occurred, or %NULL */ GdaMetaDbObject * gda_meta_struct_complement (GdaMetaStruct *mstruct, GdaMetaDbObjectType type, const GValue *catalog, const GValue *schema, const GValue *name, GError **error) { GdaMetaDbObject *dbo = NULL; GdaMetaDbObjectType real_type = type; GValue *short_name = NULL, *full_name=NULL, *owner=NULL; GValue *icatalog = NULL, *ischema = NULL, *iname = NULL; /* GValue with identifiers ready to be compared */ /* checks */ g_return_val_if_fail (mstruct->priv->store, NULL); g_return_val_if_fail (GDA_IS_META_STRUCT (mstruct), NULL); g_return_val_if_fail (name && (G_VALUE_TYPE (name) == G_TYPE_STRING), NULL); if (catalog && (gda_value_is_null (catalog) || !g_value_get_string (catalog))) catalog = NULL; if (schema && (gda_value_is_null (schema) || !g_value_get_string (schema))) schema = NULL; g_return_val_if_fail (!catalog || (catalog && schema), NULL); g_return_val_if_fail (!catalog || (G_VALUE_TYPE (catalog) == G_TYPE_STRING), NULL); g_return_val_if_fail (!schema || (G_VALUE_TYPE (schema) == G_TYPE_STRING), NULL); /* create ready to compare strings for catalog, schema and name */ gchar *schema_s, *name_s; if (_split_identifier_string (g_value_dup_string (name), &schema_s, &name_s)) { g_value_take_string ((iname = gda_value_new (G_TYPE_STRING)), prepare_sql_identifier_for_compare (name_s)); if (schema_s) g_value_take_string ((ischema = gda_value_new (G_TYPE_STRING)), prepare_sql_identifier_for_compare (schema_s)); } else g_value_take_string ((iname = gda_value_new (G_TYPE_STRING)), prepare_sql_identifier_for_compare (g_value_dup_string (name))); if (catalog) g_value_take_string ((icatalog = gda_value_new (G_TYPE_STRING)), prepare_sql_identifier_for_compare (g_value_dup_string (catalog))); if (schema && !ischema) g_value_take_string ((ischema = gda_value_new (G_TYPE_STRING)), prepare_sql_identifier_for_compare (g_value_dup_string (schema))); if (!icatalog) { if (ischema) { g_return_val_if_fail (ischema && (G_VALUE_TYPE (ischema) == G_TYPE_STRING), NULL); if (! determine_db_object_from_schema_and_name (mstruct, &real_type, &icatalog, &short_name, &full_name, &owner, ischema, iname)) { gchar *tmp; tmp = get_user_obj_name (catalog, schema, name); g_set_error (error, GDA_META_STRUCT_ERROR, GDA_META_STRUCT_UNKNOWN_OBJECT_ERROR, _("Could not find object named '%s'"), tmp); g_free (tmp); gda_value_free (ischema); gda_value_free (iname); return NULL; } } else { GValue *real_name = NULL; if (! determine_db_object_from_short_name (mstruct, &real_type, &icatalog, &ischema, &real_name, &short_name, &full_name, &owner, iname)) { gchar *tmp; tmp = get_user_obj_name (catalog, schema, name); g_set_error (error, GDA_META_STRUCT_ERROR, GDA_META_STRUCT_UNKNOWN_OBJECT_ERROR, _("Could not find object named '%s'"), tmp); g_free (tmp); gda_value_free (iname); return NULL; } if (real_name) { gda_value_free (iname); iname = real_name; } } } else if (type == GDA_META_DB_UNKNOWN) { if (! determine_db_object_from_missing_type (mstruct, &real_type, &short_name, &full_name, &owner, icatalog, ischema, iname)) { gchar *tmp; tmp = get_user_obj_name (catalog, schema, name); g_set_error (error, GDA_META_STRUCT_ERROR, GDA_META_STRUCT_UNKNOWN_OBJECT_ERROR, _("Could not find object named '%s'"), tmp); g_free (tmp); gda_value_free (icatalog); gda_value_free (ischema); gda_value_free (iname); return NULL; } } type = real_type; dbo = _meta_struct_complement (mstruct, type, icatalog, ischema, iname, short_name, full_name, owner, error); gda_value_free (icatalog); gda_value_free (ischema); gda_value_free (iname); if (short_name) gda_value_free (short_name); if (full_name) gda_value_free (full_name); if (owner) gda_value_free (owner); return dbo; } static GdaMetaForeignKeyPolicy policy_string_to_value (const gchar *string) { if (!string) return GDA_META_FOREIGN_KEY_UNKNOWN; if (*string == 'C') return GDA_META_FOREIGN_KEY_CASCADE; else if (*string == 'R') return GDA_META_FOREIGN_KEY_RESTRICT; else if (*string == 'N') { if (!strcmp (string + 1, "ONE")) return GDA_META_FOREIGN_KEY_NONE; else return GDA_META_FOREIGN_KEY_NO_ACTION; } else if (*string == 'S') { if (!strcmp (string + 1, "ET NULL")) return GDA_META_FOREIGN_KEY_SET_NULL; else return GDA_META_FOREIGN_KEY_SET_DEFAULT; } return GDA_META_FOREIGN_KEY_UNKNOWN; } /* * Find if there is already a declared foreign for @dbo to @ref_dbo using the columns * listed in @columns * * @columns contains 4 columns, with no NULL value, and with nrows>0 * - @dbo's column name (string) * - @dbo's column name ordinal position (int) * - @ref_dbo's column name (string) * - @ref_dbo's column name ordinal position (int) */ static GdaMetaTableForeignKey * find_real_foreign_key (GdaMetaTable *table, GdaMetaDbObject *ref_dbo, GdaDataModel *columns) { GSList *list; gint nrows = -1; for (list = table->fk_list; list; list = list->next) { GdaMetaTableForeignKey *fk = (GdaMetaTableForeignKey*) list->data; gint i; gboolean *mapping; if (fk->depend_on != ref_dbo) continue; if (nrows == -1) nrows = gda_data_model_get_n_rows (columns); if (nrows != fk->cols_nb) continue; mapping = g_new0 (gboolean, nrows); for (i = 0; i < nrows; i++) { const GValue *cvalues[4]; const gchar *fk_colname, *ref_colname; gint fk_pos, ref_pos; gint j; for (j = 0; j < 4; j++) { cvalues[j] = gda_data_model_get_value_at (columns, j, i, NULL); if (!cvalues[j]) { g_free (mapping); goto out; } } fk_colname = g_value_get_string (cvalues[0]); fk_pos = g_value_get_int (cvalues[1]); ref_colname = g_value_get_string (cvalues[2]); ref_pos = g_value_get_int (cvalues[3]); if (!fk_colname || !*fk_colname || !ref_colname || !*ref_colname) { g_free (mapping); goto out; } for (j = 0; j < nrows; j++) { if (mapping [j]) /* row already mapped? */ continue; if ((fk_pos == fk->fk_cols_array[j]) && (ref_pos == fk->ref_pk_cols_array[j]) && !strcmp (fk_colname, fk->fk_names_array[j]) && !strcmp (ref_colname, fk->ref_pk_names_array[j])) { mapping [j] = TRUE; break; } } if (j == nrows) /* i'th row has not been mapped */ break; } g_free (mapping); if (i == nrows) /* all the rows have been mapped */ return fk; else continue; } out: return NULL; } static gboolean add_declared_foreign_keys (GdaMetaStruct *mstruct, GdaMetaTable *mt, GError **error) { const gchar *sql1 = "SELECT DISTINCT constraint_name, ref_table_catalog, ref_table_schema, ref_table_name FROM __declared_fk WHERE table_catalog = ##tc::string AND table_schema = ##ts::string AND table_name = ##tname::string ORDER BY constraint_name, ref_table_catalog, ref_table_schema, ref_table_name"; const gchar *sql2 = "select de.column_name, c.ordinal_position, de.ref_column_name, rc.ordinal_position FROM __declared_fk de LEFT JOIN _columns c ON (de.table_catalog=c.table_catalog AND de.table_schema=c.table_schema AND de.table_name=c.table_name AND de.column_name=c.column_name) LEFT JOIN _columns rc ON (de.ref_table_catalog=rc.table_catalog AND de.ref_table_schema=rc.table_schema AND de.ref_table_name=rc.table_name AND de.ref_column_name=rc.column_name) WHERE de.constraint_name=##cname::string AND de.table_catalog=##tc::string AND de.table_schema=##ts::string AND de.table_name=##tname::string"; GdaDataModel *model, *cmodel = NULL; gint i, nrows; GValue *v1, *v2, *v3; GdaMetaDbObject *dbo; dbo = (GdaMetaDbObject*) mt; g_value_set_string ((v1 = gda_value_new (G_TYPE_STRING)), dbo->obj_catalog); g_value_set_string ((v2 = gda_value_new (G_TYPE_STRING)), dbo->obj_schema); g_value_set_string ((v3 = gda_value_new (G_TYPE_STRING)), dbo->obj_name); model = gda_meta_store_extract (mstruct->priv->store, sql1, error, "tc", v1, "ts", v2, "tname", v3, NULL); if (!model) goto onerror; nrows = gda_data_model_get_n_rows (model); for (i = 0; i < nrows; i++) { GdaMetaDbObject *ref_dbo = NULL; GdaMetaTableForeignKey *tfk = NULL; const GValue *fk_catalog, *fk_schema, *fk_tname, *fk_name; gboolean ignore = FALSE; fk_name = gda_data_model_get_value_at (model, 0, i, error); if (!fk_name) goto onerror; fk_catalog = gda_data_model_get_value_at (model, 1, i, error); if (!fk_catalog) goto onerror; fk_schema = gda_data_model_get_value_at (model, 2, i, error); if (!fk_schema) goto onerror; fk_tname = gda_data_model_get_value_at (model, 3, i, error); if (!fk_tname) goto onerror; /* get columns */ cmodel = gda_meta_store_extract (mstruct->priv->store, sql2, error, "tc", v1, "ts", v2, "tname", v3, "cname", fk_name, NULL); if (!cmodel) goto onerror; /* create new FK structure */ tfk = g_new0 (GdaMetaTableForeignKey, 1); tfk->meta_table = dbo; tfk->declared = TRUE; tfk->fk_name = g_value_dup_string (fk_name); tfk->on_update_policy = GDA_META_FOREIGN_KEY_NONE; tfk->on_delete_policy = GDA_META_FOREIGN_KEY_NONE; /* ignore if some columns are not found in the schema * (maybe wrong or outdated FK decl) */ gint j, cnrows; cnrows = gda_data_model_get_n_rows (cmodel); if (cnrows == 0) ignore = TRUE; else { tfk->cols_nb = cnrows; tfk->fk_cols_array = g_new0 (gint, cnrows); tfk->fk_names_array = g_new0 (gchar *, cnrows); tfk->ref_pk_cols_array = g_new0 (gint, cnrows); tfk->ref_pk_names_array = g_new0 (gchar *, cnrows); } for (j = 0; j < cnrows; j++) { const GValue *ov; ov = gda_data_model_get_value_at (cmodel, 0, j, error); if (!ov) goto onerror; if (G_VALUE_TYPE (ov) != G_TYPE_STRING) { ignore = TRUE; break; } tfk->fk_names_array [j] = g_value_dup_string (ov); ov = gda_data_model_get_value_at (cmodel, 1, j, error); if (!ov) goto onerror; if (G_VALUE_TYPE (ov) != G_TYPE_INT) { ignore = TRUE; break; } tfk->fk_cols_array [j] = g_value_get_int (ov); ov = gda_data_model_get_value_at (cmodel, 2, j, error); if (!ov) goto onerror; if (G_VALUE_TYPE (ov) != G_TYPE_STRING) { ignore = TRUE; break; } tfk->ref_pk_names_array [j] = g_value_dup_string (ov); ov = gda_data_model_get_value_at (cmodel, 3, j, error); if (!ov) goto onerror; if (G_VALUE_TYPE (ov) != G_TYPE_INT) { ignore = TRUE; break; } tfk->ref_pk_cols_array [j] = g_value_get_int (ov); } /* ignore if there is already an implemented FK */ if (! ignore) { ref_dbo = _meta_struct_get_db_object (mstruct, fk_catalog, fk_schema, fk_tname); if (ref_dbo && find_real_foreign_key (mt, ref_dbo, cmodel)) ignore = TRUE; } if (ignore) { /* ignore this FK end move on to the next one */ if (tfk) gda_meta_table_foreign_key_free (tfk); continue; } tfk->depend_on = ref_dbo; if (!tfk->depend_on) { gchar *str; tfk->depend_on = g_new0 (GdaMetaDbObject, 1); tfk->depend_on->obj_type = GDA_META_DB_UNKNOWN; tfk->depend_on->obj_catalog = g_strdup (g_value_get_string (fk_catalog)); tfk->depend_on->obj_schema = g_strdup (g_value_get_string (fk_schema)); tfk->depend_on->obj_name = g_strdup (g_value_get_string (fk_tname)); mstruct->priv->db_objects = g_slist_append (mstruct->priv->db_objects, tfk->depend_on); str = g_strdup_printf ("%s.%s.%s", g_value_get_string (fk_catalog), g_value_get_string (fk_schema), g_value_get_string (fk_tname)); g_hash_table_insert (mstruct->priv->index, str, tfk->depend_on); } else if (tfk->depend_on->obj_type == GDA_META_DB_TABLE) { GdaMetaTable *dot = GDA_META_TABLE (tfk->depend_on); dot->reverse_fk_list = g_slist_prepend (dot->reverse_fk_list, tfk); } dbo->depend_list = g_slist_append (dbo->depend_list, tfk->depend_on); mt->fk_list = g_slist_prepend (mt->fk_list, tfk); } g_object_unref (model); if (cmodel) g_object_unref (cmodel); gda_value_free (v1); gda_value_free (v2); gda_value_free (v3); return TRUE; onerror: if (model) g_object_unref (model); if (cmodel) g_object_unref (cmodel); gda_value_free (v1); gda_value_free (v2); gda_value_free (v3); return FALSE; } static GdaMetaDbObject * _meta_struct_complement (GdaMetaStruct *mstruct, GdaMetaDbObjectType type, const GValue *icatalog, const GValue *ischema, const GValue *iname, const GValue *short_name, const GValue *full_name, const GValue *owner, GError **error) { /* at this point icatalog, ischema and iname are NOT NULL */ GdaMetaDbObject *dbo = NULL; const GValue *cvalue; /* create new GdaMetaDbObject or get already existing one */ dbo = _meta_struct_get_db_object (mstruct, icatalog, ischema, iname); if (!dbo) { dbo = g_new0 (GdaMetaDbObject, 1); dbo->obj_catalog = g_strdup (g_value_get_string (icatalog)); dbo->obj_schema = g_strdup (g_value_get_string (ischema)); dbo->obj_name = g_strdup (g_value_get_string (iname)); if (short_name) dbo->obj_short_name = g_strdup (g_value_get_string (short_name)); if (full_name) dbo->obj_full_name = g_strdup (g_value_get_string (full_name)); if (owner && !gda_value_is_null (owner)) dbo->obj_owner = g_strdup (g_value_get_string (owner)); } else if (dbo->obj_type == type) return dbo; /* nothing to do */ else if (dbo->obj_type != GDA_META_DB_UNKNOWN) { /* DB Object already exists, return an error */ g_set_error (error, GDA_META_STRUCT_ERROR, GDA_META_STRUCT_DUPLICATE_OBJECT_ERROR, _("Object %s.%s.%s already exists in GdaMetaStruct and has a different object type"), g_value_get_string (icatalog), g_value_get_string (ischema), g_value_get_string (iname)); dbo = NULL; goto onerror; } dbo->obj_type = type; switch (type) { case GDA_META_DB_VIEW: { gchar *sql = "SELECT view_definition, is_updatable, table_short_name, table_full_name, table_owner " "FROM _views NATURAL JOIN _tables " "WHERE table_catalog = ##tc::string " "AND table_schema = ##ts::string AND table_name = ##tname::string"; GdaDataModel *model; gint nrows; GdaMetaView *mv; model = gda_meta_store_extract (mstruct->priv->store, sql, error, "tc", icatalog, "ts", ischema, "tname", iname, NULL); if (!model) goto onerror; nrows = gda_data_model_get_n_rows (model); if (nrows < 1) { g_object_unref (model); g_set_error (error, GDA_META_STRUCT_ERROR, GDA_META_STRUCT_UNKNOWN_OBJECT_ERROR, _("View %s.%s.%s not found in meta store object"), g_value_get_string (icatalog), g_value_get_string (ischema), g_value_get_string (iname)); goto onerror; } if (!dbo->obj_short_name) { cvalue = gda_data_model_get_value_at (model, 2, 0, error); if (!cvalue) goto onerror; dbo->obj_short_name = g_value_dup_string (cvalue); } if (!dbo->obj_full_name) { cvalue = gda_data_model_get_value_at (model, 3, 0, error); if (!cvalue) goto onerror; dbo->obj_full_name = g_value_dup_string (cvalue); } if (!dbo->obj_owner) { cvalue = gda_data_model_get_value_at (model, 4, 0, error); if (!cvalue) goto onerror; if (!gda_value_is_null (cvalue)) dbo->obj_owner = g_value_dup_string (cvalue); } mv = GDA_META_VIEW (dbo); cvalue = gda_data_model_get_value_at (model, 0, 0, error); if (!cvalue) goto onerror; if (G_VALUE_TYPE (cvalue) != GDA_TYPE_NULL) mv->view_def = g_value_dup_string (cvalue); else mv->view_def = g_strdup (""); cvalue = gda_data_model_get_value_at (model, 1, 0, error); if (!cvalue) goto onerror; if (G_VALUE_TYPE (cvalue) != GDA_TYPE_NULL) mv->is_updatable = g_value_get_boolean (cvalue); else mv->is_updatable = FALSE; /* view's dependencies, from its definition */ if ((mstruct->priv->features & GDA_META_STRUCT_FEATURE_VIEW_DEPENDENCIES) && mv->view_def && *mv->view_def) { static GdaSqlParser *parser = NULL; GdaStatement *stmt; const gchar *remain; if (!parser) parser = gda_sql_parser_new (); stmt = gda_sql_parser_parse_string (parser, mv->view_def, &remain, NULL); if (stmt && ((gda_statement_get_statement_type (stmt) == GDA_SQL_STATEMENT_SELECT) || (gda_statement_get_statement_type (stmt) == GDA_SQL_STATEMENT_COMPOUND))) { GdaSqlStatement *sqlst; g_object_get (G_OBJECT (stmt), "structure", &sqlst, NULL); compute_view_dependencies (mstruct, dbo, sqlst); gda_sql_statement_free (sqlst); g_object_unref (stmt); #ifdef GDA_DEBUG_NO g_print ("View %s depends on: ", dbo->obj_name); GSList *list; for (list = dbo->depend_list; list; list = list->next) g_print ("%s ", GDA_META_DB_OBJECT (list->data)->obj_name); g_print ("\n"); #endif } } } case GDA_META_DB_TABLE: { /* columns */ gchar *sql = "SELECT c.column_name, c.data_type, c.gtype, c.is_nullable, t.table_short_name, t.table_full_name, c.column_default, t.table_owner, c.array_spec, c.extra, c.column_comments, coalesce (c.character_maximum_length, c.character_octet_length) FROM _tables as t LEFT NATURAL JOIN _columns as c WHERE table_catalog = ##tc::string AND table_schema = ##ts::string AND table_name = ##tname::string ORDER BY ordinal_position"; GdaMetaTable *mt; GdaDataModel *model; gint i, nrows; model = gda_meta_store_extract (mstruct->priv->store, sql, error, "tc", icatalog, "ts", ischema, "tname", iname, NULL); if (!model) goto onerror; nrows = gda_data_model_get_n_rows (model); if (nrows < 1) { g_object_unref (model); g_set_error (error, GDA_META_STRUCT_ERROR, GDA_META_STRUCT_UNKNOWN_OBJECT_ERROR, _("Table %s.%s.%s not found (or missing columns information)"), g_value_get_string (icatalog), g_value_get_string (ischema), g_value_get_string (iname)); goto onerror; } if (!dbo->obj_short_name) { cvalue = gda_data_model_get_value_at (model, 4, 0, error); if (!cvalue) goto onerror; dbo->obj_short_name = g_value_dup_string (cvalue); } if (!dbo->obj_full_name) { cvalue = gda_data_model_get_value_at (model, 5, 0, error); if (!cvalue) goto onerror; dbo->obj_full_name = g_value_dup_string (cvalue); } if (!dbo->obj_owner) { cvalue = gda_data_model_get_value_at (model, 7, 0, error); if (!cvalue) goto onerror; if (!gda_value_is_null (cvalue)) dbo->obj_owner = g_value_dup_string (cvalue); } cvalue = gda_data_model_get_value_at (model, 0, 0, error); if (cvalue && (G_VALUE_TYPE (cvalue) == GDA_TYPE_NULL)) { if (type == GDA_META_DB_VIEW) { /* we don't have the list of columns for the view. * This can sometimes happen in SQLite */ nrows = 0; } } mt = GDA_META_TABLE (dbo); for (i = 0; i < nrows; i++) { GdaMetaTableColumn *tcol; const gchar *cstr = NULL; gint len = -1; tcol = g_new0 (GdaMetaTableColumn, 1); /* Note: tcol->pkey is not determined here */ mt->columns = g_slist_prepend (mt->columns, tcol); cvalue = gda_data_model_get_value_at (model, 0, i, error); if (!cvalue) goto onerror; tcol->column_name = g_value_dup_string (cvalue); cvalue = gda_data_model_get_value_at (model, 1, i, error); if (!cvalue) goto onerror; if (!gda_value_is_null (cvalue)) cstr = g_value_get_string (cvalue); cvalue = gda_data_model_get_value_at (model, 11, i, error); if (!cvalue) goto onerror; if (!gda_value_is_null (cvalue)) len = g_value_get_int (cvalue); if (cstr && *cstr) { if (len >= 0) tcol->column_type = g_strdup_printf ("%s (%d)", cstr, len); else tcol->column_type = g_strdup (cstr); } else { cvalue = gda_data_model_get_value_at (model, 8, i, error); if (!cvalue) goto onerror; tcol->column_type = array_type_to_sql (mstruct->priv->store, cvalue); if (!tcol->column_type) goto onerror; } cvalue = gda_data_model_get_value_at (model, 2, i, error); if (!cvalue) goto onerror; tcol->gtype = gda_g_type_from_string (g_value_get_string (cvalue)); if (tcol->gtype == G_TYPE_INVALID) tcol->gtype = GDA_TYPE_NULL; cvalue = gda_data_model_get_value_at (model, 3, i, error); if (!cvalue) goto onerror; tcol->nullok = g_value_get_boolean (cvalue); cvalue = gda_data_model_get_value_at (model, 6, i, error); if (!cvalue) goto onerror; if (!gda_value_is_null (cvalue)) tcol->default_value = g_value_dup_string (cvalue); cvalue = gda_data_model_get_value_at (model, 9, i, error); if (!cvalue) goto onerror; if (!gda_value_is_null (cvalue)) { gchar **array, *tmp; gint ai; GValue *true_value; g_value_set_boolean ((true_value = gda_value_new (G_TYPE_BOOLEAN)), TRUE); cstr = g_value_get_string (cvalue); array = g_strsplit (cstr, ",", 0); for (ai = 0; array [ai]; ai++) { tmp = g_strstrip (array [ai]); if (!strcmp (tmp, GDA_EXTRA_AUTO_INCREMENT)) gda_attributes_manager_set (att_mgr, tcol, GDA_ATTRIBUTE_AUTO_INCREMENT, true_value); else g_message ("Unknown EXTRA attribute '%s', please report this bug to " "http://bugzilla.gnome.org/ for the \"libgda\" product.", tmp); } gda_value_free (true_value); g_strfreev (array); } cvalue = gda_data_model_get_value_at (model, 10, i, error); if (!cvalue) goto onerror; if (!gda_value_is_null (cvalue)) gda_attributes_manager_set (att_mgr, tcol, GDA_ATTRIBUTE_DESCRIPTION, cvalue); } mt->columns = g_slist_reverse (mt->columns); g_object_unref (model); /* primary key */ sql = "SELECT constraint_name FROM _table_constraints WHERE constraint_type='PRIMARY KEY' AND table_catalog = ##tc::string AND table_schema = ##ts::string AND table_name = ##tname::string"; model = gda_meta_store_extract (mstruct->priv->store, sql, error, "tc", icatalog, "ts", ischema, "tname", iname, NULL); if (!model) goto onerror; nrows = gda_data_model_get_n_rows (model); if (nrows >= 1) { GdaDataModel *pkmodel; sql = "SELECT column_name FROM _key_column_usage WHERE table_catalog = ##cc::string AND table_schema = ##cs::string AND table_name = ##tname::string AND constraint_name = ##cname::string ORDER BY ordinal_position"; cvalue = gda_data_model_get_value_at (model, 0, 0, error); if (!cvalue) goto onerror; pkmodel = gda_meta_store_extract (mstruct->priv->store, sql, error, "cc", icatalog, "cs", ischema, "tname", iname, "cname", cvalue, NULL); if (!pkmodel) { g_object_unref (model); goto onerror; } nrows = gda_data_model_get_n_rows (pkmodel); mt->pk_cols_nb = nrows; mt->pk_cols_array = g_new0 (gint, mt->pk_cols_nb); for (i = 0; i < nrows; i++) { GdaMetaTableColumn *tcol; cvalue = gda_data_model_get_value_at (pkmodel, 0, i, error); if (!cvalue) goto onerror; tcol = gda_meta_struct_get_table_column (mstruct, mt, cvalue); if (!tcol) { mt->pk_cols_array [i] = -1; g_set_error (error, GDA_META_STRUCT_ERROR, GDA_META_STRUCT_INCOHERENCE_ERROR, _("Internal GdaMetaStore error: column %s not found"), g_value_get_string (cvalue)); goto onerror; } else { mt->pk_cols_array [i] = g_slist_index (mt->columns, tcol); tcol->pkey = TRUE; } } g_object_unref (pkmodel); } g_object_unref (model); /* foreign keys */ if (mstruct->priv->features & GDA_META_STRUCT_FEATURE_FOREIGN_KEYS) { sql = "SELECT ref_table_catalog, ref_table_schema, ref_table_name, constraint_name, ref_constraint_name, update_rule, delete_rule, constraint_name FROM _referential_constraints WHERE table_catalog = ##tc::string AND table_schema = ##ts::string AND table_name = ##tname::string"; model = gda_meta_store_extract (mstruct->priv->store, sql, error, "tc", icatalog, "ts", ischema, "tname", iname, NULL); if (!model) goto onerror; nrows = gda_data_model_get_n_rows (model); for (i = 0; i < nrows; i++) { GdaMetaTableForeignKey *tfk = NULL; const GValue *fk_catalog, *fk_schema, *fk_tname, *fk_name; const GValue *upd_policy, *del_policy; GdaDataModel *fk_cols = NULL; GdaDataModel *ref_pk_cols = NULL; fk_catalog = gda_data_model_get_value_at (model, 0, i, error); if (!fk_catalog) goto onfkerror; fk_schema = gda_data_model_get_value_at (model, 1, i, error); if (!fk_schema) goto onfkerror; fk_tname = gda_data_model_get_value_at (model, 2, i, error); if (!fk_tname) goto onfkerror; upd_policy = gda_data_model_get_value_at (model, 5, i, error); if (!upd_policy) goto onfkerror; del_policy = gda_data_model_get_value_at (model, 6, i, error); if (!del_policy) goto onfkerror; fk_name = gda_data_model_get_value_at (model, 7, i, error); if (!fk_name) goto onfkerror; tfk = g_new0 (GdaMetaTableForeignKey, 1); tfk->meta_table = dbo; tfk->fk_name = g_value_dup_string (fk_name); tfk->depend_on = _meta_struct_get_db_object (mstruct, fk_catalog, fk_schema, fk_tname); if (!tfk->depend_on) { gchar *str; tfk->depend_on = g_new0 (GdaMetaDbObject, 1); tfk->depend_on->obj_type = GDA_META_DB_UNKNOWN; tfk->depend_on->obj_catalog = g_strdup (g_value_get_string (fk_catalog)); tfk->depend_on->obj_schema = g_strdup (g_value_get_string (fk_schema)); tfk->depend_on->obj_name = g_strdup (g_value_get_string (fk_tname)); mstruct->priv->db_objects = g_slist_append (mstruct->priv->db_objects, tfk->depend_on); str = g_strdup_printf ("%s.%s.%s", g_value_get_string (fk_catalog), g_value_get_string (fk_schema), g_value_get_string (fk_tname)); g_hash_table_insert (mstruct->priv->index, str, tfk->depend_on); } else if (tfk->depend_on->obj_type == GDA_META_DB_TABLE) { GdaMetaTable *dot = GDA_META_TABLE (tfk->depend_on); dot->reverse_fk_list = g_slist_prepend (dot->reverse_fk_list, tfk); } dbo->depend_list = g_slist_append (dbo->depend_list, tfk->depend_on); if (G_VALUE_TYPE (upd_policy) == G_TYPE_STRING) tfk->on_update_policy = policy_string_to_value (g_value_get_string (upd_policy)); else tfk->on_update_policy = GDA_META_FOREIGN_KEY_UNKNOWN; if (G_VALUE_TYPE (upd_policy) == G_TYPE_STRING) tfk->on_delete_policy = policy_string_to_value (g_value_get_string (del_policy)); else tfk->on_delete_policy = GDA_META_FOREIGN_KEY_UNKNOWN; tfk->declared = FALSE; /* FIXME: compute @cols_nb, and all the @*_array members (ref_pk_cols_array must be * initialized with -1 values everywhere */ sql = "SELECT k.column_name, c.ordinal_position FROM _key_column_usage k INNER JOIN _columns c ON (c.table_catalog = k.table_catalog AND c.table_schema = k.table_schema AND c.table_name=k.table_name AND c.column_name=k.column_name) WHERE k.table_catalog = ##tc::string AND k.table_schema = ##ts::string AND k.table_name = ##tname::string AND k.constraint_name = ##cname::string ORDER BY k.ordinal_position"; gboolean fkerror = FALSE; cvalue = gda_data_model_get_value_at (model, 3, i, error); if (!cvalue) goto onfkerror; fk_cols = gda_meta_store_extract (mstruct->priv->store, sql, error, "tc", icatalog, "ts", ischema, "tname", iname, "cname", cvalue, NULL); /*g_print ("tname=%s cvalue=%s\n", gda_value_stringify (iname), gda_value_stringify (cvalue));*/ cvalue = gda_data_model_get_value_at (model, 4, i, error); if (!cvalue) goto onfkerror; ref_pk_cols = gda_meta_store_extract (mstruct->priv->store, sql, error, "tc", fk_catalog, "ts", fk_schema, "tname", fk_tname, "cname", cvalue, NULL); /*g_print ("tname=%s cvalue=%s\n", gda_value_stringify (fk_tname), gda_value_stringify (cvalue));*/ if (fk_cols && ref_pk_cols) { gint fk_nrows, ref_pk_nrows; fk_nrows = gda_data_model_get_n_rows (fk_cols); ref_pk_nrows = gda_data_model_get_n_rows (ref_pk_cols); if (fk_nrows != ref_pk_nrows) { /*gda_data_model_dump (fk_cols, stdout); gda_data_model_dump (ref_pk_cols, stdout);*/ if (ref_pk_nrows > 0) fkerror = TRUE; else { /* not an error, only the referenced table is not present * in the meta store, which is possible if the meta * store was only partially updated */ } } else { gint n; tfk->cols_nb = fk_nrows; tfk->fk_cols_array = g_new0 (gint, fk_nrows); tfk->fk_names_array = g_new0 (gchar *, fk_nrows); tfk->ref_pk_cols_array = g_new0 (gint, fk_nrows); tfk->ref_pk_names_array = g_new0 (gchar *, fk_nrows); for (n = 0; n < fk_nrows; n++) { const GValue *cv; cv = gda_data_model_get_value_at (fk_cols, 1, n, error); if (!cv) goto onfkerror; tfk->fk_cols_array [n] = g_value_get_int (cv); cv = gda_data_model_get_value_at (fk_cols, 0, n, error); if (!cv) goto onfkerror; tfk->fk_names_array [n] = g_value_dup_string (cv); cv = gda_data_model_get_value_at (ref_pk_cols, 1, n, error); if (!cv) goto onfkerror; tfk->ref_pk_cols_array [n] = g_value_get_int (cv); cv = gda_data_model_get_value_at (ref_pk_cols, 0, n, error); if (!cv) goto onfkerror; tfk->ref_pk_names_array [n] = g_value_dup_string (cv); } } } else fkerror = TRUE; if (fkerror) { g_set_error (error, GDA_META_STRUCT_ERROR, GDA_META_STRUCT_INCOHERENCE_ERROR, _("Meta data incoherence in foreign key constraint for table %s.%s.%s " "referencing table %s.%s.%s"), g_value_get_string (icatalog), g_value_get_string (ischema), g_value_get_string (iname), g_value_get_string (fk_catalog), g_value_get_string (fk_schema), g_value_get_string (fk_tname)); goto onfkerror; } if (fk_cols) g_object_unref (fk_cols); if (ref_pk_cols) g_object_unref (ref_pk_cols); mt->fk_list = g_slist_prepend (mt->fk_list, tfk); continue; onfkerror: if (fk_cols) g_object_unref (fk_cols); if (ref_pk_cols) g_object_unref (ref_pk_cols); if (tfk) mt->fk_list = g_slist_prepend (mt->fk_list, tfk); goto onerror; } mt->fk_list = g_slist_reverse (mt->fk_list); g_object_unref (model); /* Note: mt->reverse_fk_list is not determined here */ add_declared_foreign_keys (mstruct, mt, NULL); } break; } default: TO_IMPLEMENT; } if (dbo && !g_slist_find (mstruct->priv->db_objects, dbo)) { gchar *str; mstruct->priv->db_objects = g_slist_append (mstruct->priv->db_objects, dbo); str = g_strdup_printf ("%s.%s.%s", g_value_get_string (icatalog), g_value_get_string (ischema), g_value_get_string (iname)); g_hash_table_insert (mstruct->priv->index, str, dbo); } if (dbo && (dbo->obj_type == GDA_META_DB_TABLE) && (mstruct->priv->features & GDA_META_STRUCT_FEATURE_FOREIGN_KEYS)) { /* compute GdaMetaTableForeignKey's @ref_pk_cols_array arrays and GdaMetaTable' @reverse_fk_list lists*/ GSList *list; for (list = mstruct->priv->db_objects; list; list = list->next) { GdaMetaDbObject *tmpdbo; tmpdbo = GDA_META_DB_OBJECT (list->data); if (tmpdbo->obj_type != GDA_META_DB_TABLE) continue; GdaMetaTable *mt = GDA_META_TABLE (tmpdbo); GSList *klist; for (klist = mt->fk_list; klist; klist = klist->next) { GdaMetaTableForeignKey *tfk = GDA_META_TABLE_FOREIGN_KEY (klist->data); GdaMetaTable *ref_mt = GDA_META_TABLE (tfk->depend_on); gint i; if (tfk->depend_on != dbo) continue; for (i = 0; i < tfk->cols_nb; i++) { gint col; GdaMetaTableColumn *r_tcol; GValue *r_val; if (tfk->ref_pk_cols_array[i] != -1) /* already correctly set */ continue; if (tfk->depend_on->obj_type != GDA_META_DB_TABLE) continue; /* can't be set now */ g_value_set_string ((r_val = gda_value_new (G_TYPE_STRING)), tfk->ref_pk_names_array[i]); r_tcol = gda_meta_struct_get_table_column (mstruct, ref_mt, r_val); gda_value_free (r_val); col = g_slist_index (ref_mt->columns, r_tcol); if (!r_tcol || (col < 0)) { g_set_error (error, GDA_META_STRUCT_ERROR, GDA_META_STRUCT_INCOHERENCE_ERROR, _("Foreign key column '%s' not found in table '%s'"), tfk->ref_pk_names_array[i], tfk->depend_on->obj_name); dbo = NULL; goto onerror; } tfk->ref_pk_cols_array[i] = col; } GDA_META_TABLE (dbo)->reverse_fk_list = g_slist_append (GDA_META_TABLE (dbo)->reverse_fk_list, tfk); } } } return dbo; onerror: if (dbo) dbo->obj_type = GDA_META_DB_UNKNOWN; return NULL; } static gchar * array_type_to_sql (GdaMetaStore *store, const GValue *specific_name) { gchar *str; GdaDataModel *model; gint nrows; const GValue *cvalue; if (!specific_name || gda_value_is_null (specific_name)) return g_strdup ("[]"); model = gda_meta_store_extract (store, "SELECT data_type, array_spec FROM _element_types WHERE specific_name = ##name::string", NULL, "name", specific_name, NULL); if (!model) return g_strdup ("[]"); nrows = gda_data_model_get_n_rows (model); if (nrows != 1) { g_object_unref (model); return g_strdup ("[]"); } cvalue = gda_data_model_get_value_at (model, 0, 0, NULL); if (!cvalue) return NULL; if (gda_value_is_null (cvalue) || !g_value_get_string (cvalue)) { /* use array_spec */ gchar *str2; cvalue = gda_data_model_get_value_at (model, 1, 0, NULL); if (!cvalue) return NULL; str2 = array_type_to_sql (store, cvalue); str = g_strdup_printf ("%s[]", str2); g_free (str2); } else { cvalue = gda_data_model_get_value_at (model, 0, 0, NULL); if (!cvalue) return NULL; str = g_strdup_printf ("%s[]", g_value_get_string (cvalue)); } g_object_unref (model); return str; } /** * gda_meta_struct_complement_schema: * @mstruct: a #GdaMetaStruct object * @catalog: (allow-none): name of a catalog, or %NULL * @schema: (allow-none): name of a schema, or %NULL * @error: (allow-none): a place to store errors, or %NULL * * This method is similar to gda_meta_struct_complement() but creates #GdaMetaDbObject for all the * database object which are in the @schema schema (and in the @catalog catalog). * If @catalog is %NULL, then any catalog will be used, and * if @schema is %NULL then any schema will be used (if @schema is %NULL then catalog must also be %NULL). * * Please refer to gda_meta_struct_complement() form more information. * * Returns: TRUE if no error occurred */ gboolean gda_meta_struct_complement_schema (GdaMetaStruct *mstruct, const GValue *catalog, const GValue *schema, GError **error) { GdaDataModel *tables_model = NULL, *views_model = NULL; gint i, nrows, k; const GValue *cvalues[6]; /* schema and catalog are known */ const gchar *sql1 = "SELECT table_name " "FROM _tables WHERE table_short_name, table_full_name, table_owner, table_catalog = ##cat::string AND table_schema = ##schema::string " "AND table_type LIKE '%TABLE%' " "ORDER BY table_schema, table_name"; const gchar *sql2 = "SELECT table_short_name, table_full_name, table_owner, table_name " "FROM _tables WHERE table_catalog = ##cat::string AND table_schema = ##schema::string " "AND table_type LIKE '%VIEW%' " "ORDER BY table_schema, table_name"; /* schema is known, catalog unknown */ const gchar *sql3 = "SELECT table_short_name, table_full_name, table_owner, table_name, table_catalog, table_schema " "FROM _tables WHERE table_schema = ##schema::string AND table_type LIKE '%TABLE%' " "ORDER BY table_schema, table_name"; const gchar *sql4 = "SELECT table_short_name, table_full_name, table_owner, table_name, table_catalog, table_schema " "FROM _tables WHERE table_schema = ##schema::string AND table_type LIKE '%VIEW%' " "ORDER BY table_schema, table_name"; /* schema and catalog are unknown */ const gchar *sql5 = "SELECT table_short_name, table_full_name, table_owner, table_name, table_catalog, table_schema " "FROM _tables WHERE table_type LIKE '%TABLE%' " "ORDER BY table_schema, table_name"; const gchar *sql6 = "SELECT table_short_name, table_full_name, table_owner, table_name, table_catalog, table_schema " "FROM _tables WHERE table_type LIKE '%VIEW%' " "ORDER BY table_schema, table_name"; g_return_val_if_fail (GDA_IS_META_STRUCT (mstruct), FALSE); g_return_val_if_fail (mstruct->priv->store, FALSE); g_return_val_if_fail (!catalog || (catalog && schema), FALSE); g_return_val_if_fail (!catalog || (G_VALUE_TYPE (catalog) == G_TYPE_STRING), FALSE); g_return_val_if_fail (!schema || (G_VALUE_TYPE (schema) == G_TYPE_STRING), FALSE); if (schema) { if (catalog) { tables_model = gda_meta_store_extract (mstruct->priv->store, sql1, error, "cat", catalog, "schema", schema, NULL); if (tables_model) views_model = gda_meta_store_extract (mstruct->priv->store, sql2, error, "cat", catalog, "schema", schema, NULL); } else { tables_model = gda_meta_store_extract (mstruct->priv->store, sql3, error, "schema", schema, NULL); if (tables_model) views_model = gda_meta_store_extract (mstruct->priv->store, sql4, error, "schema", schema, NULL); } } else { tables_model = gda_meta_store_extract (mstruct->priv->store, sql5, error, NULL); if (tables_model) views_model = gda_meta_store_extract (mstruct->priv->store, sql6, error, NULL); } if (!tables_model || !views_model) return FALSE; /* tables */ nrows = gda_data_model_get_n_rows (tables_model); for (i = 0; i < nrows; i++) { for (k = 0; k <= 5; k++) { cvalues [k] = gda_data_model_get_value_at (tables_model, k, i, error); if (!cvalues [k]) { g_object_unref (tables_model); g_object_unref (views_model); return FALSE; } } if (!_meta_struct_complement (mstruct, GDA_META_DB_TABLE, catalog ? catalog : cvalues [4], schema ? schema : cvalues [5], cvalues [3], cvalues [0], cvalues [1], cvalues [2], error)) { g_object_unref (tables_model); g_object_unref (views_model); return FALSE; } } g_object_unref (tables_model); /* views */ nrows = gda_data_model_get_n_rows (views_model); for (i = 0; i < nrows; i++) { for (k = 0; k <= 5; k++) { cvalues [k] = gda_data_model_get_value_at (views_model, k, i, error); if (!cvalues [k]) { g_object_unref (views_model); return FALSE; } } if (!_meta_struct_complement (mstruct, GDA_META_DB_VIEW, catalog ? catalog : cvalues [4], schema ? schema : cvalues [5], cvalues [3], cvalues [0], cvalues [1], cvalues [2], error)) { g_object_unref (views_model); return FALSE; } } g_object_unref (views_model); return TRUE; } static gboolean real_gda_meta_struct_complement_all (GdaMetaStruct *mstruct, gboolean default_only, GError **error) { GdaDataModel *model; gint i, nrows, k; const GValue *cvalues[6]; const gchar *sql1 = "SELECT table_catalog, table_schema, table_name, table_short_name, table_full_name, table_owner " "FROM _tables WHERE table_short_name = table_name AND table_type LIKE '%TABLE%' " "ORDER BY table_schema, table_name"; const gchar *sql2 = "SELECT table_catalog, table_schema, table_name, table_short_name, table_full_name, table_owner " "FROM _tables WHERE table_short_name = table_name AND table_type='VIEW' " "ORDER BY table_schema, table_name"; const gchar *sql3 = "SELECT table_catalog, table_schema, table_name, table_short_name, table_full_name, table_owner " "FROM _tables WHERE table_type LIKE '%TABLE%' " "ORDER BY table_schema, table_name"; const gchar *sql4 = "SELECT table_catalog, table_schema, table_name, table_short_name, table_full_name, table_owner " "FROM _tables WHERE table_type='VIEW' " "ORDER BY table_schema, table_name"; g_return_val_if_fail (GDA_IS_META_STRUCT (mstruct), FALSE); g_return_val_if_fail (mstruct->priv->store, FALSE); /* tables */ model = gda_meta_store_extract (mstruct->priv->store, default_only ? sql1 : sql3, error, NULL); if (!model) return FALSE; nrows = gda_data_model_get_n_rows (model); for (i = 0; i < nrows; i++) { for (k = 0; k <= 5; k++) { cvalues [k] = gda_data_model_get_value_at (model, k, i, error); if (!cvalues [k]) { g_object_unref (model); return FALSE; } } if (!_meta_struct_complement (mstruct, GDA_META_DB_TABLE, cvalues [0], cvalues [1], cvalues [2], cvalues [3], cvalues [4], cvalues [5], error)) { g_object_unref (model); return FALSE; } } g_object_unref (model); /* views */ model = gda_meta_store_extract (mstruct->priv->store, default_only ? sql2 : sql4, error, NULL); if (!model) return FALSE; nrows = gda_data_model_get_n_rows (model); for (i = 0; i < nrows; i++) { for (k = 0; k <= 5; k++) { cvalues [k] = gda_data_model_get_value_at (model, k, i, error); if (!cvalues [k]) { g_object_unref (model); return FALSE; } } if (!_meta_struct_complement (mstruct, GDA_META_DB_VIEW, cvalues [0], cvalues [1], cvalues [2], cvalues [3], cvalues [4], cvalues [5], error)) { g_object_unref (model); return FALSE; } } g_object_unref (model); return TRUE; } /** * gda_meta_struct_complement_default: * @mstruct: a #GdaMetaStruct object * @error: (allow-none): a place to store errors, or %NULL * * This method is similar to gda_meta_struct_complement() and gda_meta_struct_complement_all() * but creates #GdaMetaDbObject for all the * database object which are usable using only their short name (that is which do not need to be prefixed by * the schema in which they are to be used). * * Please refer to gda_meta_struct_complement() form more information. * * Returns: TRUE if no error occurred */ gboolean gda_meta_struct_complement_default (GdaMetaStruct *mstruct, GError **error) { return real_gda_meta_struct_complement_all (mstruct, TRUE, error); } /** * gda_meta_struct_complement_all: * @mstruct: a #GdaMetaStruct object * @error: (allow-none): a place to store errors, or %NULL * * This method is similar to gda_meta_struct_complement() and gda_meta_struct_complement_default() * but creates #GdaMetaDbObject for all the database object. * * Please refer to gda_meta_struct_complement() form more information. * * Returns: TRUE if no error occurred */ gboolean gda_meta_struct_complement_all (GdaMetaStruct *mstruct, GError **error) { return real_gda_meta_struct_complement_all (mstruct, FALSE, error); } /** * gda_meta_struct_complement_depend: * @mstruct: a #GdaMetaStruct object * @dbo: a #GdaMetaDbObject part of @mstruct * @error: (allow-none): a place to store errors, or %NULL * * This method is similar to gda_meta_struct_complement() but creates #GdaMetaDbObject for all the dependencies * of @dbo. * * Please refer to gda_meta_struct_complement() form more information. * * Returns: TRUE if no error occurred */ gboolean gda_meta_struct_complement_depend (GdaMetaStruct *mstruct, GdaMetaDbObject *dbo, GError **error) { GSList *list; g_return_val_if_fail (GDA_IS_META_STRUCT (mstruct), FALSE); g_return_val_if_fail (mstruct->priv->store, FALSE); g_return_val_if_fail (dbo, FALSE); g_return_val_if_fail (g_slist_find (mstruct->priv->db_objects, dbo), FALSE); for (list = dbo->depend_list; list; list = list->next) { GdaMetaDbObject *dep_dbo = GDA_META_DB_OBJECT (list->data); if (dep_dbo->obj_type != GDA_META_DB_UNKNOWN) continue; GValue *cat = NULL, *schema = NULL, *name = NULL; GdaMetaDbObject *tmpobj; g_return_val_if_fail (dep_dbo->obj_name, FALSE); if (dep_dbo->obj_catalog) g_value_take_string ((cat = gda_value_new (G_TYPE_STRING)), g_strdup_printf ("\"%s\"", dep_dbo->obj_catalog)); if (dep_dbo->obj_schema) g_value_take_string ((schema = gda_value_new (G_TYPE_STRING)), g_strdup_printf ("\"%s\"", dep_dbo->obj_schema)); g_value_take_string ((name = gda_value_new (G_TYPE_STRING)), g_strdup_printf ("\"%s\"", dep_dbo->obj_name)); tmpobj = gda_meta_struct_complement (mstruct, GDA_META_DB_UNKNOWN, cat, schema, name, error); if (cat) gda_value_free (cat); if (schema) gda_value_free (schema); gda_value_free (name); if (!tmpobj) return FALSE; } return TRUE; } /* * Makes a list of all the GdaMetaDbObject structures listed in @objects * which are not present in @ordered_list and for which no dependency is in @ordered_list */ static GSList * build_pass (GSList *objects, GSList *ordered_list) { GSList *retlist = NULL, *list; for (list = objects; list; list = list->next) { gboolean has_dep = FALSE; GSList *dep_list; if (g_slist_find (ordered_list, list->data)) continue; for (dep_list = GDA_META_DB_OBJECT (list->data)->depend_list; dep_list; dep_list = dep_list->next) { if (!g_slist_find (ordered_list, dep_list->data)) { has_dep = TRUE; break; } } if (has_dep) continue; retlist = g_slist_prepend (retlist, list->data); } #ifdef GDA_DEBUG_NO g_print (">> PASS\n"); for (list = retlist; list; list = list->next) g_print ("--> %s\n", GDA_META_DB_OBJECT (list->data)->obj_name); g_print ("<<\n"); #endif return retlist; } static gint db_object_sort_func (GdaMetaDbObject *dbo1, GdaMetaDbObject *dbo2) { gint retval = 0; if (dbo1->obj_schema && dbo2->obj_schema) { retval = strcmp (dbo1->obj_schema, dbo2->obj_schema); if (retval) return retval; } else if (dbo1->obj_schema) return 1; else if (dbo2->obj_schema) return -1; if (dbo1->obj_name && dbo2->obj_name) return strcmp (dbo1->obj_name, dbo2->obj_name); else if (dbo1->obj_name) return 1; else if (dbo2->obj_name) return -1; return 0; } /** * gda_meta_struct_sort_db_objects: * @mstruct: a #GdaMetaStruct object * @sort_type: the kind of sorting requested * @error: (allow-none): a place to store errors, or %NULL * * Reorders the list of database objects within @mstruct in a way specified by @sort_type. * * Returns: TRUE if no error occurred */ gboolean gda_meta_struct_sort_db_objects (GdaMetaStruct *mstruct, GdaMetaSortType sort_type, G_GNUC_UNUSED GError **error) { GSList *pass_list; GSList *ordered_list = NULL; g_return_val_if_fail (GDA_IS_META_STRUCT (mstruct), FALSE); switch (sort_type) { case GDA_META_SORT_ALHAPETICAL: mstruct->priv->db_objects = g_slist_sort (mstruct->priv->db_objects, (GCompareFunc) db_object_sort_func); ordered_list = mstruct->priv->db_objects; break; case GDA_META_SORT_DEPENDENCIES: g_return_val_if_fail (mstruct, FALSE); for (pass_list = build_pass (mstruct->priv->db_objects, ordered_list); pass_list; pass_list = build_pass (mstruct->priv->db_objects, ordered_list)) ordered_list = g_slist_concat (ordered_list, pass_list); g_slist_free (mstruct->priv->db_objects); mstruct->priv->db_objects = ordered_list; break; default: TO_IMPLEMENT; break; } #ifdef GDA_DEBUG_NO GSList *list; for (list = ordered_list; list; list = list->next) g_print ("--> %s\n", GDA_META_DB_OBJECT (list->data)->obj_name); #endif return TRUE; } /* * Same as gda_meta_struct_get_db_object except that @catalog, @schema and @name are ready to be * compared (no need to double quote) */ static GdaMetaDbObject * _meta_struct_get_db_object (GdaMetaStruct *mstruct, const GValue *catalog, const GValue *schema, const GValue *name) { gchar *key; GdaMetaDbObject *dbo; if (catalog && schema) { g_return_val_if_fail (G_VALUE_TYPE (catalog) == G_TYPE_STRING, NULL); g_return_val_if_fail (G_VALUE_TYPE (schema) == G_TYPE_STRING, NULL); key = g_strdup_printf ("%s.%s.%s", g_value_get_string (catalog), g_value_get_string (schema), g_value_get_string (name)); dbo = g_hash_table_lookup (mstruct->priv->index, key); g_free (key); return dbo; } else { /* walk through all the objects, and pick the ones with a matching name */ GSList *list; GSList *matching = NULL; const gchar *obj_name = g_value_get_string (name); const gchar *obj_schema = NULL, *obj_catalog = NULL; if (catalog) { g_return_val_if_fail (G_VALUE_TYPE (catalog) == G_TYPE_STRING, NULL); obj_catalog = g_value_get_string (catalog); } if (schema) { g_return_val_if_fail (G_VALUE_TYPE (schema) == G_TYPE_STRING, NULL); obj_schema = g_value_get_string (schema); } for (list = mstruct->priv->db_objects; list; list = list->next) { GdaMetaDbObject *dbo; dbo = GDA_META_DB_OBJECT (list->data); 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); } if (matching && !matching->next) { GdaMetaDbObject *dbo = GDA_META_DB_OBJECT (matching->data); g_slist_free (matching); return dbo; } else { /* none or more than one found => return NULL */ if (matching) g_slist_free (matching); return NULL; } } } /** * gda_meta_struct_get_all_db_objects: * @mstruct: a #GdaMetaStruct object * * Get a list of all the #GdaMetaDbObject structures representing database objects in @mstruct. Note that * no #GdaMetaDbObject structure must not be modified. * * Returns: (transfer container) (element-type Gda.MetaDbObject): a new #GSList list of pointers to * #GdaMetaDbObject structures which must be destroyed after usage using g_slist_free(). The individual * #GdaMetaDbObject must not be modified. */ GSList * gda_meta_struct_get_all_db_objects (GdaMetaStruct *mstruct) { g_return_val_if_fail (GDA_IS_META_STRUCT (mstruct), NULL); if (mstruct->priv->db_objects) return g_slist_copy (mstruct->priv->db_objects); else return NULL; } /** * gda_meta_struct_get_db_object: * @mstruct: a #GdaMetaStruct object * @catalog: (allow-none): the catalog the object belongs to (as a G_TYPE_STRING GValue), or %NULL * @schema: (allow-none): the schema the object belongs to (as a G_TYPE_STRING GValue), or %NULL * @name: the object's name (as a G_TYPE_STRING GValue), not %NULL * * Tries to locate the #GdaMetaDbObject structure representing the database object named after * @catalog, @schema and @name. * * If one or both of @catalog and @schema are %NULL, and more than one database object matches the name, then * the return value is also %NULL. * * Returns: (transfer none): the #GdaMetaDbObject or %NULL if not found */ GdaMetaDbObject * gda_meta_struct_get_db_object (GdaMetaStruct *mstruct, const GValue *catalog, const GValue *schema, const GValue *name) { GdaMetaDbObject *dbo; GValue *icatalog = NULL, *ischema = NULL, *iname = NULL; /* GValue with identifiers ready to be compared */ g_return_val_if_fail (GDA_IS_META_STRUCT (mstruct), NULL); g_return_val_if_fail (name && (G_VALUE_TYPE (name) == G_TYPE_STRING), NULL); g_return_val_if_fail (!catalog || (catalog && schema), NULL); g_return_val_if_fail (!catalog || (G_VALUE_TYPE (catalog) == G_TYPE_STRING), NULL); g_return_val_if_fail (!schema || (G_VALUE_TYPE (schema) == G_TYPE_STRING), NULL); /* prepare identifiers */ g_value_take_string ((iname = gda_value_new (G_TYPE_STRING)), prepare_sql_identifier_for_compare (g_value_dup_string (name))); if (catalog) g_value_take_string ((icatalog = gda_value_new (G_TYPE_STRING)), prepare_sql_identifier_for_compare (g_value_dup_string (catalog))); if (schema) g_value_take_string ((ischema = gda_value_new (G_TYPE_STRING)), prepare_sql_identifier_for_compare (g_value_dup_string (schema))); dbo = _meta_struct_get_db_object (mstruct, icatalog, ischema, iname); if (icatalog) gda_value_free (icatalog); if (ischema) gda_value_free (ischema); gda_value_free (iname); return dbo; } /** * gda_meta_struct_get_table_column: (skip) * @mstruct: a #GdaMetaStruct object * @table: the #GdaMetaTable structure to find the column for * @col_name: the name of the column to find (as a G_TYPE_STRING GValue) * * Tries to find the #GdaMetaTableColumn representing the column named @col_name in @table. * * Returns: (transfer none): the #GdaMetaTableColumn or %NULL if not found */ GdaMetaTableColumn * gda_meta_struct_get_table_column (GdaMetaStruct *mstruct, GdaMetaTable *table, const GValue *col_name) { 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); cname = g_value_get_string (col_name); for (list = table->columns; list; list = list->next) { GdaMetaTableColumn *tcol = GDA_META_TABLE_COLUMN (list->data); if (gda_identifier_equal (tcol->column_name, cname)) return tcol; } return NULL; } /** * gda_meta_struct_dump_as_graph: * @mstruct: a #GdaMetaStruct object * @info: informs what kind of information to show in the resulting graph * @error: (allow-none): a place to store errors, or %NULL * * Creates a new graph (in the GraphViz syntax) representation of @mstruct. * * Returns: (transfer full): a new string, or %NULL if an error occurred. */ gchar * gda_meta_struct_dump_as_graph (GdaMetaStruct *mstruct, GdaMetaGraphInfo info, G_GNUC_UNUSED GError **error) { GString *string; gchar *result; g_return_val_if_fail (GDA_IS_META_STRUCT (mstruct), NULL); string = g_string_new ("digraph G {\nrankdir = BT;\nnode [shape = plaintext];\n"); GSList *dbo_list; for (dbo_list = mstruct->priv->db_objects; dbo_list; dbo_list = dbo_list->next) { gchar *objname, *fullname; GdaMetaDbObject *dbo = GDA_META_DB_OBJECT (dbo_list->data); GSList *list; gboolean use_html = (info & GDA_META_GRAPH_COLUMNS) ? TRUE : FALSE; /* obj human readable name, and full name */ fullname = g_strdup_printf ("%s.%s.%s", dbo->obj_catalog, dbo->obj_schema, dbo->obj_name); if (dbo->obj_short_name) objname = g_strdup (dbo->obj_short_name); else if (dbo->obj_schema) objname = g_strdup_printf ("%s.%s", dbo->obj_schema, dbo->obj_name); else objname = g_strdup (dbo->obj_name); /* node */ switch (dbo->obj_type) { case GDA_META_DB_UNKNOWN: break; case GDA_META_DB_TABLE: if (use_html) { g_string_append_printf (string, "\"%s\" [label=<", fullname); g_string_append_printf (string, "", objname); } else g_string_append_printf (string, "\"%s\" [ shape = box label = \"%s\" ]", fullname, objname); break; case GDA_META_DB_VIEW: if (use_html) { g_string_append_printf (string, "\"%s\" [label=<
%s
", fullname); g_string_append_printf (string, "", objname); } else g_string_append_printf (string, "\"%s\" [ shape = ellipse, label = \"%s\" ]", fullname, objname); break; default: TO_IMPLEMENT; g_string_append_printf (string, "\"%s\" [ shape = note label = \"%s\" ]", fullname, objname); break; } /* columns, only for tables */ if (dbo->obj_type == GDA_META_DB_TABLE) { GdaMetaTable *mt = GDA_META_TABLE (dbo); GSList *depend_dbo_list = NULL; if (info & GDA_META_GRAPH_COLUMNS) { for (list = mt->columns; list; list = list->next) { GdaMetaTableColumn *tcol = GDA_META_TABLE_COLUMN (list->data); GString *extra = g_string_new (""); if (tcol->pkey) g_string_append_printf (extra, "key"); g_string_append_printf (string, "", tcol->column_name, extra->str); g_string_free (extra, TRUE); } } if (use_html) g_string_append (string, "
%s
%s%s
>];\n"); /* foreign keys */ for (list = mt->fk_list; list; list = list->next) { GdaMetaTableForeignKey *tfk = GDA_META_TABLE_FOREIGN_KEY (list->data); if (tfk->depend_on->obj_type != GDA_META_DB_UNKNOWN) { g_string_append_printf (string, "\"%s\" -> \"%s.%s.%s\";\n", fullname, tfk->depend_on->obj_catalog, tfk->depend_on->obj_schema, tfk->depend_on->obj_name); depend_dbo_list = g_slist_prepend (depend_dbo_list, tfk->depend_on); } } /* dependencies other than foreign keys */ for (list = dbo->depend_list; list; list = list->next) { if (!g_slist_find (depend_dbo_list, list->data)) { GdaMetaDbObject *dep_dbo = GDA_META_DB_OBJECT (list->data); if (dep_dbo->obj_type != GDA_META_DB_UNKNOWN) g_string_append_printf (string, "\"%s\" -> \"%s.%s.%s\";\n", fullname, dep_dbo->obj_catalog, dep_dbo->obj_schema, dep_dbo->obj_name); } } g_slist_free (depend_dbo_list); } else if (dbo->obj_type == GDA_META_DB_VIEW) { GdaMetaTable *mt = GDA_META_TABLE (dbo); if (info & GDA_META_GRAPH_COLUMNS) { for (list = mt->columns; list; list = list->next) { GdaMetaTableColumn *tcol = GDA_META_TABLE_COLUMN (list->data); g_string_append_printf (string, "%s", tcol->column_name); } } if (use_html) g_string_append (string, ">];\n"); /* dependencies */ for (list = dbo->depend_list; list; list = list->next) { GdaMetaDbObject *ddbo = GDA_META_DB_OBJECT (list->data); if (ddbo->obj_type != GDA_META_DB_UNKNOWN) g_string_append_printf (string, "\"%s\" -> \"%s.%s.%s\";\n", fullname, ddbo->obj_catalog, ddbo->obj_schema, ddbo->obj_name); } } g_free (objname); g_free (fullname); } g_string_append_c (string, '}'); result = string->str; g_string_free (string, FALSE); return result; } static void gda_meta_db_object_free_contents (GdaMetaDbObject *dbo) { g_free (dbo->obj_catalog); g_free (dbo->obj_schema); g_free (dbo->obj_name); g_free (dbo->obj_short_name); g_free (dbo->obj_full_name); g_free (dbo->obj_owner); switch (dbo->obj_type) { case GDA_META_DB_UNKNOWN: break; case GDA_META_DB_TABLE: gda_meta_table_free_contents (GDA_META_TABLE (dbo)); break; case GDA_META_DB_VIEW: gda_meta_view_free_contents (GDA_META_VIEW (dbo)); break; default: TO_IMPLEMENT; } g_slist_free (dbo->depend_list); memset (dbo, 0, sizeof (GdaMetaDbObject)); } static void gda_meta_db_object_free (GdaMetaDbObject *dbo) { gda_meta_db_object_free_contents (dbo); g_free (dbo); } static void gda_meta_table_free_contents (GdaMetaTable *table) { g_slist_foreach (table->columns, (GFunc) gda_meta_table_column_free, NULL); g_slist_free (table->columns); g_free (table->pk_cols_array); g_slist_foreach (table->fk_list, (GFunc) gda_meta_table_foreign_key_free, NULL); g_slist_free (table->fk_list); g_slist_free (table->reverse_fk_list); } static void gda_meta_view_free_contents (GdaMetaView *view) { gda_meta_table_free_contents ((GdaMetaTable*) view); g_free (view->view_def); } static void gda_meta_table_column_free (GdaMetaTableColumn *tcol) { g_free (tcol->column_name); g_free (tcol->column_type); g_free (tcol->default_value); gda_attributes_manager_clear (att_mgr, tcol); g_free (tcol); } static void gda_meta_table_foreign_key_free (GdaMetaTableForeignKey *tfk) { gint i; for (i = 0; i < tfk->cols_nb; i++) { g_free (tfk->fk_names_array[i]); g_free (tfk->ref_pk_names_array[i]); } g_free (tfk->fk_cols_array); g_free (tfk->fk_names_array); g_free (tfk->ref_pk_cols_array); g_free (tfk->ref_pk_names_array); g_free (tfk->fk_name); g_free (tfk); } static gboolean determine_db_object_from_schema_and_name (GdaMetaStruct *mstruct, GdaMetaDbObjectType *in_out_type, GValue **out_catalog, GValue **out_short_name, GValue **out_full_name, GValue **out_owner, const GValue *schema, const GValue *name) { const GValue *cvalue; GdaDataModel *model = NULL; *out_catalog = NULL; *out_short_name = NULL; *out_full_name = NULL; *out_owner = NULL; switch (*in_out_type) { case GDA_META_DB_UNKNOWN: { GdaMetaDbObjectType type = GDA_META_DB_TABLE; if (determine_db_object_from_schema_and_name (mstruct, &type, out_catalog, out_short_name, out_full_name, out_owner, schema, name)) { *in_out_type = GDA_META_DB_TABLE; return TRUE; } type = GDA_META_DB_VIEW; if (determine_db_object_from_schema_and_name (mstruct, &type, out_catalog, out_short_name, out_full_name, out_owner, schema, name)) { *in_out_type = GDA_META_DB_VIEW; return TRUE; } return FALSE; break; } case GDA_META_DB_TABLE: { const gchar *sql = "SELECT table_catalog, table_short_name, table_full_name, table_owner FROM _tables as t WHERE table_schema = ##ts::string AND table_name = ##tname::string AND table_name NOT IN (SELECT v.table_name FROM _views as v WHERE v.table_catalog=t.table_catalog AND v.table_schema=t.table_schema)"; gint nrows; model = gda_meta_store_extract (mstruct->priv->store, sql, NULL, "ts", schema, "tname", name, NULL); if (!model) return FALSE; nrows = gda_data_model_get_n_rows (model); if (nrows != 1) { g_object_unref (model); return FALSE; } cvalue = gda_data_model_get_value_at (model, 0, 0, NULL); if (!cvalue) goto copyerror; *out_catalog = gda_value_copy (cvalue); cvalue = gda_data_model_get_value_at (model, 1, 0, NULL); if (!cvalue) goto copyerror; *out_short_name = gda_value_copy (cvalue); cvalue = gda_data_model_get_value_at (model, 2, 0, NULL); if (!cvalue) goto copyerror; *out_full_name = gda_value_copy (cvalue); cvalue = gda_data_model_get_value_at (model, 3, 0, NULL); if (!cvalue) goto copyerror; *out_owner = gda_value_copy (cvalue); g_object_unref (model); return TRUE; } case GDA_META_DB_VIEW:{ const gchar *sql = "SELECT table_catalog, table_short_name, table_full_name, table_owner FROM _tables NATURAL JOIN _views WHERE table_schema = ##ts::string AND table_name = ##tname::string"; gint nrows; model = gda_meta_store_extract (mstruct->priv->store, sql, NULL, "ts", schema, "tname", name, NULL); if (!model) return FALSE; nrows = gda_data_model_get_n_rows (model); if (nrows != 1) { g_object_unref (model); return FALSE; } cvalue = gda_data_model_get_value_at (model, 0, 0, NULL); if (!cvalue) goto copyerror; *out_catalog = gda_value_copy (cvalue); cvalue = gda_data_model_get_value_at (model, 1, 0, NULL); if (!cvalue) goto copyerror; *out_short_name = gda_value_copy (cvalue); cvalue = gda_data_model_get_value_at (model, 2, 0, NULL); if (!cvalue) goto copyerror; *out_full_name = gda_value_copy (cvalue); cvalue = gda_data_model_get_value_at (model, 3, 0, NULL); if (!cvalue) goto copyerror; *out_owner = gda_value_copy (cvalue); g_object_unref (model); return TRUE; } default: TO_IMPLEMENT; } copyerror: if (model) g_object_unref (model); if (*out_catalog) { gda_value_free (*out_catalog); *out_catalog = NULL; } if (*out_short_name) { gda_value_free (*out_short_name); *out_short_name = NULL; } if (*out_full_name) { gda_value_free (*out_full_name); *out_full_name = NULL; } if (*out_owner) { gda_value_free (*out_owner); *out_owner = NULL; } return FALSE; } static gboolean determine_db_object_from_short_name (GdaMetaStruct *mstruct, GdaMetaDbObjectType *in_out_type, GValue **out_catalog, GValue **out_schema, GValue **out_name, GValue **out_short_name, GValue **out_full_name, GValue **out_owner, const GValue *name) { const GValue *cvalue; GdaDataModel *model = NULL; *out_name = NULL; *out_schema = NULL; *out_catalog = NULL; *out_short_name = NULL; *out_full_name = NULL; *out_owner = NULL; /* general lookup */ switch (*in_out_type) { case GDA_META_DB_UNKNOWN: { GdaMetaDbObjectType type = GDA_META_DB_TABLE; if (determine_db_object_from_short_name (mstruct, &type, out_catalog, out_schema, out_name, out_short_name, out_full_name, out_owner, name)) { *in_out_type = GDA_META_DB_TABLE; return TRUE; } type = GDA_META_DB_VIEW; if (determine_db_object_from_short_name (mstruct, &type, out_catalog, out_schema, out_name, out_short_name, out_full_name, out_owner, name)) { *in_out_type = GDA_META_DB_VIEW; return TRUE; } return FALSE; break; } case GDA_META_DB_TABLE: { const gchar *sql = "SELECT table_catalog, table_schema, table_name, table_short_name, table_full_name, table_owner FROM _tables as t WHERE table_short_name = ##tname::string AND table_name NOT IN (SELECT v.table_name FROM _views as v WHERE v.table_catalog=t.table_catalog AND v.table_schema=t.table_schema)"; gint nrows; model = gda_meta_store_extract (mstruct->priv->store, sql, NULL, "tname", name, NULL); if (!model) return FALSE; nrows = gda_data_model_get_n_rows (model); if (nrows != 1) { g_object_unref (model); goto next; } cvalue = gda_data_model_get_value_at (model, 0, 0, NULL); if (!cvalue) goto copyerror; *out_catalog = gda_value_copy (cvalue); cvalue = gda_data_model_get_value_at (model, 1, 0, NULL); if (!cvalue) goto copyerror; *out_schema = gda_value_copy (cvalue); cvalue = gda_data_model_get_value_at (model, 2, 0, NULL); if (!cvalue) goto copyerror; *out_name = gda_value_copy (cvalue); cvalue = gda_data_model_get_value_at (model, 3, 0, NULL); if (!cvalue) goto copyerror; *out_short_name = gda_value_copy (cvalue); cvalue = gda_data_model_get_value_at (model, 4, 0, NULL); if (!cvalue) goto copyerror; *out_full_name = gda_value_copy (cvalue); cvalue = gda_data_model_get_value_at (model, 5, 0, NULL); if (!cvalue) goto copyerror; *out_owner = gda_value_copy (cvalue); g_object_unref (model); return TRUE; } case GDA_META_DB_VIEW:{ const gchar *sql = "SELECT table_catalog, table_schema, table_name, table_short_name, table_full_name, table_owner FROM _tables NATURAL JOIN _views WHERE table_short_name = ##tname::string"; gint nrows; model = gda_meta_store_extract (mstruct->priv->store, sql, NULL, "tname", name, NULL); if (!model) return FALSE; nrows = gda_data_model_get_n_rows (model); if (nrows != 1) { g_object_unref (model); goto next; } cvalue = gda_data_model_get_value_at (model, 0, 0, NULL); if (!cvalue) goto copyerror; *out_catalog = gda_value_copy (cvalue); cvalue = gda_data_model_get_value_at (model, 1, 0, NULL); if (!cvalue) goto copyerror; *out_schema = gda_value_copy (cvalue); cvalue = gda_data_model_get_value_at (model, 2, 0, NULL); if (!cvalue) goto copyerror; *out_name = gda_value_copy (cvalue); cvalue = gda_data_model_get_value_at (model, 3, 0, NULL); if (!cvalue) goto copyerror; *out_short_name = gda_value_copy (cvalue); cvalue = gda_data_model_get_value_at (model, 4, 0, NULL); if (!cvalue) goto copyerror; *out_full_name = gda_value_copy (cvalue); cvalue = gda_data_model_get_value_at (model, 5, 0, NULL); if (!cvalue) goto copyerror; *out_owner = gda_value_copy (cvalue); g_object_unref (model); return TRUE; } default: TO_IMPLEMENT; } next: model = NULL; { /* treat the case where name is in fact . */ gchar *obj_schema; gchar *obj_name; if (_split_identifier_string (g_strdup ((gchar *) g_value_get_string (name)), &obj_schema, &obj_name)) { GValue *sv, *nv; gboolean retval; if (!obj_schema) { g_free (obj_name); return FALSE; } if (!obj_name) { g_free (obj_schema); return FALSE; } g_value_take_string ((sv = gda_value_new (G_TYPE_STRING)), prepare_sql_identifier_for_compare (obj_schema)); g_value_take_string ((nv = gda_value_new (G_TYPE_STRING)), prepare_sql_identifier_for_compare (obj_name)); retval = determine_db_object_from_schema_and_name (mstruct, in_out_type, out_catalog, out_short_name, out_full_name, out_owner, sv, nv); if (retval) { *out_schema = sv; *out_name = nv; } else { gda_value_free (sv); gda_value_free (nv); } return retval; } } copyerror: if (model) g_object_unref (model); if (*out_catalog) { gda_value_free (*out_catalog); *out_catalog = NULL; } if (*out_schema) { gda_value_free (*out_schema); *out_schema = NULL; } if (*out_name) { gda_value_free (*out_name); *out_name = NULL; } if (*out_short_name) { gda_value_free (*out_short_name); *out_short_name = NULL; } if (*out_full_name) { gda_value_free (*out_full_name); *out_full_name = NULL; } if (*out_owner) { gda_value_free (*out_owner); *out_owner = NULL; } return FALSE; } static gboolean determine_db_object_from_missing_type (GdaMetaStruct *mstruct, GdaMetaDbObjectType *out_type, GValue **out_short_name, GValue **out_full_name, GValue **out_owner, const GValue *catalog, const GValue *schema, const GValue *name) { /* try as a view first */ const gchar *sql = "SELECT table_short_name, table_full_name, table_owner FROM _tables NATURAL JOIN _views WHERE table_catalog = ##tc::string AND table_schema = ##ts::string AND table_name = ##tname::string"; GdaDataModel *model = NULL; const GValue *cvalue; *out_type = GDA_META_DB_UNKNOWN; *out_short_name = NULL; *out_full_name = NULL; *out_owner = NULL; model = gda_meta_store_extract (mstruct->priv->store, sql, NULL, "tc", catalog, "ts", schema, "tname", name, NULL); if (model && (gda_data_model_get_n_rows (model) == 1)) { *out_type = GDA_META_DB_VIEW; cvalue = gda_data_model_get_value_at (model, 0, 0, NULL); if (!cvalue) goto copyerror; *out_short_name = gda_value_copy (cvalue); cvalue = gda_data_model_get_value_at (model, 1, 0, NULL); if (!cvalue) goto copyerror; *out_full_name = gda_value_copy (cvalue); cvalue = gda_data_model_get_value_at (model, 2, 0, NULL); if (!cvalue) goto copyerror; *out_owner = gda_value_copy (cvalue); g_object_unref (model); return TRUE; } if (model) g_object_unref (model); /* try as a table */ sql = "SELECT table_short_name, table_full_name, table_owner FROM _tables WHERE table_catalog = ##tc::string AND table_schema = ##ts::string AND table_name = ##tname::string"; model = gda_meta_store_extract (mstruct->priv->store, sql, NULL, "tc", catalog, "ts", schema, "tname", name, NULL); if (model && (gda_data_model_get_n_rows (model) == 1)) { *out_type = GDA_META_DB_TABLE; cvalue = gda_data_model_get_value_at (model, 0, 0, NULL); if (!cvalue) goto copyerror; *out_short_name = gda_value_copy (cvalue); cvalue = gda_data_model_get_value_at (model, 1, 0, NULL); if (!cvalue) goto copyerror; *out_full_name = gda_value_copy (cvalue); cvalue = gda_data_model_get_value_at (model, 2, 0, NULL); if (!cvalue) goto copyerror; *out_owner = gda_value_copy (cvalue); g_object_unref (model); return TRUE; } if (model) { g_object_unref (model); model = NULL; } copyerror: if (model) g_object_unref (model); *out_type = GDA_META_DB_UNKNOWN; if (*out_short_name) { gda_value_free (*out_short_name); *out_short_name = NULL; } if (*out_full_name) { gda_value_free (*out_full_name); *out_full_name = NULL; } if (*out_owner) { gda_value_free (*out_owner); *out_owner = NULL; } return FALSE; } /** * _gda_meta_struct_add_db_object: * @mstruct: a #GdaMetaStruct object * @dbo: a #GdaMetaDbObject structure * @error: (allow-none): a place to store errors, or %NULL * * Adds @dbo to the database objects known to @mstruct. In any case (whether an error occured or not) * @dbo's ownership is then transferred to @smtruct and should * not be used after calling this function (it may have been destroyed). If you need a pointer to the #GdaMetaDbObject * for a database object, use gda_meta_struct_get_db_object(). * * Returns: (transfer none): a pointer to the #GdaMetaDbObject used in @mstruct to represent the added database object (may be @dbo or not) */ GdaMetaDbObject * _gda_meta_struct_add_db_object (GdaMetaStruct *mstruct, GdaMetaDbObject *dbo, GError **error) { GdaMetaDbObject *edbo; GValue *v1 = NULL, *v2 = NULL, *v3 = NULL; g_return_val_if_fail (GDA_IS_META_STRUCT (mstruct), NULL); g_return_val_if_fail (dbo, NULL); if (!dbo->obj_name) { g_set_error (error, GDA_META_STRUCT_ERROR, GDA_META_STRUCT_INCOHERENCE_ERROR, "%s", _("Missing object name in GdaMetaDbObject structure")); gda_meta_db_object_free (dbo); return NULL; } if (dbo->obj_catalog) g_value_set_string ((v1 = gda_value_new (G_TYPE_STRING)), dbo->obj_catalog); if (dbo->obj_schema) g_value_set_string ((v2 = gda_value_new (G_TYPE_STRING)), dbo->obj_schema); g_value_set_string ((v3 = gda_value_new (G_TYPE_STRING)), dbo->obj_name); edbo = gda_meta_struct_get_db_object (mstruct, v1, v2, v3); if (v1) gda_value_free (v1); if (v2) gda_value_free (v2); gda_value_free (v3); if (edbo) { if (edbo->obj_type == GDA_META_DB_UNKNOWN) { /* overwrite */ gda_meta_db_object_free_contents (edbo); *edbo = *dbo; g_free (dbo); return edbo; } else { g_set_error (error, GDA_META_STRUCT_ERROR, GDA_META_STRUCT_DUPLICATE_OBJECT_ERROR, _("Database object '%s' already exists"), edbo->obj_full_name); gda_meta_db_object_free (dbo); return NULL; } } else { mstruct->priv->db_objects = g_slist_append (mstruct->priv->db_objects, dbo); g_hash_table_insert (mstruct->priv->index, g_strdup (dbo->obj_full_name), dbo); return dbo; } } /** * gda_meta_table_column_get_attribute: * @tcol: a #GdaMetaTableColumn * @attribute: attribute name as a string * * Get the value associated to a named attribute. * * Attributes can have any name, but Libgda proposes some default names, see this section. * * Returns: (transfer none): a read-only #GValue, or %NULL if not attribute named @attribute has been set for @column */ const GValue * gda_meta_table_column_get_attribute (GdaMetaTableColumn *tcol, const gchar *attribute) { return gda_attributes_manager_get (att_mgr, tcol, attribute); } /** * gda_meta_table_column_set_attribute: * @tcol: a #GdaMetaTableColumn * @attribute: attribute name as a static string * @value: (allow-none): a #GValue, or %NULL * @destroy: (allow-none): function called when @attribute has to be freed, or %NULL * * Set the value associated to a named attribute. * * Attributes can have any name, but Libgda proposes some default names, see this section. * If there is already an attribute named @attribute set, then its value is replaced with the new @value, * except if @value is %NULL, in which case the attribute is removed. * * Warning: @attribute is not copied, if it needs to be freed when not used anymore, then @destroy should point to * the functions which will free it (typically g_free()). If @attribute does not need to be freed, then @destroy can be %NULL. */ void gda_meta_table_column_set_attribute (GdaMetaTableColumn *tcol, const gchar *attribute, const GValue *value, GDestroyNotify destroy) { const GValue *cvalue; cvalue = gda_attributes_manager_get (att_mgr, tcol, attribute); if ((value && cvalue && !gda_value_differ (cvalue, value)) || (!value && !cvalue)) return; gda_attributes_manager_set_full (att_mgr, tcol, attribute, value, destroy); } /** * gda_meta_table_column_foreach_attribute: * @tcol: a #GdaMetaTableColumn * @func: (scope call): a #GdaAttributesManagerFunc function * @data: (closure): user data to be passed as last argument of @func each time it is called * * Calls @func for each attribute set to tcol */ void gda_meta_table_column_foreach_attribute (GdaMetaTableColumn *tcol, GdaAttributesManagerFunc func, gpointer data) { gda_attributes_manager_foreach (att_mgr, tcol, func, data); }