2012-04-16 13:19:40 +02:00
# -*- coding: utf-8 -*-
"""
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Name : DB Manager
2013-06-09 18:28:52 +02:00
Description : Database manager plugin for QGIS
2012-04-16 13:19:40 +02:00
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 PyQt4 . QtCore import *
from PyQt4 . QtGui import *
from . . db_plugins import createDbPlugin
from . html_elems import HtmlParagraph , HtmlTable
class BaseError ( Exception ) :
""" Base class for exceptions in the plugin. """
def __init__ ( self , e ) :
2013-05-27 01:29:53 +02:00
if isinstance ( e , Exception ) :
msg = e . args [ 0 ] if len ( e . args ) > 0 else ' '
else :
msg = e
2012-04-16 13:19:40 +02:00
try :
msg = unicode ( msg )
except UnicodeDecodeError :
msg = unicode ( msg , ' utf-8 ' )
2013-05-27 01:29:53 +02:00
self . msg = msg
2012-04-16 13:19:40 +02:00
Exception . __init__ ( self , msg )
def __unicode__ ( self ) :
2013-05-27 01:29:53 +02:00
return self . msg
2012-04-16 13:19:40 +02:00
def __str__ ( self ) :
return unicode ( 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 = unicode ( query ) if query != None else None
def __unicode__ ( self ) :
2013-05-27 01:29:53 +02:00
if self . query is None :
2012-04-16 13:19:40 +02:00
return BaseError . __unicode__ ( self )
2013-06-09 18:28:52 +02:00
msg = QApplication . translate ( " DBManagerPlugin " , " Error: \n %s " ) % BaseError . __unicode__ ( self )
2012-04-16 13:19:40 +02:00
if self . query :
2013-06-09 18:28:52 +02:00
msg + = QApplication . translate ( " DBManagerPlugin " , " \n \n Query: \n %s " ) % self . query
2012-04-16 13:19:40 +02:00
return msg
class DBPlugin ( QObject ) :
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 connectionName ( self ) :
return self . connName
def database ( self ) :
return self . db
def info ( self ) :
from . info_model import DatabaseInfo
return DatabaseInfo ( None )
def connectToUri ( self , uri ) :
self . db = self . databasesFactory ( self , uri )
2012-12-10 00:12:07 +01:00
if self . db :
2012-04-16 13:19:40 +02:00
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 ( ) )
@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 = QSettings ( )
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 ) :
2012-12-10 00:12:07 +01:00
return None
2012-04-16 13:19:40 +02:00
class DbItemObject ( QObject ) :
def __init__ ( self , parent = None ) :
QObject . __init__ ( self , parent )
def database ( self ) :
return None
def refresh ( self ) :
self . emit ( SIGNAL ( ' changed ' ) ) # refresh the item data reading them from the db
def aboutToChange ( self ) :
self . emit ( SIGNAL ( ' aboutToChange ' ) )
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 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 )
2012-08-02 23:57:11 +02:00
def toSqlLayer ( self , sql , geomCol , uniqueCol , layerName = " QueryLayer " , layerType = None , avoidSelectById = False ) :
2012-04-16 13:19:40 +02:00
from qgis . core import QgsMapLayer , QgsVectorLayer , QgsRasterLayer
uri = self . uri ( )
2013-06-04 07:20:54 +02:00
uri . setDataSource ( " " , u " ( %s \n ) " % sql , geomCol , " " , uniqueCol )
2012-08-02 23:57:11 +02:00
if avoidSelectById :
uri . disableSelectAtId ( True )
2012-04-16 13:19:40 +02:00
provider = self . dbplugin ( ) . providerName ( )
if layerType == QgsMapLayer . RasterLayer :
return QgsRasterLayer ( uri . uri ( ) , layerName , provider )
return QgsVectorLayer ( uri . uri ( ) , 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 ( ) )
except ImportError :
pass
else :
load ( self , mainWindow )
def registerDatabaseActions ( self , mainWindow ) :
2013-06-09 18:28:52 +02:00
action = QAction ( QApplication . translate ( " DBManagerPlugin " , " &Re-connect " ) , self )
mainWindow . registerAction ( action , QApplication . translate ( " DBManagerPlugin " , " &Database " ) , self . reconnectActionSlot )
2012-12-10 00:12:07 +01:00
2012-04-16 13:19:40 +02:00
if self . schemas ( ) != None :
2013-06-09 18:28:52 +02:00
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 )
2012-04-16 13:19:40 +02:00
2013-06-09 18:28:52 +02:00
action = QAction ( QApplication . translate ( " DBManagerPlugin " , " Delete selected item " ) , self )
2012-04-16 13:19:40 +02:00
mainWindow . registerAction ( action , None , self . deleteActionSlot )
action . setShortcuts ( QKeySequence . Delete )
2013-06-09 18:28:52 +02:00
action = QAction ( QIcon ( " :/db_manager/actions/create_table " ) , QApplication . translate ( " DBManagerPlugin " , " &Create table " ) , self )
mainWindow . registerAction ( action , QApplication . translate ( " DBManagerPlugin " , " &Table " ) , self . createTableActionSlot )
action = QAction ( QIcon ( " :/db_manager/actions/edit_table " ) , QApplication . translate ( " DBManagerPlugin " , " &Edit table " ) , self )
mainWindow . registerAction ( action , QApplication . translate ( " DBManagerPlugin " , " &Table " ) , self . editTableActionSlot )
action = QAction ( QIcon ( " :/db_manager/actions/del_table " ) , 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 )
2012-04-16 13:19:40 +02:00
if self . schemas ( ) != None :
2013-06-09 18:28:52 +02:00
action = QAction ( QApplication . translate ( " DBManagerPlugin " , " &Move to schema " ) , self )
2012-04-16 13:19:40 +02:00
action . setMenu ( QMenu ( mainWindow ) )
invoke_callback = lambda : mainWindow . invokeCallback ( self . prepareMenuMoveTableToSchemaActionSlot )
QObject . connect ( action . menu ( ) , SIGNAL ( " aboutToShow() " ) , invoke_callback )
2013-06-09 18:28:52 +02:00
mainWindow . registerAction ( action , QApplication . translate ( " DBManagerPlugin " , " &Table " ) )
2012-04-16 13:19:40 +02:00
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 ( )
2013-06-09 18:28:52 +02:00
QMessageBox . information ( parent , QApplication . translate ( " DBManagerPlugin " , " Sorry " ) , QApplication . translate ( " DBManagerPlugin " , " Cannot delete the selected item. " ) )
2012-04-16 13:19:40 +02:00
QApplication . setOverrideCursor ( Qt . WaitCursor )
def createSchemaActionSlot ( self , item , action , parent ) :
QApplication . restoreOverrideCursor ( )
try :
if not isinstance ( item , ( DBPlugin , Schema , Table ) ) or item . database ( ) == None :
2013-06-09 18:28:52 +02:00
QMessageBox . information ( parent , QApplication . translate ( " DBManagerPlugin " , " Sorry " ) , QApplication . translate ( " DBManagerPlugin " , " No database selected or you are not connected to it. " ) )
2012-04-16 13:19:40 +02:00
return
2013-06-09 18:28:52 +02:00
( schema , ok ) = QInputDialog . getText ( parent , QApplication . translate ( " DBManagerPlugin " , " New schema " ) , QApplication . translate ( " DBManagerPlugin " , " Enter new schema name " ) )
2012-04-16 13:19:40 +02:00
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 ) :
2013-06-09 18:28:52 +02:00
QMessageBox . information ( parent , QApplication . translate ( " DBManagerPlugin " , " Sorry " ) , QApplication . translate ( " DBManagerPlugin " , " Select an empty SCHEMA for deletion. " ) )
2012-04-16 13:19:40 +02:00
return
2013-06-09 18:28:52 +02:00
res = QMessageBox . question ( parent , QApplication . translate ( " DBManagerPlugin " , " hey! " ) , QApplication . translate ( " DBManagerPlugin " , " Really delete schema %s ? " ) % item . name , QMessageBox . Yes | QMessageBox . No )
2012-04-16 13:19:40 +02:00
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 != None :
schemas = map ( lambda x : self . schemasFactory ( x , self ) , 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 ( ) == None :
2013-06-09 18:28:52 +02:00
QMessageBox . information ( parent , QApplication . translate ( " DBManagerPlugin " , " Sorry " ) , QApplication . translate ( " DBManagerPlugin " , " No database selected or you are not connected to it. " ) )
2012-04-16 13:19:40 +02:00
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 :
2013-06-09 18:28:52 +02:00
QMessageBox . information ( parent , QApplication . translate ( " DBManagerPlugin " , " Sorry " ) , QApplication . translate ( " DBManagerPlugin " , " Select a TABLE for editation. " ) )
2012-04-16 13:19:40 +02:00
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 ) :
2013-06-09 18:28:52 +02:00
QMessageBox . information ( parent , QApplication . translate ( " DBManagerPlugin " , " Sorry " ) , QApplication . translate ( " DBManagerPlugin " , " Select a TABLE/VIEW for deletion. " ) )
2012-04-16 13:19:40 +02:00
return
2013-06-09 18:28:52 +02:00
res = QMessageBox . question ( parent , QApplication . translate ( " DBManagerPlugin " , " hey! " ) , QApplication . translate ( " DBManagerPlugin " , " Really delete table/view %s ? " ) % item . name , QMessageBox . Yes | QMessageBox . No )
2012-04-16 13:19:40 +02:00
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 :
2013-06-09 18:28:52 +02:00
QMessageBox . information ( parent , QApplication . translate ( " DBManagerPlugin " , " Sorry " ) , QApplication . translate ( " DBManagerPlugin " , " Select a TABLE to empty it. " ) )
2012-04-16 13:19:40 +02:00
return
2013-06-09 18:28:52 +02:00
res = QMessageBox . question ( parent , QApplication . translate ( " DBManagerPlugin " , " hey! " ) , QApplication . translate ( " DBManagerPlugin " , " Really delete all items from table %s ? " ) % item . name , QMessageBox . Yes | QMessageBox . No )
2012-04-16 13:19:40 +02:00
if res != QMessageBox . Yes :
return
finally :
QApplication . setOverrideCursor ( Qt . WaitCursor )
item . empty ( )
def prepareMenuMoveTableToSchemaActionSlot ( self , item , menu , mainWindow ) :
""" populate menu with schemas """
slot = lambda x : lambda : mainWindow . invokeCallback ( self . moveTableToSchemaActionSlot , [ x ] )
menu . clear ( )
for schema in self . schemas ( ) :
action = menu . addAction ( schema . name , slot ( schema ) )
2012-12-10 00:12:07 +01:00
2012-04-16 13:19:40 +02:00
def moveTableToSchemaActionSlot ( self , item , action , parent , new_schema ) :
QApplication . restoreOverrideCursor ( )
try :
if not isinstance ( item , Table ) :
2013-06-09 18:28:52 +02:00
QMessageBox . information ( parent , QApplication . translate ( " DBManagerPlugin " , " Sorry " ) , QApplication . translate ( " DBManagerPlugin " , " Select a TABLE/VIEW. " ) )
2012-04-16 13:19:40 +02:00
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 ) :
tables = self . connector . getTables ( schema . name if schema else None )
if tables != None :
tables = map ( lambda x : self . tablesFactory ( x , self , schema ) , tables )
return tables
def createTable ( self , table , fields , schema = None ) :
field_defs = map ( lambda x : x . definition ( ) , fields )
pkeys = filter ( lambda x : x . primaryKey , fields )
pk_name = pkeys [ 0 ] . name if len ( pkeys ) > 0 else None
ret = self . connector . createTable ( ( schema , table ) , field_defs , pk_name )
if ret != False :
self . refresh ( )
return ret
def createVectorTable ( self , table , fields , geom , schema = None ) :
ret = self . createTable ( table , fields , schema )
if ret == False :
return False
try :
createGeomCol = geom != None
if createGeomCol :
geomCol , geomType , geomSrid , geomDim = geom [ : 4 ]
createSpatialIndex = geom [ 4 ] == True 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
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 ( )
ret = self . database ( ) . connector . deleteSchema ( self . name )
if ret != False :
self . emit ( SIGNAL ( ' deleted ' ) )
return ret
def rename ( self , new_name ) :
self . aboutToChange ( )
ret = self . database ( ) . connector . renameSchema ( self . name , new_name )
if ret != 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 = 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 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 ( )
if self . isView :
ret = self . database ( ) . connector . deleteView ( ( self . schemaName ( ) , self . name ) )
else :
ret = self . database ( ) . connector . deleteTable ( ( self . schemaName ( ) , self . name ) )
2012-12-10 00:12:07 +01:00
if ret != False :
2012-04-16 13:19:40 +02:00
self . emit ( SIGNAL ( ' deleted ' ) )
return ret
def rename ( self , new_name ) :
self . aboutToChange ( )
ret = self . database ( ) . connector . renameTable ( ( self . schemaName ( ) , self . name ) , new_name )
if ret != False :
self . name = new_name
self . refresh ( )
return ret
def empty ( self ) :
self . aboutToChange ( )
ret = self . database ( ) . connector . emptyTable ( ( self . schemaName ( ) , self . name ) )
if ret != False :
self . refreshRowCount ( )
return ret
def moveToSchema ( self , schema ) :
self . aboutToChange ( )
if self . schema ( ) == schema :
return True
ret = self . database ( ) . connector . moveTableToSchema ( ( self . schemaName ( ) , self . name ) , schema . name )
if ret != 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 ' '
2013-06-04 07:20:54 +02:00
geomCol = self . geomColumn if self . type in [ Table . VectorType , Table . RasterType ] else " "
2012-06-09 23:51:27 +01:00
uniqueCol = self . getValidQGisUniqueFields ( True ) if self . isView else None
2013-06-04 07:20:54 +02:00
uri . setDataSource ( schema , self . name , geomCol if geomCol else " " , " " , uniqueCol . name if uniqueCol else " " )
2012-04-16 13:19:40 +02:00
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 ( ) )
def toMapLayer ( self ) :
from qgis . core import QgsVectorLayer , QgsRasterLayer
provider = self . database ( ) . dbplugin ( ) . providerName ( )
uri = self . uri ( ) . uri ( )
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.
2012-12-10 00:12:07 +01:00
QGis automatically search for a valid unique field , so it ' s
2012-06-09 23:51:27 +01:00
needed only for queries and views """
2012-04-16 13:19:40 +02:00
ret = [ ]
# add the pk
pkcols = filter ( lambda x : x . primaryKey , self . fields ( ) )
if len ( pkcols ) == 1 : ret . append ( pkcols [ 0 ] )
2012-06-09 23:51:27 +01:00
# then add both oid, serial and int fields with an unique index
2012-04-16 13:19:40 +02:00
indexes = self . indexes ( )
if indexes != None :
for idx in indexes :
if idx . isUnique and len ( idx . columns ) == 1 :
fld = idx . fields ( ) [ idx . columns [ 0 ] ]
2012-06-09 23:51:27 +01:00
if fld . dataType in [ " oid " , " serial " , " int4 " , " int8 " ] and fld not in ret :
2012-04-16 13:19:40 +02:00
ret . append ( fld )
2012-06-09 23:51:27 +01:00
# 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 )
2012-04-16 13:19:40 +02:00
if onlyOne :
return ret [ 0 ] if len ( ret ) > 0 else None
return ret
def tableDataModel ( self , parent ) :
pass
def tableFieldsFactory ( self ) :
return None
def fields ( self ) :
if self . _fields == None :
fields = self . database ( ) . connector . getTableFields ( ( self . schemaName ( ) , self . name ) )
if fields != None :
self . _fields = map ( lambda x : self . tableFieldsFactory ( x , self ) , fields )
return self . _fields
def refreshFields ( self ) :
self . _fields = None # refresh table fields
self . refresh ( )
def addField ( self , fld ) :
self . aboutToChange ( )
ret = self . database ( ) . connector . addTableColumn ( ( self . schemaName ( ) , self . name ) , fld . definition ( ) )
if ret != False :
self . refreshFields ( )
return ret
def deleteField ( self , fld ) :
self . aboutToChange ( )
ret = self . database ( ) . connector . deleteTableColumn ( ( self . schemaName ( ) , self . name ) , fld . name )
if ret != False :
self . refreshFields ( )
2012-12-10 00:28:42 +01:00
self . refreshConstraints ( )
self . refreshIndexes ( )
2012-04-16 13:19:40 +02:00
return ret
def addGeometryColumn ( self , geomCol , geomType , srid , dim , createSpatialIndex = False ) :
self . aboutToChange ( )
ret = self . database ( ) . connector . addGeometryColumn ( ( self . schemaName ( ) , self . name ) , geomCol , geomType , srid , dim )
if ret == False :
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 == None :
constraints = self . database ( ) . connector . getTableConstraints ( ( self . schemaName ( ) , self . name ) )
if constraints != None :
self . _constraints = map ( lambda x : self . tableConstraintsFactory ( x , self ) , constraints )
return self . _constraints
def refreshConstraints ( self ) :
self . _constraints = None # refresh table constraints
self . refresh ( )
def addConstraint ( self , constr ) :
self . aboutToChange ( )
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 != False :
self . refreshConstraints ( )
return ret
def deleteConstraint ( self , constr ) :
self . aboutToChange ( )
ret = self . database ( ) . connector . deleteTableConstraint ( ( self . schemaName ( ) , self . name ) , constr . name )
if ret != False :
self . refreshConstraints ( )
return ret
def tableIndexesFactory ( self ) :
return None
def indexes ( self ) :
if self . _indexes == None :
indexes = self . database ( ) . connector . getTableIndexes ( ( self . schemaName ( ) , self . name ) )
if indexes != None :
self . _indexes = map ( lambda x : self . tableIndexesFactory ( x , self ) , indexes )
return self . _indexes
def refreshIndexes ( self ) :
self . _indexes = None # refresh table indexes
self . refresh ( )
def addIndex ( self , idx ) :
self . aboutToChange ( )
ret = self . database ( ) . connector . createTableIndex ( ( self . schemaName ( ) , self . name ) , idx . name , idx . fields ( ) [ idx . columns [ 0 ] ] . name )
if ret != False :
self . refreshIndexes ( )
return ret
def deleteIndex ( self , idx ) :
self . aboutToChange ( )
ret = self . database ( ) . connector . deleteTableIndex ( ( self . schemaName ( ) , self . name ) , idx . name )
if ret != False :
self . refreshIndexes ( )
return ret
def tableTriggersFactory ( self , row , table ) :
return None
def triggers ( self ) :
if self . _triggers == None :
triggers = self . database ( ) . connector . getTableTriggers ( ( self . schemaName ( ) , self . name ) )
if triggers != None :
self . _triggers = map ( lambda x : self . tableTriggersFactory ( x , self ) , 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 == None :
rules = self . database ( ) . connector . getTableRules ( ( self . schemaName ( ) , self . name ) )
if rules != None :
self . _rules = map ( lambda x : self . tableRulesFactory ( x , self ) , rules )
return self . _rules
def refreshRules ( self ) :
self . _rules = None # refresh table rules
self . refresh ( )
def refreshRowCount ( self ) :
self . aboutToChange ( )
prevRowCount = self . rowCount
try :
self . rowCount = self . database ( ) . connector . getTableRowCount ( ( self . schemaName ( ) , self . name ) )
self . rowCount = int ( self . rowCount ) if self . rowCount != None else None
except DbError :
self . rowCount = None
if self . rowCount != prevRowCount :
self . refresh ( )
def runAction ( self , action ) :
action = unicode ( action )
if action . startswith ( " rows/ " ) :
if action == " rows/count " :
self . refreshRowCount ( )
return True
elif action . startswith ( " triggers/ " ) :
parts = action . split ( ' / ' )
trigger_action = parts [ 1 ]
2013-06-09 18:28:52 +02:00
msg = QApplication . translate ( " DBManagerPlugin " , " Do you want to %s all triggers? " ) % trigger_action
2012-04-16 13:19:40 +02:00
QApplication . restoreOverrideCursor ( )
try :
2013-06-09 18:28:52 +02:00
if QMessageBox . question ( None , QApplication . translate ( " DBManagerPlugin " , " Table triggers " ) , msg , QMessageBox . Yes | QMessageBox . No ) == QMessageBox . No :
2012-04-16 13:19:40 +02:00
return False
finally :
QApplication . setOverrideCursor ( Qt . WaitCursor )
if trigger_action == " enable " or trigger_action == " disable " :
enable = trigger_action == " enable "
self . aboutToChange ( )
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 ]
2013-06-09 18:28:52 +02:00
msg = QApplication . translate ( " DBManagerPlugin " , " Do you want to %s trigger %s ? " ) % ( trigger_action , trigger_name )
2012-04-16 13:19:40 +02:00
QApplication . restoreOverrideCursor ( )
try :
2013-06-09 18:28:52 +02:00
if QMessageBox . question ( None , QApplication . translate ( " DBManagerPlugin " , " Table trigger " ) , msg , QMessageBox . Yes | QMessageBox . No ) == QMessageBox . No :
2012-04-16 13:19:40 +02:00
return False
finally :
QApplication . setOverrideCursor ( Qt . WaitCursor )
if trigger_action == " delete " :
self . aboutToChange ( )
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 ( )
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 != None else self . geomColumn
fld = None
for fld in self . fields ( ) :
if fld . name == geom_column :
break
if fld == 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 ( )
geom_column = geom_column if geom_column != None else self . geomColumn
ret = self . database ( ) . connector . createSpatialIndex ( ( self . schemaName ( ) , self . name ) , geom_column )
if ret != False :
self . refreshIndexes ( )
return ret
def deleteSpatialIndex ( self , geom_column = None ) :
self . aboutToChange ( )
geom_column = geom_column if geom_column != None else self . geomColumn
ret = self . database ( ) . connector . deleteSpatialIndex ( ( self . schemaName ( ) , self . name ) , geom_column )
if ret != 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 = unicode ( action )
if action . startswith ( " spatialindex/ " ) :
parts = action . split ( ' / ' )
spatialIndex_action = parts [ 1 ]
2013-06-09 18:28:52 +02:00
msg = QApplication . translate ( " DBManagerPlugin " , " Do you want to %s spatial index for field %s ? " ) % ( spatialIndex_action , self . geomColumn )
2012-04-16 13:19:40 +02:00
QApplication . restoreOverrideCursor ( )
try :
2013-06-09 18:28:52 +02:00
if QMessageBox . question ( None , QApplication . translate ( " DBManagerPlugin " , " Spatial Index " ) , msg , QMessageBox . Yes | QMessageBox . No ) == QMessageBox . No :
2012-04-16 13:19:40 +02:00
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 == 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 != 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 ( )
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 != False :
self . table ( ) . refreshFields ( )
return ret
class TableConstraint ( TableSubItemObject ) :
""" class that represents a constraint of a table (relation) """
2012-12-10 00:12:07 +01:00
2013-05-27 01:24:56 +02:00
TypeCheck , TypeForeignKey , TypePrimaryKey , TypeUnique , TypeExclusion , TypeUnknown = range ( 6 )
types = { " c " : TypeCheck , " f " : TypeForeignKey , " p " : TypePrimaryKey , " u " : TypeUnique , " x " : TypeExclusion }
2012-12-10 00:12:07 +01:00
2012-04-16 13:19:40 +02:00
onAction = { " a " : " NO ACTION " , " r " : " RESTRICT " , " c " : " CASCADE " , " n " : " SET NULL " , " d " : " SET DEFAULT " }
2013-12-27 15:00:20 -05:00
matchTypes = { " u " : " UNSPECIFIED " , " f " : " FULL " , " p " : " PARTIAL " , " s " : " SIMPLE " }
2012-04-16 13:19:40 +02:00
def __init__ ( self , table ) :
TableSubItemObject . __init__ ( self , table )
self . name = self . type = self . columns = None
def type2String ( self ) :
2013-06-09 18:28:52 +02:00
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 ' )
2012-04-16 13:19:40 +02:00
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 """
2012-12-10 00:12:07 +01:00
2012-04-16 13:19:40 +02:00
# 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
2012-12-10 00:12:07 +01:00