瀏覽代碼

temporal framework: Enabling mapset specific temporal databases. This feature is very experimental.

git-svn-id: https://svn.osgeo.org/grass/grass/trunk@61984 15284696-431f-4ddb-bdfa-cd5b030d7da7
Soeren Gebbert 10 年之前
父節點
當前提交
4126bb1e2b

+ 13 - 14
lib/python/temporal/abstract_space_time_dataset.py

@@ -350,7 +350,7 @@ class AbstractSpaceTimeDataset(AbstractDataset):
             sql = sql.replace("SPACETIME_REGISTER_TABLE", stds_register_table)
             sql = sql.replace("SPACETIME_REGISTER_TABLE", stds_register_table)
             statement += sql
             statement += sql
 
 
-            if dbif.dbmi.__name__ == "sqlite3":
+            if dbif.get_dbmi().__name__ == "sqlite3":
                 statement += "CREATE INDEX %s_index ON %s (id);"%(stds_register_table, stds_register_table)
                 statement += "CREATE INDEX %s_index ON %s (id);"%(stds_register_table, stds_register_table)
 
 
             # Set the map register table name
             # Set the map register table name
@@ -1454,10 +1454,9 @@ class AbstractSpaceTimeDataset(AbstractDataset):
                 sql += " AND (%s)" % (where.split(";")[0])
                 sql += " AND (%s)" % (where.split(";")[0])
             if order is not None and order != "":
             if order is not None and order != "":
                 sql += " ORDER BY %s" % (order.split(";")[0])
                 sql += " ORDER BY %s" % (order.split(";")[0])
-
             try:
             try:
-                dbif.cursor.execute(sql)
-                rows = dbif.cursor.fetchall()
+                dbif.execute(sql,  mapset=self.base.mapset)
+                rows = dbif.fetchall(mapset=self.base.mapset)
             except:
             except:
                 if connected:
                 if connected:
                     dbif.close()
                     dbif.close()
@@ -1844,7 +1843,7 @@ class AbstractSpaceTimeDataset(AbstractDataset):
 
 
         dbif, connected = init_dbif(dbif)
         dbif, connected = init_dbif(dbif)
 
 
-        if dbif.dbmi.__name__ != "sqlite3":
+        if dbif.get_dbmi().__name__ != "sqlite3":
             self.msgr.fatal(_("Renaming of space time datasets is not supported for PostgreSQL."))
             self.msgr.fatal(_("Renaming of space time datasets is not supported for PostgreSQL."))
 
 
         # SELECT all needed information from the database
         # SELECT all needed information from the database
@@ -1874,7 +1873,7 @@ class AbstractSpaceTimeDataset(AbstractDataset):
                                                              new_map_register_table)
                                                              new_map_register_table)
 
 
         # We need to take care of the stds index in the sqlite3 database
         # We need to take care of the stds index in the sqlite3 database
-        if dbif.dbmi.__name__ == "sqlite3":
+        if dbif.get_dbmi().__name__ == "sqlite3":
             statement += "DROP INDEX %s_index;\n" % (old_map_register_table)
             statement += "DROP INDEX %s_index;\n" % (old_map_register_table)
             statement += "CREATE INDEX %s_index ON %s (id);"%(new_map_register_table,
             statement += "CREATE INDEX %s_index ON %s (id);"%(new_map_register_table,
                                                               new_map_register_table)
                                                               new_map_register_table)
@@ -1970,15 +1969,15 @@ class AbstractSpaceTimeDataset(AbstractDataset):
 
 
         # Check if map is already registered
         # Check if map is already registered
         if stds_register_table is not None:
         if stds_register_table is not None:
-            if dbif.dbmi.paramstyle == "qmark":
+            if dbif.get_dbmi().paramstyle == "qmark":
                 sql = "SELECT id FROM " + \
                 sql = "SELECT id FROM " + \
                     stds_register_table + " WHERE id = (?)"
                     stds_register_table + " WHERE id = (?)"
             else:
             else:
                 sql = "SELECT id FROM " + \
                 sql = "SELECT id FROM " + \
                     stds_register_table + " WHERE id = (%s)"
                     stds_register_table + " WHERE id = (%s)"
             try:
             try:
-                dbif.cursor.execute(sql, (map_id,))
-                row = dbif.cursor.fetchone()
+                dbif.execute(sql, (map_id,),  mapset=self.base.mapset)
+                row = dbif.fetchone(mapset=self.base.mapset)
             except:
             except:
                 self.msgr.warning(_("Error in register table request"))
                 self.msgr.warning(_("Error in register table request"))
                 raise
                 raise
@@ -2115,7 +2114,7 @@ class AbstractSpaceTimeDataset(AbstractDataset):
                                                  dbif=dbif, execute=False)
                                                  dbif=dbif, execute=False)
 
 
         # Now put the raster name in the stds map register table
         # Now put the raster name in the stds map register table
-        if dbif.dbmi.paramstyle == "qmark":
+        if dbif.get_dbmi().paramstyle == "qmark":
             sql = "INSERT INTO " + stds_register_table + \
             sql = "INSERT INTO " + stds_register_table + \
                 " (id) " + "VALUES (?);\n"
                 " (id) " + "VALUES (?);\n"
         else:
         else:
@@ -2185,7 +2184,7 @@ class AbstractSpaceTimeDataset(AbstractDataset):
         # Remove the map from the space time dataset register
         # Remove the map from the space time dataset register
         stds_register_table = self.get_map_register()
         stds_register_table = self.get_map_register()
         if stds_register_table is not None:
         if stds_register_table is not None:
-            if dbif.dbmi.paramstyle == "qmark":
+            if dbif.get_dbmi().paramstyle == "qmark":
                 sql = "DELETE FROM " + stds_register_table + " WHERE id = ?;\n"
                 sql = "DELETE FROM " + stds_register_table + " WHERE id = ?;\n"
             else:
             else:
                 sql = "DELETE FROM " + \
                 sql = "DELETE FROM " + \
@@ -2301,12 +2300,12 @@ class AbstractSpaceTimeDataset(AbstractDataset):
                     None).get_type())
                     None).get_type())
                 sql = sql.replace("SPACETIME_REGISTER_TABLE", stds_register_table)
                 sql = sql.replace("SPACETIME_REGISTER_TABLE", stds_register_table)
 
 
-            dbif.cursor.execute(sql)
-            row = dbif.cursor.fetchone()
+            dbif.execute(sql,  mapset=self.base.mapset)
+            row = dbif.fetchone(mapset=self.base.mapset)
 
 
             if row is not None:
             if row is not None:
                 # This seems to be a bug in sqlite3 Python driver
                 # This seems to be a bug in sqlite3 Python driver
-                if dbif.dbmi.__name__ == "sqlite3":
+                if dbif.get_dbmi().__name__== "sqlite3":
                     tstring = row[0]
                     tstring = row[0]
                     # Convert the unicode string into the datetime format
                     # Convert the unicode string into the datetime format
                     if self.is_time_absolute():
                     if self.is_time_absolute():

+ 28 - 23
lib/python/temporal/base.py

@@ -215,6 +215,7 @@ class SQLDatabaseInterface(DictSQLSerializer):
 
 
         >>> init()
         >>> init()
         >>> t = SQLDatabaseInterface("raster", "soil@PERMANENT")
         >>> t = SQLDatabaseInterface("raster", "soil@PERMANENT")
+        >>> t.mapset = get_current_mapset()
         >>> t.D["name"] = "soil"
         >>> t.D["name"] = "soil"
         >>> t.D["mapset"] = "PERMANENT"
         >>> t.D["mapset"] = "PERMANENT"
         >>> t.D["creator"] = "soeren"
         >>> t.D["creator"] = "soeren"
@@ -255,6 +256,11 @@ class SQLDatabaseInterface(DictSQLSerializer):
         self.ident = ident
         self.ident = ident
         self.msgr = get_tgis_message_interface()
         self.msgr = get_tgis_message_interface()
 
 
+        if self.ident and self.ident.find("@") >= 0:
+            self.mapset = self.ident.split("@""")[1]
+        else:
+            self.mapset = None
+
     def get_table_name(self):
     def get_table_name(self):
         """!Return the name of the table in which the internal
         """!Return the name of the table in which the internal
            data are inserted, updated or selected
            data are inserted, updated or selected
@@ -279,11 +285,11 @@ class SQLDatabaseInterface(DictSQLSerializer):
         #print sql
         #print sql
 
 
         if dbif:
         if dbif:
-            dbif.cursor.execute(sql)
+            dbif.execute(sql,   mapset=self.mapset)
         else:
         else:
             dbif = SQLDatabaseInterfaceConnection()
             dbif = SQLDatabaseInterfaceConnection()
             dbif.connect()
             dbif.connect()
-            dbif.cursor.execute(sql)
+            dbif.execute(sql,   mapset=self.mapset)
             dbif.close()
             dbif.close()
 
 
     def get_is_in_db_statement(self):
     def get_is_in_db_statement(self):
@@ -303,16 +309,15 @@ class SQLDatabaseInterface(DictSQLSerializer):
         """
         """
 
 
         sql = self.get_is_in_db_statement()
         sql = self.get_is_in_db_statement()
-        #print sql
 
 
         if dbif:
         if dbif:
-            dbif.cursor.execute(sql)
-            row = dbif.cursor.fetchone()
+            dbif.execute(sql, mapset=self.mapset)
+            row = dbif.fetchone(mapset=self.mapset)
         else:
         else:
             dbif = SQLDatabaseInterfaceConnection()
             dbif = SQLDatabaseInterfaceConnection()
             dbif.connect()
             dbif.connect()
-            dbif.cursor.execute(sql)
-            row = dbif.cursor.fetchone()
+            dbif.execute(sql, mapset=self.mapset)
+            row = dbif.fetchone(mapset=self.mapset)
             dbif.close()
             dbif.close()
 
 
         # Nothing found
         # Nothing found
@@ -339,7 +344,7 @@ class SQLDatabaseInterface(DictSQLSerializer):
         if not dbif:
         if not dbif:
             dbif = SQLDatabaseInterfaceConnection()
             dbif = SQLDatabaseInterfaceConnection()
 
 
-        return dbif.mogrify_sql_statement(self.get_select_statement())
+        return dbif.mogrify_sql_statement(self.get_select_statement(), mapset=self.mapset)
 
 
     def select(self, dbif=None):
     def select(self, dbif=None):
         """!Select the content from the temporal database and store it
         """!Select the content from the temporal database and store it
@@ -354,18 +359,18 @@ class SQLDatabaseInterface(DictSQLSerializer):
 
 
         if dbif:
         if dbif:
             if len(args) == 0:
             if len(args) == 0:
-                dbif.cursor.execute(sql)
+                dbif.execute(sql,  mapset=self.mapset)
             else:
             else:
-                dbif.cursor.execute(sql, args)
-            row = dbif.cursor.fetchone()
+                dbif.execute(sql, args,  mapset=self.mapset)
+            row = dbif.fetchone(mapset=self.mapset)
         else:
         else:
             dbif = SQLDatabaseInterfaceConnection()
             dbif = SQLDatabaseInterfaceConnection()
             dbif.connect()
             dbif.connect()
             if len(args) == 0:
             if len(args) == 0:
-                dbif.cursor.execute(sql)
+                dbif.execute(sql, mapset=self.mapset)
             else:
             else:
-                dbif.cursor.execute(sql, args)
-            row = dbif.cursor.fetchone()
+                dbif.execute(sql, args, mapset=self.mapset)
+            row = dbif.fetchone(mapset=self.mapset)
             dbif.close()
             dbif.close()
 
 
         # Nothing found
         # Nothing found
@@ -396,7 +401,7 @@ class SQLDatabaseInterface(DictSQLSerializer):
         if not dbif:
         if not dbif:
             dbif = SQLDatabaseInterfaceConnection()
             dbif = SQLDatabaseInterfaceConnection()
 
 
-        return dbif.mogrify_sql_statement(self.get_insert_statement())
+        return dbif.mogrify_sql_statement(self.get_insert_statement(), mapset=self.mapset)
 
 
     def insert(self, dbif=None):
     def insert(self, dbif=None):
         """!Serialize the content of this object and store it in the temporal
         """!Serialize the content of this object and store it in the temporal
@@ -410,11 +415,11 @@ class SQLDatabaseInterface(DictSQLSerializer):
         #print args
         #print args
 
 
         if dbif:
         if dbif:
-            dbif.cursor.execute(sql, args)
+            dbif.execute(sql, args, mapset=self.mapset)
         else:
         else:
             dbif = SQLDatabaseInterfaceConnection()
             dbif = SQLDatabaseInterfaceConnection()
             dbif.connect()
             dbif.connect()
-            dbif.cursor.execute(sql, args)
+            dbif.execute(sql, args, mapset=self.mapset)
             dbif.close()
             dbif.close()
 
 
     def get_update_statement(self, ident=None):
     def get_update_statement(self, ident=None):
@@ -443,7 +448,7 @@ class SQLDatabaseInterface(DictSQLSerializer):
         if not dbif:
         if not dbif:
             dbif = SQLDatabaseInterfaceConnection()
             dbif = SQLDatabaseInterfaceConnection()
 
 
-        return dbif.mogrify_sql_statement(self.get_update_statement(ident))
+        return dbif.mogrify_sql_statement(self.get_update_statement(ident), mapset=self.mapset)
 
 
     def update(self, dbif=None, ident=None):
     def update(self, dbif=None, ident=None):
         """!Serialize the content of this object and update it in the temporal
         """!Serialize the content of this object and update it in the temporal
@@ -463,11 +468,11 @@ class SQLDatabaseInterface(DictSQLSerializer):
         #print args
         #print args
 
 
         if dbif:
         if dbif:
-            dbif.cursor.execute(sql, args)
+            dbif.execute(sql, args, mapset=self.mapset)
         else:
         else:
             dbif = SQLDatabaseInterfaceConnection()
             dbif = SQLDatabaseInterfaceConnection()
             dbif.connect()
             dbif.connect()
-            dbif.cursor.execute(sql, args)
+            dbif.execute(sql, args, mapset=self.mapset)
             dbif.close()
             dbif.close()
 
 
     def get_update_all_statement(self, ident=None):
     def get_update_all_statement(self, ident=None):
@@ -495,7 +500,7 @@ class SQLDatabaseInterface(DictSQLSerializer):
         if not dbif:
         if not dbif:
             dbif = SQLDatabaseInterfaceConnection()
             dbif = SQLDatabaseInterfaceConnection()
 
 
-        return dbif.mogrify_sql_statement(self.get_update_all_statement(ident))
+        return dbif.mogrify_sql_statement(self.get_update_all_statement(ident), mapset=self.mapset)
 
 
     def update_all(self, dbif=None, ident=None):
     def update_all(self, dbif=None, ident=None):
         """!Serialize the content of this object, including None objects,
         """!Serialize the content of this object, including None objects,
@@ -513,11 +518,11 @@ class SQLDatabaseInterface(DictSQLSerializer):
         #print args
         #print args
 
 
         if dbif:
         if dbif:
-            dbif.cursor.execute(sql, args)
+            dbif.execute(sql, args, mapset=self.mapset)
         else:
         else:
             dbif = SQLDatabaseInterfaceConnection()
             dbif = SQLDatabaseInterfaceConnection()
             dbif.connect()
             dbif.connect()
-            dbif.cursor.execute(sql, args)
+            dbif.execute(sql, args, mapset=self.mapset)
             dbif.close()
             dbif.close()
 
 
 ###############################################################################
 ###############################################################################

+ 111 - 18
lib/python/temporal/c_libraries_interface.py

@@ -16,7 +16,7 @@ for details.
 """
 """
 
 
 import sys
 import sys
-from multiprocessing import Process, Lock, Pipe
+from multiprocessing import Process, Lock, Pipe,  Queue
 import logging
 import logging
 from ctypes import *
 from ctypes import *
 from core import *
 from core import *
@@ -27,6 +27,7 @@ import grass.lib.vector as libvector
 import grass.lib.date as libdate
 import grass.lib.date as libdate
 import grass.lib.raster3d as libraster3d
 import grass.lib.raster3d as libraster3d
 import grass.lib.temporal as libtgis
 import grass.lib.temporal as libtgis
+import signal, os
 
 
 ###############################################################################
 ###############################################################################
 
 
@@ -43,6 +44,10 @@ class RPCDefs(object):
     AVAILABLE_MAPSETS = 8
     AVAILABLE_MAPSETS = 8
     GET_DRIVER_NAME = 9
     GET_DRIVER_NAME = 9
     GET_DATABASE_NAME = 10
     GET_DATABASE_NAME = 10
+    G_MAPSET = 11
+    G_LOCATION = 12
+    G_GISDBASE = 13
+    G_FATAL_ERROR = 14
 
 
     TYPE_RASTER=0
     TYPE_RASTER=0
     TYPE_RASTER3D=1
     TYPE_RASTER3D=1
@@ -50,6 +55,46 @@ class RPCDefs(object):
 
 
 ###############################################################################
 ###############################################################################
 
 
+def _fatal_error(lock, conn, data):
+    """Calls G_fatal_error()"""
+    libgis.G_fatal_error("Fatal Error in C library server")
+
+def _get_mapset(lock, conn, data):
+    """Return the current mapset
+    
+       :param lock: A multiprocessing.Lock instance
+       :param conn: A multiprocessing.Pipe instance used to send True or False
+       :param data: The mapset as list entry 1 [function_id]
+       
+       :returns: Name of the current mapset
+    """    
+    mapset = libgis.G_mapset()
+    conn.send(mapset) 
+    
+def _get_location(lock, conn, data):
+    """Return the current location
+    
+       :param lock: A multiprocessing.Lock instance
+       :param conn: A multiprocessing.Pipe instance used to send True or False
+       :param data: The mapset as list entry 1 [function_id]
+       
+       :returns: Name of the location
+    """    
+    location = libgis.G_location()
+    conn.send(location) 
+   
+def _get_gisdbase(lock, conn, data):
+    """Return the current gisdatabase
+    
+       :param lock: A multiprocessing.Lock instance
+       :param conn: A multiprocessing.Pipe instance used to send True or False
+       :param data: The mapset as list entry 1 [function_id]
+       
+       :returns: Name of the gisdatabase
+    """    
+    gisdbase = libgis.G_gisdbase()
+    conn.send(gisdbase) 
+
 def _get_driver_name(lock, conn, data):
 def _get_driver_name(lock, conn, data):
     """Return the temporal database driver of a specific mapset
     """Return the temporal database driver of a specific mapset
     
     
@@ -59,9 +104,11 @@ def _get_driver_name(lock, conn, data):
        
        
        :returns: Name of the driver or None if no temporal database present
        :returns: Name of the driver or None if no temporal database present
     """
     """
+    mapset = data[1]
+    if not mapset:
+        mapset = libgis.G_mapset()
     
     
-    drstring = libtgis.tgis_get_mapset_driver_name(data[1])
-    
+    drstring = libtgis.tgis_get_mapset_driver_name(mapset)
     conn.send(drstring) 
     conn.send(drstring) 
 
 
 ###############################################################################
 ###############################################################################
@@ -75,13 +122,17 @@ def _get_database_name(lock, conn, data):
        
        
        :returns: Name of the database or None if no temporal database present
        :returns: Name of the database or None if no temporal database present
     """
     """
-    dbstring = libtgis.tgis_get_mapset_database_name(data[1])
+    mapset = data[1]
+    if not mapset:
+        mapset = libgis.G_mapset()
+    dbstring = libtgis.tgis_get_mapset_database_name(mapset)
+
     if dbstring:
     if dbstring:
         # We substitute GRASS variables if they are located in the database string
         # We substitute GRASS variables if they are located in the database string
         # This behavior is in conjunction with db.connect
         # This behavior is in conjunction with db.connect
-        dbstring = dbstring.replace("$GISDBASE", corefunc.current_gisdbase)
-        dbstring = dbstring.replace("$LOCATION_NAME", corefunc.current_location)
-        dbstring = dbstring.replace("$MAPSET", corefunc.current_mapset)
+        dbstring = dbstring.replace("$GISDBASE", libgis.G_gisdbase())
+        dbstring = dbstring.replace("$LOCATION_NAME", libgis.G_location())
+        dbstring = dbstring.replace("$MAPSET", libgis.G_mapset())
     conn.send(dbstring) 
     conn.send(dbstring) 
 
 
 ###############################################################################
 ###############################################################################
@@ -649,12 +700,15 @@ def _convert_timestamp_from_grass(ts):
 ###############################################################################
 ###############################################################################
 
 
 def _stop(lock, conn, data):
 def _stop(lock, conn, data):
+    libgis.G_debug(1, "Stop C-interface server")
     conn.close()
     conn.close()
     lock.release()
     lock.release()
-    libgis.G_debug(1, "Stop C-interface server")
     sys.exit()
     sys.exit()
 
 
 ###############################################################################
 ###############################################################################
+# Global server connection
+server_connection = None
+server_lock = None
 
 
 def c_library_server(lock, conn):
 def c_library_server(lock, conn):
     """The GRASS C-libraries server function designed to be a target for
     """The GRASS C-libraries server function designed to be a target for
@@ -662,9 +716,9 @@ def c_library_server(lock, conn):
 
 
        :param lock: A multiprocessing.Lock
        :param lock: A multiprocessing.Lock
        :param conn: A multiprocessing.Pipe
        :param conn: A multiprocessing.Pipe
-    """
+    """   
     # Crerate the function array
     # Crerate the function array
-    functions = [0]*11
+    functions = [0]*15
     functions[RPCDefs.STOP] = _stop
     functions[RPCDefs.STOP] = _stop
     functions[RPCDefs.HAS_TIMESTAMP] = _has_timestamp
     functions[RPCDefs.HAS_TIMESTAMP] = _has_timestamp
     functions[RPCDefs.WRITE_TIMESTAMP] = _write_timestamp
     functions[RPCDefs.WRITE_TIMESTAMP] = _write_timestamp
@@ -675,6 +729,10 @@ def c_library_server(lock, conn):
     functions[RPCDefs.AVAILABLE_MAPSETS] = _available_mapsets
     functions[RPCDefs.AVAILABLE_MAPSETS] = _available_mapsets
     functions[RPCDefs.GET_DRIVER_NAME] = _get_driver_name
     functions[RPCDefs.GET_DRIVER_NAME] = _get_driver_name
     functions[RPCDefs.GET_DATABASE_NAME] = _get_database_name
     functions[RPCDefs.GET_DATABASE_NAME] = _get_database_name
+    functions[RPCDefs.G_MAPSET] = _get_mapset
+    functions[RPCDefs.G_LOCATION] = _get_location
+    functions[RPCDefs.G_GISDBASE] = _get_gisdbase
+    functions[RPCDefs.G_FATAL_ERROR] = _fatal_error
 
 
     libgis.G_gisinit("c_library_server")
     libgis.G_gisinit("c_library_server")
     libgis.G_debug(1, "Start C-interface server")
     libgis.G_debug(1, "Start C-interface server")
@@ -807,6 +865,10 @@ class CLibrariesInterface(object):
        'sqlite'
        'sqlite'
        >>> ciface.get_database_name().split("/")[-1]
        >>> ciface.get_database_name().split("/")[-1]
        'sqlite.db'
        'sqlite.db'
+       
+       >>> mapset = ciface.get_mapset()
+       >>> location = ciface.get_location()
+       >>> gisdbase = ciface.get_gisdbase()
 
 
        >>> gscript.del_temp_region()
        >>> gscript.del_temp_region()
 
 
@@ -815,11 +877,12 @@ class CLibrariesInterface(object):
     def __init__(self):
     def __init__(self):
         self.client_conn = None
         self.client_conn = None
         self.server_conn = None
         self.server_conn = None
+        self.queue = None
         self.server = None
         self.server = None
         self.start_server()
         self.start_server()
 
 
     def start_server(self):
     def start_server(self):
-        self.client_conn, self.server_conn = Pipe()
+        self.client_conn, self.server_conn = Pipe(True)
         self.lock = Lock()
         self.lock = Lock()
         self.server = Process(target=c_library_server, args=(self.lock,
         self.server = Process(target=c_library_server, args=(self.lock,
                                                           self.server_conn))
                                                           self.server_conn))
@@ -1135,9 +1198,6 @@ class CLibrariesInterface(object):
            
            
            :returns: Name of the driver or None if no temporal database present
            :returns: Name of the driver or None if no temporal database present
         """
         """
-        if mapset is None or mapset is "":
-            mapset = corefunc.get_current_mapset()
-
         self._check_restart_server()
         self._check_restart_server()
         self.client_conn.send([RPCDefs.GET_DRIVER_NAME, mapset])
         self.client_conn.send([RPCDefs.GET_DRIVER_NAME, mapset])
         return self.client_conn.recv()
         return self.client_conn.recv()
@@ -1149,14 +1209,47 @@ class CLibrariesInterface(object):
            
            
            :returns: Name of the database or None if no temporal database present
            :returns: Name of the database or None if no temporal database present
         """
         """
-        
-        if mapset is None or mapset is "":
-            mapset = corefunc.get_current_mapset()
-        
         self._check_restart_server()
         self._check_restart_server()
         self.client_conn.send([RPCDefs.GET_DATABASE_NAME, mapset])
         self.client_conn.send([RPCDefs.GET_DATABASE_NAME, mapset])
         return self.client_conn.recv()
         return self.client_conn.recv()
     
     
+    def get_mapset(self):
+        """Return the current mapset
+                   
+           :returns: Name of the current mapset
+        """
+        self._check_restart_server()
+        self.client_conn.send([RPCDefs.G_MAPSET,])
+        return self.client_conn.recv()
+        
+    def get_location(self):
+        """Return the location
+                   
+           :returns: Name of the location
+        """
+        self._check_restart_server()
+        self.client_conn.send([RPCDefs.G_LOCATION,])
+        return self.client_conn.recv()
+        
+    def get_gisdbase(self):
+        """Return the gisdatabase
+                   
+           :returns: Name of the gisdatabase
+        """
+        self._check_restart_server()
+        self.client_conn.send([RPCDefs.G_GISDBASE,])
+        return self.client_conn.recv()
+        
+    def fatal_error(self, mapset=None):
+        """Return the temporal database name of a specific mapset
+        
+           :param mapset: Name of the mapset
+           
+           :returns: Name of the database or None if no temporal database present
+        """
+        self._check_restart_server()
+        self.client_conn.send([RPCDefs.G_FATAL_ERROR])
+
     def stop(self):
     def stop(self):
         """Stop the messenger server and close the pipe
         """Stop the messenger server and close the pipe
         
         

+ 191 - 50
lib/python/temporal/core.py

@@ -351,8 +351,8 @@ def get_tgis_metadata(dbif=None):
     # Select metadata if the table is present
     # Select metadata if the table is present
     try:
     try:
         statement = "SELECT * FROM tgis_metadata;\n"
         statement = "SELECT * FROM tgis_metadata;\n"
-        dbif.cursor.execute(statement)
-        rows = dbif.cursor.fetchall()
+        dbif.execute(statement)
+        rows = dbif.fetchall()
     except:
     except:
         rows = None
         rows = None
 
 
@@ -556,8 +556,8 @@ def init(raise_fatal_error=False):
         if os.path.exists(tgis_database_string):
         if os.path.exists(tgis_database_string):
             dbif.connect()
             dbif.connect()
             # Check for raster_base table
             # Check for raster_base table
-            dbif.cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='raster_base';")
-            name = dbif.cursor.fetchone()
+            dbif.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='raster_base';")
+            name = dbif.fetchone()
             if name and name[0] == "raster_base":
             if name and name[0] == "raster_base":
                 db_exists = True
                 db_exists = True
             dbif.close()
             dbif.close()
@@ -565,9 +565,9 @@ def init(raise_fatal_error=False):
         # Connect to database
         # Connect to database
         dbif.connect()
         dbif.connect()
         # Check for raster_base table
         # Check for raster_base table
-        dbif.cursor.execute("SELECT EXISTS(SELECT * FROM information_schema.tables "
+        dbif.execute("SELECT EXISTS(SELECT * FROM information_schema.tables "
                    "WHERE table_name=%s)", ('raster_base',))
                    "WHERE table_name=%s)", ('raster_base',))
-        if dbif.cursor.fetchone()[0]:
+        if dbif.fetchone()[0]:
             db_exists = True
             db_exists = True
 
 
     backup_howto = "The format of your actual temporal database is not supported any more.\n"\
     backup_howto = "The format of your actual temporal database is not supported any more.\n"\
@@ -611,7 +611,7 @@ def init(raise_fatal_error=False):
 def get_database_info_string():
 def get_database_info_string():
     dbif = SQLDatabaseInterfaceConnection()
     dbif = SQLDatabaseInterfaceConnection()
 
 
-    info  = "\nDBMI interface:..... " + str(dbif.dbmi.__name__)
+    info  = "\nDBMI interface:..... " + str(dbif.get_dbmi().__name__)
     info += "\nTemporal database:.. " + str( get_tgis_database_string())
     info += "\nTemporal database:.. " + str( get_tgis_database_string())
     return info
     return info
 
 
@@ -775,7 +775,138 @@ def _create_tgis_metadata_table(content, dbif=None):
 
 
 ###############################################################################
 ###############################################################################
 
 
-class SQLDatabaseInterfaceConnection():
+class SQLDatabaseInterfaceConnection(object):
+    def __init__(self):
+        self.tgis_mapsets = get_available_temporal_mapsets()
+        self.current_mapset = get_current_mapset()
+        self.connections = {}
+        self.connected = False
+        
+        self.unique_connections = {}
+        
+        for mapset in self.tgis_mapsets.keys():
+            driver,  dbstring = self.tgis_mapsets[mapset]
+            
+            if dbstring not in self.unique_connections.keys():
+                self.unique_connections[dbstring] = DBConnection(driver)
+            
+            self.connections[mapset] = self.unique_connections[dbstring]
+
+    def get_dbmi(self,  mapset=None):
+        if mapset is None:
+            mapset = self.current_mapset
+        return self.connections[mapset].dbmi
+
+    def rollback(self,  mapset=None):
+        """
+            Roll back the last transaction. This must be called
+            in case a new query should be performed after a db error.
+
+            This is only relevant for postgresql database.
+        """
+        if mapset is None:
+            mapset = self.current_mapset
+
+    def connect(self):
+        """Connect to the DBMI to execute SQL statements
+
+           Supported backends are sqlite3 and postgresql
+        """
+        for mapset in self.tgis_mapsets.keys():
+            driver,  dbstring = self.tgis_mapsets[mapset]
+            conn = self.connections[mapset]
+            if conn.is_connected() is False:
+                conn .connect(dbstring)
+                
+        self.connected = True
+        
+    def is_connected(self):
+        return self.connected
+
+    def close(self):
+        """Close the DBMI connection
+
+           There may be several temporal databases in a location, hence 
+           close all temporal databases that have been opened. 
+        """
+        for key in self.unique_connections.keys():
+            self.unique_connections[key] .close()
+        
+        self.connected = False
+
+    def mogrify_sql_statement(self, content, mapset=None):
+        """Return the SQL statement and arguments as executable SQL string
+
+           :param content: The content as tuple with two entries, the first
+                           entry is the SQL statement with DBMI specific
+                           place holder (?), the second entry is the argument
+                           list that should substitute the place holder.
+           :param mapset: The mapset of the abstract dataset or temporal
+                          database location, if None the current mapset 
+                          will be used
+        """
+        if mapset is None:
+            mapset = self.current_mapset
+
+        return self.connections[mapset].mogrify_sql_statement(content)
+
+    def check_table(self, table_name, mapset=None):
+        """Check if a table exists in the temporal database
+
+           :param table_name: The name of the table to be checked for existence
+           :param mapset: The mapset of the abstract dataset or temporal
+                          database location, if None the current mapset 
+                          will be used
+           :returns: True if the table exists, False otherwise
+           
+           TODO:
+           There may be several temporal databases in a location, hence 
+           the mapset is used to query the correct temporal database.
+        """
+        if mapset is None:
+            mapset = self.current_mapset
+
+        return self.connections[mapset].check_table(table_name)
+
+    def execute(self,  statement,  args=None,  mapset=None):
+        """""
+           :param mapset: The mapset of the abstract dataset or temporal
+                          database location, if None the current mapset 
+                          will be used
+        """
+        if mapset is None:
+            mapset = self.current_mapset
+
+        return self.connections[mapset].execute(statement,  args)
+
+    def fetchone(self,  mapset=None):
+        if mapset is None:
+            mapset = self.current_mapset
+
+        return self.connections[mapset].fetchone()
+
+    def fetchall(self,  mapset=None):
+        if mapset is None:
+            mapset = self.current_mapset
+
+        return self.connections[mapset].fetchall()
+
+    def execute_transaction(self, statement, mapset=None):
+        """Execute a transactional SQL statement
+
+           The BEGIN and END TRANSACTION statements will be added automatically
+           to the sql statement
+
+           :param statement: The executable SQL statement or SQL script
+        """
+        if mapset is None:
+            mapset = self.current_mapset
+
+        return self.connections[mapset].execute_transaction(statement)
+ 
+###############################################################################
+
+class DBConnection(object):
     """This class represents the database interface connection
     """This class represents the database interface connection
        and provides access to the chisen backend modules.
        and provides access to the chisen backend modules.
 
 
@@ -784,31 +915,29 @@ class SQLDatabaseInterfaceConnection():
          - postgresql via psycopg2
          - postgresql via psycopg2
 
 
     """
     """
-    def __init__(self):
-        """TODO:
-           Create a list of all accessible mapsets that have temporal database definitions
-           Create a database connection for each mapset, reuse existing database connections
-           in case the connection specifications are identical (a single temporal database).
-           
-           Database string and river are mapset specific, hence the driver may change with the mapset.
-        """
+    def __init__(self ,  backend=None):
         self.connected = False
         self.connected = False
-        global tgis_backend
-        if tgis_backend == "sqlite":
-            self.dbmi = sqlite3
+        if backend is None:
+            global tgis_backend
+            if tgis_backend == "sqlite":
+                self.dbmi = sqlite3
+            else:
+                self.dbmi = psycopg2
         else:
         else:
-            self.dbmi = psycopg2
+            if backend == "sqlite":
+                self.dbmi = sqlite3
+            else:
+                self.dbmi = psycopg2
 
 
         self.msgr = get_tgis_message_interface()
         self.msgr = get_tgis_message_interface()
         self.msgr.debug(1, "SQLDatabaseInterfaceConnection constructor")
         self.msgr.debug(1, "SQLDatabaseInterfaceConnection constructor")
 
 
     def __del__(self):
     def __del__(self):
-        """TODO:
-           Close all mapset specific connections, be aware that different 
-           mapsets may have identical connections.
-        """
         if self.connected is True:
         if self.connected is True:
             self.close()
             self.close()
+            
+    def is_connected(self):
+        return self.connected
 
 
     def rollback(self):
     def rollback(self):
         """
         """
@@ -821,22 +950,19 @@ class SQLDatabaseInterfaceConnection():
             if self.connected:
             if self.connected:
                 self.connection.rollback()
                 self.connection.rollback()
 
 
-    def connect(self):
+    def connect(self,  dbstring=None):
         """Connect to the DBMI to execute SQL statements
         """Connect to the DBMI to execute SQL statements
 
 
            Supported backends are sqlite3 and postgresql
            Supported backends are sqlite3 and postgresql
-           
-           TODO:
-           Create connections for each mapset that has a temporal database.
-           Open existing connections only once.
-           The dbmi, connection and cursor are mapset specific 
-           and must be managed in a dict.
         """
         """
-        global tgis_database_string
+        # Connection in the current mapset
+        if dbstring is None:
+            global tgis_database_string
+            dbstring = tgis_database_string
 
 
         try:
         try:
             if self.dbmi.__name__ == "sqlite3":
             if self.dbmi.__name__ == "sqlite3":
-                self.connection = self.dbmi.connect(tgis_database_string,
+                self.connection = self.dbmi.connect(dbstring,
                         detect_types = self.dbmi.PARSE_DECLTYPES | self.dbmi.PARSE_COLNAMES)
                         detect_types = self.dbmi.PARSE_DECLTYPES | self.dbmi.PARSE_COLNAMES)
                 self.connection.row_factory = self.dbmi.Row
                 self.connection.row_factory = self.dbmi.Row
                 self.connection.isolation_level = None
                 self.connection.isolation_level = None
@@ -844,7 +970,7 @@ class SQLDatabaseInterfaceConnection():
                 self.cursor.execute("PRAGMA synchronous = OFF")
                 self.cursor.execute("PRAGMA synchronous = OFF")
                 self.cursor.execute("PRAGMA journal_mode = MEMORY")
                 self.cursor.execute("PRAGMA journal_mode = MEMORY")
             elif self.dbmi.__name__ == "psycopg2":
             elif self.dbmi.__name__ == "psycopg2":
-                self.connection = self.dbmi.connect(tgis_database_string)
+                self.connection = self.dbmi.connect(dbstring)
                 #self.connection.set_isolation_level(dbmi.extensions.ISOLATION_LEVEL_AUTOCOMMIT)
                 #self.connection.set_isolation_level(dbmi.extensions.ISOLATION_LEVEL_AUTOCOMMIT)
                 self.cursor = self.connection.cursor(
                 self.cursor = self.connection.cursor(
                     cursor_factory = self.dbmi.extras.DictCursor)
                     cursor_factory = self.dbmi.extras.DictCursor)
@@ -868,7 +994,7 @@ class SQLDatabaseInterfaceConnection():
         self.cursor.close()
         self.cursor.close()
         self.connected = False
         self.connected = False
 
 
-    def mogrify_sql_statement(self, content, mapset=None):
+    def mogrify_sql_statement(self, content):
         """Return the SQL statement and arguments as executable SQL string
         """Return the SQL statement and arguments as executable SQL string
         
         
            TODO:
            TODO:
@@ -949,7 +1075,7 @@ class SQLDatabaseInterfaceConnection():
 
 
                 return statement
                 return statement
 
 
-    def check_table(self, table_name, mapset=None):
+    def check_table(self, table_name):
         """Check if a table exists in the temporal database
         """Check if a table exists in the temporal database
 
 
            :param table_name: The name of the table to be checked for existence
            :param table_name: The name of the table to be checked for existence
@@ -987,20 +1113,38 @@ class SQLDatabaseInterfaceConnection():
 
 
         return table_exists
         return table_exists
     
     
-    def execute(self, statement, mapset=None):
+    def execute(self, statement,  args=None):
         """Execute a SQL statement
         """Execute a SQL statement
 
 
            :param statement: The executable SQL statement or SQL script
            :param statement: The executable SQL statement or SQL script
-           :param mapset: The mapset of the abstract dataset or temporal
-                          database location, if None the current mapset 
-                          will be used
-           
-           NOTE: not implemented, the purpose of this function is to
-                 replace all dbif.cursor.execute() calls in the temporal
-                 framework to allow SQL statement execution with distributed
-                 temporal databases identified by their mapset name
         """
         """
-        pass
+        connected = False
+        if not self.connected:
+            self.connect()
+            connected = True
+        try:
+            if args:
+                self.cursor.execute(statement,  args)
+            else:
+                self.cursor.execute(statement)
+        except:
+            if connected:
+                self.close()
+            self.msgr.error(_("Unable to execute :\n %(sql)s" % {"sql":statement}))
+            raise
+
+        if connected:
+            self.close()
+        
+    def fetchone(self):
+        if self.connected:
+            return self.cursor.fetchone()
+        return None
+
+    def fetchall(self):
+        if self.connected:
+            return self.cursor.fetchall()
+        return None
 
 
     def execute_transaction(self, statement, mapset=None):
     def execute_transaction(self, statement, mapset=None):
         """Execute a transactional SQL statement
         """Execute a transactional SQL statement
@@ -1009,9 +1153,6 @@ class SQLDatabaseInterfaceConnection():
            to the sql statement
            to the sql statement
 
 
            :param statement: The executable SQL statement or SQL script
            :param statement: The executable SQL statement or SQL script
-           :param mapset: The mapset of the abstract dataset or temporal
-                          database location, if None the current mapset 
-                          will be used
         """
         """
         connected = False
         connected = False
         if not self.connected:
         if not self.connected:
@@ -1064,7 +1205,7 @@ def init_dbif(dbif):
         dbif = SQLDatabaseInterfaceConnection()
         dbif = SQLDatabaseInterfaceConnection()
         dbif.connect()
         dbif.connect()
         return dbif, True
         return dbif, True
-    elif dbif.connected is False:
+    elif dbif.is_connected() is False:
         dbif.connect()
         dbif.connect()
         return dbif, True
         return dbif, True
 
 

+ 21 - 17
lib/python/temporal/gui_support.py

@@ -89,27 +89,31 @@ def tlist(type, dbif=None):
     id = None
     id = None
     sp = dataset_factory(type, id)
     sp = dataset_factory(type, id)
     dbif, connected = init_dbif(dbif)
     dbif, connected = init_dbif(dbif)
+    
+    mapsets = get_available_temporal_mapsets()
 
 
     output = []
     output = []
     temporal_type = ["absolute", 'relative']
     temporal_type = ["absolute", 'relative']
     for type in temporal_type:
     for type in temporal_type:
-        # Table name
-        if type == "absolute":
-            table = sp.get_type() + "_view_abs_time"
-        else:
-            table = sp.get_type() + "_view_rel_time"
-
-        # Create the sql selection statement
-        sql = "SELECT id FROM " + table
-        sql += " ORDER BY id"
-
-        dbif.cursor.execute(sql)
-        rows = dbif.cursor.fetchall()
-
-        # Append the ids of the space time datasets
-        for row in rows:
-            for col in row:
-                output.append(str(col))
+        # For each available mapset
+        for mapset in mapsets.keys():
+            # Table name
+            if type == "absolute":
+                table = sp.get_type() + "_view_abs_time"
+            else:
+                table = sp.get_type() + "_view_rel_time"
+
+            # Create the sql selection statement
+            sql = "SELECT id FROM " + table
+            sql += " ORDER BY id"
+
+            dbif.execute(sql,  mapset=mapset)
+            rows = dbif.fetchall(mapset=mapset)
+
+            # Append the ids of the space time datasets
+            for row in rows:
+                for col in row:
+                    output.append(str(col))
 
 
     if connected is True:
     if connected is True:
         dbif.close()
         dbif.close()

+ 4 - 4
lib/python/temporal/list_stds.py

@@ -70,11 +70,11 @@ def get_dataset_list(type,  temporal_type,  columns=None,  where=None,  order=No
     dbif = SQLDatabaseInterfaceConnection()
     dbif = SQLDatabaseInterfaceConnection()
     dbif.connect()
     dbif.connect()
     
     
-    mapsets = get_tgis_c_library_interface().available_mapsets()
+    mapsets = get_available_temporal_mapsets()
     
     
     result = {}
     result = {}
     
     
-    for mapset in mapsets:
+    for mapset in mapsets.keys():
         
         
         if temporal_type == "absolute":
         if temporal_type == "absolute":
             table = sp.get_type() + "_view_abs_time"
             table = sp.get_type() + "_view_abs_time"
@@ -95,8 +95,8 @@ def get_dataset_list(type,  temporal_type,  columns=None,  where=None,  order=No
         if order:
         if order:
             sql += " ORDER BY " + order
             sql += " ORDER BY " + order
 
 
-        dbif.cursor.execute(sql)
-        rows = dbif.cursor.fetchall()
+        dbif.execute(sql,  mapset=mapset)
+        rows = dbif.fetchall(mapset=mapset)
         
         
         if rows:
         if rows:
             result[mapset] = rows
             result[mapset] = rows

+ 1 - 1
lib/python/temporal/register.py

@@ -290,7 +290,7 @@ def register_maps_in_space_time_dataset(
 
 
         # Sqlite3 performance is better for huge datasets when committing in
         # Sqlite3 performance is better for huge datasets when committing in
         # small chunks
         # small chunks
-        if dbif.dbmi.__name__ == "sqlite3":
+        if dbif.get_dbmi().__name__ == "sqlite3":
             if count % 100 == 0:
             if count % 100 == 0:
                 if statement is not None and statement != "":
                 if statement is not None and statement != "":
                     dbif.execute_transaction(statement)
                     dbif.execute_transaction(statement)

+ 2 - 0
lib/python/temporal/testsuite/test_register_function.py

@@ -12,6 +12,7 @@ for details.
 import grass.temporal as tgis
 import grass.temporal as tgis
 import grass.gunittest
 import grass.gunittest
 import datetime
 import datetime
+import os
 
 
 
 
 class TestRegisterFunctions(grass.gunittest.TestCase):
 class TestRegisterFunctions(grass.gunittest.TestCase):
@@ -20,6 +21,7 @@ class TestRegisterFunctions(grass.gunittest.TestCase):
     def setUpClass(cls):
     def setUpClass(cls):
         """!Initiate the temporal GIS and set the region
         """!Initiate the temporal GIS and set the region
         """
         """
+        os.putenv("GRASS_OVERWRITE", "1")
         # Use always the current mapset as temporal database
         # Use always the current mapset as temporal database
         cls.runModule("g.gisenv", set="TGIS_USE_CURRENT_MAPSET=1")
         cls.runModule("g.gisenv", set="TGIS_USE_CURRENT_MAPSET=1")
         tgis.init()
         tgis.init()

+ 2 - 0
lib/python/temporal/testsuite/test_temporal_raster3d_algebra.py

@@ -13,6 +13,7 @@ import grass.script
 import grass.temporal as tgis
 import grass.temporal as tgis
 import grass.gunittest as gunittest
 import grass.gunittest as gunittest
 import datetime
 import datetime
+import os
 
 
 
 
 class TestRegisterFunctions(gunittest.TestCase):
 class TestRegisterFunctions(gunittest.TestCase):
@@ -21,6 +22,7 @@ class TestRegisterFunctions(gunittest.TestCase):
     def setUpClass(cls):
     def setUpClass(cls):
         """!Initiate the temporal GIS and set the region
         """!Initiate the temporal GIS and set the region
         """
         """
+        os.putenv("GRASS_OVERWRITE", "1")
         tgis.init(True) # Raise on error instead of exit(1)
         tgis.init(True) # Raise on error instead of exit(1)
         grass.script.use_temp_region()
         grass.script.use_temp_region()
         ret = grass.script.run_command("g.region", n=80.0, s=0.0, e=120.0,
         ret = grass.script.run_command("g.region", n=80.0, s=0.0, e=120.0,

+ 2 - 0
lib/python/temporal/testsuite/test_temporal_raster_algebra.py

@@ -13,6 +13,7 @@ import grass.script
 import grass.temporal as tgis
 import grass.temporal as tgis
 import grass.gunittest as gunittest
 import grass.gunittest as gunittest
 import datetime
 import datetime
+import os
 
 
 
 
 class TestRegisterFunctions(gunittest.TestCase):
 class TestRegisterFunctions(gunittest.TestCase):
@@ -21,6 +22,7 @@ class TestRegisterFunctions(gunittest.TestCase):
     def setUpClass(cls):
     def setUpClass(cls):
         """!Initiate the temporal GIS and set the region
         """!Initiate the temporal GIS and set the region
         """
         """
+        os.putenv("GRASS_OVERWRITE", "1")
         tgis.init(True) # Raise on error instead of exit(1)
         tgis.init(True) # Raise on error instead of exit(1)
         grass.script.use_temp_region()
         grass.script.use_temp_region()
         ret = grass.script.run_command("g.region", n=80.0, s=0.0, e=120.0,
         ret = grass.script.run_command("g.region", n=80.0, s=0.0, e=120.0,