1# -*- coding: utf-8 -*-
2
3"""
4/***************************************************************************
5Name                 : DB Manager plugin for virtual layers
6Date                 : December 2015
7copyright            : (C) 2015 by Hugo Mercier
8email                : hugo dot mercier at oslandia dot com
9
10 ***************************************************************************/
11
12/***************************************************************************
13 *                                                                         *
14 *   This program is free software; you can redistribute it and/or modify  *
15 *   it under the terms of the GNU General Public License as published by  *
16 *   the Free Software Foundation; either version 2 of the License, or     *
17 *   (at your option) any later version.                                   *
18 *                                                                         *
19 ***************************************************************************/
20"""
21
22# this will disable the dbplugin if the connector raise an ImportError
23from .connector import VLayerConnector
24
25from qgis.PyQt.QtCore import QCoreApplication
26from qgis.PyQt.QtGui import QIcon
27from qgis.core import QgsApplication, QgsVectorLayer, QgsProject, QgsVirtualLayerDefinition
28
29from ..plugin import DBPlugin, Database, Table, VectorTable, TableField
30
31
32def classFactory():
33    return VLayerDBPlugin
34
35
36class VLayerDBPlugin(DBPlugin):
37
38    @classmethod
39    def icon(self):
40        return QgsApplication.getThemeIcon("/mIconVirtualLayer.svg")
41
42    def connectionIcon(self):
43        return QgsApplication.getThemeIcon("/providerQgis.svg")
44
45    @classmethod
46    def typeName(self):
47        return 'vlayers'
48
49    @classmethod
50    def typeNameString(self):
51        return QCoreApplication.translate('db_manager', 'Virtual Layers')
52
53    @classmethod
54    def providerName(self):
55        return 'virtual'
56
57    @classmethod
58    def connectionSettingsKey(self):
59        return 'vlayers'
60
61    @classmethod
62    def connections(self):
63        return [VLayerDBPlugin(QCoreApplication.translate('db_manager', 'Project layers'))]
64
65    def databasesFactory(self, connection, uri):
66        return FakeDatabase(connection, uri)
67
68    def database(self):
69        return self.db
70
71    # def info( self ):
72
73    def connect(self, parent=None):
74        self.connectToUri("qgis")
75        return True
76
77
78class FakeDatabase(Database):
79
80    def __init__(self, connection, uri):
81        Database.__init__(self, connection, uri)
82
83    def connectorsFactory(self, uri):
84        return VLayerConnector(uri)
85
86    def dataTablesFactory(self, row, db, schema=None):
87        return LTable(row, db, schema)
88
89    def vectorTablesFactory(self, row, db, schema=None):
90        return LVectorTable(row, db, schema)
91
92    def rasterTablesFactory(self, row, db, schema=None):
93        return None
94
95    def info(self):
96        from .info_model import LDatabaseInfo
97        return LDatabaseInfo(self)
98
99    def sqlResultModel(self, sql, parent):
100        from .data_model import LSqlResultModel
101        return LSqlResultModel(self, sql, parent)
102
103    def sqlResultModelAsync(self, sql, parent):
104        from .data_model import LSqlResultModelAsync
105        return LSqlResultModelAsync(self, sql, parent)
106
107    def toSqlLayer(self, sql, geomCol, uniqueCol, layerName="QueryLayer", layerType=None, avoidSelectById=False, _filter=""):
108        df = QgsVirtualLayerDefinition()
109        df.setQuery(sql)
110        if uniqueCol is not None:
111            uniqueCol = uniqueCol.strip('"').replace('""', '"')
112            df.setUid(uniqueCol)
113        if geomCol is not None:
114            df.setGeometryField(geomCol)
115        vl = QgsVectorLayer(df.toString(), layerName, "virtual")
116        if _filter:
117            vl.setSubsetString(_filter)
118        return vl
119
120    def registerDatabaseActions(self, mainWindow):
121        return
122
123    def runAction(self, action):
124        return
125
126    def uniqueIdFunction(self):
127        return None
128
129    def explicitSpatialIndex(self):
130        return True
131
132    def spatialIndexClause(self, src_table, src_column, dest_table, dest_column):
133        return '"%s"._search_frame_ = "%s"."%s"' % (src_table, dest_table, dest_column)
134
135    def supportsComment(self):
136        return False
137
138
139class LTable(Table):
140
141    def __init__(self, row, db, schema=None):
142        Table.__init__(self, db, None)
143        self.name, self.isView, self.isSysTable = row
144
145    def tableFieldsFactory(self, row, table):
146        return LTableField(row, table)
147
148    def tableDataModel(self, parent):
149        from .data_model import LTableDataModel
150        return LTableDataModel(self, parent)
151
152    def canBeAddedToCanvas(self):
153        return False
154
155
156class LVectorTable(LTable, VectorTable):
157
158    def __init__(self, row, db, schema=None):
159        LTable.__init__(self, row[:-5], db, schema)
160        VectorTable.__init__(self, db, schema)
161        # SpatiaLite does case-insensitive checks for table names, but the
162        # SL provider didn't do the same in QGIS < 1.9, so self.geomTableName
163        # stores the table name like stored in the geometry_columns table
164        self.geomTableName, self.geomColumn, self.geomType, self.geomDim, self.srid = row[
165            -5:]
166
167    def uri(self):
168        uri = self.database().uri()
169        uri.setDataSource('', self.geomTableName, self.geomColumn)
170        return uri
171
172    def hasSpatialIndex(self, geom_column=None):
173        return True
174
175    def createSpatialIndex(self, geom_column=None):
176        return
177
178    def deleteSpatialIndex(self, geom_column=None):
179        return
180
181    def refreshTableEstimatedExtent(self):
182        self.extent = self.database().connector.getTableExtent(
183            ("id", self.geomTableName), None)
184
185    def runAction(self, action):
186        return
187
188    def toMapLayer(self, geometryType=None, crs=None):
189        return QgsProject.instance().mapLayer(self.geomTableName)
190
191
192class LTableField(TableField):
193
194    def __init__(self, row, table):
195        TableField.__init__(self, table)
196        self.num, self.name, self.dataType, self.notNull, self.default, self.primaryKey = row
197        self.hasDefault = self.default
198