[Zope-CVS] CVS: Products/AdaptableStorage/gateway_sql - SQLGatewayBase.py:1.1 SQLObjectData.py:1.1 PsycopgConnection.py:1.4 SQLClassification.py:1.3 SQLFolderItems.py:1.4 SQLItemId.py:1.4 SQLKeychainGenerator.py:1.3 SQLRemainder.py:1.4 public.py:1.2

Shane Hathaway shane@zope.com
Mon, 23 Dec 2002 23:30:03 -0500


Update of /cvs-repository/Products/AdaptableStorage/gateway_sql
In directory cvs.zope.org:/tmp/cvs-serv30532/gateway_sql

Modified Files:
	PsycopgConnection.py SQLClassification.py SQLFolderItems.py 
	SQLItemId.py SQLKeychainGenerator.py SQLRemainder.py public.py 
Added Files:
	SQLGatewayBase.py SQLObjectData.py 
Log Message:
Provided a way to configure ObjectMappers, with the intent of making
AdaptableStorage easier to explain.  Added IConfigurableObjectMapper
and converted all the mapper setup code to use it.  Included a
checkConfiguration() method which validates the entire object mapper
tree.  Then converted the DBTab-based configuration to use a mapper
factory, which can point to any mapper factory function installed
anywhere.  Tangents to this:

- Refactored Zope2FS and Zope2SQL to use the same code for setting up
mappers, leaving "holes" for the gateways.

- Added connect() and close() methods to ITPCConnection (which doesn't
technically exist yet since I need to choose a name for it. ;-) )

- Factored out common parts of the SQL gateways.

- Implemented the newKey() method of IKeyedObjectSystem, which will
help ZEO environments, in theory.



=== Added File Products/AdaptableStorage/gateway_sql/SQLGatewayBase.py ===
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""SQL gateway base class

$Id: SQLGatewayBase.py,v 1.1 2002/12/24 04:29:32 shane Exp $
"""

class SQLGatewayBase:

    table_base_name = '(override this)'
    checkexist_sql = '(override this)'
    create_sql = '(override this)'
    clear_sql = 'DELETE FROM %(table)s'

    def __init__(self, conn):
        self.conn = conn
        self.table = conn.prefix + '_' + self.table_base_name
        if conn.isConnected():
            self.setupTables()
        else:
            conn.addConnectCallback(self.setupTables)

    def setupTables(self):
        conn = self.conn
        try:
            self.execute(self.checkexist_sql)
        except conn.error:
            conn.db.rollback()
            self.execute(self.create_sql)
            conn.db.commit()

    def execute(self, text, *args, **kw):
        text = text.replace('%(table)s', self.table) # XXX workaround
        return self.conn.execute(text, *args, **kw)

    def clear(self):
        self.execute(self.clear_sql)



=== Added File Products/AdaptableStorage/gateway_sql/SQLObjectData.py ===
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""SQL object data gateway

$Id: SQLObjectData.py,v 1.1 2002/12/24 04:29:32 shane Exp $
"""

from serial_public import IGateway, FieldSchema

from SQLGatewayBase import SQLGatewayBase


class SQLObjectData (SQLGatewayBase):

    __implements__ = IGateway

    schema = FieldSchema('data', 'string')

    table_base_name = 'object_data'

    checkexist_sql = '''SELECT key FROM %(table)s LIMIT 1'''

    create_sql = '''CREATE TABLE %(table)s (
    key int PRIMARY KEY,
    data bytea
    )'''

    read_sql = '''SELECT data from %(table)s
    WHERE key = %(key)s'''

    update_sql = '''UPDATE %(table)s
    SET data = %(data)s
    WHERE key = %(key)s'''

    insert_sql = '''INSERT INTO %(table)s
    (key, data)
    VALUES (%(key)s, %(data)s)'''


    def getSchema(self):
        return self.schema

    def load(self, event):
        key = int(event.getKeychain()[-1])
        items = self.execute(self.read_sql, 1, key=key)
        if items:
            state = items[0][0]
        else:
            state = ''
        return state, state

    def store(self, event, state):
        key = int(event.getKeychain()[-1])
        items = self.execute(self.read_sql, 1, key=key)
        kw = {'key': key, 'data': self.conn.asBinary(state)}
        if items:
            # update.
            self.execute(self.update_sql, **kw)
        else:
            # insert.
            self.execute(self.insert_sql, **kw)
        return state



=== Products/AdaptableStorage/gateway_sql/PsycopgConnection.py 1.3 => 1.4 ===
--- Products/AdaptableStorage/gateway_sql/PsycopgConnection.py:1.3	Tue Dec 10 17:51:37 2002
+++ Products/AdaptableStorage/gateway_sql/PsycopgConnection.py	Mon Dec 23 23:29:32 2002
@@ -22,23 +22,43 @@
 class PsycopgConnection:
 
     _final = 0
+    db = None
+    cursor = None
+    error = psycopg.DatabaseError
 
     def __init__(self, params='', prefix='zodb'):
         self.params = params
-        self.db = psycopg.connect(params)
-        self.cursor = self.db.cursor()
         self.prefix = prefix
-        self.error = psycopg.DatabaseError
+        self.connect_callbacks = []
+
+    def isConnected(self):
+        return (self.db is not None)
+
+    def addConnectCallback(self, f):
+        self.connect_callbacks.append(f)
+
+    def connect(self):
+        self.db = psycopg.connect(self.params)
+        self.cursor = self.db.cursor()
+        for f in self.connect_callbacks:
+            f()
+        self.connect_callbacks = []
 
     def sortKey(self):
-        return self.getName()
+        return repr(self)
 
     def getName(self):
-        return 'Psycopg connection "%s"' % self.params
+        return repr(self)
+
+    def __repr__(self):
+        return 'PsycopgConnection(%s, %s)' % (
+            repr(self.params), repr(self.prefix))
 
     def execute(self, text, fetch=0, cursor=None, **kw):
         if cursor is None:
             cursor = self.cursor
+            if cursor is None:
+                raise RuntimeError('Not connected')
         cursor.execute(text, kw)
         if fetch:
             return cursor.fetchall()
@@ -68,4 +88,11 @@
                 self.db.commit()
             finally:
                 self.reset()
+
+    def close(self):
+        if self.isConnected():
+            self.cursor.close()
+            self.cursor = None
+            self.db.close()
+            self.db = None
 


=== Products/AdaptableStorage/gateway_sql/SQLClassification.py 1.2 => 1.3 ===
--- Products/AdaptableStorage/gateway_sql/SQLClassification.py:1.2	Tue Dec 10 17:27:00 2002
+++ Products/AdaptableStorage/gateway_sql/SQLClassification.py	Mon Dec 23 23:29:32 2002
@@ -18,57 +18,43 @@
 
 from serial_public import IGateway, FieldSchema
 
-checkexist_sql = '''SELECT key FROM %(table)s LIMIT 1'''
+from SQLGatewayBase import SQLGatewayBase
 
-create_sql = '''CREATE TABLE %(table)s (
-  key int PRIMARY KEY,
-  meta_type character varying(255),
-  class_name character varying(255)  
-)'''
 
-read_sql = '''SELECT meta_type, class_name from %(table)s
-  WHERE key = %(key)s'''
+class SQLClassification (SQLGatewayBase):
 
-update_sql = '''UPDATE %(table)s
-  SET meta_type=%(meta_type)s, class_name=%(class_name)s
-  WHERE key = %(key)s'''
-
-insert_sql = '''INSERT INTO %(table)s
-  (key, meta_type, class_name)
-  VALUES (%(key)s, %(meta_type)s, %(class_name)s)'''
-
-clear_sql = '''DELETE FROM %(table)s'''
+    __implements__ = IGateway
 
+    schema = FieldSchema('classification', 'classification')
 
-class SQLClassification:
+    table_base_name = 'classification'
 
-    __implements__ = IGateway
+    checkexist_sql = '''SELECT key FROM %(table)s LIMIT 1'''
 
-    schema = FieldSchema('classification', 'classification')
+    create_sql = '''CREATE TABLE %(table)s (
+    key int PRIMARY KEY,
+    meta_type character varying(255),
+    class_name character varying(255)  
+    )'''
+
+    read_sql = '''SELECT meta_type, class_name from %(table)s
+    WHERE key = %(key)s'''
+
+    update_sql = '''UPDATE %(table)s
+    SET meta_type=%(meta_type)s, class_name=%(class_name)s
+    WHERE key = %(key)s'''
+
+    insert_sql = '''INSERT INTO %(table)s
+    (key, meta_type, class_name)
+    VALUES (%(key)s, %(meta_type)s, %(class_name)s)'''
 
-    def __init__(self, conn):
-        self.conn = conn
-        self.table = conn.prefix + '_classification'
-        try:
-            self.execute(checkexist_sql)
-        except conn.error:
-            conn.db.rollback()
-            self.execute(create_sql)
-            conn.db.commit()
 
     def getSchema(self):
         return self.schema
 
-    def execute(self, text, *args, **kw):
-        text = text.replace('%(table)s', self.table) # XXX workaround
-        return self.conn.execute(text, *args, **kw)
-
-    def clear(self):
-        self.execute(clear_sql)
-
     def load(self, event):
         key = int(event.getKeychain()[-1])
-        items = self.execute(read_sql, 1, key=key)
+        items = self.execute(self.read_sql, 1, key=key)
         classification = {}
         if items:
             rec = items[0]
@@ -82,15 +68,15 @@
 
     def store(self, event, classification):
         key = int(event.getKeychain()[-1])
-        items = self.execute(read_sql, 1, key=key)
+        items = self.execute(self.read_sql, 1, key=key)
         mt = classification.get('meta_type', '')
         cn = classification.get('class_name', '')
         kw = {'key': key, 'meta_type': mt, 'class_name': cn}
         if items:
             # update.
-            self.execute(update_sql, **kw)
+            self.execute(self.update_sql, **kw)
         else:
             # insert.
-            self.execute(insert_sql, **kw)
+            self.execute(self.insert_sql, **kw)
         return (mt, cn)
 


=== Products/AdaptableStorage/gateway_sql/SQLFolderItems.py 1.3 => 1.4 ===
--- Products/AdaptableStorage/gateway_sql/SQLFolderItems.py:1.3	Fri Dec 13 16:21:45 2002
+++ Products/AdaptableStorage/gateway_sql/SQLFolderItems.py	Mon Dec 23 23:29:32 2002
@@ -16,67 +16,51 @@
 $Id$
 """
 
+from SQLGatewayBase import SQLGatewayBase
 from serial_public import IGateway, RowSequenceSchema
 
 
-checkexist_sql = '''SELECT key FROM %(table)s LIMIT 1'''
+class SQLFolderItems (SQLGatewayBase):
 
-create_sql = '''CREATE TABLE %(table)s (
-  key int,
-  name character varying(255),
-  child_key int
-)'''
+    __implements__ = IGateway
 
-read_sql = '''SELECT name, child_key FROM %(table)s
-  WHERE key = %(key)s'''
+    schema = RowSequenceSchema()
+    schema.addField('id', 'string', 1)
+    schema.addField('keychain', 'keychain')
 
-update_sql = '''UPDATE %(table)s
-  SET child_key=%(child_key)s
-  WHERE key = %(key)s and name = %(name)s'''
+    table_base_name = 'folder_items'
 
-insert_sql = '''INSERT INTO %(table)s
-  (key, name, child_key)
-  VALUES (%(key)s, %(name)s, %(child_key)s)'''
+    checkexist_sql = '''SELECT key FROM %(table)s LIMIT 1'''
 
-delete_sql = '''DELETE FROM %(table)s
-  WHERE key = %(key)s and name = %(name)s'''
+    create_sql = '''CREATE TABLE %(table)s (
+    key int,
+    name character varying(255),
+    child_key int
+    )'''
 
-clear_sql = '''DELETE FROM %(table)s'''
+    read_sql = '''SELECT name, child_key FROM %(table)s
+    WHERE key = %(key)s'''
 
+    update_sql = '''UPDATE %(table)s
+    SET child_key=%(child_key)s
+    WHERE key = %(key)s and name = %(name)s'''
 
-class SQLFolderItems:
+    insert_sql = '''INSERT INTO %(table)s
+    (key, name, child_key)
+    VALUES (%(key)s, %(name)s, %(child_key)s)'''
 
-    __implements__ = IGateway
+    delete_sql = '''DELETE FROM %(table)s
+    WHERE key = %(key)s and name = %(name)s'''
 
-    schema = RowSequenceSchema()
-    schema.addField('id', 'string', 1)
-    schema.addField('keychain', 'keychain')
-
-    def __init__(self, conn):
-        self.conn = conn
-        self.table = conn.prefix + '_folder_items'
-        try:
-            self.execute(checkexist_sql)
-        except conn.error:
-            conn.db.rollback()
-            self.execute(create_sql)
-            conn.db.commit()
 
     def getSchema(self):
         return self.schema
 
-    def execute(self, text, *args, **kw):
-        text = text.replace('%(table)s', self.table) # XXX workaround
-        return self.conn.execute(text, *args, **kw)
-
-    def clear(self):
-        self.execute(clear_sql)
-
     def load(self, event):
         keychain = event.getKeychain()
         key = int(keychain[-1])
         prefix = keychain[:-1]
-        rows = self.execute(read_sql, 1, key=key)
+        rows = self.execute(self.read_sql, 1, key=key)
         rows.sort()
         res = [(row[0], prefix + (row[1],)) for row in rows]
         return res, tuple(res)
@@ -89,14 +73,14 @@
         for name, child_keychain in state:
             state_dict[name] = 1
 
-        rows = self.execute(read_sql, 1, key=key)
+        rows = self.execute(self.read_sql, 1, key=key)
         db_dict = {}
         for name, child_key in rows:
             if state_dict.has_key(name):
                 db_dict[name] = child_key
             else:
                 # Remove this item from the database.
-                self.execute(delete_sql, key=key, name=name)
+                self.execute(self.delete_sql, key=key, name=name)
 
         state = list(state)
         state.sort()
@@ -109,9 +93,9 @@
             if db_dict.has_key(name):
                 if db_dict[name] != child_key:
                     # Change the OID of this item.
-                    self.execute(update_sql, **kw)
+                    self.execute(self.update_sql, **kw)
             else:
                 # Add this item to the database.
-                self.execute(insert_sql, **kw)
+                self.execute(self.insert_sql, **kw)
         return tuple(state)
 


=== Products/AdaptableStorage/gateway_sql/SQLItemId.py 1.3 => 1.4 ===
--- Products/AdaptableStorage/gateway_sql/SQLItemId.py:1.3	Fri Dec 13 16:21:45 2002
+++ Products/AdaptableStorage/gateway_sql/SQLItemId.py	Mon Dec 23 23:29:32 2002
@@ -19,33 +19,31 @@
 
 from serial_public import IGateway, FieldSchema
 
+from SQLGatewayBase import SQLGatewayBase
 
-read_sql = '''SELECT name from %(table)s
-  WHERE child_key = %(child_key)s'''
 
-
-class SQLItemId:
+class SQLItemId (SQLGatewayBase):
 
     __implements__ = IGateway
 
     schema = FieldSchema('id', 'string')
 
-    def __init__(self, conn):
-        self.conn = conn
-        self.table = conn.prefix + '_folder_items'
+    table_base_name = 'folder_items'
+
+    read_sql = 'SELECT name from %(table)s WHERE child_key = %(child_key)s'
+
+    def setupTables(self):
+        # No action necessary
+        pass
 
     def getSchema(self):
         return self.schema
 
-    def execute(self, text, *args, **kw):
-        text = text.replace('%(table)s', self.table) # XXX workaround
-        return self.conn.execute(text, *args, **kw)
-
     def load(self, event):
         key = int(event.getKeychain()[-1])
-        rows = self.execute(read_sql, 1, child_key=key)
+        rows = self.execute(self.read_sql, 1, child_key=key)
         assert len(rows) >= 1
-        name = rows[0][0]
+        name = rows[0][0]  # Other names will be ignored
         return name, None
 
     def store(self, event, state):


=== Products/AdaptableStorage/gateway_sql/SQLKeychainGenerator.py 1.2 => 1.3 ===
--- Products/AdaptableStorage/gateway_sql/SQLKeychainGenerator.py:1.2	Tue Dec 10 17:27:00 2002
+++ Products/AdaptableStorage/gateway_sql/SQLKeychainGenerator.py	Mon Dec 23 23:29:32 2002
@@ -16,46 +16,34 @@
 $Id$
 """
 
-from serial_public import IKeychainGenerator
+from serial_public import FieldSchema, IKeychainGenerator, IAspectEvent
 
+from SQLGatewayBase import SQLGatewayBase
 
-checkexist_sql = '''SELECT last_value FROM %(sequence)s'''
 
-create_sql = '''CREATE SEQUENCE %(sequence)s'''
+class SQLKeychainGenerator (SQLGatewayBase):
 
-read_sql = """SELECT nextval('%(sequence)s')"""
+    __implements__ = IKeychainGenerator
 
-clear_sql = """SELECT setval('%(sequence)s', 1)"""
+    table_base_name = 'key_seq'
 
+    checkexist_sql = "SELECT last_value FROM %(table)s"
 
-class SQLKeychainGenerator:
+    create_sql = "CREATE SEQUENCE %(table)s"
 
-    __implements__ = IKeychainGenerator
+    read_sql = "SELECT nextval('%(table)s')"
+
+    clear_sql = "SELECT setval('%(table)s', 1)"
 
-    def __init__(self, conn):
-        self.conn = conn
-        self.sequence = conn.prefix + '_key_seq'
-        try:
-            self.execute(checkexist_sql)
-        except conn.error:
-            conn.db.rollback()
-            self.execute(create_sql)
-            conn.db.commit()
-
-    def getSchema(self):
-        return self.schema
-
-    def execute(self, text, *args, **kw):
-        text = text.replace('%(sequence)s', self.sequence) # XXX workaround
-        return self.conn.execute(text, *args, **kw)
 
     def makeKeychain(self, event, name, stored):
         if not stored:
             raise RuntimeError(
                 'Keychains generated by SQLKeychain must be stored')
-        n = self.execute(read_sql, 1)[0][0]
+        if IAspectEvent.isImplementedBy(event):
+            # Request that the other side do the work (for ZEO)
+            n = event.getKeyedObjectSystem().newKey()
+        else:
+            n = self.execute(self.read_sql, 1)[0][0]
         return event.getKeychain()[:-1] + (n,)
-
-    def clear(self):
-        self.execute(clear_sql)
 


=== Products/AdaptableStorage/gateway_sql/SQLRemainder.py 1.3 => 1.4 ===
--- Products/AdaptableStorage/gateway_sql/SQLRemainder.py:1.3	Tue Dec 10 17:51:37 2002
+++ Products/AdaptableStorage/gateway_sql/SQLRemainder.py	Mon Dec 23 23:29:32 2002
@@ -16,74 +16,28 @@
 $Id$
 """
 
-from serial_public import IGateway, FieldSchema
+from SQLObjectData import SQLObjectData
 
 
-checkexist_sql = '''SELECT key FROM %(table)s LIMIT 1'''
+class SQLRemainder (SQLObjectData):
 
-create_sql = '''CREATE TABLE %(table)s (
-  key int PRIMARY KEY,
-  pickle bytea
-)'''
-
-read_sql = '''SELECT pickle from %(table)s
-  WHERE key = %(key)s'''
-
-update_sql = '''UPDATE %(table)s
-  SET pickle=%(pickle)s
-  WHERE key = %(key)s'''
-
-insert_sql = '''INSERT INTO %(table)s
-  (key, pickle)
-  VALUES (%(key)s, %(pickle)s)'''
-
-clear_sql = '''DELETE FROM %(table)s'''
-
-
-class SQLRemainder:
-
-    __implements__ = IGateway
-
-    schema = FieldSchema('data', 'string')
-
-    def __init__(self, conn):
-        self.conn = conn
-        self.table = conn.prefix + '_remainder'
-        try:
-            self.execute(checkexist_sql)
-        except conn.error:
-            conn.db.rollback()
-            self.execute(create_sql)
-            conn.db.commit()
-
-    def getSchema(self):
-        return self.schema
-
-    def execute(self, text, *args, **kw):
-        text = text.replace('%(table)s', self.table) # XXX workaround
-        return self.conn.execute(text, *args, **kw)
-
-    def clear(self):
-        self.execute(clear_sql)
-
-    def load(self, event):
-        key = int(event.getKeychain()[-1])
-        items = self.execute(read_sql, 1, key=key)
-        if items:
-            state = items[0][0]
-        else:
-            state = ''
-        return state, state
-
-    def store(self, event, state):
-        key = int(event.getKeychain()[-1])
-        items = self.execute(read_sql, 1, key=key)
-        kw = {'key': key, 'pickle': self.conn.asBinary(state)}
-        if items:
-            # update.
-            self.execute(update_sql, **kw)
-        else:
-            # insert.
-            self.execute(insert_sql, **kw)
-        return state
+    __implements__ = SQLObjectData.__implements__
+
+    table_base_name = 'remainder'
+
+    create_sql = '''CREATE TABLE %(table)s (
+    key int PRIMARY KEY,
+    pickle bytea
+    )'''
+
+    read_sql = '''SELECT pickle from %(table)s
+    WHERE key = %(key)s'''
+
+    update_sql = '''UPDATE %(table)s
+    SET pickle = %(data)s
+    WHERE key = %(key)s'''
+
+    insert_sql = '''INSERT INTO %(table)s
+    (key, pickle)
+    VALUES (%(key)s, %(data)s)'''
 


=== Products/AdaptableStorage/gateway_sql/public.py 1.1 => 1.2 ===
--- Products/AdaptableStorage/gateway_sql/public.py:1.1	Tue Dec 10 15:37:52 2002
+++ Products/AdaptableStorage/gateway_sql/public.py	Mon Dec 23 23:29:32 2002
@@ -18,8 +18,10 @@
 
 from PsycopgConnection import PsycopgConnection
 from SQLClassification import SQLClassification
+from SQLGatewayBase import SQLGatewayBase
 from SQLFolderItems import SQLFolderItems
 from SQLItemId import SQLItemId
 from SQLKeychainGenerator import SQLKeychainGenerator
+from SQLObjectData import SQLObjectData
 from SQLRemainder import SQLRemainder