connector.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424
  1. """
  2. /***************************************************************************
  3. Name : Virtual layers plugin for DB Manager
  4. Date : December 2015
  5. copyright : (C) 2015 by Hugo Mercier
  6. email : hugo dot mercier at oslandia dot com
  7. ***************************************************************************/
  8. /***************************************************************************
  9. * *
  10. * This program is free software; you can redistribute it and/or modify *
  11. * it under the terms of the GNU General Public License as published by *
  12. * the Free Software Foundation; either version 2 of the License, or *
  13. * (at your option) any later version. *
  14. * *
  15. ***************************************************************************/
  16. """
  17. from qgis.PyQt.QtCore import QUrl, QTemporaryFile
  18. from ..connector import DBConnector
  19. from ..plugin import Table
  20. from qgis.core import (
  21. QgsDataSourceUri,
  22. QgsVirtualLayerDefinition,
  23. QgsProject,
  24. QgsMapLayerType,
  25. QgsVectorLayer,
  26. QgsCoordinateReferenceSystem,
  27. QgsWkbTypes
  28. )
  29. import sqlite3
  30. class sqlite3_connection:
  31. def __init__(self, sqlite_file):
  32. self.conn = sqlite3.connect(sqlite_file)
  33. def __enter__(self):
  34. return self.conn
  35. def __exit__(self, ex_type, value, traceback):
  36. self.conn.close()
  37. return ex_type is None
  38. def getQueryGeometryName(sqlite_file):
  39. # introspect the file
  40. with sqlite3_connection(sqlite_file) as conn:
  41. c = conn.cursor()
  42. for r in c.execute("SELECT url FROM _meta"):
  43. d = QgsVirtualLayerDefinition.fromUrl(QUrl(r[0]))
  44. if d.hasDefinedGeometry():
  45. return d.geometryField()
  46. return None
  47. def classFactory():
  48. return VLayerConnector
  49. # Tables in DB Manager are identified by their display names
  50. # This global registry maps a display name with a layer id
  51. # It is filled when getVectorTables is called
  52. class VLayerRegistry:
  53. _instance = None
  54. @classmethod
  55. def instance(cls):
  56. if cls._instance is None:
  57. cls._instance = VLayerRegistry()
  58. return cls._instance
  59. def __init__(self):
  60. self.layers = {}
  61. def reset(self):
  62. self.layers = {}
  63. def has(self, k):
  64. return k in self.layers
  65. def get(self, k):
  66. return self.layers.get(k)
  67. def __getitem__(self, k):
  68. return self.get(k)
  69. def set(self, k, l):
  70. self.layers[k] = l
  71. def __setitem__(self, k, l):
  72. self.set(k, l)
  73. def items(self):
  74. return list(self.layers.items())
  75. def getLayer(self, l):
  76. lid = self.layers.get(l)
  77. if lid is None:
  78. return lid
  79. if lid not in QgsProject.instance().mapLayers().keys():
  80. self.layers.pop(l)
  81. return None
  82. return QgsProject.instance().mapLayer(lid)
  83. class VLayerConnector(DBConnector):
  84. def __init__(self, uri):
  85. pass
  86. def _execute(self, cursor, sql):
  87. # This is only used to get list of fields
  88. class DummyCursor:
  89. def __init__(self, sql):
  90. self.sql = sql
  91. def close(self):
  92. pass
  93. return DummyCursor(sql)
  94. def _get_cursor(self, name=None):
  95. # fix_print_with_import
  96. print(("_get_cursor_", name))
  97. def _get_cursor_columns(self, c):
  98. tf = QTemporaryFile()
  99. tf.open()
  100. tmp = tf.fileName()
  101. tf.close()
  102. df = QgsVirtualLayerDefinition()
  103. df.setFilePath(tmp)
  104. df.setQuery(c.sql)
  105. p = QgsVectorLayer(df.toString(), "vv", "virtual")
  106. if not p.isValid():
  107. return []
  108. f = [f.name() for f in p.fields()]
  109. if p.geometryType() != QgsWkbTypes.NullGeometry:
  110. gn = getQueryGeometryName(tmp)
  111. if gn:
  112. f += [gn]
  113. return f
  114. def uri(self):
  115. return QgsDataSourceUri("qgis")
  116. def getInfo(self):
  117. return "info"
  118. def getSpatialInfo(self):
  119. return None
  120. def hasSpatialSupport(self):
  121. return True
  122. def hasRasterSupport(self):
  123. return False
  124. def hasCustomQuerySupport(self):
  125. return True
  126. def hasTableColumnEditingSupport(self):
  127. return False
  128. def fieldTypes(self):
  129. return [
  130. "integer", "bigint", "smallint", # integers
  131. "real", "double", "float", "numeric", # floats
  132. "varchar", "varchar(255)", "character(20)", "text", # strings
  133. "date", "datetime" # date/time
  134. ]
  135. def getSchemas(self):
  136. return None
  137. def getTables(self, schema=None, add_sys_tables=False):
  138. """ get list of tables """
  139. return self.getVectorTables()
  140. def getVectorTables(self, schema=None):
  141. """ get list of table with a geometry column
  142. it returns:
  143. name (table name)
  144. is_system_table
  145. type = 'view' (is a view?)
  146. geometry_column:
  147. f_table_name (the table name in geometry_columns may be in a wrong case, use this to load the layer)
  148. f_geometry_column
  149. type
  150. coord_dimension
  151. srid
  152. """
  153. reg = VLayerRegistry.instance()
  154. VLayerRegistry.instance().reset()
  155. lst = []
  156. for _, l in QgsProject.instance().mapLayers().items():
  157. if l.type() == QgsMapLayerType.VectorLayer:
  158. lname = l.name()
  159. # if there is already a layer with this name, use the layer id
  160. # as name
  161. if reg.has(lname):
  162. lname = l.id()
  163. VLayerRegistry.instance().set(lname, l.id())
  164. geomType = None
  165. dim = None
  166. if l.isSpatial():
  167. g = l.dataProvider().wkbType()
  168. g_flat = QgsWkbTypes.flatType(g)
  169. geomType = QgsWkbTypes.displayString(g_flat).upper()
  170. if geomType:
  171. dim = 'XY'
  172. if QgsWkbTypes.hasZ(g):
  173. dim += 'Z'
  174. if QgsWkbTypes.hasM(g):
  175. dim += 'M'
  176. lst.append(
  177. (Table.VectorType, lname, False, False, l.id(), 'geometry', geomType, dim, l.crs().postgisSrid()))
  178. else:
  179. lst.append((Table.TableType, lname, False, False))
  180. return lst
  181. def getRasterTables(self, schema=None):
  182. return []
  183. def getTableRowCount(self, table):
  184. t = table[1]
  185. l = VLayerRegistry.instance().getLayer(t)
  186. if not l or not l.isValid():
  187. return None
  188. return l.featureCount()
  189. def getTableFields(self, table):
  190. """ return list of columns in table """
  191. t = table[1]
  192. l = VLayerRegistry.instance().getLayer(t)
  193. if not l or not l.isValid():
  194. return []
  195. # id, name, type, nonnull, default, pk
  196. n = l.dataProvider().fields().size()
  197. f = [(i, f.name(), f.typeName(), False, None, False)
  198. for i, f in enumerate(l.dataProvider().fields())]
  199. if l.isSpatial():
  200. f += [(n, "geometry", "geometry", False, None, False)]
  201. return f
  202. def getTableIndexes(self, table):
  203. return []
  204. def getTableConstraints(self, table):
  205. return None
  206. def getTableTriggers(self, table):
  207. return []
  208. def deleteTableTrigger(self, trigger, table=None):
  209. return
  210. def getTableExtent(self, table, geom):
  211. is_id, t = table
  212. if is_id:
  213. l = QgsProject.instance().mapLayer(t)
  214. else:
  215. l = VLayerRegistry.instance().getLayer(t)
  216. if not l or not l.isValid():
  217. return None
  218. e = l.extent()
  219. r = (e.xMinimum(), e.yMinimum(), e.xMaximum(), e.yMaximum())
  220. return r
  221. def getViewDefinition(self, view):
  222. print("**unimplemented** getViewDefinition")
  223. def getSpatialRefInfo(self, srid):
  224. crs = QgsCoordinateReferenceSystem(srid)
  225. return crs.description()
  226. def isVectorTable(self, table):
  227. return True
  228. def isRasterTable(self, table):
  229. return False
  230. def createTable(self, table, field_defs, pkey):
  231. print("**unimplemented** createTable")
  232. return False
  233. def deleteTable(self, table):
  234. print("**unimplemented** deleteTable")
  235. return False
  236. def emptyTable(self, table):
  237. print("**unimplemented** emptyTable")
  238. return False
  239. def renameTable(self, table, new_table):
  240. print("**unimplemented** renameTable")
  241. return False
  242. def moveTable(self, table, new_table, new_schema=None):
  243. print("**unimplemented** moveTable")
  244. return False
  245. def createView(self, view, query):
  246. print("**unimplemented** createView")
  247. return False
  248. def deleteView(self, view):
  249. print("**unimplemented** deleteView")
  250. return False
  251. def renameView(self, view, new_name):
  252. print("**unimplemented** renameView")
  253. return False
  254. def runVacuum(self):
  255. print("**unimplemented** runVacuum")
  256. return False
  257. def addTableColumn(self, table, field_def):
  258. print("**unimplemented** addTableColumn")
  259. return False
  260. def deleteTableColumn(self, table, column):
  261. print("**unimplemented** deleteTableColumn")
  262. def updateTableColumn(self, table, column, new_name, new_data_type=None, new_not_null=None, new_default=None, comment=None):
  263. print("**unimplemented** updateTableColumn")
  264. def renameTableColumn(self, table, column, new_name):
  265. print("**unimplemented** renameTableColumn")
  266. return False
  267. def setColumnType(self, table, column, data_type):
  268. print("**unimplemented** setColumnType")
  269. return False
  270. def setColumnDefault(self, table, column, default):
  271. print("**unimplemented** setColumnDefault")
  272. return False
  273. def setColumnNull(self, table, column, is_null):
  274. print("**unimplemented** setColumnNull")
  275. return False
  276. def isGeometryColumn(self, table, column):
  277. print("**unimplemented** isGeometryColumn")
  278. return False
  279. def addGeometryColumn(self, table, geom_column='geometry', geom_type='POINT', srid=-1, dim=2):
  280. print("**unimplemented** addGeometryColumn")
  281. return False
  282. def deleteGeometryColumn(self, table, geom_column):
  283. print("**unimplemented** deleteGeometryColumn")
  284. return False
  285. def addTableUniqueConstraint(self, table, column):
  286. print("**unimplemented** addTableUniqueConstraint")
  287. return False
  288. def deleteTableConstraint(self, table, constraint):
  289. print("**unimplemented** deleteTableConstraint")
  290. return False
  291. def addTablePrimaryKey(self, table, column):
  292. print("**unimplemented** addTablePrimaryKey")
  293. return False
  294. def createTableIndex(self, table, name, column, unique=False):
  295. print("**unimplemented** createTableIndex")
  296. return False
  297. def deleteTableIndex(self, table, name):
  298. print("**unimplemented** deleteTableIndex")
  299. return False
  300. def createSpatialIndex(self, table, geom_column='geometry'):
  301. print("**unimplemented** createSpatialIndex")
  302. return False
  303. def deleteSpatialIndex(self, table, geom_column='geometry'):
  304. print("**unimplemented** deleteSpatialIndex")
  305. return False
  306. def hasSpatialIndex(self, table, geom_column='geometry'):
  307. print("**unimplemented** hasSpatialIndex")
  308. return False
  309. def execution_error_types(self):
  310. print("**unimplemented** execution_error_types")
  311. return False
  312. def connection_error_types(self):
  313. print("**unimplemented** connection_error_types")
  314. return False
  315. def getSqlDictionary(self):
  316. from .sql_dictionary import getSqlDictionary
  317. sql_dict = getSqlDictionary()
  318. items = []
  319. for tbl in self.getTables():
  320. items.append(tbl[1]) # table name
  321. for fld in self.getTableFields((None, tbl[1])):
  322. items.append(fld[1]) # field name
  323. sql_dict["identifier"] = items
  324. return sql_dict
  325. def getQueryBuilderDictionary(self):
  326. from .sql_dictionary import getQueryBuilderDictionary
  327. return getQueryBuilderDictionary()