Commit ad2ffcd6 authored by Andreas Jung's avatar Andreas Jung

(re)moved out of the core (see Products.ZGadflyDA)

parent 329fe167
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors. All Rights Reserved.
#
# 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
#
##############################################################################
database_type='Gadfly'
__doc__='''%s Database Connection
$Id$''' % database_type
__version__='$Revision: 1.16 $'[11:-2]
from db import DB, manage_DataSources
import sys, DABase, Globals
import Shared.DC.ZRDB.Connection, ThreadLock
from zExceptions import BadRequest
_Connection=Shared.DC.ZRDB.Connection.Connection
_connections={}
_connections_lock=ThreadLock.allocate_lock()
data_sources=manage_DataSources
addConnectionForm=Globals.HTMLFile('dtml/connectionAdd',globals())
def manage_addZGadflyConnection(
self, id, title, connection, check=None, REQUEST=None):
"""Add a DB connection to a folder"""
# Note - type checking is taken care of by _setObject
# and the Connection object constructor.
self._setObject(id, Connection(
id, title, connection, check))
if REQUEST is not None: return self.manage_main(self,REQUEST)
class Connection(DABase.Connection):
" "
database_type=database_type
id='%s_database_connection' % database_type
meta_type=title='Z %s Database Connection' % database_type
icon='misc_/Z%sDA/conn' % database_type
manage_properties=Globals.HTMLFile('dtml/connectionEdit', globals(),
data_sources=data_sources)
def connected(self):
if hasattr(self, '_v_database_connection'):
return self._v_database_connection.opened
return ''
def title_and_id(self):
s=_Connection.inheritedAttribute('title_and_id')(self)
if (hasattr(self, '_v_database_connection') and
self._v_database_connection.opened):
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_and_id')(self)
if (hasattr(self, '_v_database_connection') and
self._v_database_connection.opened):
s="%s (connected)" % s
else:
s="%s (<font color=red> not connected</font>)" % s
return s
def connect(self,s):
_connections_lock.acquire()
try:
c=_connections
if c.has_key(s):
c=self._v_database_connection=c[s]
if not c.opened: c.open()
return self
try:
try:
self._v_database_connection=c[s]=DB(s)
except:
t, v, tb = sys.exc_info()
raise BadRequest, (
'<strong>Invalid connection string: </strong>'
'<CODE>%s</CODE><br>\n'
'<!--\n%s\n%s\n-->\n'
% (s,t,v)), tb
finally: tb=None
return self
finally:
_connections_lock.release()
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors. All Rights Reserved.
#
# 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__='''Database Connection
$Id$'''
__version__='$Revision: 1.12 $'[11:-2]
from db import manage_DataSources
import Shared.DC.ZRDB.Connection, sys
from Globals import HTMLFile
from ExtensionClass import Base
import Acquisition
class Connection(Shared.DC.ZRDB.Connection.Connection):
_isAnSQLConnection=1
manage_options=Shared.DC.ZRDB.Connection.Connection.manage_options+(
{'label': 'Browse', 'action':'manage_browse'},
# {'label': 'Design', 'action':'manage_tables'},
)
manage_tables=HTMLFile('dtml/tables',globals())
manage_browse=HTMLFile('dtml/browse',globals())
info=None
def tpValues(self):
#if hasattr(self, '_v_tpValues'): return self._v_tpValues
r=[]
# self._v_tables=tables=TableBrowserCollection()
#tables=tables.__dict__
c=self._v_database_connection
try:
for d in c.tables(rdb=0):
try:
name=d['TABLE_NAME']
b=TableBrowser()
b.__name__=name
b._d=d
b._c=c
# b._columns=c.columns(name)
try: b.icon=table_icons[d['TABLE_TYPE']]
except: pass
r.append(b)
# tables[name]=b
except:
# print d['TABLE_NAME'], sys.exc_type, sys.exc_value
pass
finally: pass #print sys.exc_type, sys.exc_value
#self._v_tpValues=r
return r
def __getitem__(self, name):
if name=='tableNamed':
if not hasattr(self, '_v_tables'): self.tpValues()
return self._v_tables.__of__(self)
raise KeyError, name
def manage_wizard(self, tables):
" "
def manage_join(self, tables, select_cols, join_cols, REQUEST=None):
"""Create an SQL join"""
def manage_insert(self, table, cols, REQUEST=None):
"""Create an SQL insert"""
def manage_update(self, table, keys, cols, REQUEST=None):
"""Create an SQL update"""
class TableBrowserCollection(Acquisition.Implicit):
"Helper class for accessing tables via URLs"
class Browser(Base):
def __getattr__(self, name):
try: return self._d[name]
except KeyError: raise AttributeError, name
class TableBrowser(Browser, Acquisition.Implicit):
icon='what'
Description=check=''
info=HTMLFile('dtml/table_info',globals())
menu=HTMLFile('dtml/table_menu',globals())
def tpValues(self):
r=[]
tname=self.__name__
for d in self._c.columns(tname):
b=ColumnBrowser()
b._d=d
try: b.icon=field_icons[d['Type']]
except: pass
b.TABLE_NAME=tname
r.append(b)
return r
def tpId(self): return self._d['TABLE_NAME']
def tpURL(self): return "Table/%s" % self._d['TABLE_NAME']
def Name(self): return self._d['TABLE_NAME']
def Type(self): return self._d['TABLE_TYPE']
manage_designInput=HTMLFile('dtml/designInput',globals())
def manage_buildInput(self, id, source, default, REQUEST=None):
"Create a database method for an input form"
args=[]
values=[]
names=[]
columns=self._columns
for i in range(len(source)):
s=source[i]
if s=='Null': continue
c=columns[i]
d=default[i]
t=c['Type']
n=c['Name']
names.append(n)
if s=='Argument':
values.append("<dtml-sql-value %s type=%s>'" %
(n, vartype(t)))
a='%s%s' % (n, boboType(t))
if d: a="%s=%s" % (a,d)
args.append(a)
elif s=='Property':
values.append("<dtml-sql-value %s type=%s>'" %
(n, vartype(t)))
else:
if isStringType(t):
if find(d,"\'") >= 0: d=join(split(d,"\'"),"''")
values.append("'%s'" % d)
elif d:
values.append(str(d))
else:
raise ValueError, (
'no default was given for <em>%s</em>' % n)
class ColumnBrowser(Browser):
icon='field'
def check(self):
return ('\t<input type=checkbox name="%s.%s">' %
(self.TABLE_NAME, self._d['Name']))
def tpId(self): return self._d['Name']
def tpURL(self): return "Column/%s" % self._d['Name']
def Description(self):
d=self._d
if d['Scale']:
return " %(Type)s(%(Precision)s,%(Scale)s) %(Nullable)s" % d
else:
return " %(Type)s(%(Precision)s) %(Nullable)s" % d
table_icons={
'TABLE': 'table',
'VIEW':'view',
'SYSTEM_TABLE': 'stable',
}
field_icons={
'BIGINT': 'int',
'BINARY': 'bin',
'BIT': 'bin',
'CHAR': 'text',
'DATE': 'date',
'DECIMAL': 'float',
'DOUBLE': 'float',
'FLOAT': 'float',
'INTEGER': 'int',
'LONGVARBINARY': 'bin',
'LONGVARCHAR': 'text',
'NUMERIC': 'float',
'REAL': 'float',
'SMALLINT': 'int',
'TIME': 'time',
'TIMESTAMP': 'datetime',
'TINYINT': 'int',
'VARBINARY': 'bin',
'VARCHAR': 'text',
}
ZGadflyDA
The ZGadflyDA product provides support for Gadfly database adapter
objects in the Zope environment.
Using Multiple Databases with ZGadflyDA
The ZGadflyDA is intended mainly for demonstration purposes.
It automatically creates a 'gadfly' directory in your Zope var directory
with a 'demo' subdirectory, which provides a demo database.
To add or use additional databases, simply add (or link) additional
directories to your 'var/gadfly' directory.
#install DA.py
#install DABase.py
#install __init__.py
#install browse.dtml
#install connectionAdd.dtml
#install db.py
#install gadfly
#install icons
#install table_info.dtml
#install table_menu.dtml
#install tables.dtml
#install CHANGES.txt
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors. All Rights Reserved.
#
# 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.
#
##############################################################################
"""Generic Database Adapter Package Registration.
$Id$
"""
# Don't warn when the product is imported by the startup code
import sys
if sys._getframe(1).f_code.co_name != 'import_product': # OFS.Application
import warnings
warnings.warn('Using Gadfly and ZGadflyDA is deprecated. '
'The module will be removed in Zope 2.11)',
DeprecationWarning,
stacklevel=2)
import Globals, os
classes=('DA.Connection',)
database_type='Gadfly'
class GadflyError(Exception):
pass
class QueryError(GadflyError):
pass
misc_={'conn':
Globals.ImageFile('Shared/DC/ZRDB/www/DBAdapterFolder_icon.gif')}
for icon in ('table', 'view', 'stable', 'what',
'field', 'text','bin','int','float',
'date','time','datetime'):
misc_[icon]=Globals.ImageFile('icons/%s.gif' % icon, globals())
DA=None
def getDA():
global DA
if DA is None:
home=Globals.package_home(globals())
from gadfly import sqlwhere
sqlwhere.filename="%s/gadfly/sql.mar" % home
import DA
return DA
getDA()
__module_aliases__=(
('Products.AqueductGadfly.DA', DA),
)
def manage_addZGadflyConnectionForm(self, REQUEST, *args, **kw):
" "
DA=getDA()
return DA.addConnectionForm(
self, self, REQUEST,
database_type=database_type,
data_sources=DA.data_sources)
def manage_addZGadflyConnection(
self, id, title, connection, check=None, REQUEST=None):
" "
return getDA().manage_addZGadflyConnection(
self, id, title, connection, check, REQUEST)
def initialize(context):
context.registerClass(
DA.Connection,
permission='Add Z Gadfly Database Connections',
constructors=(manage_addZGadflyConnectionForm,
manage_addZGadflyConnection),
legacy=(manage_addZGadflyConnectionForm,
manage_addZGadflyConnection),
)
# from App.config import getConfiguration
# j=os.path.join
# d=j(getConfiguration().clienthome,'gadfly')
# if not os.path.exists(d):
# os.mkdir(d)
# os.mkdir(j(d,'demo'))
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors. All Rights Reserved.
#
# 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
#
##############################################################################
'''$Id$'''
__version__='$Revision: 1.14 $'[11:-2]
import os
from string import strip, split
import gadfly
import Globals, Shared.DC.ZRDB.THUNK
from DateTime import DateTime
data_dir=os.path.join(Globals.data_dir,'gadfly')
from Products.ZGadflyDA import GadflyError, QueryError
def manage_DataSources():
if not os.path.exists(data_dir):
try:
os.mkdir(data_dir)
os.mkdir(os.path.join(data_dir,'demo'))
except:
raise GadflyError, (
"""
The Zope Gadfly Database Adapter requires the
existence of the directory, <code>%s</code>. An error
occurred while trying to create this directory.
""" % data_dir)
if not os.path.isdir(data_dir):
raise GadflyError, (
"""
The Zope Gadfly Database Adapter requires the
existence of the directory, <code>%s</code>. This
exists, but is not a directory.
""" % data_dir)
return map(
lambda d: (d,''),
filter(lambda f, i=os.path.isdir, d=data_dir, j=os.path.join:
i(j(d,f)),
os.listdir(data_dir))
)
class DB(Shared.DC.ZRDB.THUNK.THUNKED_TM):
database_error=gadfly.error
opened=''
def tables(self,*args,**kw):
if self.db is None: self.open()
return map(
lambda name: {
'TABLE_NAME': name,
'TABLE_TYPE': 'TABLE',
},
filter(self.db.database.datadefs.has_key, self.db.table_names())
)
def columns(self, table_name):
if self.db is None: self.open()
return map(lambda col: {
'Name': col.colid, 'Type': col.datatype, 'Precision': 0,
'Scale': 0, 'Nullable': 'with Null'
}, self.db.database.datadefs[table_name].colelts)
def open(self):
connection=self.connection
path=os.path
dir=path.join(data_dir,connection)
if not path.isdir(dir):
raise self.database_error, 'invalid database error, ' + connection
if not path.exists(path.join(dir,connection+".gfd")):
db=gadfly.gadfly()
db.startup(connection,dir)
else: db=gadfly.gadfly(connection,dir)
self.db=db
self.opened=DateTime()
def close(self):
self.db=None
del self.opened
def __init__(self,connection):
self.connection=connection
self.open()
def query(self,query_string, max_rows=9999999):
if self.db is None: self.open()
self._register()
c=self.db.cursor()
queries=filter(None, map(strip,split(query_string, '\0')))
if not queries: raise QueryError, 'empty query'
desc=None
result=[]
for qs in queries:
c.execute(qs)
d=c.description
if d is None: continue
if desc is None: desc=d
elif d != desc:
raise QueryError, (
'Multiple incompatible selects in '
'multiple sql-statement query'
)
if not result: result=c.fetchmany(max_rows)
elif len(result) < max_rows:
result=result+c.fetchmany(max_rows-len(result))
if desc is None: return (),()
items=[]
for name, type, width, ds, p, scale, null_ok in desc:
if type=='NUMBER':
if scale==0: type='i'
else: type='n'
elif type=='DATE':
type='d'
else: type='s'
items.append({
'name': name,
'type': type,
'width': width,
'null': null_ok,
})
return items, result
# Gadfly needs the extra checkpoint call.
def _abort(self):
self.db.rollback()
self.db.checkpoint()
<dtml-var manage_page_header>
<dtml-var manage_tabs>
<dtml-tree header=info>
<IMG SRC="&dtml-BASEPATH1;/misc_/ZGadflyDA/&dtml-icon;"
ALT="&dtml-Type;" BORDER="0">
&dtml-Name;&dtml-Description;
</dtml-tree>
<dtml-var manage_page_footer>
<dtml-var manage_page_header>
<dtml-var "manage_form_title(this(), _,
form_title='Add Z %s Database Connection' % database_type,
)">
<dtml-if data_sources>
<form action="manage_addZ&dtml-database_type;Connection" 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">
Data Source
</div>
</td>
<td align="left" valign="top">
<div class="form-text">
<em>
Additional data sources may be created by making additional
directories in the <code>var/gadfly</code> subdirectory of
your Zope installation.
</em>
</div>
<div class="form-element">
<select name="connection" size="5">
<dtml-in data_sources>
<option value="&dtml-sequence-key;">&dtml-sequence-key;<dtml-if sequence-item>, &dtml-sequence-item;</dtml-if></option>
</dtml-in>
</select>
</div>
</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">
<input class="form-element" type="submit" name="submit"
value=" Add " />
</div>
</td>
</tr>
</table>
</form>
<dtml-else>
<p class="form-help">
Sorry, you cannot create any Zope &dtml-database_type; Database
Connections because no &dtml-database_type; databases exist, or
all of the existing databases are in use.
</p>
</dtml-if>
<hr>
<p class="form-help">
<em><strong>Note:</strong>
The Zope Gadfly product is a free Zope database adapter intended
for demonstration purposes only. It is only suitable for learning
about Zope SQL Methods. Database operations are performed in memory, so it
should not be used to create large databases. This installation is using a
non-optimized version of Gadfly and should not be used to assess
the performance of either Gadfly or Zope.</em>
</p>
<p class="form-help">
The Zope Gadfly Product uses the
<a href="http://starship.skyport.net/crew/aaron_watters/kwParsing/">
Gadfly SQL Database Engine</a> by Aaron Waters. See the Gadfly
copyright below.
</p>
<h3>Gadfly Copyright Notice</h3>
<p>The kjParsing source is copyrighted, but you can freely use and copy it
as long as you don't change or remove the copyright:</p>
<p>Copyright Aaron Robert Watters, 1994</p>
<p><center>All Rights Reserved</center></p>
<p>Permission to use, copy, modify, and distribute this software and its
documentation for any purpose and without fee is hereby granted,
provided that the above copyright notice appears in all copies and that
both that copyright notice and this permission notice appear in
supporting documentation.</p>
<p>AARON ROBERT WATTERS DISCLAIMS ALL WARRANTIES WITH REGARD TO
THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS, IN NO EVENT SHALL AARON ROBERT WATTERS BE LIABLE
FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.</p>
<h4>Signature</h4>
<pre>
Aaron Robert Watters
Department of Computer and Information Sciences
New Jersey Institute of Technology
University Heights
Newark, NJ 07102
phone (201)596-2666
fax (201)596-5777
home phone (908)545-3367
email: aaron@vienna.njit.edu
</pre>
<dtml-var manage_page_footer>
<dtml-var manage_page_header>
<dtml-var manage_tabs>
<form action="manage_edit" 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">
<div class="form-text">
&dtml-id;
</div>
</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">
Data Source
</div>
</td>
<td align="left" valign="top">
<div class="form-text">
<em>
Additional data sources may be created by making additional
directories in the <code>var/gadfly</code> subdirectory of
your Zope installation.
</em>
</div>
<div class="form-element">
<select name="connection_string" size="5">
<dtml-in data_sources>
<option value="&dtml-sequence-key;"<dtml-if
"_['sequence-key']==connection_string"> selected</dtml-if>>&dtml-sequence-key;<dtml-if sequence-item>, &dtml-sequence-item;</dtml-if></option>
</dtml-in>
</select>
</div>
</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">
<input class="form-element" type="submit" name="submit"
value="Save Changes" />
</div>
</td>
</tr>
</table>
</form>
<dtml-var manage_page_footer>
<dtml-var standard_html_header>
&dtml-TABLE_TYPE;<dtml-if TABLE_OWNER>
owned by &dtml-TABLE_OWNER;</dtml-if>
<dtml-if REMARKS><br>&dtml-REMARKS;</dtml-if>
<dtml-var standard_html_footer>
<dtml-var standard_html_header>
<a href="tableNamed/&dtml.url_quote-Name;/manage_designInput">Design Input *</a>
<a href="tableNamed/&dtml.url_quote-Name;/manage_designUpdate">Design Update *</a>
<a href="tableNamed/&dtml.url_quote-Name;/manage_designDelete">Design Delete</a>
<dtml-var standard_html_footer>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html lang="en">
<head><title>&dtml-title_or_id; tables</title></head>
<body bgcolor="#FFFFFF" link="#000099" vlink="#555555" alink="#77003B">
<dtml-var manage_tabs>
<h2>&dtml-title_or_id; tables</h2>
<form action="manage_wizard" method="POST">
<table cellspacing="2">
<tr>
<th align="LEFT" valign="TOP">Available tables</th>
<td align="LEFT" valign="TOP">
<select name="tables:list" size=9 multiple>
<dtml-in table_info>
<option value="&dtml-sequence-key;">
&dtml-sequence-key; &dtml-sequence-item;
</option>
</dtml-in>
</select>
</td>
</tr>
<tr>
<th align="LEFT" valign="TOP">Statement type</th>
<td align="LEFT" valign="TOP">
<select name="statement">
<option>SELECT</option>
<option>INSERT</option>
<option>UPDATE</option>
</select>
</td>
</tr>
<tr>
<td></td>
<td><br><input type="SUBMIT" value="Generate SQL"></td>
</tr>
</table>
</form>
</body>
</html>
The following copyright is modified from the python copyright.
Copyright Notice
----------------
The kjParsing source is copyrighted, but you can freely use and copy it
as long as you don't change or remove the copyright:
Copyright Aaron Robert Watters, 1994
All Rights Reserved
Permission to use, copy, modify, and distribute this software and its
documentation for any purpose and without fee is hereby granted,
provided that the above copyright notice appears in all copies and that
both that copyright notice and this permission notice appear in
supporting documentation.
AARON ROBERT WATTERS DISCLAIMS ALL WARRANTIES WITH REGARD TO
THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS, IN NO EVENT SHALL AARON ROBERT WATTERS BE LIABLE
FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
Signature
---------
Aaron Robert Watters
Department of Computer and Information Sciences
New Jersey Institute of Technology
University Heights
Newark, NJ 07102
phone (201)596-2666
fax (201)596-5777
home phone (908)545-3367
email: aaron@vienna.njit.edu
# Grammar generation
# for lisp lists with strings, ints, vars, print, and setq
# set this variable to regenerate the grammar on each load
REGENERATEONLOAD = 1
import string
GRAMMARSTRING ="""
Value :: ## indicates Value is the root nonterminal for the grammar
@R SetqRule :: Value >> ( setq var Value )
@R ListRule :: Value >> ( ListTail
@R TailFull :: ListTail >> Value ListTail
@R TailEmpty :: ListTail >> )
@R Varrule :: Value >> var
@R Intrule :: Value >> int
@R Strrule :: Value >> str
@R PrintRule :: Value >> ( print Value )
"""
COMPILEDFILENAME = "TESTLispG.py"
MARSHALLEDFILENAME = "TESTLispG.mar"
LISPCOMMENTREGEX = ";.*"
INTREGEX = "["+string.digits+"]+"
STRREGEX = '"[^\n"]*"'
VARREGEX = "["+string.letters+"]["+string.letters+string.digits+"]*"
### declare interpretation functions and regex's for terminals
def intInterp( str ):
return string.atoi(str)
def stripQuotes( str ):
return str[1:len(str)-1]
def echo(string):
return string
def DeclareTerminals(Grammar):
Grammar.Addterm("int", INTREGEX, intInterp)
Grammar.Addterm("str", STRREGEX, stripQuotes)
Grammar.Addterm("var", VARREGEX, echo)
### declare the rule reduction interpretation functions.
def EchoValue( list, Context ):
return list[0]
def VarValue( list, Context ):
varName = list[0]
if Context.has_key(varName):
return Context[varName]
else:
raise NameError, "no such lisp variable in context "+varName
def NilTail( list, Context ):
return []
def AddToList( list, Context ):
return [ list[0] ] + list[1]
def MakeList( list, Context ):
return list[1]
def DoSetq( list, Context):
Context[ list[2] ] = list[3]
return list[3]
def DoPrint( list, Context ):
print list[2]
return list[2]
def BindRules(Grammar):
Grammar.Bind( "Intrule", EchoValue )
Grammar.Bind( "Strrule", EchoValue )
Grammar.Bind( "Varrule", VarValue )
Grammar.Bind( "TailEmpty", NilTail )
Grammar.Bind( "TailFull", AddToList )
Grammar.Bind( "ListRule", MakeList )
Grammar.Bind( "SetqRule", DoSetq )
Grammar.Bind( "PrintRule", DoPrint )
# This function generates the grammar and dumps it to a file.
def GrammarBuild():
import kjParseBuild
LispG = kjParseBuild.NullCGrammar()
LispG.SetCaseSensitivity(0) # grammar is not case sensitive for keywords
DeclareTerminals(LispG)
LispG.Keywords("setq print")
LispG.punct("().")
LispG.Nonterms("Value ListTail")
LispG.comments([LISPCOMMENTREGEX])
LispG.Declarerules(GRAMMARSTRING)
LispG.Compile()
print "dumping as python to "+COMPILEDFILENAME
outfile = open(COMPILEDFILENAME, "w")
LispG.Reconstruct("LispG",outfile,"GRAMMAR")
outfile.close()
print "dumping as binary to "+MARSHALLEDFILENAME
outfile = open(MARSHALLEDFILENAME, "w")
LispG.MarshalDump(outfile)
outfile.close()
BindRules(LispG)
return LispG
# this function initializes the compiled grammar from the generated file.
def LoadLispG():
import TESTLispG
# reload to make sure we get the most recent version!
# (only needed when debugging the grammar).
reload(TESTLispG)
LispG = TESTLispG.GRAMMAR()
DeclareTerminals(LispG)
BindRules(LispG)
return LispG
def unMarshalLispG():
import kjParser
infile = open(MARSHALLEDFILENAME, "r")
LispG = kjParser.UnMarshalGram(infile)
infile.close()
DeclareTerminals(LispG)
BindRules(LispG)
return LispG
########## test the grammar generation
if REGENERATEONLOAD:
print "(re)generating the LispG grammar in file TESTLispG.py"
Dummy = GrammarBuild()
print "(re)generation done."
print "loading grammar as python"
LispG = LoadLispG()
### declare an initial context, and do some tests.
Context = { 'x':3 }
test1 = LispG.DoParse1( '()', Context)
test2 = LispG.DoParse1( '(123)', Context)
test3 = LispG.DoParse1( '(x)', Context)
test4 = LispG.DoParse1( '" a string "', Context)
test5 = LispG.DoParse1( '(setq y (1 2 3) )', Context )
test6 = LispG.DoParse1( '(SeTq x ("a string" "another" 0))', Context )
test7str = """
; this is a lisp comment
(setq abc (("a" x)
("b" (setq d 12))
("c" y) ) ; another lisp comment
)
"""
test7 = LispG.DoParse1( test7str, Context)
test8 = LispG.DoParse1( '(print (1 x d))', Context)
print "unmarshalling the grammar"
LispG2 = unMarshalLispG()
### declare an initial context, and do some tests.
Context = { 'x':3 }
test1 = LispG2.DoParse1( '()', Context)
test2 = LispG2.DoParse1( '(123)', Context)
test3 = LispG2.DoParse1( '(x)', Context)
test4 = LispG2.DoParse1( '" a string "', Context)
test5 = LispG2.DoParse1( '(setq y (1 2 3) )', Context )
test6 = LispG2.DoParse1( '(SeTq x ("a string" "another" 0))', Context )
test7str = """
; this is a lisp comment
(setq abc (("a" x)
("b" (setq d 12))
("c" y) ) ; another lisp comment
)
"""
test7 = LispG2.DoParse1( test7str, Context)
test8 = LispG2.DoParse1( '(print (1 x d))', Context)
#
# test for kjParseBuild module automatic parser generation
#
# lisp lists with strings, ints, vars, and setq
import string
### The string representation for the grammar.
### Since this is used only by GrammarBuild()
### it could be put in a separate file with GrammarBuild()
### to save space/load time after Grammar compilation.
###
GRAMMARSTRING ="""
Value :: ## indicates Value is the root nonterminal for the grammar
@R SetqRule :: Value >> ( setq var Value )
@R ListRule :: Value >> ( ListTail
@R TailFull :: ListTail >> Value ListTail
@R TailEmpty :: ListTail >> )
@R Varrule :: Value >> var
@R Intrule :: Value >> int
@R Strrule :: Value >> str
"""
### the name of the file in which to create the compiled
### grammar declarations
COMPILEDFILENAME = "TESTLispG2.py"
### declare comment form(s) as regular expressions
LISPCOMMENTREGEX = ";.*"
### declare regular expression string constants for terminals
#integer terminal:::::::
INTREGEX = "["+string.digits+"]+"
#string terminal::::::::
STRREGEX = '"[^\n"]*"'
#var terminal::::::::
VARREGEX = "["+string.letters+"]["+string.letters+string.digits+"]*"
### declare interpretation functions for terminals
# int interpretation function: translates string to int:
# Could use string.atoi without the extra level of indirection
# but for demo purposes here it is.
#
def intInterp( str ):
return string.atoi(str)
# interpretation function for strings strips off the surrounding quotes.
def stripQuotes( str ):
if len(str)<2:
TypeError, "string too short?"
return str[1:len(str)-1]
# interpretation function for vars just returns the recognized string
def echo(string):
return string
# This function declares the nonterminals both in the
# "grammar generation phase" and in loading the compiled
# grammar after generation
#
def DeclareTerminals(Grammar):
Grammar.Addterm("int", INTREGEX, intInterp)
Grammar.Addterm("str", STRREGEX, stripQuotes)
Grammar.Addterm("var", VARREGEX, echo)
### declare the rule reduction interpretation functions.
# EchoValue() serves for Intrule and Strrule, since
# we just want to echo the value returned by the
# respective terminal interpretation functions.
#
# Parser delivers list of form [ interpreted_value ]
def EchoValue( list, Context ):
if len(list)!=1:
raise TypeError, "this shouldn't happen! (1)"
return list[0]
# for Varrule interpreter must try to look up the value
# in the Context dictionary
#
# Parser delivers list of form [ var_name ]
def VarValue( list, Context ):
if len(list)!=1:
raise TypeError, "Huh? (2)"
varName = list[0]
if Context.has_key(varName):
return Context[varName]
else:
raise NameError, "no such lisp variable in context "+varName
# for an empty tail, return the empty list
#
# Parser delivers list of form [")"]
def NilTail( list, Context ):
if len(list) != 1 or list[0] != ")":
return TypeError, "Bad reduction?"
return []
# For a full tail, add the new element to the front of the list
#
# Parser delivers list of form [Value, TailValue]
def AddToList( list, Context ):
if len(list) !=2:
return TypeError, "Bad reduction?"
return [ list[0] ] + list[1]
# For a list, simply return the list determined by the tail
#
# Parser delivers list of form ["(", TailValue ]
def MakeList( list, Context ):
if len(list)!=2 or list[0]!="(":
raise TypeError, "Bad reduction? (3)"
return list[1]
# For a setq, declare a new variable in the Context dictionary
#
# Parser delivers list of form # ["(", "setq", varName, Value, ")"]
def DoSetq( list, Context):
if len(list) != 5\
or list[0] != "("\
or list[1] != "setq"\
or list[4] != ")":
print list
raise TypeError, "Bad reduction? (4)"
VarName = list[2]
if type(VarName) != type(''):
raise TypeError, "Bad var name? (5)"
Value = list[3]
# add or set the variable in the Context dictionary
Context[ VarName ] = Value
return Value
# This function Binds the named rules of the Grammar string to their
# interpretation functions in a Grammar.
#
def BindRules(Grammar):
Grammar.Bind( "Intrule", EchoValue )
Grammar.Bind( "Strrule", EchoValue )
Grammar.Bind( "Varrule", VarValue )
Grammar.Bind( "TailEmpty", NilTail )
Grammar.Bind( "TailFull", AddToList )
Grammar.Bind( "ListRule", MakeList )
Grammar.Bind( "SetqRule", DoSetq )
# This function generates the grammar and dumps it to a file.
# Since it will be used only once (after debugging),
# it probably should be put in another file save memory/load-time.
#
# the result returned is a Grammar Object that can be used
# for testing/debugging purposes.
#
# (maybe this should be made into a generic function?)
def GrammarBuild():
import kjParseBuild
# initialize a Null compilable grammar to define
LispG = kjParseBuild.NullCGrammar()
# declare terminals for the grammar
DeclareTerminals(LispG)
# declare the keywords for the grammar
# defun is not used, included here for demo purposes only
LispG.Keywords("setq defun")
# Declare punctuations
# dot is not used here
LispG.punct("().")
# Declare Nonterms
LispG.Nonterms("Value ListTail")
# Declare comment forms
LispG.comments([LISPCOMMENTREGEX])
# Declare rules
LispG.Declarerules(GRAMMARSTRING)
# Compile the grammar
LispG.Compile()
# Write the grammar to a file except for
# the function bindings (which must be rebound)
outfile = open(COMPILEDFILENAME, "w")
LispG.Reconstruct("LispG",outfile,"GRAMMAR")
outfile.close()
# for debugging purposes only, bind the rules
# in the generated grammar
BindRules(LispG)
# return the generated Grammar
return LispG
# this function initializes the compiled grammar from
# generated file.
def LoadLispG():
import TESTLispG2
# make sure we have most recent version (during debugging)
reload(TESTLispG2)
# evaluate the grammar function from generated file
LispG = TESTLispG2.GRAMMAR()
# bind the semantics functions
DeclareTerminals(LispG)
BindRules(LispG)
return LispG
########## test grammar generation
# do generation
Dummy = GrammarBuild()
# load the grammar from the file as LispG
LispG = LoadLispG()
# declare an initial context, and do some tests.
Context = { "x":3 }
test1 = LispG.DoParse1( "()", Context)
test2 = LispG.DoParse1( "(123)", Context)
test3 = LispG.DoParse1( "(x)", Context)
test4 = LispG.DoParse1( '" a string "', Context)
test5 = LispG.DoParse1( "(setq y (1 2 3) )", Context )
test6 = LispG.DoParse1( '(setq x ("a string" "another" 0))', Context )
test7str = """
; this is a lisp comment
(setq abc (("a" x)
("b" (setq d 12))
("c" y) ) ; another lisp comment
)
"""
test7 = LispG.DoParse1( test7str, Context)
# this was used for debugging null productions (a nearly full sql grammar
# is available on request).
#set this to automatically rebuild the grammar.
REBUILD = 1
MARSHALFILE = "SQLTEST.mar"
SELECTRULES = """
## highest level for select statement (not select for update)
select-statement ::
@R selectR :: select-statement >>
SELECT
from-clause
where-clause
group-by-clause
having-clause
## generalized to allow null from clause eg: select 2+2
@R fromNull :: from-clause >>
@R fromFull :: from-clause >> FROM
@R whereNull :: where-clause >>
@R whereFull :: where-clause >> WHERE
@R groupNull :: group-by-clause >>
@R groupFull :: group-by-clause >> GROUP BY
@R havingNull :: having-clause >>
@R havingFull :: having-clause >> HAVING
@R unionNull :: union-clause >>
@R unionFull :: union-clause >> UNION
"""
SELECTNONTERMS = """
select-statement
all-distinct select-list table-reference-list
where-clause group-by-clause having-clause union-clause
maybe-order-by
search-condition column-list maybe-all order-by-clause
column-name from-clause
"""
# of these the following need resolution
# (select-list) (table-reference-list)
# (search-condition) order-by-clause (column-name)
SELECTKEYWORDS = """
SELECT FROM WHERE GROUP BY HAVING UNION DISTINCT ALL AS
"""
# test generation of the grammar
def BuildSQLG():
import kjParseBuild
SQLG = kjParseBuild.NullCGrammar()
SQLG.SetCaseSensitivity(0)
SQLG.Keywords(SELECTKEYWORDS)
SQLG.Nonterms(SELECTNONTERMS)
# no comments yet
SQLG.Declarerules(SELECTRULES)
print "building"
SQLG.Compile()
print "marshaling"
outfile = open( MARSHALFILE, "w")
SQLG.MarshalDump(outfile)
outfile.close()
return SQLG
# load function
def LoadSQLG():
import kjParser
print "unmarshalling"
infile = open(MARSHALFILE, "r")
SQLG = kjParser.UnMarshalGram(infile)
infile.close()
return SQLG
#### for testing
if REBUILD:
SQLG0 = BuildSQLG()
print " rebuilt SQLG0 as compilable grammar"
SQLG = LoadSQLG()
print " build SQLG as reloaded grammar"
\ No newline at end of file
# Package handling monstrocities
import os
import sqlwhere
# Stuff filename into the namespace so that you don't have to hard code
# this information ahead of time. Should be portable across platforms.
sqlwhere.filename = filename = __path__[0] + os.sep + 'sql.mar'
# Yank the previous namespace in so that nothing breaks
from gadfly import *
This diff is collapsed.
This diff is collapsed.
<html>
<head>
<title>
Gadfly SQL constructs
</title>
</head>
<body bgcolor="#ffffff">
<table>
<tr>
<td>
<img src="gadfly.JPG">
</td>
<td>
<h1>Gadfly SQL constructs</h1>
</td></tr></table>
<blockquote>
This document describes SQL constructs supported by Gadfly.
The presentation
does not define the complete syntax -- see sqlgram.py for
the precise syntax as BNF -- nor the complete semantics --
see a good book on SQL for more detailed coverage of semantic
(or use the source, Luke ;c) ).
Also, please have a look at my
<a href="http://mulder.rutgers.edu/~aaron/dbnotes.cgi">evolving
database course notes</a> for more coverage of SQL.
Examples of all supported constructs are also shown in the
test suite source file gftest.py.
This document is only
a very brief guide, primarily of use to those who already
understand something about SQL -- it is neither a tutorial
nor a detailed discussion of syntax and semantics.
</blockquote>
<h1>The Standard, with omissions</h1>
<p>
Gadfly supports a large subset of ODBC 2.0 SQL. The reason
ODBC 2.0 is chosen is because it provides a fairly strong set of
constructs, but does not include some of the more obscure
features of other SQL standards which would be extremely
difficult and complex to implement correctly
(and perhaps, not used very frequently (?)).
<p>
Supported features include views, groupings, aggregates,
subquery expressions, quantified subquery comparisons,
EXISTS, IN, UNION, EXCEPT, INTERSECT, searched mutations and
Indices, among others (see below).
<p>
Some important omissions from ODBC 2.0 at this point are
<pre>
Nulls.
Outer joins.
CHECK conditions.
Enforced data type constraints.
Alter table (can't implement until NULLs arrive).
Date, Time, and Interval data types
</pre>
It is hoped these will be implemented at some future time.
<p>
Less important omissions include
<pre>
Cursor based updates and deletes
(justification: if you really need them the db design
is flawed, and it's possible to use python instead).
LIKE string predicate
(justification: use Python regexes in python code).
Users and permissions
(justification: Gadfly is not intended for full multiuser
use at this time).
</pre>
These may or may not be implemented at some future time.
<h1>Statements</h1>
All interaction with SQL databases is mediated by
SQL statements, or statement sequences. Statement
sequences are statements separated by semicolons.
SQL keywords and user defined names are not case
sensitive (but string values are, of course).
<p>
SQL statements include the following.
<h3>Select Statement</h3>
The select statement derives a table from tables
in the database. It's general form is
<pre>
sub_query
optorder_by
</pre>
Where sub_query is given by
<pre>
SELECT alldistinct select_list
FROM table_reference_list
optwhere
optgroup
opthaving
optunion
</pre>
Read the statement:
<pre>
SELECT [DISTINCT|ALL] expressions or *
FROM tables
[WHERE condition]
[GROUP BY group-expressions]
[HAVING aggregate-condition]
[union-clause]
[ORDER BY columns]
</pre>
as follows:
<pre>
1) Make all combinations of rows from the tables (FROM line)
2) Eliminate those combinations not satisfying condition (WHERE line)
3) (if GROUP present) form aggregate groups that match on group-expressions
4) (if HAVING present) eliminate aggregate groups that don't satisfy
the aggregate-condition.
5) compute the columns to keep (SELECT line).
6) (if union-clause present) combine (union, except, intersect)
the result with the result of another select statement.
7) if DISTINCT, throw out redundant entries.
8) (if ORDER present) order the result by the columns (ascending
or descending as specified, with precedence as listed).
</pre>
This reading has little to do with the actual implementation,
but the answer produced should match this intuitive reading.
<h3>Create and drop table</h3>
The create and drop table constructs
initialize and destroy a table structure, respectively.
<pre>
CREATE TABLE user_defined_name ( colelts )
DROP TABLE user_defined_name
</pre>
The colelts declare the names of the columns for
the table and their data types. The data types are
not checked or enforced in any way at this time.
<h3>Table mutations (INSERT, UPDATE, DELETE)</h3>
Insert, Update, and Delete statements insert rows
into tables, modify rows in tables in place, or
remove rows from tables respectively.
<pre>
INSERT INTO table_name optcolids insert_spec
DELETE FROM user_defined_name optwhere
UPDATE user_defined_name
SET assns
optwhere
</pre>
The insert statement has two variants (in this implementation)
INSERT sub-select and INSERT VALUES.
<pre>
insert into r (a,b,c) select a,b,c from s
insert into r (a,b,c) values (1,2,3)
</pre>
The first inserts
the result of a SELECT statement into the target table
and the other inserts explicit values (which may be dynamic
parameters, see below).
<P>
Cursor based updates are not supported at the SQL level,
eg
<pre>
update r set a=1 where current of curs
</pre>
is not supported.
<h3>Indices</h3>
The create and drop index statements initialize and
destroy index structures respectively.
<pre>
CREATE INDEX user_defined_name
ON user_defined_name
( namelist )
DROP INDEX user_defined_name
</pre>
Indices allow fast access to a table, based on values
for the indexed columns in the namelist.
<p>
Indices can be UNIQUE, meaning that the attributes
of the index cannot take on the same values in the table
twice.
<pre>
CREATE UNIQUE INDEX user_defined_name
ON user_defined_name ( namelist )
</pre>
Unique indices can be used to enforce primary and
secondary key constraints. After a UNIQUE index on
a table is created inserts that attempt to insert
repeat values for the indexed columns will be rejected.
<h3>Views</h3>
Create view and drop view statements initialize and
drop views, respectively.
<pre>
CREATE VIEW user_defined_name optnamelist
AS select_statement
DROP VIEW user_defined_name
</pre>
Views are "derived tables" which are defined
as stored SELECT statements. They can be used
as tables, except that they cannot be directly
mutated.
<p>
It is possible to "implement your own views
in Python". Please see remotetest.py, gfintrospect
and the FAQ for discussion.
<h1>Conditions</h1>
Conditions are truth valued boolean expressions
formed from basic conditions possibly combined using
NOT, AND, OR (where NOT has highest precedence and
OR has lowest precedence) and parentheses.
<p>
Basic conditions include simple comparisons
<pre>
expression = expression
expression &lt; expression
expression &gt; expression
expression &lt;= expression
expression &gt;= expression
expression &lt;&gt; expression
</pre>
Variants of the simple comparisons are the quantified
subquery comparisons
<pre>
expression = ANY ( subquery )
expression = ALL ( subquery )
</pre>
(and similarly for the other comparison operators).
The IN predicate tests membership (like =ANY)
<pre>
expression IN ( subquery )
expression NOT IN ( subquery )
</pre>
For all the quantified comparisons and IN the
subquery must generate a single column table.
<p>
Also included are the the BETWEEN and NOT BETWEEN predicates
<pre>
expression BETWEEN expression AND expression
expression NOT BETWEEN expression AND expression
</pre>
<p>
The most general subquery predicate is EXISTS and NOT EXISTS
which places no restriction on the subquery:
<pre>
EXISTS (subquery)
NOT EXISTS (subquery)
</pre>
<h1>Expressions</h1>
Expressions occur in conditions (WHERE, HAVING, etc.),
in UPDATE searched assignments,
and in the select list of select statements.
<p>
Expressions are formed from primary expressions,
possibly combined using the standard arithmetic operators
and parentheses with the normal precedence.
<p>
Primary expressions include numeric and string literals.
Numeric literals supported are the Python numeric literals.
String constants are set off by apostrophies, where two
apostrophe's in sequence represent an apostrophy in the
string:
<pre>
'SQL string literals ain''t pretty'
</pre>
Column name expressions may be unqualified if they are
unambiguous, or may be qualified with a table name
or table alias
<pre>
bar
frequents.bar
f.bar
</pre>
The rules for scoping of column names are not covered
here. Column names in subqueries may refer to bindings
in the query (or queries) that contain the sub-query.
<p>
Subquery expressions of form
<pre>
( select_statement )
</pre>
must produce a single column and single row table.
<p>
Aggregate operations are only permitted in the select
list or in the HAVING condition of SELECT statements
(including subselects).
<pre>
COUNT(*)
COUNT(expression)
AVG(expression)
MAX(expression)
SUM(expression)
MIN(expression)
</pre>
<em><strong>and also including the non-standard extension MEDIAN
<pre>
MEDIAN(expression)
</pre>
</strong></em>
Aggregate operations can be applied to distinct values
as in
<pre>
COUNT(DISTINCT expression)
</pre>
The Dynamic expression "?" is a placeholder for a value
bound at evaluation time (from Python values). See the
<a href="gadfly.html">
API discussions </a>
(Use) for more details on the use of
dynamic parameters.
<p>
<a href="mailto:arw@ifu.net">feedback</a><br>
<a href="../index.html">home</a><br>
<a href="index.html">Gadfly home</a>
</body>
</html>
\ No newline at end of file
"""client access to gadfly server. (gfserve.py)
Imported as a module this module provides interfaces
that remotely access a gadfly database server.
Remote connections: gfclient
connection = gfclient.gfclient(
policy, # the name of the connection policy ["admin" for admin]
port, # the port number the server is running on
password,# the password of the policy
[machine]) # (optional) the machine where server is running
# (defaults to localhost)
methods for gfclient connections:
gfclient.checkpoint() checkpoint the server (fails silently
if connection is not "admin").
gfclient.restart() restart the server (fails silently
if connection is not "admin").
gfclient.shutdown() shutdown the server (fails silently
if connection is not "admin").
cursor = gfclient.cursor() returns a cursor on this connection
methods for cursor objects:
cursor.execute(statement, dynamic_parameters=None)
execute the statement with optional dynamic parameters.
Dynamic parameters can be a list of tuples for INSERT
VALUES statements, otherwise they must be a tuple
of values.
cursor.execute_prepared(name, dynamic_parameters=None)
execute a named statement configured for this connection
policy, with optional dynamic parameters. Dynamic
parameters permitted as for execute depending on the
statement the name refers to.
cursor.fetchall()
return results of the last executed statement
(None for non-queries, or list of tuples).
See gfstest.py for example usage.
SCRIPT INTERPRETATION:
Server maintenance utilities
COMMAND LINE:
python gfclient.py action port admin_password [machine]
TEST EXAMPLE:
python gfclient.py shutdown 2222 admin
action: one of
shutdown: shut down the server with no checkpoint
restart: restart the server (re-read the database and recover)
checkpoint: force a database checkpoint now
port: the port the server is running on
admin_password: the administrative password for the server
machine: [optional] the machine the server runs on.
"""
import gfsocket
def main():
import sys
try:
done=0
argv = sys.argv
[action, port, admin_password] = argv[1:4]
from string import atoi
port = atoi(port)
if len(argv)>4:
machine = argv[4]
else:
machine = None
print action, port, admin_password, machine
if action not in ["shutdown", "restart", "checkpoint"]:
print "bad action", action
print
return
dosimple(action, port, admin_password, machine)
done=1
finally:
if not done:
print __doc__
def dosimple(action, port, pw, machine=None):
import socket
if machine is None:
machine = socket.gethostname()
conn = gfclient("admin", port, pw, machine)
action = getattr(conn, action)
print action()
# copied from gfserve
# shut down the server (admin policy only)
# arguments = ()
# shutdown the server with no checkpoint
SHUTDOWN = "SHUTDOWN"
# restart the server (admin only)
# arguments = ()
# restart the server (recover)
# no checkpoint
RESTART = "RESTART"
# checkpoint the server (admin only)
# arguments = ()
# checkpoint the server
CHECKPOINT = "CHECKPOINT"
# exec prepared statement
# arguments = (prepared_name_string, dyn=None)
# execute the prepared statement with dynamic args.
# autocommit.
EXECUTE_PREPARED = "EXECUTE_PREPARED"
# exec any statement (only if not disabled)
# arguments = (statement_string, dyn=None)
# execute the statement with dynamic args.
# autocommit.
EXECUTE_STATEMENT = "EXECUTE_STATEMENT"
class gfclient:
closed = 0
def __init__(self, policy, port, password, machine=None):
import socket
self.policy = policy
self.port = port
self.password = password
if machine is None:
machine = socket.gethostname()
self.machine = machine
def open_connection(self):
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#print type(sock), sock
sock.connect((self.machine, self.port))
return sock
def send_action(self, action, arguments, socket):
gfsocket.send_certified_action(
self.policy, action, arguments, self.password, socket)
def checkpoint(self):
return self.simple_action(CHECKPOINT)
def simple_action(self, action, args=()):
"""only valid for admin policy: force a server checkpoint"""
sock = self.open_connection()
self.send_action(action, args, sock)
data = gfsocket.recv_data(sock)
data = gfsocket.interpret_response(data)
return data
def restart(self):
"""only valid for admin policy: force a server restart"""
return self.simple_action(RESTART)
def shutdown(self):
"""only valid for admin policy: shut down the server"""
return self.simple_action(SHUTDOWN)
def close(self):
self.closed = 1
def commit(self):
# right now all actions autocommit
pass
# cannot rollback, autocommit on success
rollback = commit
def cursor(self):
"""return a cursor to this policy"""
if self.closed:
raise ValueError, "connection is closed"
return gfClientCursor(self)
class gfClientCursor:
statement = None
results = None
description = None
def __init__(self, connection):
self.connection = connection
# should add fetchone fetchmany
def fetchall(self):
return self.results
def execute(self, statement=None, params=None):
con = self.connection
data = con.simple_action(EXECUTE_STATEMENT, (statement, params))
(self.description, self.results) = data
def execute_prepared(self, name, params=None):
con = self.connection
data = con.simple_action(EXECUTE_PREPARED, (name, params))
if data is None:
self.description = self.results = None
else:
(self.description, self.results) = data
def setoutputsizes(self, *args):
pass # not implemented
def setinputsizes(self):
pass # not implemented
if __name__=="__main__":
main()
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
"test parses for sql grammar"
test = [
"select a from x where b=c",
"select distinct x.a from x where x.b=c",
"select all a from x where b=c",
"select a from x, y where b=c or x.d=45",
"select a as k from x d, y as m where b=c",
"select 1 as n, a from x where b=c",
"select * from x",
"select a from x where b=c",
"select a from x where not b=c or d=1 and e=5",
"select a from x where a=1 and (x.b=3 or not b=c)",
"select -1 from x",
"select -1e6j from x",
"insert into table1 (a,b,c) values (-1e6+3j, -34e10, 56j)"
]
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
'''this module indicates where the sql datastructures are marshalled
Auto generated on install: better not touch!
'''
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