data_model.py 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  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.QtCore import QTime
  22. from qgis.core import QgsMessageLog
  23. from ..data_model import (TableDataModel,
  24. SqlResultModel,
  25. SqlResultModelAsync,
  26. SqlResultModelTask,
  27. BaseTableModel)
  28. from ..plugin import DbError
  29. from ..plugin import BaseError
  30. class ORTableDataModel(TableDataModel):
  31. def __init__(self, table, parent=None):
  32. self.cursor = None
  33. TableDataModel.__init__(self, table, parent)
  34. if not self.table.rowCount:
  35. self.table.refreshRowCount()
  36. self.table.aboutToChange.connect(self._deleteCursor)
  37. self._createCursor()
  38. def _createCursor(self):
  39. fields_txt = ", ".join(self.fields)
  40. table_txt = self.db.quoteId(
  41. (self.table.schemaName(), self.table.name))
  42. self.cursor = self.db._get_cursor()
  43. sql = "SELECT {} FROM {}".format(fields_txt, table_txt)
  44. self.db._execute(self.cursor, sql)
  45. def _sanitizeTableField(self, field):
  46. # get fields, ignore geometry columns
  47. if field.dataType.upper() == "SDO_GEOMETRY":
  48. return ("CASE WHEN {0} IS NULL THEN NULL ELSE 'GEOMETRY'"
  49. "END AS {0}".format(
  50. self.db.quoteId(field.name)))
  51. if field.dataType.upper() == "DATE":
  52. return "CAST({} AS VARCHAR2(8))".format(
  53. self.db.quoteId(field.name))
  54. if "TIMESTAMP" in field.dataType.upper():
  55. return "TO_CHAR({}, 'YYYY-MM-DD HH:MI:SS.FF')".format(
  56. self.db.quoteId(field.name))
  57. if field.dataType.upper() == "NUMBER":
  58. if not field.charMaxLen:
  59. return "CAST({} AS VARCHAR2(135))".format(
  60. self.db.quoteId(field.name))
  61. elif field.modifier:
  62. nbChars = 2 + int(field.charMaxLen) + \
  63. int(field.modifier)
  64. return "CAST({} AS VARCHAR2({}))".format(
  65. self.db.quoteId(field.name),
  66. str(nbChars))
  67. return "CAST({} As VARCHAR2({}))".format(
  68. self.db.quoteId(field.name), field.charMaxLen)
  69. def _deleteCursor(self):
  70. self.db._close_cursor(self.cursor)
  71. self.cursor = None
  72. def __del__(self):
  73. self.table.aboutToChange.disconnect(self._deleteCursor)
  74. self._deleteCursor()
  75. def getData(self, row, col):
  76. if (row < self.fetchedFrom or
  77. row >= self.fetchedFrom + self.fetchedCount):
  78. margin = self.fetchedCount / 2
  79. if row + margin >= self.rowCount():
  80. start = int(self.rowCount() - margin)
  81. else:
  82. start = int(row - margin)
  83. if start < 0:
  84. start = 0
  85. self.fetchMoreData(start)
  86. # For some improbable cases
  87. if row - self.fetchedFrom >= len(self.resdata):
  88. return None
  89. return self.resdata[row - self.fetchedFrom][col]
  90. def fetchMoreData(self, row_start):
  91. if not self.cursor:
  92. self._createCursor()
  93. self.cursor.scroll(row_start - 1)
  94. self.resdata = self.cursor.fetchmany(self.fetchedCount)
  95. self.fetchedFrom = row_start
  96. class ORSqlResultModelTask(SqlResultModelTask):
  97. def __init__(self, db, sql, parent):
  98. super().__init__(db, sql, parent)
  99. def run(self):
  100. try:
  101. self.model = ORSqlResultModel(self.db, self.sql, None)
  102. except BaseError as e:
  103. self.error = e
  104. QgsMessageLog.logMessage(e.msg)
  105. return False
  106. return True
  107. def cancel(self):
  108. self.db.connector.cancel()
  109. SqlResultModelTask.cancel(self)
  110. class ORSqlResultModelAsync(SqlResultModelAsync):
  111. def __init__(self, db, sql, parent):
  112. super().__init__()
  113. self.task = ORSqlResultModelTask(db, sql, parent)
  114. self.task.taskCompleted.connect(self.modelDone)
  115. self.task.taskTerminated.connect(self.modelDone)
  116. class ORSqlResultModel(SqlResultModel):
  117. def __init__(self, db, sql, parent=None):
  118. self.db = db.connector
  119. t = QTime()
  120. t.start()
  121. c = self.db._execute(None, str(sql))
  122. self._affectedRows = 0
  123. data = []
  124. header = self.db._get_cursor_columns(c)
  125. if not header:
  126. header = []
  127. try:
  128. if len(header) > 0:
  129. data = self.db._fetchall(c)
  130. self._affectedRows = len(data)
  131. except DbError:
  132. # nothing to fetch!
  133. data = []
  134. header = []
  135. self._secs = t.elapsed() / 1000.0
  136. del t
  137. BaseTableModel.__init__(self, header, data, parent)
  138. # commit before closing the cursor to make sure that the
  139. # changes are stored
  140. self.db._commit()
  141. c.close()
  142. del c