Re: [Planner Dev] Planner database version



Hi guys!

After reading the Lincoln and Jani emails, and coding and testing 2
hours, here goes a new patch with the database version for all planner
projects in a separate table. With this approach, we can grow the table
to meet Liconln hosting needs and also, Jani ideas.

But for the moment, I want to close a first working version in which we
can continue working.

The patch is very similar to the last one but:

- Checks for database version and store database version after creating
or upgrading tables.

- More checks for the sql files: if upgrade and new database files don't
finish with the same database version, a warning in console is displayed
and the process fails. So, everytime we change the database between
planner versions, we need upgrade files and also, database files with
the complete description of tables and relations.

- Removed asking the user if she wants to create the tables. She have
said that she want the new database so ... sure she will want also the
tables ;-)


With the patch I attach the new sql files. Hmmm, I think the email will
be queued because size :(

Cheers

-- Alvaro

El sáb, 31-07-2004 a las 09:23, Alvaro del Castillo escribió:
> Hi!
> 
> The database create/upgrade patch only misses actually a way to know the
> version for planner database. Thinking about how we can save in database
> the version (needed for the upgrade system), I finally think that the
> best thing we can do is to store it in "property" table as a "text"
> property.
> 
> property records are related to projects in table "project_to_property"
> so it could be clear that if a property isn't in project_to_property, it
> is a global property for all planner projects, like the database
> version.
> 
> But we can't do it that way because properties types are related to
> projects. So if we want to use a property to store the database version,
> we need a pseudo-project in which to store the global properties for
> planner. I am not sure how "hackish" it sounds to you. For me yes, it is
> a bit hackish but the other solution will be to create a separate table:
> 
> CREATE TABLE property_global (
>        	prop_id    	  serial,
> 	prop_name	  text NOT NULL,
> 	value		  text,
> 	PRIMARY KEY (prop_id)
> ); 
> 
> Then we can't have different properties types as in properties for
> projects but you know, in this table for the moment we will have only 1
> record and I can't think now about other things we ca store here.
> 
> I think I will follow the new table approach but, what do you think?
> 
> http://cvs.gnome.org/viewcvs/planner/data/sql/database-0.11.sql?rev=1.1&view=auto (database tables)
> 
> Cheers
> 
> -- Alvaro
> 
>  
> 
> ______________________________________________________________________
> _______________________________________________
> Planner-dev mailing list
> Planner-dev lists imendio com
> http://lists.imendio.com/mailman/listinfo/planner-dev
Index: data/sql/Makefile.am
===================================================================
RCS file: /cvs/gnome/planner/data/sql/Makefile.am,v
retrieving revision 1.2
diff -u -b -B -p -r1.2 Makefile.am
--- data/sql/Makefile.am	30 Jun 2004 10:47:32 -0000	1.2
+++ data/sql/Makefile.am	31 Jul 2004 09:24:59 -0000
@@ -1,8 +1,10 @@
 sqldir = $(datadir)/planner/sql
 
 sql_DATA = 				\
+	database-0.13.sql		\
 	database-0.11.sql		\
 	database.sql			\
+	upgrade-0.11-0.13.sql		\
 	upgrade-0.6.x-0.11.sql
 
 EXTRA_DIST = $(sql_DATA)
Index: libplanner/Makefile.am
===================================================================
RCS file: /cvs/gnome/planner/libplanner/Makefile.am,v
retrieving revision 1.7
diff -u -b -B -p -r1.7 Makefile.am
--- libplanner/Makefile.am	2 May 2004 13:30:16 -0000	1.7
+++ libplanner/Makefile.am	31 Jul 2004 09:25:00 -0000
@@ -2,7 +2,8 @@ INCLUDES = \
 	-I. -I$(top_srcdir) \
 	$(LIBPLANNER_CFLAGS) $(WARN_CFLAGS) \
 	-DMRP_STORAGEMODULEDIR=\""$(libdir)/planner/storage-modules"\" \
-	-DMRP_FILE_MODULES_DIR=\""$(libdir)/planner/file-modules"\"
+	-DMRP_FILE_MODULES_DIR=\""$(libdir)/planner/file-modules"\" \
+	-DDATADIR=\""$(datadir)"\"
 
 lib_LTLIBRARIES = libplanner-1.la
 
Index: libplanner/mrp-project.c
===================================================================
RCS file: /cvs/gnome/planner/libplanner/mrp-project.c,v
retrieving revision 1.10
diff -u -b -B -p -r1.10 mrp-project.c
Index: libplanner/mrp-sql.c
===================================================================
RCS file: /cvs/gnome/planner/libplanner/mrp-sql.c,v
retrieving revision 1.8
diff -u -b -B -p -r1.8 mrp-sql.c
--- libplanner/mrp-sql.c	25 Jun 2004 09:59:35 -0000	1.8
+++ libplanner/mrp-sql.c	31 Jul 2004 09:25:02 -0000
@@ -37,7 +37,7 @@
 #define REVISION "sql-storage-revision"
 
 /* Struct to keep calendar data before we can build the tree, create the
- * calendars and insert the in the project.
+ * calendars and insert them in the project.
  */
 typedef struct {
 	gint    id;
@@ -206,7 +206,7 @@ sql_get_last_error (GdaConnection *conne
 
 	error = (GdaError *) g_list_last (list)->data;
       
-	/* Poor user, she won't get localized messages */
+	/* FIXME: Poor user, she won't get localized messages */
 	error_txt = gda_error_get_description (error);
 
 	return error_txt;
@@ -430,7 +430,7 @@ sql_read_project (SQLData *data, gint pr
 	g_free (query);
 	
 	if (res == NULL) {
-		g_warning ("Couldn't get cursor for project %s.", 
+		g_warning ("DECLARE CURSOR command failed (project) %s.", 
 				sql_get_last_error (data->con));
 		goto out;
 	}
@@ -604,7 +604,7 @@ sql_read_property_specs (SQLData *data)
 	
 
 	if (res == NULL) {
-		g_warning ("DECLARE CURSOR command failed (propecty_specs) %s.",
+		g_warning ("DECLARE CURSOR command failed (propecty_type) %s.",
 				sql_get_last_error (data->con));
 		goto out;
 	}
@@ -612,7 +612,7 @@ sql_read_property_specs (SQLData *data)
 
 	res = sql_execute_query (data->con, "FETCH ALL in mycursor");
 	if (res == NULL) {
-		g_warning ("FETCH ALL failed for property_specs %s.", 
+		g_warning ("FETCH ALL failed for property_type %s.", 
 				sql_get_last_error (data->con));
 		goto out;
 	}
@@ -695,9 +695,10 @@ sql_read_property_specs (SQLData *data)
 						  TRUE /* FIXME: user_defined, should 
 							  be read from the file */);
 					
-			g_hash_table_insert (data->property_type_id_hash, GINT_TO_POINTER (property_type_id), property);
+			g_hash_table_insert (data->property_type_id_hash, 
+					     GINT_TO_POINTER (property_type_id), property);
 		} else {
-			/* Properties that are already added (e.g. cost). */
+			/* FIXME: Properties that are already added (e.g. cost). */
 			property = mrp_project_get_property (data->project, name, owner);
 			g_hash_table_insert (data->property_type_id_hash, GINT_TO_POINTER (property_type_id), property);
 		}
@@ -2146,6 +2147,7 @@ mrp_sql_load_project (MrpStorageSQL *sto
 	data = g_new0 (SQLData, 1);
 
 	data->project_id = -1;
+	/* data->project_id = project_id; */
 	data->day_id_hash = g_hash_table_new (NULL, NULL);
 	data->calendar_id_hash = g_hash_table_new (NULL, NULL);
 	data->group_id_hash = g_hash_table_new (NULL, NULL);
Index: libplanner/mrp-storage-sql.c
===================================================================
RCS file: /cvs/gnome/planner/libplanner/mrp-storage-sql.c,v
retrieving revision 1.2
diff -u -b -B -p -r1.2 mrp-storage-sql.c
Index: src/Makefile.am
===================================================================
RCS file: /cvs/gnome/planner/src/Makefile.am,v
retrieving revision 1.16
diff -u -b -B -p -r1.16 Makefile.am
--- src/Makefile.am	21 Jun 2004 20:57:05 -0000	1.16
+++ src/Makefile.am	31 Jul 2004 09:25:02 -0000
@@ -12,6 +12,8 @@ INCLUDES = \
 	-DGLADEDIR=\""$(datadir)/planner/glade"\"		\
 	-DMRP_VIEWDIR=\""$(libdir)/planner/views"\"		\
 	-DMRP_PLUGINDIR=\""$(libdir)/planner/plugins"\"		\
+	-DSQL_DIR=\""$(datadir)/planner/sql"\"			\
+	-DVERSION=\""$(VERSION)"\" 				\
 	$(GNOMEUI_UNSTABLE)
 
 if HAVE_PYTHON_PLUGIN
Index: src/planner-sql-plugin.c
===================================================================
RCS file: /cvs/gnome/planner/src/planner-sql-plugin.c,v
retrieving revision 1.12
diff -u -b -B -p -r1.12 planner-sql-plugin.c
--- src/planner-sql-plugin.c	25 Jun 2004 09:59:35 -0000	1.12
+++ src/planner-sql-plugin.c	31 Jul 2004 09:25:03 -0000
@@ -77,6 +77,10 @@ static void     sql_plugin_save         
 static GdaDataModel * 
                 sql_execute_query              (GdaConnection      *con, 
 					        gchar              *query);
+
+/* FIXME: The same in mrp-sql.c. Create a SQL API in libplanner? */ 
+static const gchar * sql_get_last_error        (GdaConnection      *connection);
+
 void            plugin_init                    (PlannerPlugin      *plugin,
 						PlannerWindow      *main_window);
 void            plugin_exit                    (void);
@@ -112,6 +116,27 @@ sql_execute_query (GdaConnection *con, g
 	return res;
 }
 
+static const gchar *
+sql_get_last_error (GdaConnection *connection)
+{
+	GList       *list;
+	GdaError    *error;
+	const gchar *error_txt;
+
+	list = (GList *) gda_connection_get_errors (connection);
+
+	if (list == NULL) {
+		return _("No errors reported.");
+	}
+
+	error = (GdaError *) g_list_last (list)->data;
+      
+	/* FIXME: Poor user, she won't get localized messages */
+	error_txt = gda_error_get_description (error);
+
+	return error_txt;
+}
+
 
 /**
  * Helper to get an int.
@@ -291,6 +316,373 @@ row_activated_cb (GtkWidget         *tre
 	gtk_widget_activate (ok_button);
 }
 
+/* Planner versions:
+   1.x is always lower than 2.x.
+   0.6 is lower than 0.11 
+   If 0.11.90 we don't look ".90".
+*/
+static gboolean
+is_newer_version (const gchar *version_new_txt, 
+		  const gchar *version_old_txt)
+{
+	guint   subversion_old, subversion_new;
+	guint   version_old, version_new;
+	gchar **versionv_new, **versionv_old;
+
+	g_return_val_if_fail (version_new_txt != NULL && 
+			      version_old_txt != NULL, FALSE);
+	
+	version_old = g_ascii_strtod (version_old_txt, NULL);
+	version_new = g_ascii_strtod (version_new_txt, NULL);
+
+	if (version_new > version_old) {
+		return TRUE;
+	}
+	else if (version_old > version_new) {
+		return FALSE;
+	}
+	
+	/* Need to check subversion */
+	versionv_old = g_strsplit (version_old_txt,".",-1);
+	versionv_new = g_strsplit (version_new_txt,".",-1);
+
+	subversion_old = g_ascii_strtod (versionv_old[1], NULL);
+	subversion_new = g_ascii_strtod (versionv_new[1], NULL);
+
+	g_strfreev (versionv_new);
+	g_strfreev (versionv_old);
+
+	if (subversion_new > subversion_old) {
+		return TRUE;
+	}
+	return FALSE;
+}
+
+static gboolean
+check_database_tables (GdaConnection *conn,
+		       PlannerPlugin *plugin)
+{
+	GtkWindow    *window;
+	GdaDataModel *res;
+	GtkWidget    *dialog;
+	gint          result;
+	GDir*         dir;
+	const gchar  *name;
+	gboolean      upgradable = FALSE;
+	gboolean      create_tables;
+	gboolean      can_create_tables = FALSE;
+	gchar        *max_version_database;
+	gchar        *max_version_upgrade;
+	gchar        *upgrade_file = NULL;
+	gchar        *database_file = NULL;
+	gchar        *database_version = VERSION;
+	const gchar  *database_name;
+	gboolean      retval = FALSE;
+
+	max_version_database = g_strdup ("0.0");
+	max_version_upgrade = g_strdup ("0.0");
+	database_name = gda_connection_get_database (conn);
+
+	window = GTK_WINDOW (plugin->main_window);
+
+	/* Try to get the database version */
+	res = sql_execute_query (conn, "SELECT value FROM property_global WHERE prop_name='database_version'");		
+	if (res == NULL) {
+		create_tables = TRUE;
+	} else {
+		create_tables = FALSE;
+		database_version = get_string (res, 0, 0);
+		g_message ("Database version : %s", database_version);
+		if (database_version == NULL) {
+			database_version = VERSION;
+		}
+		g_object_unref (res);
+	}
+
+	/* Check for tables */
+	dir = g_dir_open (SQL_DIR, 0, NULL);
+	while ((name = g_dir_read_name (dir)) != NULL) {
+		gchar **namev = NULL, **versionv = NULL;
+		gchar  *version;
+		gchar  *sql_file = g_build_filename (SQL_DIR, name, NULL);
+
+		if (strncmp (name + strlen (name) - 4, ".sql", 4) != 0) {
+			g_warning ("Strange file in SQL data Planner directory: %s%s", 
+				   SQL_DIR, name);
+			continue;
+		}
+
+		/* Find version between "-" and ".sql" */
+		namev = g_strsplit (sql_file,"-",-1);
+		/* Upgrade: 2 versions in file */
+		if (namev[1] && namev[2]) {
+			versionv = g_strsplit (namev[2],".sql",-1);
+			if (is_newer_version (versionv[0], namev[1])) {
+				if (!strcmp (namev[1], database_version)) {
+					upgradable = TRUE;
+					if (is_newer_version (versionv[0], 
+							      max_version_upgrade)) {
+						if (upgrade_file) {
+							g_free (upgrade_file);
+						}
+						upgrade_file = g_strdup (sql_file);
+						g_free (max_version_upgrade);
+						max_version_upgrade = g_strdup (versionv[0]);
+					}
+				}
+			} else {
+				g_warning ("Incorrect upgrade file name: %s", sql_file);
+			}
+		}
+		/* Create tables */
+		else if (namev[1]) {
+			versionv = g_strsplit (namev[1],".sql",-1);
+			if (is_newer_version (versionv[0], max_version_database)) {
+				if (database_file) {
+					g_free (database_file);
+				}
+				database_file = g_strdup (sql_file);
+				g_free (max_version_database);
+				max_version_database = g_strdup (versionv[0]);
+			}
+			
+			can_create_tables = TRUE;
+			version = g_strdup (versionv[0]);
+			g_free (version);
+			
+		} else {
+			if (!database_file) {
+				database_file = g_strdup (sql_file);
+			}
+			g_warning ("File with no version: %s", sql_file);
+			can_create_tables = TRUE;
+		}
+		if (versionv) {
+			g_strfreev (versionv);
+		}
+		if (namev) {
+			g_strfreev (namev);
+		}
+		g_free (sql_file);
+	}
+
+	/* With each database change we need the new complete database description
+	   and the upgrades files from different versions */
+	if ((is_newer_version (max_version_upgrade, max_version_database) ||
+	    is_newer_version (max_version_database, max_version_upgrade))
+	    && 
+	    (upgradable || create_tables)) {
+		g_warning ("Database file version %s (%s) is different from upgrade file version %s (%s)",
+			   max_version_database,
+			   database_file,
+			   max_version_upgrade,
+			   upgrade_file);
+		retval = FALSE;
+		upgradable = FALSE;
+		can_create_tables = FALSE;		
+	}
+
+	if (!upgradable && !create_tables) {
+		retval = TRUE;
+	}
+	else if (upgradable && !create_tables) {
+		gchar *contents;
+
+		dialog = gtk_message_dialog_new (window,
+						 GTK_DIALOG_DESTROY_WITH_PARENT,
+						 GTK_MESSAGE_QUESTION,
+						 GTK_BUTTONS_NONE,
+						 _("Database %s need to be upgraded "
+						   "from version %s to version %s."),
+						 database_name, database_version, 
+						 max_version_upgrade);
+		gtk_dialog_add_buttons ((GtkDialog *) dialog, 
+					GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, 
+					_("Upgrade"), GTK_RESPONSE_YES,  
+					NULL);						 
+		
+		result = gtk_dialog_run (GTK_DIALOG (dialog));
+		gtk_widget_destroy (dialog);
+		if (result == GTK_RESPONSE_YES) {
+			g_file_get_contents (upgrade_file, &contents, NULL, NULL);
+			res = sql_execute_query (conn, contents);
+			g_free (contents);
+			if (res == NULL) {
+				dialog = gtk_message_dialog_new (window,
+								 GTK_DIALOG_DESTROY_WITH_PARENT,
+								 GTK_MESSAGE_WARNING,
+								 GTK_BUTTONS_CLOSE,
+								 _("Could not upgrade database %s.\n Upgrade file used: %s."
+								   "\n\nDatabase error: \n%s"),
+								 database_name, upgrade_file,
+								 sql_get_last_error (conn));
+				
+				gtk_dialog_run (GTK_DIALOG (dialog));
+				gtk_widget_destroy (dialog);
+				retval = FALSE;
+			} else {
+				gchar *query;
+
+				sql_execute_query (conn, "DELETE * FROM property_global WHERE prop_name='database_version'");
+				query = g_strdup_printf ("INSERT INTO property_global (prop_name, value) VALUES ('database_version','%s')", max_version_upgrade); 
+
+				sql_execute_query (conn, query);
+				g_free (query);
+				g_free (res);
+				retval = TRUE;
+			}
+		} else {
+			retval = FALSE;
+		}
+		g_free (upgrade_file);
+	}
+
+	else if (create_tables && !can_create_tables) {
+		g_warning ("Need to create tables but no database file");
+		retval = FALSE;
+	}
+
+	else if (create_tables && can_create_tables) {
+		gchar  *contents;
+
+		g_file_get_contents (database_file, &contents, NULL, NULL);
+		res = sql_execute_query (conn, contents);
+		g_free (contents);
+		if (res == NULL) {
+			dialog = gtk_message_dialog_new (window,
+							 GTK_DIALOG_DESTROY_WITH_PARENT,
+							 GTK_MESSAGE_WARNING,
+							 GTK_BUTTONS_CLOSE,
+							 _("Can't create tables in database %s"),
+							 database_name);
+			
+			result = gtk_dialog_run (GTK_DIALOG (dialog));
+			gtk_widget_destroy (dialog);
+			retval = FALSE;
+		} else {
+			gchar *query;
+			
+			query = g_strdup_printf ("INSERT INTO property_global (prop_name, value) VALUES ('database_version','%s')", max_version_database); 
+			
+			sql_execute_query (conn, query);
+			g_free (query);
+			g_free (res);
+			retval = TRUE;
+		}
+		g_free (database_file);
+	}
+	
+	g_free (max_version_upgrade);
+	g_free (max_version_database);
+	return retval;
+}
+
+/* Try to create the database */
+static gboolean
+create_database (const gchar   *dsn_name,
+		 const gchar   *db_name,
+		 PlannerPlugin *plugin)
+{
+	GtkWidget         *dialog;
+	GtkWindow         *window;
+	guint              result;
+	gboolean           retval;
+	GdaConnection     *conn;
+	GdaClient         *client;
+	GdaDataSourceInfo *dsn;
+	gchar             *cnc_string_orig;
+	/* FIXME: In postgresql we use template1 as the connection database */
+	gchar             *init_database = "template1";
+	gchar             *query;
+
+	dsn = gda_config_find_data_source (dsn_name);
+	cnc_string_orig = dsn->cnc_string;
+	retval = FALSE;
+
+	window = GTK_WINDOW (plugin->main_window);
+
+	/* Use same data but changing the database */
+	dsn->cnc_string = g_strdup_printf ("DATABASE=%s", init_database); 
+	gda_config_save_data_source_info (dsn);
+
+	client = gda_client_new ();
+	conn = gda_client_open_connection (client, dsn_name, NULL, NULL, 0);
+	if (conn == NULL) {
+		g_warning ("Can't connect to database server in order to check/create the database: %s", cnc_string_orig);
+	} else {
+		dialog = gtk_message_dialog_new (window,
+						 GTK_DIALOG_DESTROY_WITH_PARENT,
+						 GTK_MESSAGE_QUESTION,
+						 GTK_BUTTONS_YES_NO,
+						 _("Database %s is not setup for Planner. "
+						   "Do you want to do that?"),
+						 db_name);
+		
+		result = gtk_dialog_run (GTK_DIALOG (dialog));
+		gtk_widget_destroy (dialog);
+		
+		if (result == GTK_RESPONSE_YES) {
+			query = g_strdup_printf ("CREATE DATABASE %s WITH ENCODING = 'UTF8'", 
+						 db_name); 
+			sql_execute_query (conn, query);
+			g_free (query);
+			/* FIXME: Tables will need the group: dirty relation between 
+			   code and tables definitions in sql file.*/
+			query = g_strdup_printf ("CREATE GROUP planner WITH USER %s", 
+						 gda_connection_get_username (conn));
+			sql_execute_query (conn, query);
+			g_free (query);
+			retval = TRUE;
+		} else {
+			retval = FALSE;
+		}
+		gda_connection_close (conn);
+		g_object_unref (client);
+	}
+	g_free (dsn->cnc_string);
+	dsn->cnc_string = cnc_string_orig;
+	gda_config_save_data_source_info (dsn);
+
+	return retval;
+}
+
+/* Test database status: database exists, correct tables, correct version */
+static GdaConnection *
+sql_get_tested_connection (const gchar   *dsn_name,
+			   const gchar   *db_name,
+			   GdaClient     *client,
+			   PlannerPlugin *plugin) 
+{
+	GdaConnection *conn;
+	gchar         *str;
+
+	conn = gda_client_open_connection (client, dsn_name, NULL, NULL, 0);
+
+	if (conn == NULL) {
+		if (!create_database (dsn_name, db_name, plugin)) {
+			str = g_strdup_printf (_("Connection to database '%s' failed."), 
+					       db_name);
+			show_error_dialog (plugin, str);
+			conn = NULL;
+		} else {
+			conn = gda_client_open_connection (client, dsn_name, NULL, NULL, 0);
+		}
+	}
+
+	if (conn != NULL) {
+		if (!check_database_tables (conn, plugin)) {		
+			str = g_strdup_printf (_("Test to tables in database '%s' failed."), db_name);
+			show_error_dialog (plugin, str);
+			g_free (str);
+			gda_connection_close (conn);
+			conn = NULL;	
+		}
+	}
+
+	/* g_object_unref (client); */
+	return conn;
+}
+
 /**
  * Display a list with projects and let the user select one. Returns the project
  * id of the selected one.
@@ -306,7 +698,6 @@ sql_plugin_retrieve_project_id (PlannerP
 	GdaConnection     *conn;
 	GdaDataModel      *res;
 	GdaClient         *client;
-	gchar             *str;
 	GladeXML          *gui;
 	GtkWidget         *dialog;
 	GtkWidget         *treeview;
@@ -331,13 +722,9 @@ sql_plugin_retrieve_project_id (PlannerP
 	g_free (db_txt);
 
 	client = gda_client_new ();
+	conn = sql_get_tested_connection (dsn_name, database, client, plugin);
 	
-	conn = gda_client_open_connection (client, dsn_name, NULL, NULL, 0);
-
-	if (!GDA_IS_CONNECTION (conn)) {
-		str = g_strdup_printf (_("Connection to database '%s' failed."), database);
-		show_error_dialog (plugin, str);
-		g_free (str);
+	if (conn == NULL) {
 		return -1;
 	}
 
@@ -347,7 +734,6 @@ sql_plugin_retrieve_project_id (PlannerP
 		return -1;
 	}
 	g_object_unref (res);
-	res = NULL;
 
 	res = sql_execute_query (conn,
 				 "DECLARE mycursor CURSOR FOR SELECT proj_id, name,"
@@ -694,6 +1079,8 @@ sql_plugin_save (BonoboUIComponent *comp
 		 gpointer           user_data,
 		 const gchar       *cname)
 {
+	GdaClient     *client;
+	GdaConnection *conn;
 	PlannerPlugin  *plugin = user_data;
 	MrpProject    *project;
 	GObject       *object;
@@ -703,7 +1090,11 @@ sql_plugin_save (BonoboUIComponent *comp
 	gchar         *login = NULL;
 	gchar         *password = NULL;
 	gchar         *uri = NULL;
+	const gchar   *uri_plan = NULL;
 	GError        *error = NULL;
+	gchar         *db_txt;
+	const gchar   *dsn_name = "planner-auto";
+	const gchar   *provider = "PostgreSQL";
 		
 	project = planner_window_get_project (plugin->main_window);
 
@@ -717,19 +1108,56 @@ sql_plugin_save (BonoboUIComponent *comp
 		return;
 	}
 
+	db_txt = g_strdup_printf ("DATABASE=%s",database);
+	gda_config_save_data_source (dsn_name, 
+                                     provider, 
+                                     db_txt,
+                                     "planner project", login, password);
+	g_free (db_txt);
+	client = gda_client_new ();
+	conn = sql_get_tested_connection (dsn_name, database, client, plugin);
+	if (conn == NULL) {
+		g_object_unref (client);
+		return;
+	}
+	gda_connection_close (conn);
+	g_object_unref (client);
+
 	/* This code is prepared for getting support for selecting a project to
 	 * save over. Needs finishing though. Pass project id -1 for now (always
 	 * create a new project).
 	 */
-	uri = create_sql_uri (server, port, database, login, password, -1);
+	uri_plan = mrp_project_get_uri (project);
 
+	/* First time project */
+	if (uri_plan == NULL) {
+		uri = create_sql_uri (server, port, database, login, password, -1);	
 	if (!mrp_project_save_as (project, uri, FALSE, &error)) {
 		show_error_dialog (plugin, error->message);
 		g_clear_error (&error);
 		goto fail;
 	}
+		g_free (uri);
 	
+	} 
+	/* Project was in database */
+	else if (strncmp (uri_plan, "sql://", 6) == 0) {
+		if (!mrp_project_save (project, FALSE, &error)) { 
+			show_error_dialog (plugin, error->message);
+			g_clear_error (&error);
+			goto fail;
+		}
+	} 
+	/* Project wasn't in database */
+	else {
+		uri = create_sql_uri (server, port, database, login, password, -1);	
+		if (!mrp_project_save_as (project, uri, FALSE, &error)) { 
+			show_error_dialog (plugin, error->message);
+			g_clear_error (&error);
+			goto fail;
+		}
 	g_free (uri);
+	}
 		
 	object = G_OBJECT (plugin->main_window);
 	
-- $Id: database-0.11.sql,v 1.1 2004/06/30 10:07:41 alvaro Exp $

-- Planner Database Schema

-- Daniel Lundin <daniel codefactory se>
-- Richard Hult <richard imendio com>
-- Copyright 2003 CodeFactory AB

--
-- Project
--
CREATE TABLE project (
       	proj_id	         serial,
       	name           	 text NOT NULL,
	company		 text,
	manager		 text,
	proj_start	 date NOT NULL DEFAULT CURRENT_TIMESTAMP,
	cal_id	         integer,
	phase	         text,
	default_group_id integer,
	revision         integer,
	last_user        text NOT NULL DEFAULT (user),
	PRIMARY KEY (proj_id)
);
GRANT select,insert,update,delete ON project TO GROUP planner;
GRANT select,update ON project_proj_id_seq TO GROUP planner;

--
-- Phases
--
CREATE TABLE phase (
        phase_id        serial,
        proj_id         integer,
        name            text NOT NULL,
	FOREIGN KEY (proj_id) REFERENCES project (proj_id) 
		ON DELETE CASCADE ON UPDATE CASCADE,
	PRIMARY KEY (phase_id)
);
GRANT select,insert,update,delete ON phase TO GROUP planner;
GRANT select,update ON phase_phase_id_seq TO GROUP planner;

--
-- Day Types
--
CREATE TABLE daytype (
	dtype_id	serial,
       	proj_id		integer,
       	name           	text,
       	descr          	text,
	is_work	        boolean NOT NULL DEFAULT FALSE,
	is_nonwork      boolean NOT NULL DEFAULT FALSE,
	UNIQUE (proj_id, name),
	FOREIGN KEY (proj_id) REFERENCES project (proj_id) 
		ON DELETE CASCADE ON UPDATE CASCADE,
	PRIMARY KEY (dtype_id)
);
GRANT select,insert,update,delete ON daytype TO GROUP planner;
GRANT select,update ON daytype_dtype_id_seq TO GROUP planner;


--
-- Calendar
--
CREATE TABLE calendar (
	cal_id		serial,
       	proj_id		integer,
	parent_cid	integer,
       	name           	text,
	day_mon		integer DEFAULT NULL,
	day_tue		integer DEFAULT NULL,
	day_wed		integer DEFAULT NULL,
	day_thu		integer DEFAULT NULL,
	day_fri		integer DEFAULT NULL,
	day_sat		integer DEFAULT NULL,
	day_sun		integer DEFAULT NULL,
	FOREIGN KEY (proj_id) REFERENCES project (proj_id) 
		ON DELETE CASCADE ON UPDATE CASCADE,
	FOREIGN KEY (day_mon) REFERENCES daytype (dtype_id) 
		ON DELETE SET DEFAULT ON UPDATE CASCADE
		DEFERRABLE INITIALLY DEFERRED,
	FOREIGN KEY (day_tue) REFERENCES daytype (dtype_id) 
		ON DELETE SET DEFAULT ON UPDATE CASCADE
		DEFERRABLE INITIALLY DEFERRED,
	FOREIGN KEY (day_wed) REFERENCES daytype (dtype_id) 
		ON DELETE SET DEFAULT ON UPDATE CASCADE
		DEFERRABLE INITIALLY DEFERRED,
	FOREIGN KEY (day_thu) REFERENCES daytype (dtype_id) 
		ON DELETE SET DEFAULT ON UPDATE CASCADE
		DEFERRABLE INITIALLY DEFERRED,
	FOREIGN KEY (day_fri) REFERENCES daytype (dtype_id) 
		ON DELETE SET DEFAULT ON UPDATE CASCADE
		DEFERRABLE INITIALLY DEFERRED,
	FOREIGN KEY (day_sat) REFERENCES daytype (dtype_id) 
		ON DELETE SET DEFAULT ON UPDATE CASCADE
		DEFERRABLE INITIALLY DEFERRED,
	FOREIGN KEY (day_sun) REFERENCES daytype (dtype_id) 
		ON DELETE SET DEFAULT ON UPDATE CASCADE
		DEFERRABLE INITIALLY DEFERRED,
	FOREIGN KEY (parent_cid) REFERENCES calendar (cal_id) 
		ON DELETE SET NULL ON UPDATE CASCADE,
	PRIMARY KEY (cal_id)
);
GRANT select,insert,update,delete ON calendar TO GROUP planner;
GRANT select,update ON calendar_cal_id_seq TO GROUP planner;
ALTER TABLE project ADD CONSTRAINT project_cal_id 
	FOREIGN KEY (cal_id) REFERENCES calendar (cal_id) 
	ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED;


--
-- Day
--
CREATE TABLE day (
	day_id		serial,
       	cal_id		integer,
	dtype_id	integer,
	date		date,	
	FOREIGN KEY (dtype_id) REFERENCES daytype (dtype_id) 
		ON DELETE CASCADE ON UPDATE CASCADE,
	FOREIGN KEY (cal_id) REFERENCES calendar (cal_id) 
		ON DELETE CASCADE ON UPDATE CASCADE,
	PRIMARY KEY (day_id)
);
GRANT select,insert,update,delete ON day TO GROUP planner;


--
-- Day (working) Interval
--
CREATE TABLE day_interval (
       	cal_id		integer,
	dtype_id	integer,
       	start_time      time with time zone,
       	end_time       	time with time zone,
	FOREIGN KEY (dtype_id) REFERENCES daytype (dtype_id) 
		ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE,
	FOREIGN KEY (cal_id) REFERENCES calendar (cal_id) 
		ON DELETE CASCADE ON UPDATE CASCADE,
	PRIMARY KEY (dtype_id, cal_id, start_time, end_time)
);
GRANT select,insert,update,delete ON day_interval TO GROUP planner;


--
-- Task
--
CREATE TABLE task (
       	task_id           serial,
	parent_id	  integer,
	proj_id	          integer,
       	name              text NOT NULL,
	note		  text,
	start	          timestamp with time zone,
	finish	          timestamp with time zone,
	work	 	  integer DEFAULT 0,
	duration	  integer DEFAULT 0,
	percent_complete  integer DEFAULT 0,
	priority          integer DEFAULT 0,
	is_milestone	  boolean NOT NULL DEFAULT FALSE,
	is_fixed_work     boolean NOT NULL DEFAULT TRUE,
	constraint_type   text NOT NULL DEFAULT 'ASAP',
        constraint_time   timestamp with time zone,
	CHECK (constraint_type = 'ASAP' OR constraint_type = 'MSO' OR constraint_type = 'FNLT' OR constraint_type = 'SNET'),
 	CHECK (percent_complete > -1 AND percent_complete < 101),
	CHECK (priority > -1 AND priority < 10000),
	FOREIGN KEY (proj_id) REFERENCES project (proj_id) 
		ON DELETE CASCADE ON UPDATE CASCADE,
	FOREIGN KEY (parent_id) REFERENCES task (task_id) 
		ON DELETE SET NULL ON UPDATE CASCADE,
	PRIMARY KEY (task_id)
);
GRANT select,insert,update,delete ON task TO GROUP planner;
GRANT select,update ON task_task_id_seq TO GROUP planner;

-- FIXME: Add triggers to handle different types of tasks/milestones


--
-- Predecessor (tasks)
--
CREATE TABLE predecessor (
	task_id	 	 integer NOT NULL,
	pred_task_id	 integer NOT NULL,
       	pred_id          serial,
       	type             text NOT NULL DEFAULT 'FS',
        lag              integer DEFAULT 0,
	CHECK (type = 'FS' OR type = 'FF' OR type = 'SS' OR type = 'SF'),
	UNIQUE (pred_id),
	FOREIGN KEY (task_id) REFERENCES task (task_id) 
		ON DELETE CASCADE ON UPDATE CASCADE,
	FOREIGN KEY (pred_task_id) REFERENCES task (task_id) 
		ON DELETE CASCADE ON UPDATE CASCADE,
	PRIMARY KEY (task_id, pred_task_id)
);
GRANT select,insert,update,delete ON predecessor TO GROUP planner;
GRANT select,update ON predecessor_pred_id_seq TO GROUP planner;


--
-- Property types
--
CREATE TABLE property_type (
       	proptype_id    	  serial,
       	proj_id           integer,
       	name           	  text NOT NULL,
	label		  text NOT NULL,
	type		  text NOT NULL DEFAULT 'text',
	owner		  text NOT NULL DEFAULT 'project',
	descr		  text,
	CHECK (type = 'date' OR type = 'duration' OR type = 'float' 
	       OR type = 'int' OR type = 'text' OR type = 'text-list'
	       OR type = 'cost'),
	CHECK (owner = 'project' OR owner = 'task' OR owner = 'resource'),
	UNIQUE (proj_id, name),
	FOREIGN KEY (proj_id) REFERENCES project (proj_id) 
		ON DELETE CASCADE ON UPDATE CASCADE,
	PRIMARY KEY (proptype_id)
);
GRANT select,insert,update,delete ON property_type TO GROUP planner;
GRANT select,update ON property_type_proptype_id_seq TO GROUP planner;


--
-- Properties
--
CREATE TABLE property (
       	prop_id    	  serial,
	proptype_id	  integer NOT NULL,
	value		  text,
	FOREIGN KEY (proptype_id) REFERENCES property_type (proptype_id) 
		ON DELETE CASCADE ON UPDATE CASCADE,
	PRIMARY KEY (prop_id)
);
GRANT select,insert,update,delete ON property TO GROUP planner;
GRANT select,update ON property_prop_id_seq TO GROUP planner;


--
-- Project properties
--
CREATE TABLE project_to_property (
       	proj_id         integer,
	prop_id	        integer,
	FOREIGN KEY (proj_id) REFERENCES project (proj_id) 
		ON DELETE CASCADE ON UPDATE CASCADE,
	FOREIGN KEY (prop_id) REFERENCES property (prop_id) 
		ON DELETE CASCADE ON UPDATE CASCADE,
	PRIMARY KEY (proj_id, prop_id)
);
GRANT select,insert,update,delete ON project_to_property TO GROUP planner;


--
-- Task properties
--
CREATE TABLE task_to_property (
	prop_id	        integer,
       	task_id         integer,
	FOREIGN KEY (task_id) REFERENCES task (task_id) 
		ON DELETE CASCADE ON UPDATE CASCADE,
	FOREIGN KEY (prop_id) REFERENCES property (prop_id) 
		ON DELETE CASCADE ON UPDATE CASCADE,
	PRIMARY KEY (task_id, prop_id)
);
GRANT select,insert,update,delete ON task_to_property TO GROUP planner;


--
-- Resource Group
--
CREATE TABLE resource_group (
       	group_id         serial,
	proj_id	        integer,
       	name            text NOT NULL,
	admin_name	text,
	admin_phone	text,
	admin_email	text,
	FOREIGN KEY (proj_id) REFERENCES project (proj_id) 
		ON DELETE CASCADE ON UPDATE CASCADE,
	PRIMARY KEY (group_id)
);
GRANT select,insert,update,delete ON resource_group TO GROUP planner;
GRANT select,update ON resource_group_group_id_seq TO GROUP planner;


--
-- Resource
--
CREATE TABLE resource (
       	res_id          serial,
	proj_id	        integer,
	group_id	integer,
       	name            text,
	short_name	text,
	email		text,
	note		text,
	is_worker	boolean NOT NULL DEFAULT TRUE,
	units		real NOT NULL DEFAULT 1.0,
	std_rate	real NOT NULL DEFAULT 0.0,	
	ovt_rate	real NOT NULL DEFAULT 0.0,
	cal_id		integer,
	FOREIGN KEY (proj_id) REFERENCES project (proj_id) 
		ON DELETE CASCADE ON UPDATE CASCADE,
	FOREIGN KEY (group_id) REFERENCES resource_group (group_id) 
		ON DELETE SET NULL ON UPDATE CASCADE,
	FOREIGN KEY (cal_id) REFERENCES calendar (cal_id) 
		ON DELETE CASCADE ON UPDATE CASCADE,
	PRIMARY KEY (res_id)
);
GRANT select,insert,update,delete ON resource TO GROUP planner;
GRANT select,update ON resource_res_id_seq TO GROUP planner;


--
-- Resource properties
--
CREATE TABLE resource_to_property (
	prop_id	        integer,
       	res_id          integer,
	FOREIGN KEY (res_id) REFERENCES resource (res_id) 
		ON DELETE CASCADE ON UPDATE CASCADE,
	FOREIGN KEY (prop_id) REFERENCES property (prop_id) 
		ON DELETE CASCADE ON UPDATE CASCADE,
	PRIMARY KEY (res_id, prop_id)
);
GRANT select,insert,update,delete ON resource_to_property TO GROUP planner;


--
-- Allocations (of resources)
--
CREATE TABLE allocation (
	task_id	        integer,
       	res_id          integer,
	units		real NOT NULL DEFAULT 1.0,
	FOREIGN KEY (res_id) REFERENCES resource (res_id) 
		ON DELETE CASCADE ON UPDATE CASCADE,
	FOREIGN KEY (task_id) REFERENCES task (task_id) 
		ON DELETE CASCADE ON UPDATE CASCADE,
	PRIMARY KEY (res_id, task_id)
);
GRANT select,insert,update,delete ON allocation TO GROUP planner;

--
-- Global planner properties
--
CREATE TABLE property_global (
        prop_id           serial,
        prop_name         text NOT NULL,
        value             text,
        PRIMARY KEY (prop_id)
);

-- Planner Database Schema update
                                                                                                                             
-- Alvaro del Castillo <acs barrapunto com>
-- 
-- New table to store global properties for Planner
-- Actually (0.13) it is used to store the database version

CREATE TABLE property_global (
        prop_id           serial,
        prop_name         text NOT NULL,
        value             text,
        PRIMARY KEY (prop_id)
);

Attachment: signature.asc
Description: Esta parte del mensaje =?ISO-8859-1?Q?est=E1?= firmada digitalmente



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