db.py 6.8 KB

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