37 |
|
|
38 |
from string import upper, lower, rstrip |
from string import upper, lower, rstrip |
39 |
import sys |
import sys |
40 |
from gnue.common.datasources import GDataObjects, GConditions |
from gnue.common.datasources import GDataObjects, GConditions, GConnections |
41 |
from gnue.common.apps import GDebug |
from gnue.common.apps import GDebug |
42 |
from gnue.common.datasources.drivers import DBSIG2 |
from gnue.common.datasources.drivers import DBSIG2 |
43 |
|
from DataObject import * |
44 |
|
from gnue.common.datasources.drivers.interbase.Schema.Discovery.Introspection import Introspection |
45 |
|
|
46 |
|
|
47 |
try: |
try: |
48 |
import kinterbasdb as SIG2api |
import kinterbasdb as SIG2api |
51 |
raise GConnections.AdapterNotInstalled, tmsg |
raise GConnections.AdapterNotInstalled, tmsg |
52 |
|
|
53 |
|
|
54 |
class RecordSet(DBSIG2.RecordSet): |
###################################################################### |
55 |
pass |
# |
56 |
|
# GConnection object for Interbase drivers |
57 |
|
# |
58 |
|
class Connection(DBSIG2.Connection): |
59 |
|
|
60 |
class ResultSet(DBSIG2.ResultSet): |
defaultBehavior = Introspection |
61 |
def __init__(self, dataObject, cursor=None, defaultValues={}, masterRecordSet=None): |
_DatabaseError = SIG2api.DatabaseError |
62 |
DBSIG2.ResultSet.__init__(self, dataObject, \ |
supportedDataObjects = { |
63 |
cursor, defaultValues, masterRecordSet) |
'object': DataObject_Object, |
64 |
self._recordSetClass = RecordSet |
'sql': DataObject_SQL |
65 |
|
} |
66 |
|
# The date/time format used in insert/select statements |
67 |
class DataObject(DBSIG2.DataObject): |
# (based on format used for time.strftime()) |
68 |
def __init__(self): |
_dateTimeFormat = "cast('%Y-%m-%d %H:%M:%S' as timestamp)" |
|
DBSIG2.DataObject.__init__(self) |
|
|
self._DatabaseError = SIG2api.DatabaseError |
|
|
self._resultSetClass = ResultSet |
|
|
self._primaryKeyFields = [] |
|
|
|
|
|
# The date/time format used in insert/select statements |
|
|
# (based on format used for time.strftime()) |
|
|
self._dateTimeFormat = "cast('%Y-%m-%d %H:%M:%S' as timestamp)" |
|
69 |
|
|
70 |
def connect(self, connectData={}): |
def connect(self, connectData={}): |
71 |
GDebug.printMesg(1,"Interbase database driver initializing") |
GDebug.printMesg(1,"Interbase database driver initializing") |
88 |
self._postConnect() |
self._postConnect() |
89 |
|
|
90 |
|
|
|
def _createResultSet(self, conditions={}, readOnly=0, masterRecordSet=None,sql=""): |
|
|
|
|
|
# Used by drivers with a unique id (like rowid) |
|
|
if not self._primaryIdChecked: self._checkForPrimaryId() |
|
|
|
|
|
try: |
|
|
cursor = self.native.cursor() |
|
|
|
|
|
cursor.arraysize = self.cache |
|
|
cursor.execute(self._buildQuery(conditions, additionalSQL=sql)) |
|
|
|
|
|
# pull a record count |
|
|
if self._strictQueryCount: |
|
|
# recordCount = cursor.rowcount |
|
|
# #disable the count query and see if anyone screams |
|
|
# #recordCount = self._getQueryCount(conditions,sql) |
|
|
|
|
|
#kinterbasdb screams :( |
|
|
recordCount = self._getQueryCount(conditions,sql) |
|
|
|
|
|
except self._DatabaseError, err: |
|
|
raise GDataObjects.ConnectionError, err |
|
|
|
|
|
rs = self._resultSetClass(self, cursor=cursor, masterRecordSet=masterRecordSet) |
|
|
if self._strictQueryCount: |
|
|
rs._recordCount = recordCount |
|
|
if readOnly: |
|
|
rs._readonly = readOnly |
|
|
|
|
|
return rs |
|
|
|
|
|
|
|
|
|
|
|
# |
|
|
# Schema (metadata) functions |
|
|
# |
|
|
|
|
|
# Return a list of the types of Schema objects this driver provides |
|
|
def getSchemaTypes(self): |
|
|
return [('view',_('Views'),1), |
|
|
('table',_('Tables'),1)] |
|
|
|
|
|
# Return a list of Schema objects |
|
|
def getSchemaList(self, type=None): |
|
|
|
|
|
# This excludes any system tables and views. |
|
|
statement = "select rdb$relation_name, rdb$view_source "+\ |
|
|
"from rdb$relations " + \ |
|
|
"where rdb$system_flag=0 " + \ |
|
|
"order by rdb$relation_name" |
|
|
|
|
|
cursor = self.native.cursor() |
|
|
cursor.execute(statement) |
|
|
|
|
|
# TODO: rdb$view_source is null for table and rdb$view_source is not null for view |
|
|
list = [] |
|
|
for rs in cursor.fetchall(): |
|
|
list.append(GDataObjects.Schema(attrs={'id':rs[0], 'name':rstrip(rs[0]), |
|
|
'type':'table', |
|
|
'primarykey': self.__getPrimaryKey(rstrip(rs[0]))}, |
|
|
getChildSchema=self.__getFieldSchema)) |
|
|
|
|
|
cursor.close() |
|
|
return list |
|
|
|
|
|
# Find a schema object with specified name |
|
|
def getSchemaByName(self, name, type=None): |
|
|
|
|
|
statement = "select rdb$relation_name, rdb$view_source "+\ |
|
|
"from rdb$relations " + \ |
|
|
"where rdb$relation_name = '%s'" % (name) |
|
|
|
|
|
cursor = self.native.cursor() |
|
|
cursor.execute(statement) |
|
|
|
|
|
rs = cursor.fetchone() |
|
|
if rs: |
|
|
schema = GDataObjects.Schema(attrs={'id':rs[0], 'name':rstrip(rs[0]), |
|
|
'type':'table', |
|
|
'primarykey': self.__getPrimaryKey(rstrip(rs[0]))}, |
|
|
getChildSchema=self.__getFieldSchema) |
|
|
else: |
|
|
schema = None |
|
|
|
|
|
cursor.close() |
|
|
return schema |
|
|
|
|
|
# Return a list of fields (for _buildDeleteStatement and for _buildUpdateStatement) |
|
|
def __getPrimaryKey(self, relname): |
|
|
statement = "select rdb$relation_name, rdb$field_name, "+\ |
|
|
"rdb$constraint_name, rdb$field_position "+\ |
|
|
"from rdb$relation_constraints rc, rdb$index_segments ri "+\ |
|
|
"where ri.rdb$index_name = rc.rdb$index_name "+\ |
|
|
"and rc.rdb$constraint_type = 'PRIMARY KEY' "+\ |
|
|
"and rc.rdb$relation_name = '%s' " % (relname)+\ |
|
|
"order by ri.rdb$field_position" |
|
|
|
|
|
cursor = self.native.cursor() |
|
|
cursor.execute(statement) |
|
|
|
|
|
list = [] |
|
|
for rs in cursor.fetchall(): |
|
|
list.append(lower(rstrip(rs[1]))) |
|
|
|
|
|
cursor.close() |
|
|
return list |
|
|
|
|
|
# Get fields for a table |
|
|
def __getFieldSchema(self, parent): |
|
|
|
|
|
statement = "select * from %s"%(parent.name) + " where (0=1)" |
|
|
|
|
|
cursor = self.native.cursor() |
|
|
cursor.execute(statement) |
|
|
|
|
|
list = [] |
|
|
|
|
|
for d in cursor.description: |
|
|
try: |
|
|
nativetype = lower(d[SIG2api.DESCRIPTION_TYPE_CODE].__name__) |
|
|
except AttributeError: |
|
|
nativetype='unknown' |
|
|
|
|
|
attrs={'id':d[SIG2api.DESCRIPTION_NAME], |
|
|
'name':lower(d[SIG2api.DESCRIPTION_NAME]), |
|
|
'type':'field', |
|
|
'nativetype': nativetype, |
|
|
'required': d[SIG2api.DESCRIPTION_NULL_OK]==0, |
|
|
'length': d[SIG2api.DESCRIPTION_DISPLAY_SIZE]} |
|
|
|
|
|
if nativetype in ('int','float','long'): |
|
|
attrs['datatype']='number' |
|
|
attrs['precision']=d[SIG2api.DESCRIPTION_SCALE] |
|
|
elif nativetype == 'tuple': |
|
|
attrs['datatype']='date' |
|
|
else: |
|
|
attrs['datatype']='text' |
|
|
|
|
|
cursor.execute("select rdb$default_source from rdb$relation_fields"+ \ |
|
|
" where rdb$relation_name = '%s' " % (parent.name)+ \ |
|
|
" and rdb$field_name = '%s'" % (upper(attrs['name']))) |
|
|
defrs = cursor.fetchone() |
|
|
if defrs[0]: |
|
|
dflt = defrs[0] |
|
|
if dflt[9:12] == "NOW": |
|
|
attrs['defaulttype'] = 'timestamp' |
|
|
else: |
|
|
attrs['defaulttype'] = 'constant' |
|
|
attrs['defaultval'] = dflt[8:] |
|
|
|
|
|
list.append(GDataObjects.Schema(attrs=attrs)) |
|
|
|
|
|
cursor.close() |
|
|
return list |
|
|
|
|
|
def _postConnect(self): |
|
|
self.triggerExtensions = TriggerExtensions(self.native) |
|
|
|
|
|
|
|
|
class DataObject_Object(DataObject, \ |
|
|
DBSIG2.DataObject_Object): |
|
|
|
|
|
def __init__(self): |
|
|
DataObject.__init__(self) |
|
|
|
|
|
def _buildQuery(self, conditions={},forDetail=None,additionalSQL=""): |
|
|
return DBSIG2.DataObject_Object._buildQuery(self, conditions,forDetail,\ |
|
|
additionalSQL) |
|
|
|
|
|
|
|
|
class DataObject_SQL(DataObject, \ |
|
|
DBSIG2.DataObject_SQL): |
|
|
def __init__(self): |
|
|
# Call DBSIG init first because DataObject needs to overwrite |
|
|
# some of its values |
|
|
DBSIG2.DataObject_SQL.__init__(self) |
|
|
DataObject.__init__(self) |
|
|
|
|
|
def _buildQuery(self, conditions={},forDetail=None,additionalSQL=""): |
|
|
return DBSIG2.DataObject_SQL._buildQuery(self, conditions, forDetail,\ |
|
|
additionalSQL) |
|
|
|
|
|
|
|
|
# |
|
|
# Extensions to Trigger Namespaces |
|
|
# |
|
|
class TriggerExtensions: |
|
|
|
|
|
def __init__(self, connection): |
|
|
self.__connection = connection |
|
|
|
|
91 |
# Return the current date, according to database |
# Return the current date, according to database |
92 |
def getTimeStamp(self): |
def getTimeStamp(self): |
93 |
return self.__singleQuery("select cast('now' as date) from rdb$database") |
return self.__singleQuery("select cast('now' as date) from rdb$database") |
123 |
return rv[0] |
return rv[0] |
124 |
except: |
except: |
125 |
return None |
return None |
|
|
|
|
###################################### |
|
|
# |
|
|
# The following hashes describe |
|
|
# this driver's characteristings. |
|
|
# |
|
|
###################################### |
|
|
|
|
|
# |
|
|
# All datasouce "types" and corresponding DataObject class |
|
|
# |
|
|
supportedDataObjects = { |
|
|
'object': DataObject_Object, |
|
|
'sql': DataObject_SQL |
|
|
} |
|
|
|
|
|
|
|
|
|
|