dbobj3 0.1.0__tar.gz → 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: dbobj3
3
- Version: 0.1.0
3
+ Version: 0.2.0
4
4
  Summary: High-level MySQL/MSSQL/Oracle access module
5
5
  Requires-Python: >=3.6
6
6
  Requires-Dist: pymysql
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "dbobj3"
7
- version = "0.1.0"
7
+ version = "0.2.0"
8
8
  description = "High-level MySQL/MSSQL/Oracle access module"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.6"
@@ -16,7 +16,7 @@ dependencies = [
16
16
  packages = ["src/dbobj3.py"]
17
17
 
18
18
  [tool.bumpversion]
19
- current_version = "0.1.0"
19
+ current_version = "0.2.0"
20
20
  commit = true
21
21
  tag = true
22
22
  parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)"
@@ -21,18 +21,42 @@ Example:
21
21
  rsServices.next()
22
22
 
23
23
  """
24
+ __version__ = "0.2.0"
24
25
 
25
- __version__ = "0.1.0"
26
+ try:
27
+ import MySQLdb
28
+ import MySQLdb.cursors
29
+ except ImportError:
30
+ try:
31
+ import pymysql as MySQLdb
32
+ import pymysql.cursors
33
+ except ImportError:
34
+ print('could neither import pymysql nor MySQLdb')
35
+
36
+ try:
37
+ import pymssql
38
+ except ImportError:
39
+ print('could not import pymssql')
26
40
 
27
- import MySQLdb
28
- import MySQLdb.cursors
29
- import pymssql
30
- import sqlite3
31
- import getpass
32
41
  try:
33
42
  import cx_Oracle
34
43
  except ImportError:
35
- pass
44
+ print('could not import cx_Oracle')
45
+
46
+ try:
47
+ import sqlite3
48
+ except ImportError:
49
+ print('could not import sqlite3')
50
+
51
+ try:
52
+ import pyodbc
53
+ except ImportError:
54
+ print('could not import pyodbc')
55
+
56
+ import os, getpass, time
57
+ currUser = getpass.getuser()
58
+ homeDir = '/root' if currUser == 'root' else f'/home/{currUser}'
59
+
36
60
 
37
61
  class dbConnMessage:
38
62
  """
@@ -56,16 +80,17 @@ class dbObjCursor:
56
80
  self.cursor = None
57
81
  self.numFields = 0
58
82
  self.numRows = 0
59
- self.lstRecords = [] # Used by Oracle and Sqlite3
83
+ self.lstRecords = [] # Used by Oracle and Sqlite3 and Progress
60
84
  self.recordPointer = 0
61
85
  self.success = True
62
86
  self.dbType = prmDBType.upper()
63
87
  self.create(self.dbType)
64
-
88
+
89
+
65
90
  def __del__(self):
66
91
  if(self.dbType == "MYSQL"):
67
92
  self.cursor.close()
68
-
93
+
69
94
  def create(self, prmDBType="MYSQL"):
70
95
  if(prmDBType=="MYSQL"):
71
96
  self.createMySql()
@@ -75,7 +100,9 @@ class dbObjCursor:
75
100
  self.createOracle()
76
101
  elif(prmDBType=="SQLITE" or prmDBType=="SQLITE3"):
77
102
  self.createSqlite3()
78
-
103
+ elif(prmDBType=="PROGRESS"):
104
+ self.createProgress()
105
+
79
106
  def createMySql(self):
80
107
  self.cursor = self.connection.cursor(MySQLdb.cursors.DictCursor)
81
108
 
@@ -88,10 +115,15 @@ class dbObjCursor:
88
115
  def createSqlite3(self):
89
116
  self.cursor = self.connection.cursor()
90
117
 
118
+ def createProgress(self):
119
+ self.cursor = self.connection.cursor()
120
+ #for ln in [ i for i in dir(self.cursor) if not i[0]=='_']:
121
+ # print(ln)
122
+
91
123
  def moveNext(self):
92
124
  """Same as next"""
93
125
  self.next()
94
-
126
+
95
127
  def next(self):
96
128
  """Move to the next record in the record set and loads the data into the fields property. Sets EOF at end of records."""
97
129
  if(self.dbType == "MYSQL"):
@@ -102,6 +134,8 @@ class dbObjCursor:
102
134
  self.oracleNext()
103
135
  elif(self.dbType == "SQLITE" or self.dbType == "SQLITE3"):
104
136
  self.sqlite3Next()
137
+ elif(self.dbType == "PROGRESS"):
138
+ self.progressNext()
105
139
 
106
140
  def mySqlNext(self):
107
141
  resultSet = self.cursor.fetchone()
@@ -119,13 +153,13 @@ class dbObjCursor:
119
153
  resultSet = self.cursor.fetchone()
120
154
  except Exception as e:
121
155
  print(e)
122
- self.EOF = 1
156
+ self.EOF = 1
123
157
  return
124
158
  if(resultSet != None):
125
159
  self.fields = resultSet
126
160
  self.EOF = 0
127
161
  else:
128
- self.EOF = 1
162
+ self.EOF = 1
129
163
 
130
164
  def oracleNext(self):
131
165
  if(self.recordPointer <= self.numRows):
@@ -140,7 +174,7 @@ class dbObjCursor:
140
174
  self.recordPointer += 1
141
175
  self.EOF = 0
142
176
  else:
143
- self.EOF = 1
177
+ self.EOF = 1
144
178
 
145
179
  def sqlite3Next(self):
146
180
  if(self.recordPointer <= self.numRows):
@@ -155,21 +189,36 @@ class dbObjCursor:
155
189
  self.recordPointer += 1
156
190
  self.EOF = 0
157
191
  else:
158
- self.EOF = 1
192
+ self.EOF = 1
193
+
194
+ def progressNext(self):
195
+ if(self.recordPointer <= self.numRows):
196
+ self.fieldByName = {}
197
+ self.fieldNames = []
198
+ fieldNo = 0
199
+ for cursorFieldname in self.cursor.description:
200
+ self.fieldByName[cursorFieldname[0]] = self.lstRecords[(self.recordPointer-1)][fieldNo]
201
+ self.fieldNames.append(cursorFieldname[0])
202
+ fieldNo = fieldNo + 1
203
+ self.fields = self.fieldByName
204
+ self.recordPointer += 1
205
+ self.EOF = 0
206
+ else:
207
+ self.EOF = 1
159
208
 
160
209
  def first(self):
161
210
  """Move to the next record in the record set and loads the data into the fields property. Sets EOF at end of records."""
162
211
  if(self.dbType == "MYSQL"):
163
212
  self.mySqlFirst()
164
-
213
+
165
214
  def mySqlFirst(self):
166
215
  resultSet = self.cursor.scroll(0,'absolute')
167
216
  self.mySqlNext()
168
-
217
+
169
218
  def oracleExecute(self, prmSql):
170
219
  """
171
220
  Oracle had to be handled differently due to cursor.rowcount failing. This method is called
172
- transparently by dbObjConnection.execute and fetches all records at once where the other
221
+ transparently by dbObjConnection.execute and fetches all records at once where the other
173
222
  database types do not.
174
223
  """
175
224
  self.cursor.execute(prmSql)
@@ -181,15 +230,31 @@ class dbObjCursor:
181
230
 
182
231
  def sqlite3Execute(self, prmSql):
183
232
  """
184
- Sqlite3 method is called transparently by dbObjConnection.execute and fetches all records at once where the other
233
+ Sqlite3Execute method is called transparently by dbObjConnection.execute and fetches all records at once where the other
234
+ database types do not.
235
+ """
236
+ self.cursor.execute(prmSql)
237
+ self.lstRecords = self.cursor.fetchall()
238
+ self.numRows = len(self.lstRecords)
239
+ self.numFields = len(self.cursor.description)
240
+ if(self.numRows > 0):
241
+ self.recordPointer = 1
242
+
243
+ def progressExecute(self, prmSql):
244
+ """
245
+ progressExecute method is called transparently by dbObjConnection.execute and fetches all records at once where the other
185
246
  database types do not.
186
247
  """
248
+ t0 = time.time()
187
249
  self.cursor.execute(prmSql)
188
250
  self.lstRecords = self.cursor.fetchall()
189
251
  self.numRows = len(self.lstRecords)
190
252
  self.numFields = len(self.cursor.description)
191
253
  if(self.numRows > 0):
192
254
  self.recordPointer = 1
255
+ print('progressExecute (fetchs all) time.time()-t0: ',str(round(time.time()-t0,3)),' seconds')
256
+
257
+
193
258
 
194
259
  class dbObjConnection:
195
260
  """
@@ -197,7 +262,7 @@ class dbObjConnection:
197
262
  the database. This object supports access to MySQL, Microsoft SQL Server and Oracle.
198
263
  """
199
264
  def __init__(self, prmDBType="MYSQL"):
200
- """prmDBType can be: MYSQL, MSSQL, or ORACLE"""
265
+ """prmDBType can be: MYSQL, MSSQL, ORACLE, SQLITE3, or PROGRESS"""
201
266
  self.EOF = 1
202
267
  self.connHandle = 0
203
268
  self.dbType = prmDBType.upper()
@@ -205,12 +270,14 @@ class dbObjConnection:
205
270
  self.lastInsId = 0
206
271
 
207
272
  def __del__(self):
208
- """The default destructor will close the connection"""
209
- if(self.dbType == "MYSQL"):
210
- self.connHandle.commit()
211
- self.connHandle.close()
273
+ try:
274
+ if( self.dbTypein ( "MYSQL" ) ):
275
+ self.connHandle.commit()
276
+ self.connHandle.close()
277
+ except Exception as e:
278
+ pass
212
279
 
213
- def connect(self, prmHost="", prmDb="", prmUser="", prmPass=""):
280
+ def connect(self, prmHost="", prmDb="", prmUser="", prmPass="", prmPort=3306):
214
281
  """
215
282
  MySQL:
216
283
  result = connMySQL.connect("db1a", "internet", myuser, mypassword)
@@ -218,21 +285,24 @@ class dbObjConnection:
218
285
  result = connMSSQL.connect("flexibill-mssql", "FLEXIBILL", msuser, mspassword)
219
286
  Oracle:
220
287
  result = connOracl.connect("", "MDDB", orauser, orapassword)
221
-
222
- returns: dbConnMessage
288
+
289
+ returns: dbConnMessage
223
290
  """
224
291
  if(self.dbType == "MYSQL"):
225
- return self.mySqlConnect(prmHost, prmDb, prmUser, prmPass)
292
+ return self.mySqlConnect(prmHost, prmDb, prmUser, prmPass, prmPort)
226
293
  elif(self.dbType == "MSSQL"):
227
294
  return self.msSqlConnect(prmHost, prmDb, prmUser, prmPass)
228
295
  elif(self.dbType == "ORACLE"):
229
296
  return self.oracleConnect(prmUser, prmPass, prmDb)
230
297
  elif(self.dbType == "SQLITE" or self.dbType == "SQLITE3"):
231
298
  return self.sqlite3Connect(prmDb)
299
+ elif(self.dbType == "PROGRESS"):
300
+ return self.progressConnect(prmHost, prmDb, prmUser, prmPass, prmPort)
232
301
 
233
- def mySqlConnect(self, prmHost, prmDb, prmUser, prmPass):
302
+ def mySqlConnect(self, prmHost, prmDb, prmUser, prmPass, prmPort=3306):
234
303
  try:
235
- self.connHandle = MySQLdb.connect(host=prmHost,db=prmDb,user=prmUser,passwd=prmPass)
304
+ self.connHandle = MySQLdb.connect(host=prmHost,db=prmDb,user=prmUser,passwd=prmPass,port=prmPort)
305
+ self.connHandle.autocommit(True)
236
306
  if(self.connHandle):
237
307
  self.msgObj.message = "Connected to MySqlDatabase okay."
238
308
  self.msgObj.success = True
@@ -288,6 +358,50 @@ class dbObjConnection:
288
358
  self.msgObj.success = False
289
359
  return self.msgObj
290
360
 
361
+ def progressConnect(self, prmHost, prmDb, prmUser, prmPass, prmPort=35078):
362
+ try:
363
+ driver = '{DataDirect 7.1 Progress OpenEdge Wire Protocol}'
364
+ #driver = '/opt/Progress/DataDirect/Connect64_for_ODBC_71/lib/ddoe27.so'
365
+ # For now, this is required for users accessing the odbc:
366
+ iniSymLinks = '''
367
+ lrwxrwxrwx 1 root root 55 Jul 3 15:30 .odbc.ini -> /opt/Progress/DataDirect/Connect64_for_ODBC_71/odbc.ini
368
+ lrwxrwxrwx 1 root root 59 Jul 3 15:30 .odbcinst.ini -> /opt/Progress/DataDirect/Connect64_for_ODBC_71/odbcinst.ini
369
+ '''
370
+
371
+ homeFiles = [ f.lower() for f in os.listdir( homeDir ) ]
372
+ iniFiles = True if '.odbc.ini' in homeFiles and '.odbcinst.ini' in homeFiles else False
373
+ if not iniFiles:
374
+ msgErr = 'Missing symlinks to ini files:\n' + iniSymLinks
375
+ print(msgErr)
376
+ raise Exception(msgErr)
377
+
378
+ strCnxn = 'Driver={};'.format(driver)
379
+ strCnxn += 'Host={};'.format(prmHost)
380
+ strCnxn += 'Database={};'.format(prmDb)
381
+ strCnxn += 'UID={};'.format(prmUser)
382
+ strCnxn += 'PWD={};'.format(prmPass)
383
+ strCnxn += 'Port={};'.format(prmPort)
384
+ print(strCnxn)
385
+
386
+ cnxn = pyodbc.connect( strCnxn )
387
+ cnxn.setdecoding(pyodbc.SQL_CHAR, encoding='utf-8')
388
+ cnxn.setdecoding(pyodbc.SQL_WCHAR, encoding='utf-8')
389
+ cnxn.setencoding(encoding='utf-8')
390
+
391
+ self.connHandle = cnxn
392
+ if(self.connHandle):
393
+ self.msgObj.message = "Connected to Progress OpenEdge Database okay."
394
+ self.msgObj.success = True
395
+ else:
396
+ self.msgObj.message = "Connection Failed."
397
+ self.msgObj.success = False
398
+
399
+ except Exception as e:
400
+ self.msgObj.message = "Connection Failed: "+str(e)
401
+ self.msgObj.success = False
402
+
403
+ return self.msgObj
404
+
291
405
  def disconnect(self):
292
406
  """Disconnect the connection handle to the database"""
293
407
  if(self.dbType == "MYSQL"):
@@ -319,6 +433,31 @@ class dbObjConnection:
319
433
  return self.msSqlExecute(prmSql)
320
434
  if(self.dbType == "ORACLE"):
321
435
  return self.oracleExecute(prmSql)
436
+ if(self.dbType in ("SQLITE","SQLITE3")):
437
+ return self.sqlite3Execute(prmSql)
438
+ if(self.dbType in ("PROGRESS")):
439
+ return self.progressExecute(prmSql)
440
+
441
+ def fetchall(self, prmSql):
442
+ """Returns result set as a list of dictionaries"""
443
+ rset = []
444
+ crsr = self.execute( prmSql )
445
+ while not crsr.EOF:
446
+ rset.append( crsr.fields )
447
+ crsr.next()
448
+ return rset
449
+
450
+ def fetchone(self, prmSql):
451
+ """Returns a single row as a dictionary"""
452
+ crsr = self.execute( prmSql )
453
+ dictRow = {} if crsr.EOF else crsr.fields
454
+ return dictRow
455
+
456
+ def fetchval(self, prmSql):
457
+ """Returns a single value"""
458
+ dictRow = self.fetchone( prmSql )
459
+ listVals = list(dictRow.values())
460
+ return None if not listVals else listVals[0]
322
461
 
323
462
  def mySqlExecute(self, prmSql):
324
463
  mySqlCursor = dbObjCursor(self.connHandle)
@@ -335,6 +474,7 @@ class dbObjConnection:
335
474
  mySqlCursor.numRows = 0
336
475
  mySqlCursor.EOF = 1
337
476
  mySqlCursor.success = False
477
+ print(self.msgObj.message)
338
478
  return mySqlCursor
339
479
  try:
340
480
  mySqlCursor.numFields = len(mySqlCursor.cursor.description)
@@ -359,6 +499,7 @@ class dbObjConnection:
359
499
  msSqlCursor.numRows = 0
360
500
  msSqlCursor.EOF = 1
361
501
  msSqlCursor.success = False
502
+ print(self.msgObj.message)
362
503
  return mySqlCursor
363
504
  msSqlCursor.next()
364
505
  return msSqlCursor
@@ -372,22 +513,65 @@ class dbObjConnection:
372
513
  except Exception as e:
373
514
  self.msgObj.message = "SQL Failed: "+prmSql+" - "+str(e)
374
515
  self.msgObj.success = False
375
- print(str(e))
376
516
  oracleCursor.numFields = 0
377
517
  oracleCursor.numRows = 0
378
518
  oracleCursor.EOF = 1
379
519
  oracleCursor.success = False
520
+ print(self.msgObj.message)
380
521
  return mySqlCursor
381
522
  if(oracleCursor.numRows):
382
523
  oracleCursor.next()
383
524
  else:
384
525
  oracleCursor.EOF = 1
385
526
  return oracleCursor
386
-
527
+
528
+
529
+ def sqlite3Execute(self, prmSql):
530
+ sqliteCursor = dbObjCursor(self.connHandle, self.dbType)
531
+ try:
532
+ sqliteCursor.sqlite3Execute(prmSql)
533
+ self.msgObj.message = "SQL Executed: "+prmSql
534
+ self.msgObj.success = True
535
+ except Exception as e:
536
+ self.msgObj.message = "SQL Failed: "+prmSql+" - "+str(e)
537
+ self.msgObj.success = False
538
+ sqliteCursor.numFields = 0
539
+ sqliteCursor.numRows = 0
540
+ sqliteCursor.EOF = 1
541
+ sqliteCursor.success = False
542
+ print(self.msgObj.message)
543
+ if(sqliteCursor.numRows):
544
+ sqliteCursor.next()
545
+ else:
546
+ sqliteCursor.EOF = 1
547
+ return sqliteCursor
548
+
549
+ def progressExecute(self, prmSql):
550
+ progressCursor = dbObjCursor(self.connHandle, self.dbType)
551
+ try:
552
+ progressCursor.progressExecute(prmSql)
553
+ self.msgObj.message = "SQL Executed: "+prmSql
554
+ self.msgObj.success = True
555
+ except Exception as e:
556
+ self.msgObj.message = "SQL Failed: "+prmSql+" - "+str(e)
557
+ self.msgObj.success = False
558
+ progressCursor.numFields = 0
559
+ progressCursor.numRows = 0
560
+ progressCursor.EOF = 1
561
+ progressCursor.success = False
562
+ print(self.msgObj.message)
563
+ return progressCursor
564
+ if(progressCursor.numRows):
565
+ progressCursor.next()
566
+ else:
567
+ progressCursor.EOF = 1
568
+ return progressCursor
569
+
570
+
387
571
  def commit(self):
388
572
  if(self.dbType == "MYSQL"):
389
573
  self.connHandle.commit()
390
-
574
+
391
575
  if __name__== '__main__' :
392
576
  # MySQL Testing - two cursors - nested
393
577
  print("********************** MySQL Test **********************")
File without changes