Skip to content

Commit 644bdf0

Browse files
committed
Merge pull request #2222 from medspx/dbmanager-oracle
DBManager Oracle Spatial support (fixes #9163)
2 parents 7439db1 + bda76e5 commit 644bdf0

15 files changed

+4344
-1
lines changed

python/plugins/db_manager/README

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ It allows showing the DBs contents and run query on them.
66
In this moment DB Manager supports the following DBMS backends:
77
- PostgreSQL/PostGIS through the psycopg2 pymodule
88
- SQLite/SpatiaLite using the pyspatialite pymodule
9-
9+
- Oracle Spatial using PyQt QtSql module
1010

1111
For more info about the project, see at the wiki page:
1212
http://qgis.org/wiki/DB_Manager_plugin_GSoC_2011
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
ADD_SUBDIRECTORY(postgis)
22
ADD_SUBDIRECTORY(spatialite)
3+
ADD_SUBDIRECTORY(oracle)
34

45
FILE(GLOB PY_FILES *.py)
56
PLUGIN_INSTALL(db_manager db_plugins ${PY_FILES})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
2+
FILE(GLOB PY_FILES *.py)
3+
FILE(GLOB ICON_FILES icons/*.png)
4+
5+
PYQT4_ADD_RESOURCES(PYRC_FILES resources.qrc)
6+
7+
PLUGIN_INSTALL(db_manager db_plugins/oracle ${PY_FILES} ${PYRC_FILES})
8+
PLUGIN_INSTALL(db_manager db_plugins/oracle/icons ${ICON_FILES})
9+

python/plugins/db_manager/db_plugins/oracle/LICENSE

+339
Large diffs are not rendered by default.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
# -*- coding: utf-8 -*-
2+
3+
"""
4+
/***************************************************************************
5+
Name : QtSqlDB
6+
Description : DB API 2.0 interface for QtSql
7+
Date : June 6, 2015
8+
Copyright : (C) 2015 by Jürgen E. Fischer
9+
email : jef at norbit dot de
10+
11+
***************************************************************************/
12+
13+
/***************************************************************************
14+
* *
15+
* This program is free software; you can redistribute it and/or modify *
16+
* it under the terms of the GNU General Public License as published by *
17+
* the Free Software Foundation; either version 2 of the License, or *
18+
* (at your option) any later version. *
19+
* *
20+
***************************************************************************/
21+
"""
22+
23+
from PyQt4.QtCore import QVariant, QDate, QTime, QDateTime, QByteArray
24+
from PyQt4.QtSql import QSqlDatabase, QSqlQuery, QSqlField
25+
26+
paramstyle = "qmark"
27+
threadsafety = 1
28+
apilevel = "2.0"
29+
30+
import time
31+
import datetime
32+
33+
34+
def Date(year, month, day):
35+
return datetime.date(year, month, day)
36+
37+
38+
def Time(hour, minute, second):
39+
return datetime.time(hour, minute, second)
40+
41+
42+
def Timestamp(year, month, day, hour, minute, second):
43+
return datetime.datetime(year, month, day, hour, minute, second)
44+
45+
46+
def DateFromTicks(ticks):
47+
return Date(*time.localtime(ticks)[:3])
48+
49+
50+
def TimeFromTicks(ticks):
51+
return Time(*time.localtime(ticks)[3:6])
52+
53+
54+
def TimestampFromTicks(ticks):
55+
return Timestamp(*time.localtime(ticks)[:6])
56+
57+
58+
class ConnectionError(StandardError):
59+
60+
def __init__(self, *args, **kwargs):
61+
super(StandardError, self).__init__(*args, **kwargs)
62+
63+
64+
class ExecError(StandardError):
65+
66+
def __init__(self, *args, **kwargs):
67+
super(StandardError, self).__init__(*args, **kwargs)
68+
69+
70+
class QtSqlDBCursor:
71+
72+
def __init__(self, conn):
73+
self.qry = QSqlQuery(conn)
74+
self.description = None
75+
self.rowcount = -1
76+
self.arraysize = 1
77+
78+
def close(self):
79+
self.qry.finish()
80+
81+
def execute(self, operation, parameters=[]):
82+
if len(parameters) == 0:
83+
if not self.qry.exec_(operation):
84+
raise ExecError(self.qry.lastError().databaseText())
85+
else:
86+
if not self.qry.prepare(operation):
87+
raise ExecError(self.qry.lastError().databaseText())
88+
89+
for i in range(len(parameters)):
90+
self.qry.bindValue(i, parameters[i])
91+
92+
if not self.qry.exec_():
93+
raise ExecError(self.qry.lastError().databaseText())
94+
95+
self.rowcount = self.qry.size()
96+
self.description = []
97+
for c in range(self.qry.record().count()):
98+
f = self.qry.record().field(c)
99+
100+
if f.type() == QVariant.Date:
101+
t = Date
102+
elif f.type() == QVariant.Time:
103+
t = Time
104+
elif f.type() == QVariant.DateTime:
105+
t = Timestamp
106+
elif f.type() == QVariant.Double:
107+
t = float
108+
elif f.type() == QVariant.Int:
109+
t = int
110+
elif f.type() == QVariant.String:
111+
t = unicode
112+
elif f.type() == QVariant.ByteArray:
113+
t = unicode
114+
else:
115+
continue
116+
117+
self.description.append([
118+
f.name(), # name
119+
t, # type_code
120+
f.length(), # display_size
121+
f.length(), # internal_size
122+
f.precision(), # precision
123+
None, # scale
124+
f.requiredStatus() != QSqlField.Required # null_ok
125+
])
126+
127+
def executemany(self, operation, seq_of_parameters):
128+
if len(seq_of_parameters) == 0:
129+
return
130+
131+
if not self.qry.prepare(operation):
132+
raise ExecError(self.qry.lastError().databaseText())
133+
134+
for r in seq_of_parameters:
135+
for i in range(len(r)):
136+
self.qry.bindValue(i, r[i])
137+
138+
if not self.qry.exec_():
139+
raise ExecError(self.qry.lastError().databaseText())
140+
141+
def scroll(self, row):
142+
return self.qry.seek(row)
143+
144+
def fetchone(self):
145+
if not self.qry.next():
146+
return None
147+
148+
row = []
149+
for i in range(len(self.description)):
150+
value = self.qry.value(i)
151+
if (isinstance(value, QDate)
152+
or isinstance(value, QTime)
153+
or isinstance(value, QDateTime)):
154+
value = value.toString()
155+
elif isinstance(value, QByteArray):
156+
value = u"GEOMETRY"
157+
# value = value.toHex()
158+
159+
row.append(value)
160+
161+
return row
162+
163+
def fetchmany(self, size=10):
164+
rows = []
165+
while len(rows) < size:
166+
row = self.fetchone()
167+
if row is None:
168+
break
169+
rows.append(row)
170+
171+
return rows
172+
173+
def fetchall(self):
174+
rows = []
175+
while True:
176+
row = self.fetchone()
177+
if row is None:
178+
break
179+
rows.append(row)
180+
181+
return rows
182+
183+
def setinputsize(self, sizes):
184+
raise ExecError("nyi")
185+
186+
def setoutputsize(self, size, column=None):
187+
raise ExecError("nyi")
188+
189+
190+
class QtSqlDBConnection:
191+
connections = 0
192+
193+
def __init__(self, driver, dbname, user, passwd):
194+
self.conn = QSqlDatabase.addDatabase(
195+
driver, "qtsql_%d" % QtSqlDBConnection.connections)
196+
QtSqlDBConnection.connections += 1
197+
self.conn.setDatabaseName(dbname)
198+
self.conn.setUserName(user)
199+
self.conn.setPassword(passwd)
200+
201+
if not self.conn.open():
202+
raise ConnectionError(self.conn.lastError().databaseText())
203+
204+
def close(self):
205+
self.conn.close()
206+
207+
def commit(self):
208+
self.conn.commit()
209+
210+
def rollback(self):
211+
self.conn.rollback()
212+
213+
def cursor(self):
214+
return QtSqlDBCursor(self.conn)
215+
216+
217+
def connect(driver, dbname, user, passwd):
218+
return QtSqlDBConnection(driver, dbname, user, passwd)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# Oracle implementation of QGis DBManager plugin
2+
3+
## Introduction
4+
5+
This Python code try to implement the Oracle part of the QGis DBManager plugin. DBManager plugin is a good tool from QGis with which you can easily manage your databases and create your own queries which can be dynamically added to QGis maps.
6+
7+
For the moment, DBManager plugin is only able to connect to PostGIS and Spatialite databases. If you want to manage your Oracle Spatial repository, you can (try) to do this with this code implementation.
8+
9+
The code base of this implementation was the Postgis one. I tried to make every features of the PostGIS work under Oracle but there are some limitations. Read TODO.md to have more details about what is working and what needs to be done.
10+
11+
Expect bugs !
12+
13+
14+
## Installation
15+
16+
The code does not need [cx_Oracle](http://cx-oracle.sourceforge.net/) anymore ! Thanks to [Jürgen Fischer](https://github.com/jef-n), all Oracle connections uses PyQt QtSql module which is included in QGIS.
17+
18+
To install DBManager oracle plugin, you just have to clone the git repository in a directory named `oracle` in the `db_plugins` directory of the db_manager installation.
19+
20+
For MS-Windows users:
21+
22+
* If you have git for MS-Windows:
23+
$ cd "C:\Program Files\QGis Wien\apps\qgis\python\plugins\db_manager\db_plugins"
24+
$ git clone https://github.com/medspx/dbmanager-oracle.git oracle
25+
26+
* Or:
27+
* Just create a directory named `oracle` in "C:\Program Files\QGis Wien\apps\qgis\python\plugins\db_manager\db_plugins"
28+
* unzip `https://github.com/medspx/dbmanager-oracle/archive/master.zip` into "C:\Program Files\QGis Wien\apps\qgis\python\plugins\db_manager\db_plugins\oracle"
29+
30+
For GNU/Linux users:
31+
32+
# cd /usr/share/qgis/python/plugins/db_manager/db_plugins
33+
# git clone https://github.com/medspx/dbmanager-oracle.git oracle
34+
35+
36+
## Limitations
37+
38+
* You have to define Oracle connections directly in QGis for the plugin to work (same thing than PostGIS and Spatialite).
39+
* Oracle Spatial Rasters are not supported (as I've don't have a way to test them).
40+
* The code try to use the maximum of your Oracle connections parameters. If you have a huge geographic database with a lot of layers, listing tables can take time. So be careful about your connections parameters (try to restrict to user tables to reduce internal queries duration).
41+
* Tests have been done with QGis 2.4, 2.6 and 2.8.2. You probably should use the latest version because before 2.4 the Oracle provider of QGis was not able to load dynamic queries.
42+
* Some things could not have been well tested, particularly everything that requires administrative rights on DB like schema creation/deletion.
43+
* Tests have been done against an Oracle 10g database. I tried to incorporate the official Oracle 12c "dictionary" of commands and the internal queries should also work with 11g and 12c versions of Oracle Database server.
44+
* Some tasks cannot been done under Oracle Database like moving a table from a schema to another. There is also no PostgreSQL Rules features under Oracle.
45+
* Code has been tested only under MS-Windows (bad) but as it is Python code, I hope it will also works under other OS.
46+
47+
48+
## Bug reports
49+
50+
For the moment, use the ["issues" tool of GitHub](https://github.com/medspx/dbmanager-oracle/issues) to report bugs.
51+
52+
53+
## Main goal
54+
55+
My main goal is that this code can be incorporated in the official QGis source code repository. Once this has been done, the code upgrades will take place there.
56+
57+
58+
## License
59+
60+
This code is released under the GNU GPLv2 license. Read headers code for more information.

0 commit comments

Comments
 (0)