~psycopg/psycopg/2.0.x

« back to all changes in this revision

Viewing changes to ZPsycopgDA/DA.py

  • Committer: Federico Di Gregorio
  • Date: 2004-10-29 16:15:45 UTC
  • Revision ID: fog-a4e5ae2c65dfd04775bcecac32ed8e8dda664017
Undo of wrong commit of 1.1.x source code over 2.x.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
##############################################################################
2
 
3
 
# Zope Public License (ZPL) Version 1.0
4
 
# -------------------------------------
5
 
6
 
# Copyright (c) Digital Creations.  All rights reserved.
7
 
8
 
# This license has been certified as Open Source(tm).
9
 
10
 
# Redistribution and use in source and binary forms, with or without
11
 
# modification, are permitted provided that the following conditions are
12
 
# met:
13
 
14
 
# 1. Redistributions in source code must retain the above copyright
15
 
#    notice, this list of conditions, and the following disclaimer.
16
 
17
 
# 2. Redistributions in binary form must reproduce the above copyright
18
 
#    notice, this list of conditions, and the following disclaimer in
19
 
#    the documentation and/or other materials provided with the
20
 
#    distribution.
21
 
22
 
# 3. Digital Creations requests that attribution be given to Zope
23
 
#    in any manner possible. Zope includes a "Powered by Zope"
24
 
#    button that is installed by default. While it is not a license
25
 
#    violation to remove this button, it is requested that the
26
 
#    attribution remain. A significant investment has been put
27
 
#    into Zope, and this effort will continue if the Zope community
28
 
#    continues to grow. This is one way to assure that growth.
29
 
30
 
# 4. All advertising materials and documentation mentioning
31
 
#    features derived from or use of this software must display
32
 
#    the following acknowledgement:
33
 
34
 
#      "This product includes software developed by Digital Creations
35
 
#      for use in the Z Object Publishing Environment
36
 
#      (http://www.zope.org/)."
37
 
38
 
#    In the event that the product being advertised includes an
39
 
#    intact Zope distribution (with copyright and license included)
40
 
#    then this clause is waived.
41
 
42
 
# 5. Names associated with Zope or Digital Creations must not be used to
43
 
#    endorse or promote products derived from this software without
44
 
#    prior written permission from Digital Creations.
45
 
46
 
# 6. Modified redistributions of any form whatsoever must retain
47
 
#    the following acknowledgment:
48
 
49
 
#      "This product includes software developed by Digital Creations
50
 
#      for use in the Z Object Publishing Environment
51
 
#      (http://www.zope.org/)."
52
 
53
 
#    Intact (re-)distributions of any official Zope release do not
54
 
#    require an external acknowledgement.
55
 
56
 
# 7. Modifications are encouraged but must be packaged separately as
57
 
#    patches to official Zope releases.  Distributions that do not
58
 
#    clearly separate the patches from the original work must be clearly
59
 
#    labeled as unofficial distributions.  Modifications which do not
60
 
#    carry the name Zope may be packaged in any form, as long as they
61
 
#    conform to all of the clauses above.
62
 
63
 
64
 
# Disclaimer
65
 
66
 
#   THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS ``AS IS'' AND ANY
67
 
#   EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
68
 
#   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
69
 
#   PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL DIGITAL CREATIONS OR ITS
70
 
#   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
71
 
#   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
72
 
#   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
73
 
#   USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
74
 
#   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
75
 
#   OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
76
 
#   OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
77
 
#   SUCH DAMAGE.
78
 
79
 
80
 
# This software consists of contributions made by Digital Creations and
81
 
# many individuals on behalf of Digital Creations.  Specific
82
 
# attributions are listed in the accompanying credits file.
83
 
84
 
##############################################################################
85
 
database_type='Psycopg'
86
 
__doc__='''%s Database Connection
87
 
 
88
 
$Id: DA.py 531 2004-09-18 09:54:40Z fog $''' % database_type
89
 
__version__='$Revision: 1.20.2.14 $'[11:-2]
90
 
__psycopg_versions__ = ('1.1.12', '1.1.13', '1.1.14', '1.1.15', '1.1.16')
91
 
 
 
1
# ZPsycopgDA/DA.py - ZPsycopgDA Zope product: Database Connection
 
2
#
 
3
# Copyright (C) 2004 Federico Di Gregorio <fog@initd.org>
 
4
#
 
5
# This program is free software; you can redistribute it and/or modify
 
6
# it under the terms of the GNU General Public License as published by the
 
7
# Free Software Foundation; either version 2, or (at your option) any later
 
8
# version.
 
9
#
 
10
# Or, at your option this program (ZPsycopgDA) can be distributed under the
 
11
# Zope Public License (ZPL) Version 1.0, as published on the Zope web site,
 
12
# http://www.zope.org/Resources/ZPL.
 
13
#
 
14
# This program is distributed in the hope that it will be useful, but
 
15
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY
 
16
# or FITNESS FOR A PARTICULAR PURPOSE.
 
17
#
 
18
# See the LICENSE file for details.
 
19
 
 
20
 
 
21
ALLOWED_PSYCOPG_VERSIONS = ('1.99.9',)
 
22
 
 
23
import sys
 
24
import db
 
25
import DABase
 
26
import Shared.DC.ZRDB.Connection
92
27
 
93
28
from db import DB
94
 
import Shared.DC.ZRDB.Connection, sys, DABase, time
95
 
from Globals import HTMLFile, ImageFile
 
29
from Globals import DTMLFile
 
30
from Globals import HTMLFile
 
31
from ImageFile import ImageFile
96
32
from ExtensionClass import Base
97
 
from string import find, join, split, rindex
98
 
try:
99
 
    import psycopg
100
 
    from psycopg import new_type, register_type, DATETIME, TIME, DATE, INTERVAL
101
 
except StandardError, err:
102
 
    print err
103
 
try:
104
 
    from DateTime import DateTime
105
 
except StandardError, err:
106
 
    print err
107
 
try:
108
 
    from App.Dialogs import MessageDialog
109
 
except:
110
 
    pass
111
 
import time
112
 
 
113
 
manage_addZPsycopgConnectionForm = HTMLFile('connectionAdd', globals())
114
 
def manage_addZPsycopgConnection(self, id, title,
115
 
                                 connection_string, zdatetime=None,
116
 
                                 tilevel=2, check=None, REQUEST=None):
117
 
    """Add a DB connection to a folder"""
118
 
    self._setObject(id, Connection(id, title, connection_string, zdatetime,
119
 
                                   check, tilevel))
120
 
    if REQUEST is not None: return self.manage_main(self,REQUEST)
121
 
 
122
 
 
123
 
# Convert an ISO timestamp string from postgres to a DateTime (zope version)
124
 
# object.
125
 
def cast_DateTime(str):
126
 
    if str:
127
 
        # this will split us into [date, time, GMT/AM/PM(if there)]
128
 
        dt = split(str, ' ')
129
 
        if len(dt) > 1:
130
 
            # we now should split out any timezone info
131
 
            dt[1] = split(dt[1], '-')[0]
132
 
            dt[1] = split(dt[1], '+')[0]
133
 
            t = time.mktime(time.strptime(join(dt[:2], ' '), '%Y-%m-%d %H:%M:%S'))
134
 
        else:
135
 
            t = time.mktime(time.strptime(dt[0], '%Y-%m-%d %H:%M:%S'))
136
 
        return DateTime(t)
137
 
 
138
 
# Convert an ISO date string from postgres to a DateTime(zope version)
139
 
# object.
140
 
def cast_Date(str):
141
 
    if str:
142
 
        return DateTime(time.mktime(time.strptime(str, '%Y-%m-%d')))
143
 
 
144
 
# Convert a time string from postgres to a DateTime(zope version) object.
145
 
# WARNING: We set the day as today before feeding to DateTime so
146
 
# that it has the same DST settings.
147
 
def cast_Time(str):
148
 
    if str:
149
 
        return DateTime(time.strftime('%Y-%m-%d %H:%M:%S',
150
 
                                  time.localtime(time.time())[:3]+
151
 
                                  time.strptime(str[:8], "%H:%M:%S")[3:]))
152
 
 
153
 
# Convert a time string from postgres to a DateTime(zope version) object.
154
 
# WARNING: We set the day as the epoch day (1970-01-01) since this
155
 
# DateTime does not support time deltas. (EXPERIMENTAL USE WITH CARE!)
156
 
def cast_Interval(str):
157
 
    return str
158
 
 
 
33
from DateTime import DateTime
 
34
 
 
35
# import psycopg and functions/singletons needed for date/time conversions
 
36
 
 
37
import psycopg
 
38
from psycopg import DATETIME
 
39
from psycopg.extensions import TIME, DATE, INTERVAL
 
40
from psycopg.extensions import new_type, register_type
 
41
 
 
42
 
 
43
 
 
44
# add a new connection to a folder
 
45
 
 
46
manage_addZPsycopgConnectionForm = DTMLFile('dtml/add',globals())
 
47
 
 
48
def manage_addZPsycopgConnection(self, id, title, connection_string,
 
49
                                 zdatetime=None, tilevel=2,
 
50
                                 check=None, REQUEST=None):
 
51
    """Add a DB connection to a folder."""
 
52
    self._setObject(id, Connection(id, title, connection_string,
 
53
                                   zdatetime, check, tilevel))
 
54
    if REQUEST is not None: return self.manage_main(self, REQUEST)
 
55
 
 
56
 
 
57
 
 
58
# the connection object
159
59
 
160
60
class Connection(DABase.Connection):
161
 
    "The connection class."
162
 
    database_type = database_type
163
 
    id = '%s_database_connection' % database_type
164
 
    meta_type = title = 'Z %s Database Connection' % database_type
165
 
    icon = 'misc_/Z%sDA/conn' % database_type
 
61
    """ZPsycopg Connection."""
 
62
    id = 'Psycopg_database_connection' 
 
63
    database_type     = 'Psycopg'
 
64
    meta_type = title = 'Z Psycopg Database Connection'
 
65
    icon              = 'misc_/ZPsycopg/conn'
166
66
 
167
 
    def __init__(self, id, title, connection_string, zdatetime,
168
 
                 check=None, tilevel=2, encoding='UTF-8'):
169
 
        self.zdatetime=zdatetime
170
 
        self.id=str(id)
 
67
    def __init__(self, id, title, connection_string,
 
68
                 zdatetime, check=None, tilevel=2, encoding=''):
 
69
        self.zdatetime = zdatetime
 
70
        self.id = str(id)
171
71
        self.edit(title, connection_string, zdatetime,
172
72
                  check=check, tilevel=tilevel, encoding=encoding)
173
 
 
174
 
    def edit(self, title, connection_string, zdatetime,
175
 
             check=1, tilevel=2, encoding='UTF-8'):
176
 
        self.title=title
177
 
        self.connection_string=connection_string
178
 
        self.zdatetime=zdatetime
179
 
        self.tilevel=tilevel
180
 
        self.encoding=encoding
 
73
        
 
74
    def factory(self):
 
75
        return DB
 
76
 
 
77
    def table_info(self):
 
78
        return self._v_database_connection.table_info()
 
79
 
 
80
    def edit(self, title, connection_string,
 
81
             zdatetime, check=None, tilevel=2, encoding=''):
 
82
        self.title = title
 
83
        self.connection_string = connection_string
 
84
        self.zdatetime = zdatetime
 
85
        self.tilevel = tilevel
 
86
        self.encoding = encoding
 
87
 
181
88
        self.set_type_casts()
182
 
        if check: self.connect(connection_string)
 
89
        
 
90
        if check: self.connect(self.connection_string)
183
91
 
184
 
    manage_properties=HTMLFile('connectionEdit', globals())
 
92
    manage_properties = DTMLFile('dtml/edit', globals())
185
93
 
186
94
    def manage_edit(self, title, connection_string,
187
95
                    zdatetime=None, check=None, tilevel=2, encoding='UTF-8',
188
96
                    REQUEST=None):
189
 
        """Change connection
190
 
        """
 
97
        """Edit the DB connection."""
191
98
        self.edit(title, connection_string, zdatetime,
192
99
                  check=check, tilevel=tilevel, encoding=encoding)
193
100
        if REQUEST is not None:
194
 
            return MessageDialog(
195
 
                title='Edited',
196
 
                message='<strong>%s</strong> has been edited.' % self.id,
197
 
                action ='./manage_main',
198
 
                )
 
101
            msg = "Connection edited."
 
102
            return self.manage_main(self,REQUEST,manage_tabs_message=msg)
 
103
 
 
104
    def connect(self, s):
 
105
        try:
 
106
            self._v_database_connection.close()
 
107
        except:
 
108
            pass
 
109
 
 
110
        # check psycopg version and raise exception if does not match
 
111
        if psycopg.__version__ not in ALLOWED_PSYCOPG_VERSIONS:
 
112
            raise ImportError("psycopg version mismatch (imported %s)" +
 
113
                              psycopg.__version__)
 
114
 
 
115
        self.set_type_casts()
 
116
        self._v_connected = ''
 
117
        dbf = self.factory()
199
118
        
 
119
        # TODO: let the psycopg exception propagate, or not?
 
120
        self._v_database_connection = dbf(
 
121
            self.connection_string, self.tilevel, self.encoding)
 
122
        self._v_database_connection.open()
 
123
        self._v_connected = DateTime()
 
124
 
 
125
        return self
 
126
 
200
127
    def set_type_casts(self):
201
 
        "Make changes to psycopg default typecast list"
 
128
        # note that in both cases order *is* important
202
129
        if self.zdatetime:
203
 
            #use zope internal datetime routines
204
 
            ZDATETIME=new_type((1184,1114), "ZDATETIME", cast_DateTime)
205
 
            ZDATE=new_type((1082,), "ZDATE", cast_Date)
206
 
            ZTIME=new_type((1083,), "ZTIME", cast_Time)
207
 
            ZINTERVAL=new_type((1186,), "ZINTERVAL", cast_Interval)
 
130
            # use zope internal datetime routines
208
131
            register_type(ZDATETIME)
209
132
            register_type(ZDATE)
210
133
            register_type(ZTIME)
211
134
            register_type(ZINTERVAL)
212
135
        else:
213
 
            #use the standard. WARN: order is important!
 
136
            # use the standard
214
137
            register_type(DATETIME)
215
138
            register_type(DATE)
216
139
            register_type(TIME)
217
140
            register_type(INTERVAL)
218
 
            
219
 
    def factory(self):
220
 
        return DB
221
 
 
222
 
    def table_info(self):
223
 
        return self._v_database_connection.table_info()
224
 
 
225
 
    def connect(self,s):
226
 
        try: self._v_database_connection.close()
227
 
        except: pass
228
 
 
229
 
        # check psycopg version and raise exception if does not match
230
 
        if psycopg.__version__ not in __psycopg_versions__:
231
 
            raise ImportError("psycopg version mismatch: " +
232
 
                              psycopg.__version__)
233
 
 
234
 
        self.set_type_casts()
235
 
        self._v_connected=''
236
 
        DB=self.factory()
237
 
        try:
238
 
            try:
239
 
                # this is necessary when upgrading from old installs without
240
 
                # having to recreate the connection object
241
 
                if not hasattr(self, 'tilevel'):
242
 
                    self.tilevel = 2
243
 
                if not hasattr(self, 'encoding'):
244
 
                    self.encoding = 'UTF-8'
245
 
                self._v_database_connection=DB(s, self.tilevel, self.encoding)
246
 
            except:
247
 
                t, v, tb = sys.exc_info()
248
 
                raise 'BadRequest', (
249
 
                    '<strong>Could not open connection.<br>'
250
 
                    'Connection string: </strong><CODE>%s</CODE><br>\n'
251
 
                    '<pre>\n%s\n%s\n</pre>\n'
252
 
                    % (s,t,v)), tb
253
 
        finally: tb=None
254
 
        self._v_connected=DateTime()
255
 
 
256
 
        return self
257
 
 
258
 
    def sql_quote__(self, v):
259
 
        # quote dictionary
260
 
        quote_dict = {"\'": "''", "\\": "\\\\"}
261
 
        for dkey in quote_dict.keys():
262
 
            if find(v, dkey) >= 0:
263
 
                v=join(split(v,dkey),quote_dict[dkey])
264
 
        return "'%s'" % v
265
 
 
266
 
 
267
 
classes = ('DA.Connection',)
268
 
 
269
 
meta_types=(
270
 
    {'name':'Z %s Database Connection' % database_type,
271
 
     'action':'manage_addZ%sConnectionForm' % database_type},)
272
 
 
273
 
folder_methods={
 
141
    
 
142
# database connection registration data
 
143
 
 
144
classes = (Connection,)
 
145
 
 
146
meta_types = ({'name':'Z Psycopg Database Connection',
 
147
               'action':'manage_addZPsycopgConnectionForm'},)
 
148
 
 
149
folder_methods = {
274
150
    'manage_addZPsycopgConnection': manage_addZPsycopgConnection,
275
151
    'manage_addZPsycopgConnectionForm': manage_addZPsycopgConnectionForm}
276
152
 
277
 
__ac_permissions__=(
 
153
__ac_permissions__ = (
278
154
    ('Add Z Psycopg Database Connections',
279
155
     ('manage_addZPsycopgConnectionForm', 'manage_addZPsycopgConnection')),)
280
156
 
281
 
misc_={
282
 
    'conn': ImageFile('Shared/DC/ZRDB/www/DBAdapterFolder_icon.gif')}
283
 
 
284
 
for icon in ('table', 'view', 'stable', 'what',
285
 
             'field', 'text','bin','int','float',
286
 
             'date','time','datetime'):
 
157
# add icons
 
158
 
 
159
misc_={'conn': ImageFile('Shared/DC/ZRDB/www/DBAdapterFolder_icon.gif')}
 
160
 
 
161
for icon in ('table', 'view', 'stable', 'what', 'field', 'text', 'bin',
 
162
             'int', 'float', 'date', 'time', 'datetime'):
287
163
    misc_[icon] = ImageFile('icons/%s.gif' % icon, globals())
 
164
 
 
165
# zope-specific psycopg typecasters
 
166
 
 
167
# convert an ISO timestamp string from postgres to a Zope DateTime object
 
168
def _cast_DateTime(str):
 
169
    if str:
 
170
        # this will split us into [date, time, GMT/AM/PM(if there)]
 
171
        dt = split(str, ' ')
 
172
        if len(dt) > 1:
 
173
            # we now should split out any timezone info
 
174
            dt[1] = split(dt[1], '-')[0]
 
175
            dt[1] = split(dt[1], '+')[0]
 
176
            return DateTime(join(dt[:2], ' '))
 
177
        else:
 
178
            return DateTime(dt[0])
 
179
 
 
180
# convert an ISO date string from postgres to a Zope DateTime object
 
181
def _cast_Date(str):
 
182
    if str:
 
183
        return DateTime(str)
 
184
 
 
185
# Convert a time string from postgres to a Zope DateTime object.
 
186
# NOTE: we set the day as today before feeding to DateTime so
 
187
# that it has the same DST settings.
 
188
def _cast_Time(str):
 
189
    if str:
 
190
        return DateTime(time.strftime('%Y-%m-%d %H:%M:%S',
 
191
                                      time.localtime(time.time())[:3]+
 
192
                                      time.strptime(str[:8], "%H:%M:%S")[3:]))
 
193
 
 
194
# TODO: DateTime does not support intervals: what's the best we can do?
 
195
def _cast_Interval(str):
 
196
    return str
 
197
 
 
198
ZDATETIME = new_type((1184, 1114), "ZDATETIME", _cast_DateTime)
 
199
ZINTERVAL = new_type((1186,), "ZINTERVAL", _cast_Interval)
 
200
ZDATE = new_type((1082,), "ZDATE", _cast_Date)
 
201
ZTIME = new_type((1083,), "ZTIME", _cast_Time)
 
202