core.py 26 KB

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