core.py 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719
  1. """!@package grass.temporal
  2. @brief GRASS Python scripting module (temporal GIS functions)
  3. Temporal GIS core functions to be used in library modules and scripts.
  4. This module provides the functionality to create the temporal
  5. SQL database and to establish a connection to the database.
  6. Usage:
  7. @code
  8. >>> import grass.temporal as tgis
  9. >>> # Create the temporal database
  10. >>> tgis.init()
  11. >>> # Establish a database connection
  12. >>> dbif, connected = tgis.init_dbif(None)
  13. >>> dbif.connect()
  14. >>> # Execute a SQL statement
  15. >>> dbif.execute_transaction("SELECT datetime(0, 'unixepoch', 'localtime');")
  16. >>> # Mogrify an SQL statement
  17. >>> dbif.mogrify_sql_statement(["SELECT name from raster_base where name = ?",
  18. ... ("precipitation",)])
  19. "SELECT name from raster_base where name = 'precipitation'"
  20. >>> dbif.close()
  21. @endcode
  22. (C) 2008-2011 by the GRASS Development Team
  23. This program is free software under the GNU General Public
  24. License (>=v2). Read the file COPYING that comes with GRASS
  25. for details.
  26. @author Soeren Gebbert
  27. """
  28. import os
  29. import grass.script.core as core
  30. from datetime import datetime
  31. # Import all supported database backends
  32. # Ignore import errors since they are checked later
  33. try:
  34. import sqlite3
  35. except ImportError:
  36. pass
  37. # Postgresql is optional, existence is checked when needed
  38. try:
  39. import psycopg2
  40. import psycopg2.extras
  41. except:
  42. pass
  43. # Uncomment this to raise and exception in case of a fatal error
  44. # core.set_raise_on_error(True)
  45. # Global variable that defines the backend
  46. # of the temporal GIS
  47. # It can either be "sqlite" or "pg"
  48. tgis_backend = None
  49. # The version of the temporal framework
  50. # this value must be an integer larger than 0
  51. # Increase this value in case of backward incompatible changes in the TGIS API
  52. tgis_version=1
  53. # The version of the temporal database since framework and database version can differ
  54. # this value must be an integer larger than 0
  55. # Increase this value in case of backward incompatible changes
  56. # temporal database SQL layout
  57. tgis_db_version=1
  58. # We need to access the current mapset quite often in the framework, so we make
  59. # global variable that will be initiated when init() is called
  60. current_mapset=None
  61. ###############################################################################
  62. def get_current_mapset():
  63. """!Return the current mapset
  64. This is the fastest way to receive the current mapset.
  65. The current mapset is set by init() and stored in a global variable.
  66. This function provides access to this global variable.
  67. """
  68. global current_mapset
  69. return current_mapset
  70. def _set_current_mapset(mapset=None):
  71. """!This functions set the global current mapset variable to
  72. the current mapset by calling g.gisenv.
  73. @param mapset The current mapset, g.gisenv will be called
  74. if this variable is set to None
  75. """
  76. global current_mapset
  77. if mapset == None:
  78. mapset = core.gisenv()["MAPSET"]
  79. current_mapset = mapset
  80. ###############################################################################
  81. # The global variable that stores the PyGRASS Messenger object that
  82. # provides a fast and exit safe interface to the C-library message functions
  83. message_interface=None
  84. def _set_tgis_message_interface():
  85. """!Set the global mesage interface variable
  86. @param messenger The grass.pyhrass.message.Messenger() object
  87. """
  88. global message_interface
  89. from grass.pygrass import messages
  90. message_interface = messages.Messenger()
  91. def get_tgis_message_interface():
  92. """!Return the temporal GIS message interface which is of type
  93. grass.pyhrass.message.Messenger()
  94. Use this message interface to print messages to stdout using the
  95. GRASS C-library messaging system.
  96. """
  97. global message_interface
  98. return message_interface
  99. ###############################################################################
  100. def get_tgis_version():
  101. """!Get the verion number of the temporal framework
  102. @return The version number of the temporal framework as string
  103. """
  104. global tgis_version
  105. return tgis_version
  106. ###############################################################################
  107. def get_tgis_metadata(dbif=None):
  108. """!Return the tgis metadata table as a list of rows (dicts)
  109. or None if not present
  110. @param dbif The database interface to be used
  111. @return The selected rows with key/value comumns or None
  112. """
  113. dbif, connected = init_dbif(dbif)
  114. # Select metadata if the table is present
  115. try:
  116. statement = "SELECT * FROM tgis_metadata;\n"
  117. dbif.cursor.execute(statement)
  118. rows = dbif.cursor.fetchall()
  119. except:
  120. rows = None
  121. if connected:
  122. dbif.close()
  123. return rows
  124. ###############################################################################
  125. def get_temporal_dbmi_init_string(kv=None, grassenv=None):
  126. """!Return the database initialization string
  127. @param kv dictionary generated by grass.script.parse_command("t.connect", flags="pg")
  128. @param grassenv Grass environemntal variables created by grass.script.gisenv()
  129. """
  130. if kv == None:
  131. kv = core.parse_command("t.connect", flags="pg")
  132. if grassenv == None:
  133. grassenv = core.gisenv()
  134. global tgis_backend
  135. if tgis_backend == "sqlite":
  136. # We substitute GRASS variables if they are located in the database string
  137. # This behavior is in conjunction with db.connect
  138. if "database" in kv:
  139. string = kv["database"]
  140. string = string.replace("$GISDBASE", grassenv["GISDBASE"])
  141. string = string.replace(
  142. "$LOCATION_NAME", grassenv["LOCATION_NAME"])
  143. string = string.replace("$MAPSET", grassenv["MAPSET"])
  144. return string
  145. else:
  146. core.fatal(_("Unable to initialize the temporal GIS DBMI "
  147. "interface. Use t.connect to specify the driver "
  148. "and the database string"))
  149. elif tgis_backend == "pg":
  150. if "database" in kv:
  151. string = kv["database"]
  152. return string
  153. else:
  154. core.fatal(_("Unable to initialize the temporal GIS DBMI "
  155. "interface. Use t.connect to specify the driver "
  156. "and the database string"))
  157. ###############################################################################
  158. # This variable specifies if the ctypes interface to the grass
  159. # libraries should be used to read map specific data. If set to False
  160. # the grass scripting library will be used to get map informations.
  161. # The advantage of the ctypes inteface is speed, the disadvantage is that
  162. # the GRASS C functions may call G_fatal_error() which exits the process.
  163. # That is not catchable in Python.
  164. use_ctypes_map_access = True
  165. def set_use_ctypes_map_access(use_ctype = True):
  166. """!Define the map access method for the temporal GIS library
  167. Using ctypes to read map metadata is much faster
  168. then using the grass.script interface that calls grass modules.
  169. The disadvantage is that GRASS C-library function will call
  170. G_fatal_error() that will exit the calling process.
  171. GUI developer should set this flag to False.
  172. @param use_ctype True use ctypes interface, False use grass.script interface
  173. """
  174. global use_ctypes_map_access
  175. use_ctypes_map_access = use_ctype
  176. ###############################################################################
  177. def get_use_ctypes_map_access():
  178. """!Return true if ctypes is used for map access """
  179. global use_ctypes_map_access
  180. return use_ctypes_map_access
  181. ###############################################################################
  182. def get_sql_template_path():
  183. base = os.getenv("GISBASE")
  184. base_etc = os.path.join(base, "etc")
  185. return os.path.join(base_etc, "sql")
  186. ###############################################################################
  187. def init():
  188. """!This function set the correct database backend from the environmental variables
  189. and creates the grass location database structure for raster,
  190. vector and raster3d maps as well as for the space-time datasets strds,
  191. str3ds and stvds in case it not exists.
  192. ATTENTION: This functions must be called before any spatio-temporal processing
  193. can be started
  194. """
  195. # We need to set the correct database backend from the environment variables
  196. global tgis_backend
  197. core.run_command("t.connect", flags="c")
  198. kv = core.parse_command("t.connect", flags="pg")
  199. grassenv = core.gisenv()
  200. # Set the global variable current_mapset for fast mapset access
  201. _set_current_mapset(grassenv["MAPSET"])
  202. # Start the GRASS message interface server
  203. _set_tgis_message_interface()
  204. msgr = get_tgis_message_interface()
  205. if "driver" in kv:
  206. if kv["driver"] == "sqlite":
  207. tgis_backend = kv["driver"]
  208. try:
  209. import sqlite3
  210. except ImportError:
  211. msgr.error("Unable to locate the sqlite SQL Python interface module sqlite3.")
  212. raise
  213. dbmi = sqlite3
  214. elif kv["driver"] == "pg":
  215. tgis_backend = kv["driver"]
  216. try:
  217. import psycopg2
  218. except ImportError:
  219. msgr.error("Unable to locate the Postgresql SQL Python interface module psycopg2.")
  220. raise
  221. dbmi = psycopg2
  222. else:
  223. core.fatal(_("Unable to initialize the temporal DBMI interface. Use "
  224. "t.connect to specify the driver and the database string"))
  225. dbmi = sqlite3
  226. else:
  227. # Set the default sqlite3 connection in case nothing was defined
  228. core.run_command("t.connect", flags="d")
  229. db_exists = False
  230. database = get_temporal_dbmi_init_string(kv=kv, grassenv=grassenv)
  231. dbif = SQLDatabaseInterfaceConnection()
  232. # Check if the database already exists
  233. if tgis_backend == "sqlite":
  234. # Check path of the sqlite database
  235. if os.path.exists(database):
  236. dbif.connect()
  237. # Check for raster_base table
  238. dbif.cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='raster_base';")
  239. name = dbif.cursor.fetchone()
  240. if name and name[0] == "raster_base":
  241. db_exists = True
  242. dbif.close()
  243. elif tgis_backend == "pg":
  244. # Connect to database
  245. dbif.connect()
  246. # Check for raster_base table
  247. dbif.cursor.execute("SELECT EXISTS(SELECT * FROM information_schema.tables "
  248. "WHERE table_name=%s)", ('raster_base',))
  249. if dbif.cursor.fetchone()[0]:
  250. db_exists = True
  251. if db_exists:
  252. # Check if we have to add the command column
  253. add_command_col = True
  254. rows = get_tgis_metadata(dbif)
  255. if rows:
  256. for row in rows:
  257. if row["key"] == "tgis_db_version":
  258. version = int(row["value"])
  259. if version >= 1:
  260. add_command_col = False
  261. if add_command_col:
  262. # Try to add the command column to the space time dataset metadata tables
  263. # this is due backward compatibility with old databases
  264. try:
  265. dbif.cursor.execute('ALTER TABLE strds_metadata ADD COLUMN command VARCHAR;')
  266. except:
  267. pass
  268. try:
  269. dbif.cursor.execute('ALTER TABLE str3ds_metadata ADD COLUMN command VARCHAR;')
  270. except:
  271. pass
  272. try:
  273. dbif.cursor.execute('ALTER TABLE stvds_metadata ADD COLUMN command VARCHAR;')
  274. except:
  275. pass
  276. if db_exists == True:
  277. dbif.close()
  278. return
  279. create_temporal_database(dbif, database)
  280. ###############################################################################
  281. def create_temporal_database(dbif, database):
  282. """!This function will create the temporal database
  283. It will create all tables and triggers that are needed to run
  284. the temporal GIS
  285. @param dbif The database interface to be used
  286. """
  287. global tgis_backend
  288. global tgis_version
  289. global tgis_db_version
  290. template_path = get_sql_template_path()
  291. # Read all SQL scripts and templates
  292. map_tables_template_sql = open(os.path.join(
  293. template_path, "map_tables_template.sql"), 'r').read()
  294. raster_metadata_sql = open(os.path.join(
  295. get_sql_template_path(), "raster_metadata_table.sql"), 'r').read()
  296. raster3d_metadata_sql = open(os.path.join(template_path,
  297. "raster3d_metadata_table.sql"),
  298. 'r').read()
  299. vector_metadata_sql = open(os.path.join(template_path,
  300. "vector_metadata_table.sql"),
  301. 'r').read()
  302. raster_views_sql = open(os.path.join(template_path, "raster_views.sql"),
  303. 'r').read()
  304. raster3d_views_sql = open(os.path.join(template_path,
  305. "raster3d_views.sql"), 'r').read()
  306. vector_views_sql = open(os.path.join(template_path, "vector_views.sql"),
  307. 'r').read()
  308. stds_tables_template_sql = open(os.path.join(template_path,
  309. "stds_tables_template.sql"),
  310. 'r').read()
  311. strds_metadata_sql = open(os.path.join(template_path,
  312. "strds_metadata_table.sql"),
  313. 'r').read()
  314. str3ds_metadata_sql = open(os.path.join(template_path,
  315. "str3ds_metadata_table.sql"),
  316. 'r').read()
  317. stvds_metadata_sql = open(os.path.join(template_path,
  318. "stvds_metadata_table.sql"),
  319. 'r').read()
  320. strds_views_sql = open(os.path.join(template_path, "strds_views.sql"),
  321. 'r').read()
  322. str3ds_views_sql = open(os.path.join(template_path, "str3ds_views.sql"),
  323. 'r').read()
  324. stvds_views_sql = open(os.path.join(template_path, "stvds_views.sql"),
  325. 'r').read()
  326. # Create the raster, raster3d and vector tables SQL statements
  327. raster_tables_sql = map_tables_template_sql.replace("GRASS_MAP", "raster")
  328. vector_tables_sql = map_tables_template_sql.replace("GRASS_MAP", "vector")
  329. raster3d_tables_sql = map_tables_template_sql.replace(
  330. "GRASS_MAP", "raster3d")
  331. # Create the space-time raster, raster3d and vector dataset tables
  332. # SQL statements
  333. strds_tables_sql = stds_tables_template_sql.replace("STDS", "strds")
  334. stvds_tables_sql = stds_tables_template_sql.replace("STDS", "stvds")
  335. str3ds_tables_sql = stds_tables_template_sql.replace("STDS", "str3ds")
  336. core.message(_("Create temporal database: %s" % (database)))
  337. if tgis_backend == "sqlite":
  338. # We need to create the sqlite3 database path if it does not exists
  339. tgis_dir = os.path.dirname(database)
  340. if not os.path.exists(tgis_dir):
  341. os.makedirs(tgis_dir)
  342. # Sqlite needs some trigger to emulate the foreign keys
  343. sqlite3_delete_trigger_sql = open(os.path.join(template_path,
  344. "sqlite3_delete_trigger.sql"),
  345. 'r').read()
  346. # Connect now to the database
  347. if not dbif.connected:
  348. dbif.connect()
  349. # Execute the SQL statements for sqlite
  350. # Create the global tables for the native grass datatypes
  351. dbif.execute_transaction(raster_tables_sql)
  352. dbif.execute_transaction(raster_metadata_sql)
  353. dbif.execute_transaction(raster_views_sql)
  354. dbif.execute_transaction(vector_tables_sql)
  355. dbif.execute_transaction(vector_metadata_sql)
  356. dbif.execute_transaction(vector_views_sql)
  357. dbif.execute_transaction(raster3d_tables_sql)
  358. dbif.execute_transaction(raster3d_metadata_sql)
  359. dbif.execute_transaction(raster3d_views_sql)
  360. # Create the tables for the new space-time datatypes
  361. dbif.execute_transaction(strds_tables_sql)
  362. dbif.execute_transaction(strds_metadata_sql)
  363. dbif.execute_transaction(strds_views_sql)
  364. dbif.execute_transaction(stvds_tables_sql)
  365. dbif.execute_transaction(stvds_metadata_sql)
  366. dbif.execute_transaction(stvds_views_sql)
  367. dbif.execute_transaction(str3ds_tables_sql)
  368. dbif.execute_transaction(str3ds_metadata_sql)
  369. dbif.execute_transaction(str3ds_views_sql)
  370. if tgis_backend == "sqlite":
  371. dbif.execute_transaction(sqlite3_delete_trigger_sql)
  372. # Create the tgis metadata table to store the database
  373. # initial configuration
  374. # The metadata table content
  375. metadata = {}
  376. metadata["tgis_version"] = tgis_version
  377. metadata["tgis_db_version"] = tgis_db_version
  378. metadata["has_command_column"] = True
  379. metadata["creation_time"] = datetime.today()
  380. _create_tgis_metadata_table(metadata, dbif)
  381. dbif.close()
  382. ###############################################################################
  383. def _create_tgis_metadata_table(content, dbif=None):
  384. """!Create the temporal gis metadata table which stores all metadata
  385. information about the temporal database.
  386. @param content The dictionary that stores the key:value metadata
  387. that should be stored in the metadata table
  388. @param dbif The database interface to be used
  389. """
  390. dbif, connected = init_dbif(dbif)
  391. statement = "CREATE TABLE tgis_metadata (key VARCHAR NOT NULL, value VARCHAR);\n";
  392. dbif.execute_transaction(statement)
  393. for key in content.keys():
  394. statement = "INSERT INTO tgis_metadata (key, value) VALUES " + \
  395. "(\'%s\' , \'%s\');\n"%(str(key), str(content[key]))
  396. dbif.execute_transaction(statement)
  397. if connected:
  398. dbif.close()
  399. ###############################################################################
  400. class SQLDatabaseInterfaceConnection():
  401. """!This class represents the database interface connection
  402. and provides access to the chisen backend modules.
  403. The following DBMS are supported:
  404. - sqlite via the sqlite3 standard library
  405. - postgresql via psycopg2
  406. """
  407. def __init__(self):
  408. self.connected = False
  409. global tgis_backend
  410. if tgis_backend == "sqlite":
  411. self.dbmi = sqlite3
  412. else:
  413. self.dbmi = psycopg2
  414. def rollback(self):
  415. """
  416. Roll back the last transaction. This must be called
  417. in case a new query should be performed after a db error.
  418. This is only relevant for postgresql database.
  419. """
  420. if self.dbmi.__name__ == "psycopg2":
  421. if self.connected:
  422. self.connection.rollback()
  423. def connect(self):
  424. """!Connect to the DBMI to execute SQL statements
  425. Supported backends are sqlite3 and postgresql
  426. """
  427. self.database = get_temporal_dbmi_init_string()
  428. #print "Connect to", self.database
  429. if self.dbmi.__name__ == "sqlite3":
  430. self.connection = self.dbmi.connect(self.database,
  431. detect_types = self.dbmi.PARSE_DECLTYPES | self.dbmi.PARSE_COLNAMES)
  432. self.connection.row_factory = self.dbmi.Row
  433. self.connection.isolation_level = None
  434. self.cursor = self.connection.cursor()
  435. self.cursor.execute("PRAGMA synchronous = OFF")
  436. self.cursor.execute("PRAGMA journal_mode = MEMORY")
  437. elif self.dbmi.__name__ == "psycopg2":
  438. self.connection = self.dbmi.connect(self.database)
  439. #self.connection.set_isolation_level(dbmi.extensions.ISOLATION_LEVEL_AUTOCOMMIT)
  440. self.cursor = self.connection.cursor(
  441. cursor_factory = self.dbmi.extras.DictCursor)
  442. self.connected = True
  443. def close(self):
  444. """!Close the DBMI connection"""
  445. #print "Close connection to", self.database
  446. self.connection.commit()
  447. self.cursor.close()
  448. self.connected = False
  449. def mogrify_sql_statement(self, content):
  450. """!Return the SQL statement and arguments as executable SQL string
  451. @param content The content as tuple with two entries, the first
  452. entry is the SQL statement with DBMI specific
  453. place holder (?), the second entry is the argument
  454. list that should substitue the place holder.
  455. Usage:
  456. @code
  457. >>> init()
  458. >>> dbif = SQLDatabaseInterfaceConnection()
  459. >>> dbif.mogrify_sql_statement(["SELECT ctime FROM raster_base WHERE id = ?",
  460. ... ["soil@PERMANENT",]])
  461. "SELECT ctime FROM raster_base WHERE id = 'soil@PERMANENT'"
  462. @endcode
  463. """
  464. sql = content[0]
  465. args = content[1]
  466. if self.dbmi.__name__ == "psycopg2":
  467. if len(args) == 0:
  468. return sql
  469. else:
  470. if self.connected:
  471. try:
  472. return self.cursor.mogrify(sql, args)
  473. except:
  474. print sql, args
  475. raise
  476. else:
  477. self.connect()
  478. statement = self.cursor.mogrify(sql, args)
  479. self.close()
  480. return statement
  481. elif self.dbmi.__name__ == "sqlite3":
  482. if len(args) == 0:
  483. return sql
  484. else:
  485. # Unfortunately as sqlite does not support
  486. # the transformation of sql strings and qmarked or
  487. # named arguments we must make our hands dirty
  488. # and do it by ourself. :(
  489. # Doors are open for SQL injection because of the
  490. # limited python sqlite3 implementation!!!
  491. pos = 0
  492. count = 0
  493. maxcount = 100
  494. statement = sql
  495. while count < maxcount:
  496. pos = statement.find("?", pos + 1)
  497. if pos == -1:
  498. break
  499. if args[count] is None:
  500. statement = "%sNULL%s" % (statement[0:pos],
  501. statement[pos + 1:])
  502. elif isinstance(args[count], (int, long)):
  503. statement = "%s%d%s" % (statement[0:pos], args[count],
  504. statement[pos + 1:])
  505. elif isinstance(args[count], float):
  506. statement = "%s%f%s" % (statement[0:pos], args[count],
  507. statement[pos + 1:])
  508. else:
  509. # Default is a string, this works for datetime
  510. # objects too
  511. statement = "%s\'%s\'%s" % (statement[0:pos],
  512. str(args[count]),
  513. statement[pos + 1:])
  514. count += 1
  515. return statement
  516. def check_table(self, table_name):
  517. """!Check if a table exists in the temporal database
  518. @param table_name The name of the table to be checked for existance
  519. @return True if the table exists, False otherwise
  520. """
  521. table_exists = False
  522. connected = False
  523. if not self.connected:
  524. self.connect()
  525. connected = True
  526. # Check if the database already exists
  527. if self.dbmi.__name__ == "sqlite3":
  528. self.cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='%s';"%table_name)
  529. name = self.cursor.fetchone()
  530. if name and name[0] == table_name:
  531. table_exists = True
  532. else:
  533. # Check for raster_base table
  534. self.cursor.execute("SELECT EXISTS(SELECT * FROM information_schema.tables "
  535. "WHERE table_name=%s)", ('%s'%table_name,))
  536. if self.cursor.fetchone()[0]:
  537. table_exists = True
  538. if connected:
  539. self.close()
  540. return table_exists
  541. def execute_transaction(self, statement):
  542. """!Execute a transactional SQL statement
  543. The BEGIN and END TRANSACTION statements will be added automatically
  544. to the sql statement
  545. @param statement The executable SQL statement or SQL script
  546. """
  547. connected = False
  548. if not self.connected:
  549. self.connect()
  550. connected = True
  551. sql_script = ""
  552. sql_script += "BEGIN TRANSACTION;\n"
  553. sql_script += statement
  554. sql_script += "END TRANSACTION;"
  555. try:
  556. if self.dbmi.__name__ == "sqlite3":
  557. self.cursor.executescript(statement)
  558. else:
  559. self.cursor.execute(statement)
  560. self.connection.commit()
  561. except:
  562. if connected:
  563. self.close()
  564. core.error(_("Unable to execute transaction:\n %(sql)s" % \
  565. {"sql":statement}))
  566. raise
  567. if connected:
  568. self.close()
  569. ###############################################################################
  570. def init_dbif(dbif):
  571. """!This method checks if the database interface connection exists,
  572. if not a new one will be created, connected and True will be returned
  573. Usage code sample:
  574. @code
  575. dbif, connect = tgis.init_dbif(None)
  576. sql = dbif.mogrify_sql_statement(["SELECT * FROM raster_base WHERE ? = ?"],
  577. ["id", "soil@PERMANENT"])
  578. dbif.execute_transaction(sql)
  579. if connect:
  580. dbif.close()
  581. @endcode
  582. """
  583. if dbif is None:
  584. dbif = SQLDatabaseInterfaceConnection()
  585. dbif.connect()
  586. return dbif, True
  587. return dbif, False
  588. ###############################################################################
  589. if __name__ == "__main__":
  590. import doctest
  591. doctest.testmod()