core.py 26 KB

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