Commit 2b10c869 authored by Jim Fulton's avatar Jim Fulton

initial

parent 6bdedcbb
database_type='Gadfly'
###########################################################################
#
# Copyright
#
# Copyright 1996 Digital Creations, L.C., 910 Princess Anne
# Street, Suite 300, Fredericksburg, Virginia 22401 U.S.A. All
# rights reserved.
#
###########################################################################
__doc__='''%s Database Connection
$Id: DA.py,v 1.1 1998/04/15 15:10:37 jim Exp $''' % database_type
__version__='$Revision: 1.1 $'[11:-2]
from db import DB, manage_DataSources
import sys, DABase, Globals
_connections={}
def data_sources():
return filter(lambda ds, used=_connections.has_key: not used(ds[0]),
manage_DataSources())
addConnectionForm=Globals.HTMLFile('connectionAdd',globals())
def manage_addAqueductGadflyConnection(
self, id, title, connection, check=None, REQUEST=None):
"""Add a DB connection to a folder"""
self._setObject(id, Connection(
id, title, connection, check))
if REQUEST is not None: return self.manage_main(self,REQUEST)
return self.manage_main(self,REQUEST)
class Connection(DABase.Connection):
database_type=database_type
id='%s_database_connection' % database_type
meta_type=title='Aqueduct %s Database Connection' % database_type
icon='misc_/Aqueduct%s/conn' % database_type
def factory(self): return DB
def connect(self,s):
c=_connections
if c.has_key(s) and c[s] != self._p_oid:
raise 'In Use', (
'The database <em>%s</em> is in use.' % s)
c[s]=self._p_oid
return Connection.inheritedAttribute('connect')(self, s)
def __del__(self):
s=self.connection_string
c=_connections
if c.has_key(s) and c[s] == self._p_oid: del c[s]
def manage_close_connection(self, REQUEST):
" "
s=self.connection_string
c=_connections
if c.has_key(s) and c[s] == self._p_oid: del c[s]
return Connection.inheritedAttribute('manage_close_connection')(self, REQUEST)
##############################################################################
#
# $Log: DA.py,v $
# Revision 1.1 1998/04/15 15:10:37 jim
# initial
#
###########################################################################
#
# Copyright
#
# Copyright 1996 Digital Creations, L.C., 910 Princess Anne
# Street, Suite 300, Fredericksburg, Virginia 22401 U.S.A. All
# rights reserved.
#
###########################################################################
__doc__='''Database Connection
$Id: DABase.py,v 1.1 1998/04/15 15:10:38 jim Exp $'''
__version__='$Revision: 1.1 $'[11:-2]
from db import manage_DataSources
import AqueductDA.Connection, sys
from Globals import HTMLFile
from ImageFile import ImageFile
from ExtensionClass import Base
import Acquisition
class Connection(AqueductDA.Connection.Connection):
_isAnSQLConnection=1
manage_options=AqueductDA.Connection.Connection.manage_options+(
{'label': 'Browse', 'action':'manage_browse'},
{'label': 'Design', 'action':'manage_tables'},
)
manage_tables=HTMLFile('tables',globals())
manage_browse=HTMLFile('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._d=d
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('table_info',globals())
menu=HTMLFile('table_menu',globals())
def tpValues(self):
r=[]
for d in self._columns:
b=ColumnBrowser()
b._d=d
try: b.icon=field_icons[d['Type']]
except: pass
b.TABLE_NAME=self._d['TABLE_NAME']
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('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("<!--#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("<!--#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',
}
##############################################################################
#
# $Log: DABase.py,v $
# Revision 1.1 1998/04/15 15:10:38 jim
# initial
#
# Revision 1.8 1998/01/29 16:26:44 brian
# Added eval support
#
# Revision 1.7 1998/01/16 18:35:34 jim
# Updated with new da protocols.
#
# Revision 1.5 1998/01/07 16:28:27 jim
# Brought up to date with latest Principia models, and got rid of DA objects and search interface.
#
# Revision 1.4 1997/12/02 19:35:34 jim
# changed to get rid of DA folders.
#
# Revision 1.5 1997/11/26 20:04:22 jim
# New Architecture, note that backward compatibility tools are needed
#
# Revision 1.4 1997/09/22 18:46:12 jim
# Got rid of ManageHTML
#
# Revision 1.3 1997/08/06 18:20:50 jim
# Renamed description->title and name->id and other changes
#
# Revision 1.2 1997/07/28 21:32:24 jim
# Added add method.
#
# Revision 1.1 1997/07/25 16:52:44 jim
# initial
#
# Revision 1.1 1997/07/25 15:49:40 jim
# initial
#
#
#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
##############################################################################
#
# Copyright
#
# Copyright 1996 Digital Creations, L.C., 910 Princess Anne
# Street, Suite 300, Fredericksburg, Virginia 22401 U.S.A. All
# rights reserved.
#
##############################################################################
__doc__='''Generic Database Adapter Package Registration
$Id: __init__.py,v 1.1 1998/04/15 15:10:39 jim Exp $'''
__version__='$Revision: 1.1 $'[11:-2]
import Globals, ImageFile
classes=('DA.Connection',)
database_type='Gadfly'
misc_={'conn':
ImageFile.ImageFile('AqueductDA/www/DBAdapterFolder_icon.gif')}
for icon in ('table', 'view', 'stable', 'what',
'field', 'text','bin','int','float',
'date','time','datetime'):
misc_[icon]=ImageFile.ImageFile('icons/%s.gif' % icon, globals())
meta_types=(
{'name':'Aqueduct %s Database Connection' % database_type,
'action':'manage_addAqueduct%sConnectionForm' % database_type,
},
)
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()
def manage_addAqueductGadflyConnectionForm(self, REQUEST, *args, **kw):
" "
DA=getDA()
return DA.addConnectionForm(
self,REQUEST,
database_type=database_type,
data_sources=DA.data_sources)
def manage_addAqueductGadflyConnection(
self, id, title, connection, check=None, REQUEST=None):
" "
return getDA().manage_addAqueductGadflyConnection(
self, id, title, connection, check, REQUEST)
methods={
'manage_addAqueductGadflyConnection':
manage_addAqueductGadflyConnection,
'manage_addAqueductGadflyConnectionForm':
manage_addAqueductGadflyConnectionForm,
}
##############################################################################
#
# $Log: __init__.py,v $
# Revision 1.1 1998/04/15 15:10:39 jim
# initial
#
# Revision 1.4 1998/01/29 16:26:45 brian
# Added eval support
#
# Revision 1.3 1998/01/16 18:35:08 jim
# Updated with new da protocols.
#
# Revision 1.2 1997/11/26 20:04:22 jim
# New Architecture, note that backward compatibility tools are needed
#
# Revision 1.1 1997/07/25 16:52:46 jim
# initial
#
# Revision 1.1 1997/07/25 15:49:41 jim
# initial
#
#
<html>
<head><title><!--#var title_or_id--> tables</title></head>
<body bgcolor="#FFFFFF" link="#000099" vlink="#555555" alink="#77003B">
<!--#var manage_tabs-->
<!--#tree header=info-->
<IMG SRC="<!--#var SCRIPT_NAME-->/misc_/AqueductGadfly/<!--#var icon-->"
ALT="<!--#var Type-->" BORDER="0">
<!--#var Name--><!--#var Description-->
<!--#/tree-->
</body>
</html>
<html>
<head>
<title>Add Aqueduct <!--#var database_type--> Database Connection</title>
</head>
<body bgcolor="#FFFFFF" link="#000099" vlink="#555555" alink="#77003B">
<h2>Add Aqueduct <!--#var database_type--> Database Connection</h2>
<!--#if data_sources-->
<form action="manage_addAqueduct<!--#var database_type-->Connection"
method="POST">
<table cellspacing="2">
<tr>
<th align="LEFT" valign="TOP">Id</th>
<td align="LEFT" valign="TOP">
<input type="TEXT" name="id" size="40"
value="<!--#var database_type-->_database_connection">
</td>
</tr>
<tr>
<th align="LEFT" valign="TOP"><em>Title</em></th>
<td align="LEFT" valign="TOP">
<input type="TEXT" name="title" size="40"
value="Aqueduct <!--#var database_type--> Database Connection">
</td>
</tr>
<tr>
<th align="LEFT" valign="TOP">Select a Data Source</th>
<td align="LEFT" valign="TOP"><select name=connection size=7>
<!--#in data_sources-->
<option value="<!--#var sequence-key-->">
<!--#var sequence-key--><!--#if
sequence-item-->, <!--#var sequence-item--><!--#/if-->
</option-->
<!--#/in-->
</select></td>
</tr>
<tr>
<th align="LEFT" valign="TOP">Connect immediately</th>
<td align="LEFT" valign="TOP">
<input name="check" type="CHECKBOX" value="YES" CHECKED>
</td>
</tr>
<tr>
<td></td>
<td><br><input type="SUBMIT" value="Add"></td>
</tr>
</table>
</form>
<!--#else-->
Sorry, you cannot create any Aqueduct <!--#var database_type--> Database
Connections because no <!--#var database_type--> databases exist, or
all of the existing databases are in use.
<!--#/if-->
</body>
</html>
'''$Id: db.py,v 1.1 1998/04/15 15:10:41 jim Exp $'''
# 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.1 $'[11:-2]
import string, sys, os
from string import strip, split, find, join
from gadfly import gadfly
import Globals
data_dir=os.path.join(Globals.data_dir,'gadfly')
def manage_DataSources():
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:
database_error=gadfly.error
def tables(self,*args,**kw):
return map(lambda name: {
'TABLE_NAME': name,
'TABLE_TYPE': 'TABLE',
}, self.db.table_names())
def columns(self, table_name):
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 __init__(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.connection=connection
self.db=db
self.cursor=db.cursor()
def query(self,query_string, max_rows=9999999):
self._register()
c=self.db.cursor()
queries=filter(None, map(strip,split(query_string, '\0')))
if not queries: raise 'Query Error', 'empty query'
names=None
result='cool\ns\n'
for qs in queries:
c.execute(qs)
d=c.description
if d is None: continue
if names is not None:
raise 'Query Error', (
'select in multiple sql-statement query'
)
names=map(lambda d: d[0], d)
results=c.fetchmany(max_rows)
nv=len(names)
indexes=range(nv)
row=['']*nv
defs=[maybe_int]*nv
j=join
rdb=[j(names,'\t'),None]
append=rdb.append
for result in results:
for i in indexes:
try: row[i]=defs[i](result[i])
except NewType, v: row[i], defs[i] = v
append(j(row,'\t'))
rdb[1]=j(map(lambda d, Defs=Defs: Defs[d], defs),'\t')
rdb.append('')
result=j(rdb,'\n')
return result
class _p_jar:
# This is place holder for new transaction machinery 2pc
def __init__(self, db=None): self.db=db
def begin_commit(self, *args): pass
def finish_commit(self, *args): pass
_p_jar=_p_jar(_p_jar())
_p_oid=_p_changed=_registered=None
def _register(self):
if not self._registered:
try:
get_transaction().register(self)
self._registered=1
except: pass
def __inform_commit__(self, *ignored):
self.db.commit()
self._registered=0
def __inform_abort__(self, *ignored):
self.db.rollback()
self.db.checkpoint()
self._registered=0
NewType="Excecption to raise when sniffing types, blech"
def maybe_int(v, int_type=type(0), float_type=type(0.0), t=type):
t=t(v)
if t is int_type: return str(v)
if v is None or v=='': return ''
if t is float_type: raise NewType, (maybe_float(v), maybe_float)
raise NewType, (maybe_string(v), maybe_string)
def maybe_float(v, int_type=type(0), float_type=type(0.0), t=type):
t=t(v)
if t is int_type or t is float_type: return str(v)
if v is None or v=='': return ''
raise NewType, (maybe_string(v), maybe_string)
def maybe_string(v):
v=str(v)
if find(v,'\t') >= 0 or find(v,'\t'):
raise NewType, (must_be_text(v), must_be_text)
return v
def must_be_text(v, f=find, j=join, s=split):
if f(v,'\\'):
v=j(s(v,'\\'),'\\\\')
v=j(s(v,'\t'),'\\t')
v=j(s(v,'\n'),'\\n')
return v
Defs={maybe_int: 'i', maybe_float:'n', maybe_string:'s', must_be_text:'t'}
##########################################################################
#
# $Log: db.py,v $
# Revision 1.1 1998/04/15 15:10:41 jim
# initial
#
# Revision 1.3 1997/08/06 18:21:27 jim
# Renamed description->title and name->id and other changes
#
# Revision 1.2 1997/08/06 14:29:35 jim
# Changed to use abstract dbi base.
#
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
This diff is collapsed.
This diff is collapsed.
<html>
<head>
<title>
Gadfly SQL constructs
</title>
</head>
<body bgcolor="#ffffdd">
<h1>Gadfly SQL constructs</h1>
<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.
Primary key constraints.
Unique indices.
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.
<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.
<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.
</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.
#!/usr/local/bin/python
"""build the sql grammar.
usage
python <thismodule>
for a simple install or
python <thismodule> force
for a full rebuild (with grammar regeneration).
In the current directory find or create sql.mar and sqlwhere.py
where sql.mar has the marshalled grammar data structures
for parsing sql and sqlwhere.py is a module that indicates
where the grammar file is as value of sqlwhere.filename.
"""
marfile = "sql.mar"
modfile = "sqlwhere.py"
print __doc__
from os import getcwd
cwd = getcwd()
modtemplate ="""
'''this module indicates where the sql datastructures are marshalled
Auto generated on install: better not touch!
'''
filename = '%s'
"""
wheremod = cwd + "/" + modfile
where = cwd + "/" + marfile
print
print "now creating", wheremod
f = open(wheremod, "w")
f.write( modtemplate % (where,) )
f.close()
from sqlgen import BuildSQL, getSQL
import sys
argv = sys.argv
force = 0
#print argv
if len(argv)>1 and argv[1]=="force":
force = 1
if not force:
try:
sql = getSQL()
except:
print "exception", sys.exc_type, sys.exc_value
print "during load of SQL grammar structures."
print "Apparently the SQL grammar requires regeneration"
force = 1
if force:
print "now generating parser structures (this might take a while)..."
where = cwd + "/" + marfile
print "building in", where
sql = BuildSQL(cwd + "/" + marfile)
print
print "done."
<HTML>
<HEAD>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=windows-1252">
<META NAME="Generator" CONTENT="Microsoft Word 97">
<TITLE>Gadfly: recovery</TITLE>
<META NAME="Template" CONTENT="C:\PROGRAM FILES\MICROSOFT OFFICE\OFFICE\html.dot">
</HEAD>
<BODY LINK="#0000ff" VLINK="#800080" BGCOLOR="#ff5555">
<H1>Gadfly Recovery</H1>
<P>In the event of a software glitch or crash Gadfly may terminate without having stored committed updates.
A recovery strategy attempts to make sure
that the unapplied commited updates are applied when the database restarts.
It is always assumed that there is only one primary (server) process controlling the database (possibly with
multiple clients). </P>
<P>Gadfly uses a simple LOG with deferred updates recovery mechanism. Recovery should be possible in the
presence of non-disk failures (server crash, system crash). Recovery after a disk crash is not available
for Gadfly as yet, sorry. </P>
<P>Due to portability problems Gadfly does not prevent multiple processes from "controlling" the database at
once. For read only access multiple instances are not a problem, but for access with modification, the processes
may collide and corrupt the database. For a read-write database, make sure only one (server) process controls
the database at any given time. </P>
<P>The only concurrency control mechanism that provides serializability for Gadfly as yet is the trivial one --
the server serves all clients serially. This will likely change for some variant of the system at some point. </P>
<P>This section explains the basic recovery mechanism. </P>
<H1>Normal operation</H1>
<H3>Precommit</H3>
<P>During normal operations any active tables are in memory in the process.
Uncommitted updates for a transaction are kept in "shadow tables" until the transaction commits using
<pre>
connection.commit()
</pre>
The shadow tables remember the mutations that have been applied to them. The permanent table copies
are only modified after commit time. A commit commits all updates for all cursors for the connection.
Unless the autocommit feature is disabled (see below) a
commit normally always triggers a checkpoint too.</P>
A rollback
<pre>
connection.rollback()
</pre>
explicitly discards all uncommitted updates and restores the connection to the previously
committed state.</p>
<P>There is a 3rd level of shadowing for statement sequences executed by a cursor.
In particular the design attempts to make sure that if
<pre>
cursor.execute(statement)
</pre>
fails with an error, then the shadow database will contain no updates from
the partially executed statement (which may be a sequence of statements)
but will reflect other completed updates that may have not been committed.
<H3>Commit</H3>
<P>At commit, operations applied to shadow tables are written
out in order of application to a log file before being permanently
applied to the active database. Finally a commit record is written to
the log and the log is flushed. At this point the transaction is considered
committed and recoverable, and a new transaction begins.
Finally the values of the shadow tables replace
the values of the permanent tables in the active database,
(but not in the database disk files until checkpoint, if autocheckpoint
is disabled). </P>
<H3>Checkpoint</H3>
<P>A checkpoint operation brings the persistent copies of the tables on
disk in sync with the in-memory copies in the active database. Checkpoints
occur at server shut down or periodically during server operation.
The checkpoint operation runs in isolation (with no database access
allowed during checkpoint). </P>
<p><em>Note: database connections normally run a checkpoint
after every commit, unless you set
<pre>
connection.autocheckpoint = 0
</pre>
which asks that checkpoints be done explicitly by the program using
<pre>
connection.commit() # if appropriate
connection.checkpoint()
</pre>
Explicit checkpoints should make the database perform better,
since the disk files are written less frequently, but
in order to prevent unneeded (possibly time consuming)
recovery operations after a database
is shutdown and restarted it is important to always execute an explicit
checkpoint at server shutdown, and periodically during long server
runs.</em></p>
<p><strong>Note that if any outstanding operations are uncommitted
at the time of a checkpoint (when autocheckpoint is disabled) the
updates will be lost (ie, it is equivalent to a rollback).
</strong></p>
<P>At checkpoint the old persistent value of each table that has been updated since
the last checkpoint is copied to a back up file, and the currently active value is
written to the permanent table file. Finally if the data definitions have changed
the old definitions are stored to a backup file and the new definitions are written
to the permanent data definition file. To signal successful checkpoint the
log file is then deleted.</P>
<P>
At this point (after log deletion) the database is considered
quiescent (no recovery required). Finally all back up table files are deleted.
[Note, it might be good to keep old logs around... Comments?] </P>
<P>Each table file representation is annotated with a checksum,
so the recovery system can check that the file was stored correctly. </P>
<H1>Recovery</H1>
<P>When a database restarts it automatically determines whether
the last active instance shut down normally and whether recovery
is required. Gadfly discovers the need for recovery by detecting
a non-empty current log file. </P>
<P>To recover the system Gadfly first scans the log file to determine committed transactions.
Then Gadfly rescans the log file applying the operations of committed
transactions to the in memory table values in the order recorded.
When reading in table values for the purpose of recovery Gadfly looks
for a backup file for the table first. If the backup is not corrupt,
its value is used, otherwise the permanent table file is used. </P>
<P>After recovery Gadfly runs a normal checkpoint before resuming
normal operation. </P>
<p>
<strong>
Please note: Although I have attempted to provide a robust
implementation
for this software I do not guarantee its correctness. I hope
it will work well for you but I do not assume any legal
responsibility for problems anyone may have during use
of these programs.
</strong>
</BODY>
</HTML>
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.
"""grammar generation stuff for sql.
This module does not bind any rule semantics, it
just generates the parser data structures.
"""
### interpretation functions and regexen for terminals
MARSHALFILE = "sql.mar"
import string
alphanum = string.letters+string.digits + "_"
userdefre = "[%s][%s]*" % (string.letters +"_", alphanum)
commentre = "--.*"
def userdeffn(str):
from string import upper
return upper(str)
charstre = "'[^\n']*'"
def charstfn(str):
return str[1:-1]
numlitre = "[%s][%s\.]*" % (string.digits, alphanum) # not really...
def numlitfn(str):
"""Note: this is "safe" because regex
filters out dangerous things."""
return eval(str)
def DeclareTerminals(Grammar):
Grammar.Addterm("user_defined_name", userdefre, userdeffn)
Grammar.Addterm("character_string_literal", charstre, charstfn)
Grammar.Addterm("numeric_literal", numlitre, numlitfn)
def BuildSQL(filename=MARSHALFILE):
import kjParseBuild
from sqlgram import sqlrules, nonterms, keywords, puncts
SQLG = kjParseBuild.NullCGrammar()
SQLG.SetCaseSensitivity(0)
DeclareTerminals(SQLG)
SQLG.Keywords(keywords)
SQLG.punct(puncts)
SQLG.Nonterms(nonterms)
SQLG.comments([commentre])
# should add comments
SQLG.Declarerules(sqlrules)
print "working..."
SQLG.Compile()
print "testing"
from sqlgtest import test
for x in test:
print SQLG.DoParse1(x)
print "dumping to", filename
outfile = open(filename, "wb")
SQLG.MarshalDump(outfile)
outfile.close()
return SQLG
def reloadSQLG(filename=MARSHALFILE):
"""does not bind any interpretation functions."""
import kjParser
infile = open(filename, "rb")
SQLG = kjParser.UnMarshalGram(infile)
infile.close()
DeclareTerminals(SQLG)
return SQLG
def getSQL():
from sqlwhere import filename
return reloadSQLG(filename)
\ No newline at end of file
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)",
]
\ 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!
'''
filename = '/ext/dev/users/jim2/lib/python/Products/AqueductGadfly/gadfly/sql.mar'
<!--#var standard_html_header-->
<!--#var TABLE_TYPE--><!--#if TABLE_OWNER-->
owned by <!--#var TABLE_OWNER--><!--#/if-->
<!--#if REMARKS--><br><!--#var REMARKS--><!--#/if-->
<!--#var standard_html_footer-->
<!--#var standard_html_header-->
<a href="tableNamed/<!--#var Name-->/manage_designInput">Design Input *</a>
<a href="tableNamed/<!--#var Name-->/manage_designUpdate">Design Update *</a>
<a href="tableNamed/<!--#var Name-->/manage_designDelete">Design Delete</a>
<!--#var standard_html_footer-->
<html>
<head><title><!--#var title_or_id--> tables</title></head>
<body bgcolor="#FFFFFF" link="#000099" vlink="#555555" alink="#77003B">
<!--#var manage_tabs-->
<h2><!--#var 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>
<!--#in table_info-->
<option value="<!--#var sequence-key-->">
<!--#var sequence-key--> <!--#var sequence-item-->
</option>
<!--#/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>
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