~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:08:31 UTC
  • Revision ID: fog-3d8587a243fa1d89012217ea99d2486813eb1955
SVN repo up to date (1.1.16pre1).

Show diffs side-by-side

added added

removed removed

Lines of Context:
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
 
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
 
27
92
 
28
93
from db import DB
29
 
from Globals import DTMLFile
30
 
from Globals import HTMLFile
31
 
from ImageFile import ImageFile
 
94
import Shared.DC.ZRDB.Connection, sys, DABase, time
 
95
from Globals import HTMLFile, ImageFile
32
96
from ExtensionClass import Base
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
 
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
 
59
159
 
60
160
class Connection(DABase.Connection):
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'
 
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
66
166
 
67
 
    def __init__(self, id, title, connection_string,
68
 
                 zdatetime, check=None, tilevel=2, encoding=''):
69
 
        self.zdatetime = zdatetime
70
 
        self.id = str(id)
 
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)
71
171
        self.edit(title, connection_string, zdatetime,
72
172
                  check=check, tilevel=tilevel, 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
 
 
 
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
88
181
        self.set_type_casts()
89
 
        
90
 
        if check: self.connect(self.connection_string)
 
182
        if check: self.connect(connection_string)
91
183
 
92
 
    manage_properties = DTMLFile('dtml/edit', globals())
 
184
    manage_properties=HTMLFile('connectionEdit', globals())
93
185
 
94
186
    def manage_edit(self, title, connection_string,
95
187
                    zdatetime=None, check=None, tilevel=2, encoding='UTF-8',
96
188
                    REQUEST=None):
97
 
        """Edit the DB connection."""
 
189
        """Change connection
 
190
        """
98
191
        self.edit(title, connection_string, zdatetime,
99
192
                  check=check, tilevel=tilevel, encoding=encoding)
100
193
        if REQUEST is not None:
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()
 
194
            return MessageDialog(
 
195
                title='Edited',
 
196
                message='<strong>%s</strong> has been edited.' % self.id,
 
197
                action ='./manage_main',
 
198
                )
118
199
        
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
 
 
127
200
    def set_type_casts(self):
128
 
        # note that in both cases order *is* important
 
201
        "Make changes to psycopg default typecast list"
129
202
        if self.zdatetime:
130
 
            # use zope internal datetime routines
 
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)
131
208
            register_type(ZDATETIME)
132
209
            register_type(ZDATE)
133
210
            register_type(ZTIME)
134
211
            register_type(ZINTERVAL)
135
212
        else:
136
 
            # use the standard
 
213
            #use the standard. WARN: order is important!
137
214
            register_type(DATETIME)
138
215
            register_type(DATE)
139
216
            register_type(TIME)
140
217
            register_type(INTERVAL)
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 = {
 
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={
150
274
    'manage_addZPsycopgConnection': manage_addZPsycopgConnection,
151
275
    'manage_addZPsycopgConnectionForm': manage_addZPsycopgConnectionForm}
152
276
 
153
 
__ac_permissions__ = (
 
277
__ac_permissions__=(
154
278
    ('Add Z Psycopg Database Connections',
155
279
     ('manage_addZPsycopgConnectionForm', 'manage_addZPsycopgConnection')),)
156
280
 
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'):
 
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'):
163
287
    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