/** * midgard_replicator_export: * @object: #MidgardDBObject instance * * Given object is not serialized. Its storage record is marked as exported. * * @see midgard_object_update() to find out possible error codes returned in case of #MidgardObject failure. * * Returns: %TRUE on success, %FALSE otherwise. */ gboolean midgard_replicator_export (MidgardDBObject *object) { g_return_val_if_fail (object != NULL, FALSE); MidgardConnection *mgd = MGD_OBJECT_CNC(object); g_return_val_if_fail (mgd != NULL, FALSE); const gchar *guid = MGD_OBJECT_GUID(object); if (guid == NULL){ MIDGARD_ERRNO_SET_STRING (mgd, MGD_ERR_INVALID_PROPERTY_VALUE, "Empty guid value! "); return FALSE; } /* FIXME, invoke update as MidgardDBObject virtual method */ if (MIDGARD_IS_OBJECT (object)) { g_signal_emit(object, MIDGARD_OBJECT_GET_CLASS(object)->signal_action_export, 0); return _midgard_object_update(MIDGARD_OBJECT(object), OBJECT_UPDATE_EXPORTED, NULL); } return FALSE; }
/** * midgard_connection_open_config: * @self: [in]newly initialized #MidgardConnection object * @config: #MidgardConfig object * * Opens a #MidgardConnection with the given configuration. * * Take a look at midgard_connection_open() wrt #MidgardSchema. * * If #MidgardConnection is already associated with given config, method returns %TRUE. * If associated with another one, %FALSE is returned and MGD_ERR_INTERNAL error is set. * * Returns: %TRUE on success, %FALSE otherwise */ gboolean midgard_connection_open_config( MidgardConnection *self, MidgardConfig *config) { g_assert(self != NULL); g_assert(config != NULL); MidgardConfig *self_config = self->priv->config; gboolean rv = TRUE; __SELF_REOPEN (self, rv); if (!rv) return rv; /* Emulate the same config pointer, as we have copy associated */ if (self_config && (g_str_equal (self_config->database, config->database) && g_str_equal (self_config->dbtype, config->dbtype) && g_str_equal (self_config->host, config->host))) { return TRUE; } else if (self_config) { MIDGARD_ERRNO_SET_STRING (self, MGD_ERR_INTERNAL, "Midgard connection already associated with configuration"); return FALSE; } self->priv->config = midgard_config_copy (config); GHashTable *hash = NULL; if(!__midgard_connection_open(self, &hash, TRUE)) rv = FALSE; if(hash) g_hash_table_destroy(hash); return rv; }
gboolean __midgard_connection_open( MidgardConnection *mgd, GHashTable **hashtable, gboolean init_schema) { g_return_val_if_fail(mgd != NULL, FALSE); MIDGARD_ERRNO_SET (mgd, MGD_ERR_OK); gchar *host, *dbname, *dbuser, *dbpass, *loglevel, *tmpstr; guint port = 0; gchar *auth = NULL; MidgardConfig *config = mgd->priv->config; host = config->host; dbname = config->database; dbuser = config->dbuser; dbpass = config->dbpass; loglevel = config->loglevel; port = config->dbport; gboolean enable_threads = config->gdathreads; /* Get 30% performance boost for non threaded applications */ if(!enable_threads) g_setenv("LIBGDA_NO_THREADS", "yes", TRUE); /* Initialize libgda */ gda_init (); midgard_connection_set_loglevel(mgd, loglevel, NULL); if(config->priv->dbtype == MIDGARD_DB_TYPE_SQLITE) { gchar *path = NULL; gchar *dbdir = config->dbdir; if (!dbdir || *dbdir == '\0') { const gchar *sqlite_dir[] = {"data", NULL}; path = midgard_core_config_build_path(sqlite_dir, NULL, TRUE); } else { path = g_strdup(dbdir); } tmpstr = g_strconcat("DB_DIR=", path, ";", "DB_NAME=", dbname, NULL); g_free(path); } else if (config->priv->dbtype == MIDGARD_DB_TYPE_ORACLE) { GString *cnc = g_string_sized_new(100); cnc_add_part(cnc, "TNSNAME", dbname, MGD_MYSQL_HOST); cnc_add_part(cnc, "HOST", host, MGD_MYSQL_HOST); cnc_add_part(cnc, "DB_NAME", dbname, MGD_MYSQL_DATABASE); tmpstr = g_string_free(cnc, FALSE); cnc = g_string_sized_new(100); cnc_add_part(cnc, "USERNAME", dbuser, MGD_MYSQL_USERNAME); cnc_add_part(cnc, "PASSWORD", dbpass, MGD_MYSQL_PASSWORD); auth = g_string_free(cnc, FALSE); } else { GString *cnc = g_string_sized_new(100); cnc_add_part(cnc, "HOST", host, MGD_MYSQL_HOST); if (port > 0) { GString *_strp = g_string_new(""); g_string_append_printf (_strp, "%d", port); cnc_add_part (cnc, "PORT", _strp->str, ""); g_string_free (_strp, TRUE); } cnc_add_part(cnc, "DB_NAME", dbname, MGD_MYSQL_DATABASE); tmpstr = g_string_free(cnc, FALSE); GString *auth_str = g_string_sized_new(100); cnc_add_part(auth_str, "USERNAME", dbuser, MGD_MYSQL_USERNAME); cnc_add_part(auth_str, "PASSWORD", dbpass, MGD_MYSQL_PASSWORD); auth = g_string_free(auth_str, FALSE); } GError *error = NULL; GdaConnection *connection = gda_connection_open_from_string( config->dbtype, tmpstr, auth, GDA_CONNECTION_OPTIONS_NONE, &error); g_free(auth); if(connection == NULL) { MIDGARD_ERRNO_SET_STRING (mgd, MGD_ERR_NOT_CONNECTED, " Database [%s]. %s", tmpstr, error->message); g_free(tmpstr); return FALSE; } g_free(tmpstr); mgd->priv->parser = gda_connection_create_parser (connection); if (!mgd->priv->parser) mgd->priv->parser = gda_sql_parser_new(); g_assert (mgd->priv->parser != NULL); mgd->priv->connection = connection; midgard_core_connection_connect_error_callback (mgd); if(init_schema) { if(!g_type_from_name("midgard_quota")) { MidgardSchema *schema = g_object_new(MIDGARD_TYPE_SCHEMA, NULL); gchar *path = g_build_path(G_DIR_SEPARATOR_S, config->sharedir, "MidgardObjects.xml", NULL); midgard_schema_init(schema, (const gchar *)path); g_free(path); midgard_schema_read_dir(schema, config->sharedir); mgd->priv->schema = schema; } } //midgard_connection_set_loglevel(mgd, loglevel, NULL); /* Loads available authentication types */ midgard_core_connection_initialize_auth_types(mgd); g_signal_emit (mgd, MIDGARD_CONNECTION_GET_CLASS (mgd)->signal_id_connected, 0); return TRUE; }
GList * midgard_core_qb_set_object_from_query (MidgardQueryBuilder *builder, guint select_type, MidgardObject **nobject) { g_assert(builder != NULL); guint ret_rows, ret_fields; MidgardConnection *mgd = builder->priv->mgd; gchar *sql = midgard_core_qb_get_sql( builder, select_type, midgard_query_builder_get_object_select(builder, select_type)); GSList *olist = NULL; MidgardDBObjectClass *dbklass = MIDGARD_DBOBJECT_CLASS(g_type_class_peek(builder->priv->type)); if (!dbklass) { MIDGARD_ERRNO_SET_STRING (mgd, MGD_ERR_INTERNAL, "Failed to peek MidgardDBObjectClass pointer"); return NULL; } if (dbklass->dbpriv->set_from_sql) { if (select_type != MQB_SELECT_GUID) { olist = dbklass->dbpriv->set_from_sql(mgd, builder->priv->type, ((const gchar *)sql)); g_free(sql); return (GList *)olist; } } GdaDataModel *model = midgard_core_query_get_model(builder->priv->mgd, sql); g_free(sql); if(!model) return NULL; MidgardObject *object = NULL; gint rows, columns; const GValue *gvalue = NULL; ret_rows = gda_data_model_get_n_rows(model); ret_fields = gda_data_model_get_n_columns(model); /* records found , allocate as many objects as many returned rows */ GList *list = NULL; if(ret_rows == 0) { g_object_unref(model); return list; } /* We count rows only */ if(select_type == MQB_SELECT_GUID) { gvalue = midgard_data_model_get_value_at(model, 0, 0); if (!gvalue || !G_IS_VALUE (gvalue)) { g_object_unref (model); return 0; } MidgardTypeHolder *holder = g_new(MidgardTypeHolder, 1); GValue val = {0, }; g_value_init (&val, G_TYPE_INT64); g_value_transform (gvalue, &val); holder->elements = (guint)g_value_get_int64((GValue*)&val); list = g_list_append(list, holder); g_object_unref(model); g_value_unset (&val); return list; } /* Get every row */ for (rows = 0; rows < ret_rows; rows++) { if(!nobject) object = g_object_new (builder->priv->type, "connection", mgd, NULL); else object = *nobject; MGD_OBJECT_IN_STORAGE (object) = TRUE; if(dbklass->dbpriv->__set_from_sql != NULL) { dbklass->dbpriv->__set_from_sql(MIDGARD_DBOBJECT(object), model, rows); } else { /* Compute number of metadata properties */ guint n_md_props = 0; MidgardMetadata *mklass = (MidgardMetadata *)MGD_DBCLASS_METADATA_CLASS (dbklass); if (mklass) n_md_props = g_hash_table_size (MGD_DBCLASS_STORAGE_DATA (mklass)->prophash); guint __cols = n_md_props + 1; /* Add one for guid */ /* we have guid and metadata columns first */ for (columns = __cols; columns < ret_fields; columns++) { gvalue = midgard_data_model_get_value_at(model, columns, rows); const gchar *coltitle = gda_data_model_get_column_title(model, columns); GParamSpec *pspec = g_object_class_find_property(G_OBJECT_GET_CLASS(object), coltitle); if(G_IS_VALUE(gvalue)) { if (!pspec) { g_warning("Failed to found (unregistered) %s property (%s class)", coltitle, G_OBJECT_TYPE_NAME(object)); continue; } if (G_VALUE_TYPE (gvalue) == GDA_TYPE_BINARY && G_TYPE_FUNDAMENTAL (pspec->value_type) == G_TYPE_STRING) { gchar *stringified = midgard_core_query_binary_stringify ((GValue *)gvalue); g_object_set (G_OBJECT (object), coltitle, stringified, NULL); g_free (stringified); } else if (pspec->value_type != G_VALUE_TYPE (gvalue)) { GValue _convert = {0, }; g_value_init (&_convert, pspec->value_type); if (g_value_transform (gvalue, &_convert)) { /* FIXME, remove workaround once it's fixed in GDA */ /* https://bugzilla.gnome.org/show_bug.cgi?id=617550 */ guint dbtype = mgd->priv->config->priv->dbtype; if (dbtype == MIDGARD_DB_TYPE_MYSQL && G_VALUE_TYPE (gvalue) == GDA_TYPE_BLOB) { gchar *tmp_str = __default_unescape_string (g_value_get_string (&_convert)); if (tmp_str) g_value_take_string (&_convert, tmp_str); } g_object_set_property (G_OBJECT (object), coltitle, &_convert); } else { g_warning ("Failed to convert %s to %s for %s property", G_VALUE_TYPE_NAME (gvalue), G_VALUE_TYPE_NAME (&_convert), coltitle); } g_value_unset (&_convert); } else { g_object_set_property(G_OBJECT(object), coltitle, gvalue); } } else if (gda_value_is_null(gvalue)) { switch (pspec->value_type) { case G_TYPE_INT: case G_TYPE_UINT: g_object_set(G_OBJECT(object), coltitle, 0, NULL); break; case G_TYPE_STRING: g_object_set(G_OBJECT(object), coltitle, "", NULL); break; default: g_warning("Found (%s) not a value at %d.%d (%s)", g_type_name(pspec->value_type), columns, rows, gda_data_model_get_column_title(model, columns)); break; } } } } /* Set guid */ const gchar *guid; gvalue = midgard_data_model_get_value_at(model, 0, rows); if(G_IS_VALUE(gvalue) && G_VALUE_HOLDS_STRING(gvalue)){ guid = g_value_get_string(gvalue); g_free((gchar *)MIDGARD_DBOBJECT(object)->dbpriv->guid); MIDGARD_DBOBJECT(object)->dbpriv->guid = g_strdup(guid); } /* Set metadata */ MidgardMetadata *metadata = MGD_DBOBJECT_METADATA (object); if (metadata) { GParamSpec *pspec = g_object_class_find_property(G_OBJECT_GET_CLASS(object), "metadata"); if (pspec) __mqb_set_metadata(metadata, model, rows); } list = g_list_append(list, G_OBJECT(object)); } g_object_unref(model); return list; }
/** * midgard_replicator_import_object: * @object: #MidgardDBObject instance * @force: toggle to force import * * Imports given object to underlying storage * * Cases to return %FALSE: * <itemizedlist> * <listitem><para> * Given guid is NULL or empty string (MGD_ERR_INVALID_PROPERTY_VALUE) * </para></listitem> * <listitem><para> * Object is already imported (MGD_ERR_OBJECT_IMPORTED) * </para></listitem> * <listitem><para> * Object identified is deleted (MGD_ERR_OBJECT_DELETED) * </para></listitem> * <listitem><para> * Object doesn't provide metadata one (MGD_ERR_NO_METADATA) * </para></listitem> * </itemizedlist> * * Set @force toggle if you want to import object even if it's already imported or deleted. * * Returns: %TRUE on success, %FALSE otherwise */ gboolean midgard_replicator_import_object (MidgardDBObject *object, gboolean force) { MidgardConnection *mgd = MGD_OBJECT_CNC (object); const gchar *guid = MGD_OBJECT_GUID (object); g_return_val_if_fail (mgd != NULL, FALSE); g_return_val_if_fail (g_type_is_a (G_OBJECT_TYPE (object), MIDGARD_TYPE_DBOBJECT), FALSE); MIDGARD_ERRNO_SET(object->dbpriv->mgd, MGD_ERR_OK); gboolean ret_val = FALSE; if (guid == NULL || (guid && *guid == '\0')) { MIDGARD_ERRNO_SET_STRING (mgd, MGD_ERR_INVALID_PROPERTY_VALUE, "NULL or empty guid"); return ret_val; } MidgardMetadata *dbmetadata = NULL; MidgardMetadata *metadata = MGD_DBOBJECT_METADATA (object); if (!metadata) { MIDGARD_ERRNO_SET_STRING (mgd, MGD_ERR_NO_METADATA, "Metadata class not available."); return ret_val; } g_signal_emit(object, MIDGARD_OBJECT_GET_CLASS(object)->signal_action_import, 0); /* Get object from database */ MidgardQueryBuilder *builder = midgard_query_builder_new(mgd, G_OBJECT_TYPE_NAME(object)); if(!builder) return FALSE; GValue pval = {0, }; /* Add guid constraint */ g_value_init(&pval,G_TYPE_STRING); g_object_get_property(G_OBJECT(object), "guid", &pval); midgard_query_builder_add_constraint(builder, "guid", "=", &pval); g_value_unset(&pval); /* Get db object which is "elder" than imported one */ /* g_value_init(&pval,G_TYPE_STRING); g_object_get_property(G_OBJECT(object->metadata), "revised", &pval); midgard_query_builder_add_constraint(builder, "metadata.revised", "<", &pval); g_value_unset(&pval); */ /* Get deleted or undeleted object */ midgard_query_builder_include_deleted (builder); guint n_objects; GObject **_dbobjects = midgard_query_builder_execute (builder, &n_objects); MidgardObject *dbobject; if (!_dbobjects){ g_object_unref (G_OBJECT (builder)); if ((mgd->errnum == MGD_ERR_OBJECT_PURGED) && force) { /* we need to delete repligard entry here * In any other case we need to change core's API a bit * and make create method more complicated with * additional needless cases */ GString *sql = g_string_new ("DELETE from repligard WHERE "); g_string_append_printf (sql, "typename = '%s' AND guid = '%s' ", G_OBJECT_TYPE_NAME (object), MGD_OBJECT_GUID (object)); midgard_core_query_execute (MGD_OBJECT_CNC (object), sql->str, TRUE); g_string_free (sql, TRUE); ret_val = _midgard_object_create (MIDGARD_OBJECT (object), MGD_OBJECT_GUID (object), OBJECT_UPDATE_IMPORTED); return ret_val; } if (mgd->errnum == MGD_ERR_NOT_EXISTS || mgd->errnum == MGD_ERR_OK) { ret_val = _midgard_object_create (MIDGARD_OBJECT(object), MGD_OBJECT_GUID(object), OBJECT_UPDATE_IMPORTED); return ret_val; } } else { gchar *updated, *dbupdated; dbobject = (MidgardObject *)_dbobjects[0]; g_free (_dbobjects); GValue updated_timestamp = {0, }; g_value_init (&updated_timestamp, MGD_TYPE_TIMESTAMP); GValue dbupdated_timestamp = {0, }; g_value_init (&dbupdated_timestamp, MGD_TYPE_TIMESTAMP); metadata = MGD_DBOBJECT_METADATA (object); dbmetadata = MGD_DBOBJECT_METADATA (dbobject); /* Compare revised datetimes. We must know if imported * object is newer than that one which exists in database */ g_object_get_property (G_OBJECT (metadata), "revised", &updated_timestamp); g_object_get_property (G_OBJECT (dbmetadata), "revised", &dbupdated_timestamp); updated = midgard_timestamp_get_string_from_value ((const GValue *)&updated_timestamp); dbupdated = midgard_timestamp_get_string_from_value ((const GValue *)&dbupdated_timestamp); g_value_unset (&updated_timestamp); g_value_unset (&dbupdated_timestamp); /* We can use g_ascii_strcasecmp as it type cast every single * pointer to integer and unsigned char returning substract result */ gint datecmp; if (updated == NULL) { g_warning ("Trying to import ivalid object. metadata.revised property holds NULL (Object: %s)", MGD_OBJECT_GUID (object)); if (dbupdated) g_free (dbupdated); g_object_unref (dbobject); return FALSE; } if (dbupdated == NULL) { g_warning ("Trying to import object for invalid object stored in database. metadata.revised property holds NULL (Database object:%s)", MGD_OBJECT_GUID (object)); if (updated) g_free (updated); g_object_unref (dbobject); return FALSE; } if (force) { datecmp = -1; } else { datecmp = g_ascii_strcasecmp ((const gchar *)dbupdated, (const gchar *)updated); } g_free(updated); g_free(dbupdated); gboolean deleted; gboolean ret; if (datecmp > 0 || datecmp == 0) { /* Database object is more recent or exactly the same */ g_object_unref (dbobject); MIDGARD_ERRNO_SET (mgd, MGD_ERR_OBJECT_IMPORTED); return FALSE; } else if (datecmp < 0) { /* Database object is elder */ /* DELETE */ g_object_get (G_OBJECT (metadata), "deleted", &deleted, NULL); /* Imported object is marked as deleted , so * * we delete object from database */ if (deleted) { ret = midgard_object_delete (dbobject, FALSE); g_object_unref (dbobject); return ret; } /* UPDATE */ MidgardMetadata *db_metadata = MGD_DBOBJECT_METADATA (dbobject); /* Check if dbobject is deleted */ g_object_get (db_metadata, "deleted", &deleted, NULL); guint undelete; g_object_get (G_OBJECT (metadata), "deleted", &undelete, NULL); if ((deleted && !undelete)) { midgard_schema_object_factory_object_undelete (mgd, MGD_OBJECT_GUID (dbobject)); goto _update_object; } if (deleted && !force) { MIDGARD_ERRNO_SET (mgd, MGD_ERR_OBJECT_DELETED); g_object_unref (dbobject); return FALSE; } _update_object: ret = _midgard_object_update (MIDGARD_OBJECT (object), OBJECT_UPDATE_IMPORTED, NULL); g_object_unref (dbobject); return ret; } } return FALSE; }
/** * midgard_replicator_export_by_guid: * @mgd: #MidgardConnection instance * @guid: guid which identifies object to be exported * * Marks object's storage record as exported. * * Cases to return %FALSE: * <itemizedlist> * <listitem><para> * Given guid is NULL or empty string (MGD_ERR_INVALID_PROPERTY_VALUE) * </para></listitem> * <listitem><para> * Object identified by given guid doesn't exist (MGD_ERR_NOT_EXISTS) * </para></listitem> * <listitem><para> * Object identified by given guid is purged (MGD_ERR_OBJECT_PURGED) * </para></listitem> * <listitem><para> * Internal storage error (MGD_ERR_INTERNAL) * </para></listitem> * </itemizedlist> * * Returns: %TRUE on success, %FALSE otherwise */ gboolean midgard_replicator_export_by_guid (MidgardConnection *mgd, const gchar *guid) { g_return_val_if_fail (mgd != NULL, FALSE); if(guid == NULL || (guid && (*guid == '\0'))) { MIDGARD_ERRNO_SET_STRING (mgd, MGD_ERR_INVALID_PROPERTY_VALUE, "Empty or NULL guid given"); return FALSE; } MIDGARD_ERRNO_SET(mgd, MGD_ERR_OK); MidgardDBObject *repligard = NULL; GError *error = NULL; GValue gval = {0, }; g_value_init (&gval, G_TYPE_STRING); g_value_set_string (&gval, guid); midgard_core_query_get_object (mgd, g_type_name (MIDGARD_TYPE_REPLIGARD), &repligard, FALSE, &error, "guid", &gval, NULL); g_value_unset (&gval); if (error) { MIDGARD_ERRNO_SET_STRING (mgd, error->code, "%s", error->message); g_clear_error (&error); if (repligard) g_object_unref (G_OBJECT (repligard)); return FALSE; } guint action; gchar *classname; g_object_get (repligard, "action", &action, "type", &classname, NULL); g_object_unref (repligard); MidgardObjectClass *klass = MIDGARD_OBJECT_GET_CLASS_BY_NAME(classname); if(klass == NULL) { g_warning("Failed to get class pointer for '%s', an object identified with '%s'", classname, guid); MIDGARD_ERRNO_SET(mgd, MGD_ERR_INTERNAL); g_free (classname); return FALSE; } g_free (classname); const gchar *table = midgard_core_class_get_table(MIDGARD_DBOBJECT_CLASS(klass)); GValue tval = {0, }; gchar *timeupdated; gint qr; GString *sql = NULL; switch(action) { case MGD_OBJECT_ACTION_PURGE: MIDGARD_ERRNO_SET(mgd, MGD_ERR_OBJECT_PURGED); return FALSE; break; default: sql = g_string_new ("UPDATE "); g_string_append_printf(sql, "%s SET ", table); midgard_timestamp_new_current (&tval); timeupdated = midgard_timestamp_get_string_from_value (&tval); g_string_append_printf(sql, "metadata_exported='%s' WHERE guid = '%s' ", timeupdated, guid); qr = midgard_core_query_execute(mgd, sql->str, TRUE); g_string_free(sql, TRUE); g_value_unset (&tval); g_free(timeupdated); if (qr == 0) { MIDGARD_ERRNO_SET(mgd, MGD_ERR_INTERNAL); return FALSE; } return TRUE; break; } return FALSE; }