Commit dc2f0691 authored by Hanno Schlichting's avatar Hanno Schlichting

Factored out the `Products.ZSQLMethod` into its own distribution. The...

Factored out the `Products.ZSQLMethod` into its own distribution. The distribution also includes the `Shared.DC.ZRDB` code.
parent f4e591f7
......@@ -19,6 +19,7 @@ parts =
sources-dir = develop
auto-checkout =
DateTime
Products.ZSQLMethods
[test]
......@@ -51,6 +52,7 @@ eggs =
MultiMapping
Persistence
Products.ZCTextIndex
Products.ZSQLMethods
Record
RestrictedPython
initgroups
......
......@@ -22,6 +22,9 @@ Bugs Fixed
Restructuring
+++++++++++++
- Factored out the `Products.ZSQLMethod` into its own distribution. The
distribution also includes the `Shared.DC.ZRDB` code.
- Made both `Shared` and `Shared.DC` namespace packages.
- Removed fallback code for old Python versions from
......
......@@ -48,6 +48,7 @@ setup(name='Zope2',
'MultiMapping',
'Persistence',
'Products.ZCTextIndex',
'Products.ZSQLMethods',
'Record',
'RestrictedPython',
'ZConfig',
......
......@@ -10,6 +10,7 @@ MultiMapping = svn svn://svn.zope.org/repos/main/MultiMapping/trunk
nt_svcutils = svn svn://svn.zope.org/repos/main/nt_svcutils/trunk
Persistence = svn svn://svn.zope.org/repos/main/Persistence/trunk
Products.ZCTextIndex = svn svn://svn.zope.org/repos/main/Products.ZCTextIndex/trunk
Products.ZSQLMethods = svn svn://svn.zope.org/repos/main/Products.ZSQLMethods/trunk
Record = svn svn://svn.zope.org/repos/main/Record/trunk
tempstorage = svn svn://svn.zope.org/repos/main/tempstorage/trunk
zExceptions = svn svn://svn.zope.org/repos/main/zExceptions/trunk
......
Aqueduct SQL Methods Changes
AqueductSQLMethods 1.2.1
Bugs Fixed
- The new Aqueduct.Results module was ommitted.
AqueductSQLMethods 1.2
Bugs Fixed
- Source changes were lost when resizing the editing screen.
- When testing, on DAs that use new DA protocol,
non-select SQL is reported as such.
Features Added
- Support for new Aqueduct DA protocol, providing much faster
retrievals.
- Edit screen size preferences are now distinct from Document
size preferences.
AqueductSQLMethods 1.1.5
Features Added
- Added permission settings to work with recent Principia versions.
Aqueduct SQL Methods 1.1.4
Bugs Fixed
- Generated reports failed in cases where column names contained
odd characters, as would be the case with::
select salary*1.2 from payrole
Aqueduct SQL Methods 1.1.3
Bugs Fixed
- Changed permission settings to be in line with principia 1.2
Aqueduct SQL Methods 1.1.2
Bugs Fixed
- If strings contained backslashes but not backslash-t or
backslash-n, then the backslashes were getting doubled.
Aqueduct SQL Methods 1.1.1
Bugs Fixed
- The SQL string quoting done by the sqlvar and sqltest tags
failed on non-ANSI-SQL databases like mySQL. Extra hooks
were added to allow database adapters to provide custom quoting
logic.
Aqueduct SQL Methods 1.1.0
Features
- New DTML tags are available in SQLMethods that greatly
improve the ease, safety, and flexibility of database methods.
- SQL Templates are now rendered in the context of the containing
folder, rather than the method itself.
- Input arguments are shown in input forms in the order that
they were defined, rather than in alphabetical order.
Aqueduct SQL Methods 1.0.5
Features Fixed
- Database methods are slightly better about binding themselves
to the correct object when acquired.
Aqueduct SQL Methods 1.0.4
Features Fixed
- RDBMS transaction boundaries now coincide with Principia
tranasaction boundaries. For example, if a Document
runs 3 database methods and the third method raises an error,
then the other methods are aborted.
Aqueduct SQL Methods 1.0.3
Bugs Fixed
- Add permissions were not editable.
- Methods on database records could not use acquired attributes.
AqueductSQLMethods 1.0.1
Bug Fixes
- Error reporting was broken in test mode.
Features
- Include generated SQL source in test output.
ZSQLMethods
The ZSQLMethods product provides support for SQL Method objects
which can be used in conjunction with any database adapter to
use relational database data from within the Zope environment.
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
__doc__='''SQL Methods
$Id$'''
__version__='$Revision: 1.21 $'[11:-2]
from AccessControl.class_init import InitializeClass
from AccessControl.Permissions import change_database_methods
from AccessControl.SecurityInfo import ClassSecurityInfo
from App.special_dtml import DTMLFile
from Shared.DC.ZRDB.DA import DA
def SQLConnectionIDs(self):
"""Find SQL database connections in the current folder and above
This function return a list of ids.
"""
ids={}
have_id=ids.has_key
StringType=type('')
while self is not None:
if hasattr(self, 'objectValues'):
for o in self.objectValues():
if (hasattr(o,'_isAnSQLConnection') and o._isAnSQLConnection
and hasattr(o,'id')):
id=o.id
if type(id) is not StringType: id=id()
if not have_id(id):
if hasattr(o,'title_and_id'): o=o.title_and_id()
else: o=id
ids[id]=id
if hasattr(self, 'aq_parent'): self=self.aq_parent
else: self=None
ids=map(lambda item: (item[1], item[0]), ids.items())
ids.sort()
return ids
manage_addZSQLMethodForm=DTMLFile('dtml/add', globals())
def manage_addZSQLMethod(self, id, title,
connection_id, arguments, template,
REQUEST=None, submit=None):
"""Add an SQL Method
The 'connection_id' argument is the id of a database connection
that resides in the current folder or in a folder above the
current folder. The database should understand SQL.
The 'arguments' argument is a string containing an arguments
specification, as would be given in the SQL method cration form.
The 'template' argument is a string containing the source for the
SQL Template.
"""
# Note - type checking is handled by _setObject and constructor.
self._setObject(id, SQL(id, title, connection_id, arguments, template))
if REQUEST is not None:
try: u=self.DestinationURL()
except: u=REQUEST['URL1']
if submit==" Add and Edit ":
u="%s/%s/manage_main" % (u,id)
elif submit==" Add and Test ":
u="%s/%s/manage_testForm" % (u,id)
else:
u=u+'/manage_main'
REQUEST.RESPONSE.redirect(u)
return ''
class SQL(DA):
"""SQL Database methods
SQL Database methods are used to access external SQL databases.
They support three important abstractions:
- Method
SQL Methods behave like methods of the folders they are
accessed in. In particular, they can be used from other
methods, like Documents, ExternalMethods, and even other SQL
Methods.
- Searchability
Database methods support the Searchable Object Interface.
Search interface wizards can be used to build user
interfaces to them. They can be used in joins and
unions. They provide meta-data about their input parameters
and result data.
For more information, see the searchable-object interface
specification.
- Containment
Database methods support URL traversal to access and invoke
methods on individual record objects. For example, suppose you
had an 'employees' database method that took a single argument
'employee_id'. Suppose that employees had a 'service_record'
method (defined in a record class or acquired from a
folder). The 'service_record' method could be accessed with a
URL like::
employees/employee_id/1234/service_record
"""
meta_type='Z SQL Method'
security = ClassSecurityInfo()
security.declareProtected(change_database_methods, 'manage')
security.declareProtected(change_database_methods, 'manage_main')
manage=manage_main=DTMLFile('dtml/edit', globals())
manage_main._setName('manage_main')
InitializeClass(SQL)
# install SQL.py
# install __init__.py
# install add.dtml
# install edit.dtml
# install CHANGES.txt
# package Aqueduct
# package AqueductDA
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""SQL Method Product.
$Id$
"""
import Shared.DC.ZRDB.Search, Shared.DC.ZRDB.Aqueduct, SQL
import Shared.DC.ZRDB.RDB
import Shared.DC.ZRDB.sqlvar, Shared.DC.ZRDB.sqlgroup, Shared.DC.ZRDB.sqltest
def initialize(context):
context.registerClass(
SQL.SQL,
permission='Add Database Methods',
constructors=(SQL.manage_addZSQLMethodForm, SQL.manage_addZSQLMethod),
icon='sqlmethod.gif',
# XXX: can this permission be removed?
permissions=('Open/Close Database Connections',),
legacy=(SQL.SQLConnectionIDs,)
)
context.registerClass(
meta_type='Z Search Interface',
permission='Add Documents, Images, and Files',
constructors=(Shared.DC.ZRDB.Search.addForm,
Shared.DC.ZRDB.Search.manage_addZSearch),
legacy=(Shared.DC.ZRDB.Search.ZQueryIds,)
)
context.registerHelp()
context.registerHelpTitle('Zope Help')
__module_aliases__=(
('Products.AqueductSQLMethods','Products.ZSQLMethods'),
('Aqueduct', Shared.DC.ZRDB),
('AqueductDA', Shared.DC.ZRDB),
('Products.AqueductSQLMethods.SQL', SQL),
('Aqueduct.Aqueduct', Shared.DC.ZRDB.Aqueduct),
('AqueductDA.DA', Shared.DC.ZRDB.DA),
('Aqueduct.RDB', Shared.DC.ZRDB.RDB),
('AqueductDA.sqlvar', Shared.DC.ZRDB.sqlvar),
('AqueductDA.sqltest', Shared.DC.ZRDB.sqltest),
('AqueductDA.sqlgroup', Shared.DC.ZRDB.sqlgroup),
)
<dtml-var manage_page_header>
<dtml-var "manage_form_title(this(), _,
form_title='Add SQL Method',
help_product='ZSQLMethods',
help_topic='Z-SQL-Method_Add.stx'
)">
<dtml-if SQLConnectionIDs>
<p class="form-help">
A SQL Method allows you to access a SQL database. For more information see
the <a href="http://www.zope.org/Documentation/Guides/ZSQL">Z SQL Methods
User's Guide</a>.
</p>
<p class="form-help">
In the form below <EM>connection id</EM> is the name of the SQL Database
Connection to use. <EM>Arguments</EM> is a list of variables which the
SQL Method accepts. <EM>Query template</EM> is a template of the SQL
statement which the SQL Method will execute.
</p>
<form action="manage_addZSQLMethod" method="post">
<table cellspacing="0" cellpadding="2" border="0">
<tr>
<td align="left" valign="top">
<div class="form-label">
Id
</div>
</td>
<td align="left" valign="top">
<input type="text" name="id" size="40" value="" />
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-optional">
Title
</div>
</td>
<td align="left" valign="top">
<input type="text" name="title" size="40" />
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Connection Id
</div>
</td>
<td align="left" valign="top">
<div class="form-element">
<select name="connection_id">
<dtml-in SQLConnectionIDs>
<option value="&dtml-sequence-item;">
&dtml-sequence-key;</option>
</dtml-in>
</select>
</div>
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-optional">
Arguments
</div>
</td>
<td align="left" valign="top">
<textarea name="arguments" cols="40" rows="4"></textarea>
</td>
</tr>
<tr>
<td align="left" valign="top" colspan="2">
<span class="form-label">Query Template</span>
<br />
<div style="width: 100%;">
<textarea style="width: 100%;" name="template:text" rows="9" cols="60"
wrap="off">select * from data</textarea></div>
</td>
</tr>
<tr>
<td align="left" valign="top" colspan="2">
<div class="form-element">
<input class="form-element" type="submit" name="submit"
value=" Add " />
<input class="form-element" type="submit" name="submit"
value=" Add and Edit " />
<input class="form-element" type="submit" name="submit"
value=" Add and Test " />
</div>
</td>
</tr>
</table>
</form>
<dtml-else>
<p class="form-text">
There are no SQL database connections. You need to add a Zope
SQL database connection before you can create a Zope SQL Method.
</p>
</dtml-if>
<dtml-var manage_page_footer>
<dtml-var manage_page_header>
<dtml-var manage_tabs>
<dtml-if SQLConnectionIDs>
<form action="manage_edit" method="POST">
<table cellpadding="2" cellspacing="0" width="100%" border="0">
<tr>
<td align="left" valign="top">
<div class="form-optional">
Title
</div>
</td>
<td align="left" valign="top">
<input type="text" name="title" size="40" value="<dtml-if
title>&dtml-title;</dtml-if>">
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Connection Id
</div>
</td>
<td align="left" valign="top">
<div class="form-element">
<select name="connection_id">
<dtml-in SQLConnectionIDs>
<option value="&dtml-sequence-item;"<dtml-if
expr="connection_id==_vars['sequence-item']">
selected</dtml-if>>
&dtml-sequence-key;</option>
</dtml-in>
</select>
<dtml-if connectionIsValid>
<dtml-if connected><dtml-else>
<p style="{color:red;}">
<strong>Warning:</strong>
The database connection used by this method is closed.
</p>
</dtml-if>
<dtml-else>
<p style="{color:red;}">
<strong>Warning:</strong>
The selected database connection (&dtml-connection_id;)
cannot be found!
</p>
</dtml-if>
</div>
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-optional">
Arguments
</div>
</td>
<td align="left" valign="top">
<textarea name="arguments" cols="40" rows="4">&dtml-arguments_src;</textarea>
</td>
</tr>
<tr>
<td align="left" valign="top" colspan="2">
<dtml-let cols="REQUEST.get('dtpref_cols', '100%')"
rows="REQUEST.get('dtpref_rows', '20')">
<dtml-if expr="cols[-1]=='%'">
<textarea name="template:text" wrap="off" style="width: &dtml-cols;;"
<dtml-else>
<textarea name="template:text" wrap="off" cols="&dtml-cols;"
</dtml-if>
rows="&dtml-rows;">&dtml-src;</textarea>
</dtml-let>
</td>
</tr>
<tr>
<td align="left" valign="top" colspan="2">
<div class="form-element">
<dtml-if wl_isLocked>
<em>Locked by WebDAV</em>
<dtml-else>
<input class="form-element" type="submit" name="SUBMIT"
value="Save Changes">
<input class="form-element" type="submit" name="SUBMIT"
value="Change and Test">
</dtml-if wl_isLocked>
<br />
<input class="form-element" type="submit" name="SUBMIT" value="Taller">
<input class="form-element" type="submit" name="SUBMIT" value="Shorter">
<input class="form-element" type="submit" name="SUBMIT" value="Wider">
<input class="form-element" type="submit" name="SUBMIT" value="Narrower">
</div>
</td>
</tr>
</table>
</form>
<dtml-else>
<p class="form-help">
There are no SQL database connections. You need to add a Zope SQL
database connection before you can edit a Zope SQL Method.
</p>
</dtml-if>
<dtml-var manage_page_footer>
Z SQL Method Object: Query relational databases with SQL
Description
Z SQL Methods allows you to access data in SQL databases.
Z SQL Methods define and call SQL statements on databases which
are defined by database adapter objects. To create a new Z SQL
Method you will need to setup a database adapter first. Every Z
SQL Method is directly linked to a database connection through a
database adapter.
**Note:** You must associate a Z SQL method with a database
adapter. If Zope cannot find a database adapter from your
location, you will not be able to create a Z SQL Method.
Z SQL Method - Add: Create new Z SQL Method
Description
This view allows you to create a new Z SQL Method.
Controls
'ID' -- Specifies the id of the sql method.
'Title' -- Specifies the title of the sql method.
'Connection id' -- Select list that specifies the database
connection on which the sql method operates.
**Note:** If you change the ID of your DB Connection object, all
the Z SQL Methods using this connection object will *NOT* be
updated. You have to update all Methods one by one.
'Arguments' -- Allows you to specify a list of arguments that the sql
method takes. The arguments should be separated by spaces or
a newline in the field. Furthermore, you can specify the
type of the argument as well as a default
value.
**Example:** 'title:string="No title!"', where 'title' is the
attribute, 'string' is the type of the attribute, and '"No
title!"' is the default value.
'Query template' --
Text area that specifies the SQL query that will execute when
this method is called. The query template can contain DTML
markup to tailor the SQL statement to the arguments. There are
three additional DTML tags called 'SQLVAR', 'SQLTEST', and
'SQLGROUP'. Please refer to their references get more details on
how to use them in your SQL statement.
**Note:** the SQL statement need not be a 'SELECT' query, it may
be any valid SQL statement including an INSERT or UPDATE.
**Important:** You can have several SQL statements in one ZSQL
Method.
Z SQL Method - Advanced: Manage advanced settings
Description
Manage the advanced settings such as caching and plugable
brains of a SQL method.
Controls
'ID' -- Indicates the id of the sql method.
'Title' -- Allows you to specify the title of the sql method.
'Connection Hook' -- Specifies the id of a method to be called when the database connection is made. This method will return a string containing the id of the database connection to use. If left blank there is no effect, the connection id specified will be used.
'Maximum number of rows retrieved' -- Specify an upper limit
for the number of rows a sql method can retrieve. Setting this
option correctly (based on your specific environment) can
increase the execution time of the method.
'Maximum number of results in the cache' -- Set the size of
the sql method cache.
'Maximum time (seconds) to cache results' -- The time in
seconds that results are cached. Setting to zero disables
caching.
Rows returned from this method can be turned into 'Result
objects' that are instances of a class you can specify below.
'Class name' -- Allows you to specify the name of the Python class.
'Class file' -- Allows you to specify the name of the Python
module that the class is defined in. The class file should
reside in the Zope 'Extensions' directory.
Z SQL Method - Edit: Change attributes of a SQL method
Description
This view allows you to edit the attributes of a SQL method.
Controls
'ID' -- Specifies the id of the sql method.
'Title' -- Specifies the title of the sql method.
'Connection id' -- Select list that specifies the database
connection on which the sql method operates.
**Note:** If you change the ID of your DB Connection object, all
the Z SQL Methods using this connection object will *NOT* be
updated. You have to update all Methods one by one.
'Arguments' -- Allows you to specify a list of arguments that the sql
method takes. The arguments should be separated by spaces or
a newline in the field. Furthermore, you can specify the
type of the argument as well as a default
value.
**Example:** 'title:string="No title!"', where 'title' is the
attribute, 'string' is the type of the attribute, and '"No
title!"' is the default value.
'Query template' --
Text area that specifies the SQL query that will execute when
this method is called. The query template can contain DTML
markup to tailor the SQL statement to the arguments. There are
three additional DTML tags called 'SQLVAR', 'SQLTEST', and
'SQLGROUP'. Please refer to their references get more details on
how to use them in your SQL statement.
**Note:** the SQL statement need not be a 'SELECT' query, it may
be any valid SQL statement including an INSERT or UPDATE.
**Important:** You can have several SQL statements in one ZSQL
Method.
Z SQL Method - Test: Test the validity of your SQL statement
Description
Test a SQL method.
Controls
'*[arument names]*' -- There will be one text field for each
argument specified for the SQL method. The text field will
contain the default value. **Important:** You must enter a
value matching the specified argument type, otherwise a "Type
Error" is returned.
'Submit Query' -- Causes the query to be executed and the
results are returned as an HTML table. If the query was had a
syntax error or the accessed table did not exist, a
database-specific error message is returned in form of an
exception.
If you have a SQL statement that does not return any data,
Zope will tell you that this SQL statement was not a
query and will not display any results.
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
def manage_addZSQLMethod(id, title,
connection_id, arguments, template):
"""
Add an SQL Method to an ObjectManager.
The 'connection_id' argument is the id of a database connection
that resides in the current folder or in a folder above the
current folder. The database should understand SQL.
The 'arguments' argument is a string containing an arguments
specification, as would be given in the SQL method cration form.
The 'template' argument is a string containing the source for the
SQL Template.
"""
class ZSQLMethod:
"""
ZSQLMethods abstract SQL code in Zope.
SQL Methods behave like methods of the folders they are
accessed in. In particular, they can be used from other
methods, like Documents, ExternalMethods, and even other SQL
Methods.
Database methods support the Searchable Object Interface.
Search interface wizards can be used to build user
interfaces to them. They can be used in joins and
unions. They provide meta-data about their input parameters
and result data.
For more information, see the searchable-object interface
specification.
Database methods support URL traversal to access and invoke
methods on individual record objects. For example, suppose you
had an 'employees' database method that took a single argument
'employee_id'. Suppose that employees had a 'service_record'
method (defined in a record class or acquired from a
folder). The 'service_record' method could be accessed with a
URL like::
employees/employee_id/1234/service_record
Search results are returned as Record objects. The schema of
a Record objects matches the schema of the table queried in
the search.
"""
__constructor__=manage_addZSQLMethod
def __call__(REQUEST=None, **kw):
"""
Call the ZSQLMethod.
The arguments to the method should be passed via keyword
arguments, or in a single mapping object. If no arguments are
given, and if the method was invoked through the Web, then the
method will try to acquire and use the Web REQUEST object as
the argument mapping.
The returned value is a sequence of record objects.
"""
def manage_edit(title,connection_id,arguments,template):
"""
Change database method properties.
The 'connection_id' argument is the id of a database
connection that resides in the current folder or in a folder
above the current folder. The database should understand SQL.
The 'arguments' argument is a string containing an arguments
specification, as would be given in the SQL method creation
form.
The 'template' argument is a string containing the source for
the SQL Template.
"""
import unittest
class SQLMethodTests(unittest.TestCase):
def _getTargetClass(self):
from Products.ZSQLMethods.SQL import SQL
return SQL
def _makeOne(self, *args, **kw):
return self._getTargetClass()(*args, **kw)
def test_class_conforms_to_IWriteLock(self):
from zope.interface.verify import verifyClass
from webdav.interfaces import IWriteLock
verifyClass(IWriteLock, self._getTargetClass())
def test_suite():
return unittest.TestSuite((
unittest.makeSuite(SQLMethodTests),
))
if __name__ == '__main__':
unittest.main(defaultTest='test_suite')
This diff is collapsed.
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
__doc__='''Generic Database Connection Support
$Id$'''
__version__='$Revision: 1.39 $'[11:-2]
from cgi import escape
from cStringIO import StringIO
from logging import getLogger
import string
import sys
from AccessControl.class_init import InitializeClass
from AccessControl.Permissions import view_management_screens
from AccessControl.Permissions import change_database_connections
from AccessControl.Permissions import test_database_connections
from AccessControl.Permissions import open_close_database_connection
from AccessControl.SecurityInfo import ClassSecurityInfo
from Acquisition import Implicit
from App.Dialogs import MessageDialog
from App.special_dtml import DTMLFile
from DateTime.DateTime import DateTime
from DocumentTemplate import HTML
from OFS.role import RoleManager
from OFS.SimpleItem import Item
from Persistence import Persistent
from zExceptions import BadRequest
from Aqueduct import custom_default_report
import RDB
from Results import Results
LOG = getLogger('ZRDB.Connection')
class Connection(Persistent,
RoleManager,
Item,
Implicit,
):
security = ClassSecurityInfo()
# Specify definitions for tabs:
manage_options=(
(
{'label':'Status', 'action':'manage_main'},
{'label':'Properties', 'action':'manage_properties'},
{'label':'Test', 'action':'manage_testForm'},
)
+ RoleManager.manage_options
+ Item.manage_options
)
_v_connected=''
connection_string=''
def __init__(self, id, title, connection_string, check=None):
self.id=str(id)
self.edit(title, connection_string, check)
def __setstate__(self, state):
Persistent.__setstate__(self, state)
if self.connection_string:
try: self.connect(self.connection_string)
except:
LOG.error('Error connecting to relational database.',
exc_info=True)
def title_and_id(self):
s=Connection.inheritedAttribute('title_and_id')(self)
if hasattr(self, '_v_connected') and self._v_connected:
s="%s, which is connected" % s
else:
s="%s, which is <font color=red> not connected</font>" % s
return s
def title_or_id(self):
s=Connection.inheritedAttribute('title_or_id')(self)
if hasattr(self, '_v_connected') and self._v_connected:
s="%s (connected)" % s
else:
s="%s (<font color=red> not connected</font>)" % s
return s
def connected(self): return self._v_connected
def edit(self, title, connection_string, check=1):
self.title=title
self.connection_string=connection_string
if check: self.connect(connection_string)
manage_properties=DTMLFile('dtml/connectionEdit', globals())
security.declareProtected(change_database_connections, 'manage_edit')
def manage_edit(self, title, connection_string, check=None, REQUEST=None):
"""Change connection
"""
self.edit(title, connection_string, check)
if REQUEST is not None:
return MessageDialog(
title='Edited',
message='<strong>%s</strong> has been edited.' % escape(self.id),
action ='./manage_main',
)
security.declareProtected(test_database_connections, 'manage_testForm')
manage_testForm=DTMLFile('dtml/connectionTestForm', globals())
security.declareProtected(test_database_connections, 'manage_test')
def manage_test(self, query, REQUEST=None):
"Executes the SQL in parameter 'query' and returns results"
dbc=self() #get our connection
res=dbc.query(query)
if type(res) is type(''):
f=StringIO()
f.write(res)
f.seek(0)
result=RDB.File(f)
else:
result=Results(res)
if REQUEST is None:
return result #return unadulterated result objects
if result._searchable_result_columns():
r=custom_default_report(self.id, result)
else:
r='This statement returned no results.'
report = HTML(
'<html><body bgcolor="#ffffff" link="#000099" vlink="#555555">\n'
'<dtml-var name="manage_tabs">\n<hr>\n%s\n\n'
'<hr><h4>SQL Used:</strong><br>\n<pre>\n%s\n</pre>\n<hr>\n'
'</body></html>'
% (r, query))
report = apply(report,(self,REQUEST),{self.id:result})
return report
security.declareProtected(view_management_screens, 'manage_main')
manage_main=DTMLFile('dtml/connectionStatus', globals())
security.declareProtected(open_close_database_connection,
'manage_close_connection')
def manage_close_connection(self, REQUEST=None):
" "
try:
if hasattr(self,'_v_database_connection'):
self._v_database_connection.close()
except:
LOG.error('Error closing relational database connection.',
exc_info=True)
self._v_connected=''
if REQUEST is not None:
return self.manage_main(self, REQUEST)
security.declareProtected(open_close_database_connection,
'manage_open_connection')
def manage_open_connection(self, REQUEST=None):
" "
self.connect(self.connection_string)
return self.manage_main(self, REQUEST)
def __call__(self, v=None):
try: return self._v_database_connection
except AttributeError:
s=self.connection_string
if s:
self.connect(s)
return self._v_database_connection
raise BadRequest,(
'''The database connection is not connected''')
def connect(self,s):
self.manage_close_connection()
DB=self.factory()
try:
try:
self._v_database_connection=DB(s)
except:
t, v, tb = sys.exc_info()
raise BadRequest, (
'<strong>Error connecting to DB.</strong><br>\n'
'<!--\n%s\n%s\n-->\n'
% (t,v)), tb
finally: tb=None
self._v_connected=DateTime()
return self
def sql_quote__(self, v):
if string.find(v,"\'") >= 0:
v = string.join(string.split(v,"\'"),"''")
return "'%s'" % v
InitializeClass(Connection)
This diff is collapsed.
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
__doc__='''Class for reading RDB files
$Id$'''
__version__='$Revision: 1.33 $'[11:-2]
from string import split, strip, lower, upper, atof, atoi, atol, find, join,find
import DateTime,re
from Missing import MV
from array import array
from Record import Record
from Acquisition import Implicit
import ExtensionClass
def parse_text(s):
if find(s,'\\') < 0 and (find(s,'\\t') < 0 and find(s,'\\n') < 0): return s
r=[]
for x in split(s,'\\\\'):
x=join(split(x,'\\n'),'\n')
r.append(join(split(x,'\\t'),'\t'))
return join(r,'\\')
Parsers={'n': atof,
'i': atoi,
'l': atol,
'd': DateTime.DateTime,
't': parse_text,
}
class SQLAlias(ExtensionClass.Base):
def __init__(self, name): self._n=name
def __of__(self, parent): return getattr(parent, self._n)
class NoBrains: pass
class DatabaseResults:
"""Class for reading RDB files
"""
_index=None
# We need to allow access to not-explicitly-protected
# individual record objects contained in the result.
__allow_access_to_unprotected_subobjects__=1
def __init__(self,file,brains=NoBrains, parent=None, zbrains=None):
self._file=file
readline=file.readline
line=readline()
self._parent=parent
if zbrains is None: zbrains=NoBrains
while line and line.find('#') != -1 : line=readline()
line=line[:-1]
if line and line[-1:] in '\r\n': line=line[:-1]
self._names=names=split(line,'\t')
if not names: raise ValueError, 'No column names'
aliases=[]
self._schema=schema={}
i=0
for name in names:
name=strip(name)
if not name:
raise ValueError, 'Empty column name, %s' % name
if schema.has_key(name):
raise ValueError, 'Duplicate column name, %s' % name
schema[name]=i
n=lower(name)
if n != name: aliases.append((n, SQLAlias(name)))
n=upper(name)
if n != name: aliases.append((n, SQLAlias(name)))
i=i+1
self._nv=nv=len(names)
line=readline()
line=line[:-1]
if line[-1:] in '\r\n': line=line[:-1]
self._defs=defs=split(line,'\t')
if not defs: raise ValueError, 'No column definitions'
if len(defs) != nv:
raise ValueError, (
"""The number of column names and the number of column
definitions are different.""")
i=0
self._parsers=parsers=[]
defre=re.compile(r'([0-9]*)([a-zA-Z])?')
self._data_dictionary=dd={}
self.__items__=items=[]
for _def in defs:
_def=strip(_def)
if not _def:
raise ValueError, ('Empty column definition for %s' % names[i])
mo = defre.match(_def)
if mo is None:
raise ValueError, (
'Invalid column definition for, %s, for %s'
% _def, names[i])
type = mo.group(2).lower()
width = mo.group(1)
if width: width=atoi(width)
else: width=8
try: parser=Parsers[type]
except: parser=str
name=names[i]
d={'name': name, 'type': type, 'width': width, 'parser': parser}
items.append(d)
dd[name]=d
parsers.append((i,parser))
i=i+1
# Create a record class to hold the records.
names=tuple(names)
class r(Record, Implicit, brains, zbrains):
'Result record class'
r.__record_schema__=schema
for k in filter(lambda k: k[:2]=='__', Record.__dict__.keys()):
setattr(r,k,getattr(Record,k))
# Add SQL Aliases
for k, v in aliases:
if not hasattr(r,k):
setattr(r, k, v)
if hasattr(brains, '__init__'):
binit=brains.__init__
if hasattr(binit,'im_func'): binit=binit.im_func
def __init__(self, data, parent, binit=binit):
Record.__init__(self,data)
binit(self.__of__(parent))
setattr(r, '__init__', __init__)
self._class=r
# OK, we've read meta data, now get line indexes
p=file.tell()
save=self._lines=array('i')
save=save.append
l=readline()
while l:
save(p)
p=p+len(l)
l=readline()
def _searchable_result_columns(self): return self.__items__
def names(self): return self._names
def data_dictionary(self): return self._data_dictionary
def __len__(self): return len(self._lines)
def __getitem__(self,index):
if index==self._index: return self._row
file=self._file
file.seek(self._lines[index])
line=file.readline()
line=line[:-1]
if line and line[-1:] in '\r\n': line=line[:-1]
fields=split(line,'\t')
l=len(fields)
nv=self._nv
if l != nv:
if l < nv:
fields=fields+['']*(nv-l)
else:
raise ValueError, (
"""The number of items in record %s is invalid
<pre>%s\n%s\n%s\n%s</pre>
"""
% (index, ('='*40), line, ('='*40), fields))
for i, parser in self._parsers:
try: v=parser(fields[i])
except:
if fields[i]:
raise ValueError, (
"""Invalid value, %s, for %s in record %s"""
% (fields[i], self._names[i], index))
else: v=MV
fields[i]=v
parent=self._parent
fields=self._class(fields, parent)
self._index=index
self._row=fields
if parent is None: return fields
return fields.__of__(parent)
File=DatabaseResults
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
import ExtensionClass
from Acquisition import Implicit
from Record import Record
class SQLAlias(ExtensionClass.Base):
def __init__(self, name): self._n=name
def __of__(self, parent): return getattr(parent, self._n)
class NoBrains: pass
class Results:
"""Class for providing a nice interface to DBI result data
"""
_index=None
# We need to allow access to not-explicitly-protected
# individual record objects contained in the result.
__allow_access_to_unprotected_subobjects__=1
def __init__(self,(items,data),brains=NoBrains, parent=None,
zbrains=None):
self._data=data
self.__items__=items
self._parent=parent
self._names=names=[]
self._schema=schema={}
self._data_dictionary=dd={}
aliases=[]
if zbrains is None: zbrains=NoBrains
i=0
for item in items:
name=item['name']
name=name.strip()
if not name:
raise ValueError, 'Empty column name, %s' % name
if schema.has_key(name):
raise ValueError, 'Duplicate column name, %s' % name
schema[name]=i
n=name.lower()
if n != name: aliases.append((n, SQLAlias(name)))
n=name.upper()
if n != name: aliases.append((n, SQLAlias(name)))
dd[name]=item
names.append(name)
i=i+1
self._nv=nv=len(names)
# Create a record class to hold the records.
names=tuple(names)
class r(Record, Implicit, brains, zbrains):
'Result record class'
r.__record_schema__=schema
for k in Record.__dict__.keys():
if k[:2]=='__':
setattr(r,k,getattr(Record,k))
# Add SQL Aliases
for k, v in aliases:
if not hasattr(r, k):
setattr(r, k, v)
if hasattr(brains, '__init__'):
binit=brains.__init__
if hasattr(binit,'im_func'): binit=binit.im_func
def __init__(self, data, parent, binit=binit):
Record.__init__(self,data)
if parent is not None: self=self.__of__(parent)
binit(self)
setattr(r, '__init__', __init__)
self._class=r
# OK, we've read meta data, now get line indexes
def _searchable_result_columns(self): return self.__items__
def names(self): return self._names
def data_dictionary(self): return self._data_dictionary
def __len__(self): return len(self._data)
def __getitem__(self,index):
if index==self._index: return self._row
parent=self._parent
fields=self._class(self._data[index], parent)
if parent is not None: fields=fields.__of__(parent)
self._index=index
self._row=fields
return fields
def tuples(self):
return map(tuple, self)
def dictionaries(self):
r=[]
a=r.append
names=self.names()
for row in self:
d={}
for n in names: d[n]=row[n]
a(d)
return r
def asRDB(self): # Waaaaa
r=[]
append=r.append
strings=[]
nstrings=[]
items=self.__items__
indexes=range(len(items))
for i in indexes:
item=items[i]
t=item['type'].lower()
if t=='s' or t=='t':
t=='t'
strings.append(i)
else: nstrings.append(i)
if item.has_key('width'): append('%s%s' % (item['width'], t))
else: r.append(t)
r=['\t'.join(self._names), '\t'.join(r)]
append=r.append
row=['']*len(items)
tostr=str
for d in self._data:
for i in strings:
v=tostr(d[i])
if v:
if v.find('\\') > 0: v='\\\\'.join(v.split('\\'))
if v.find('\t') > 0: v='\\t'.join(v.split('\t'))
if v.find('\n') > 0: v='\\n'.join(v.split('\n'))
row[i]=v
for i in nstrings:
row[i]=tostr(d[i])
append('\t'.join(row))
append('')
return '\n'.join(r)
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
__doc__='''Search Interface Wizard
$Id$'''
__version__='$Revision: 1.22 $'[11:-2]
from string import join
from cgi import escape
from App.special_dtml import DTMLFile
from Aqueduct import custom_default_report
from Aqueduct import custom_default_zpt_report
from Aqueduct import nicify
from Aqueduct import Args
from AccessControl import getSecurityManager
addForm=DTMLFile('dtml/searchAdd', globals())
def manage_addZSearch(self, report_id, report_title, report_style,
input_id, input_title, object_type, queries=[],
REQUEST=None):
'add a report'
if not queries: raise ValueError, (
'No <em>searchable objects</em> were selected')
if not report_id: raise ValueError, (
'No <em>report id</em> were specified')
if input_title and not input_id: raise ValueError, (
'No <em>input id</em> were specified')
qs=map(lambda q, self=self: _getquery(self, q), queries)
arguments={}
keys=[]
checkPermission=getSecurityManager().checkPermission
for q in qs:
url=q.absolute_url()
if input_id:
for name, arg in q._searchable_arguments().items():
if len(qs) > 1: key="%s/%s" % (id,name)
else: key=name
arguments[key]=arg
keys.append(key)
if q._searchable_result_columns() is None:
raise ValueError,(
"""The input searchable object, <em>%s</em>,
has not been tested. Until it has been tested,
it\'s output schema is unknown, and a report
cannot be generated. Before creating a report
from this query, you must try out the query. To
try out the query, <a href="%s">click here</a>.
""" % (escape(q.title_and_id()), escape(url, 1)))
if object_type == 'dtml_methods':
if not checkPermission('Add DTML Methods', self):
raise Unauthorized, (
'You are not authorized to add DTML Methods.'
)
if input_id:
arguments=Args(arguments, keys)
self.manage_addDocument(
input_id,input_title,
default_input_form(arguments, report_id))
self.manage_addDocument(
report_id,report_title,
('<html><head><title><dtml-var title_or_id></title>'
'</head><body bgcolor="#FFFFFF">\n%s\n'
'</body></html>' %
join(map(lambda q, report_style=report_style:
custom_default_report(q.id, q, no_table=report_style), qs),
'\n<hr>\n')))
if REQUEST: return self.manage_main(self,REQUEST)
elif object_type == 'page_templates':
if not checkPermission('Add Page Templates', self):
raise Unauthorized, (
'You are not authorized to add Page Templates.'
)
if input_id:
arguments = Args(arguments, keys)
self.manage_addProduct['PageTemplates'].manage_addPageTemplate(
input_id, input_title,
default_input_zpt_form(arguments, report_id))
self.manage_addProduct['PageTemplates'].manage_addPageTemplate(
report_id,report_title,
('<html><body>\n%s\n'
'</body></html>' %
join(map(lambda q, report_style=report_style:
custom_default_zpt_report(q.id, q, no_table=report_style), qs),
'\n<hr>\n')))
if REQUEST: return self.manage_main(self,REQUEST)
def ZQueryIds(self):
# Note that report server configurations will expend on this
t=[]
ids={}
old=ids.has_key
o=self
n=0
while 1:
# Look for queries
try: map=o.objectMap()
except AttributeError: map=()
for i in map:
try:
id=i['id']
if (not old(id) and
hasattr(getattr(o,id),'_searchable_arguments')
):
t.append(i['id'])
ids[id]=1
except: pass
# Now extend search to parent
try: o=o.aq_parent
except: return t
if n > 100: return t # Seat belt
n=n+1
def _getquery(self,id):
o=self
i=0
while 1:
__traceback_info__=o
q=getattr(o,id)
try:
if hasattr(q,'_searchable_arguments'):
try: q=q.__of__(self.aq_parent)
except: pass
return q
except: pass
if i > 100: raise AttributeError, id
i=i+1
o=o.aq_parent
def default_input_form(arguments,action='query',
tabs=''):
if arguments:
items=arguments.items()
return (
"%s\n%s%s" % (
'<html><head><title><dtml-var title_or_id></title>'
'</head><body bgcolor="#FFFFFF">\n%s\n'
'<form action="%s" method="get">\n'
'<h2><dtml-var document_title></h2>\n'
'Enter query parameters:<br>'
'<table>\n'
% (tabs,action),
join(
map(
lambda a:
('<tr><th>%s</th>\n'
' <td><input name="%s"\n'
' size="30" value="%s">'
'</td></tr>'
% (nicify(a[0]),
(
a[1].has_key('type') and
("%s:%s" % (a[0],a[1]['type'])) or
a[0]
),
a[1].has_key('default') and a[1]['default'] or ''
))
, items
),
'\n'),
'\n<tr><td colspan=2 align=center>\n'
'<input type="SUBMIT" name="SUBMIT" value="Submit Query">\n'
'</td></tr>\n</table>\n</form>\n'
'</body></html>\n'
)
)
else:
return (
'<html><head><title><dtml-var title_or_id></title>'
'</head><body bgcolor="#FFFFFF">\n%s\n'
'<form action="%s" method="get">\n'
'<h2><dtml-var document_title></h2>\n'
'This query requires no input.<p>\n'
'<input type="SUBMIT" name="SUBMIT" value="Submit Query">\n'
'</form>\n'
'</body></html>\n'
% (tabs, action)
)
def default_input_zpt_form(arguments,action='query',
tabs=''):
if arguments:
items=arguments.items()
return (
"%s\n%s%s" % (
'<html><body>\n%s\n'
'<form action="%s" method="get">\n'
'<h2 tal:content="template/title_or_id">Title</h2>\n'
'Enter query parameters:<br>'
'<table>\n'
% (tabs,action),
join(
map(
lambda a:
('<tr><th>%s</th>\n'
' <td><input name="%s"\n'
' size="30" value="%s">'
'</td></tr>'
% (nicify(a[0]),
(
a[1].has_key('type') and
("%s:%s" % (a[0],a[1]['type'])) or
a[0]
),
a[1].has_key('default') and a[1]['default'] or ''
))
, items
),
'\n'),
'\n<tr><td colspan=2 align=center>\n'
'<input type="SUBMIT" name="SUBMIT" value="Submit Query">\n'
'</td></tr>\n</table>\n</form>\n'
'</body></html>\n'
)
)
else:
return (
'<html><body>\n%s\n'
'<form action="%s" method="get">\n'
'<h2 tal:content="template/title_or_id">Title</h2>\n'
'<p>This query requires no input.</p>\n'
'<input type="SUBMIT" name="SUBMIT" value="Submit Query">\n'
'</form>\n'
'</body></html>\n'
% (tabs, action)
)
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
import thread
import TM
from TM import Surrogate
import transaction
thunk_lock = thread.allocate_lock()
class THUNKED_TM(TM.TM):
"""A big heavy hammer for handling non-thread safe DAs
"""
def _register(self):
if not self._registered:
thunk_lock.acquire()
try:
transaction.get().register(Surrogate(self))
self._begin()
except:
thunk_lock.release()
raise
else:
self._registered=1
def tpc_finish(self, *ignored):
if self._registered:
try:
self._finish()
finally:
thunk_lock.release()
self._registered=0
def abort(self, *ignored):
if self._registered:
try:
self._abort()
finally:
thunk_lock.release()
self._registered=0
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
"""Provide support for linking an external transaction manager with Zope's
"""
import transaction
class TM:
"""Mix-in class that provides transaction management support
A sub class should call self._register() whenever it performs any
transaction-dependent operations (e.g. sql statements).
The sub class will need to override _finish, to finalize work,
_abort, to roll-back work, and perhaps _begin, if any work is
needed at the start of a transaction.
A subclass that uses locking during transaction commit must
define a sortKey() method.
"""
_registered=None
def _begin(self): pass
def _register(self):
if not self._registered:
try:
transaction.get().register(Surrogate(self))
self._begin()
self._registered = 1
self._finalize = 0
except: pass
def tpc_begin(self, *ignored): pass
commit=tpc_begin
def _finish(self):
self.db.commit()
def _abort(self):
self.db.rollback()
def tpc_vote(self, *ignored):
self._finalize = 1
def tpc_finish(self, *ignored):
if self._finalize:
try: self._finish()
finally: self._registered=0
def abort(self, *ignored):
try: self._abort()
finally: self._registered=0
tpc_abort = abort
# Most DA's talking to RDBMS systems do not care about commit order, so
# return the constant 1
_sort_key = 1
def sortKey(self, *ignored):
""" The sortKey method is used by the transaction subsystem to have a
known commit order for lock acquisition.
"""
return self._sort_key
def setSortKey(self, sort_key):
self._sort_key = sort_key
class Surrogate:
def __init__(self, db):
self._p_jar=db
self.__inform_commit__=db.tpc_finish
self.__inform_abort__=db.tpc_abort
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
__doc__='''short description
$Id$'''
# Copyright
#
# Copyright 1997 Digital Creations, Inc, 910 Princess Anne
# Street, Suite 300, Fredericksburg, Virginia 22401 U.S.A. All
# rights reserved.
#
__version__='$Revision: 1.11 $'[11:-2]
import string, sys
from string import strip, split, find, join
import transaction
failures=0
calls=0
nonselect_desc=[
('Query', 'STRING', 62, 62, 0, 0, 1),
('Status', 'STRING', 12, 12, 0, 0, 1),
('Calls', 'STRING', 12, 12, 0, 0, 1),
]
class QueryError(Exception):
pass
class DB:
_p_oid=_p_changed=_registered=None
defs={'STRING':'s', 'NUMBER':'n', 'DATE':'d'}
def Database_Connection(self, string):
# Create a dbi-compatible database connection
raise NotImplemetedError, (
'attempt to create a database connection for an abstract dbi')
Database_Error='Should be overriden by subclass'
def __init__(self,connection):
self.connection=connection
db=self.db=self.Database_Connection(connection)
self.cursor=db.cursor()
def str(self,v, StringType=type('')):
if v is None: return ''
r=str(v)
if r[-1:]=='L' and type(v) is not StringType: r=r[:-1]
return r
def __inform_commit__(self, *ignored):
self._registered=None
self.db.commit()
def __inform_abort__(self, *ignored):
self._registered=None
self.db.rollback()
def register(self):
if self._registered: return
transaction.get().register(self)
self._registered=1
def query(self,query_string, max_rows=9999999):
global failures, calls
calls=calls+1
try:
c=self.cursor
self.register()
queries=filter(None, map(strip,split(query_string, '\0')))
if not queries: raise QueryError, 'empty query'
if len(queries) > 1:
result=[]
for qs in queries:
r=c.execute(qs)
if r is None: raise QueryError, (
'select in multiple sql-statement query'
)
result.append((qs, str(`r`), calls))
desc=nonselect_desc
else:
query_string=queries[0]
r=c.execute(query_string)
if r is None:
result=c.fetchmany(max_rows)
desc=c.description
else:
result=((query_string, str(`r`), calls),)
desc=nonselect_desc
failures=0
c.close()
except self.Database_Error, mess:
c.close()
self.db.rollback()
failures=failures+1
if ((find(mess,": invalid") < 0 and
find(mess,"PARSE") < 0) or
# DBI IS stupid
find(mess,
"Error while trying to retrieve text for error") > 0
or
# If we have a large number of consecutive failures,
# our connection is probably dead.
failures > 100
):
# Hm. maybe the db is hosed. Let's try once to restart it.
failures=0
c.close()
self.db.close()
db=self.db=self.Database_Connection(self.connection)
self.cursor=db.cursor()
c=self.cursor
c.execute(query_string)
result=c.fetchall()
desc=c.description
else:
raise sys.exc_info()
if result:
result=join(
map(
lambda row, self=self:
join(map(self.str,row),'\t'),
result),
'\n')+'\n'
else:
result=''
return (
"%s\n%s\n%s" % (
join(map(lambda d: d[0],desc), '\t'),
join(
map(
lambda d, defs=self.defs: "%d%s" % (d[2],defs[d[1]]),
desc),
'\t'),
result,
)
)
<dtml-var manage_page_header>
<dtml-var manage_tabs>
<form action="manage_advanced" method="post">
<table>
<table cellspacing="0" cellpadding="2" border="0">
<tr>
<td align="left" valign="top">
<div class="form-optional">
Connection Hook
</div>
</td>
<td align="LEFT" valign="TOP">
<input type="TEXT" name="connection_hook" size="40"
value="<dtml-var connection_hook null="" missing="" html_quote>">
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Maximum rows to retrieve
</div>
</td>
<td align="left" valign="top">
<input name="max_rows:int" size="10" value="&dtml-max_rows_;">
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Maximum results to cache
</div>
</td>
<td align="left" valign="top">
<input name="max_cache:int" size="10" value="&dtml-max_cache_;">
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Maximum time (sec) to cache
</div>
</td>
<td align="left" valign="top">
<input name="cache_time:int" size="10" value="&dtml-cache_time_;">
</td>
</tr>
<dtml-if da_has_single_argument>
<tr>
<td align="left" valign="top">
<div class="form-label">
Allow "simple" direct traversal
</div>
</td>
<td align="left" valign="top">
<input name="direct" type="checkbox" <dtml-if
allow_simple_one_argument_traversal>checked</dtml-if> />
</td>
</tr>
</dtml-if>
<tr>
<td align="left" valign="top" colspan="2">
<div class="form-text">
<br />
You may specify a <strong>class</strong> for the data records. This
class must be defined in a file that resides in the <code>Extensions</code>
directory of this Zope installation.
</div>
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Class Name
</div>
</td>
<td align="left" valign="top">
<input name="class_name" size="30" value="&dtml-class_name_;">
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Class File
</div>
</td>
<td align="left" valign="top">
<input name="class_file" size="30" value="&dtml-class_file_;">
</td>
</tr>
<tr>
<td align="left" valign="top">
</td>
<td align="left" valign="top">
<div class="form-element">
<input class="form-element" type="submit" name="submit"
value="Save Changes">
</div>
</td>
</tr>
</table>
</form>
<dtml-var manage_page_footer>
<dtml-var manage_page_header>
<dtml-var "manage_form_title(this(), _,
form_title='Add %s Connection' % database_type
)">
<form action="manage_addZ&dtml-database_type;Connection"
method="POST">
<table cellspacing="2">
<tr>
<td align="left" valign="top">
<div class="form-label">
Id
</div>
</td>
<td align="LEFT" valign="TOP">
<input type="TEXT" name="id" size="40"
value="&dtml-default_id;">
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-optional">
Title
</div>
</td>
<td align="LEFT" valign="TOP">
<input type="TEXT" name="title" size="40"
value="&dtml-default_title;">
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Connection String
</div>
</td>
<td align="LEFT" valign="TOP">
<input type="TEXT" name="connection_string" size="40">
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Connect Immediately?
</div>
</td>
<td align="LEFT" valign="TOP">
<input name="check" type="CHECKBOX" value="YES" CHECKED>
</td>
</tr>
<tr>
<td align="left" valign="top">
</td>
<td align="left" valign="top">
<div class="form-element">
<br />
<input class="form-element" type="submit" name="submit" value="Add">
</div>
</td>
</tr>
</table>
</form>
<dtml-var manage_page_footer>
<dtml-var manage_page_header>
<dtml-var manage_tabs>
<form action="manage_edit" method="POST">
<table cellspacing="2">
<tr>
<td align="left" valign="top">
<div class="form-label">
Id
</div>
</td>
<td align="LEFT" valign="TOP">&dtml-id;</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-optional">
Title
</div>
</td>
<td align="LEFT" valign="TOP">
<input type="TEXT" name="title" size="40"
value="&dtml-title;">
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Connection String
</div>
</td>
<td align="LEFT" valign="TOP">
<input type="TEXT" name="connection_string" size="40"
value="&dtml-connection_string;">
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Connect Immediately
</div>
</td>
<td align="LEFT" valign="TOP">
<input name="check" type="CHECKBOX" value="YES" CHECKED>
</td>
</tr>
<tr>
<td align="left" valign="top">
</td>
<td align="left" valign="top">
<div class="form-element">
<br />
<input class="form-element" type="submit" name="submit"
value="Save Changes">
</div>
</td>
</tr>
</table>
</form>
<dtml-var manage_page_footer>
<dtml-var manage_page_header>
<dtml-var manage_tabs>
<p class="form-text">
The database connection is <dtml-if
connected>open<dtml-else>closed</dtml-if>.
</p>
<p>
<div class="form-element">
<dtml-if connected>
<form action="manage_close_connection" method="get">
<input class="form-element" type="submit" name="submit"
value="Close Connection">
</form>
<dtml-else>
<form action="manage_open_connection" method="get">
<input class="form-element" type="submit" name="submit"
value="Open Connection">
</form>
</dtml-if>
</div>
</p>
<dtml-var manage_page_footer>
<dtml-var manage_page_header>
<dtml-var manage_tabs>
<dtml-if connected>
<p class="form-help">
You can write a test query and run it with this database connection
using the form below. Click <em>submit query</em> to run the query.
</p>
<form action="manage_test" method="post">
<table cellspacing="0" cellpadding="2" border="0">
<tr>
<td align="left" valign="top">
<div style="width: 100%;">
<textarea style="width: 100%;" rows="10" cols="60" name="query:text"
wrap="off" accesskey="e" tabindex="1"></textarea>
</div>
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-element">
<input class="form-element" type="submit" name="submit"
value="Submit Query" tabindex="2">
</div>
</td>
</tr>
</table>
</form>
<dtml-else>
<p class="form-help">
This database connection is currently closed. You must click on the
<em>status</em> tab and open the connection before you can test it.
</p>
</dtml-if>
<dtml-var manage_page_footer>
<dtml-in %(id)s size=20 start=query_start>
<dtml-if sequence-start>
<dtml-if previous-sequence>
<a href="&dtml-URL;?query_start=&dtml-previous-sequence-start-number;&query=<dtml-var query url_quote missing=''>">
(Previous <dtml-var previous-sequence-size> results)
</a>
</dtml-if previous-sequence>
%(else no_table)[<table border>
%(heading)s
%(else)]
</dtml-if sequence-start>
%(row)s
<dtml-if sequence-end>
%(else no_table)[</table>%(else)]
<dtml-if next-sequence>
<a href="&dtml-URL;?query_start=&dtml-next-sequence-start-number;&query=<dtml-var query url_quote missing=''>">
(Next <dtml-var next-sequence-size> results)
</a>
</dtml-if next-sequence>
</dtml-if sequence-end>
<dtml-else>
There was no data matching this &dtml-title_or_id; query.
</dtml-in>
<html>
<body tal:define="results here/%(id)s;
start request/start|python:0;
batch python:modules['ZTUtils'].Batch(results,
size=20,
start=start);
previous python:batch.previous;
next python:batch.next">
<p>
<a tal:condition="previous"
tal:attributes="href string:${request/URL0}?start:int=${previous/first}"
href="previous_url">previous <span tal:replace="previous/length">20</span> results</a>
<a tal:condition="next"
tal:attributes="href string:${request/URL0}?start:int=${next/first}"
href="next_url">next <span tal:replace="next/length">20</span> results</a>
</p>
%(else no_table)[<table border>
%(heading)s
%(else)]
<tal:x repeat="result batch" >
%(row)s
</tal:x>
%(else no_table)[</table>%(else)]
<p>
<a tal:condition="previous"
tal:attributes="href string:${request/URL0}?start:int=${previous/first}"
href="previous_url">previous <span tal:replace="previous/length">20</span> results</a>
<a tal:condition="next"
tal:attributes="href string:${request/URL0}?start:int=${next/first}"
href="next_url">next <span tal:replace="next/length">20</span> results</a>
</p>
</body>
</html>
<dtml-var manage_page_header>
<dtml-var "manage_form_title(this(), _,
form_title='Add Search Interface',
help_product='OFSP',
help_topic='ZSearch-Interface_Add.stx'
)">
<form action="manage_addZSearch" method="POST">
<p class="form-help">
A Search Interface allows you to search Zope databases.
The Search Interface will create a
search-input form and a report for displaying the search results.
</p>
<p class="form-help">
In the form below, <em>seachable objects</em> are the objects
(usually SQL Methods) to be searched. <em>report id</em> and
<em>search input id</em> are the ids of the
report and search form objects that will be created.
<em>report style</em> indicates the type of report to generate.
</p>
<table>
<tr>
<td align="left" valign="top">
<div class="form-label">
Select one<br>or more<br>searchable <br>objects
</div>
</td>
<td align="left" valign="top">
<div class="form-element">
<select name="queries:list" size="4" multiple>
<dtml-in ZQueryIds>
<option>&dtml-sequence-item;</option>
</dtml-in>
</select>
</div>
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Report Id
</div>
</td>
<td align="left" valign="top">
<input type="text" name="report_id" size="40" value="" />
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-optional">
Report Title
</div>
</td>
<td align="left" valign="top">
<input type="text" name="report_title" size="40" value="" />
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Report Style
</div>
</td>
<td align="left" valign="top">
<div class="form-element">
<select name='report_style:int'>
<option value="0">Tabular</option>
<option value="1">Records</option>
</select>
</div>
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-optional">
Search Input Id
</div>
</td>
<td align="left" valign="top">
<input type="text" name="input_id" size="40" value="" />
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-optional">
Search Input Title
</div>
</td>
<td align="left" valign="top">
<input type="text" name="input_title" size="40" value="" />
</td>
</tr
<tr>
<td align="left" valign="top">
</td>
<td align="left" valign="top">
<div class="form-element">
<input class="form-element" type="radio" name="object_type"
value="dtml_methods">Generate DTML Methods
</div>
</td>
</tr>
<tr>
<td align="left" valign="top">
</td>
<td align="left" valign="top">
<div class="form-element">
<input class="form-element" type="radio" name="object_type"
value="page_templates">Generate Page Templates
</div>
</td>
</tr>
<tr>
<td align="left" valign="top">
</td>
<td align="left" valign="top">
<div class="form-element">
<input class="form-element" type="submit" name="submit"
value="Add">
</div>
</td>
</tr>
</table>
</form>
<dtml-var manage_page_footer>
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
"""Inserting optional tests with 'sqlgroup'
It is sometimes useful to make inputs to an SQL statement
optinal. Doing so can be difficult, because not only must the
test be inserted conditionally, but SQL boolean operators may or
may not need to be inserted depending on whether other, possibly
optional, comparisons have been done. The 'sqlgroup' tag
automates the conditional insertion of boolean operators.
The 'sqlgroup' tag is a block tag. It can
have any number of 'and' and 'or' continuation tags.
The 'sqlgroup' tag has an optional attribure, 'required' to
specify groups that must include at least one test. This is
useful when you want to make sure that a query is qualified, but
want to be very flexible about how it is qualified.
Suppose we want to find people with a given first or nick name,
city or minimum and maximum age. Suppose we want all inputs to be
optional, but want to require *some* input. We can
use DTML source like the following::
<dtml-sqlgroup required>
<dtml-sqlgroup>
<dtml-sqltest name column=nick_name type=nb multiple optional>
<dtml-or>
<dtml-sqltest name column=first_name type=nb multiple optional>
</dtml-sqlgroup>
<dtml-and>
<dtml-sqltest home_town type=nb optional>
<dtml-and>
<dtml-if minimum_age>
age >= <dtml-sqlvar minimum_age type=int>
</dtml-if>
<dtml-and>
<dtml-if maximum_age>
age <= <dtml-sqlvar maximum_age type=int>
</dtml-if>
</dtml-sqlgroup>
This example illustrates how groups can be nested to control
boolean evaluation order. It also illustrates that the grouping
facility can also be used with other DTML tags like 'if' tags.
The 'sqlgroup' tag checks to see if text to be inserted contains
other than whitespace characters. If it does, then it is inserted
with the appropriate boolean operator, as indicated by use of an
'and' or 'or' tag, otherwise, no text is inserted.
$Id$
"""
############################################################################
# Copyright
#
# Copyright 1996 Digital Creations, L.C., 910 Princess Anne
# Street, Suite 300, Fredericksburg, Virginia 22401 U.S.A. All
# rights reserved.
#
############################################################################
__rcs_id__='$Id$'
__version__='$Revision: 1.10 $'[11:-2]
from DocumentTemplate.DT_Util import parse_params
str = __builtins__['str']
from string import strip, join
import sys
_TNAME_MAPPING = {'comma': ','}
class SQLGroup:
blockContinuations = 'and', 'or', 'comma'
name = 'sqlgroup'
required = None
where = None
set = None
noparens = None
def __init__(self, blocks):
self.blocks = blocks
tname, args, section = blocks[0]
self.__name__ = "%s %s" % (tname, args)
args = parse_params(args, required=1, where=1, set=1, noparens=1)
if args.has_key(''):
args[args['']] = 1
if args.has_key('required'):
self.required = args['required']
if args.has_key('where'):
self.where = args['where']
if args.has_key('set'):
self.set = args['set']
if args.has_key('noparens'):
self.noparens = args['noparens']
def render(self,md):
r = []
for tname, args, section in self.blocks:
__traceback_info__ = tname
s = strip(section(None, md))
if s:
if r:
r.append(_TNAME_MAPPING.get(tname, tname))
if self.noparens:
r.append(s)
else:
r.append("%s\n" % s)
if r:
if len(r) > 1:
if self.noparens:
r = "%s\n" % join(r,' ')
else:
r = "(%s)\n" % join(r,' ')
else:
r = r[0]
if self.set:
r = "set\n"+r
if self.where:
r = "where\n"+r
return r
if self.required:
raise ValueError, 'Not enough input was provided!<p>'
return ''
__call__ = render
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
'''Inserting optional tests with 'sqlgroup'
It is sometimes useful to make inputs to an SQL statement
optinal. Doing so can be difficult, because not only must the
test be inserted conditionally, but SQL boolean operators may or
may not need to be inserted depending on whether other, possibly
optional, comparisons have been done. The 'sqlgroup' tag
automates the conditional insertion of boolean operators.
The 'sqlgroup' tag is a block tag that has no attributes. It can
have any number of 'and' and 'or' continuation tags.
Suppose we want to find all people with a given first or nick name
and optionally constrain the search by city and minimum and
maximum age. Suppose we want all inputs to be optional. We can
use DTML source like the following::
<dtml-sqlgroup>
<dtml-sqlgroup>
<dtml-sqltest name column=nick_name type=nb multiple optional>
<dtml-or>
<dtml-sqltest name column=first_name type=nb multiple optional>
</dtml-sqlgroup>
<dtml-and>
<dtml-sqltest home_town type=nb optional>
<dtml-and>
<dtml-if minimum_age>
age >= <dtml-sqlvar minimum_age type=int>
</dtml-if>
<dtml-and>
<dtml-if maximum_age>
age <= <dtml-sqlvar maximum_age type=int>
</dtml-if>
</dtml-sqlgroup>
This example illustrates how groups can be nested to control
boolean evaluation order. It also illustrates that the grouping
facility can also be used with other DTML tags like 'if' tags.
The 'sqlgroup' tag checks to see if text to be inserted contains
other than whitespace characters. If it does, then it is inserted
with the appropriate boolean operator, as indicated by use of an
'and' or 'or' tag, otherwise, no text is inserted.
'''
__rcs_id__='$Id$'
############################################################################
# Copyright
#
# Copyright 1996 Digital Creations, L.C., 910 Princess Anne
# Street, Suite 300, Fredericksburg, Virginia 22401 U.S.A. All
# rights reserved.
#
############################################################################
__version__='$Revision: 1.5 $'[11:-2]
import sys
from DocumentTemplate.DT_Util import ParseError, parse_params, name_param
str=__builtins__['str']
from string import find, split, join, atoi, atof
from types import ListType, TupleType, StringType
class SQLTest:
name='sqltest'
optional=multiple=None
def __init__(self, args):
args = parse_params(args, name='', expr='', type=None, column=None,
multiple=1, optional=1, op=None)
name,expr = name_param(args,'sqlvar',1)
if expr is None:
expr=name
else: expr=expr.eval
self.__name__, self.expr = name, expr
self.args=args
if not args.has_key('type'):
raise ParseError, ('the type attribute is required', 'sqltest')
self.type=t=args['type']
if not valid_type(t):
raise ParseError, ('invalid type, %s' % t, 'sqltest')
if args.has_key('optional'): self.optional=args['optional']
if args.has_key('multiple'): self.multiple=args['multiple']
if args.has_key('column'):
self.column=args['column']
elif self.__name__ is None:
err = ' the column attribute is required if an expression is used'
raise ParseError, (err, 'sqltest')
else:
self.column=self.__name__
# Deal with optional operator specification
op = '=' # Default
if args.has_key('op'):
op = args['op']
# Try to get it from the chart, otherwise use the one provided
op = comparison_operators.get(op, op)
self.op = op
def render(self, md):
name=self.__name__
t=self.type
args=self.args
try:
expr=self.expr
if type(expr) is type(''):
v=md[expr]
else:
v=expr(md)
except KeyError:
if args.has_key('optional') and args['optional']:
return ''
raise ValueError, 'Missing input variable, <em>%s</em>' % name
if type(v) in (ListType, TupleType):
if len(v) > 1 and not self.multiple:
raise ValueError, (
'multiple values are not allowed for <em>%s</em>'
% name)
else: v=[v]
vs=[]
for v in v:
if not v and type(v) is StringType and t != 'string': continue
if t=='int':
try:
if type(v) is StringType:
if v[-1:]=='L':
v=v[:-1]
atoi(v)
else: v=str(int(v))
except ValueError:
raise ValueError, (
'Invalid integer value for <em>%s</em>' % name)
elif t=='float':
if not v and type(v) is StringType: continue
try:
if type(v) is StringType: atof(v)
else: v=str(float(v))
except ValueError:
raise ValueError, (
'Invalid floating-point value for <em>%s</em>' % name)
else:
if not isinstance(v, (str, unicode)):
v = str(v)
v=md.getitem('sql_quote__',0)(v)
#if find(v,"\'") >= 0: v=join(split(v,"\'"),"''")
#v="'%s'" % v
vs.append(v)
if not vs and t=='nb':
if args.has_key('optional') and args['optional']:
return ''
else:
err = 'Invalid empty string value for <em>%s</em>' % name
raise ValueError, err
if not vs:
if self.optional: return ''
raise ValueError, (
'No input was provided for <em>%s</em>' % name)
if len(vs) > 1:
vs=join(map(str,vs),', ')
if self.op == '<>':
## Do the equivalent of 'not-equal' for a list,
## "a not in (b,c)"
return "%s not in (%s)" % (self.column, vs)
else:
## "a in (b,c)"
return "%s in (%s)" % (self.column, vs)
return "%s %s %s" % (self.column, self.op, vs[0])
__call__=render
valid_type={'int':1, 'float':1, 'string':1, 'nb': 1}.has_key
comparison_operators = { 'eq': '=', 'ne': '<>',
'lt': '<', 'le': '<=', 'lte': '<=',
'gt': '>', 'ge': '>=', 'gte': '>=' }
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
'''Inserting values with the 'sqlvar' tag
The 'sqlvar' tag is used to type-safely insert values into SQL
text. The 'sqlvar' tag is similar to the 'var' tag, except that
it replaces text formatting parameters with SQL type information.
The sqlvar tag has the following attributes:
name -- The name of the variable to insert. As with other
DTML tags, the 'name=' prefix may be, and usually is,
ommitted.
type -- The data type of the value to be inserted. This
attribute is required and may be one of 'string',
'int', 'float', or 'nb'. The 'nb' data type indicates a
string that must have a length that is greater than 0.
optional -- A flag indicating that a value is optional. If a
value is optional and is not provided (or is blank
when a non-blank value is expected), then the string
'null' is inserted.
For example, given the tag::
<dtml-sqlvar x type=nb optional>
if the value of 'x' is::
Let\'s do it
then the text inserted is:
'Let''s do it'
however, if x is ommitted or an empty string, then the value
inserted is 'null'.
'''
__rcs_id__='$Id$'
############################################################################
# Copyright
#
# Copyright 1996 Digital Creations, L.C., 910 Princess Anne
# Street, Suite 300, Fredericksburg, Virginia 22401 U.S.A. All
# rights reserved.
#
############################################################################
__version__='$Revision: 1.15 $'[11:-2]
from DocumentTemplate.DT_Util import ParseError, parse_params, name_param
from string import find, split, join, atoi, atof
StringType = str
str = __builtins__['str']
class SQLVar:
name='sqlvar'
def __init__(self, args):
args = parse_params(args, name='', expr='', type=None, optional=1)
name,expr=name_param(args,'sqlvar',1)
if expr is None: expr=name
else: expr=expr.eval
self.__name__, self.expr = name, expr
self.args=args
if not args.has_key('type'):
raise ParseError('the type attribute is required', 'dtvar')
t=args['type']
if not valid_type(t):
raise ParseError('invalid type, %s' % t, 'dtvar')
def render(self, md):
name=self.__name__
args=self.args
t=args['type']
try:
expr=self.expr
if type(expr) is type(''): v=md[expr]
else: v=expr(md)
except:
if args.has_key('optional') and args['optional']:
return 'null'
if type(expr) is not type(''):
raise
raise ValueError, 'Missing input variable, <em>%s</em>' % name
if v is None:
return 'null'
if t=='int':
try:
if type(v) is StringType:
if v[-1:]=='L':
v=v[:-1]
atoi(v)
else: v=str(int(v))
except:
if not v and args.has_key('optional') and args['optional']:
return 'null'
raise ValueError, (
'Invalid integer value for <em>%s</em>' % name)
elif t=='float':
try:
if type(v) is StringType:
if v[-1:]=='L':
v=v[:-1]
atof(v)
else: v=str(float(v))
except:
if not v and args.has_key('optional') and args['optional']:
return 'null'
raise ValueError, (
'Invalid floating-point value for <em>%s</em>' % name)
else:
if not isinstance(v, (str, unicode)):
v=str(v)
if not v and t=='nb':
if args.has_key('optional') and args['optional']:
return 'null'
else:
raise ValueError, (
'Invalid empty string value for <em>%s</em>' % name)
v=md.getitem('sql_quote__',0)(v)
#if find(v,"\'") >= 0: v=join(split(v,"\'"),"''")
#v="'%s'" % v
return v
__call__=render
valid_type={'int':1, 'float':1, 'string':1, 'nb': 1}.has_key
from unittest import TestCase, TestSuite, makeSuite, main
from cStringIO import StringIO
from ExtensionClass import Base
from Shared.DC.ZRDB.Results import Results
from Shared.DC.ZRDB import RDB
class Brain:
def __init__(self, *args): pass
Parent = Base()
class TestResults(TestCase):
def test_results(self):
r = Results(([{'name':'foo', 'type':'integer'},
{'name':'bar', 'type':'integer'}],
((1, 2), (3, 4))),
brains=Brain,
parent=Parent)
self.assertEquals(len(r), 2)
row = r[0]
self.assertEquals(row[0], 1)
self.assertEquals(row[1], 2)
self.assertEquals(row.foo, 1)
self.assertEquals(row.bar, 2)
self.assertEquals(row.FOO, 1)
self.assertEquals(row.BAR, 2)
row = r[1]
self.assertEquals(row[0], 3)
self.assertEquals(row[1], 4)
self.assertEquals(row.foo, 3)
self.assertEquals(row.bar, 4)
self.assertEquals(row.FOO, 3)
self.assertEquals(row.BAR, 4)
self.failUnless(isinstance(row, Brain))
def test_rdb_file(self):
infile = StringIO("""\
foo\tbar
2i\t2i
1\t2
3\t4\
""")
r = RDB.File(infile,
brains=Brain,
parent=Parent)
self.assertEquals(len(r), 2)
row = r[0]
self.assertEquals(row[0], 1)
self.assertEquals(row[1], 2)
self.assertEquals(row.foo, 1)
self.assertEquals(row.bar, 2)
self.assertEquals(row.FOO, 1)
self.assertEquals(row.BAR, 2)
row = r[1]
self.assertEquals(row[0], 3)
self.assertEquals(row[1], 4)
self.assertEquals(row.foo, 3)
self.assertEquals(row.bar, 4)
self.assertEquals(row.FOO, 3)
self.assertEquals(row.BAR, 4)
self.failUnless(isinstance(row, Brain))
def test_suite():
return TestSuite((makeSuite(TestResults),))
if __name__ == '__main__':
main(defaultTest='test_suite')
##############################################################################
#
# Copyright (c) 2010 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
from unittest import TestCase, TestSuite, makeSuite
from Shared.DC.ZRDB.TM import TM
class TestTM(TestCase):
def test_sortKey(self):
tm = TM()
# the default Transaction Manager should have .sortKey() of 1 for
# backward compatibility
self.assertEquals(tm.sortKey(), 1)
# but the sortKey() should be adjustable
tm.setSortKey(())
self.assertEquals(tm.sortKey(), ())
def test_suite():
return TestSuite((makeSuite(TestTM),))
This diff is collapsed.
""" Unit tests for Products.RHGDelivery.simpleresults
$Id: test_results.py,v 1.2 2005/09/07 21:25:47 tseaver Exp $
"""
import unittest
from ExtensionClass import Base
from Acquisition import aq_parent
class Brain:
def __init__(self, *args): pass
Parent = Base()
class TestResults(unittest.TestCase):
# test fixtures
columns = [ {'name' : 'string', 'type' : 't', 'width':1},
{'name':'int', 'type': 'i'} ]
data = [['string1', 1], ['string2', 2]]
def _getTargetClass(self):
from Shared.DC.ZRDB.Results import Results
return Results
def _makeOne(self, *args, **kw):
return self._getTargetClass()(*args, **kw)
def test_searchable_result_columns(self):
ob = self._makeOne((self.columns, self.data))
self.assertEqual(ob._searchable_result_columns(), self.columns)
def test_names(self):
ob = self._makeOne((self.columns, self.data))
self.assertEqual(ob.names(), ['string', 'int'])
def test_data_dictionary(self):
ob = self._makeOne((self.columns, self.data))
self.assertEqual(
ob.data_dictionary(),
{ 'string':{'name' : 'string', 'type' : 't', 'width':1},
'int':{'name':'int', 'type': 'i'} }
)
def test_len(self):
ob = self._makeOne((self.columns, self.data))
self.assertEqual(len(ob), 2)
def test_getitem(self):
ob = self._makeOne((self.columns, self.data))
row = ob[0]
self.assertEquals(row[0], 'string1')
self.assertEquals(row[1], 1)
row = ob[1]
self.assertEquals(row[0], 'string2')
self.assertEquals(row[1], 2)
def test_getattr_and_aliases(self):
ob = self._makeOne((self.columns, self.data))
row = ob[0]
self.assertEqual(row.string, 'string1')
self.assertEqual(row.int, 1)
self.assertEqual(row.STRING, 'string1')
self.assertEqual(row.INT, 1)
row = ob[1]
self.assertEqual(row.string, 'string2')
self.assertEqual(row.int, 2)
self.assertEqual(row.STRING, 'string2')
self.assertEqual(row.INT, 2)
def test_suppliedbrain(self):
ob = self._makeOne((self.columns, self.data), brains=Brain)
row = ob[0]
self.failUnless(isinstance(row, Brain))
def test_suppliedparent(self):
ob = self._makeOne((self.columns, self.data), parent=Parent)
row = ob[0]
self.failUnless(aq_parent(row) is Parent)
def test_tuples(self):
ob = self._makeOne((self.columns, self.data))
tuples = ob.tuples()
self.assertEqual( tuples, [('string1', 1), ('string2', 2)] )
def test_dictionaries(self):
ob = self._makeOne((self.columns, self.data))
dicts = ob.dictionaries()
self.assertEqual( dicts, [{'string':'string1', 'int':1},
{'string':'string2', 'int':2}] )
def test_asRDB(self):
ob = self._makeOne((self.columns, self.data))
asrdb = ob.asRDB()
columns = ['string\tint', '1t\ti', 'string1\t1', 'string2\t2\n']
self.assertEqual(asrdb, '\n'.join(columns))
def _set_noschema(self, row):
row.cantdoit = 1
def test_recordschema(self):
ob = self._makeOne((self.columns, self.data))
row = ob[0]
self.assertEqual(row.__record_schema__, {'string':0, 'int':1})
self.assertRaises(AttributeError, self._set_noschema, row)
def test_record_as_read_mapping(self):
ob = self._makeOne((self.columns, self.data))
row = ob[0]
self.assertEqual('%(string)s %(int)s' % row, 'string1 1')
row = ob[1]
self.assertEqual('%(string)s %(int)s' % row, 'string2 2')
def test_record_as_write_mapping(self):
ob = self._makeOne((self.columns, self.data))
row = ob[0]
row['int'] = 5
self.assertEqual('%(string)s %(int)s' % row, 'string1 5')
def test_record_as_write_mapping2(self):
ob = self._makeOne((self.columns, self.data))
row = ob[0]
row.int = 5
self.assertEqual('%(string)s %(int)s' % row, 'string1 5')
def test_record_as_sequence(self):
ob = self._makeOne((self.columns, self.data))
row = ob[0]
self.assertEqual(row[0], 'string1')
self.assertEqual(row[1], 1)
self.assertEqual(list(row), ['string1', 1])
row = ob[1]
self.assertEqual(row[0], 'string2')
self.assertEqual(row[1], 2)
self.assertEqual(list(row), ['string2', 2])
def test_record_of(self):
ob = self._makeOne((self.columns, self.data))
row = ob[0]
wrapped = row.__of__(Parent)
self.assertEqual(wrapped.aq_self, row)
self.assertEqual(wrapped.aq_parent, Parent)
def test_record_hash(self):
ob = self._makeOne((self.columns, self.data))
row = ob[0]
self.assert_(isinstance(hash(row), int))
def test_record_len(self):
ob = self._makeOne((self.columns, self.data))
row = ob[0]
self.assertEqual(len(row), 2)
def _add(self, row1, row2):
return row1 + row2
def test_record_add(self):
ob = self._makeOne((self.columns, self.data))
row1 = ob[0]
row2 = ob[1]
self.assertRaises(TypeError, self._add, row1, row2)
def _slice(self, row):
return row[1:]
def test_record_slice(self):
ob = self._makeOne((self.columns, self.data))
row = ob[0]
self.assertRaises(TypeError, self._slice, row)
def _mul(self, row):
return row * 3
def test_record_mul(self):
ob = self._makeOne((self.columns, self.data))
row = ob[0]
self.assertRaises(TypeError, self._mul, row)
def _del(self, row):
del row[0]
def test_record_delitem(self):
ob = self._makeOne((self.columns, self.data))
row = ob[0]
self.assertRaises(TypeError, self._del, row)
def test_suite():
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(TestResults))
return suite
if __name__ == '__main__':
unittest.main(defaultTest='test_suite')
##############################################################################
#
# Copyright (c) 2005 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
import unittest
from UserDict import UserDict
def _sql_quote(v):
return '"%s"' % v
class SQLGroupTests(unittest.TestCase):
def _getTargetClass(self):
from Shared.DC.ZRDB.sqlgroup import SQLGroup
return SQLGroup
def _makeOne(self, *args, **kw):
return self._getTargetClass()(*args, **kw)
def test_ctor_empty_args(self):
group = self._makeOne([('sqlgroup', '', None)])
self.assertEqual(group.__name__, 'sqlgroup ')
self.failIf(group.required)
self.failIf(group.where)
self.failIf(group.set)
self.failIf(group.noparens)
def test_ctor_required(self):
group = self._makeOne([('sqlgroup', 'required', None)])
self.assertEqual(group.__name__, 'sqlgroup required')
self.failUnless(group.required)
self.failIf(group.where)
self.failIf(group.set)
self.failIf(group.noparens)
def test_ctor_where(self):
group = self._makeOne([('sqlgroup', 'where', None)])
self.assertEqual(group.__name__, 'sqlgroup where')
self.failIf(group.required)
self.failUnless(group.where)
self.failIf(group.set)
self.failIf(group.noparens)
def test_ctor_noparens(self):
group = self._makeOne([('sqlgroup', 'noparens', None)])
self.assertEqual(group.__name__, 'sqlgroup noparens')
self.failIf(group.required)
self.failIf(group.where)
self.failIf(group.set)
self.failUnless(group.noparens)
def test_ctor_set(self):
group = self._makeOne([('sqlgroup', 'set', None)])
self.assertEqual(group.__name__, 'sqlgroup set')
self.failIf(group.required)
self.failIf(group.where)
self.failUnless(group.set)
self.failIf(group.noparens)
def test_render_empty_optional(self):
group = self._makeOne([('sqlgroup', '', lambda x, y:'')])
md = {}
self.assertEqual(group.render(md), '')
def test_render_empty_optional_where(self):
group = self._makeOne([('sqlgroup', 'where', lambda x, y:'')])
md = {}
self.assertEqual(group.render(md), '')
def test_render_empty_optional_set(self):
group = self._makeOne([('sqlgroup', 'set', lambda x, y:'')])
md = {}
self.assertEqual(group.render(md), '')
def test_render_empty_required_raises_ValueError(self):
group = self._makeOne([('sqlgroup', 'required', lambda x, y:'')])
md = {}
self.assertRaises(ValueError, group.render, md)
def test_render_one_block(self):
group = self._makeOne([('sqlgroup', '', lambda x, y:'abc'),
])
md = {}
rendered = group.render(md)
rendered = ''.join(rendered.split('\n'))
self.assertEqual(rendered, 'abc')
def test_render_one_block_where(self):
group = self._makeOne([('sqlgroup', 'where', lambda x, y:'abc'),
])
md = {}
rendered = group.render(md)
self.assertEqual(rendered, 'where\nabc\n')
def test_render_one_block_set(self):
group = self._makeOne([('sqlgroup', 'set', lambda x, y:'abc'),
])
md = {}
rendered = group.render(md)
self.assertEqual(rendered, 'set\nabc\n')
def test_render_multiple_blocks_with_tname(self):
group = self._makeOne([('sqlgroup', '', lambda x, y:'abc'),
('baz', '', lambda x, y: 'def'),
('qux', '', lambda x, y: 'ghi'),
])
md = {}
rendered = group.render(md)
rendered = ''.join(rendered.split('\n'))
self.assertEqual(rendered, '(abc baz def qux ghi)')
def test_render_multiple_blocks_with_tname_noparens(self):
group = self._makeOne([('sqlgroup', 'noparens', lambda x, y:'abc'),
('baz', '', lambda x, y: 'def'),
('qux', '', lambda x, y: 'ghi'),
])
md = {}
rendered = group.render(md)
rendered = ''.join(rendered.split('\n'))
self.assertEqual(rendered, 'abc baz def qux ghi')
def test_render_multiple_blocks_with_tname_and_where(self):
group = self._makeOne([('sqlgroup', 'where', lambda x, y:'abc'),
('baz', '', lambda x, y: 'def'),
('qux', '', lambda x, y: 'ghi'),
])
md = {}
rendered = group.render(md)
rendered = ''.join(rendered.split('\n'))
self.assertEqual(rendered, 'where(abc baz def qux ghi)')
def test_parsed_rendered_complex_where(self):
# something of a functional test, as we use nvSQL to get parsed.
from Shared.DC.ZRDB.DA import nvSQL
template = nvSQL(WHERE_EXAMPLE)
mapping = {}
mapping['name'] = 'Goofy'
mapping['home_town'] = 'Orlando'
mapping['sql_quote__'] = _sql_quote
rendered = template(None, mapping)
self.assertEqual(rendered,
'select * from actors\n'
'where\n'
'((nick_name = "Goofy"\n'
' or first_name = "Goofy"\n)\n'
' and home_town = "Orlando"\n)\n'
)
def test_parsed_rendered_complex_set(self):
# something of a functional test, as we use nvSQL to get parsed.
from Shared.DC.ZRDB.DA import nvSQL
template = nvSQL(UPDATE_EXAMPLE)
mapping = {}
mapping['nick_name'] = 'Goofy'
mapping['home_town'] = 'Orlando'
mapping['sql_quote__'] = _sql_quote
rendered = template(None, mapping)
self.assertEqual(rendered,
'update actors\n'
'set\nnick_name = "Goofy" , home_town = "Orlando"\n'
)
WHERE_EXAMPLE = """\
select * from actors
<dtml-sqlgroup where required>
<dtml-sqlgroup>
<dtml-sqltest name column=nick_name type=nb multiple optional>
<dtml-or>
<dtml-sqltest name column=first_name type=nb multiple optional>
</dtml-sqlgroup>
<dtml-and>
<dtml-sqltest home_town type=nb optional>
<dtml-and>
<dtml-if minimum_age>
age >= <dtml-sqlvar minimum_age type=int>
</dtml-if>
<dtml-and>
<dtml-if maximum_age>
age <= <dtml-sqlvar maximum_age type=int>
</dtml-if>
</dtml-sqlgroup>
"""
UPDATE_EXAMPLE = """\
update actors
<dtml-sqlgroup set noparens>
<dtml-sqltest nick_name type=nb optional>
<dtml-comma>
<dtml-sqltest home_town type=nb optional>
</dtml-sqlgroup>
"""
def test_suite():
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(SQLGroupTests))
return suite
if __name__ == '__main__':
unittest.main(defaultTest='test_suite')
This diff is collapsed.
......@@ -16,6 +16,7 @@ MultiMapping = 2.13.0
nt-svcutils = 2.13.0
Persistence = 2.13.2
Products.ZCTextIndex = 2.13.0
Products.ZSQLMethods =
Record = 2.13.0
tempstorage = 2.11.3
zExceptions = 2.13.0
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment