db.py 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  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 line in s.splitlines():
  43. f = line.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(
  74. "db.describe",
  75. flags="c",
  76. table=table,
  77. stdout=nuldev,
  78. stderr=nuldev,
  79. env=env,
  80. **args,
  81. )
  82. except CalledModuleError:
  83. ok = False
  84. finally:
  85. nuldev.close()
  86. return ok
  87. def db_connection(force=False, env=None):
  88. """Return the current database connection parameters
  89. (interface to `db.connect -g`). Example:
  90. >>> db_connection()
  91. {'group': '', 'schema': '', 'driver': 'sqlite', 'database': '$GISDBASE/$LOCATION_NAME/$MAPSET/sqlite/sqlite.db'}
  92. :param force True to set up default DB connection if not defined
  93. :param env: environment
  94. :return: parsed output of db.connect
  95. """
  96. try:
  97. nuldev = open(os.devnull, "w")
  98. conn = parse_command("db.connect", flags="g", stderr=nuldev, env=env)
  99. nuldev.close()
  100. except CalledModuleError:
  101. conn = None
  102. if not conn and force:
  103. run_command("db.connect", flags="c", env=env)
  104. conn = parse_command("db.connect", flags="g", env=env)
  105. return conn
  106. def db_select(sql=None, filename=None, table=None, env=None, **args):
  107. """Perform SQL select statement
  108. Note: one of <em>sql</em>, <em>filename</em>, or <em>table</em>
  109. arguments must be provided.
  110. Examples:
  111. >>> run_command('g.copy', vector='firestations,myfirestations')
  112. 0
  113. >>> db_select(sql = 'SELECT cat,CITY FROM myfirestations WHERE cat < 4')
  114. (('1', 'Morrisville'), ('2', 'Morrisville'), ('3', 'Apex'))
  115. Simplyfied usage (it performs <tt>SELECT * FROM myfirestations</tt>.)
  116. >>> db_select(table = 'myfirestations') # doctest: +ELLIPSIS
  117. (('1', '24', 'Morrisville #3', ... 'HS2A', '1.37'))
  118. >>> run_command('g.remove', flags='f', type='vector', name='myfirestations')
  119. 0
  120. :param str sql: SQL statement to perform (or None)
  121. :param str filename: name of file with SQL statements (or None)
  122. :param str table: name of table to query (or None)
  123. :param str args: see *db.select* arguments
  124. :param env: environment
  125. """
  126. fname = tempfile(create=False, env=env)
  127. if sql:
  128. args["sql"] = sql
  129. elif filename:
  130. args["input"] = filename
  131. elif table:
  132. args["table"] = table
  133. else:
  134. fatal(
  135. _(
  136. "Programmer error: '%(sql)s', '%(filename)s', or '%(table)s' must be provided"
  137. )
  138. % {"sql": "sql", "filename": "filename", "table": "table"}
  139. )
  140. if "sep" not in args:
  141. args["sep"] = "|"
  142. try:
  143. run_command("db.select", quiet=True, flags="c", output=fname, env=env, **args)
  144. except CalledModuleError:
  145. fatal(_("Fetching data failed"))
  146. ofile = open(fname)
  147. result = [tuple(x.rstrip(os.linesep).split(args["sep"])) for x in ofile.readlines()]
  148. ofile.close()
  149. try_remove(fname)
  150. return tuple(result)
  151. def db_table_in_vector(table, mapset=".", env=None):
  152. """Return the name of vector connected to the table.
  153. By default it check only in the current mapset, because the same table
  154. name could be used also in other mapset by other vector.
  155. It returns None if no vectors are connected to the table.
  156. >>> run_command('g.copy', vector='firestations,myfirestations')
  157. 0
  158. >>> db_table_in_vector('myfirestations')
  159. ['myfirestations@user1']
  160. >>> db_table_in_vector('mfirestations')
  161. >>> run_command('g.remove', flags='f', type='vector', name='myfirestations')
  162. 0
  163. :param str table: name of table to query
  164. :param env: environment
  165. """
  166. from .vector import vector_db
  167. nuldev = open(os.devnull, "w")
  168. used = []
  169. vects = list_strings("vector", mapset=mapset, env=env)
  170. for vect in vects:
  171. for f in vector_db(vect, stderr=nuldev, env=env).values():
  172. if not f:
  173. continue
  174. if f["table"] == table:
  175. used.append(vect)
  176. break
  177. if len(used) > 0:
  178. return used
  179. else:
  180. return None
  181. def db_begin_transaction(driver):
  182. """Begin transaction.
  183. :return: SQL command as string
  184. """
  185. if driver in ("sqlite", "pg"):
  186. return "BEGIN"
  187. if driver == "mysql":
  188. return "START TRANSACTION"
  189. return ""
  190. def db_commit_transaction(driver):
  191. """Commit transaction.
  192. :return: SQL command as string
  193. """
  194. if driver in ("sqlite", "pg", "mysql"):
  195. return "COMMIT"
  196. return ""