v.db.reconnect.all.py 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. #!/usr/bin/env python3
  2. ############################################################################
  3. #
  4. # MODULE: v.db.reconnect.all
  5. # AUTHOR(S): Radim Blazek
  6. # Converted to Python by Glynn Clements
  7. # Update for GRASS 7 by Markus Metz
  8. # PURPOSE: Reconnect all vector maps from the current mapset
  9. # COPYRIGHT: (C) 2004, 2012 by the GRASS Development Team
  10. #
  11. # This program is free software under the GNU General
  12. # Public License (>=v2). Read the file COPYING that
  13. # comes with GRASS for details.
  14. #
  15. #############################################################################
  16. # %module
  17. # % description: Reconnects attribute tables for all vector maps from the current mapset to a new database.
  18. # % keyword: vector
  19. # % keyword: attribute table
  20. # % keyword: database
  21. # %end
  22. # %flag
  23. # % key: c
  24. # % description: Copy attribute tables to the target database if not exist
  25. # %end
  26. # %flag
  27. # % key: d
  28. # % description: Delete attribute tables from the source database
  29. # %end
  30. # %option G_OPT_DB_DATABASE
  31. # % key: old_database
  32. # % description: Name of source database
  33. # %end
  34. # %option G_OPT_DB_SCHEMA
  35. # % key: old_schema
  36. # % label: Name of source database schema
  37. # %end
  38. # %option
  39. # % key: new_driver
  40. # % description: Name for target driver
  41. # %end
  42. # %option G_OPT_DB_DATABASE
  43. # % key: new_database
  44. # % description: Name for target database
  45. # %end
  46. # %option G_OPT_DB_SCHEMA
  47. # % key: new_schema
  48. # % label: Name for target database schema
  49. # %end
  50. import sys
  51. import os
  52. import string
  53. import grass.script as gscript
  54. from grass.exceptions import CalledModuleError
  55. # substitute variables (gisdbase, location_name, mapset)
  56. def substitute_db(database):
  57. gisenv = gscript.gisenv()
  58. tmpl = string.Template(database)
  59. return tmpl.substitute(
  60. GISDBASE=gisenv["GISDBASE"],
  61. LOCATION_NAME=gisenv["LOCATION_NAME"],
  62. MAPSET=gisenv["MAPSET"],
  63. )
  64. # create database if doesn't exist
  65. def create_db(driver, database):
  66. subst_database = substitute_db(database)
  67. if driver == "dbf":
  68. path = subst_database
  69. # check if destination directory exists
  70. if not os.path.isdir(path):
  71. # create dbf database
  72. os.makedirs(path)
  73. return True
  74. return False
  75. if driver == "sqlite":
  76. path = os.path.dirname(subst_database)
  77. # check if destination directory exists
  78. if not os.path.isdir(path):
  79. os.makedirs(path)
  80. if (
  81. subst_database
  82. in gscript.read_command("db.databases", quiet=True, driver=driver).splitlines()
  83. ):
  84. return False
  85. gscript.info(
  86. _(
  87. "Target database doesn't exist, "
  88. "creating a new database using <%s> driver..."
  89. )
  90. % driver
  91. )
  92. try:
  93. gscript.run_command("db.createdb", driver=driver, database=subst_database)
  94. except CalledModuleError:
  95. gscript.fatal(
  96. _("Unable to create database <%s> by driver <%s>")
  97. % (subst_database, driver)
  98. )
  99. return False
  100. # copy tables if required (-c)
  101. def copy_tab(from_driver, from_database, from_table, to_driver, to_database, to_table):
  102. if (
  103. to_table
  104. in gscript.read_command(
  105. "db.tables",
  106. quiet=True,
  107. driver=to_driver,
  108. database=to_database,
  109. stderr=nuldev,
  110. ).splitlines()
  111. ):
  112. return False
  113. gscript.info("Copying table <%s> to target database..." % to_table)
  114. try:
  115. gscript.run_command(
  116. "db.copy",
  117. from_driver=from_driver,
  118. from_database=from_database,
  119. from_table=from_table,
  120. to_driver=to_driver,
  121. to_database=to_database,
  122. to_table=to_table,
  123. )
  124. except CalledModuleError:
  125. gscript.fatal(_("Unable to copy table <%s>") % from_table)
  126. return True
  127. # drop tables if required (-d)
  128. def drop_tab(vector, layer, table, driver, database):
  129. # disconnect
  130. try:
  131. gscript.run_command(
  132. "v.db.connect", flags="d", quiet=True, map=vector, layer=layer, table=table
  133. )
  134. except CalledModuleError:
  135. gscript.warning(
  136. _("Unable to disconnect table <%s> from vector <%s>") % (table, vector)
  137. )
  138. # drop table
  139. try:
  140. gscript.run_command(
  141. "db.droptable",
  142. quiet=True,
  143. flags="f",
  144. driver=driver,
  145. database=database,
  146. table=table,
  147. )
  148. except CalledModuleError:
  149. gscript.fatal(_("Unable to drop table <%s>") % table)
  150. # create index on key column
  151. def create_index(driver, database, table, index_name, key):
  152. if driver == "dbf":
  153. return False
  154. gscript.info(_("Creating index <%s>...") % index_name)
  155. try:
  156. gscript.run_command(
  157. "db.execute",
  158. quiet=True,
  159. driver=driver,
  160. database=database,
  161. sql="create unique index %s on %s(%s)" % (index_name, table, key),
  162. )
  163. except CalledModuleError:
  164. gscript.warning(_("Unable to create index <%s>") % index_name)
  165. def main():
  166. # old connection
  167. old_database = options["old_database"]
  168. old_schema = options["old_schema"]
  169. # new connection
  170. default_connection = gscript.db_connection()
  171. if options["new_driver"]:
  172. new_driver = options["new_driver"]
  173. else:
  174. new_driver = default_connection["driver"]
  175. if options["new_database"]:
  176. new_database = options["new_database"]
  177. else:
  178. new_database = default_connection["database"]
  179. if options["new_schema"]:
  180. new_schema = options["new_schema"]
  181. else:
  182. new_schema = default_connection["schema"]
  183. if old_database == "":
  184. old_database = None
  185. old_database_subst = None
  186. if old_database is not None:
  187. old_database_subst = substitute_db(old_database)
  188. new_database_subst = substitute_db(new_database)
  189. if old_database_subst == new_database_subst and old_schema == new_schema:
  190. gscript.fatal(
  191. _("Old and new database connection is identical. " "Nothing to do.")
  192. )
  193. mapset = gscript.gisenv()["MAPSET"]
  194. vectors = gscript.list_grouped("vect")[mapset]
  195. num_vectors = len(vectors)
  196. if flags["c"]:
  197. # create new database if not existing
  198. create_db(new_driver, new_database)
  199. i = 0
  200. for vect in vectors:
  201. vect = "%s@%s" % (vect, mapset)
  202. i += 1
  203. gscript.message(
  204. _("%s\nReconnecting vector map <%s> " "(%d of %d)...\n%s")
  205. % ("-" * 80, vect, i, num_vectors, "-" * 80)
  206. )
  207. for f in gscript.vector_db(vect, stderr=nuldev).values():
  208. layer = f["layer"]
  209. schema_table = f["table"]
  210. key = f["key"]
  211. database = f["database"]
  212. driver = f["driver"]
  213. # split schema.table
  214. if "." in schema_table:
  215. schema, table = schema_table.split(".", 1)
  216. else:
  217. schema = ""
  218. table = schema_table
  219. if new_schema:
  220. new_schema_table = "%s.%s" % (new_schema, table)
  221. else:
  222. new_schema_table = table
  223. gscript.debug(
  224. "DATABASE = '%s' SCHEMA = '%s' TABLE = '%s' ->\n"
  225. " NEW_DATABASE = '%s' NEW_SCHEMA_TABLE = '%s'"
  226. % (old_database, schema, table, new_database, new_schema_table)
  227. )
  228. do_reconnect = True
  229. if old_database_subst is not None:
  230. if database != old_database_subst:
  231. do_reconnect = False
  232. if database == new_database_subst:
  233. do_reconnect = False
  234. if schema != old_schema:
  235. do_reconnect = False
  236. if do_reconnect:
  237. gscript.verbose(_("Reconnecting layer %d...") % layer)
  238. if flags["c"]:
  239. # check if table exists in new database
  240. copy_tab(
  241. driver,
  242. database,
  243. schema_table,
  244. new_driver,
  245. new_database,
  246. new_schema_table,
  247. )
  248. # drop original table if required
  249. if flags["d"]:
  250. drop_tab(vect, layer, schema_table, driver, substitute_db(database))
  251. # reconnect tables (don't use substituted new_database)
  252. # NOTE: v.db.connect creates an index on the key column
  253. try:
  254. gscript.run_command(
  255. "v.db.connect",
  256. flags="o",
  257. quiet=True,
  258. map=vect,
  259. layer=layer,
  260. driver=new_driver,
  261. database=new_database,
  262. table=new_schema_table,
  263. key=key,
  264. )
  265. except CalledModuleError:
  266. gscript.warning(
  267. _(
  268. "Unable to connect table <%s> to vector "
  269. "<%s> on layer <%s>"
  270. )
  271. % (table, vect, str(layer))
  272. )
  273. else:
  274. if database != new_database_subst:
  275. gscript.warning(
  276. _(
  277. "Layer <%d> will not be reconnected "
  278. "because database or schema do not "
  279. "match."
  280. )
  281. % layer
  282. )
  283. return 0
  284. if __name__ == "__main__":
  285. options, flags = gscript.parser()
  286. nuldev = open(os.devnull, "w")
  287. sys.exit(main())