db.py 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. """
  2. Database related functions to be used in Python scripts.
  3. Usage:
  4. ::
  5. from grass.script import db as grass
  6. grass.db_describe(table)
  7. ...
  8. (C) 2008-2015 by the GRASS Development Team
  9. This program is free software under the GNU General Public
  10. License (>=v2). Read the file COPYING that comes with GRASS
  11. for details.
  12. .. sectionauthor:: Glynn Clements
  13. .. sectionauthor:: Martin Landa <landa.martin gmail.com>
  14. """
  15. from __future__ import absolute_import
  16. from .core import *
  17. from .utils import try_remove
  18. from grass.exceptions import CalledModuleError
  19. def db_describe(table, env=None, **args):
  20. """Return the list of columns for a database table
  21. (interface to `db.describe -c`). Example:
  22. >>> run_command('g.copy', vector='firestations,myfirestations')
  23. 0
  24. >>> db_describe('myfirestations') # doctest: +ELLIPSIS
  25. {'nrows': 71, 'cols': [['cat', 'INTEGER', '20'], ... 'ncols': 22}
  26. >>> run_command('g.remove', flags='f', type='vector', name='myfirestations')
  27. 0
  28. :param str table: table name
  29. :param list args:
  30. :param env: environment
  31. :return: parsed module output
  32. """
  33. if 'database' in args and args['database'] == '':
  34. args.pop('database')
  35. if 'driver' in args and args['driver'] == '':
  36. args.pop('driver')
  37. s = read_command('db.describe', flags='c', table=table, env=env, **args)
  38. if not s:
  39. fatal(_("Unable to describe table <%s>") % table)
  40. cols = []
  41. result = {}
  42. for l in s.splitlines():
  43. f = l.split(':')
  44. key = f[0]
  45. f[1] = f[1].lstrip(' ')
  46. if key.startswith('Column '):
  47. n = int(key.split(' ')[1])
  48. cols.insert(n, f[1:])
  49. elif key in ['ncols', 'nrows']:
  50. result[key] = int(f[1])
  51. else:
  52. result[key] = f[1:]
  53. result['cols'] = cols
  54. return result
  55. def db_table_exist(table, env=None, **args):
  56. """Check if table exists.
  57. If no driver or database are given, then default settings is used
  58. (check db_connection()).
  59. >>> run_command('g.copy', vector='firestations,myfirestations')
  60. 0
  61. >>> db_table_exist('myfirestations')
  62. True
  63. >>> run_command('g.remove', flags='f', type='vector', name='myfirestations')
  64. 0
  65. :param str table: table name
  66. :param args:
  67. :param env: environment
  68. :return: True for success, False otherwise
  69. """
  70. nuldev = open(os.devnull, 'w+')
  71. ok = True
  72. try:
  73. run_command('db.describe', flags='c', table=table,
  74. stdout=nuldev, stderr=nuldev, env=env, **args)
  75. except CalledModuleError:
  76. ok = False
  77. finally:
  78. nuldev.close()
  79. return ok
  80. def db_connection(force=False, env=None):
  81. """Return the current database connection parameters
  82. (interface to `db.connect -g`). Example:
  83. >>> db_connection()
  84. {'group': '', 'schema': '', 'driver': 'sqlite', 'database': '$GISDBASE/$LOCATION_NAME/$MAPSET/sqlite/sqlite.db'}
  85. :param force True to set up default DB connection if not defined
  86. :param env: environment
  87. :return: parsed output of db.connect
  88. """
  89. try:
  90. nuldev = open(os.devnull, 'w')
  91. conn = parse_command('db.connect', flags='g', stderr=nuldev, env=env)
  92. nuldev.close()
  93. except CalledModuleError:
  94. conn = None
  95. if not conn and force:
  96. run_command('db.connect', flags='c', env=env)
  97. conn = parse_command('db.connect', flags='g', env=env)
  98. return conn
  99. def db_select(sql=None, filename=None, table=None, env=None, **args):
  100. """Perform SQL select statement
  101. Note: one of <em>sql</em>, <em>filename</em>, or <em>table</em>
  102. arguments must be provided.
  103. Examples:
  104. >>> run_command('g.copy', vector='firestations,myfirestations')
  105. 0
  106. >>> db_select(sql = 'SELECT cat,CITY FROM myfirestations WHERE cat < 4')
  107. (('1', 'Morrisville'), ('2', 'Morrisville'), ('3', 'Apex'))
  108. Simplyfied usage (it performs <tt>SELECT * FROM myfirestations</tt>.)
  109. >>> db_select(table = 'myfirestations') # doctest: +ELLIPSIS
  110. (('1', '24', 'Morrisville #3', ... 'HS2A', '1.37'))
  111. >>> run_command('g.remove', flags='f', type='vector', name='myfirestations')
  112. 0
  113. :param str sql: SQL statement to perform (or None)
  114. :param str filename: name of file with SQL statements (or None)
  115. :param str table: name of table to query (or None)
  116. :param str args: see \gmod{db.select} arguments
  117. :param env: environment
  118. """
  119. fname = tempfile(create=False, env=env)
  120. if sql:
  121. args['sql'] = sql
  122. elif filename:
  123. args['input'] = filename
  124. elif table:
  125. args['table'] = table
  126. else:
  127. fatal(_("Programmer error: '%(sql)s', '%(filename)s', or '%(table)s' must be provided") %
  128. {'sql': 'sql', 'filename': 'filename', 'table': 'table'} )
  129. if 'sep' not in args:
  130. args['sep'] = '|'
  131. try:
  132. run_command('db.select', quiet=True, flags='c',
  133. output=fname, env=env, **args)
  134. except CalledModuleError:
  135. fatal(_("Fetching data failed"))
  136. ofile = open(fname)
  137. result = [tuple(x.rstrip(os.linesep).split(args['sep'])) for x in ofile.readlines()]
  138. ofile.close()
  139. try_remove(fname)
  140. return tuple(result)
  141. def db_table_in_vector(table, mapset='.', env=None):
  142. """Return the name of vector connected to the table.
  143. By default it check only in the current mapset, because the same table
  144. name could be used also in other mapset by other vector.
  145. It returns None if no vectors are connected to the table.
  146. >>> run_command('g.copy', vector='firestations,myfirestations')
  147. 0
  148. >>> db_table_in_vector('myfirestations')
  149. ['myfirestations@user1']
  150. >>> db_table_in_vector('mfirestations')
  151. >>> run_command('g.remove', flags='f', type='vector', name='myfirestations')
  152. 0
  153. :param str table: name of table to query
  154. :param env: environment
  155. """
  156. from .vector import vector_db
  157. nuldev = open(os.devnull, 'w')
  158. used = []
  159. vects = list_strings('vector', mapset=mapset, env=env)
  160. for vect in vects:
  161. for f in vector_db(vect, stderr=nuldev, env=env).values():
  162. if not f:
  163. continue
  164. if f['table'] == table:
  165. used.append(vect)
  166. break
  167. if len(used) > 0:
  168. return used
  169. else:
  170. return None
  171. def db_begin_transaction(driver):
  172. """Begin transaction.
  173. :return: SQL command as string
  174. """
  175. if driver in ('sqlite', 'pg'):
  176. return 'BEGIN'
  177. if driver == 'mysql':
  178. return 'START TRANSACTION'
  179. return ''
  180. def db_commit_transaction(driver):
  181. """Commit transaction.
  182. :return: SQL command as string
  183. """
  184. if driver in ('sqlite', 'pg', 'mysql'):
  185. return 'COMMIT'
  186. return ''