[Zope-DB] ZSQL Caching
Andy McKay
andym@ActiveState.com
Tue, 30 Oct 2001 21:48:10 -0800
This is a multi-part message in MIME format.
------=_NextPart_000_0630_01C1618C.916781C0
Content-Type: text/plain;
charset="iso-8859-1"
Content-Transfer-Encoding: 7bit
We were discussing ZSQL caching and work, and we wanted to debug the
contents of our cache, so I through together a quick method that cleans up
the cache a little and adds a Cache tab. There you can see the contents of
the cache and flush it.
For quick debugging we found it quite useful.
Patch and new source for ZRDB/DA.py and viewCache.dtml attached.
Cheers.
--
Andy McKay
------=_NextPart_000_0630_01C1618C.916781C0
Content-Type: text/plain;
name="ZSQLDiff.txt"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;
filename="ZSQLDiff.txt"
--- f:\lib\python\Shared\DC\ZRDB\DA.py Wed Oct 03 11:48:37 2001=0D
+++ l:\lib\python\Shared\DC\ZRDB\DA.py Tue Oct 23 16:08:38 2001=0D
@@ -155,6 +155,7 @@=0D
'help':('ZSQLMethods','Z-SQL-Method_Test.stx')},=0D
{'label':'Advanced', 'action':'manage_advancedForm',=0D
'help':('ZSQLMethods','Z-SQL-Method_Advanced.stx')},=0D
+ {'label':'Cache', 'action':'manage_viewCache',},=0D
)=0D
+AccessControl.Role.RoleManager.manage_options=0D
+OFS.SimpleItem.Item.manage_options=0D
@@ -165,7 +166,8 @@=0D
('View management screens',=0D
(=0D
'manage_main', 'index_html',=0D
- 'manage_advancedForm', 'PrincipiaSearchSource', 'document_src'=0D
+ 'manage_advancedForm', 'PrincipiaSearchSource', 'document_src',=0D
+ 'manage_viewCache', 'size_of_cache', 'clean_cache', 'reset_cache',=
'get_cache'=0D
)),=0D
('Change Database Methods',=0D
('manage_edit','manage_advanced', 'manage_testForm','manage_test'=
,=0D
@@ -179,6 +181,7 @@=0D
self.manage_edit(title, connection_id, arguments, template)=0D
=0D
manage_advancedForm=3DDTMLFile('dtml/advanced', globals())=0D
+ manage_viewCache=3DDTMLFile('dtml/viewCache', globals())=0D
=0D
test_url___roles__=3DNone=0D
def test_url_(self):=0D
@@ -409,36 +416,92 @@=0D
def _searchable_result_columns(self): return self._col=0D
=0D
def _cached_result(self, DB__, query):=0D
+ ''' get the cached sql, look up the result,=0D
+ if its not in the cache, add it in '''=0D
+ cache, tcache =3D self.__get_cache()=0D
+=0D
+ # if the cache size is half of the max,=0D
+ # clean, otherwise carry on=0D
+ if len(cache) > self.max_cache_ / 2:=0D
+ self.__clean_cache(cache, tcache)=0D
=0D
- # Try to fetch from cache=0D
- if hasattr(self,'_v_cache'): cache=3Dself._v_cache=0D
- else: cache=3Dself._v_cache=3D{}, Bucket()=0D
- cache, tcache =3D cache=0D
- max_cache=3Dself.max_cache_=0D
now=3Dtime()=0D
t=3Dnow-self.cache_time_=0D
- if len(cache) > max_cache / 2:=0D
- keys=3Dtcache.keys()=0D
- keys.reverse()=0D
- while keys and (len(keys) > max_cache or keys[-1] < t):=0D
- key=3Dkeys[-1]=0D
- q=3Dtcache[key]=0D
- del tcache[key]=0D
- if int(cache[q][0]) =3D=3D key:=0D
- del cache[q]=0D
- del keys[-1]=0D
- =0D
+=0D
+ # pull result out of cache based on=0D
+ # query=0D
if cache.has_key(query):=0D
k, r =3D cache[query]=0D
if k > t: return r=0D
=0D
result=3Dapply(DB__.query, query)=0D
+=0D
+ # update cache if necessary=0D
if self.cache_time_ > 0:=0D
tcache[int(now)]=3Dquery=0D
cache[query]=3D now, result=0D
=0D
return result=0D
=0D
+ def __get_cache(self):=0D
+ ''' get the cached sql queries '''=0D
+ if hasattr(self,'_v_cache'): cache=3Dself._v_cache=0D
+ else: cache=3Dself._v_cache=3D{}, Bucket()=0D
+ return cache=0D
+=0D
+ def __clean_cache(self, cache, tcache):=0D
+ ''' clean the cache '''=0D
+ max_cache=3Dself.max_cache_=0D
+ now=3Dtime()=0D
+ t=3Dnow-self.cache_time_=0D
+=0D
+ keys=3Dtcache.keys()=0D
+ keys.reverse()=0D
+ while keys and (len(keys) > max_cache or keys[-1] < t):=0D
+ key=3Dkeys[-1]=0D
+ q=3Dtcache[key]=0D
+ del tcache[key]=0D
+ if int(cache[q][0]) =3D=3D key:=0D
+ del cache[q]=0D
+ del keys[-1]=0D
+=0D
+ return cache, tcache=0D
+=0D
+ def __reset_cache(self):=0D
+ self._v_cache=3D{}, Bucket()=0D
+=0D
+ def size_of_cache(self):=0D
+ ''' No of items in the cache '''=0D
+ return len(self.__get_cache()[0])=0D
+=0D
+ def clean_cache(self, REQUEST=3DNone):=0D
+ ''' Cleans the cache by removing only invalid objects '''=0D
+ c, t =3D self.__get_cache()=0D
+ self.__clean_cache(c, t)=0D
+ if REQUEST:=0D
+ return self.manage_viewCache(self, REQUEST, manage_tabs_messag=
e=3D'Cache cleaned')=0D
+=0D
+ def reset_cache(self, REQUEST=3DNone):=0D
+ ''' Removes all items from the cache '''=0D
+ self.__reset_cache()=0D
+ if REQUEST:=0D
+ return self.manage_viewCache(self, REQUEST, manage_tabs_messag=
e=3D'Cache reset')=0D
+=0D
+ def get_cache(self):=0D
+ ''' Shows the cache '''=0D
+ c =3D self.__get_cache()[0] # cache=0D
+ l =3D [] # easily presentable version...=0D
+ now=3Dtime()=0D
+=0D
+ for k, v in c.items():=0D
+ d =3D { =0D
+ 'query':k[0], =0D
+ 'expired': now > (self.cache_time_+int(v[0])), =0D
+ 'secs_since_run':int(now-int(v[0])),=0D
+ }=0D
+ l.append(d)=0D
+ return l=0D
+=0D
------=_NextPart_000_0630_01C1618C.916781C0
Content-Type: text/plain;
name="DA.py"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;
filename="DA.py"
###########################################################################=
###=0D
# =0D
# Zope Public License (ZPL) Version 1.0=0D
# -------------------------------------=0D
# =0D
# Copyright (c) Digital Creations. All rights reserved.=0D
# =0D
# This license has been certified as Open Source(tm).=0D
# =0D
# Redistribution and use in source and binary forms, with or without=0D
# modification, are permitted provided that the following conditions are=0D
# met:=0D
# =0D
# 1. Redistributions in source code must retain the above copyright=0D
# notice, this list of conditions, and the following disclaimer.=0D
# =0D
# 2. Redistributions in binary form must reproduce the above copyright=0D
# notice, this list of conditions, and the following disclaimer in=0D
# the documentation and/or other materials provided with the=0D
# distribution.=0D
# =0D
# 3. Digital Creations requests that attribution be given to Zope=0D
# in any manner possible. Zope includes a "Powered by Zope"=0D
# button that is installed by default. While it is not a license=0D
# violation to remove this button, it is requested that the=0D
# attribution remain. A significant investment has been put=0D
# into Zope, and this effort will continue if the Zope community=0D
# continues to grow. This is one way to assure that growth.=0D
# =0D
# 4. All advertising materials and documentation mentioning=0D
# features derived from or use of this software must display=0D
# the following acknowledgement:=0D
# =0D
# "This product includes software developed by Digital Creations=0D
# for use in the Z Object Publishing Environment=0D
# (http://www.zope.org/)."=0D
# =0D
# In the event that the product being advertised includes an=0D
# intact Zope distribution (with copyright and license included)=0D
# then this clause is waived.=0D
# =0D
# 5. Names associated with Zope or Digital Creations must not be used to=0D
# endorse or promote products derived from this software without=0D
# prior written permission from Digital Creations.=0D
# =0D
# 6. Modified redistributions of any form whatsoever must retain=0D
# the following acknowledgment:=0D
# =0D
# "This product includes software developed by Digital Creations=0D
# for use in the Z Object Publishing Environment=0D
# (http://www.zope.org/)."=0D
# =0D
# Intact (re-)distributions of any official Zope release do not=0D
# require an external acknowledgement.=0D
# =0D
# 7. Modifications are encouraged but must be packaged separately as=0D
# patches to official Zope releases. Distributions that do not=0D
# clearly separate the patches from the original work must be clearly=0D
# labeled as unofficial distributions. Modifications which do not=0D
# carry the name Zope may be packaged in any form, as long as they=0D
# conform to all of the clauses above.=0D
# =0D
# =0D
# Disclaimer=0D
# =0D
# THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS ``AS IS'' AND ANY=0D
# EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE=0D
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR=0D
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DIGITAL CREATIONS OR ITS=0D
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,=0D
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT=0D
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF=0D
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND=0D
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,=0D
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT=0D
# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF=0D
# SUCH DAMAGE.=0D
# =0D
# =0D
# This software consists of contributions made by Digital Creations and=0D
# many individuals on behalf of Digital Creations. Specific=0D
# attributions are listed in the accompanying credits file.=0D
# =0D
###########################################################################=
###=0D
__doc__=3D'''Generic Database adapter'''=0D
=0D
=0D
__version__=3D'$Revision: 1.103 $'[11:-2]=0D
=0D
import OFS.SimpleItem, Aqueduct, RDB, re=0D
import DocumentTemplate, marshal, md5, base64, Acquisition, os=0D
from Aqueduct import decodestring, parse=0D
from Aqueduct import custom_default_report, default_input_form=0D
from Globals import DTMLFile, MessageDialog=0D
from cStringIO import StringIO=0D
import sys, Globals, OFS.SimpleItem, AccessControl.Role=0D
from string import atoi, find, join, split, rstrip=0D
import DocumentTemplate, sqlvar, sqltest, sqlgroup=0D
from time import time=0D
from zlib import compress, decompress=0D
from DateTime.DateTime import DateTime=0D
md5new=3Dmd5.new=0D
import ExtensionClass=0D
import DocumentTemplate.DT_Util=0D
from cPickle import dumps, loads=0D
from Results import Results=0D
from App.Extensions import getBrain=0D
from AccessControl import getSecurityManager=0D
from AccessControl.DTML import RestrictedDTML=0D
from webdav.Resource import Resource=0D
from webdav.Lockable import ResourceLockedError=0D
try: from IOBTree import Bucket=0D
except: Bucket=3Dlambda:{}=0D
=0D
=0D
class nvSQL(DocumentTemplate.HTML):=0D
# Non-validating SQL Template for use by SQLFiles.=0D
commands=3D{}=0D
for k, v in DocumentTemplate.HTML.commands.items(): commands[k]=3Dv=0D
commands['sqlvar' ]=3Dsqlvar.SQLVar=0D
commands['sqltest']=3Dsqltest.SQLTest=0D
commands['sqlgroup' ]=3Dsqlgroup.SQLGroup=0D
=0D
_proxy_roles=3D()=0D
=0D
=0D
class SQL(RestrictedDTML, ExtensionClass.Base, nvSQL):=0D
# Validating SQL template for Zope SQL Methods.=0D
pass=0D
=0D
=0D
class DA(=0D
Aqueduct.BaseQuery,Acquisition.Implicit,=0D
Globals.Persistent,=0D
AccessControl.Role.RoleManager,=0D
OFS.SimpleItem.Item,=0D
Resource=0D
):=0D
'Database Adapter'=0D
=0D
_col=3DNone=0D
max_rows_=3D1000=0D
cache_time_=3D0=0D
max_cache_=3D100=0D
class_name_=3Dclass_file_=3D''=0D
_zclass=3DNone=0D
allow_simple_one_argument_traversal=3DNone=0D
template_class=3DSQL=0D
=0D
manage_options=3D(=0D
(=0D
{'label':'Edit', 'action':'manage_main',=0D
'help':('ZSQLMethods','Z-SQL-Method_Edit.stx')},=0D
{'label':'Test', 'action':'manage_testForm',=0D
'help':('ZSQLMethods','Z-SQL-Method_Test.stx')},=0D
{'label':'Advanced', 'action':'manage_advancedForm',=0D
'help':('ZSQLMethods','Z-SQL-Method_Advanced.stx')},=0D
{'label':'Cache', 'action':'manage_viewCache',},=0D
)=0D
+AccessControl.Role.RoleManager.manage_options=0D
+OFS.SimpleItem.Item.manage_options=0D
)=0D
=0D
# Specify how individual operations add up to "permissions":=0D
__ac_permissions__=3D(=0D
('View management screens',=0D
(=0D
'manage_main', 'index_html',=0D
'manage_advancedForm', 'PrincipiaSearchSource', 'document_src',=0D
'manage_viewCache', 'size_of_cache', 'clean_cache', 'reset_cache', =
'get_cache'=0D
)),=0D
('Change Database Methods',=0D
('manage_edit','manage_advanced', 'manage_testForm','manage_test',=
=0D
'manage_product_zclass_info', 'PUT')),=0D
('Use Database Methods', ('__call__',''), ('Anonymous','Manager')),=
=0D
)=0D
=0D
=0D
def __init__(self, id, title, connection_id, arguments, template):=0D
self.id=3Dstr(id)=0D
self.manage_edit(title, connection_id, arguments, template)=0D
=0D
manage_advancedForm=3DDTMLFile('dtml/advanced', globals())=0D
manage_viewCache=3DDTMLFile('dtml/viewCache', globals())=0D
=0D
test_url___roles__=3DNone=0D
def test_url_(self):=0D
'Method for testing server connection information'=0D
return 'PING'=0D
=0D
_size_changes=3D{=0D
'Bigger': (5,5),=0D
'Smaller': (-5,-5),=0D
'Narrower': (0,-5),=0D
'Wider': (0,5),=0D
'Taller': (5,0),=0D
'Shorter': (-5,0),=0D
}=0D
=0D
def _er(self,title,connection_id,arguments,template,=0D
SUBMIT,sql_pref__cols,sql_pref__rows,REQUEST):=0D
dr,dc =3D self._size_changes[SUBMIT]=0D
=0D
rows=3Dmax(1,atoi(sql_pref__rows)+dr)=0D
cols=3Dmax(40,atoi(sql_pref__cols)+dc)=0D
e=3D(DateTime('GMT') + 365).rfc822()=0D
resp=3DREQUEST['RESPONSE']=0D
resp.setCookie('sql_pref__rows',str(rows),path=3D'/',expires=3De)=
=0D
resp.setCookie('sql_pref__cols',str(cols),path=3D'/',expires=3De)=
=0D
return self.manage_main(=0D
self,REQUEST,=0D
title=3Dtitle,=0D
arguments_src=3Darguments,=0D
connection_id=3Dconnection_id,=0D
src=3Dtemplate,=0D
sql_pref__cols=3Dcols,sql_pref__rows=3Drows)=0D
=0D
def manage_edit(self,title,connection_id,arguments,template,=0D
SUBMIT=3D'Change',sql_pref__cols=3D'50', sql_pref__rows=
=3D'20',=0D
REQUEST=3DNone):=0D
"""Change database method properties=0D
=0D
The 'connection_id' argument is the id of a database connection=0D
that resides in the current folder or in a folder above the=0D
current folder. The database should understand SQL.=0D
=0D
The 'arguments' argument is a string containing an arguments=0D
specification, as would be given in the SQL method cration form.=0D
=0D
The 'template' argument is a string containing the source for the=
=0D
SQL Template.=0D
"""=0D
=0D
if self._size_changes.has_key(SUBMIT):=0D
return self._er(title,connection_id,arguments,template,=0D
SUBMIT,sql_pref__cols,sql_pref__rows,REQUEST)=
=0D
=0D
if self.wl_isLocked():=0D
raise ResourceLockedError, 'SQL Method is locked via WebDAV'=0D
=0D
self.title=3Dstr(title)=0D
self.connection_id=3Dstr(connection_id)=0D
arguments=3Dstr(arguments)=0D
self.arguments_src=3Darguments=0D
self._arg=3Dparse(arguments)=0D
template=3Dstr(template)=0D
self.src=3Dtemplate=0D
self.template=3Dt=3Dself.template_class(template)=0D
t.cook()=0D
self._v_cache=3D{}, Bucket()=0D
if REQUEST:=0D
if SUBMIT=3D=3D'Change and Test':=0D
return self.manage_testForm(REQUEST)=0D
message=3D'ZSQL Method content changed'=0D
return self.manage_main(self, REQUEST, manage_tabs_message=3Dme=
ssage)=0D
return ''=0D
=0D
=0D
def manage_advanced(self, max_rows, max_cache, cache_time,=0D
class_name, class_file, direct=3DNone,=0D
REQUEST=3DNone, zclass=3D''):=0D
"""Change advanced properties=0D
=0D
The arguments are:=0D
=0D
max_rows -- The maximum number of rows to be returned from a query.=
=0D
=0D
max_cache -- The maximum number of results to cache=0D
=0D
cache_time -- The maximum amound of time to use a cached result.=0D
=0D
class_name -- The name of a class that provides additional=0D
attributes for result record objects. This class will be a=0D
base class of the result record class.=0D
=0D
class_file -- The name of the file containing the class=0D
definition.=0D
=0D
The class file normally resides in the 'Extensions'=0D
directory, however, the file name may have a prefix of=0D
'product.', indicating that it should be found in a product=0D
directory.=0D
=0D
For example, if the class file is: 'ACMEWidgets.foo', then an=0D
attempt will first be made to use the file=0D
'lib/python/Products/ACMEWidgets/Extensions/foo.py'. If this=0D
failes, then the file 'Extensions/ACMEWidgets.foo.py' will be=0D
used.=0D
=0D
"""=0D
# paranoid type checking=0D
if type(max_rows) is not type(1):=0D
max_rows=3Datoi(max_rows)=0D
if type(max_cache) is not type(1):=0D
max_cache=3Datoi(max_cache)=0D
if type(cache_time) is not type(1):=0D
cache_time=3Datoi(cache_time) =0D
class_name=3Dstr(class_name)=0D
class_file=3Dstr(class_file)=0D
=0D
self.max_rows_ =3D max_rows=0D
self.max_cache_, self.cache_time_ =3D max_cache, cache_time=0D
self._v_cache=3D{}, Bucket()=0D
self.class_name_, self.class_file_ =3D class_name, class_file=0D
self._v_brain=3DgetBrain(self.class_file_, self.class_name_, 1)=0D
self.allow_simple_one_argument_traversal=3Ddirect=0D
=0D
if zclass:=0D
for d in self.aq_acquire('_getProductRegistryData')('zclasses')=
:=0D
if ("%s/%s" % (d.get('product'),d.get('id'))) =3D=3D zclass=
:=0D
self._zclass=3Dd['meta_class']=0D
break=0D
=0D
=0D
if REQUEST is not None:=0D
m=3D"ZSQL Method advanced settings have been set"=0D
return self.manage_advancedForm(self,REQUEST,manage_tabs_messag=
e=3Dm)=0D
## return self.manage_editedDialog(REQUEST)=0D
=0D
#def getFindContent(self):=0D
# """Return content for use by the Find machinery."""=0D
# return '%s\n%s' % (self.arguments_src, self.src)=0D
=0D
def PrincipiaSearchSource(self):=0D
"""Return content for use by the Find machinery."""=0D
return '%s\n%s' % (self.arguments_src, self.src)=0D
=0D
=0D
# WebDAV / FTP support=0D
=0D
default_content_type =3D 'text/plain'=0D
=0D
def document_src(self, REQUEST=3DNone, RESPONSE=3DNone):=0D
"""Return unprocessed document source."""=0D
if RESPONSE is not None:=0D
RESPONSE.setHeader('Content-Type', 'text/plain')=0D
return '<params>%s</params>\n%s' % (self.arguments_src, self.src)=
=0D
=0D
def manage_FTPget(self):=0D
"""Get source for FTP download"""=0D
self.REQUEST.RESPONSE.setHeader('Content-Type', 'text/plain')=0D
return '<params>%s</params>\n%s' % (self.arguments_src, self.src)=
=0D
=0D
def PUT(self, REQUEST, RESPONSE):=0D
"""Handle put requests"""=0D
self.dav__init(REQUEST, RESPONSE)=0D
self.dav__simpleifhandler(REQUEST, RESPONSE, refresh=3D1)=0D
body =3D REQUEST.get('BODY', '')=0D
m =3D re.match('\s*<params>(.*)</params>\s*\n', body, re.I | re.S)=
=0D
if m:=0D
self.arguments_src =3D m.group(1)=0D
self._arg=3Dparse(self.arguments_src)=0D
body =3D body[m.end():]=0D
template =3D body=0D
self.src =3D template=0D
self.template=3Dt=3Dself.template_class(template)=0D
t.cook()=0D
self._v_cache=3D{}, Bucket()=0D
RESPONSE.setStatus(204)=0D
return RESPONSE=0D
=0D
=0D
def manage_testForm(self, REQUEST):=0D
" "=0D
input_src=3Ddefault_input_form(self.title_or_id(),=0D
self._arg, 'manage_test',=0D
'<dtml-var manage_tabs>')=0D
return DocumentTemplate.HTML(input_src)(self, REQUEST, HTTP_REFERER=
=3D'')=0D
=0D
def manage_test(self, REQUEST):=0D
"""Test an SQL method."""=0D
# Try to render the query template first so that the rendered=0D
# source will be available for the error message in case some=0D
# error occurs...=0D
try: src=3Dself(REQUEST, src__=3D1)=0D
except: src=3D"Could not render the query template!"=0D
result=3D()=0D
t=3Dv=3Dtb=3DNone=0D
try:=0D
try:=0D
src, result=3Dself(REQUEST, test__=3D1)=0D
if find(src,'\0'):=0D
src=3Djoin(split(src,'\0'),'\n'+'-'*60+'\n')=0D
if result._searchable_result_columns():=0D
r=3Dcustom_default_report(self.id, result)=0D
else:=0D
r=3D'This statement returned no results.'=0D
except:=0D
t, v, tb =3D sys.exc_info()=0D
r=3D'<strong>Error, <em>%s</em>:</strong> %s' % (t, v)=0D
=0D
report=3DDocumentTemplate.HTML(=0D
'<html>\n'=0D
'<BODY BGCOLOR=3D"#FFFFFF" LINK=3D"#000099" VLINK=3D"#55555=
5">\n'=0D
'<dtml-var manage_tabs>\n<hr>\n%s\n\n'=0D
'<hr><strong>SQL used:</strong><br>\n<pre>\n%s\n</pre>\n<hr=
>\n'=0D
'</body></html>'=0D
% (r,src))=0D
=0D
report=3Dapply(report,(self,REQUEST),{self.id:result})=0D
=0D
if tb is not None:=0D
self.raise_standardErrorMessage(=0D
None, REQUEST, t, v, tb, None, report)=0D
=0D
return report=0D
=0D
finally: tb=3DNone=0D
=0D
def index_html(self, REQUEST):=0D
""" """=0D
REQUEST.RESPONSE.redirect("%s/manage_testForm" % REQUEST['URL1'])=
=0D
=0D
def _searchable_arguments(self): return self._arg=0D
=0D
def _searchable_result_columns(self): return self._col=0D
=0D
def _cached_result(self, DB__, query):=0D
''' get the cached sql, look up the result,=0D
if its not in the cache, add it in '''=0D
cache, tcache =3D self.__get_cache()=0D
=0D
# if the cache size is half of the max,=0D
# clean, otherwise carry on=0D
if len(cache) > self.max_cache_ / 2:=0D
self.__clean_cache(cache, tcache)=0D
=0D
now=3Dtime()=0D
t=3Dnow-self.cache_time_=0D
=0D
# pull result out of cache based on=0D
# query=0D
if cache.has_key(query):=0D
k, r =3D cache[query]=0D
if k > t: return r=0D
=0D
result=3Dapply(DB__.query, query)=0D
=0D
# update cache if necessary=0D
if self.cache_time_ > 0:=0D
tcache[int(now)]=3Dquery=0D
cache[query]=3D now, result=0D
=0D
return result=0D
=0D
def __get_cache(self):=0D
''' get the cached sql queries '''=0D
if hasattr(self,'_v_cache'): cache=3Dself._v_cache=0D
else: cache=3Dself._v_cache=3D{}, Bucket()=0D
return cache=0D
=0D
def __clean_cache(self, cache, tcache):=0D
''' clean the cache '''=0D
max_cache=3Dself.max_cache_=0D
now=3Dtime()=0D
t=3Dnow-self.cache_time_=0D
=0D
keys=3Dtcache.keys()=0D
keys.reverse()=0D
while keys and (len(keys) > max_cache or keys[-1] < t):=0D
key=3Dkeys[-1]=0D
q=3Dtcache[key]=0D
del tcache[key]=0D
if int(cache[q][0]) =3D=3D key:=0D
del cache[q]=0D
del keys[-1]=0D
=0D
return cache, tcache=0D
=0D
def __reset_cache(self):=0D
self._v_cache=3D{}, Bucket()=0D
=0D
def size_of_cache(self):=0D
''' No of items in the cache '''=0D
return len(self.__get_cache()[0])=0D
=0D
def clean_cache(self, REQUEST=3DNone):=0D
''' Cleans the cache by removing only invalid objects '''=0D
c, t =3D self.__get_cache()=0D
self.__clean_cache(c, t)=0D
if REQUEST:=0D
return self.manage_viewCache(self, REQUEST, manage_tabs_message=
=3D'Cache cleaned')=0D
=0D
def reset_cache(self, REQUEST=3DNone):=0D
''' Removes all items from the cache '''=0D
self.__reset_cache()=0D
if REQUEST:=0D
return self.manage_viewCache(self, REQUEST, manage_tabs_message=
=3D'Cache reset')=0D
=0D
def get_cache(self):=0D
''' Shows the cache '''=0D
c =3D self.__get_cache()[0] # cache=0D
l =3D [] # easily presentable version...=0D
now=3Dtime()=0D
=0D
for k, v in c.items():=0D
d =3D { =0D
'query':k[0], =0D
'expired': now > (self.cache_time_+int(v[0])), =0D
'secs_since_run':int(now-int(v[0])),=0D
}=0D
l.append(d)=0D
return l=0D
=0D
def __call__(self, REQUEST=3DNone, __ick__=3DNone, src__=3D0, test__=3D=
0, **kw):=0D
"""Call the database method=0D
=0D
The arguments to the method should be passed via keyword=0D
arguments, or in a single mapping object. If no arguments are=0D
given, and if the method was invoked through the Web, then the=0D
method will try to acquire and use the Web REQUEST object as=0D
the argument mapping.=0D
=0D
The returned value is a sequence of record objects.=0D
"""=0D
=0D
if REQUEST is None:=0D
if kw: REQUEST=3Dkw=0D
else:=0D
if hasattr(self, 'REQUEST'): REQUEST=3Dself.REQUEST=0D
else: REQUEST=3D{}=0D
=0D
try: dbc=3Dgetattr(self, self.connection_id)=0D
except AttributeError:=0D
raise AttributeError, (=0D
"The database connection <em>%s</em> cannot be found." % (=
=0D
self.connection_id))=0D
=0D
try: DB__=3Ddbc()=0D
except: raise 'Database Error', (=0D
'%s is not connected to a database' % self.id)=0D
=0D
if hasattr(self, 'aq_parent'):=0D
p=3Dself.aq_parent=0D
if self._isBeingAccessedAsZClassDefinedInstanceMethod():=0D
p=3Dp.aq_parent=0D
else: p=3DNone=0D
=0D
argdata=3Dself._argdata(REQUEST)=0D
argdata['sql_delimiter']=3D'\0'=0D
argdata['sql_quote__']=3Ddbc.sql_quote__=0D
=0D
for k,v in kw.items(): argdata[k] =3D v=0D
=0D
security=3DgetSecurityManager()=0D
security.addContext(self)=0D
try: query=3Dapply(self.template, (p,), argdata)=0D
finally: security.removeContext(self)=0D
=0D
if src__: return query=0D
=0D
if self.cache_time_ > 0 and self.max_cache_ > 0:=0D
result=3Dself._cached_result(DB__, (query, self.max_rows_))=0D
else: result=3DDB__.query(query, self.max_rows_)=0D
=0D
if hasattr(self, '_v_brain'): brain=3Dself._v_brain=0D
else:=0D
brain=3Dself._v_brain=3DgetBrain(self.class_file_, self.class_n=
ame_)=0D
=0D
zc=3Dself._zclass=0D
if zc is not None: zc=3Dzc._zclass_=0D
=0D
if type(result) is type(''):=0D
f=3DStringIO()=0D
f.write(result)=0D
f.seek(0)=0D
result=3DRDB.File(f,brain,p, zc)=0D
else:=0D
result=3DResults(result, brain, p, zc)=0D
columns=3Dresult._searchable_result_columns()=0D
if test__ and columns !=3D self._col: self._col=3Dcolumns=0D
=0D
# If run in test mode, return both the query and results so=0D
# that the template doesn't have to be rendered twice!=0D
if test__: return query, result=0D
=0D
return result=0D
=0D
def da_has_single_argument(self): return len(self._arg)=3D=3D1=0D
=0D
def __getitem__(self, key):=0D
args=3Dself._arg=0D
if self.allow_simple_one_argument_traversal and len(args)=3D=3D1:=
=0D
results=3Dself({args.keys()[0]: key})=0D
if results:=0D
if len(results) > 1: raise KeyError, key=0D
else: raise KeyError, key=0D
r=3Dresults[0]=0D
# if hasattr(self, 'aq_parent'): r=3Dr.__of__(self.aq_parent)=
=0D
return r=0D
=0D
self._arg[key] # raise KeyError if not an arg=0D
return Traverse(self,{},key)=0D
=0D
def connectionIsValid(self):=0D
return (hasattr(self, self.connection_id) and=0D
hasattr(getattr(self, self.connection_id), 'connected'))=0D
=0D
def connected(self):=0D
return getattr(getattr(self, self.connection_id), 'connected')()=0D
=0D
=0D
def manage_product_zclass_info(self):=0D
r=3D[]=0D
Z=3Dself._zclass=0D
Z=3Dgetattr(Z, 'aq_self', Z)=0D
for d in self.aq_acquire('_getProductRegistryData')('zclasses'):=0D
z=3Dd['meta_class']=0D
if hasattr(z._zclass_,'_p_deactivate'):=0D
# Eek, persistent=0D
continue=0D
x=3D{}=0D
x.update(d)=0D
x['selected'] =3D (z is Z) and 'selected' or ''=0D
del x['meta_class']=0D
r.append(x)=0D
=0D
return r =0D
=0D
=0D
=0D
Globals.default__class_init__(DA)=0D
=0D
=0D
=0D
ListType=3Dtype([])=0D
class Traverse(ExtensionClass.Base):=0D
"""Helper class for 'traversing' searches during URL traversal=0D
"""=0D
_r=3DNone=0D
_da=3DNone=0D
=0D
def __init__(self, da, args, name=3DNone):=0D
self._da=3Dda=0D
self._args=3Dargs=0D
self._name=3Dname=0D
=0D
def __bobo_traverse__(self, REQUEST, key):=0D
name=3Dself._name=0D
da=3Dself.__dict__['_da']=0D
args=3Dself._args=0D
if name:=0D
if args.has_key(name):=0D
v=3Dargs[name]=0D
if type(v) is not ListType: v=3D[v]=0D
v.append(key)=0D
key=3Dv=0D
=0D
args[name]=3Dkey=0D
=0D
if len(args) < len(da._arg): =0D
return self.__class__(da, args)=0D
key=3Dself # "consume" key=0D
=0D
elif da._arg.has_key(key): return self.__class__(da, args, key)=0D
=0D
results=3Dda(args)=0D
if results:=0D
if len(results) > 1:=0D
try: return results[atoi(key)].__of__(da)=0D
except: raise KeyError, key=0D
else: raise KeyError, key=0D
r=3Dresults[0]=0D
# if hasattr(da, 'aq_parent'): r=3Dr.__of__(da.aq_parent)=0D
self._r=3Dr=0D
=0D
if key is self: return r=0D
=0D
if hasattr(r,'__bobo_traverse__'):=0D
try: return r.__bobo_traverse__(REQUEST, key)=0D
except: pass=0D
=0D
try: return getattr(r,key)=0D
except AttributeError, v:=0D
if str(v) !=3D key: raise AttributeError, v=0D
=0D
return r[key]=0D
=0D
def __getattr__(self, name):=0D
r=3Dself.__dict__['_r']=0D
if hasattr(r, name): return getattr(r,name)=0D
return getattr(self.__dict__['_da'], name)=0D
=0D
------=_NextPart_000_0630_01C1618C.916781C0
Content-Type: application/octet-stream;
name="viewCache.dtml"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;
filename="viewCache.dtml"
<dtml-var manage_page_header>=0D
<dtml-var manage_tabs>=0D
=0D
<p class=3D"form-help">This shows you the current contents of a ZSQL Method=
cache.</p>=0D
<p>Cache size: <b><dtml-var size_of_cache></b> =0D
[ <a href=3D"clean_cache">Clean</a> ] =0D
[ <a href=3D"reset_cache">Reset</a> ] =0D
</p>=0D
=0D
<table cellpadding=3D"2" cellspacing=3D"0">=0D
<tr class=3D"list-header"><td>Status</td><td>Query</td><td>Last run (se=
cs)</td></tr>=0D
<dtml-in get_cache>=0D
<dtml-with sequence-item mapping>=0D
<tr class=3D"row-<dtml-if sequence-odd>hilite<dtml-else>normal</dtml-if=
>">=0D
<td>=0D
<dtml-if expired>Expired<dtml-else>Ok</dtml-if>=0D
</td>=0D
<td>&dtml-query;</td>=0D
<td>&dtml-secs_since_run;</td>=0D
</tr>=0D
</dtml-with>=0D
<dtml-else>=0D
<tr class=3D"row-normal"><td colspan=3D"3">Cache is empty</td></tr>=0D
</dtml-in get_cache>=0D
</table>=0D
=0D
<dtml-var manage_page_footer>=0D
=0D
------=_NextPart_000_0630_01C1618C.916781C0--