connector.py 60 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738
  1. """
  2. /***************************************************************************
  3. Name : DB Manager
  4. Description : Database manager plugin for QGIS (Oracle)
  5. Date : Aug 27, 2014
  6. copyright : (C) 2014 by Médéric RIBREUX
  7. email : mederic.ribreux@gmail.com
  8. The content of this file is based on
  9. - PG_Manager by Martin Dobias <wonder.sk@gmail.com> (GPLv2 license)
  10. - DB Manager by Giuseppe Sucameli <brush.tyler@gmail.com> (GPLv2 license)
  11. ***************************************************************************/
  12. /***************************************************************************
  13. * *
  14. * This program is free software; you can redistribute it and/or modify *
  15. * it under the terms of the GNU General Public License as published by *
  16. * the Free Software Foundation; either version 2 of the License, or *
  17. * (at your option) any later version. *
  18. * *
  19. ***************************************************************************/
  20. """
  21. from qgis.PyQt.QtSql import QSqlDatabase
  22. from ..connector import DBConnector
  23. from ..plugin import ConnectionError, DbError, Table
  24. import os
  25. from qgis.core import Qgis, QgsApplication, NULL, QgsWkbTypes
  26. from . import QtSqlDB
  27. import sqlite3
  28. from functools import cmp_to_key
  29. def classFactory():
  30. if QSqlDatabase.isDriverAvailable("QOCISPATIAL"):
  31. return OracleDBConnector
  32. else:
  33. return None
  34. class OracleDBConnector(DBConnector):
  35. ORGeomTypes = {
  36. 2001: QgsWkbTypes.Point,
  37. 2002: QgsWkbTypes.LineString,
  38. 2003: QgsWkbTypes.Polygon,
  39. 2005: QgsWkbTypes.MultiPoint,
  40. 2006: QgsWkbTypes.MultiLineString,
  41. 2007: QgsWkbTypes.MultiPolygon,
  42. 3001: QgsWkbTypes.Point25D,
  43. 3002: QgsWkbTypes.LineString25D,
  44. 3003: QgsWkbTypes.Polygon25D,
  45. 3005: QgsWkbTypes.MultiPoint25D,
  46. 3006: QgsWkbTypes.MultiLineString25D,
  47. 3007: QgsWkbTypes.MultiPolygon25D
  48. }
  49. def __init__(self, uri, connName):
  50. DBConnector.__init__(self, uri)
  51. self.connName = connName
  52. self.user = uri.username() or os.environ.get('USER')
  53. self.passwd = uri.password()
  54. self.host = uri.host()
  55. if self.host != "":
  56. self.dbname = self.host
  57. if uri.port() != "" and uri.port() != "1521":
  58. self.dbname += ":" + uri.port()
  59. if uri.database() != "":
  60. self.dbname += "/" + uri.database()
  61. elif uri.database() != "":
  62. self.dbname = uri.database()
  63. # Connection options
  64. self.useEstimatedMetadata = uri.useEstimatedMetadata()
  65. self.userTablesOnly = uri.param('userTablesOnly').lower() == "true"
  66. self.geometryColumnsOnly = uri.param(
  67. 'geometryColumnsOnly').lower() == "true"
  68. self.allowGeometrylessTables = uri.param(
  69. 'allowGeometrylessTables').lower() == "true"
  70. self.onlyExistingTypes = uri.param(
  71. 'onlyExistingTypes').lower() == "true"
  72. self.includeGeoAttributes = uri.param(
  73. 'includeGeoAttributes').lower() == "true"
  74. # For refreshing
  75. self.populated = False
  76. try:
  77. self.connection = QtSqlDB.connect(
  78. "QOCISPATIAL", self.dbname, self.user, self.passwd)
  79. except self.connection_error_types() as e:
  80. raise ConnectionError(e)
  81. # Find if we can connect to data_sources_cache.db
  82. sqlite_cache_file = os.path.join(
  83. QgsApplication.qgisSettingsDirPath(), "data_sources_cache.db")
  84. if (os.path.isfile(sqlite_cache_file)):
  85. try:
  86. self.cache_connection = sqlite3.connect(sqlite_cache_file)
  87. except sqlite3.Error:
  88. self.cache_connection = False
  89. else:
  90. self.cache_connection = False
  91. # Find if there is cache for our connection:
  92. if self.cache_connection:
  93. try:
  94. cache_c = self.cache_connection.cursor()
  95. query = ("SELECT COUNT(*) FROM meta_oracle WHERE"
  96. " conn = '{}'".format(self.connName))
  97. cache_c.execute(query)
  98. has_cached = cache_c.fetchone()[0]
  99. cache_c.close()
  100. if not has_cached:
  101. self.cache_connection = False
  102. except sqlite3.Error:
  103. self.cache_connection = False
  104. self._checkSpatial()
  105. self._checkGeometryColumnsTable()
  106. def _connectionInfo(self):
  107. return str(self._uri.connectionInfo(True))
  108. def _checkSpatial(self):
  109. """Check whether Oracle Spatial is present in catalog."""
  110. query = ("SELECT count(*) FROM v$option WHERE parameter = "
  111. " 'Spatial' AND value = 'TRUE'")
  112. c = self._execute(None, query)
  113. self.has_spatial = self._fetchone(c)[0] > 0
  114. c.close()
  115. return self.has_spatial
  116. def _checkGeometryColumnsTable(self):
  117. """Check if user can read *_SDO_GEOM_METADATA view."""
  118. # First check if user can read ALL_SDO_GEOM_METADATA
  119. privs = self.getRawTablePrivileges('ALL_SDO_GEOM_METADATA',
  120. 'MDSYS', 'PUBLIC')
  121. # Otherwise, try with USER_SDO_GEOM_METADATA
  122. if not privs[0]:
  123. privs = self.getRawTablePrivileges('USER_SDO_GEOM_METADATA',
  124. 'MDSYS', 'PUBLIC')
  125. if privs[0]:
  126. self.has_geometry_columns = True
  127. self.has_geometry_columns_access = True
  128. self.is_geometry_columns_view = True
  129. return True
  130. else:
  131. self.has_geometry_columns = False
  132. self.has_geometry_columns_access = False
  133. self.is_geometry_columns_view = False
  134. return False
  135. def getInfo(self):
  136. """Returns Oracle Database server version."""
  137. c = self._execute(None, "SELECT * FROM V$VERSION WHERE ROWNUM < 2")
  138. res = self._fetchone(c)
  139. c.close()
  140. return res
  141. def hasCache(self):
  142. """Returns self.cache_connection."""
  143. if self.cache_connection:
  144. return True
  145. return False
  146. def getSpatialInfo(self):
  147. """Returns Oracle Spatial version."""
  148. if not self.has_spatial:
  149. return
  150. try:
  151. c = self._execute(None, "SELECT SDO_VERSION FROM DUAL")
  152. except DbError:
  153. return
  154. res = self._fetchone(c)
  155. c.close()
  156. return res
  157. def hasSpatialSupport(self):
  158. """Find if there is Spatial support."""
  159. return self.has_spatial
  160. def hasRasterSupport(self):
  161. """No raster support for the moment!"""
  162. # return self.has_raster
  163. return False
  164. def hasCustomQuerySupport(self):
  165. """From QGIS v2.2 onwards Oracle custom queries are supported."""
  166. return Qgis.QGIS_VERSION_INT >= 20200
  167. def hasTableColumnEditingSupport(self):
  168. """Tables can always be edited."""
  169. return True
  170. def hasCreateSpatialViewSupport(self):
  171. """We can create Spatial Views."""
  172. return True
  173. def fieldTypes(self):
  174. """From
  175. http://docs.oracle.com/cd/B28359_01/server.111/b28318/datatype.htm#CNCPT1828
  176. """
  177. return [
  178. "number", "number(9)", # integers
  179. "number(9,2)", "number(*,4)", "binary_float",
  180. "binary_double", # floats
  181. "varchar2(255)", "char(20)", "nvarchar2(255)",
  182. "nchar(20)", # strings
  183. "date", "timestamp" # date/time
  184. ]
  185. def getSchemaPrivileges(self, schema):
  186. """
  187. Schema privileges:
  188. (can create new objects, can access objects in schema)
  189. """
  190. # TODO: find the best way in Oracle do determine schema privileges
  191. schema = self.user if not schema else schema
  192. # In Oracle world, rights seems quite simple: only schema_owner can
  193. # create table in the schema
  194. if schema == self.user:
  195. return (True, True)
  196. # getSchemas request only extract schemas where user has access
  197. return (False, True)
  198. def getRawTablePrivileges(self, table, owner, grantee):
  199. """
  200. Retrieve privileges on a table in a schema for a specific
  201. user.
  202. """
  203. result = [False, False, False, False]
  204. # Inspect in all tab privs
  205. sql = """
  206. SELECT DISTINCT PRIVILEGE
  207. FROM ALL_TAB_PRIVS_RECD
  208. WHERE PRIVILEGE IN ('SELECT','INSERT','UPDATE','DELETE')
  209. AND TABLE_NAME = {}
  210. AND OWNER = {}
  211. AND GRANTEE IN ({}, {})
  212. """.format(self.quoteString(table),
  213. self.quoteString(owner),
  214. self.quoteString(grantee),
  215. self.quoteString(grantee.upper()))
  216. c = self._execute(None, sql)
  217. res = self._fetchall(c)
  218. c.close()
  219. # Find which privilege is returned
  220. for line in res:
  221. if line[0] == "SELECT":
  222. result[0] = True
  223. if line[0] == "INSERT":
  224. result[1] = True
  225. if line[0] == "UPDATE":
  226. result[2] = True
  227. if line[0] == "DELETE":
  228. result[3] = True
  229. return result
  230. def getTablePrivileges(self, table):
  231. """Retrieve table privileges: (select, insert, update, delete)."""
  232. schema, tablename = self.getSchemaTableName(table)
  233. if self.user == schema:
  234. return [True, True, True, True]
  235. return self.getRawTablePrivileges(tablename, schema, self.user)
  236. def getSchemasCache(self):
  237. """Get the list of schemas from the cache."""
  238. sql = """
  239. SELECT DISTINCT ownername
  240. FROM "oracle_{}"
  241. ORDER BY ownername
  242. """.format(self.connName)
  243. c = self.cache_connection.cursor()
  244. c.execute(sql)
  245. res = c.fetchall()
  246. c.close()
  247. return res
  248. def getSchemas(self):
  249. """Get list of schemas in tuples:
  250. (oid, name, owner, perms, comment).
  251. """
  252. if self.userTablesOnly:
  253. return [(self.user,)]
  254. if self.hasCache():
  255. return self.getSchemasCache()
  256. # Use cache if available:
  257. metatable = ("all_objects WHERE object_type IN "
  258. "('TABLE','VIEW','SYNONYM')")
  259. if self.geometryColumnsOnly:
  260. metatable = "all_sdo_geom_metadata"
  261. sql = """SELECT DISTINCT owner FROM {} ORDER BY owner""".format(
  262. metatable)
  263. c = self._execute(None, sql)
  264. res = self._fetchall(c)
  265. c.close()
  266. return res
  267. def getTables(self, schema=None, add_sys_tables=False):
  268. """Get list of tables."""
  269. if self.hasCache() and not self.populated:
  270. self.populated = True
  271. return self.getTablesCache(schema)
  272. tablenames = []
  273. items = []
  274. try:
  275. vectors = self.getVectorTables(schema)
  276. for tbl in vectors:
  277. tablenames.append((tbl[2], tbl[1]))
  278. items.append(tbl)
  279. except DbError:
  280. pass
  281. if self.allowGeometrylessTables:
  282. # get all non geographic tables and views
  283. prefix = "ALL"
  284. owner = "o.owner"
  285. where = ""
  286. if self.userTablesOnly:
  287. prefix = "USER"
  288. owner = "user As OWNER"
  289. if schema and not self.userTablesOnly:
  290. where = "AND o.owner = {} ".format(
  291. self.quoteString(schema))
  292. sql = """
  293. SELECT o.OBJECT_NAME, {},
  294. CASE o.OBJECT_TYPE
  295. WHEN 'VIEW' THEN 1
  296. ELSE 0 END As isView
  297. FROM {}_OBJECTS o
  298. WHERE o.object_type IN ('TABLE','VIEW','SYNONYM')
  299. {} {}
  300. ORDER BY o.OBJECT_NAME
  301. """.format(owner, prefix, where,
  302. "" if add_sys_tables
  303. else "AND o.OBJECT_NAME NOT LIKE 'MDRT_%'")
  304. c = self._execute(None, sql)
  305. for tbl in self._fetchall(c):
  306. if tablenames.count((tbl[1], tbl[0])) <= 0:
  307. item = list(tbl)
  308. item.insert(0, Table.TableType)
  309. items.append(item)
  310. c.close()
  311. self.populated = True
  312. listTables = sorted(items, key=cmp_to_key(lambda x, y: (x[1] > y[1]) - (x[1] < y[1])))
  313. if self.hasCache():
  314. self.updateCache(listTables, schema)
  315. return self.getTablesCache(schema)
  316. return listTables
  317. def getTablesCache(self, schema=None):
  318. """Get list of tables from SQLite cache."""
  319. tablenames = []
  320. items = []
  321. try:
  322. vectors = self.getVectorTablesCache(schema)
  323. for tbl in vectors:
  324. tablenames.append((tbl[2], tbl[1]))
  325. items.append(tbl)
  326. except DbError:
  327. pass
  328. if not self.allowGeometrylessTables:
  329. return sorted(items, key=cmp_to_key(lambda x, y: (x[1] > y[1]) - (x[1] < y[1])))
  330. # get all non geographic tables and views
  331. schema_where = ""
  332. if self.userTablesOnly:
  333. schema_where = "AND ownername = '{}'".format(
  334. self.user)
  335. if schema and not self.userTablesOnly:
  336. schema_where = "AND ownername = '{}'".format(
  337. schema)
  338. sql = """
  339. SELECT tablename, ownername, isview
  340. FROM "oracle_{}"
  341. WHERE geometrycolname IS '' {}
  342. ORDER BY tablename
  343. """.format(self.connName, schema_where)
  344. c = self.cache_connection.cursor()
  345. c.execute(sql)
  346. for tbl in c.fetchall():
  347. if tablenames.count((tbl[1], tbl[0])) <= 0:
  348. item = list(tbl)
  349. item.insert(0, Table.TableType)
  350. items.append(item)
  351. c.close()
  352. return sorted(items, key=cmp_to_key(lambda x, y: (x[1] > y[1]) - (x[1] < y[1])))
  353. def updateCache(self, tableList, schema=None):
  354. """Updates the SQLite cache of table list for a schema."""
  355. data = []
  356. # First, we treat the list
  357. for table in tableList:
  358. line = ()
  359. # if the table is a view bring pkCols
  360. pkCols = None
  361. if int(table[3]) == 1:
  362. pkCols = self.pkCols((schema, table[1]))
  363. # Deals with non-geographic tables
  364. if table[0] == Table.TableType:
  365. line = (table[1], table[2], int(table[3]),
  366. "",
  367. ",".join(pkCols) if pkCols else "",
  368. 100, 0, "")
  369. # Deals with vector tables
  370. elif table[0] == Table.VectorType:
  371. line = (table[1], table[2], int(table[3]),
  372. table[4],
  373. ",".join(pkCols) if pkCols else "",
  374. table[9],
  375. table[8] if table[10] == "-1" else table[10],
  376. "")
  377. else:
  378. continue
  379. data.append(line)
  380. # Then, empty the cache list
  381. sql = """
  382. DELETE FROM "oracle_{}" {}
  383. """.format(self.connName,
  384. "WHERE ownername = '{}'".format(schema) if schema else "")
  385. self.cache_connection.execute(sql)
  386. self.cache_connection.commit()
  387. # Then we insert into SQLite database
  388. sql = """
  389. INSERT INTO "oracle_{}"(tablename, ownername, isview,
  390. geometrycolname, pkcols, geomtypes, geomsrids, sql)
  391. VALUES (?, ?, ?, ?, ?, ?, ?, ?)
  392. """.format(self.connName)
  393. c = self.cache_connection.cursor()
  394. c.executemany(sql, data)
  395. c.close()
  396. self.cache_connection.commit()
  397. def singleGeomTypes(self, geomtypes, srids):
  398. """Intelligent wkbtype grouping (multi with non multi)"""
  399. if (QgsWkbTypes.Polygon in geomtypes
  400. and QgsWkbTypes.MultiPolygon in geomtypes):
  401. srids.pop(geomtypes.index(QgsWkbTypes.Polygon))
  402. geomtypes.pop(geomtypes.index(QgsWkbTypes.Polygon))
  403. if (QgsWkbTypes.Point in geomtypes
  404. and QgsWkbTypes.MultiPoint in geomtypes):
  405. srids.pop(geomtypes.index(QgsWkbTypes.Point))
  406. geomtypes.pop(geomtypes.index(QgsWkbTypes.Point))
  407. if (QgsWkbTypes.LineString in geomtypes
  408. and QgsWkbTypes.MultiLineString in geomtypes):
  409. srids.pop(geomtypes.index(QgsWkbTypes.LineString))
  410. geomtypes.pop(geomtypes.index(QgsWkbTypes.LineString))
  411. if QgsWkbTypes.Unknown in geomtypes and len(geomtypes) > 1:
  412. srids.pop(geomtypes.index(QgsWkbTypes.Unknown))
  413. geomtypes.pop(geomtypes.index(QgsWkbTypes.Unknown))
  414. return geomtypes, srids
  415. def getVectorTablesCache(self, schema=None):
  416. """Get list of table with a geometry column from SQLite cache
  417. it returns:
  418. name (table name)
  419. namespace (schema)
  420. type = 'view' (is a view?)
  421. geometry_column
  422. geometry_types (as WKB type)
  423. srids
  424. """
  425. schema_where = ""
  426. if self.userTablesOnly:
  427. schema_where = "AND ownername = '{}'".format(
  428. self.user)
  429. if schema and not self.userTablesOnly:
  430. schema_where = "AND ownername = '{}'".format(
  431. schema)
  432. sql = """
  433. SELECT tablename, ownername, isview,
  434. geometrycolname,
  435. geomtypes, geomsrids
  436. FROM "oracle_{}"
  437. WHERE geometrycolname IS NOT '' {}
  438. ORDER BY tablename
  439. """.format(self.connName, schema_where)
  440. items = []
  441. c = self.cache_connection.cursor()
  442. c.execute(sql)
  443. lst_tables = c.fetchall()
  444. c.close()
  445. # Handle multiple geometries tables
  446. for i, tbl in enumerate(lst_tables):
  447. item = list(tbl)
  448. srids = item.pop()
  449. geomtypes = item.pop()
  450. item.insert(0, Table.VectorType)
  451. if len(geomtypes) > 0 and len(srids) > 0:
  452. geomtypes = [int(l) for l in str(geomtypes).split(",")]
  453. srids = [int(l) for l in str(srids).split(",")]
  454. geomtypes, srids = self.singleGeomTypes(geomtypes, srids)
  455. for j in range(len(geomtypes)):
  456. buf = list(item)
  457. geomtype = geomtypes[j]
  458. srid = srids[j]
  459. datatype = QgsWkbTypes.displayString(QgsWkbTypes.flatType(QgsWkbTypes.singleType(geomtype)))
  460. geo = datatype.upper()
  461. buf.append(geo)
  462. buf.append(geomtype)
  463. buf.append(QgsWkbTypes.coordDimensions(geomtype)) # Dimensions
  464. buf.append(srid)
  465. buf.append(None) # To respect ORTableVector row
  466. buf.append(None) # To respect ORTableVector row
  467. items.append(buf)
  468. return items
  469. def getVectorTables(self, schema=None):
  470. """Get list of table with a geometry column
  471. it returns a table of tuples:
  472. name (table name)
  473. namespace (schema/owner)
  474. isView (is a view?)
  475. geometry_column
  476. srid
  477. """
  478. if not self.has_spatial:
  479. return []
  480. # discovery of all geographic tables
  481. prefix = "all"
  482. owner = "c.owner"
  483. where = None
  484. if not self.geometryColumnsOnly:
  485. where = "WHERE c.data_type = 'SDO_GEOMETRY'"
  486. if schema and not self.userTablesOnly:
  487. where = "{} c.owner = {}".format(
  488. "{} AND".format(where) if where else "WHERE",
  489. self.quoteString(schema))
  490. if self.userTablesOnly:
  491. prefix = "user"
  492. owner = "user As owner"
  493. if self.geometryColumnsOnly:
  494. where = ""
  495. sql = """
  496. SELECT c.table_name, {0},
  497. CASE o.OBJECT_TYPE
  498. WHEN 'VIEW' THEN 1
  499. ELSE 0 END As isView,
  500. c.column_name,
  501. {1}
  502. FROM {2}_{3} c
  503. JOIN {2}_objects o ON c.table_name = o.object_name
  504. AND o.object_type IN ('TABLE','VIEW','SYNONYM') {4} {5}
  505. ORDER BY TABLE_NAME
  506. """.format(owner,
  507. "c.srid" if self.geometryColumnsOnly
  508. else "NULL as srid",
  509. prefix,
  510. "sdo_geom_metadata" if self.geometryColumnsOnly
  511. else "tab_columns",
  512. "" if self.userTablesOnly
  513. else "AND c.owner = o.owner",
  514. where)
  515. # For each table, get all of the details
  516. items = []
  517. c = self._execute(None, sql)
  518. lst_tables = self._fetchall(c)
  519. c.close()
  520. for i, tbl in enumerate(lst_tables):
  521. item = list(tbl)
  522. detectedSrid = item.pop()
  523. if detectedSrid == NULL:
  524. detectedSrid = "-1"
  525. else:
  526. detectedSrid = int(detectedSrid)
  527. if schema:
  528. table_name = "{}.{}".format(self.quoteId(schema), self.quoteId(item[0]))
  529. else:
  530. table_name = self.quoteId(item[0])
  531. geocol = self.quoteId(item[3])
  532. geomMultiTypes, multiSrids = self.getTableGeomTypes(
  533. table_name, geocol)
  534. geomtypes = list(geomMultiTypes)
  535. srids = list(multiSrids)
  536. item.insert(0, Table.VectorType)
  537. geomtypes, srids = self.singleGeomTypes(geomtypes, srids)
  538. for j in range(len(geomtypes)):
  539. buf = list(item)
  540. geomtype = geomtypes[j]
  541. datatype = QgsWkbTypes.displayString(QgsWkbTypes.flatType(QgsWkbTypes.singleType(geomtype)))
  542. geo = datatype.upper()
  543. buf.append(geo) # Geometry type as String
  544. buf.append(geomtype) # Qgis.WkbType
  545. buf.append(QgsWkbTypes.coordDimensions(geomtype)) # Dimensions
  546. buf.append(detectedSrid) # srid
  547. if not self.onlyExistingTypes:
  548. geomMultiTypes.append(0)
  549. multiSrids.append(multiSrids[0])
  550. buf.append(",".join([str(x) for x in
  551. geomMultiTypes]))
  552. buf.append(",".join([str(x) for x in multiSrids]))
  553. items.append(buf)
  554. if self.allowGeometrylessTables and buf[-6] != "UNKNOWN":
  555. copybuf = list(buf)
  556. copybuf[4] = ""
  557. copybuf[-6] = "UNKNOWN"
  558. copybuf[-5] = QgsWkbTypes.NullGeometry
  559. copybuf[-2] = QgsWkbTypes.NullGeometry
  560. copybuf[-1] = "0"
  561. items.append(copybuf)
  562. return items
  563. def getTableComment(self, table, objectType):
  564. """Return the general comment for the object"""
  565. schema, tablename = self.getSchemaTableName(table)
  566. data_prefix = "ALL" if schema else "USER"
  567. where = "AND OWNER = {}".format(
  568. self.quoteString(schema)) if schema else ""
  569. if objectType in ["TABLE", "VIEW"]:
  570. data_table = "{}_TAB_COMMENTS"
  571. table = "TABLE"
  572. elif objectType == "MATERIALIZED VIEW":
  573. data_table = "{}_MVIEW_COMMENTS"
  574. table = "MVIEW"
  575. else:
  576. return None
  577. data_table = data_table.format(data_prefix)
  578. sql = """
  579. SELECT COMMENTS FROM {} WHERE {}_NAME = {}
  580. {}
  581. """.format(data_table, table,
  582. self.quoteString(tablename),
  583. where)
  584. c = self._execute(None, sql)
  585. res = self._fetchone(c)
  586. c.close()
  587. if res:
  588. return res[0]
  589. return None
  590. def getTableType(self, table):
  591. """Return the type of a table between the following:
  592. * Table
  593. * View
  594. * Materialized view
  595. """
  596. schema, tablename = self.getSchemaTableName(table)
  597. sql = """
  598. SELECT OBJECT_TYPE FROM {0} WHERE OBJECT_NAME = {1} {2}
  599. """
  600. if schema:
  601. sql = sql.format("ALL_OBJECTS",
  602. self.quoteString(tablename),
  603. "AND OWNER = {}".format(
  604. self.quoteString(schema)))
  605. else:
  606. sql = sql.format("USER_OBJECTS",
  607. self.quoteString(tablename),
  608. "")
  609. c = self._execute(None, sql)
  610. res = self._fetchall(c)
  611. c.close()
  612. # Analyze return values
  613. if not res:
  614. return False
  615. else:
  616. types = [x[0] for x in res]
  617. if "MATERIALIZED VIEW" in types:
  618. return "MATERIALIZED VIEW"
  619. elif "VIEW" in types:
  620. return "VIEW"
  621. else:
  622. return "TABLE"
  623. def pkCols(self, table):
  624. """Return the primary keys candidates for a view."""
  625. schema, tablename = self.getSchemaTableName(table)
  626. sql = """
  627. SELECT column_name
  628. FROM all_tab_columns
  629. WHERE owner={}
  630. AND table_name={}
  631. ORDER BY column_id
  632. """.format(self.quoteString(schema) if schema else self.user,
  633. self.quoteString(tablename))
  634. c = self._execute(None, sql)
  635. res = self._fetchall(c)
  636. c.close()
  637. return [x[0] for x in res] if res else None
  638. def getTableGeomTypes(self, table, geomCol):
  639. """Return all the wkbTypes for a table by requesting geometry
  640. column.
  641. """
  642. estimated = ""
  643. if self.useEstimatedMetadata:
  644. estimated = "AND ROWNUM < 100"
  645. # Grab all of geometry types from the layer
  646. query = """
  647. SELECT DISTINCT a.{0}.SDO_GTYPE As gtype,
  648. a.{0}.SDO_SRID
  649. FROM {1} a
  650. WHERE a.{0} IS NOT NULL {2}
  651. ORDER BY a.{0}.SDO_GTYPE
  652. """.format(geomCol, table, estimated)
  653. try:
  654. c = self._execute(None, query)
  655. except DbError: # handle error views or other problems
  656. return [QgsWkbTypes.Unknown], [-1]
  657. rows = self._fetchall(c)
  658. c.close()
  659. # Handle results
  660. if len(rows) == 0:
  661. return [QgsWkbTypes.Unknown], [-1]
  662. # A dict to store the geomtypes
  663. geomtypes = []
  664. srids = []
  665. for row in rows:
  666. if row[1] == NULL:
  667. srids.append(-1)
  668. else:
  669. srids.append(int(row[1]))
  670. if int(row[0]) in list(OracleDBConnector.ORGeomTypes.keys()):
  671. geomtypes.append(OracleDBConnector.ORGeomTypes[int(row[0])])
  672. else:
  673. geomtypes.append(QgsWkbTypes.Unknown)
  674. return geomtypes, srids
  675. def getTableMainGeomType(self, table, geomCol):
  676. """Return the best wkbType for a table by requesting geometry
  677. column.
  678. """
  679. geomTypes, srids = self.getTableGeomTypes(table, geomCol)
  680. # Make the decision:
  681. wkbType = QgsWkbTypes.Unknown
  682. srid = -1
  683. order = [QgsWkbTypes.MultiPolygon25D, QgsWkbTypes.Polygon25D,
  684. QgsWkbTypes.MultiPolygon, QgsWkbTypes.Polygon,
  685. QgsWkbTypes.MultiLineString25D, QgsWkbTypes.LineString25D,
  686. QgsWkbTypes.MultiLineString, QgsWkbTypes.LineString,
  687. QgsWkbTypes.MultiPoint25D, QgsWkbTypes.Point25D,
  688. QgsWkbTypes.MultiPoint, QgsWkbTypes.Point]
  689. for geomType in order:
  690. if geomType in geomTypes:
  691. wkbType = geomType
  692. srid = srids[geomTypes.index(geomType)]
  693. break
  694. return wkbType, srid
  695. def getTableRowEstimation(self, table):
  696. """ Find the estimated number of rows of a table. """
  697. schema, tablename = self.getSchemaTableName(table)
  698. prefix = "ALL" if schema else "USER"
  699. where = "AND OWNER = {}".format(
  700. self.quoteString(schema)) if schema else ""
  701. sql = """
  702. SELECT NUM_ROWS FROM {}_ALL_TABLES
  703. WHERE TABLE_NAME = {}
  704. {}
  705. """.format(prefix, self.quoteString(tablename), where)
  706. c = self._execute(None, sql)
  707. res = self._fetchone(c)
  708. c.close()
  709. if not res or res[0] == NULL:
  710. return 0
  711. else:
  712. return int(res[0])
  713. def getTableDates(self, table):
  714. """ Returns the modification/creation dates of an object"""
  715. schema, tablename = self.getSchemaTableName(table)
  716. prefix = "ALL" if schema else "USER"
  717. where = "AND OWNER = {}".format(
  718. self.quoteString(schema)) if schema else ""
  719. sql = """
  720. SELECT CREATED, LAST_DDL_TIME FROM {}_OBJECTS
  721. WHERE OBJECT_NAME = {}
  722. {}
  723. """.format(prefix, self.quoteString(tablename), where)
  724. c = self._execute(None, sql)
  725. res = self._fetchone(c)
  726. c.close()
  727. if not res:
  728. return None, None
  729. return res[0], res[1]
  730. def getTableRowCount(self, table):
  731. """Returns the number of rows of the table."""
  732. c = self._execute(
  733. None, "SELECT COUNT(*) FROM {}".format(self.quoteId(table)))
  734. res = self._fetchone(c)[0]
  735. c.close()
  736. return res
  737. def getTableFields(self, table):
  738. """Returns list of columns in table."""
  739. schema, tablename = self.getSchemaTableName(table)
  740. schema_where = " AND a.OWNER={}".format(
  741. self.quoteString(schema) if schema else "")
  742. sql = """
  743. SELECT a.COLUMN_ID As ordinal_position,
  744. a.COLUMN_NAME As column_name,
  745. a.DATA_TYPE As data_type,
  746. CASE a.DATA_TYPE
  747. WHEN 'NUMBER' THEN a.DATA_PRECISION
  748. ELSE a.DATA_LENGTH END As char_max_len,
  749. a.DATA_SCALE As modifier,
  750. a.NULLABLE As nullable,
  751. a.DEFAULT_LENGTH As hasdefault,
  752. a.DATA_DEFAULT As default_value,
  753. a.DATA_TYPE As formatted_type,
  754. c.COMMENTS
  755. FROM ALL_TAB_COLUMNS a
  756. JOIN ALL_COL_COMMENTS c ON
  757. a.TABLE_NAME = c.TABLE_NAME
  758. AND a.COLUMN_NAME = c.COLUMN_NAME
  759. AND a.OWNER = c.OWNER
  760. WHERE a.TABLE_NAME = {} {}
  761. ORDER BY a.COLUMN_ID
  762. """.format(self.quoteString(tablename), schema_where)
  763. c = self._execute(None, sql)
  764. res = self._fetchall(c)
  765. c.close()
  766. return res
  767. def getSpatialFields(self, table):
  768. """Returns the list of geometric columns"""
  769. fields = self.getTableFields(table)
  770. geomFields = []
  771. for field in fields:
  772. if field[2] == "SDO_GEOMETRY":
  773. geomFields.append(field[1])
  774. return geomFields
  775. def getTableIndexes(self, table):
  776. """Get info about table's indexes."""
  777. schema, tablename = self.getSchemaTableName(table)
  778. schema_where = " AND i.OWNER = {} ".format(
  779. self.quoteString(schema) if schema else "")
  780. sql = """
  781. SELECT i.INDEX_NAME, c.COLUMN_NAME, i.ITYP_NAME,
  782. i.STATUS, i.LAST_ANALYZED, i.COMPRESSION,
  783. i.UNIQUENESS
  784. FROM ALL_INDEXES i
  785. INNER JOIN ALL_IND_COLUMNS c ON i.index_name = c.index_name
  786. WHERE i.table_name = {} {}
  787. """.format(self.quoteString(tablename), schema_where)
  788. c = self._execute(None, sql)
  789. res = self._fetchall(c)
  790. c.close()
  791. return res
  792. def getMViewInfo(self, table):
  793. """Find some information about materialized views"""
  794. schema, tablename = self.getSchemaTableName(table)
  795. where = " AND a.OWNER = {} ".format(
  796. self.quoteString(schema)) if schema else ""
  797. prefix = "ALL" if schema else "USER"
  798. sql = """
  799. SELECT a.REFRESH_MODE,
  800. a.REFRESH_METHOD, a.BUILD_MODE, a.FAST_REFRESHABLE,
  801. a.LAST_REFRESH_TYPE, a.LAST_REFRESH_DATE, a.STALENESS,
  802. a.STALE_SINCE, a.COMPILE_STATE, a.USE_NO_INDEX
  803. FROM {}_MVIEWS a
  804. WHERE MVIEW_NAME = {}
  805. {}
  806. """.format(prefix, self.quoteString(tablename), where)
  807. c = self._execute(None, sql)
  808. res = self._fetchone(c)
  809. c.close()
  810. return res
  811. def getTableConstraints(self, table):
  812. """Find all the constraints for a table."""
  813. schema, tablename = self.getSchemaTableName(table)
  814. schema_where = " AND c.OWNER={} ".format(
  815. self.quoteString(schema)) if schema else ""
  816. sql = """
  817. SELECT a.CONSTRAINT_NAME, a.CONSTRAINT_TYPE,
  818. c.COLUMN_NAME, a.VALIDATED, a.GENERATED, a.STATUS,
  819. a.SEARCH_CONDITION, a.DELETE_RULE,
  820. CASE WHEN b.TABLE_NAME IS NULL THEN NULL
  821. ELSE b.OWNER || '.' || b.TABLE_NAME END
  822. As F_TABLE, b.COLUMN_NAME As F_COLUMN
  823. FROM ALL_CONS_COLUMNS c
  824. INNER JOIN ALL_CONSTRAINTS a ON
  825. a.CONSTRAINT_NAME = c.CONSTRAINT_NAME
  826. LEFT OUTER JOIN ALL_CONS_COLUMNS b ON
  827. b.CONSTRAINT_NAME = a.R_CONSTRAINT_NAME
  828. AND a.R_OWNER = b.OWNER
  829. AND b.POSITION = c.POSITION
  830. WHERE c.TABLE_NAME = {} {}
  831. """.format(self.quoteString(tablename), schema_where)
  832. c = self._execute(None, sql)
  833. res = self._fetchall(c)
  834. c.close()
  835. return res
  836. def getTableTriggers(self, table):
  837. """Find all the triggers of the table."""
  838. schema, tablename = self.getSchemaTableName(table)
  839. sql = """
  840. SELECT TRIGGER_NAME, TRIGGERING_EVENT, TRIGGER_TYPE, STATUS
  841. FROM ALL_TRIGGERS
  842. WHERE TABLE_OWNER = {}
  843. AND TABLE_NAME = {}
  844. """.format(self.quoteString(schema), self.quoteString(tablename))
  845. c = self._execute(None, sql)
  846. res = self._fetchall(c)
  847. c.close()
  848. return res
  849. def enableAllTableTriggers(self, enable, table):
  850. """Enable or disable all triggers on table."""
  851. triggers = [l[0] for l in self.getTableTriggers(table)]
  852. for trigger in triggers:
  853. self.enableTableTrigger(trigger, enable, table)
  854. def enableTableTrigger(self, trigger, enable, table):
  855. """Enable or disable one trigger on table."""
  856. schema, tablename = self.getSchemaTableName(table)
  857. trigger = ".".join([self.quoteId(schema), self.quoteId(trigger)])
  858. sql = "ALTER TRIGGER {} {}".format(trigger, "ENABLE" if enable else "DISABLE")
  859. self._execute_and_commit(sql)
  860. def deleteTableTrigger(self, trigger, table):
  861. """Deletes the trigger on a table."""
  862. schema, tablename = self.getSchemaTableName(table)
  863. trigger = ".".join([self.quoteId(schema), self.quoteId(trigger)])
  864. sql = "DROP TRIGGER {}".format(trigger)
  865. self._execute_and_commit(sql)
  866. def canUpdateMetadata(self, table):
  867. """Verify if user can update metadata table
  868. returns False or metadata table name.
  869. """
  870. schema, tablename = self.getSchemaTableName(table)
  871. metadata = False
  872. # User can only update in USER_SDO_GEOM_METADATA
  873. if self.getRawTablePrivileges('USER_SDO_GEOM_METADATA', 'MDSYS',
  874. 'PUBLIC')[2]:
  875. tbQuery = """
  876. SELECT COUNT(*) FROM USER_SDO_GEOM_METADATA
  877. WHERE TABLE_NAME = {}
  878. """.format(self.quoteString(tablename))
  879. c = self._execute(None, tbQuery)
  880. res = self._fetchone(c)
  881. c.close()
  882. if res:
  883. if res[0] > 0:
  884. metadata = True
  885. return metadata
  886. def getTableExtent(self, table, geom):
  887. """Calculate the real table extent."""
  888. schema, tablename = self.getSchemaTableName(table)
  889. tableQuote = "'{}.{}'".format(schema, tablename)
  890. # Extent calculation without spatial index
  891. extentFunction = """SDO_AGGR_MBR("{}")""".format(geom)
  892. fromTable = '"{}"."{}"'.format(schema, tablename)
  893. # if table as spatial index:
  894. indexes = self.getTableIndexes(table)
  895. if indexes:
  896. if "SPATIAL_INDEX" in [f[2] for f in indexes]:
  897. extentFunction = "SDO_TUNE.EXTENT_OF({}, {})".format(
  898. tableQuote, self.quoteString(geom))
  899. fromTable = "DUAL"
  900. sql = """
  901. SELECT
  902. SDO_GEOM.SDO_MIN_MBR_ORDINATE({0}, 1),
  903. SDO_GEOM.SDO_MIN_MBR_ORDINATE({0}, 2),
  904. SDO_GEOM.SDO_MAX_MBR_ORDINATE({0}, 1),
  905. SDO_GEOM.SDO_MAX_MBR_ORDINATE({0}, 2)
  906. FROM {1}
  907. """.format(extentFunction, fromTable)
  908. try:
  909. c = self._execute(None, sql)
  910. except DbError: # no spatial index on table, try aggregation
  911. return None
  912. res = self._fetchone(c)
  913. c.close()
  914. if not res:
  915. res = None
  916. return res if res else None
  917. def getTableEstimatedExtent(self, table, geom):
  918. """Find out estimated extent (from metadata view)."""
  919. res = []
  920. schema, tablename = self.getSchemaTableName(table)
  921. where = """
  922. WHERE TABLE_NAME = {}
  923. AND COLUMN_NAME = {}
  924. """.format(self.quoteString(tablename),
  925. self.quoteString(geom))
  926. if schema:
  927. where = "{} AND OWNER = {}".format(
  928. where, self.quoteString(schema))
  929. request = """
  930. SELECT SDO_LB, SDO_UB
  931. FROM ALL_SDO_GEOM_METADATA m,
  932. TABLE(m.DIMINFO)
  933. {0}
  934. AND SDO_DIMNAME = '{1}'
  935. """
  936. for dimension in ["X", "Y"]:
  937. sql = request.format(where, dimension)
  938. try:
  939. c = self._execute(None, sql)
  940. except DbError: # no statistics for the current table
  941. return None
  942. res_d = self._fetchone(c)
  943. c.close()
  944. if not res_d or len(res_d) < 2:
  945. return None
  946. elif res_d[0] == NULL:
  947. return None
  948. else:
  949. res.extend(res_d)
  950. return [res[0], res[2], res[1], res[3]]
  951. def getDefinition(self, view, objectType):
  952. """Returns definition of the view."""
  953. schema, tablename = self.getSchemaTableName(view)
  954. where = ""
  955. if schema:
  956. where = " AND OWNER={} ".format(
  957. self.quoteString(schema))
  958. # Query to grab a view definition
  959. if objectType == "VIEW":
  960. sql = """
  961. SELECT TEXT FROM ALL_VIEWS WHERE VIEW_NAME = {} {}
  962. """.format(self.quoteString(tablename), where)
  963. elif objectType == "MATERIALIZED VIEW":
  964. sql = """
  965. SELECT QUERY FROM ALL_MVIEWS WHERE MVIEW_NAME = {} {}
  966. """.format(self.quoteString(tablename), where)
  967. else:
  968. return None
  969. c = self._execute(None, sql)
  970. res = self._fetchone(c)
  971. c.close()
  972. return res[0] if res else None
  973. def getSpatialRefInfo(self, srid):
  974. """Returns human name from an srid as describe in Oracle sys
  975. table.
  976. """
  977. if not self.has_spatial:
  978. return
  979. try:
  980. c = self._execute(
  981. None,
  982. ("SELECT CS_NAME FROM MDSYS.CS_SRS WHERE"
  983. " SRID = {}".format(srid)))
  984. except DbError:
  985. return
  986. sr = self._fetchone(c)
  987. c.close()
  988. return sr[0] if sr else None
  989. def isVectorTable(self, table):
  990. """Determine if a table is a vector one by looking into
  991. metadata view.
  992. """
  993. if self.has_geometry_columns and self.has_geometry_columns_access:
  994. schema, tablename = self.getSchemaTableName(table)
  995. where = "WHERE TABLE_NAME = {}".format(
  996. self.quoteString(tablename))
  997. if schema:
  998. where = "{} AND OWNER = {}".format(where,
  999. self.quoteString(schema))
  1000. sql = """
  1001. SELECT COUNT(*)
  1002. FROM ALL_SDO_GEOM_METADATA
  1003. {}
  1004. """.format(where)
  1005. c = self._execute(None, sql)
  1006. res = self._fetchone(c)
  1007. c.close()
  1008. return res is not None and res[0] > 0
  1009. return False
  1010. def createTable(self, table, field_defs, pkey):
  1011. """Creates ordinary table
  1012. 'fields' is array containing field definitions
  1013. 'pkey' is the primary key name
  1014. """
  1015. if len(field_defs) == 0:
  1016. return False
  1017. sql = "CREATE TABLE {} (".format(self.quoteId(table))
  1018. sql += ", ".join(field_defs)
  1019. if pkey:
  1020. sql += ", PRIMARY KEY ({})".format(self.quoteId(pkey))
  1021. sql += ")"
  1022. self._execute_and_commit(sql)
  1023. return True
  1024. def deleteTable(self, table):
  1025. """Deletes table and its reference in sdo_geom_metadata."""
  1026. schema, tablename = self.getSchemaTableName(table)
  1027. if self.isVectorTable(table):
  1028. self.deleteMetadata(table)
  1029. sql = "DROP TABLE {}".format(self.quoteId(table))
  1030. self._execute_and_commit(sql)
  1031. def emptyTable(self, table):
  1032. """Deletes all the rows of a table."""
  1033. sql = "TRUNCATE TABLE {}".format(self.quoteId(table))
  1034. self._execute_and_commit(sql)
  1035. def renameTable(self, table, new_table):
  1036. """Renames a table inside the database."""
  1037. schema, tablename = self.getSchemaTableName(table)
  1038. if new_table == tablename:
  1039. return
  1040. c = self._get_cursor()
  1041. # update geometry_columns if Spatial is enabled
  1042. if self.isVectorTable(table):
  1043. self.updateMetadata(table, None, new_table=new_table)
  1044. sql = "RENAME {} TO {}".format(
  1045. self.quoteId(tablename), self.quoteId(new_table))
  1046. self._execute(c, sql)
  1047. self._commit()
  1048. def createView(self, view, query):
  1049. """Creates a view as defined."""
  1050. sql = "CREATE VIEW {} AS {}".format(self.quoteId(view), query)
  1051. self._execute_and_commit(sql)
  1052. def createSpatialView(self, view, query):
  1053. """Creates a spatial view and update metadata table."""
  1054. # What is the view name ?
  1055. if len(view.split(".")) > 1:
  1056. schema, view = view.split(".")
  1057. else:
  1058. schema = self.user
  1059. view = (schema, view)
  1060. # First create the view
  1061. self.createView(view, query)
  1062. # Grab the geometric column(s)
  1063. fields = self.getSpatialFields(view)
  1064. if not fields:
  1065. return False
  1066. for geoCol in fields:
  1067. # Grab SRID
  1068. geomTypes, srids = self.getTableGeomTypes(view, geoCol)
  1069. # Calculate the extent
  1070. extent = self.getTableExtent(view, geoCol)
  1071. # Insert information into metadata table
  1072. self.insertMetadata(view, geoCol, extent, srids[0])
  1073. return True
  1074. def deleteView(self, view):
  1075. """Deletes a view."""
  1076. schema, tablename = self.getSchemaTableName(view)
  1077. if self.isVectorTable(view):
  1078. self.deleteMetadata(view)
  1079. sql = "DROP VIEW {}".format(self.quoteId(view))
  1080. self._execute_and_commit(sql)
  1081. def createSchema(self, schema):
  1082. """Creates a new empty schema in database."""
  1083. # Not tested
  1084. sql = "CREATE SCHEMA AUTHORIZATION {}".format(
  1085. self.quoteId(schema))
  1086. self._execute_and_commit(sql)
  1087. def deleteSchema(self, schema):
  1088. """Drops (empty) schema from database."""
  1089. sql = "DROP USER {} CASCADE".format(self.quoteId(schema))
  1090. self._execute_and_commit(sql)
  1091. def renameSchema(self, schema, new_schema):
  1092. """Renames a schema in the database."""
  1093. # Unsupported in Oracle
  1094. pass
  1095. def addTableColumn(self, table, field_def):
  1096. """Adds a column to a table."""
  1097. sql = "ALTER TABLE {} ADD {}".format(self.quoteId(table), field_def)
  1098. self._execute_and_commit(sql)
  1099. def deleteTableColumn(self, table, column):
  1100. """Deletes column from a table."""
  1101. # Delete all the constraints for this column
  1102. constraints = [f[0] for f in self.getTableConstraints(table)
  1103. if f[2] == column]
  1104. for constraint in constraints:
  1105. self.deleteTableConstraint(table, constraint)
  1106. # Delete all the indexes for this column
  1107. indexes = [f[0] for f in self.getTableIndexes(table) if f[1] == column]
  1108. for ind in indexes:
  1109. self.deleteTableIndex(table, ind)
  1110. # Delete metadata is we have a geo column
  1111. if self.isGeometryColumn(table, column):
  1112. self.deleteMetadata(table, column)
  1113. sql = "ALTER TABLE {} DROP COLUMN {}".format(
  1114. self.quoteId(table), self.quoteId(column))
  1115. self._execute_and_commit(sql)
  1116. def updateTableColumn(self, table, column, new_name=None,
  1117. data_type=None, not_null=None,
  1118. default=None, comment=None):
  1119. """Updates properties of a column in a table."""
  1120. schema, tablename = self.getSchemaTableName(table)
  1121. c = self._get_cursor()
  1122. # update column definition
  1123. col_actions = []
  1124. if data_type:
  1125. col_actions.append("{}".format(data_type))
  1126. if default:
  1127. col_actions.append("DEFAULT {}".format(default))
  1128. else:
  1129. col_actions.append("DEFAULT NULL")
  1130. if not_null:
  1131. col_actions.append("NOT NULL")
  1132. if not_null is None:
  1133. col_actions.append("NULL")
  1134. if col_actions:
  1135. sql = "ALTER TABLE {} MODIFY ( {} {} )".format(
  1136. self.quoteId(table), self.quoteId(column),
  1137. " ".join(col_actions))
  1138. self._execute(c, sql)
  1139. # rename the column
  1140. if new_name and new_name != column:
  1141. isGeo = self.isGeometryColumn(table, column)
  1142. sql = "ALTER TABLE {} RENAME COLUMN {} TO {}".format(
  1143. self.quoteId(table), self.quoteId(column),
  1144. self.quoteId(new_name))
  1145. self._execute(c, sql)
  1146. # update geometry_columns if Spatial is enabled
  1147. if isGeo:
  1148. self.updateMetadata(table, column, new_name)
  1149. self._commit()
  1150. def renameTableColumn(self, table, column, new_name):
  1151. """Renames column in a table."""
  1152. return self.updateTableColumn(table, column, new_name)
  1153. def setTableColumnType(self, table, column, data_type):
  1154. """Changes column type."""
  1155. return self.updateTableColumn(table, column, None, data_type)
  1156. def setTableColumnNull(self, table, column, is_null):
  1157. """Changes whether column can contain null values."""
  1158. return self.updateTableColumn(table, column, None, None, not is_null)
  1159. def setTableColumnDefault(self, table, column, default):
  1160. """Changes column's default value.
  1161. If default=None or an empty string drop default value.
  1162. """
  1163. return self.updateTableColumn(table, column, None, None, None, default)
  1164. def isGeometryColumn(self, table, column):
  1165. """Find if a column is geometric."""
  1166. schema, tablename = self.getSchemaTableName(table)
  1167. prefix = "ALL" if schema else "USER"
  1168. where = "AND owner = {} ".format(
  1169. self.quoteString(schema)) if schema else ""
  1170. sql = """
  1171. SELECT COUNT(*)
  1172. FROM {}_SDO_GEOM_METADATA
  1173. WHERE TABLE_NAME = {}
  1174. AND COLUMN_NAME = {} {}
  1175. """.format(prefix, self.quoteString(tablename),
  1176. self.quoteString(column.upper()), where)
  1177. c = self._execute(None, sql)
  1178. res = self._fetchone(c)[0] > 0
  1179. c.close()
  1180. return res
  1181. def refreshMView(self, table):
  1182. """Refreshes an MVIEW"""
  1183. schema, tablename = self.getSchemaTableName(table)
  1184. mview = "{}.{}".format(schema, tablename) if schema else tablename
  1185. sql = """
  1186. BEGIN
  1187. DBMS_MVIEW.REFRESH({},'?');
  1188. END;
  1189. """.format(self.quoteString(mview))
  1190. self._execute_and_commit(sql)
  1191. def deleteMetadata(self, table, geom_column=None):
  1192. """Deletes the metadata entry for a table"""
  1193. schema, tablename = self.getSchemaTableName(table)
  1194. if not (self.getRawTablePrivileges('USER_SDO_GEOM_METADATA',
  1195. 'MDSYS',
  1196. 'PUBLIC')[3] and
  1197. schema == self.user):
  1198. return False
  1199. where = "WHERE TABLE_NAME = {}".format(self.quoteString(tablename))
  1200. if geom_column:
  1201. where = ("{} AND COLUMN_NAME = "
  1202. "{}".format(where,
  1203. self.quoteString(geom_column)))
  1204. sql = "DELETE FROM USER_SDO_GEOM_METADATA {}".format(where)
  1205. self._execute_and_commit(sql)
  1206. def updateMetadata(self, table, geom_column, new_geom_column=None,
  1207. new_table=None, extent=None, srid=None):
  1208. """Updates the metadata table with the new information"""
  1209. schema, tablename = self.getSchemaTableName(table)
  1210. if not (self.getRawTablePrivileges('USER_SDO_GEOM_METADATA',
  1211. 'MDSYS',
  1212. 'PUBLIC')[2] and
  1213. schema == self.user):
  1214. return False
  1215. where = "WHERE TABLE_NAME = {}".format(self.quoteString(tablename))
  1216. if geom_column:
  1217. # in Metadata view, geographic column is always in uppercase
  1218. where = ("{} AND COLUMN_NAME = "
  1219. "{}".format(where,
  1220. self.quoteString(geom_column.upper())))
  1221. update = "SET"
  1222. if srid == 0:
  1223. srid = -1
  1224. if srid:
  1225. update = "{} SRID = {}".format(update, srid)
  1226. if extent:
  1227. if len(extent) == 4:
  1228. if update != "SET":
  1229. update = "{},".format(update)
  1230. update = """{4} DIMINFO = MDSYS.SDO_DIM_ARRAY(
  1231. MDSYS.SDO_DIM_ELEMENT('X', {0:.9f}, {1:.9f}, 0.005),
  1232. MDSYS.SDO_DIM_ELEMENT('Y', {2:.9f}, {3:.9f}, 0.005))
  1233. """.format(extent[0], extent[2], extent[1],
  1234. extent[3], update)
  1235. if new_geom_column:
  1236. if update != "SET":
  1237. update = "{},".format(update)
  1238. # in Metadata view, geographic column is always in uppercase
  1239. update = ("{} COLUMN_NAME = "
  1240. "{}".format(update,
  1241. self.quoteString(new_geom_column.upper())))
  1242. if new_table:
  1243. if update != "SET":
  1244. update = "{},".format(update)
  1245. update = ("{} TABLE_NAME = "
  1246. "{}".format(update,
  1247. self.quoteString(new_table)))
  1248. sql = "UPDATE USER_SDO_GEOM_METADATA {} {}".format(update, where)
  1249. self._execute_and_commit(sql)
  1250. def insertMetadata(self, table, geom_column, extent, srid, dim=2):
  1251. """Inserts a line for the table in Oracle Metadata table."""
  1252. schema, tablename = self.getSchemaTableName(table)
  1253. if not (self.getRawTablePrivileges('USER_SDO_GEOM_METADATA',
  1254. 'MDSYS',
  1255. 'PUBLIC')[1] and
  1256. schema == self.user):
  1257. return False
  1258. # in Metadata view, geographic column is always in uppercase
  1259. geom_column = geom_column.upper()
  1260. if srid == 0:
  1261. srid = -1
  1262. if len(extent) != 4:
  1263. return False
  1264. dims = ['X', 'Y', 'Z', 'T']
  1265. extentParts = []
  1266. for i in range(dim):
  1267. extentParts.append(
  1268. """MDSYS.SDO_DIM_ELEMENT(
  1269. '{}', {:.9f}, {:.9f}, 0.005)""".format(dims[i], extent[i], extent[i + 1]))
  1270. extentParts = ",".join(extentParts)
  1271. sqlExtent = """MDSYS.SDO_DIM_ARRAY(
  1272. {})
  1273. """.format(extentParts)
  1274. sql = """
  1275. INSERT INTO USER_SDO_GEOM_METADATA (TABLE_NAME,
  1276. COLUMN_NAME, DIMINFO,
  1277. SRID)
  1278. VALUES({}, {},
  1279. {},
  1280. {})
  1281. """.format(self.quoteString(tablename),
  1282. self.quoteString(geom_column),
  1283. sqlExtent, str(srid))
  1284. self._execute_and_commit(sql)
  1285. def addGeometryColumn(self, table, geom_column='GEOM',
  1286. geom_type=None, srid=-1, dim=2):
  1287. """Adds a geometry column and update Oracle Spatial
  1288. metadata.
  1289. """
  1290. schema, tablename = self.getSchemaTableName(table)
  1291. # in Metadata view, geographic column is always in uppercase
  1292. geom_column = geom_column.upper()
  1293. # Add the column to the table
  1294. sql = "ALTER TABLE {} ADD {} SDO_GEOMETRY".format(
  1295. self.quoteId(table), self.quoteId(geom_column))
  1296. self._execute_and_commit(sql)
  1297. # Then insert the metadata
  1298. extent = []
  1299. for i in range(dim):
  1300. extent.extend([-100000, 10000])
  1301. self.insertMetadata(table, geom_column,
  1302. [-100000, 100000, -10000, 10000],
  1303. srid, dim)
  1304. def deleteGeometryColumn(self, table, geom_column):
  1305. """Deletes a geometric column."""
  1306. return self.deleteTableColumn(table, geom_column)
  1307. def addTableUniqueConstraint(self, table, column):
  1308. """Adds a unique constraint to a table."""
  1309. sql = "ALTER TABLE {} ADD UNIQUE ({})".format(
  1310. self.quoteId(table), self.quoteId(column))
  1311. self._execute_and_commit(sql)
  1312. def deleteTableConstraint(self, table, constraint):
  1313. """Deletes constraint in a table."""
  1314. sql = "ALTER TABLE {} DROP CONSTRAINT {}".format(
  1315. self.quoteId(table), self.quoteId(constraint))
  1316. self._execute_and_commit(sql)
  1317. def addTablePrimaryKey(self, table, column):
  1318. """Adds a primary key (with one column) to a table."""
  1319. sql = "ALTER TABLE {} ADD PRIMARY KEY ({})".format(
  1320. self.quoteId(table), self.quoteId(column))
  1321. self._execute_and_commit(sql)
  1322. def createTableIndex(self, table, name, column):
  1323. """Creates index on one column using default options."""
  1324. sql = "CREATE INDEX {} ON {} ({})".format(
  1325. self.quoteId(name), self.quoteId(table),
  1326. self.quoteId(column))
  1327. self._execute_and_commit(sql)
  1328. def rebuildTableIndex(self, table, name):
  1329. """Rebuilds a table index"""
  1330. schema, tablename = self.getSchemaTableName(table)
  1331. sql = "ALTER INDEX {} REBUILD".format(self.quoteId((schema, name)))
  1332. self._execute_and_commit(sql)
  1333. def deleteTableIndex(self, table, name):
  1334. """Deletes an index on a table."""
  1335. schema, tablename = self.getSchemaTableName(table)
  1336. sql = "DROP INDEX {}".format(self.quoteId((schema, name)))
  1337. self._execute_and_commit(sql)
  1338. def createSpatialIndex(self, table, geom_column='GEOM'):
  1339. """Creates a spatial index on a geometric column."""
  1340. geom_column = geom_column.upper()
  1341. schema, tablename = self.getSchemaTableName(table)
  1342. idx_name = self.quoteId("sidx_{}_{}".format(tablename, geom_column))
  1343. sql = """
  1344. CREATE INDEX {}
  1345. ON {}({})
  1346. INDEXTYPE IS MDSYS.SPATIAL_INDEX
  1347. """.format(idx_name, self.quoteId(table),
  1348. self.quoteId(geom_column))
  1349. self._execute_and_commit(sql)
  1350. def deleteSpatialIndex(self, table, geom_column='GEOM'):
  1351. """Deletes a spatial index of a geometric column."""
  1352. schema, tablename = self.getSchemaTableName(table)
  1353. idx_name = self.quoteId("sidx_{}_{}".format(tablename, geom_column))
  1354. return self.deleteTableIndex(table, idx_name)
  1355. def execution_error_types(self):
  1356. return QtSqlDB.ExecError
  1357. def connection_error_types(self):
  1358. return QtSqlDB.ConnectionError
  1359. def error_types(self):
  1360. return self.connection_error_types(), self.execution_error_types()
  1361. def _close_cursor(self, c):
  1362. """new implementation of _close_cursor (because c.closed is
  1363. psycopg2 specific and not DB API 2.0
  1364. """
  1365. try:
  1366. if c:
  1367. c.close()
  1368. except self.error_types():
  1369. pass
  1370. return
  1371. # moved into the parent class: DbConnector._execute()
  1372. # def _execute(self, cursor, sql):
  1373. # pass
  1374. # moved into the parent class: DbConnector._execute_and_commit()
  1375. # def _execute_and_commit(self, sql):
  1376. # pass
  1377. # moved into the parent class: DbConnector._get_cursor()
  1378. # def _get_cursor(self, name=None):
  1379. # pass
  1380. # moved into the parent class: DbConnector._fetchall()
  1381. # def _fetchall(self, c):
  1382. # pass
  1383. # moved into the parent class: DbConnector._fetchone()
  1384. # def _fetchone(self, c):
  1385. # pass
  1386. # moved into the parent class: DbConnector._commit()
  1387. # def _commit(self):
  1388. # pass
  1389. # moved into the parent class: DbConnector._rollback()
  1390. # def _rollback(self):
  1391. # pass
  1392. # moved into the parent class: DbConnector._get_cursor_columns()
  1393. # def _get_cursor_columns(self, c):
  1394. # pass
  1395. def getSqlDictionary(self):
  1396. """Returns the dictionary for SQL dialog."""
  1397. from .sql_dictionary import getSqlDictionary
  1398. sql_dict = getSqlDictionary()
  1399. # get schemas, tables and field names
  1400. items = []
  1401. # First look into the cache if available
  1402. if self.hasCache():
  1403. sql = """
  1404. SELECT DISTINCT tablename FROM "oracle_{0}"
  1405. UNION
  1406. SELECT DISTINCT ownername FROM "oracle_{0}"
  1407. """.format(self.connName)
  1408. if self.userTablesOnly:
  1409. sql = """
  1410. SELECT DISTINCT tablename
  1411. FROM "oracle_{conn}" WHERE ownername = '{user}'
  1412. UNION
  1413. SELECT DISTINCT ownername
  1414. FROM "oracle_{conn}" WHERE ownername = '{user}'
  1415. """.format(conn=self.connName, user=self.user)
  1416. c = self.cache_connection.cursor()
  1417. c.execute(sql)
  1418. for row in c.fetchall():
  1419. items.append(row[0])
  1420. c.close()
  1421. if self.hasCache():
  1422. sql = """
  1423. SELECT DISTINCT COLUMN_NAME FROM {}_TAB_COLUMNS
  1424. """.format("USER" if self.userTablesOnly else
  1425. "ALL")
  1426. elif self.userTablesOnly:
  1427. sql = """
  1428. SELECT DISTINCT TABLE_NAME FROM USER_ALL_TABLES
  1429. UNION
  1430. SELECT USER FROM DUAL
  1431. UNION
  1432. SELECT DISTINCT COLUMN_NAME FROM USER_TAB_COLUMNS
  1433. """
  1434. else:
  1435. sql = """
  1436. SELECT TABLE_NAME FROM ALL_ALL_TABLES
  1437. UNION
  1438. SELECT DISTINCT OWNER FROM ALL_ALL_TABLES
  1439. UNION
  1440. SELECT DISTINCT COLUMN_NAME FROM ALL_TAB_COLUMNS
  1441. """
  1442. c = self._execute(None, sql)
  1443. for row in self._fetchall(c):
  1444. items.append(row[0])
  1445. c.close()
  1446. sql_dict["identifier"] = items
  1447. return sql_dict
  1448. def getQueryBuilderDictionary(self):
  1449. from .sql_dictionary import getQueryBuilderDictionary
  1450. return getQueryBuilderDictionary()
  1451. def cancel(self):
  1452. # how to cancel an Oracle query?
  1453. pass