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 |
|
# sapdb/DBdriver.py |
23 |
|
# |
24 |
|
# DESCRIPTION: |
25 |
|
# Driver to provide access to data via SAP's SAP-DB/Python Driver |
26 |
|
# Requires SAP-DB (http://www.sapdb.org/) |
27 |
|
# |
28 |
|
# NOTES: |
29 |
|
# |
30 |
|
# Supported attributes (via connections.conf or <database> tag) |
31 |
|
# |
32 |
|
# host= This is the SAP-DB host for your connection (optional) |
33 |
|
# dbname= This is the SAP-DB database to use (required) |
34 |
|
# timeout= Command timeout in seconds (optional) |
35 |
|
# isolation= Isolation level (options) |
36 |
|
# sqlmode= INTERNAl or ORACLE (optional) |
37 |
|
# sqlsubmode= ODBC or empty (optional) |
38 |
|
# |
39 |
|
|
40 |
|
_exampleConfig = """ |
41 |
|
# This connection uses the SAP DB driver |
42 |
|
# We will be connecting to the SAP DB server on |
43 |
|
# "localhost" to a database called "TST". |
44 |
|
[sapdb] |
45 |
|
comment = XYZ Development Database |
46 |
|
provider = sapdb |
47 |
|
dbname = TST |
48 |
|
# host = localhost # (optional) |
49 |
|
# sqlmode = INTERNAL # (default) or ORACLE |
50 |
|
# sqlsubmode = ODBC # (for compatibility with the SAP DB ODBC driver) |
51 |
|
# timeout = 900 # (command timeout in seconds) |
52 |
|
# isolation = 1 # 0, 1 (default), 10, 15, 2, 20, 3, 30 |
53 |
|
""" |
54 |
|
#### THIS IS AN UNTESTED DRIVER #### |
55 |
|
#### Any volunteers? #### |
56 |
|
|
57 |
|
import string |
58 |
|
from string import lower |
59 |
|
import sys |
60 |
|
from gnue.common.datasources import GDataObjects, GConditions, GConnections |
61 |
|
from gnue.common.apps import GDebug |
62 |
|
from gnue.common.datasources.drivers.DBSIG2.Driver \ |
63 |
|
import DBSIG2.RecordSet, DBSIG2.ResultSet, DBSIG2.DataObject, \ |
64 |
|
DBSIG2.DataObject_SQL, DBSIG2.DataObject_Object |
65 |
|
|
66 |
|
try: |
67 |
|
import sapdbapi as SIG2api |
68 |
|
except ImportError, message: |
69 |
|
tmsg = _("Driver not installed: sapdbapi for SAP-DB 7.x \n[%s]") % message |
70 |
|
raise GConnections.AdapterNotInstalled, tmsg |
71 |
|
|
72 |
|
class SAP_RecordSet(DBSIG2.RecordSet): |
73 |
|
pass |
74 |
|
|
75 |
|
|
76 |
|
class SAP_ResultSet(DBSIG2.ResultSet): |
77 |
|
def __init__(self, dataObject, cursor=None, defaultValues={}, masterRecordSet=None): |
78 |
|
DBSIG2.ResultSet.__init__(self, dataObject, \ |
79 |
|
cursor, defaultValues, masterRecordSet) |
80 |
|
self._recordSetClass = SAP_RecordSet |
81 |
|
|
82 |
|
|
83 |
|
|
84 |
|
class SAP_DataObject(DBSIG2.DataObject): |
85 |
|
def __init__(self): |
86 |
|
DBSIG2.DataObject.__init__(self) |
87 |
|
self._DatabaseError = SIG2api.DatabaseError |
88 |
|
self._resultSetClass = SAP_ResultSet |
89 |
|
|
90 |
|
|
91 |
|
def connect(self, connectData={}): |
92 |
|
GDebug.printMesg(1,"SAP database driver initializing") |
93 |
|
try: |
94 |
|
options = {'autocommit': 'off'} |
95 |
|
for gnueName, sapdbName in [('sqlmode', 'sqlmode'), |
96 |
|
('timeout', 'timeout'), |
97 |
|
('isolation', 'isolation'), |
98 |
|
('sqlsubmode', 'component')]: |
99 |
|
if connectData.has_key (gnueName): |
100 |
|
options [sapdbName] = connectData [gnueName] |
101 |
|
self._dataConnection = apply (SIG2api.connect, |
102 |
|
(connectData['_username'], connectData['_password'], |
103 |
|
connectData['dbname'], connectData.get ('host', '')), |
104 |
|
options) |
105 |
|
#self._dataConnection = SIG2api.connect( \ |
106 |
|
# user=connectData['_username'], \ |
107 |
|
# password=connectData['_password'], \ |
108 |
|
# database=connectData['dbname'], \ |
109 |
|
# host=connectData.get ('host', ''), \ |
110 |
|
# autocommit="off") |
111 |
|
except self._DatabaseError, value: |
112 |
|
raise GDataObjects.LoginError, value |
113 |
|
|
114 |
|
self._postConnect() |
115 |
|
|
116 |
|
def _postConnect(self): |
117 |
|
self.triggerExtensions = TriggerExtensions(self._dataConnection) |
118 |
|
|
119 |
|
|
120 |
|
# |
121 |
|
# Schema (metadata) functions |
122 |
|
# |
123 |
|
|
124 |
|
# Return a list of the types of Schema objects this driver provides |
125 |
|
def getSchemaTypes(self): |
126 |
|
return [ ('table', _('Tables'),1), |
127 |
|
('view', _('Views'), 1), |
128 |
|
('synonym', _('Synonyms'),1), |
129 |
|
('result', _('Result Table'),1) ] |
130 |
|
|
131 |
|
|
132 |
|
# Return a list of Schema objects |
133 |
|
def getSchemaList(self, type=None): |
134 |
|
|
135 |
|
where_user = "" |
136 |
|
if type == None: |
137 |
|
where_type = "where TYPE <> 'SYSTEM' and TYPE <> 'SYNONYM' " |
138 |
|
else: |
139 |
|
where_type = "where TYPE='%s'" % string.upper(type) |
140 |
|
|
141 |
|
|
142 |
|
statement = \ |
143 |
|
"select owner||'.'||tablename||'.'||type, " + \ |
144 |
|
"owner||'.'||tablename table_name, " + \ |
145 |
|
"type table_type " + \ |
146 |
|
"from domain.tables %s" \ |
147 |
|
% (where_type) + \ |
148 |
|
"order by tablename " |
149 |
|
|
150 |
|
GDebug.printMesg(5,statement) |
151 |
|
|
152 |
|
cursor = self._dataConnection.cursor() |
153 |
|
cursor.execute(statement) |
154 |
|
|
155 |
|
list = [] |
156 |
|
for rs in cursor.fetchall(): |
157 |
|
list.append(GDataObjects.Schema(attrs={'id':rs[0], 'name':rs[1], |
158 |
|
'type':string.lower(rs[2])}, |
159 |
|
getChildSchema=self.__getFieldSchema)) |
160 |
|
|
161 |
|
cursor.close() |
162 |
|
return list |
163 |
|
|
164 |
|
|
165 |
|
# Find a schema object with specified name |
166 |
|
def getSchemaByName(self, name, type=None): |
167 |
|
|
168 |
|
where_user = "" |
169 |
|
parts = string.split(string.upper(name),'.') |
170 |
|
name = parts[-1] |
171 |
|
if len(parts) > 1: |
172 |
|
schema = " and owner='%s'" % parts[-2] |
173 |
|
else: |
174 |
|
schema = "" |
175 |
|
|
176 |
|
statement = \ |
177 |
|
"select owner||'.'||tablename||'.'||type, " + \ |
178 |
|
"owner||'.'||tablename table_name, " + \ |
179 |
|
"type table_type, " + \ |
180 |
|
"owner, tablename " + \ |
181 |
|
"from domain.tables where tablename='%s'%s" \ |
182 |
|
% (name, schema) + \ |
183 |
|
"order by tablename " |
184 |
|
|
185 |
|
GDebug.printMesg(5,statement) |
186 |
|
|
187 |
|
cursor = self._dataConnection.cursor() |
188 |
|
cursor.execute(statement) |
189 |
|
|
190 |
|
list = [] |
191 |
|
for rs in cursor.fetchall(): |
192 |
|
list.append(GDataObjects.Schema(attrs={'id':string.lower(rs[0]), 'name':rs[1], |
193 |
|
'type':rs[2], 'sapdbId': (rs [3], rs [4])}, |
194 |
|
getChildSchema=self.__getFieldSchema)) |
195 |
|
|
196 |
|
cursor.close() |
197 |
|
|
198 |
|
try: |
199 |
|
return list[0] |
200 |
|
except: |
201 |
|
return None |
202 |
|
|
203 |
|
|
204 |
|
|
205 |
|
# Get fields for a table |
206 |
|
def __getFieldSchema(self, parent): |
207 |
|
|
208 |
|
# TODO: This does not support user-defined datatypes... |
209 |
|
# TODO: it will always report such as TEXT-like fields. |
210 |
|
|
211 |
|
schema, name, type = string.split(parent.id,'.') |
212 |
|
owner, basename = parent.sapdbId |
213 |
|
cursor = self._dataConnection.cursor() |
214 |
|
|
215 |
|
# if type == 'synonym': |
216 |
|
# statement = "select base_tabschema, base_tabname " + \ |
217 |
|
# "from syscat.tables " + \ |
218 |
|
# "where tabschema = '%s' and tabname='%s'" % (schema, name) |
219 |
|
# |
220 |
|
# GDebug.printMesg(5,statement) |
221 |
|
# |
222 |
|
# cursor.execute(statement) |
223 |
|
# rs = cursor.fetchone() |
224 |
|
# schema, name = rs |
225 |
|
|
226 |
|
statement = \ |
227 |
|
"select owner||'.'||tablename||'.'||columnname, " + \ |
228 |
|
"columnname, datatype, 'Y', len, dec " + \ |
229 |
|
"from domain.columns " + \ |
230 |
|
"where owner = '%s' and tablename = '%s' " % (owner, basename) + \ |
231 |
|
'order by "POS"' |
232 |
|
|
233 |
|
GDebug.printMesg(5,statement) |
234 |
|
|
235 |
|
cursor.execute(statement) |
236 |
|
|
237 |
|
list = [] |
238 |
|
for rs in cursor.fetchall(): |
239 |
|
|
240 |
|
attrs={'id': rs[0], 'name': rs[1], |
241 |
|
'type':'field', 'nativetype': rs[2], |
242 |
|
'required': 'N'} |
243 |
|
|
244 |
|
if rs[2] in ('BOOLEAN','FIXED','FLOAT','INTEGER','LONG','SMALLINT'): |
245 |
|
attrs['precision'] = rs[5] |
246 |
|
attrs['datatype'] = 'number' |
247 |
|
elif rs[2] in ('DATE','TIME','TIMESTAMP'): |
248 |
|
attrs['datatype'] = 'date' |
249 |
|
else: |
250 |
|
attrs['datatype'] = 'text' |
251 |
|
|
252 |
|
if rs[5] != 0: |
253 |
|
attrs['length'] = rs[4] |
254 |
|
|
255 |
|
list.append(GDataObjects.Schema(attrs=attrs)) |
256 |
|
|
257 |
|
cursor.close() |
258 |
|
return tuple(list) |
259 |
|
|
260 |
|
|
261 |
|
class SAP_DataObject_Object(SAP_DataObject, \ |
262 |
|
DBSIG2.DataObject_Object): |
263 |
|
|
264 |
|
def __init__(self): |
265 |
|
SAP_DataObject.__init__(self) |
266 |
|
|
267 |
|
def _buildQuery(self, conditions={},forDetail=None,additionalSQL=""): |
268 |
|
return DBSIG2.DataObject_Object._buildQuery(self, conditions,forDetail,additionalSQL) |
269 |
|
|
270 |
|
|
271 |
|
class SAP_DataObject_SQL(SAP_DataObject, \ |
272 |
|
DBSIG2.DataObject_SQL): |
273 |
|
def __init__(self): |
274 |
|
# Call DBSIG init first because SAP_DataObject needs to overwrite |
275 |
|
# some of its values |
276 |
|
DBSIG2.DataObject_SQL.__init__(self) |
277 |
|
SAP_DataObject.__init__(self) |
278 |
|
|
279 |
|
def _buildQuery(self, conditions={}): |
280 |
|
return DBSIG2.DataObject_SQL._buildQuery(self, conditions) |
281 |
|
|
282 |
|
|
283 |
|
# |
284 |
|
# Extensions to Trigger Namespaces |
285 |
|
# |
286 |
|
class TriggerExtensions: |
287 |
|
|
288 |
|
def __init__(self, connection): |
289 |
|
self.__connection = connection |
290 |
|
|
291 |
|
|
292 |
|
|
293 |
|
|
294 |
|
|
295 |
|
###################################### |
296 |
|
# |
297 |
|
# The following hashes describe |
298 |
|
# this driver's characteristings. |
299 |
|
# |
300 |
|
###################################### |
301 |
|
|
302 |
|
# |
303 |
|
# All datasource "types" and corresponding DataObject class |
304 |
|
# |
305 |
|
supportedDataObjects = { |
306 |
|
'object': SAP_DataObject_Object, |
307 |
|
'sql': SAP_DataObject_SQL |
308 |
|
} |
309 |
|
|
310 |
|
def createConnection (conn, **overrides): |
311 |
|
from gnue.common.datasources.GConnections import GConnections |
312 |
|
connections = GConnections (r'D:\Python22\etc\connections.conf') |
313 |
|
parameters = connections.getConnectionParameters (conn).copy () |
314 |
|
dataObject = connections.getDataObject (conn, 'object') |
315 |
|
parameters.update (overrides) |
316 |
|
dataObject.connect (parameters) |
317 |
|
return dataObject |
318 |
|
|
319 |
|
def testConnection (): |
320 |
|
conn, user, pwd = sys.argv [1:4] |
321 |
|
connection = createConnection (conn, _username = user, _password = pwd) |
322 |
|
connection.getSchemaList () |
323 |
|
|
324 |
|
if __name__ == "__main__": |
325 |
|
testConnection () |