1 |
# |
# |
2 |
# This file is part of GNU Enterprise. |
# This file is part of GNU Enterprise. |
3 |
# |
# |
4 |
# GNU Enterprise is free software; you can redistribute it |
# GNU Enterprise is free software; you can redistribute it |
5 |
# and/or modify it under the terms of the GNU General Public |
# and/or modify it under the terms of the GNU General Public |
6 |
# License as published by the Free Software Foundation; either |
# License as published by the Free Software Foundation; either |
7 |
# version 2, or(at your option) any later version. |
# version 2, or(at your option) any later version. |
8 |
# |
# |
9 |
# GNU Enterprise is distributed in the hope that it will be |
# GNU Enterprise is distributed in the hope that it will be |
10 |
# useful, but WITHOUT ANY WARRANTY; without even the implied |
# useful, but WITHOUT ANY WARRANTY; without even the implied |
11 |
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR |
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR |
12 |
# PURPOSE. See the GNU General Public License for more details. |
# PURPOSE. See the GNU General Public License for more details. |
13 |
# |
# |
14 |
# You should have received a copy of the GNU General Public |
# You should have received a copy of the GNU General Public |
15 |
# License along with program; see the file COPYING. If not, |
# License along with program; see the file COPYING. If not, |
16 |
# write to the Free Software Foundation, Inc., 59 Temple Place |
# write to the Free Software Foundation, Inc., 59 Temple Place |
17 |
# - Suite 330, Boston, MA 02111-1307, USA. |
# - Suite 330, Boston, MA 02111-1307, USA. |
18 |
# |
# |
19 |
# Copyright 2000, 2001 Free Software Foundation |
# Copyright 2000-2002 Free Software Foundation |
20 |
# |
# |
21 |
# FILE: |
# FILE: |
22 |
# _pgsql/DBdriver.py |
# _pgsql/DBdriver.py |
23 |
# |
# |
24 |
# DESCRIPTION: |
# DESCRIPTION: |
25 |
# A core Postgresql implementation of dbdriver the other |
# A core Postgresql dbdriver that the other (specific) |
26 |
# postgresql drivers can extend |
# postgresql drivers can extend |
27 |
# |
# |
28 |
# NOTES: |
# NOTES: |
29 |
# |
# |
30 |
|
|
|
|
|
|
from string import lower, join |
|
|
import sys |
|
31 |
import string |
import string |
32 |
|
from string import lower, join, split |
33 |
|
import sys |
34 |
from gnue.common import GDebug, GDataObjects |
from gnue.common import GDebug, GDataObjects |
35 |
from gnue.common.dbdrivers._dbsig.DBdriver \ |
from gnue.common.dbdrivers._dbsig.DBdriver \ |
36 |
import DBSIG_RecordSet, DBSIG_ResultSet, DBSIG_DataObject, \ |
import DBSIG_RecordSet, DBSIG_ResultSet, DBSIG_DataObject |
|
DBSIG_DataObject_Object |
|
37 |
|
|
38 |
#from gnue.common.dbdrivers._dbsig.DBdriver import DBSIG_DataObject_Object as PGSQL_DataObject_Object |
from gnue.common.dbdrivers._dbsig.DBdriver import DBSIG_DataObject_Object as PGSQL_DataObject_Object |
39 |
from gnue.common.dbdrivers._dbsig.DBdriver import DBSIG_DataObject_SQL as PGSQL_DataObject_SQL |
from gnue.common.dbdrivers._dbsig.DBdriver import DBSIG_DataObject_SQL as PGSQL_DataObject_SQL |
40 |
|
|
41 |
|
|
42 |
class PGSQL_RecordSet(DBSIG_RecordSet): |
class PGSQL_RecordSet(DBSIG_RecordSet): |
43 |
pass |
def _buildUpdateStatement(self): |
44 |
# def _buildUpdateStatement(self, table): |
updates = [] |
45 |
# updates = [] |
for field in self._modifiedFlags.keys(): |
46 |
# for field in self._modifiedFlags.keys(): |
## # To convert date from format '2002-12-31 23:59:59,99' |
47 |
# a = len (field.split ('.')) |
## # into '2002-12-31 23:59:59' format. |
48 |
# if (a == 2): |
## # We have to determine whether given string is date\time |
49 |
# [auxTable, auxField]= field.split ('.') |
## # maybe it's the most stupid way, but it should work. |
50 |
# if string.count(self._parent._dataObject.table,',') and (auxTable != table): |
## # TODO: if in ANY other field data of this format and comma in |
51 |
# continue |
## # place will exist, the remaining string from first ',' will be |
52 |
# elif (a == 1): |
## # eaten. |
53 |
# auxField= field |
## # If you know better decision - please, modify this code. |
54 |
# else: |
## |
55 |
# # raise |
## This should have been submitted as a patch for peer review. |
56 |
# pass |
## For starters, it can break non-date fields w/commas. |
57 |
|
## Secondly, such checks belong in _toSqlString! |
58 |
# updates.append ("%s=%s" % (auxField, |
## -- jcater |
59 |
# self._parent._dataObject._toSqlString(self._fields[field]))) |
## |
60 |
|
## tmpDate = self._fields[field] |
61 |
# where = [] |
## if ((len(tmpDate)==22) and (tmpDate[-3]==',')): |
62 |
|
## tmpDate = tmpDate.split(',') |
63 |
# if updates: |
## tmpDate = tmpDate[0] |
64 |
# if self._parent._dataObject._primaryKeys: |
## updates.append ("%s=%s" % (field, |
65 |
# for iterTable in self._parent._dataObject._primaryKeys.keys(): |
## self._parent._dataObject._toSqlString(tmpDate))) |
66 |
# if iterTable != table: continue |
|
67 |
# for iterField in self._parent._dataObject._primaryKeys[iterTable]: |
updates.append ("%s=%s" % (field, |
68 |
# auxWhere = "%s = '%s'" |
self._parent._dataObject._toSqlString(self._fields[field]))) |
69 |
# where.append(auxWhere % (iterField,self._initialData[table+"."+iterField])) |
|
70 |
# else: |
if self._parent._dataObject._primaryIdField: |
71 |
# for field in self._initialData.keys(): |
where = [self._parent._dataObject._primaryIdFormat % \ |
72 |
# a = len (field.split ('.')) |
self._initialData[self._parent._dataObject._primaryIdField] ] |
73 |
# if (a == 2): |
else: |
74 |
# [auxTable, auxField]= field.split ('.') |
where = [] |
75 |
# if string.count(self._parent._dataObject.table,',') and (auxTable != table): |
for field in self._initialData.keys(): |
76 |
# continue |
if self._initialData[field] == None: |
77 |
# elif (a == 1): |
where.append ("%s IS NULL" % field) |
78 |
# auxField= field |
else: |
79 |
# else: |
where.append ("%s=%s" % (field, self._parent._dataObject._toSqlString(self._initialData[field]))) |
80 |
# # raise |
return "UPDATE %s SET %s WHERE %s" % \ |
81 |
# pass |
(self._parent._dataObject.table, string.join(updates,','), \ |
82 |
|
string.join(where,' AND ') ) |
|
# if self._initialData[field] == None: |
|
|
# where.append ("%s IS NULL" % auxField) |
|
|
# else: |
|
|
# where.append ("%s=%s" % (auxField, self._parent._dataObject._toSqlString(self._initialData[field]))) |
|
|
|
|
|
# updateStmt = "BEGIN; SELECT %s FROM %s WHERE %s FOR UPDATE; " % \ |
|
|
# (string.join(updates,','), table, \ |
|
|
# string.join(where,' AND ') ) + \ |
|
|
# "UPDATE %s SET %s WHERE %s" % \ |
|
|
# (table, string.join(updates,','), \ |
|
|
# string.join(where,' AND ') ) + "; COMMIT;" |
|
83 |
|
|
|
# return updateStmt |
|
84 |
|
|
85 |
class PGSQL_ResultSet(DBSIG_ResultSet): |
class PGSQL_ResultSet(DBSIG_ResultSet): |
86 |
def __init__(self, dataObject, cursor=None, defaultValues={}, masterRecordSet=None, fieldNames=None): |
def __init__(self, dataObject, cursor=None, defaultValues={}, masterRecordSet=None, fieldNames=None): |
89 |
self._recordSetClass = PGSQL_RecordSet |
self._recordSetClass = PGSQL_RecordSet |
90 |
|
|
91 |
class PGSQL_DataObject(DBSIG_DataObject): |
class PGSQL_DataObject(DBSIG_DataObject): |
92 |
|
|
93 |
|
schema2nativeTypes={"auto":"int4", |
94 |
|
"int":"int", |
95 |
|
"number":"int", |
96 |
|
"float":"float", |
97 |
|
"decimal":"decimal", |
98 |
|
"varchar":"varchar", |
99 |
|
"char":"char", |
100 |
|
"blob":"text", |
101 |
|
"text":"text", |
102 |
|
"longtext":"text", |
103 |
|
"date":"date", |
104 |
|
"datetime":"datetime", |
105 |
|
"timestamp":"timestamp"} |
106 |
|
|
107 |
def __init__(self, pgdriver=None, pgresultset=None): |
def __init__(self, pgdriver=None, pgresultset=None): |
108 |
DBSIG_DataObject.__init__(self) |
DBSIG_DataObject.__init__(self) |
109 |
self._connectString = 'host=%s dbname=%s user=%s password=%s port=%s' |
self._connectString = 'host=%s dbname=%s user=%s password=%s port=%s' |
110 |
self._escapeSingleQuote = '\\' |
self._escapeSingleQuote = '\\' |
111 |
|
# date/time format |
112 |
|
self._dateTimeFormat = "'%Y-%m-%d %H:%M:%S'" |
113 |
|
|
114 |
if pgdriver: |
if pgdriver: |
115 |
self._pgdriver = pgdriver |
self._pgdriver = pgdriver |
116 |
self._DatabaseError = self._pgdriver.DatabaseError |
self._DatabaseError = self._pgdriver.DatabaseError |
117 |
if pgresultset: |
if pgresultset: |
118 |
self._resultSetClass = pgresultset |
self._resultSetClass = pgresultset |
119 |
|
|
120 |
def connect(self, connectData={}): |
def connect(self, connectData={}): |
121 |
GDebug.printMesg(1,"Postgresql database driver initializing") |
GDebug.printMesg(1,"Postgresql database driver initializing") |
122 |
try: |
try: |
|
# |
|
|
# Ugly dneighbo hack as no python studs to ask questions for |
|
|
# |
|
123 |
try: |
try: |
124 |
port = connectData['port'] |
port = connectData['port'] |
125 |
except: |
except: |
126 |
port = '5432' |
port = '5432' |
127 |
|
|
|
# |
|
|
# End hack (note port variable used below and defined in __init above) |
|
|
# |
|
128 |
self._dataConnection = self._pgdriver.connect(self._connectString % |
self._dataConnection = self._pgdriver.connect(self._connectString % |
129 |
(connectData['host'], |
(connectData['host'], |
130 |
connectData['dbname'], |
connectData['dbname'], |
143 |
raise GDataObjects.LoginError, value |
raise GDataObjects.LoginError, value |
144 |
|
|
145 |
try: |
try: |
146 |
encoding = connectData['encoding'] |
encoding = "" |
147 |
GDebug.printMesg(1,'Setting postgresql client_encoding to %s' % encoding) |
try: |
148 |
cursor = self._dataConnection.cursor() |
encoding = connectData['encoding'] |
149 |
cursor.execute("SET CLIENT_ENCODING TO '%s'" % encoding) |
except KeyError: |
150 |
cursor.close() |
# if encoding is not defined in connectData use gnue.conf setting instead |
151 |
except KeyError: |
try: |
152 |
pass |
encoding = gConfig('encoding') |
153 |
except self._DatabaseError: |
except: |
154 |
try: |
pass |
155 |
|
|
156 |
|
if encoding!="": |
157 |
|
GDebug.printMesg(1,'Setting postgresql client_encoding to %s' % encoding) |
158 |
|
cursor = self._dataConnection.cursor() |
159 |
|
cursor.execute("SET CLIENT_ENCODING TO '%s'" % encoding) |
160 |
|
cursor.close() |
161 |
|
|
162 |
|
except self._DatabaseError: |
163 |
|
try: |
164 |
cursor.close() |
cursor.close() |
165 |
except: |
except: |
166 |
pass |
pass |
167 |
|
|
168 |
|
if connectData.has_key('datetimeformat'): |
169 |
|
self._dateTimeFormat = "'%s'" % connectData['datetimeformat'] |
170 |
|
|
171 |
|
|
172 |
self._postConnect() |
self._postConnect() |
173 |
|
|
174 |
def _postConnect(self): |
def _postConnect(self): |
205 |
list = [] |
list = [] |
206 |
for rs in cursor.fetchall(): |
for rs in cursor.fetchall(): |
207 |
list.append(GDataObjects.Schema(attrs={'id':rs[2], 'name':rs[0], |
list.append(GDataObjects.Schema(attrs={'id':rs[2], 'name':rs[0], |
208 |
'type':rs[1] == 'v' and 'view' or 'table'}, |
'type':rs[1] == 'v' and 'view' or 'table', |
209 |
|
'primarykey': self.__getPrimaryKey(cursor, rs[2])}, |
210 |
getChildSchema=self.__getFieldSchema)) |
getChildSchema=self.__getFieldSchema)) |
211 |
|
|
212 |
cursor.close() |
cursor.close() |
224 |
rs = cursor.fetchone() |
rs = cursor.fetchone() |
225 |
if rs: |
if rs: |
226 |
schema = GDataObjects.Schema(attrs={'id':rs[2], 'name':rs[0], |
schema = GDataObjects.Schema(attrs={'id':rs[2], 'name':rs[0], |
227 |
'type':rs[1] == 'v' and 'view' or 'table'}, |
'type':rs[1] == 'v' and 'view' or 'table', |
228 |
|
'primarykey': self.__getPrimaryKey(cursor, rs[2]) }, |
229 |
getChildSchema=self.__getFieldSchema) |
getChildSchema=self.__getFieldSchema) |
230 |
else: |
else: |
231 |
schema = None |
schema = None |
233 |
cursor.close() |
cursor.close() |
234 |
return schema |
return schema |
235 |
|
|
236 |
|
def __getPrimaryKey(self, cursor, oid): |
237 |
|
cursor = self._dataConnection.cursor() |
238 |
|
cursor.execute("select indkey from pg_index where indrelid=%d" % oid) |
239 |
|
rs = cursor.fetchone() |
240 |
|
statement = "select attname from pg_attribute " \ |
241 |
|
"where attrelid = %d and attnum = %%d" % oid |
242 |
|
if rs: |
243 |
|
pks = [] |
244 |
|
for indpos in string.split(rs[0]): |
245 |
|
cursor.execute(statement % int(indpos)) |
246 |
|
pks.append(cursor.fetchone()[0]) |
247 |
|
cursor.close() |
248 |
|
return tuple(pks) |
249 |
|
else: |
250 |
|
cursor.close() |
251 |
|
return None |
252 |
|
|
253 |
# Get fields for a table |
# Get fields for a table |
254 |
def __getFieldSchema(self, parent): |
def __getFieldSchema(self, parent): |
255 |
|
|
256 |
statement = "select attname, pg_type.oid, typname, " + \ |
statement = "select attname, pg_type.oid, typname, " + \ |
257 |
" attnotnull, atthasdef, atttypmod " + \ |
" attnotnull, atthasdef, atttypmod, attnum, attlen " + \ |
258 |
"from pg_attribute, pg_type " + \ |
"from pg_attribute, pg_type " + \ |
259 |
"where attrelid = %d and " % (parent.id) + \ |
"where attrelid = %d and " % (parent.id) + \ |
260 |
"pg_type.oid = atttypid and attnum >= 0" + \ |
"pg_type.oid = atttypid and attnum >= 0" + \ |
270 |
'type':'field', 'nativetype': rs[2], |
'type':'field', 'nativetype': rs[2], |
271 |
'required': rs[3] and not rs[4]} |
'required': rs[3] and not rs[4]} |
272 |
|
|
273 |
if rs[2] in ('int8','int2','int4','numeric', |
if rs[2] in ('numeric','float4','float8','money','bool','int8','int2','int4'): |
|
'float4','float8','money','bool'): |
|
274 |
attrs['datatype']='number' |
attrs['datatype']='number' |
275 |
elif rs[2] in ('date','time','timestamp','abstime','reltime'): |
elif rs[2] in ('date','time','timestamp','abstime','reltime'): |
276 |
attrs['datatype']='date' |
attrs['datatype']='date' |
277 |
else: |
else: |
278 |
attrs['datatype']='text' |
attrs['datatype']='text' |
279 |
|
|
280 |
if rs[5] != -1: |
if rs[7] > 0: |
281 |
attrs['length'] = rs[5] |
attrs['length'] = rs[7] |
282 |
|
else: |
283 |
|
attrs['length'] = rs[5] - 4 |
284 |
|
|
285 |
|
|
286 |
|
# Find any default values |
287 |
|
if rs[4]: |
288 |
|
cursor.execute("select adsrc " + \ |
289 |
|
"from pg_attrdef " + \ |
290 |
|
"where adrelid = %d and adnum = %d" % (parent.id, rs[6])) |
291 |
|
defrs = cursor.fetchone() |
292 |
|
if defrs: |
293 |
|
dflt = defrs[0] |
294 |
|
if dflt[:8] == 'nextval(': |
295 |
|
attrs['defaulttype'] = 'sequence' |
296 |
|
attrs['defaultval'] = split(dflt,"'")[1] |
297 |
|
elif dflt == 'now()': |
298 |
|
attrs['defaulttype'] = 'system' |
299 |
|
attrs['defaultval'] = 'timestamp' |
300 |
|
else: |
301 |
|
attrs['defaulttype'] = 'constant' |
302 |
|
attrs['defaultval'] = dflt |
303 |
|
|
304 |
list.append(GDataObjects.Schema(attrs=attrs)) |
list.append(GDataObjects.Schema(attrs=attrs)) |
305 |
|
|