diff options
Diffstat (limited to 'ZPsycopgDA/DA.py')
-rw-r--r-- | ZPsycopgDA/DA.py | 397 |
1 files changed, 241 insertions, 156 deletions
diff --git a/ZPsycopgDA/DA.py b/ZPsycopgDA/DA.py index b9979b7..2242e2a 100644 --- a/ZPsycopgDA/DA.py +++ b/ZPsycopgDA/DA.py @@ -1,202 +1,287 @@ -# ZPsycopgDA/DA.py - ZPsycopgDA Zope product: Database Connection -# -# Copyright (C) 2004 Federico Di Gregorio <fog@initd.org> -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any later -# version. -# -# Or, at your option this program (ZPsycopgDA) can be distributed under the -# Zope Public License (ZPL) Version 1.0, as published on the Zope web site, -# http://www.zope.org/Resources/ZPL. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY -# or FITNESS FOR A PARTICULAR PURPOSE. -# -# See the LICENSE file for details. - - -ALLOWED_PSYCOPG_VERSIONS = ('1.99.9',) - -import sys -import db -import DABase -import Shared.DC.ZRDB.Connection +############################################################################## +# +# Zope Public License (ZPL) Version 1.0 +# ------------------------------------- +# +# Copyright (c) Digital Creations. All rights reserved. +# +# This license has been certified as Open Source(tm). +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# 1. Redistributions in source code must retain the above copyright +# notice, this list of conditions, and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions, and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# +# 3. Digital Creations requests that attribution be given to Zope +# in any manner possible. Zope includes a "Powered by Zope" +# button that is installed by default. While it is not a license +# violation to remove this button, it is requested that the +# attribution remain. A significant investment has been put +# into Zope, and this effort will continue if the Zope community +# continues to grow. This is one way to assure that growth. +# +# 4. All advertising materials and documentation mentioning +# features derived from or use of this software must display +# the following acknowledgement: +# +# "This product includes software developed by Digital Creations +# for use in the Z Object Publishing Environment +# (http://www.zope.org/)." +# +# In the event that the product being advertised includes an +# intact Zope distribution (with copyright and license included) +# then this clause is waived. +# +# 5. Names associated with Zope or Digital Creations must not be used to +# endorse or promote products derived from this software without +# prior written permission from Digital Creations. +# +# 6. Modified redistributions of any form whatsoever must retain +# the following acknowledgment: +# +# "This product includes software developed by Digital Creations +# for use in the Z Object Publishing Environment +# (http://www.zope.org/)." +# +# Intact (re-)distributions of any official Zope release do not +# require an external acknowledgement. +# +# 7. Modifications are encouraged but must be packaged separately as +# patches to official Zope releases. Distributions that do not +# clearly separate the patches from the original work must be clearly +# labeled as unofficial distributions. Modifications which do not +# carry the name Zope may be packaged in any form, as long as they +# conform to all of the clauses above. +# +# +# Disclaimer +# +# THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS ``AS IS'' AND ANY +# EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DIGITAL CREATIONS OR ITS +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# +# This software consists of contributions made by Digital Creations and +# many individuals on behalf of Digital Creations. Specific +# attributions are listed in the accompanying credits file. +# +############################################################################## +database_type='Psycopg' +__doc__='''%s Database Connection + +$Id: DA.py 531 2004-09-18 09:54:40Z fog $''' % database_type +__version__='$Revision: 1.20.2.14 $'[11:-2] +__psycopg_versions__ = ('1.1.12', '1.1.13', '1.1.14', '1.1.15', '1.1.16') + from db import DB -from Globals import DTMLFile -from Globals import HTMLFile -from ImageFile import ImageFile +import Shared.DC.ZRDB.Connection, sys, DABase, time +from Globals import HTMLFile, ImageFile from ExtensionClass import Base -from DateTime import DateTime - -# import psycopg and functions/singletons needed for date/time conversions - -import psycopg -from psycopg import DATETIME -from psycopg.extensions import TIME, DATE, INTERVAL -from psycopg.extensions import new_type, register_type - - - -# add a new connection to a folder +from string import find, join, split, rindex +try: + import psycopg + from psycopg import new_type, register_type, DATETIME, TIME, DATE, INTERVAL +except StandardError, err: + print err +try: + from DateTime import DateTime +except StandardError, err: + print err +try: + from App.Dialogs import MessageDialog +except: + pass +import time + +manage_addZPsycopgConnectionForm = HTMLFile('connectionAdd', globals()) +def manage_addZPsycopgConnection(self, id, title, + connection_string, zdatetime=None, + tilevel=2, check=None, REQUEST=None): + """Add a DB connection to a folder""" + self._setObject(id, Connection(id, title, connection_string, zdatetime, + check, tilevel)) + if REQUEST is not None: return self.manage_main(self,REQUEST) + + +# Convert an ISO timestamp string from postgres to a DateTime (zope version) +# object. +def cast_DateTime(str): + if str: + # this will split us into [date, time, GMT/AM/PM(if there)] + dt = split(str, ' ') + if len(dt) > 1: + # we now should split out any timezone info + dt[1] = split(dt[1], '-')[0] + dt[1] = split(dt[1], '+')[0] + t = time.mktime(time.strptime(join(dt[:2], ' '), '%Y-%m-%d %H:%M:%S')) + else: + t = time.mktime(time.strptime(dt[0], '%Y-%m-%d %H:%M:%S')) + return DateTime(t) -manage_addZPsycopgConnectionForm = DTMLFile('dtml/add',globals()) +# Convert an ISO date string from postgres to a DateTime(zope version) +# object. +def cast_Date(str): + if str: + return DateTime(time.mktime(time.strptime(str, '%Y-%m-%d'))) -def manage_addZPsycopgConnection(self, id, title, connection_string, - zdatetime=None, tilevel=2, - check=None, REQUEST=None): - """Add a DB connection to a folder.""" - self._setObject(id, Connection(id, title, connection_string, - zdatetime, check, tilevel)) - if REQUEST is not None: return self.manage_main(self, REQUEST) +# Convert a time string from postgres to a DateTime(zope version) object. +# WARNING: We set the day as today before feeding to DateTime so +# that it has the same DST settings. +def cast_Time(str): + if str: + return DateTime(time.strftime('%Y-%m-%d %H:%M:%S', + time.localtime(time.time())[:3]+ + time.strptime(str[:8], "%H:%M:%S")[3:])) - +# Convert a time string from postgres to a DateTime(zope version) object. +# WARNING: We set the day as the epoch day (1970-01-01) since this +# DateTime does not support time deltas. (EXPERIMENTAL USE WITH CARE!) +def cast_Interval(str): + return str -# the connection object class Connection(DABase.Connection): - """ZPsycopg Connection.""" - id = 'Psycopg_database_connection' - database_type = 'Psycopg' - meta_type = title = 'Z Psycopg Database Connection' - icon = 'misc_/ZPsycopg/conn' - - def __init__(self, id, title, connection_string, - zdatetime, check=None, tilevel=2, encoding=''): - self.zdatetime = zdatetime - self.id = str(id) + "The connection class." + database_type = database_type + id = '%s_database_connection' % database_type + meta_type = title = 'Z %s Database Connection' % database_type + icon = 'misc_/Z%sDA/conn' % database_type + + def __init__(self, id, title, connection_string, zdatetime, + check=None, tilevel=2, encoding='UTF-8'): + self.zdatetime=zdatetime + self.id=str(id) self.edit(title, connection_string, zdatetime, check=check, tilevel=tilevel, encoding=encoding) - - def factory(self): - return DB - - def table_info(self): - return self._v_database_connection.table_info() - - def edit(self, title, connection_string, - zdatetime, check=None, tilevel=2, encoding=''): - self.title = title - self.connection_string = connection_string - self.zdatetime = zdatetime - self.tilevel = tilevel - self.encoding = encoding + def edit(self, title, connection_string, zdatetime, + check=1, tilevel=2, encoding='UTF-8'): + self.title=title + self.connection_string=connection_string + self.zdatetime=zdatetime + self.tilevel=tilevel + self.encoding=encoding self.set_type_casts() - - if check: self.connect(self.connection_string) + if check: self.connect(connection_string) - manage_properties = DTMLFile('dtml/edit', globals()) + manage_properties=HTMLFile('connectionEdit', globals()) def manage_edit(self, title, connection_string, zdatetime=None, check=None, tilevel=2, encoding='UTF-8', REQUEST=None): - """Edit the DB connection.""" + """Change connection + """ self.edit(title, connection_string, zdatetime, check=check, tilevel=tilevel, encoding=encoding) if REQUEST is not None: - msg = "Connection edited." - return self.manage_main(self,REQUEST,manage_tabs_message=msg) - - def connect(self, s): - try: - self._v_database_connection.close() - except: - pass - - # check psycopg version and raise exception if does not match - if psycopg.__version__ not in ALLOWED_PSYCOPG_VERSIONS: - raise ImportError("psycopg version mismatch (imported %s)" + - psycopg.__version__) - - self.set_type_casts() - self._v_connected = '' - dbf = self.factory() + return MessageDialog( + title='Edited', + message='<strong>%s</strong> has been edited.' % self.id, + action ='./manage_main', + ) - # TODO: let the psycopg exception propagate, or not? - self._v_database_connection = dbf( - self.connection_string, self.tilevel, self.encoding) - self._v_database_connection.open() - self._v_connected = DateTime() - - return self - def set_type_casts(self): - # note that in both cases order *is* important + "Make changes to psycopg default typecast list" if self.zdatetime: - # use zope internal datetime routines + #use zope internal datetime routines + ZDATETIME=new_type((1184,1114), "ZDATETIME", cast_DateTime) + ZDATE=new_type((1082,), "ZDATE", cast_Date) + ZTIME=new_type((1083,), "ZTIME", cast_Time) + ZINTERVAL=new_type((1186,), "ZINTERVAL", cast_Interval) register_type(ZDATETIME) register_type(ZDATE) register_type(ZTIME) register_type(ZINTERVAL) else: - # use the standard + #use the standard. WARN: order is important! register_type(DATETIME) register_type(DATE) register_type(TIME) register_type(INTERVAL) - -# database connection registration data + + def factory(self): + return DB -classes = (Connection,) + def table_info(self): + return self._v_database_connection.table_info() -meta_types = ({'name':'Z Psycopg Database Connection', - 'action':'manage_addZPsycopgConnectionForm'},) + def connect(self,s): + try: self._v_database_connection.close() + except: pass -folder_methods = { - 'manage_addZPsycopgConnection': manage_addZPsycopgConnection, - 'manage_addZPsycopgConnectionForm': manage_addZPsycopgConnectionForm} + # check psycopg version and raise exception if does not match + if psycopg.__version__ not in __psycopg_versions__: + raise ImportError("psycopg version mismatch: " + + psycopg.__version__) -__ac_permissions__ = ( - ('Add Z Psycopg Database Connections', - ('manage_addZPsycopgConnectionForm', 'manage_addZPsycopgConnection')),) + self.set_type_casts() + self._v_connected='' + DB=self.factory() + try: + try: + # this is necessary when upgrading from old installs without + # having to recreate the connection object + if not hasattr(self, 'tilevel'): + self.tilevel = 2 + if not hasattr(self, 'encoding'): + self.encoding = 'UTF-8' + self._v_database_connection=DB(s, self.tilevel, self.encoding) + except: + t, v, tb = sys.exc_info() + raise 'BadRequest', ( + '<strong>Could not open connection.<br>' + 'Connection string: </strong><CODE>%s</CODE><br>\n' + '<pre>\n%s\n%s\n</pre>\n' + % (s,t,v)), tb + finally: tb=None + self._v_connected=DateTime() -# add icons + return self -misc_={'conn': ImageFile('Shared/DC/ZRDB/www/DBAdapterFolder_icon.gif')} + def sql_quote__(self, v): + # quote dictionary + quote_dict = {"\'": "''", "\\": "\\\\"} + for dkey in quote_dict.keys(): + if find(v, dkey) >= 0: + v=join(split(v,dkey),quote_dict[dkey]) + return "'%s'" % v -for icon in ('table', 'view', 'stable', 'what', 'field', 'text', 'bin', - 'int', 'float', 'date', 'time', 'datetime'): - misc_[icon] = ImageFile('icons/%s.gif' % icon, globals()) -# zope-specific psycopg typecasters +classes = ('DA.Connection',) -# convert an ISO timestamp string from postgres to a Zope DateTime object -def _cast_DateTime(str): - if str: - # this will split us into [date, time, GMT/AM/PM(if there)] - dt = split(str, ' ') - if len(dt) > 1: - # we now should split out any timezone info - dt[1] = split(dt[1], '-')[0] - dt[1] = split(dt[1], '+')[0] - return DateTime(join(dt[:2], ' ')) - else: - return DateTime(dt[0]) - -# convert an ISO date string from postgres to a Zope DateTime object -def _cast_Date(str): - if str: - return DateTime(str) +meta_types=( + {'name':'Z %s Database Connection' % database_type, + 'action':'manage_addZ%sConnectionForm' % database_type},) -# Convert a time string from postgres to a Zope DateTime object. -# NOTE: we set the day as today before feeding to DateTime so -# that it has the same DST settings. -def _cast_Time(str): - if str: - return DateTime(time.strftime('%Y-%m-%d %H:%M:%S', - time.localtime(time.time())[:3]+ - time.strptime(str[:8], "%H:%M:%S")[3:])) +folder_methods={ + 'manage_addZPsycopgConnection': manage_addZPsycopgConnection, + 'manage_addZPsycopgConnectionForm': manage_addZPsycopgConnectionForm} -# TODO: DateTime does not support intervals: what's the best we can do? -def _cast_Interval(str): - return str +__ac_permissions__=( + ('Add Z Psycopg Database Connections', + ('manage_addZPsycopgConnectionForm', 'manage_addZPsycopgConnection')),) -ZDATETIME = new_type((1184, 1114), "ZDATETIME", _cast_DateTime) -ZINTERVAL = new_type((1186,), "ZINTERVAL", _cast_Interval) -ZDATE = new_type((1082,), "ZDATE", _cast_Date) -ZTIME = new_type((1083,), "ZTIME", _cast_Time) +misc_={ + 'conn': ImageFile('Shared/DC/ZRDB/www/DBAdapterFolder_icon.gif')} +for icon in ('table', 'view', 'stable', 'what', + 'field', 'text','bin','int','float', + 'date','time','datetime'): + misc_[icon] = ImageFile('icons/%s.gif' % icon, globals()) |