mirror of
				https://github.com/qgis/QGIS.git
				synced 2025-10-31 00:06:02 -04:00 
			
		
		
		
	
		
			
				
	
	
		
			1234 lines
		
	
	
		
			44 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			1234 lines
		
	
	
		
			44 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| # -*- coding: utf-8 -*-
 | |
| 
 | |
| """
 | |
| /***************************************************************************
 | |
| Name                 : DB Manager
 | |
| Description          : Database manager plugin for QGIS
 | |
| Date                 : May 23, 2011
 | |
| copyright            : (C) 2011 by Giuseppe Sucameli
 | |
| email                : brush.tyler@gmail.com
 | |
| 
 | |
|  ***************************************************************************/
 | |
| 
 | |
| /***************************************************************************
 | |
|  *                                                                         *
 | |
|  *   This program is free software; you can redistribute it and/or modify  *
 | |
|  *   it under the terms of the GNU General Public License as published by  *
 | |
|  *   the Free Software Foundation; either version 2 of the License, or     *
 | |
|  *   (at your option) any later version.                                   *
 | |
|  *                                                                         *
 | |
|  ***************************************************************************/
 | |
| """
 | |
| from builtins import str
 | |
| from builtins import range
 | |
| 
 | |
| from qgis.PyQt.QtCore import Qt, QObject, pyqtSignal
 | |
| from qgis.PyQt.QtWidgets import QApplication, QAction, QMenu, QInputDialog, QMessageBox
 | |
| from qgis.PyQt.QtGui import QKeySequence, QIcon
 | |
| 
 | |
| from qgis.gui import QgsMessageBar
 | |
| from qgis.core import Qgis, QgsApplication, QgsSettings
 | |
| from ..db_plugins import createDbPlugin
 | |
| 
 | |
| 
 | |
| class BaseError(Exception):
 | |
| 
 | |
|     """Base class for exceptions in the plugin."""
 | |
| 
 | |
|     def __init__(self, e):
 | |
|         if isinstance(e, Exception):
 | |
|             msg = e.args[0] if len(e.args) > 0 else ''
 | |
|         else:
 | |
|             msg = e
 | |
| 
 | |
|         if not isinstance(msg, str):
 | |
|             msg = str(msg, 'utf-8', 'replace')  # convert from utf8 and replace errors (if any)
 | |
| 
 | |
|         self.msg = msg
 | |
|         Exception.__init__(self, msg)
 | |
| 
 | |
|     def __unicode__(self):
 | |
|         return self.msg
 | |
| 
 | |
|     def __str__(self):
 | |
|         return str(self).encode('utf-8')
 | |
| 
 | |
| 
 | |
| class InvalidDataException(BaseError):
 | |
|     pass
 | |
| 
 | |
| 
 | |
| class ConnectionError(BaseError):
 | |
|     pass
 | |
| 
 | |
| 
 | |
| class DbError(BaseError):
 | |
| 
 | |
|     def __init__(self, e, query=None):
 | |
|         BaseError.__init__(self, e)
 | |
|         self.query = str(query) if query is not None else None
 | |
| 
 | |
|     def __unicode__(self):
 | |
|         if self.query is None:
 | |
|             return BaseError.__unicode__(self)
 | |
| 
 | |
|         msg = QApplication.translate("DBManagerPlugin", "Error:\n{0}").format(BaseError.__unicode__(self))
 | |
|         if self.query:
 | |
|             msg += QApplication.translate("DBManagerPlugin", "\n\nQuery:\n{0}").format(self.query)
 | |
|         return msg
 | |
| 
 | |
| 
 | |
| class DBPlugin(QObject):
 | |
|     deleted = pyqtSignal()
 | |
|     changed = pyqtSignal()
 | |
|     aboutToChange = pyqtSignal()
 | |
| 
 | |
|     def __init__(self, conn_name, parent=None):
 | |
|         QObject.__init__(self, parent)
 | |
|         self.connName = conn_name
 | |
|         self.db = None
 | |
| 
 | |
|     def __del__(self):
 | |
|         pass  # print "DBPlugin.__del__", self.connName
 | |
| 
 | |
|     def connectionIcon(self):
 | |
|         return QgsApplication.getThemeIcon("/mIconDbSchema.svg")
 | |
| 
 | |
|     def connectionName(self):
 | |
|         return self.connName
 | |
| 
 | |
|     def database(self):
 | |
|         return self.db
 | |
| 
 | |
|     def info(self):
 | |
|         from .info_model import DatabaseInfo
 | |
| 
 | |
|         return DatabaseInfo(None)
 | |
| 
 | |
|     def connect(self, parent=None):
 | |
|         raise NotImplementedError('Needs to be implemented by subclasses')
 | |
| 
 | |
|     def connectToUri(self, uri):
 | |
|         self.db = self.databasesFactory(self, uri)
 | |
|         if self.db:
 | |
|             return True
 | |
|         return False
 | |
| 
 | |
|     def reconnect(self):
 | |
|         if self.db is not None:
 | |
|             uri = self.db.uri()
 | |
|             self.db.deleteLater()
 | |
|             self.db = None
 | |
|             return self.connectToUri(uri)
 | |
|         return self.connect(self.parent())
 | |
| 
 | |
|     def remove(self):
 | |
|         settings = QgsSettings()
 | |
|         settings.beginGroup(u"/%s/%s" % (self.connectionSettingsKey(), self.connectionName()))
 | |
|         settings.remove("")
 | |
|         self.deleted.emit()
 | |
|         return True
 | |
| 
 | |
|     @classmethod
 | |
|     def addConnection(self, conn_name, uri):
 | |
|         raise NotImplementedError('Needs to be implemented by subclasses')
 | |
| 
 | |
|     @classmethod
 | |
|     def icon(self):
 | |
|         return None
 | |
| 
 | |
|     @classmethod
 | |
|     def typeName(self):
 | |
|         # return the db typename (e.g. 'postgis')
 | |
|         pass
 | |
| 
 | |
|     @classmethod
 | |
|     def typeNameString(self):
 | |
|         # return the db typename string (e.g. 'PostGIS')
 | |
|         pass
 | |
| 
 | |
|     @classmethod
 | |
|     def providerName(self):
 | |
|         # return the provider's name (e.g. 'postgres')
 | |
|         pass
 | |
| 
 | |
|     @classmethod
 | |
|     def connectionSettingsKey(self):
 | |
|         # return the key used to store the connections in settings
 | |
|         pass
 | |
| 
 | |
|     @classmethod
 | |
|     def connections(self):
 | |
|         # get the list of connections
 | |
|         conn_list = []
 | |
|         settings = QgsSettings()
 | |
|         settings.beginGroup(self.connectionSettingsKey())
 | |
|         for name in settings.childGroups():
 | |
|             conn_list.append(createDbPlugin(self.typeName(), name))
 | |
|         settings.endGroup()
 | |
|         return conn_list
 | |
| 
 | |
|     def databasesFactory(self, connection, uri):
 | |
|         return None
 | |
| 
 | |
|     @classmethod
 | |
|     def addConnectionActionSlot(self, item, action, parent):
 | |
|         raise NotImplementedError('Needs to be implemented by subclasses')
 | |
| 
 | |
|     def removeActionSlot(self, item, action, parent):
 | |
|         QApplication.restoreOverrideCursor()
 | |
|         try:
 | |
|             res = QMessageBox.question(parent, QApplication.translate("DBManagerPlugin", "DB Manager"),
 | |
|                                        QApplication.translate("DBManagerPlugin",
 | |
|                                                               "Really remove connection to {0}?").format(item.connectionName()),
 | |
|                                        QMessageBox.Yes | QMessageBox.No)
 | |
|             if res != QMessageBox.Yes:
 | |
|                 return
 | |
|         finally:
 | |
|             QApplication.setOverrideCursor(Qt.WaitCursor)
 | |
| 
 | |
|         item.remove()
 | |
| 
 | |
| 
 | |
| class DbItemObject(QObject):
 | |
|     changed = pyqtSignal()
 | |
|     aboutToChange = pyqtSignal()
 | |
|     deleted = pyqtSignal()
 | |
| 
 | |
|     def __init__(self, parent=None):
 | |
|         QObject.__init__(self, parent)
 | |
| 
 | |
|     def database(self):
 | |
|         return None
 | |
| 
 | |
|     def refresh(self):
 | |
|         self.changed.emit()  # refresh the item data reading them from the db
 | |
| 
 | |
|     def info(self):
 | |
|         pass
 | |
| 
 | |
|     def runAction(self):
 | |
|         pass
 | |
| 
 | |
|     def registerActions(self, mainWindow):
 | |
|         pass
 | |
| 
 | |
| 
 | |
| class Database(DbItemObject):
 | |
| 
 | |
|     def __init__(self, dbplugin, uri):
 | |
|         DbItemObject.__init__(self, dbplugin)
 | |
|         self.connector = self.connectorsFactory(uri)
 | |
| 
 | |
|     def connectorsFactory(self, uri):
 | |
|         return None
 | |
| 
 | |
|     def __del__(self):
 | |
|         self.connector = None
 | |
|         pass  # print "Database.__del__", self
 | |
| 
 | |
|     def connection(self):
 | |
|         return self.parent()
 | |
| 
 | |
|     def dbplugin(self):
 | |
|         return self.parent()
 | |
| 
 | |
|     def database(self):
 | |
|         return self
 | |
| 
 | |
|     def uri(self):
 | |
|         return self.connector.uri()
 | |
| 
 | |
|     def publicUri(self):
 | |
|         return self.connector.publicUri()
 | |
| 
 | |
|     def delete(self):
 | |
|         self.aboutToChange.emit()
 | |
|         ret = self.connection().remove()
 | |
|         if ret is not False:
 | |
|             self.deleted.emit()
 | |
|         return ret
 | |
| 
 | |
|     def info(self):
 | |
|         from .info_model import DatabaseInfo
 | |
| 
 | |
|         return DatabaseInfo(self)
 | |
| 
 | |
|     def sqlResultModel(self, sql, parent):
 | |
|         from .data_model import SqlResultModel
 | |
| 
 | |
|         return SqlResultModel(self, sql, parent)
 | |
| 
 | |
|     def sqlResultModelAsync(self, sql, parent):
 | |
|         from .data_model import SqlResultModelAsync
 | |
| 
 | |
|         return SqlResultModelAsync(self, sql, parent)
 | |
| 
 | |
|     def columnUniqueValuesModel(self, col, table, limit=10):
 | |
|         l = ""
 | |
|         if limit is not None:
 | |
|             l = "LIMIT %d" % limit
 | |
|         return self.sqlResultModel("SELECT DISTINCT %s FROM %s %s" % (col, table, l), self)
 | |
| 
 | |
|     def uniqueIdFunction(self):
 | |
|         """Return a SQL function used to generate a unique id for rows of a query"""
 | |
|         # may be overloaded by derived classes
 | |
|         return "row_number() over ()"
 | |
| 
 | |
|     def toSqlLayer(self, sql, geomCol, uniqueCol, layerName="QueryLayer", layerType=None, avoidSelectById=False, filter=""):
 | |
|         from qgis.core import QgsMapLayer, QgsVectorLayer, QgsRasterLayer
 | |
| 
 | |
|         if uniqueCol is None:
 | |
|             if hasattr(self, 'uniqueIdFunction'):
 | |
|                 uniqueFct = self.uniqueIdFunction()
 | |
|                 if uniqueFct is not None:
 | |
|                     q = 1
 | |
|                     while "_subq_%d_" % q in sql:
 | |
|                         q += 1
 | |
|                     sql = u"SELECT %s AS _uid_,* FROM (%s\n) AS _subq_%d_" % (uniqueFct, sql, q)
 | |
|                     uniqueCol = "_uid_"
 | |
| 
 | |
|         uri = self.uri()
 | |
|         uri.setDataSource("", u"(%s\n)" % sql, geomCol, filter, uniqueCol)
 | |
|         if avoidSelectById:
 | |
|             uri.disableSelectAtId(True)
 | |
|         provider = self.dbplugin().providerName()
 | |
|         if layerType == QgsMapLayer.RasterLayer:
 | |
|             return QgsRasterLayer(uri.uri(False), layerName, provider)
 | |
|         return QgsVectorLayer(uri.uri(False), layerName, provider)
 | |
| 
 | |
|     def registerAllActions(self, mainWindow):
 | |
|         self.registerDatabaseActions(mainWindow)
 | |
|         self.registerSubPluginActions(mainWindow)
 | |
| 
 | |
|     def registerSubPluginActions(self, mainWindow):
 | |
|         # load plugins!
 | |
|         try:
 | |
|             exec(u"from .%s.plugins import load" % self.dbplugin().typeName(), globals())
 | |
|         except ImportError:
 | |
|             pass
 | |
|         else:
 | |
|             load(self, mainWindow)  # NOQA
 | |
| 
 | |
|     def registerDatabaseActions(self, mainWindow):
 | |
|         action = QAction(QApplication.translate("DBManagerPlugin", "&Re-connect"), self)
 | |
|         mainWindow.registerAction(action, QApplication.translate("DBManagerPlugin", "&Database"),
 | |
|                                   self.reconnectActionSlot)
 | |
| 
 | |
|         if self.schemas() is not None:
 | |
|             action = QAction(QApplication.translate("DBManagerPlugin", "&Create Schema…"), self)
 | |
|             mainWindow.registerAction(action, QApplication.translate("DBManagerPlugin", "&Schema"),
 | |
|                                       self.createSchemaActionSlot)
 | |
|             action = QAction(QApplication.translate("DBManagerPlugin", "&Delete (Empty) Schema"), self)
 | |
|             mainWindow.registerAction(action, QApplication.translate("DBManagerPlugin", "&Schema"),
 | |
|                                       self.deleteSchemaActionSlot)
 | |
| 
 | |
|         action = QAction(QApplication.translate("DBManagerPlugin", "Delete Selected Item"), self)
 | |
|         mainWindow.registerAction(action, None, self.deleteActionSlot)
 | |
|         action.setShortcuts(QKeySequence.Delete)
 | |
| 
 | |
|         action = QAction(QgsApplication.getThemeIcon("/mActionCreateTable.svg"),
 | |
|                          QApplication.translate("DBManagerPlugin", "&Create Table…"), self)
 | |
|         mainWindow.registerAction(action, QApplication.translate("DBManagerPlugin", "&Table"),
 | |
|                                   self.createTableActionSlot)
 | |
|         action = QAction(QgsApplication.getThemeIcon("/mActionEditTable.svg"),
 | |
|                          QApplication.translate("DBManagerPlugin", "&Edit Table…"), self)
 | |
|         mainWindow.registerAction(action, QApplication.translate("DBManagerPlugin", "&Table"), self.editTableActionSlot)
 | |
|         action = QAction(QgsApplication.getThemeIcon("/mActionDeleteTable.svg"),
 | |
|                          QApplication.translate("DBManagerPlugin", "&Delete Table/View…"), self)
 | |
|         mainWindow.registerAction(action, QApplication.translate("DBManagerPlugin", "&Table"),
 | |
|                                   self.deleteTableActionSlot)
 | |
|         action = QAction(QApplication.translate("DBManagerPlugin", "&Empty Table…"), self)
 | |
|         mainWindow.registerAction(action, QApplication.translate("DBManagerPlugin", "&Table"),
 | |
|                                   self.emptyTableActionSlot)
 | |
| 
 | |
|         if self.schemas() is not None:
 | |
|             action = QAction(QApplication.translate("DBManagerPlugin", "&Move to Schema"), self)
 | |
|             action.setMenu(QMenu(mainWindow))
 | |
| 
 | |
|             def invoke_callback():
 | |
|                 return mainWindow.invokeCallback(self.prepareMenuMoveTableToSchemaActionSlot)
 | |
| 
 | |
|             action.menu().aboutToShow.connect(invoke_callback)
 | |
|             mainWindow.registerAction(action, QApplication.translate("DBManagerPlugin", "&Table"))
 | |
| 
 | |
|     def reconnectActionSlot(self, item, action, parent):
 | |
|         db = item.database()
 | |
|         db.connection().reconnect()
 | |
|         db.refresh()
 | |
| 
 | |
|     def deleteActionSlot(self, item, action, parent):
 | |
|         if isinstance(item, Schema):
 | |
|             self.deleteSchemaActionSlot(item, action, parent)
 | |
|         elif isinstance(item, Table):
 | |
|             self.deleteTableActionSlot(item, action, parent)
 | |
|         else:
 | |
|             QApplication.restoreOverrideCursor()
 | |
|             parent.infoBar.pushMessage(QApplication.translate("DBManagerPlugin", "Cannot delete the selected item."),
 | |
|                                        Qgis.Info, parent.iface.messageTimeout())
 | |
|             QApplication.setOverrideCursor(Qt.WaitCursor)
 | |
| 
 | |
|     def createSchemaActionSlot(self, item, action, parent):
 | |
|         QApplication.restoreOverrideCursor()
 | |
|         try:
 | |
|             if not isinstance(item, (DBPlugin, Schema, Table)) or item.database() is None:
 | |
|                 parent.infoBar.pushMessage(
 | |
|                     QApplication.translate("DBManagerPlugin", "No database selected or you are not connected to it."),
 | |
|                     Qgis.Info, parent.iface.messageTimeout())
 | |
|                 return
 | |
|             (schema, ok) = QInputDialog.getText(parent, QApplication.translate("DBManagerPlugin", "New schema"),
 | |
|                                                 QApplication.translate("DBManagerPlugin", "Enter new schema name"))
 | |
|             if not ok:
 | |
|                 return
 | |
|         finally:
 | |
|             QApplication.setOverrideCursor(Qt.WaitCursor)
 | |
| 
 | |
|         self.createSchema(schema)
 | |
| 
 | |
|     def deleteSchemaActionSlot(self, item, action, parent):
 | |
|         QApplication.restoreOverrideCursor()
 | |
|         try:
 | |
|             if not isinstance(item, Schema):
 | |
|                 parent.infoBar.pushMessage(
 | |
|                     QApplication.translate("DBManagerPlugin", "Select an empty schema for deletion."),
 | |
|                     Qgis.Info, parent.iface.messageTimeout())
 | |
|                 return
 | |
|             res = QMessageBox.question(parent, QApplication.translate("DBManagerPlugin", "DB Manager"),
 | |
|                                        QApplication.translate("DBManagerPlugin",
 | |
|                                                               "Really delete schema {0}?").format(item.name),
 | |
|                                        QMessageBox.Yes | QMessageBox.No)
 | |
|             if res != QMessageBox.Yes:
 | |
|                 return
 | |
|         finally:
 | |
|             QApplication.setOverrideCursor(Qt.WaitCursor)
 | |
| 
 | |
|         item.delete()
 | |
| 
 | |
|     def schemasFactory(self, row, db):
 | |
|         return None
 | |
| 
 | |
|     def schemas(self):
 | |
|         schemas = self.connector.getSchemas()
 | |
|         if schemas is not None:
 | |
|             schemas = [self.schemasFactory(x, self) for x in schemas]
 | |
|         return schemas
 | |
| 
 | |
|     def createSchema(self, name):
 | |
|         self.connector.createSchema(name)
 | |
|         self.refresh()
 | |
| 
 | |
|     def createTableActionSlot(self, item, action, parent):
 | |
|         QApplication.restoreOverrideCursor()
 | |
|         if not hasattr(item, 'database') or item.database() is None:
 | |
|             parent.infoBar.pushMessage(
 | |
|                 QApplication.translate("DBManagerPlugin", "No database selected or you are not connected to it."),
 | |
|                 Qgis.Info, parent.iface.messageTimeout())
 | |
|             return
 | |
|         from ..dlg_create_table import DlgCreateTable
 | |
| 
 | |
|         DlgCreateTable(item, parent).exec_()
 | |
|         QApplication.setOverrideCursor(Qt.WaitCursor)
 | |
| 
 | |
|     def editTableActionSlot(self, item, action, parent):
 | |
|         QApplication.restoreOverrideCursor()
 | |
|         try:
 | |
|             if not isinstance(item, Table) or item.isView:
 | |
|                 parent.infoBar.pushMessage(QApplication.translate("DBManagerPlugin", "Select a table to edit."),
 | |
|                                            Qgis.Info, parent.iface.messageTimeout())
 | |
|                 return
 | |
|             from ..dlg_table_properties import DlgTableProperties
 | |
| 
 | |
|             DlgTableProperties(item, parent).exec_()
 | |
|         finally:
 | |
|             QApplication.setOverrideCursor(Qt.WaitCursor)
 | |
| 
 | |
|     def deleteTableActionSlot(self, item, action, parent):
 | |
|         QApplication.restoreOverrideCursor()
 | |
|         try:
 | |
|             if not isinstance(item, Table):
 | |
|                 parent.infoBar.pushMessage(
 | |
|                     QApplication.translate("DBManagerPlugin", "Select a table/view for deletion."),
 | |
|                     Qgis.Info, parent.iface.messageTimeout())
 | |
|                 return
 | |
|             res = QMessageBox.question(parent, QApplication.translate("DBManagerPlugin", "DB Manager"),
 | |
|                                        QApplication.translate("DBManagerPlugin",
 | |
|                                                               "Really delete table/view {0}?").format(item.name),
 | |
|                                        QMessageBox.Yes | QMessageBox.No)
 | |
|             if res != QMessageBox.Yes:
 | |
|                 return
 | |
|         finally:
 | |
|             QApplication.setOverrideCursor(Qt.WaitCursor)
 | |
| 
 | |
|         item.delete()
 | |
| 
 | |
|     def emptyTableActionSlot(self, item, action, parent):
 | |
|         QApplication.restoreOverrideCursor()
 | |
|         try:
 | |
|             if not isinstance(item, Table) or item.isView:
 | |
|                 parent.infoBar.pushMessage(QApplication.translate("DBManagerPlugin", "Select a table to empty it."),
 | |
|                                            Qgis.Info, parent.iface.messageTimeout())
 | |
|                 return
 | |
|             res = QMessageBox.question(parent, QApplication.translate("DBManagerPlugin", "DB Manager"),
 | |
|                                        QApplication.translate("DBManagerPlugin",
 | |
|                                                               "Really delete all items from table {0}?").format(item.name),
 | |
|                                        QMessageBox.Yes | QMessageBox.No)
 | |
|             if res != QMessageBox.Yes:
 | |
|                 return
 | |
|         finally:
 | |
|             QApplication.setOverrideCursor(Qt.WaitCursor)
 | |
| 
 | |
|         item.empty()
 | |
| 
 | |
|     def prepareMenuMoveTableToSchemaActionSlot(self, item, menu, mainWindow):
 | |
|         """ populate menu with schemas """
 | |
|         def slot(x):
 | |
|             return lambda: mainWindow.invokeCallback(self.moveTableToSchemaActionSlot, x)
 | |
| 
 | |
|         menu.clear()
 | |
|         for schema in self.schemas():
 | |
|             menu.addAction(schema.name, slot(schema))
 | |
| 
 | |
|     def moveTableToSchemaActionSlot(self, item, action, parent, new_schema):
 | |
|         QApplication.restoreOverrideCursor()
 | |
|         try:
 | |
|             if not isinstance(item, Table):
 | |
|                 parent.infoBar.pushMessage(QApplication.translate("DBManagerPlugin", "Select a table/view."),
 | |
|                                            Qgis.Info, parent.iface.messageTimeout())
 | |
|                 return
 | |
|         finally:
 | |
|             QApplication.setOverrideCursor(Qt.WaitCursor)
 | |
| 
 | |
|         item.moveToSchema(new_schema)
 | |
| 
 | |
|     def tablesFactory(self, row, db, schema=None):
 | |
|         typ, row = row[0], row[1:]
 | |
|         if typ == Table.VectorType:
 | |
|             return self.vectorTablesFactory(row, db, schema)
 | |
|         elif typ == Table.RasterType:
 | |
|             return self.rasterTablesFactory(row, db, schema)
 | |
|         return self.dataTablesFactory(row, db, schema)
 | |
| 
 | |
|     def dataTablesFactory(self, row, db, schema=None):
 | |
|         return None
 | |
| 
 | |
|     def vectorTablesFactory(self, row, db, schema=None):
 | |
|         return None
 | |
| 
 | |
|     def rasterTablesFactory(self, row, db, schema=None):
 | |
|         return None
 | |
| 
 | |
|     def tables(self, schema=None, sys_tables=False):
 | |
|         tables = self.connector.getTables(schema.name if schema else None, sys_tables)
 | |
|         if tables is not None:
 | |
|             tables = [self.tablesFactory(x, self, schema) for x in tables]
 | |
|         return tables
 | |
| 
 | |
|     def createTable(self, table, fields, schema=None):
 | |
|         field_defs = [x.definition() for x in fields]
 | |
|         pkeys = [x for x in fields if x.primaryKey]
 | |
|         pk_name = pkeys[0].name if len(pkeys) > 0 else None
 | |
| 
 | |
|         ret = self.connector.createTable((schema, table), field_defs, pk_name)
 | |
|         if ret is not False:
 | |
|             self.refresh()
 | |
|         return ret
 | |
| 
 | |
|     def createVectorTable(self, table, fields, geom, schema=None):
 | |
|         ret = self.createTable(table, fields, schema)
 | |
|         if not ret:
 | |
|             return False
 | |
| 
 | |
|         try:
 | |
|             createGeomCol = geom is not None
 | |
|             if createGeomCol:
 | |
|                 geomCol, geomType, geomSrid, geomDim = geom[:4]
 | |
|                 createSpatialIndex = geom[4] if len(geom) > 4 else False
 | |
| 
 | |
|                 self.connector.addGeometryColumn((schema, table), geomCol, geomType, geomSrid, geomDim)
 | |
| 
 | |
|                 if createSpatialIndex:
 | |
|                     # commit data definition changes, otherwise index can't be built
 | |
|                     self.connector._commit()
 | |
|                     self.connector.createSpatialIndex((schema, table), geomCol)
 | |
| 
 | |
|         finally:
 | |
|             self.refresh()
 | |
|         return True
 | |
| 
 | |
|     def explicitSpatialIndex(self):
 | |
|         return False
 | |
| 
 | |
|     def spatialIndexClause(self, src_table, src_column, dest_table, dest_table_column):
 | |
|         return None
 | |
| 
 | |
|     def hasLowercaseFieldNamesOption(self):
 | |
|         return False
 | |
| 
 | |
| 
 | |
| class Schema(DbItemObject):
 | |
| 
 | |
|     def __init__(self, db):
 | |
|         DbItemObject.__init__(self, db)
 | |
|         self.oid = self.name = self.owner = self.perms = None
 | |
|         self.comment = None
 | |
|         self.tableCount = 0
 | |
| 
 | |
|     def __del__(self):
 | |
|         pass  # print "Schema.__del__", self
 | |
| 
 | |
|     def database(self):
 | |
|         return self.parent()
 | |
| 
 | |
|     def schema(self):
 | |
|         return self
 | |
| 
 | |
|     def tables(self):
 | |
|         return self.database().tables(self)
 | |
| 
 | |
|     def delete(self):
 | |
|         self.aboutToChange.emit()
 | |
|         ret = self.database().connector.deleteSchema(self.name)
 | |
|         if ret is not False:
 | |
|             self.deleted.emit()
 | |
|         return ret
 | |
| 
 | |
|     def rename(self, new_name):
 | |
|         self.aboutToChange.emit()
 | |
|         ret = self.database().connector.renameSchema(self.name, new_name)
 | |
|         if ret is not False:
 | |
|             self.name = new_name
 | |
|             self.refresh()
 | |
|         return ret
 | |
| 
 | |
|     def info(self):
 | |
|         from .info_model import SchemaInfo
 | |
| 
 | |
|         return SchemaInfo(self)
 | |
| 
 | |
| 
 | |
| class Table(DbItemObject):
 | |
|     TableType, VectorType, RasterType = list(range(3))
 | |
| 
 | |
|     def __init__(self, db, schema=None, parent=None):
 | |
|         DbItemObject.__init__(self, db)
 | |
|         self._schema = schema
 | |
|         if hasattr(self, 'type'):
 | |
|             return
 | |
|         self.type = Table.TableType
 | |
| 
 | |
|         self.name = self.isView = self.owner = self.pages = None
 | |
|         self.comment = None
 | |
|         self.rowCount = None
 | |
| 
 | |
|         self._fields = self._indexes = self._constraints = self._triggers = self._rules = None
 | |
| 
 | |
|     def __del__(self):
 | |
|         pass  # print "Table.__del__", self
 | |
| 
 | |
|     def canBeAddedToCanvas(self):
 | |
|         return True
 | |
| 
 | |
|     def database(self):
 | |
|         return self.parent()
 | |
| 
 | |
|     def schema(self):
 | |
|         return self._schema
 | |
| 
 | |
|     def schemaName(self):
 | |
|         return self.schema().name if self.schema() else None
 | |
| 
 | |
|     def quotedName(self):
 | |
|         return self.database().connector.quoteId((self.schemaName(), self.name))
 | |
| 
 | |
|     def delete(self):
 | |
|         self.aboutToChange.emit()
 | |
|         if self.isView:
 | |
|             ret = self.database().connector.deleteView((self.schemaName(), self.name))
 | |
|         else:
 | |
|             ret = self.database().connector.deleteTable((self.schemaName(), self.name))
 | |
|         if ret is not False:
 | |
|             self.deleted.emit()
 | |
|         return ret
 | |
| 
 | |
|     def rename(self, new_name):
 | |
|         self.aboutToChange.emit()
 | |
|         ret = self.database().connector.renameTable((self.schemaName(), self.name), new_name)
 | |
|         if ret is not False:
 | |
|             self.name = new_name
 | |
|             self.refresh()
 | |
|         return ret
 | |
| 
 | |
|     def empty(self):
 | |
|         self.aboutToChange.emit()
 | |
|         ret = self.database().connector.emptyTable((self.schemaName(), self.name))
 | |
|         if ret is not False:
 | |
|             self.refreshRowCount()
 | |
|         return ret
 | |
| 
 | |
|     def moveToSchema(self, schema):
 | |
|         self.aboutToChange.emit()
 | |
|         if self.schema() == schema:
 | |
|             return True
 | |
|         ret = self.database().connector.moveTableToSchema((self.schemaName(), self.name), schema.name)
 | |
|         if ret is not False:
 | |
|             self.schema().refresh()
 | |
|             schema.refresh()
 | |
|         return ret
 | |
| 
 | |
|     def info(self):
 | |
|         from .info_model import TableInfo
 | |
| 
 | |
|         return TableInfo(self)
 | |
| 
 | |
|     def uri(self):
 | |
|         uri = self.database().uri()
 | |
|         schema = self.schemaName() if self.schemaName() else ''
 | |
|         geomCol = self.geomColumn if self.type in [Table.VectorType, Table.RasterType] else ""
 | |
|         uniqueCol = self.getValidQgisUniqueFields(True) if self.isView else None
 | |
|         uri.setDataSource(schema, self.name, geomCol if geomCol else None, None, uniqueCol.name if uniqueCol else "")
 | |
|         return uri
 | |
| 
 | |
|     def mimeUri(self):
 | |
|         layerType = "raster" if self.type == Table.RasterType else "vector"
 | |
|         return u"%s:%s:%s:%s" % (layerType, self.database().dbplugin().providerName(), self.name, self.uri().uri(False))
 | |
| 
 | |
|     def toMapLayer(self):
 | |
|         from qgis.core import QgsVectorLayer, QgsRasterLayer
 | |
| 
 | |
|         provider = self.database().dbplugin().providerName()
 | |
|         uri = self.uri().uri(False)
 | |
|         if self.type == Table.RasterType:
 | |
|             return QgsRasterLayer(uri, self.name, provider)
 | |
|         return QgsVectorLayer(uri, self.name, provider)
 | |
| 
 | |
|     def getValidQgisUniqueFields(self, onlyOne=False):
 | |
|         """ list of fields valid to load the table as layer in Qgis canvas.
 | |
|                 Qgis automatically search for a valid unique field, so it's
 | |
|                 needed only for queries and views """
 | |
| 
 | |
|         ret = []
 | |
| 
 | |
|         # add the pk
 | |
|         pkcols = [x for x in self.fields() if x.primaryKey]
 | |
|         if len(pkcols) == 1:
 | |
|             ret.append(pkcols[0])
 | |
| 
 | |
|         # then add both oid, serial and int fields with an unique index
 | |
|         indexes = self.indexes()
 | |
|         if indexes is not None:
 | |
|             for idx in indexes:
 | |
|                 if idx.isUnique and len(idx.columns) == 1:
 | |
|                     fld = idx.fields()[idx.columns[0]]
 | |
|                     if fld.dataType in ["oid", "serial", "int4", "int8"] and fld not in ret:
 | |
|                         ret.append(fld)
 | |
| 
 | |
|         # and finally append the other suitable fields
 | |
|         for fld in self.fields():
 | |
|             if fld.dataType in ["oid", "serial", "int4", "int8"] and fld not in ret:
 | |
|                 ret.append(fld)
 | |
| 
 | |
|         if onlyOne:
 | |
|             return ret[0] if len(ret) > 0 else None
 | |
|         return ret
 | |
| 
 | |
|     def tableDataModel(self, parent):
 | |
|         pass
 | |
| 
 | |
|     def tableFieldsFactory(self, row, table):
 | |
|         raise NotImplementedError('Needs to be implemented by subclasses')
 | |
| 
 | |
|     def fields(self):
 | |
|         if self._fields is None:
 | |
|             fields = self.database().connector.getTableFields((self.schemaName(), self.name))
 | |
|             if fields is not None:
 | |
|                 self._fields = [self.tableFieldsFactory(x, self) for x in fields]
 | |
|         return self._fields
 | |
| 
 | |
|     def refreshFields(self):
 | |
|         self._fields = None  # refresh table fields
 | |
|         self.refresh()
 | |
| 
 | |
|     def addField(self, fld):
 | |
|         self.aboutToChange.emit()
 | |
|         ret = self.database().connector.addTableColumn((self.schemaName(), self.name), fld.definition())
 | |
|         if ret is not False:
 | |
|             self.refreshFields()
 | |
|         return ret
 | |
| 
 | |
|     def deleteField(self, fld):
 | |
|         self.aboutToChange.emit()
 | |
|         ret = self.database().connector.deleteTableColumn((self.schemaName(), self.name), fld.name)
 | |
|         if ret is not False:
 | |
|             self.refreshFields()
 | |
|             self.refreshConstraints()
 | |
|             self.refreshIndexes()
 | |
|         return ret
 | |
| 
 | |
|     def addGeometryColumn(self, geomCol, geomType, srid, dim, createSpatialIndex=False):
 | |
|         self.aboutToChange.emit()
 | |
|         ret = self.database().connector.addGeometryColumn((self.schemaName(), self.name), geomCol, geomType, srid, dim)
 | |
|         if not ret:
 | |
|             return False
 | |
| 
 | |
|         try:
 | |
|             if createSpatialIndex:
 | |
|                 # commit data definition changes, otherwise index can't be built
 | |
|                 self.database().connector._commit()
 | |
|                 self.database().connector.createSpatialIndex((self.schemaName(), self.name), geomCol)
 | |
| 
 | |
|         finally:
 | |
|             self.schema().refresh() if self.schema() else self.database().refresh()  # another table was added
 | |
|         return True
 | |
| 
 | |
|     def tableConstraintsFactory(self):
 | |
|         return None
 | |
| 
 | |
|     def constraints(self):
 | |
|         if self._constraints is None:
 | |
|             constraints = self.database().connector.getTableConstraints((self.schemaName(), self.name))
 | |
|             if constraints is not None:
 | |
|                 self._constraints = [self.tableConstraintsFactory(x, self) for x in constraints]
 | |
|         return self._constraints
 | |
| 
 | |
|     def refreshConstraints(self):
 | |
|         self._constraints = None  # refresh table constraints
 | |
|         self.refresh()
 | |
| 
 | |
|     def addConstraint(self, constr):
 | |
|         self.aboutToChange.emit()
 | |
|         if constr.type == TableConstraint.TypePrimaryKey:
 | |
|             ret = self.database().connector.addTablePrimaryKey((self.schemaName(), self.name),
 | |
|                                                                constr.fields()[constr.columns[0]].name)
 | |
|         elif constr.type == TableConstraint.TypeUnique:
 | |
|             ret = self.database().connector.addTableUniqueConstraint((self.schemaName(), self.name),
 | |
|                                                                      constr.fields()[constr.columns[0]].name)
 | |
|         else:
 | |
|             return False
 | |
|         if ret is not False:
 | |
|             self.refreshConstraints()
 | |
|         return ret
 | |
| 
 | |
|     def deleteConstraint(self, constr):
 | |
|         self.aboutToChange.emit()
 | |
|         ret = self.database().connector.deleteTableConstraint((self.schemaName(), self.name), constr.name)
 | |
|         if ret is not False:
 | |
|             self.refreshConstraints()
 | |
|         return ret
 | |
| 
 | |
|     def tableIndexesFactory(self):
 | |
|         return None
 | |
| 
 | |
|     def indexes(self):
 | |
|         if self._indexes is None:
 | |
|             indexes = self.database().connector.getTableIndexes((self.schemaName(), self.name))
 | |
|             if indexes is not None:
 | |
|                 self._indexes = [self.tableIndexesFactory(x, self) for x in indexes]
 | |
|         return self._indexes
 | |
| 
 | |
|     def refreshIndexes(self):
 | |
|         self._indexes = None  # refresh table indexes
 | |
|         self.refresh()
 | |
| 
 | |
|     def addIndex(self, idx):
 | |
|         self.aboutToChange.emit()
 | |
|         ret = self.database().connector.createTableIndex((self.schemaName(), self.name), idx.name,
 | |
|                                                          idx.fields()[idx.columns[0]].name)
 | |
|         if ret is not False:
 | |
|             self.refreshIndexes()
 | |
|         return ret
 | |
| 
 | |
|     def deleteIndex(self, idx):
 | |
|         self.aboutToChange.emit()
 | |
|         ret = self.database().connector.deleteTableIndex((self.schemaName(), self.name), idx.name)
 | |
|         if ret is not False:
 | |
|             self.refreshIndexes()
 | |
|         return ret
 | |
| 
 | |
|     def tableTriggersFactory(self, row, table):
 | |
|         return None
 | |
| 
 | |
|     def triggers(self):
 | |
|         if self._triggers is None:
 | |
|             triggers = self.database().connector.getTableTriggers((self.schemaName(), self.name))
 | |
|             if triggers is not None:
 | |
|                 self._triggers = [self.tableTriggersFactory(x, self) for x in triggers]
 | |
|         return self._triggers
 | |
| 
 | |
|     def refreshTriggers(self):
 | |
|         self._triggers = None  # refresh table triggers
 | |
|         self.refresh()
 | |
| 
 | |
|     def tableRulesFactory(self, row, table):
 | |
|         return None
 | |
| 
 | |
|     def rules(self):
 | |
|         if self._rules is None:
 | |
|             rules = self.database().connector.getTableRules((self.schemaName(), self.name))
 | |
|             if rules is not None:
 | |
|                 self._rules = [self.tableRulesFactory(x, self) for x in rules]
 | |
|         return self._rules
 | |
| 
 | |
|     def refreshRules(self):
 | |
|         self._rules = None  # refresh table rules
 | |
|         self.refresh()
 | |
| 
 | |
|     def refreshRowCount(self):
 | |
|         self.aboutToChange.emit()
 | |
|         prevRowCount = self.rowCount
 | |
|         try:
 | |
|             self.rowCount = self.database().connector.getTableRowCount((self.schemaName(), self.name))
 | |
|             self.rowCount = int(self.rowCount) if self.rowCount is not None else None
 | |
|         except DbError:
 | |
|             self.rowCount = None
 | |
|         if self.rowCount != prevRowCount:
 | |
|             self.refresh()
 | |
| 
 | |
|     def runAction(self, action):
 | |
|         action = str(action)
 | |
| 
 | |
|         if action.startswith("rows/"):
 | |
|             if action == "rows/count":
 | |
|                 self.refreshRowCount()
 | |
|                 return True
 | |
| 
 | |
|         elif action.startswith("triggers/"):
 | |
|             parts = action.split('/')
 | |
|             trigger_action = parts[1]
 | |
| 
 | |
|             msg = QApplication.translate("DBManagerPlugin", "Do you want to {0} all triggers?").format(trigger_action)
 | |
|             QApplication.restoreOverrideCursor()
 | |
|             try:
 | |
|                 if QMessageBox.question(None, QApplication.translate("DBManagerPlugin", "Table triggers"), msg,
 | |
|                                         QMessageBox.Yes | QMessageBox.No) == QMessageBox.No:
 | |
|                     return False
 | |
|             finally:
 | |
|                 QApplication.setOverrideCursor(Qt.WaitCursor)
 | |
| 
 | |
|             if trigger_action == "enable" or trigger_action == "disable":
 | |
|                 enable = trigger_action == "enable"
 | |
|                 self.aboutToChange.emit()
 | |
|                 self.database().connector.enableAllTableTriggers(enable, (self.schemaName(), self.name))
 | |
|                 self.refreshTriggers()
 | |
|                 return True
 | |
| 
 | |
|         elif action.startswith("trigger/"):
 | |
|             parts = action.split('/')
 | |
|             trigger_name = parts[1]
 | |
|             trigger_action = parts[2]
 | |
| 
 | |
|             msg = QApplication.translate("DBManagerPlugin", "Do you want to {0} trigger {1}?").format(
 | |
|                 trigger_action, trigger_name)
 | |
|             QApplication.restoreOverrideCursor()
 | |
|             try:
 | |
|                 if QMessageBox.question(None, QApplication.translate("DBManagerPlugin", "Table trigger"), msg,
 | |
|                                         QMessageBox.Yes | QMessageBox.No) == QMessageBox.No:
 | |
|                     return False
 | |
|             finally:
 | |
|                 QApplication.setOverrideCursor(Qt.WaitCursor)
 | |
| 
 | |
|             if trigger_action == "delete":
 | |
|                 self.aboutToChange.emit()
 | |
|                 self.database().connector.deleteTableTrigger(trigger_name, (self.schemaName(), self.name))
 | |
|                 self.refreshTriggers()
 | |
|                 return True
 | |
| 
 | |
|             elif trigger_action == "enable" or trigger_action == "disable":
 | |
|                 enable = trigger_action == "enable"
 | |
|                 self.aboutToChange.emit()
 | |
|                 self.database().connector.enableTableTrigger(trigger_name, enable, (self.schemaName(), self.name))
 | |
|                 self.refreshTriggers()
 | |
|                 return True
 | |
| 
 | |
|         return False
 | |
| 
 | |
| 
 | |
| class VectorTable(Table):
 | |
| 
 | |
|     def __init__(self, db, schema=None, parent=None):
 | |
|         if not hasattr(self, 'type'):  # check if the superclass constructor was called yet!
 | |
|             Table.__init__(self, db, schema, parent)
 | |
|         self.type = Table.VectorType
 | |
|         self.geomColumn = self.geomType = self.geomDim = self.srid = None
 | |
|         self.estimatedExtent = self.extent = None
 | |
| 
 | |
|     def info(self):
 | |
|         from .info_model import VectorTableInfo
 | |
| 
 | |
|         return VectorTableInfo(self)
 | |
| 
 | |
|     def hasSpatialIndex(self, geom_column=None):
 | |
|         geom_column = geom_column if geom_column is not None else self.geomColumn
 | |
|         fld = None
 | |
|         for fld in self.fields():
 | |
|             if fld.name == geom_column:
 | |
|                 break
 | |
|         if fld is None:
 | |
|             return False
 | |
| 
 | |
|         for idx in self.indexes():
 | |
|             if fld.num in idx.columns:
 | |
|                 return True
 | |
|         return False
 | |
| 
 | |
|     def createSpatialIndex(self, geom_column=None):
 | |
|         self.aboutToChange.emit()
 | |
|         geom_column = geom_column if geom_column is not None else self.geomColumn
 | |
|         ret = self.database().connector.createSpatialIndex((self.schemaName(), self.name), geom_column)
 | |
|         if ret is not False:
 | |
|             self.refreshIndexes()
 | |
|         return ret
 | |
| 
 | |
|     def deleteSpatialIndex(self, geom_column=None):
 | |
|         self.aboutToChange.emit()
 | |
|         geom_column = geom_column if geom_column is not None else self.geomColumn
 | |
|         ret = self.database().connector.deleteSpatialIndex((self.schemaName(), self.name), geom_column)
 | |
|         if ret is not False:
 | |
|             self.refreshIndexes()
 | |
|         return ret
 | |
| 
 | |
|     def refreshTableExtent(self):
 | |
|         prevExtent = self.extent
 | |
|         try:
 | |
|             self.extent = self.database().connector.getTableExtent((self.schemaName(), self.name), self.geomColumn)
 | |
|         except DbError:
 | |
|             self.extent = None
 | |
|         if self.extent != prevExtent:
 | |
|             self.refresh()
 | |
| 
 | |
|     def refreshTableEstimatedExtent(self):
 | |
|         prevEstimatedExtent = self.estimatedExtent
 | |
|         try:
 | |
|             self.estimatedExtent = self.database().connector.getTableEstimatedExtent((self.schemaName(), self.name),
 | |
|                                                                                      self.geomColumn)
 | |
|         except DbError:
 | |
|             self.estimatedExtent = None
 | |
|         if self.estimatedExtent != prevEstimatedExtent:
 | |
|             self.refresh()
 | |
| 
 | |
|     def runAction(self, action):
 | |
|         action = str(action)
 | |
| 
 | |
|         if action.startswith("spatialindex/"):
 | |
|             parts = action.split('/')
 | |
|             spatialIndex_action = parts[1]
 | |
| 
 | |
|             msg = QApplication.translate("DBManagerPlugin", "Do you want to {0} spatial index for field {1}?").format(
 | |
|                 spatialIndex_action, self.geomColumn)
 | |
|             QApplication.restoreOverrideCursor()
 | |
|             try:
 | |
|                 if QMessageBox.question(None, QApplication.translate("DBManagerPlugin", "Spatial Index"), msg,
 | |
|                                         QMessageBox.Yes | QMessageBox.No) == QMessageBox.No:
 | |
|                     return False
 | |
|             finally:
 | |
|                 QApplication.setOverrideCursor(Qt.WaitCursor)
 | |
| 
 | |
|             if spatialIndex_action == "create":
 | |
|                 self.createSpatialIndex()
 | |
|                 return True
 | |
|             elif spatialIndex_action == "delete":
 | |
|                 self.deleteSpatialIndex()
 | |
|                 return True
 | |
| 
 | |
|         if action.startswith("extent/"):
 | |
|             if action == "extent/get":
 | |
|                 self.refreshTableExtent()
 | |
|                 return True
 | |
| 
 | |
|             if action == "extent/estimated/get":
 | |
|                 self.refreshTableEstimatedExtent()
 | |
|                 return True
 | |
| 
 | |
|         return Table.runAction(self, action)
 | |
| 
 | |
| 
 | |
| class RasterTable(Table):
 | |
| 
 | |
|     def __init__(self, db, schema=None, parent=None):
 | |
|         if not hasattr(self, 'type'):  # check if the superclass constructor was called yet!
 | |
|             Table.__init__(self, db, schema, parent)
 | |
|         self.type = Table.RasterType
 | |
|         self.geomColumn = self.geomType = self.pixelSizeX = self.pixelSizeY = self.pixelType = self.isExternal = self.srid = None
 | |
|         self.extent = None
 | |
| 
 | |
|     def info(self):
 | |
|         from .info_model import RasterTableInfo
 | |
| 
 | |
|         return RasterTableInfo(self)
 | |
| 
 | |
| 
 | |
| class TableSubItemObject(QObject):
 | |
| 
 | |
|     def __init__(self, table):
 | |
|         QObject.__init__(self, table)
 | |
| 
 | |
|     def table(self):
 | |
|         return self.parent()
 | |
| 
 | |
|     def database(self):
 | |
|         return self.table().database() if self.table() else None
 | |
| 
 | |
| 
 | |
| class TableField(TableSubItemObject):
 | |
| 
 | |
|     def __init__(self, table):
 | |
|         TableSubItemObject.__init__(self, table)
 | |
|         self.num = self.name = self.dataType = self.modifier = self.notNull = self.default = self.hasDefault = self.primaryKey = None
 | |
|         self.comment = None
 | |
| 
 | |
|     def type2String(self):
 | |
|         if self.modifier is None or self.modifier == -1:
 | |
|             return u"%s" % self.dataType
 | |
|         return u"%s (%s)" % (self.dataType, self.modifier)
 | |
| 
 | |
|     def default2String(self):
 | |
|         if not self.hasDefault:
 | |
|             return ''
 | |
|         return self.default if self.default is not None else "NULL"
 | |
| 
 | |
|     def definition(self):
 | |
|         from .connector import DBConnector
 | |
| 
 | |
|         quoteIdFunc = self.database().connector.quoteId if self.database() else DBConnector.quoteId
 | |
| 
 | |
|         name = quoteIdFunc(self.name)
 | |
|         not_null = "NOT NULL" if self.notNull else ""
 | |
| 
 | |
|         txt = u"%s %s %s" % (name, self.type2String(), not_null)
 | |
|         if self.hasDefault:
 | |
|             txt += u" DEFAULT %s" % self.default2String()
 | |
|         return txt
 | |
| 
 | |
|     def delete(self):
 | |
|         return self.table().deleteField(self)
 | |
| 
 | |
|     def rename(self, new_name):
 | |
|         return self.update(new_name)
 | |
| 
 | |
|     def update(self, new_name, new_type_str=None, new_not_null=None, new_default_str=None):
 | |
|         self.table().aboutToChange.emit()
 | |
|         if self.name == new_name:
 | |
|             new_name = None
 | |
|         if self.type2String() == new_type_str:
 | |
|             new_type_str = None
 | |
|         if self.notNull == new_not_null:
 | |
|             new_not_null = None
 | |
|         if self.default2String() == new_default_str:
 | |
|             new_default_str = None
 | |
| 
 | |
|         ret = self.table().database().connector.updateTableColumn((self.table().schemaName(), self.table().name),
 | |
|                                                                   self.name, new_name, new_type_str, new_not_null,
 | |
|                                                                   new_default_str)
 | |
|         if ret is not False:
 | |
|             self.table().refreshFields()
 | |
|         return ret
 | |
| 
 | |
| 
 | |
| class TableConstraint(TableSubItemObject):
 | |
| 
 | |
|     """ class that represents a constraint of a table (relation) """
 | |
| 
 | |
|     TypeCheck, TypeForeignKey, TypePrimaryKey, TypeUnique, TypeExclusion, TypeUnknown = list(range(6))
 | |
|     types = {"c": TypeCheck, "f": TypeForeignKey, "p": TypePrimaryKey, "u": TypeUnique, "x": TypeExclusion}
 | |
| 
 | |
|     onAction = {"a": "NO ACTION", "r": "RESTRICT", "c": "CASCADE", "n": "SET NULL", "d": "SET DEFAULT"}
 | |
|     matchTypes = {"u": "UNSPECIFIED", "f": "FULL", "p": "PARTIAL", "s": "SIMPLE"}
 | |
| 
 | |
|     def __init__(self, table):
 | |
|         TableSubItemObject.__init__(self, table)
 | |
|         self.name = self.type = self.columns = None
 | |
| 
 | |
|     def type2String(self):
 | |
|         if self.type == TableConstraint.TypeCheck:
 | |
|             return QApplication.translate("DBManagerPlugin", "Check")
 | |
|         if self.type == TableConstraint.TypePrimaryKey:
 | |
|             return QApplication.translate("DBManagerPlugin", "Primary key")
 | |
|         if self.type == TableConstraint.TypeForeignKey:
 | |
|             return QApplication.translate("DBManagerPlugin", "Foreign key")
 | |
|         if self.type == TableConstraint.TypeUnique:
 | |
|             return QApplication.translate("DBManagerPlugin", "Unique")
 | |
|         if self.type == TableConstraint.TypeExclusion:
 | |
|             return QApplication.translate("DBManagerPlugin", "Exclusion")
 | |
|         return QApplication.translate("DBManagerPlugin", 'Unknown')
 | |
| 
 | |
|     def fields(self):
 | |
|         def fieldFromNum(num, fields):
 | |
|             """ return field specified by its number or None if doesn't exist """
 | |
|             for fld in fields:
 | |
|                 if fld.num == num:
 | |
|                     return fld
 | |
|             return None
 | |
| 
 | |
|         fields = self.table().fields()
 | |
|         cols = {}
 | |
|         for num in self.columns:
 | |
|             cols[num] = fieldFromNum(num, fields)
 | |
|         return cols
 | |
| 
 | |
|     def delete(self):
 | |
|         return self.table().deleteConstraint(self)
 | |
| 
 | |
| 
 | |
| class TableIndex(TableSubItemObject):
 | |
| 
 | |
|     def __init__(self, table):
 | |
|         TableSubItemObject.__init__(self, table)
 | |
|         self.name = self.columns = self.isUnique = None
 | |
| 
 | |
|     def fields(self):
 | |
|         def fieldFromNum(num, fields):
 | |
|             """ return field specified by its number or None if doesn't exist """
 | |
|             for fld in fields:
 | |
|                 if fld.num == num:
 | |
|                     return fld
 | |
|             return None
 | |
| 
 | |
|         fields = self.table().fields()
 | |
|         cols = {}
 | |
|         for num in self.columns:
 | |
|             cols[num] = fieldFromNum(num, fields)
 | |
|         return cols
 | |
| 
 | |
|     def delete(self):
 | |
|         return self.table().deleteIndex(self)
 | |
| 
 | |
| 
 | |
| class TableTrigger(TableSubItemObject):
 | |
| 
 | |
|     """ class that represents a trigger """
 | |
| 
 | |
|     # Bits within tgtype (pg_trigger.h)
 | |
|     TypeRow = (1 << 0)  # row or statement
 | |
|     TypeBefore = (1 << 1)  # before or after
 | |
|     # events: one or more
 | |
|     TypeInsert = (1 << 2)
 | |
|     TypeDelete = (1 << 3)
 | |
|     TypeUpdate = (1 << 4)
 | |
|     TypeTruncate = (1 << 5)
 | |
| 
 | |
|     def __init__(self, table):
 | |
|         TableSubItemObject.__init__(self, table)
 | |
|         self.name = self.function = None
 | |
| 
 | |
|     def type2String(self):
 | |
|         trig_type = u''
 | |
|         trig_type += "Before " if self.type & TableTrigger.TypeBefore else "After "
 | |
|         if self.type & TableTrigger.TypeInsert:
 | |
|             trig_type += "INSERT "
 | |
|         if self.type & TableTrigger.TypeUpdate:
 | |
|             trig_type += "UPDATE "
 | |
|         if self.type & TableTrigger.TypeDelete:
 | |
|             trig_type += "DELETE "
 | |
|         if self.type & TableTrigger.TypeTruncate:
 | |
|             trig_type += "TRUNCATE "
 | |
|         trig_type += "\n"
 | |
|         trig_type += "for each "
 | |
|         trig_type += "row" if self.type & TableTrigger.TypeRow else "statement"
 | |
|         return trig_type
 | |
| 
 | |
| 
 | |
| class TableRule(TableSubItemObject):
 | |
| 
 | |
|     def __init__(self, table):
 | |
|         TableSubItemObject.__init__(self, table)
 | |
|         self.name = self.definition = None
 |