1 |
|
# |
2 |
|
# This file is part of GNU Enterprise. |
3 |
|
# |
4 |
|
# GNU Enterprise is free software; you can redistribute it |
5 |
|
# and/or modify it under the terms of the GNU General Public |
6 |
|
# License as published by the Free Software Foundation; either |
7 |
|
# version 2, or (at your option) any later version. |
8 |
|
# |
9 |
|
# GNU Enterprise is distributed in the hope that it will be |
10 |
|
# useful, but WITHOUT ANY WARRANTY; without even the implied |
11 |
|
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR |
12 |
|
# PURPOSE. See the GNU General Public License for more details. |
13 |
|
# |
14 |
|
# You should have received a copy of the GNU General Public |
15 |
|
# License along with program; see the file COPYING. If not, |
16 |
|
# write to the Free Software Foundation, Inc., 59 Temple Place |
17 |
|
# - Suite 330, Boston, MA 02111-1307, USA. |
18 |
|
# |
19 |
|
# Copyright 2000-2003 Free Software Foundation |
20 |
|
# |
21 |
|
# FILE: |
22 |
|
# gadfly/DBdriver.py |
23 |
|
# |
24 |
|
# DESCRIPTION: |
25 |
|
# Driver to provide access to data via Gadfly |
26 |
|
# |
27 |
|
# NOTES: |
28 |
|
# The Gadfly notes state that it is not safe to use in a multi-user environment |
29 |
|
# where there may be concurrent read/writes. Use at own risk :) |
30 |
|
|
31 |
|
|
32 |
|
import string |
33 |
|
import sys |
34 |
|
from gnue.common.apps import GDebug |
35 |
|
from gnue.common.datasources import GDataObjects |
36 |
|
from gnue.common.datasources.drivers.DBSIG2.Driver \ |
37 |
|
import DBSIG2.RecordSet, DBSIG2.ResultSet, DBSIG2.DataObject, \ |
38 |
|
DBSIG2.DataObject_SQL, DBSIG2.DataObject_Object |
39 |
|
|
40 |
|
try: |
41 |
|
import gadfly |
42 |
|
except ImportError, mesg: |
43 |
|
GDebug.printMesg(1,mesg) |
44 |
|
print "-"*79 |
45 |
|
print _("\nCould not load gadfly. For Gadfly support, please install \n") \ |
46 |
|
+ _("GadflyB5 1.0.0 pr1 or later from") \ |
47 |
|
+ "http://gadfly.sourceforge.net\n" |
48 |
|
print _("Error: %s") % mesg |
49 |
|
print "-"*79 |
50 |
|
sys.exit() |
51 |
|
|
52 |
|
|
53 |
|
|
54 |
|
class Gadfly_RecordSet(DBSIG2.RecordSet): |
55 |
|
pass |
56 |
|
|
57 |
|
|
58 |
|
class Gadfly_ResultSet(DBSIG2.ResultSet): |
59 |
|
def __init__(self, dataObject, cursor=None, defaultValues={}, masterRecordSet=None): |
60 |
|
cursor.rowcount=0 |
61 |
|
DBSIG2.ResultSet.__init__(self, dataObject, \ |
62 |
|
cursor, defaultValues, masterRecordSet) |
63 |
|
self._recordSetClass = Gadfly_RecordSet |
64 |
|
|
65 |
|
def _loadNextRecord(self): |
66 |
|
if self._cursor: |
67 |
|
rs = None |
68 |
|
|
69 |
|
try: |
70 |
|
rsets = self._cursor.fetchall() |
71 |
|
except self._dataObject._DatabaseError, err: |
72 |
|
raise GDataObjects.ConnectionError, err |
73 |
|
|
74 |
|
if rsets and len(rsets): |
75 |
|
for rs in(rsets): |
76 |
|
if rs: |
77 |
|
i = 0 |
78 |
|
dict = {} |
79 |
|
for f in (rs): |
80 |
|
dict[string.lower(self._fieldNames[i])] = f |
81 |
|
i += 1 |
82 |
|
self._cachedRecords.append (self._recordSetClass(parent=self, \ |
83 |
|
initialData=dict)) |
84 |
|
else: |
85 |
|
return 0 |
86 |
|
return 1 |
87 |
|
else: |
88 |
|
return 0 |
89 |
|
else: |
90 |
|
return 0 |
91 |
|
|
92 |
|
##### EVIL HACK |
93 |
|
class Error(StandardError): |
94 |
|
"""Generic Error""" |
95 |
|
|
96 |
|
class InterfaceError(Error): |
97 |
|
"""Interface Error""" |
98 |
|
|
99 |
|
class DatabaseError(InterfaceError): |
100 |
|
"""DB Error""" |
101 |
|
|
102 |
|
class DataError(DatabaseError): |
103 |
|
"""Data Error""" |
104 |
|
|
105 |
|
class OperationalError(DatabaseError): |
106 |
|
"""Operational Error""" |
107 |
|
|
108 |
|
class IntegrityError(DatabaseError): |
109 |
|
"""Integrity Error""" |
110 |
|
|
111 |
|
##### END EVIL HACK |
112 |
|
|
113 |
|
class Gadfly_DataObject(DBSIG2.DataObject): |
114 |
|
|
115 |
|
|
116 |
|
def __init__(self): |
117 |
|
DBSIG2.DataObject.__init__(self) |
118 |
|
self._DatabaseError = Error |
119 |
|
self._resultSetClass = Gadfly_ResultSet |
120 |
|
|
121 |
|
# LIKE is not supported on database level at the moment |
122 |
|
# there should be used other ways to emulate it |
123 |
|
# until that works, do a = instead of a like |
124 |
|
# EVIL HACK |
125 |
|
self.conditionElements.update({\ |
126 |
|
'like': (2, 2, '%s = %s', None ),\ |
127 |
|
'notlike': (2, 2, 'NOT (%s = %s)', None )}) |
128 |
|
# END EVIL HACK |
129 |
|
|
130 |
|
|
131 |
|
|
132 |
|
def connect(self, connectData={}): |
133 |
|
GDebug.printMesg(1,"Gadfly database driver initializing") |
134 |
|
#GDebug.printMesg(1,"Connecting with %s, %s" %( connectData['_dbname'], connectData['directory'])) |
135 |
|
try: |
136 |
|
self._dataConnection = gadfly.gadfly(connectData['dbname'], |
137 |
|
connectData['directory']) |
138 |
|
except self._DatabaseError, value: |
139 |
|
#GDebug.printMesg(1,"Boom") |
140 |
|
raise GDataObjects.LoginError, value |
141 |
|
|
142 |
|
self._beginTransaction() |
143 |
|
self._postConnect() |
144 |
|
|
145 |
|
|
146 |
|
def _postConnect(self): |
147 |
|
self.triggerExtensions = TriggerExtensions(self._dataConnection) |
148 |
|
|
149 |
|
|
150 |
|
def _beginTransaction(self): |
151 |
|
try: |
152 |
|
self._dataConnection.begin() |
153 |
|
except: |
154 |
|
pass |
155 |
|
|
156 |
|
# This should be over-ridden only if driver needs more than user/pass |
157 |
|
def getLoginFields(self): |
158 |
|
return [] |
159 |
|
|
160 |
|
# |
161 |
|
# Schema (metadata) functions |
162 |
|
# |
163 |
|
|
164 |
|
# Return a list of the types of Schema objects this driver provides |
165 |
|
def getSchemaTypes(self): |
166 |
|
return [('view',_('Views'),1), |
167 |
|
('table',_('Tables'),1)] |
168 |
|
|
169 |
|
# Return a list of Schema objects |
170 |
|
def getSchemaList(self, type=None): |
171 |
|
|
172 |
|
statement = "select * from __table_names__" |
173 |
|
|
174 |
|
cursor = self._dataConnection.cursor() |
175 |
|
GDebug.printMesg(1,"** Executing: %s **" % statement) |
176 |
|
cursor.execute(statement) |
177 |
|
|
178 |
|
list = [] |
179 |
|
for rs in cursor.fetchall(): |
180 |
|
# exclude any system tables and views. f.e. __table_names__ |
181 |
|
if rs[1][:2]!="__": |
182 |
|
list.append(GDataObjects.Schema(attrs={'id':rs[1], 'name':rs[1], \ |
183 |
|
'type':rs[0] == 1 and 'view' or 'table',}, |
184 |
|
getChildSchema=self.__getFieldSchema)) |
185 |
|
|
186 |
|
cursor.close() |
187 |
|
return list |
188 |
|
|
189 |
|
|
190 |
|
# Find a schema object with specified name |
191 |
|
def getSchemaByName(self, name, type=None): |
192 |
|
statement = "SELECT * from __table_names__ WHERE TABLE_NAME='%s'" % (name) |
193 |
|
|
194 |
|
cursor = self._dataConnection.cursor() |
195 |
|
GDebug.printMesg(1,"** Executing: %s **" % statement) |
196 |
|
cursor.execute(statement) |
197 |
|
|
198 |
|
rs = cursor.fetchone() |
199 |
|
if rs: |
200 |
|
schema = GDataObjects.Schema(attrs={'id':rs[1], 'name':rs[1], \ |
201 |
|
'type':rs[0] == 1 and 'view' or 'table',}, |
202 |
|
getChildSchema=self.__getFieldSchema) |
203 |
|
else: |
204 |
|
schema = None |
205 |
|
|
206 |
|
cursor.close() |
207 |
|
return schema |
208 |
|
|
209 |
|
|
210 |
|
# Get fields for a table |
211 |
|
def __getFieldSchema(self, parent): |
212 |
|
|
213 |
|
# TODO: Read whole definitions from __DATADEFS__ and parse them |
214 |
|
# to distinguish between varchar, float and integer |
215 |
|
|
216 |
|
statement = "SELECT * FROM __COLUMNS__ WHERE TABLE_NAME='%s'" % parent.id |
217 |
|
|
218 |
|
cursor = self._dataConnection.cursor() |
219 |
|
GDebug.printMesg(1,"** Executing: %s **" % statement) |
220 |
|
cursor.execute(statement) |
221 |
|
columns = cursor.description |
222 |
|
|
223 |
|
list = [] |
224 |
|
for rs in cursor.fetchall(): |
225 |
|
|
226 |
|
#nativetype = string.split(string.replace(rs[1],')',''),'(') |
227 |
|
|
228 |
|
|
229 |
|
attrs={'id': "%s.%s" % (parent.id, rs[0]), 'name': rs[0], |
230 |
|
'type':'field', 'nativetype': 'varchar', |
231 |
|
'required': 0} |
232 |
|
|
233 |
|
#if nativetype[0] in ('int','integer','bigint','mediumint', |
234 |
|
# 'smallint','tinyint','float','real', |
235 |
|
# 'double','decimal'): |
236 |
|
# attrs['datatype']='number' |
237 |
|
#elif nativetype[0] in ('date','time','timestamp','datetime'): |
238 |
|
# attrs['datatype']='date' |
239 |
|
#else: |
240 |
|
# attrs['datatype']='text' |
241 |
|
|
242 |
|
## MORE EVILNESS |
243 |
|
attrs['datatype']='text' |
244 |
|
##END HACK |
245 |
|
|
246 |
|
#try: |
247 |
|
# if len(nativetype) == 2: |
248 |
|
# attrs['length'] = int(string.split(nativetype[1])[0]) |
249 |
|
#except ValueError: |
250 |
|
# GDebug.printMesg(1,'WARNING: mysql native type error: %s' % nativetype) |
251 |
|
|
252 |
|
list.append(GDataObjects.Schema(attrs=attrs)) |
253 |
|
|
254 |
|
cursor.close() |
255 |
|
return list |
256 |
|
|
257 |
|
|
258 |
|
|
259 |
|
|
260 |
|
class Gadfly_DataObject_Object(Gadfly_DataObject, \ |
261 |
|
DBSIG2.DataObject_Object): |
262 |
|
|
263 |
|
def __init__(self): |
264 |
|
Gadfly_DataObject.__init__(self) |
265 |
|
|
266 |
|
def _buildQuery(self, conditions={},forDetail=None,additionalSQL=""): |
267 |
|
return DBSIG2.DataObject_Object._buildQuery(self, conditions,forDetail,additionalSQL) |
268 |
|
|
269 |
|
def _getQueryCount(self,conditions={}): |
270 |
|
cursor = self._dataConnection.cursor() |
271 |
|
|
272 |
|
cursor.execute(self._buildQueryCount(conditions)) |
273 |
|
# GADFLY throws an error if executing COUNT(*) on an empty set |
274 |
|
try: |
275 |
|
rs = cursor.fetchone() |
276 |
|
return int(rs[0]) |
277 |
|
except: |
278 |
|
return 0 |
279 |
|
|
280 |
|
def _buildQueryCount(self, conditions={}): |
281 |
|
# GADFLY seems to hate big letter "SELECT" |
282 |
|
q = "select count(*) from %s%s" % (self.table, self._conditionToSQL(conditions)) |
283 |
|
|
284 |
|
GDebug.printMesg(5,q) |
285 |
|
|
286 |
|
return q |
287 |
|
|
288 |
|
class Gadfly_DataObject_SQL(Gadfly_DataObject, \ |
289 |
|
DBSIG2.DataObject_SQL): |
290 |
|
def __init__(self): |
291 |
|
# Call DBSIG init first because Gadfly_DataObject needs to overwrite |
292 |
|
# some of its values |
293 |
|
DBSIG2.DataObject_SQL.__init__(self) |
294 |
|
Gadfly_DataObject.__init__(self) |
295 |
|
|
296 |
|
def _buildQuery(self, conditions={}): |
297 |
|
return DBSIG2.DataObject_SQL._buildQuery(self, conditions) |
298 |
|
|
299 |
|
|
300 |
|
|
301 |
|
# |
302 |
|
# Extensions to Trigger Namespaces |
303 |
|
# |
304 |
|
class TriggerExtensions: |
305 |
|
|
306 |
|
def __init__(self, connection): |
307 |
|
self.__connection = connection |
308 |
|
|
309 |
|
# Return the current date, according to database |
310 |
|
# def getDate(self): |
311 |
|
# pass |
312 |
|
|
313 |
|
# Return a sequence number from sequence 'name' |
314 |
|
# def getSequence(self, name): |
315 |
|
# pass |
316 |
|
|
317 |
|
# Run the SQL statement 'statement' |
318 |
|
# def sql(self, statement): |
319 |
|
# pass |
320 |
|
|
321 |
|
|
322 |
|
|
323 |
|
###################################### |
324 |
|
# |
325 |
|
# The following hashes describe |
326 |
|
# this driver's characteristings. |
327 |
|
# |
328 |
|
###################################### |
329 |
|
|
330 |
|
# |
331 |
|
# All datasouce "types" and corresponding DataObject class |
332 |
|
# |
333 |
|
supportedDataObjects = { |
334 |
|
'object': Gadfly_DataObject_Object, |
335 |
|
'sql': Gadfly_DataObject_SQL |
336 |
|
} |
337 |
|
|
338 |
|
|