core.py 26 KB

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