From greg_stein@eshop.com Fri Jan 5 01:10:33 1996 From: greg_stein@eshop.com (Greg Stein) Date: Thu, 4 Jan 1996 17:10:33 -0800 Subject: [PYTHON DB-SIG] database tuple representation Message-ID: Hello all! At one of the lunches at the Workshop, we discussed a way to represent a row from a database query. People wanted to see access via tuple, list, mapping, and attribute schemes. Jim had mentioned that he had some functions (for module implementations) to help with creating this style of interface. As we all know, he has a bit of a problem releasing code, though, due to ownership questions. As a result, I went forward and wrote a couple Python classes to handle this kind of data representation. Now that we actually have a SIG, I have a chance to float this by everyone for feedback, review, etc. I'm hoping that we can finalize something like this and incorporate it into the SIG's output. Lastly, the code below is probably quite generic in terms of Python data processing. Does anybody have a better name for this kind of datatype and its descriptor? -g ----------------------CUT---------------------- # # db.py -- generic database interfaces and objects # # ### other "standard" freeware release crap # # 951207, Greg Stein: created # """\ This module implements various functions and classes and constants for a generic database representation, implementation, and access. """ class TupleDescriptor: """\ Instances of this class are used to describe database tuples (which are typically instances of DatabaseTuple or one of its derivative classes). These instances specify the column names, formats, lengths, and other relevant information about the items in a particular tuple. An instance is typically shared between many database tuples (such as those returned by a single query). Note: the term database tuple is rather specific; in actuality the tuple may have come from non-database sources and/or generated by a process wholly unrelated to databases. Note again: I'm open for new names for this and the DatabaseTuple class andconcept :-) """ def __init__(self, desc): """\ An instance is created by passing a "descriptor" to fully specify the information about the related database tuple. This descriptor takes the form of a tuple or list where each element is a tuple. The first element of this tuple is the name of the column. The following elements of the tuple are used to describe the column (such as length, format, significant digits, etc). """ self.desc = tuple(desc) ### validate the names? self.names = map(lambda x: x[0], desc) self.namemap = { } for i in range(len(self.names)): self.namemap[self.names[i]] = i def __len__(self): """\ A tuple descriptor responds to __len__ to simplify some processing by allowing the use of the len() builtin function. """ return len(self.names) def __repr__(self): return '%s(%s)' % (TupleDescriptor.__name__, repr(self.desc)) def __str__(self): return str(self.desc) class DatabaseTuple: """\ Instances of this class are used to represent tuples of information, typically returned by a database query. A TupleDescriptor is used as a means of describing the information for a variety of access methods. The tuple's information can be accessed via simple indexing, slices, as a mapping where the keys are the column names (as defined by the descriptor), or via attribute-based access (where the attribute names are equivalent to the column names). This object acts as a tuple, a list, a mapping, and an instance. To retrieve "pure" tuples, lists, or mappings, the asTuple(), asList(), and asMapping() methods may be used, each returning a value equal to what this object pretends to be. There exists a potential ambiguity between attempting to act as a list or mapping and the attribute-based access to the data. In particular, if the column names are 'index', 'count', 'keys', 'items', 'values', or 'has_key', then the attribute-based access will have precedence over their related methods for lists and mappings. To actually use these methods, simply apply them to the result of the asList() or asMapping() methods. Note that column names with leading underscores may interfere with the implementation of this class, and as a result may not be accessible via the attribute-access scheme. Also, column names of asTuple, asList, and asMapping will be inaccessible via the attribute-access scheme since those will always represent the methods. To access these columns, the mapping interface can be used with the column name as the mapping key. Note that a database tuple acts as a tuple with respect to sub-scripted assignment. TypeError exceptions will be raised for several situations, and AttributeError may be raised for some methods that are intended to mutate the data (list's 'sort' method) as these methods have not been implemented. """ def __init__(self, desc, data): """\ A DatabaseTuple is initialized with a TupleDescriptor and a tuple or list specifying the data elements. """ if len(desc) != len(data): raise ValueError # descriptor does not seem to describe tuple if type(desc) == type(()) or type(desc) == type([]): desc = TupleDescriptor(desc) self.__dict__['_desc_'] = desc self.__dict__['_data_'] = tuple(data) def __str__(self): return str(self._data_) def __repr__(self): return '%s(%s,%s)' % (DatabaseTuple.__name__, repr(self._desc_), repr(self._data_)) def __cmp__(self, other): if type(self._data_) == type(other): return cmp(self._data_, other) if type(self._data_) == type( {} ): return cmp(self.asMapping(), other) if type(self._data_) == type( () ): return cmp(self.asTuple(), other) if type(self) == type(other): ### fix this: need to verify equal classes return cmp(self._data_, other._data_) return cmp(self._data_, other) def __getattr__(self, name): 'Simulate attribute-access via column names' return self._getvalue_(name) def __setattr__(self, name, value): 'Simulate attribute-access via column names' ### need to redirect into a db update raise TypeError, "can't assign to this subscripted object" def __getitem__(self, key): 'Simulate indexed (tuple/list) and mapping-style access' if type(key) == type(1): return self._data_[key] return self._getvalue_(key) def __setitem__(self, key, value): 'Simulate indexed (tuple/list) and mapping-style access' if type(key) == type(1): ### need to redirect into a db update of elem #key raise TypeError, "can't assign to this subscripted object" ### need to redirect into a db update of elem named key raise TypeError, "can't assign to this subscripted object" def __len__(self): return len(self._data_) def __getslice__(self, i, j): 'Simulate list/tuple slicing access' return self._data_[i:j] def __setslice__(self, i, j, list): 'Simulate list/tuple slicing access' ### need to redirect into a db update of elems raise TypeError, "can't assign to this subscripted object" def _keys_(self): "Simulate mapping's methods" return self._desc_.names def _has_key_(self, key): "Simulate mapping's methods" return key in self._desc_.names def _items_(self): "Simulate mapping's methods" return self.asMapping().items() def _count_(self, item): "Simulate list's methods" return self.asList().count(item) def _index_(self, item): "Simulate list's methods" return self.asList().index(item) def _getvalue_(self,name): 'Internal method for named-based value retrieval' if name not in self._desc_.names: if name == 'keys': return self._keys_ if name == 'items': return self._items_ if name == 'values': return self.asList if name == 'has_key': return self._has_key_ if name == 'count': return self._count_ if name == 'index': return self._index_ raise AttributeError return self._data_[self._desc_.namemap[name]] def asMapping(self): 'Return the "tuple" as a real mapping' value = { } for name, idx in self._desc_.namemap.items(): value[name] = self._data_[idx] return value def asTuple(self): 'Return the "tuple" as a real tuple' return self._data_ def asList(self): 'Return the "list" as a real mapping' return map(None, self._data_) ================= DB-SIG - SIG on Tabular Databases in Python send messages to: db-sig@python.org administrivia to: db-sig-request@python.org ================= From shprentz@bdm.com Wed Jan 10 19:21:08 1996 From: shprentz@bdm.com (Joel Shprentz) Date: Wed, 10 Jan 1996 14:21:08 -0500 Subject: [PYTHON DB-SIG] Objects and Relational Databases - Part 1 Message-ID: <199601101851.NAA02313@bdm.com> This and the following message are forwarded from the Coad Letter mailing list, which is sent monthly from Object International. In these two messages, Peter Coad discusses how problem domain objects should interact with data management objects. The discussion extends the data management appendix to Coad's object modelling book, which was published last year. -- Joel Shprentz (202) 863-3121 BDM Federal, Inc. shprentz@bdm.com 1501 BDM Way McLean, VA 22102 ------------------------- Forwarded Message Follows -------------------------- Return-Path: From: Date: Tue, 9 Jan 96 09:12:21 CST To: coad-letter@oi.com Subject: 23a--Data Management--"Objects and RDB" Q&A (Part 1) Sender: owner-coad-letter@oi.com Reply-To: owner-coad-letter@oi.com The Coad Letter Issue 23a Category: Data Management Title: "Objects and RDB" Q&A (Part 1) Date: Tuesday, January 9, 1996 Dear Friend, Happy New Year! And welcome to this special two-part issue of The Coad Letter. This is a *very* technical issue, aimed squarely at developers who are developing object apps that include relational databases. Presented in Q&A format, it builds upon the material contained in the book, "Object Models: Strategies, Patterns, and Applications." This will be an *awesome* new year. More, later! Sincerely, Peter Coad ---------------------------------------------------- "Objects and RDB: App Architecture and Other Practical Considerations" ---------------------------------------------------- Peter Coad and David North (with added insights from Ken Ritchie) January 1996 This special report builds upon the concepts presented in the book, "Object Models: Strategies, Patterns, and Applications." Familiarity with that book (notably the data management concepts in Chapter 1 and in Appendix C) is a prerequisite to benefiting from the question-and- answer format that follows. Acronyms: DB = database DM = data management HI = human interaction PD = problem domain RDB = relational database ODB = object database Q. What's the impact on PD classes and PD objects, when using an RDB? Every PD class with persistent objects inherits from the PersistentPD class: ------------ |PersistentPD| |------------| |inDataBase | |changed | |dmObject | |------------| |isInDB | |hasChanged | |save | |refresh | |delete | |new (c) | ------------ | specialization classes... Notes isInDB: a DM object invokes this service, asking a PD object if it is already in the database -- and deciding whether to "insert" or "update" hasChanged: a DM object invokes this service, asking a PD object if it has changed (to avoid unnecessary saving to the database) save: this service invokes the "save" service in DM object that is responsible for objects in this class refresh: this service invokes the "refresh" service in DM object that is responsible for objects in this class delete: this service invokes the "delete" service in DM object that is responsible for objects in this class new (c): this is a class (c) service. it needs to set "inDataBase" to false. and it needs to send the newly created object to the DM object for objects in that PD class. Q. Okay, then, so what about the DM component? What's needed there? A DMserver class: a DMserver object takes care of behavior across the collection of all of the DM objects. Each persistent PD class has a corresponding DM class; and there is just one DM object in that class. ------------------- ---------- | DMserver | | DM | |-------------------|n |----------| |dbConnection |-------------|pdObjects | |-------------------| |----------| |getDMobjectForClass| |save | |openConnection | |load | |closeConnection | |loadAll | ------------------- |delete | |asDBstring| |asDBfloat | | ... | ---------- | specialization classes... Notes getDMobjectForClass: given a class name or identifier as an argument, this class returns the corresponding DM object openConnection: opens a connection to an RDB closeConnection: closes a connection to an RDB save: given a PD object as an argument, this service invokes: pdObject.hasChanged -- to see if a change has occurred pdObject.inDataBase -- to determine whether an "insert" or "update" is appropriate finally, it saves the result to an RDB. load: given a key, this service searches objects already in memory, and otherwise loads values, creates an object, and initializes it. loadAll: given a table name, for the entire table: load values, create and intialize objects. delete: given a PD object as an argument, this service deletes it from an RDB. asDBstring, asDBfloat, ...: data type conversion services, for moving between programming language data types and RDB data types. Additional note for save, load, loadAll, and delete: specific SQL commands are detailed in each class that is a specialization of DM. Q. How do you work with HI-PD-DM objects? A. Here is an example. Begin with HI. Use a selection window (lists employees; buttons: add/update/close). And use a maintenance window (data entry fields for an individual employee; buttons: save/cancel). Now consider what happens when someone wants to add an employee. - someone pushes the "add" button on an employee selection window - the button invokes emplSelWindow.doAdd - here is what happens next: -------------- --------------- ------ -------- |EmplSelWindow | |EmplMaintWindow| | Empl | | EmplDM | |--------------| |---------------| |------| |--------| |doAdd | | | | | | | |-->addEmpl | | | | | | | ( ; empl) | |---|---------------|-->|new | | | ( ; empl) | |-->|openOn | | | | | (empl ; empl) |IF | | | | | | | //save |-->connectEmpl| | | | | | | (company ; ) |-->saveEmpl | | | | | | | (empl ; ) | |---|---------------|-->|save | | | ( ; ) | |---|---------------|---|------|-->|saveThis| (empl ; ) |ENDIF | | | | | | | -------------- --------------- ------ -------- Q. How about another example, where a PD object might interact with a DM object, other than the one it directly corresponds with? A. How about this: get department for department number. --------------- -------- ------------- -------------- |EmplMaintWindow| |Employee| | Company | | DepartmentDM | |---------------| |--------| |-------------| |--------------| |deptFieldExit | | | | | | |(num ; ) | |-->|setDept | | | | |(num ; ) | | | |-->|getDeptForNum| | |(num ; dept) | | | | | |-->|loadForDeptNum|(num ; dept) --------------- -------- ------------- -------------- The departmentDM object checks for the object in memory; if it is not in memory, the departmentDM object then: - invokes an SQL command to get the values it needs - sends a message to the department class, to create and initialize the needed department object - returns the department object to the sender. Q. Are there any more responsibilities for the PersistentPD class, meaning, the class that all PD classes (with persistent objects) inherit from? Here is the current list that we have: - attributes: inDataBase, changes - services: isInDB, hasChanges, save, refresh, delete, getDMobject, new (c) where (c) indicates a class attribute or service A. [Peter] Add these attributes: - correspDM -- the corresponding DM object - dmServer (c) -- the one DM server object [David] You may want to keep track of the old key values (the key values used the last time it was saved to the DB) so that you can update the correct row if you allow someone to change a value of an attribute that's a part of the key. That's all I think of, but there probably will be more you will find as you implement it and want to make it smarter. [Ken] Yes, I would *organize* the rows by OID (using object ID as "primary" key). Secondary index paths can be created, updated and destroyed at will--all without disturbing either the object store on disk or cached objects in memory. OID's are usually immutable. You can add object versioning by extending the organizing OID with a version ID, and organizing tables on OID+VID. Be careful not to include the VID in object associations, unless you don't mind the extra overhead for updates. Time series versioning can use a clock stamp in place of the VID. Normal views select latest VID for each OID used as a foreign key. Q. How do you keep the design document for the DM objects? If in the object model, do you write the connections in? A. [Peter] Yes, DM objects appear in the object model, in the data management component. DM objects appear in scenario views, too. Regarding object connections between DM and PD objects: show the object connections with text (as attributes), rather than with graphics (sometimes "short words are worth many pictures"). [David] Yes, if you are building specific DM classes for each PD class. They will have some generic services (not shown) but they will have some specific services that will be shown. Q. If you work out all of the interactions with the PD objects, you might find that it requires several SQL searches. Here is an example: calculate gender mix for each internal organization within a company. However, if you use a select statement using a join, then you can get back all of the data for all of the organizations in one table. For example, employee -- assignment -- company. You might do a join, getting a table with employees and respective company names. In this case, using a join seems like a good choice, for efficiency's sake. How would you fit this into the object model (which PD object, which DM object)? A. [Peter and David] Ask each "internal organization" object such a question -- and get the responses you need. If you wanted to do this for all internal organizations, you could ask an object that knows all of the internal organizations. Yes, a company object. - A company messages companyDM (getGenderCountsByIntOrg). If a join is needed (as it is in this example): - The companyDM object does the join, with an SQL statement. To do a join, it must know all of the objects that are involved in that join. If just a select is needed - The companyDM object messages the dmServer, asking for the employeeDM object. - The companyDM object messages the employeeDM object. - The employeeDM object does a select, with an SQL statement. This database interaction results in multiple values. You will need some way to represent these values in an object-oriented way, to return the result to the object that sent the message. This approach allows you to use the power of the RDB and still fit within the object world. And, scenario views take on added reality! Here, you'd replace an "employee" column: company -- employee with a "companyDM" column: company -- companyDM showing how you will interact with an RDB. That brings added reality to the model, especially for a system with 10,000 employee objects. Very nice indeed! You will need to take care of such specialized queries, one- by-one. Q. When you want some kind of report generated from a database, the use of a select statement using joins is often more efficient. How would you fit this into the object model (which PD object, which DM object)? A. [David] You will need a strategy for generating reports. Are you using a tool or are you going to hand-develop each report? I still like using the object paradigm when creating reports. For many reports, it works well to have a collection of objects that correspond to the lines of the reports, with services in those objects for each line. Making the database access match up to this in an efficient way takes some work. Keeping the object model in your development means that you may not always make use of all of the capabilities of the RDB. [Peter] That is not so bad. Since when does anyone use all the capabilities of any tool available for their use? Anyway, your app could be more understandable, more resilient to change. That's often a worthwhile tradeoff. Q. When you have an existing RDB, and cannot change the database structure: is it still meaningful to build an object model? A. [Peter] Yes! Certainly, RDB and SQL make it easy and convenient to reply to complex value-based queries across a collection. And DM objects can readily take advantage of that. There are some meaningful benefits for building an object model: - Adding problem-domain understanding, without RDB clutter. - Discovering functionality that adds competitive advantage. - Adding understanding of what attributes are really needed -- from working out dynamics with scenarios. - Adding understanding of functions -- from working out dynamics with scenarios. - Adding reuse impact -- reuse of both data and functions. - Gaining the benefit of better fit with the many tools that are available for building apps using objects. [David] Yes! then Q. How do you work with the existing tables? With view tables? (Some views may not support updates; so this is probably not enough)? By letting DM objects work with whatever tables already exist? In this context, would an existing data model be of help? If so, how? A. [Peter] By letting each DM object work with one or more existing tables -- whatever it needs to get its job done. In this context, a data model of the existing tables is quite helpful -- while looking for additional PD classes and attributes, content to add in after you've developed an initial object model with domain experts. A data model of existing tables is helpful when working out the details for each DM object, too. [David] Use them as input to the attributes you might need in your PD objects. Then do the mapping. If your application creates new objects, you will need all of the required fields in a table, so you can do an insert. If not, your objects will not have to include attributes for all of the columns in a table. You can use views just like mapping to tables. If the view is a join (other than a self-join), then you (1) can't update through it, and (2) need special DM objects that can create multiple objects from the same row. Q. When you want to provide adhoc query capabilities within an application, how does that fit into an object model? Is this a case of human interaction objects interacting with data management objects? (Applications might add security measures for such adhoc querying.) A. [Peter] Yes, this is a case where a human interaction object would directly interact with DM objects. Preferred approach: interact through an intermediary PD object, an Application object (a PD object that knows things about the overall application), so that the interaction pattern always follows HI--PD--DM. Such architectural consistency is quite helpful (easier to understand, easier to work with, easier to communicate to others). [David] You have two choices. You can have an adhoc application that really just builds SQL requests and sends them to the DM. Or you can build your adhoc queries around services that your PD objects support. _________________________________________________________________________ Peter Coad Object Intl, Inc. Education-Tools-Consulting. Founded in 1986 "Helping teams deliver frequent, tangible, working results" 8140 N MoPac 4-200, Austin TX 78759 USA 1-512-795-0202 fax 1-512-795-0332 direct line 1-919-851-5422 fax 1-919-851-5579 FREE newsletter. Write majordomo@oi.com, message: subscribe coad-letter coad@oi.com info@oi.com http://www.oi.com/oi_home.html pgp: 3DBA 3BDD 57B6 04EB B730 9D06 A1E1 0550 (public key via finger) ================= DB-SIG - SIG on Tabular Databases in Python send messages to: db-sig@python.org administrivia to: db-sig-request@python.org ================= From shprentz@bdm.com Wed Jan 10 19:23:32 1996 From: shprentz@bdm.com (Joel Shprentz) Date: Wed, 10 Jan 1996 14:23:32 -0500 Subject: [PYTHON DB-SIG] Objects and Relational Databases - Part 2 Message-ID: <199601101853.NAA02566@bdm.com> This and the previous message are forwarded from the Coad Letter mailing list, which is sent monthly from Object International. In these two messages, Peter Coad discusses how problem domain objects should interact with data management objects. The discussion extends the data management appendix to Coad's object modelling book, which was published last year. -- Joel Shprentz (202) 863-3121 BDM Federal, Inc. shprentz@bdm.com 1501 BDM Way McLean, VA 22102 ------------------------- Forwarded Message Follows -------------------------- Return-Path: From: Date: Tue, 9 Jan 96 14:07:06 CST To: coad-letter@oi.com Subject: 23b--Data Management--"Objects and RDB" Q&A (Part 2) Sender: owner-coad-letter@oi.com Reply-To: owner-coad-letter@oi.com The Coad Letter Issue 23b Category: Data Management Title: "Objects and RDB" Q&A (Part 2) Date: Tuesday, January 9, 1996 Continuing from Part I -- ---------------------------------------------------- "Objects and RDB: App Architecture and Other Practical Considerations" ---------------------------------------------------- Peter Coad and David North (with added insights from Ken Ritchie) January 1996 Q. Do you simply add attributes and services that came from a DM point-of- view to PD objects. For example: - Added id's and services that are only used internally - Added correlation objects - Taking a problem-domain attribute and breaking it up into fields - Taking a problem-domain attribute and adding a new class. A. [Peter] The model changes, it's true. And PD objects are affected by this. This is why view management is so very important. Responses for each example: - Added id's and services that are only used internally -- Hide these with view management. - Added correlation objects -- These are a problem. What I've done is let domain experts know I need them for relating objects in one class to another. - Taking a problem-domain attribute and breaking it up into fields -- Here, you are simply choosing to show more detail about the attributes in an object model. And that is just fine (not a big deal). - Taking a problem-domain attribute and adding a new class -- An improvement to the object model; might even be added insight that comes from building part of the app; and that's just fine. [David] Agreed. No need ending up with two models (both an object model and a data model); use an object model all the way. Q. About the DMserver object. What are its services? It seems like they are: - Find a specific DM object. - Maintain a changes log. - Save all. Does a significant difference arise if one chooses to make each DM object global, rather than have a DM server watch over them? An HI object could tell each DM object to save itself, couldn't it? A. [David] The DMserver keeps the change log in the automated version of DM. Not much change if all of the DMobjects are global. Just more global variables; I prefer to have fewer. If you are keeping a change log of all of the objects that are changed, then the HI might send the saveAll to the DMserver to save all of the changed objects. then Q. Would a DMserver object have some other services? A. [David] Yes: connect/disconnect to database(s). Q. The "change log" attribute in the DM Server object. What is it? A. [Peter] It is a collection of objects that have changed. [David] Includes deletions -- plus additions and deletions to collections, too. then Q. If it is a collection of objects, how does the DMserver work with them. Does it tell each changed PD object to save itself? Or? A. [David] Yes, the DMserver asks each to save -- and then each PD object asks its DM object to "save me." then Q. Should the change log be held by the DMserver -- or by each DM object (each responsible for the objects within a specific PD class)? A. [David] It could, but then a saveAll would have to ask each DMobject if it had any changes -- instead of just knowing which ones need to be saved. Order of the changes can be important (integrity constraints in the DB) and this can be preserved in a single change log. Q. Could you show us an example of a real DM object, how it is really done (Smalltalk would be fine)? Really, we are looking for a pseudo-code example of how a real DM object does its job -- including database interaction. A. [David] The ones that we have are like the automated version I discussed. Because they provide generic support for any object, they are very complicated. If I can work it in, I will build a simple one and send it to you, for you to look at. Q. How many objects does one bring into memory at one time? A challenging question. Yet is there some way of deciding how many should be brought in at one time? A. [Peter] That is an age-old question, when working with RDB. One heuristic: use problem-domain groupings. [David] Agreed. Usually it is one at a time or a group to support a collection. then Q. If an RDB cursor can be used in this way, can you provide an example? A. [David] Use the cursor when you want to bring in a collection of objects (loadAll, loadEmpForDept) 1) special service in the DM for this 2) issue the SQL request 3) process each row returned by the cursor and convert it to an object -- and add that object to the collection to be returned. Q. PersistentPD class? Is there a PD class, too? Are all PD classes persistent? (If not, please give examples.) A. [Peter] A PD class? Usually, no. Classes with no attributes (e.g., classes added to encapsulate behavior across a collection, with just one object in a class) do not need to be persistent; the solo objects are simply created and initialized at startup each time. [David] The PersistentPD class is the generalization class for any PD class that needs persistence. [David] Most of the time in business applications, all of the PD classes are persistent. There are cases (as Peter mentioned) in which PD classes are not persistent. I'd add these special cases to the list: special window or report structures (often just containers of persistent objects). Q. Triggers? If the database uses triggers to support its consistency, how does that relate to content in the PD component? For example, if super-org is deleted, then its sub-orgs are deleted. Who does this? And how is it shown in an object model? A. [David] If you want the database to automatically do the work by triggers, then the only place to describe that work is in the service description or comments in the object model. The example you mentioned, the delete for super-org, does all the work of deleting all of the sub-orgs; only in the delete service description would that work be described. You need to be a bit careful about this, because what if you had sub-org objects in memory, yet they end up getting deleted in the database without getting removed from memory? Again, keeping the object model in your development means that you may not always make use of all of the capabilities of the RDB. Q. If you are building a client-server system, where do you allocate the objects? Some possibilities: - some PD on each - HI to client; DM to server - DM on client, sending SQL to server A. [Peter] HI-PD-DM on the client and the RDB on the server. SQL is passed over the LAN. [David] Agreed. Q. Can you recommend a product that supports network messaging -- using objects (e.g., an object request broker of some sort). Any experience with this? A. [Peter] Mark Mayfield is working on a banking app that is doing development in this area. [David] We are looking at HP-Distributed Objects using VisualWorks, but we don't have much experience with it yet. Some of the ODB vendors also have support for this. Q. HI-PD-DM is much like the three-tiered model. Yet is this the same three-tiered system architecture model? A. [Peter] Good point. HI-PD-DM is a software architecture. The three-tiered model is most often used in the context of system architecture. These two are related (both a triad; both a separation of concerns), yet the mapping is not necessarily 1:1. [David] Conceptually they both come from the desire to separate the responsibilities. Usually today the three-tiered model is talking about the number of machines in the system architecture and the responsibilities of the machines. Q. RDBs each have their strengths. The available services vary, product to product. How many of those services should one try to use? Use it to get the most out of the database? Or limit use to what fits well with the PD objects? What should one think of in making such decisions? A. [David] The answers to all of these questions are based on application-specific tradeoffs. No strategies on this, just yet. Q. We have heard that the SQL-3 standard has some ODB capabilities defined in it. Is that really so? What might be the impact on mapping an object model to RDB? A. [Peter and David] Don't know. Q. Why are object databases not very popular for business apps? A. [Peter] RDBs are at the heart of building business systems. The common technology for business apps is the use of an RDB. More common than any one language. More common than any one operating system. Moreover, the content of an RDB represents very significant value to an organization. Few organizations could survive any major disruption of that content. Yet I see increased interest in this area; time will tell. [David] They are new and don't have a track record. The companies that offer them are (relatively) small. This all adds up to perceived risk. However, there are many business that are starting to use them and I expect that to increase. [Ken] Yes, this is a case of history repeating itself. Many business systems support high transaction rates (lots of updates) and/or large volumes of data (lots of rows in tables). A dozen years ago this very same concern generated resistance to relational systems. The new database technology will need time to mature and become more robust and more efficient (and supported with all necessary maintenance utilities) before it will gain significant market share. Q. What ODBs would you recommend for business applications? A. [Peter and David] ObjectStore, Versant, Gemstone. Q. DMserver takes care of application connect and disconnect with a database. Who handles the open and close of each table in the database? Each DM object? How often -- each time a DM object needs to interact with the database? Or? A. [David] In SQL you don't open or close tables. If you did, a DM object would handle it, though. Q. Many-to-many object connections: Is adding a correlation class the only way to implement this? Is there another way? A. [Peter] A transaction class (or a transaction class with no attributes, called a correlation class) is needed. [David] You have to have the table in the RDB to contain the information about the connection. The easiest way to use this table is to add a class to the object model. The only other choice is to make your dmObject smart enough to know about the table. Q. Are there any other responsibilities for the DM object, to map PD objects to database tables? Can they or should they provide other services? A. [David] Yes, the DM objects have the responsibility to do the mapping and you may implement several services to accomplish this work. In addition to the mapping, the DM objects do the actual communication with the database using SQL for load, save, delete. DM objects may have special services to support specific database requests. DM objects also make sure that there is only one of a given object in memory. [Ken] I prefer to resolve every many-to-many relationship in the model by injecting a correlation class into the relationship. Often there will be some additional responsibility for the correlation class, and the explicit use of such a class helps in the discovery. These correlation objects must also be persistent if the others are. Consider an example. A person may enroll in several workshops. Each workshop may have a number of persons enrolled. Thus, we recognize a many-to-many relationship between "person" and "workshop." We might choose the name "enrollment" for a correlation class, placed between the "person" and the "workshop" classes. The constraints on the enrollment class indicate exactly one person and one workshop. Now imagine this possibility: We could be asked, later on, to also keep track of the enrollment date for each specific pairing of person and workshop. If we have already identified a correlation class, we know exactly where to place the responsibility for the enrollment date. The correlation becomes a transaction class for each specific enrollment event remembered. Q. If you have several databases, and you have to access them within a single application, would the DM objects take care of all of the concerns of dealing with multiple databases? A. [Peter] Yes. Very helpful to use a class library that supports all of them consistently (e.g., Rogue Wave's dbTools). [David] Yes, this should be encapsulated in the DM objects. You will probably have to do special work to have connections to multiple databases at a time, especially if they are from different vendors. Peter is right; library support for this is valuable. Q. If you are building a distributed system, is there a good way of distributing objects among the processors? Guidelines? A. [Peter] Low coupling, high cohesion -- so distributing in light of groups of interacting objects is helpful. You make best engineering judgments; implement; and then adjust again and again. Q. What is the status of distributed business systems in the US? In the conferences, we hear a lot about distributed object systems. Yet are they really used in business apps at this point? A. [Peter] Mostly just client-server, with objects on the client and RDB on the server. Oracle has placed venture funding in a firm that is developing an object database for the client side, specifically supporting this concept (the startup is called Omniscience). Another company we're watching is Persistence (promising yet very pricey). [David] I have worked with client-server apps (multiple machines, multiple databases). Yet distributed is certainly more than client-server. Work is being done in this area by some of the larger companies. Many conference speakers talk about such matters, yet it takes a while for most businesses to sort things out and apply new technologies. Q. Who checks constraints, upon exiting a field? A PD object? Seems reasonable. Yet another approach is to do type checking and format checking (at least, simple checking of values) within the human interface. In fact, certain class libraries support such data entry checking. So, it seems like HI objects could do basic field checking and then let PD objects do more detailed checking after that. Agreed? A. [Peter and David] Let the HI object do that which is easy: type, size, range, and mandatory/required (for example, a person's last name is: string, 20, alpha, mandatory). Let PD objects check for inter-attribute consistency and algorithmic checks (checking to make sure that the next assignment does not put an employee over an agreed-upon maximum number of active assignments). [Ken] When a conflict arises, an HI object will have the responsibility to notify the user about the specific problem. You may be able to guide the user toward correcting it by taking some subsequent action. In some RDB's, you can assert referential integrities (data dependencies between objects). If the RDB raises an exception, the DM object may have to detect that by checking the results of each update query. When a transaction fails, the DM object would most likely notify its corresponding PD object. Then, the PD object can alert the HI object which had initiated the sequence. When RDB integrities aren't available, put the logic into the PD object, where it will be most apparent (and least complicated). ===== _________________________________________________________________________ Peter Coad Object Intl, Inc. Education-Tools-Consulting. Founded in 1986 "Helping teams deliver frequent, tangible, working results" 8140 N MoPac 4-200, Austin TX 78759 USA 1-512-795-0202 fax 1-512-795-0332 direct line 1-919-851-5422 fax 1-919-851-5579 FREE newsletter. Write majordomo@oi.com, message: subscribe coad-letter coad@oi.com info@oi.com http://www.oi.com/oi_home.html pgp: 3DBA 3BDD 57B6 04EB B730 9D06 A1E1 0550 (public key via finger) ================= DB-SIG - SIG on Tabular Databases in Python send messages to: db-sig@python.org administrivia to: db-sig-request@python.org ================= From greg_stein@eshop.com Sat Jan 13 02:58:36 1996 From: greg_stein@eshop.com (Greg Stein) Date: Fri, 12 Jan 1996 18:58:36 -0800 Subject: [PYTHON DB-SIG] Strawman API Message-ID: Michael McLay posted a stawman on the Meta SIG while we were starting up this list. I've lost that post (can somebody resend that, please?), but he had the right idea. Let's get a strawman out that can be discussed and shot full of holes and rebuilt. This strawman falls under the DB SIG's goal of "Create a generic interface for tabular databases." Here is a strawman for an API exposed by a database module, based on some of my own experience with the Oracle module. Some questions I pose are: - do we need a close() method or can we just assume the user can clear out all references? - what should the format be for describe() - does anything need to change for callproc() ? - should we attempt to can we extend this API to include non-tabular databases? e.g. DBM, object databases, etc. Is it even possible to? Related to this... it looks like I'm going to be building an Informix module in the next few weeks. I have no problem changing things later, but I would hope that we can get a basic structure down for a database API that wouldn't throw things off too much :-). I would think that I could release the Informix module in about 8 weeks, and possibly some prereleases sooner. -g ----- class : def __init__(self, connection_string): "Open a connection to a database, returning a Connection instance" # last Cursor opened self.implicit_cursor = None def close(self): "Close the connection now (rather than whenever __del__ is called)." pass def commit(self): "Commit any pending transaction to the database." pass def rollback(self): "Roll the database back to the start of any pending transaction." pass def cursor(self): "Return a new cursor." pass def callproc(self, name, params=None): """\ Call a stored procedure with the given (optional) parameters. Return the result of the store procedure.""" pass # Implement all Cursor methods (except close), by routing the methods # to self.implicit_cursor. This cursor will be opened on the first # exec() on self. If the database has no concept of cursors, then # simply perform the operation. class Cursor: def close(self): "Close the cursor now (rather than whenever __del__ is called)." pass def exec(self, operation, params=None): """\ Execute (prepare) a database operation (query or command). Parameters may be provided (as a tuple) and will be bound to variables in the operation.""" pass def execagain(self, params=None): """\ Rebind parameters to the current operation and execute again. If no parameters are passed, execute the same operation with the same parameters again.""" pass def fetchone(self): "Fetch the next row of a query result, returning a single tuple." pass def fetchmany(self): """\ Fetch the next set of rows of a query result, returning as a list of tuples. An empty list is returned when no more rows are available. The cursor's arraysize determines the number of rows attempted to be fetched.""" pass def fetchall(self): "Fetch all rows of a query result, returning as a list of tuples." pass def describe(self): """\ Describe the row returned by the last prepared query. Format is a 7-element tuple giving (name, type_code, display_size, internal_size, precision, scale, null_ok).""" pass def arraysize(self, size=None): "Get or set the arraysize (number of rows to fetch) for fetchmany()." pass def setbufsize(self, size, col=None): "Set a column buffer size for fetches of large columns (e.g. LONG)." pass error = ".error" ================= DB-SIG - SIG on Tabular Databases in Python send messages to: db-sig@python.org administrivia to: db-sig-request@python.org ================= From greg_stein@eshop.com Sat Jan 13 02:58:36 1996 From: greg_stein@eshop.com (Greg Stein) Date: Fri, 12 Jan 1996 18:58:36 -0800 Subject: [PYTHON DB-SIG] Strawman API Message-ID: Michael McLay posted a stawman on the Meta SIG while we were starting up this list. I've lost that post (can somebody resend that, please?), but he had the right idea. Let's get a strawman out that can be discussed and shot full of holes and rebuilt. This strawman falls under the DB SIG's goal of "Create a generic interface for tabular databases." Here is a strawman for an API exposed by a database module, based on some of my own experience with the Oracle module. Some questions I pose are: - do we need a close() method or can we just assume the user can clear out all references? - what should the format be for describe() - does anything need to change for callproc() ? - should we attempt to can we extend this API to include non-tabular databases? e.g. DBM, object databases, etc. Is it even possible to? Related to this... it looks like I'm going to be building an Informix module in the next few weeks. I have no problem changing things later, but I would hope that we can get a basic structure down for a database API that wouldn't throw things off too much :-). I would think that I could release the Informix module in about 8 weeks, and possibly some prereleases sooner. -g ----- class : def __init__(self, connection_string): "Open a connection to a database, returning a Connection instance" # last Cursor opened self.implicit_cursor = None def close(self): "Close the connection now (rather than whenever __del__ is called)." pass def commit(self): "Commit any pending transaction to the database." pass def rollback(self): "Roll the database back to the start of any pending transaction." pass def cursor(self): "Return a new cursor." pass def callproc(self, name, params=None): """\ Call a stored procedure with the given (optional) parameters. Return the result of the store procedure.""" pass # Implement all Cursor methods (except close), by routing the methods # to self.implicit_cursor. This cursor will be opened on the first # exec() on self. If the database has no concept of cursors, then # simply perform the operation. class Cursor: def close(self): "Close the cursor now (rather than whenever __del__ is called)." pass def exec(self, operation, params=None): """\ Execute (prepare) a database operation (query or command). Parameters may be provided (as a tuple) and will be bound to variables in the operation.""" pass def execagain(self, params=None): """\ Rebind parameters to the current operation and execute again. If no parameters are passed, execute the same operation with the same parameters again.""" pass def fetchone(self): "Fetch the next row of a query result, returning a single tuple." pass def fetchmany(self): """\ Fetch the next set of rows of a query result, returning as a list of tuples. An empty list is returned when no more rows are available. The cursor's arraysize determines the number of rows attempted to be fetched.""" pass def fetchall(self): "Fetch all rows of a query result, returning as a list of tuples." pass def describe(self): """\ Describe the row returned by the last prepared query. Format is a 7-element tuple giving (name, type_code, display_size, internal_size, precision, scale, null_ok).""" pass def arraysize(self, size=None): "Get or set the arraysize (number of rows to fetch) for fetchmany()." pass def setbufsize(self, size, col=None): "Set a column buffer size for fetches of large columns (e.g. LONG)." pass error = ".error" ================= DB-SIG - SIG on Tabular Databases in Python send messages to: db-sig@python.org administrivia to: db-sig-request@python.org ================= From skip@calendar.com (Skip Montanaro) Sat Jan 13 12:32:55 1996 From: skip@calendar.com (Skip Montanaro) (Skip Montanaro) Date: Sat, 13 Jan 1996 07:32:55 -0500 Subject: [PYTHON DB-SIG] Strawman API In-Reply-To: References: Message-ID: <199601131232.HAA04504@dolphin.automatrix.com> Greg Stein writes: Some questions I pose are: - do we need a close() method or can we just assume the user can clear out all references? One of the problems I run into using simpler modules like bsddb is that of shared access. I could care less if I have to close() the object, but how long can I leave it open? Obviously, the more I have to open() and close() it, the more performance is reduced. I'm not sure if this is a factor for true database modules, since they presumably implement locking to support concurrent read/write operations. Skip Montanaro | Looking for a place to promote your music venue, new CD skip@calendar.com | or next concert tour? Place a focused banner ad in (518)372-5583 | Musi-Cal! http://www.calendar.com/concerts/ ================= DB-SIG - SIG on Tabular Databases in Python send messages to: db-sig@python.org administrivia to: db-sig-request@python.org ================= From Dr Peter J. Braam" Message-ID: I am pretty new to the list, excuse me if I am missing the point. I would like to make some further suggestions regarding the API. What is suggested to be implemented here is a set of classes, which we presumably would build _using_ the API libraries of the DB Server. This is certainly a necessary and useful set of classes to have. The classes would be directly useful to access the DB on any platform where we could compile in the DB server API libaries. In practice, often, the clients will be running on Windows machines, not just on the machines where the server runs. Using Python, perhaps in combination with the MFC, could be a killer development environment on Windows. Now how can we get in touch with the DB server? To get in touch with the DB server from another machine could be done in numerous ways: 1) open sockets directly and deal with the network library of the server disadvantage: very dependent on the DB server advantage: fast, low overhead 2) purchase ODBC server and client libraries and use these disadvantage: you have to _purchase_ ODBC software which is _dependent_ on the server advantage: you could build server independent Python classes to access the database 3) extend the suggested DB api with Python classes that can be accessed over the net. In other words, concentrate right away on building a Python API as well as a Python net DB API. disadvantage: more work advantage: costs nothing. We could construct very nice classes (much nicer than the ODBC classes) either by using the basic suggested classes and implementing in terms of these, or using nice classes from the DB server. We are not adding much platform dependent code. 4) extend the suggested DB API with http-useful features. I am thinking about some python classes that: a) would be invoked through a cgi script b) would spit out HTML forms enhanced with Python or Java control code to make the forms useful for DB access through Grail or a Java aware netbrowser. With regards to 3) I am particularly concerned about things like cursors. It is a long way to go from an SQL cursor to a nicely scrollable list of rows in a windows environment. Postgres introduced so called "portals" which make this a lot easier, and we could perhaps consider implementing a portal object (or using it through Postgres directly) and making that available over the net. In relation to 4) it should be possible to build simple DB clients completely in terms of HTML and some Python/Java classes shipped down with them. In effect, we are using a universal windowing tool, which we ship enhanced standard widgets. I don't think one could easily reach the quality of a first rate custom designed interface, but it could be done in a fraction of the time. (Hopefully some CS students here are going to hack something out along these lines. Of course, Oracle is planning something similar.) So the picture I would love to see is: client Python DB classes <---> Python Net-DB API <---> DB Server or CGI script network (indep. of DBarchitecture) instead of: client Python DB classes <---> DB Server network or: client Python ODBC classes <---> ODBC Server <---> DB Server (independent of server) I would love to see some comments. Peter ================= DB-SIG - SIG on Tabular Databases in Python send messages to: db-sig@python.org administrivia to: db-sig-request@python.org ================= From skip@calendar.com (Skip Montanaro) Fri Jan 19 00:37:30 1996 From: skip@calendar.com (Skip Montanaro) (Skip Montanaro) Date: Thu, 18 Jan 1996 19:37:30 -0500 Subject: [PYTHON DB-SIG] dBASE module? Message-ID: <199601190037.TAA10292@dolphin.automatrix.com> This is perhaps a bit off the list topic... Is there a dBASE module out there somewhere? This is for Unix, not Windows. Thanks, Skip Montanaro | Looking for a place to promote your music venue, new CD, skip@calendar.com | festival or next concert tour? Place a focused banner (518)372-5583 | ad in Musi-Cal! http://www.calendar.com/concerts/ ================= DB-SIG - SIG on Tabular Databases in Python send messages to: db-sig@python.org administrivia to: db-sig-request@python.org ================= From baker@spk.hp.com Fri Jan 19 01:18:24 1996 From: baker@spk.hp.com (Bill Baker) Date: Thu, 18 Jan 96 17:18:24 PST Subject: [PYTHON DB-SIG] Informix Module Work? Message-ID: <199601190118.AA157284305@hpspk7c.spk.hp.com> I am starting work on an Informix Module based around the work done by Thomas J. Culliton for the oraclemodule. I am inexperienced at the Python API and this will only be a side-line for me (although I _do_ have a motivation because I want to move from the perl 4.0 isql implementation that I currently use for production work here). I would like to know before I get too deep whether there is other work already in progress? Bill Baker SMTC WebApprentice and PERLmeister -- Email: baker@hpspk7c.spk.hp.com | Hewlett Packard Co Voice: 1-509-921-3515 | 24001 E Mission Ave Fax: 1-509-921-3725 | Liberty Lake, WA | 99019-9599 ================= DB-SIG - SIG on Tabular Databases in Python send messages to: db-sig@python.org administrivia to: db-sig-request@python.org ================= From greg_stein@eshop.com Sat Jan 20 03:08:00 1996 From: greg_stein@eshop.com (Greg Stein) Date: Fri, 19 Jan 1996 19:08:00 -0800 Subject: [PYTHON DB-SIG] Informix Module Work? Message-ID: At 5:18 PM 1/18/96, Bill Baker wrote: >I am starting work on an Informix Module based around the work done by >Thomas J. Culliton for the oraclemodule. I am inexperienced at the Python >API and this will only be a side-line for me (although I _do_ have a motivation >because I want to move from the perl 4.0 isql implementation that I currently >use for production work here). >I would like to know before I get too deep whether there is other work already >in progress? Yes and no. Yes, we (eShop) are going to develop an Informix module within the next 10 days, and no, we haven't begun yet :-). I have worked extensively with Tom's module for about 7 months now. I've made numerous fixes to it and will actually be releasing a new version of his module within the next few weeks. Based on that experience, and with this mailing list's goal/desires for a generalized database API, I proposed a strawman API to this mailing list last week. At this point, I plan on using that (strawman) API for the interface to the Informix module. We should have the module completed quickly, but I don't think I'll be able to prepare a "formal" release until a few weeks afterwards. I can certainly arrange to get it to you before then, though. Regards, Greg Stein, eShop Inc. greg_stein@eshop.com ================= DB-SIG - SIG on Tabular Databases in Python send messages to: db-sig@python.org administrivia to: db-sig-request@python.org ================= From greg_stein@eshop.com Sat Jan 20 03:08:00 1996 From: greg_stein@eshop.com (Greg Stein) Date: Fri, 19 Jan 1996 19:08:00 -0800 Subject: [PYTHON DB-SIG] Informix Module Work? Message-ID: At 5:18 PM 1/18/96, Bill Baker wrote: >I am starting work on an Informix Module based around the work done by >Thomas J. Culliton for the oraclemodule. I am inexperienced at the Python >API and this will only be a side-line for me (although I _do_ have a motivation >because I want to move from the perl 4.0 isql implementation that I currently >use for production work here). >I would like to know before I get too deep whether there is other work already >in progress? Yes and no. Yes, we (eShop) are going to develop an Informix module within the next 10 days, and no, we haven't begun yet :-). I have worked extensively with Tom's module for about 7 months now. I've made numerous fixes to it and will actually be releasing a new version of his module within the next few weeks. Based on that experience, and with this mailing list's goal/desires for a generalized database API, I proposed a strawman API to this mailing list last week. At this point, I plan on using that (strawman) API for the interface to the Informix module. We should have the module completed quickly, but I don't think I'll be able to prepare a "formal" release until a few weeks afterwards. I can certainly arrange to get it to you before then, though. Regards, Greg Stein, eShop Inc. greg_stein@eshop.com ================= DB-SIG - SIG on Tabular Databases in Python send messages to: db-sig@python.org administrivia to: db-sig-request@python.org ================= From greg_stein@eshop.com Thu Jan 25 22:24:43 1996 From: greg_stein@eshop.com (Greg Stein) Date: Thu, 25 Jan 1996 14:24:43 -0800 Subject: [PYTHON DB-SIG] Re: Informix module Message-ID: > def exec(self, operation, params=None): > def execagain(self, params=None): > >I don't like this system...I would prefer > >def prepare(self, operation, params=None): >def exec(self): > >Obviously, your way would work, but it would complicate the most >common idiom -- construct a server (do the prepare()) then while you >are running, process each transaction (do the exec()). We talked about this. I believe we agreed to: - toss execagain(). just have exec(). - remember a reference to the operation object (a string object). - if exec() is called with the same string object, then it will skip the parse step - all parameters will attempt to reuse bind areas, realloc'ing as needed when bind area sizes change > def fetchone(self): > >You could call this just "fetch()" We agreed to keep the name, right? I believe fetchone() is clearer, particularly given that two other fetch methods exist. >Could this be implemented in a database with no built-in notion of >cursor? I hope so. The cursor() method is defined to throw an exception if cursors are not available (actually, the straw man didn't specify this, but I meant it to :-). I sort of left the discussion of exceptions empty, instead preferring to wait for feedback on the API from a higher level. The connection object is defined to act as a cursor. For simple usage, people generally won't even attempt to use a cursor, relying on the connection object instead. Vincent also made one suggestion: that the params can be specified as a list/tuple of tuples. This would only be allowed for INSERT statements and would imply array-insert operations (based on the cursor's arraysize). Lastly, I think we should probably change Cursor.arraysize() to an attribute that can be get/set. And we can change Cursor.describe() to a read-only attribute named description. Note that we can't really change the setcolsize() method because I spec'd it to be able to set individual column sizes (or default when col=None). The column is specified as a 0-based integer or by (case-sensitive) name. Greg Stein, eShop Inc. greg_stein@eshop.com ================= DB-SIG - SIG on Tabular Databases in Python send messages to: db-sig@python.org administrivia to: db-sig-request@python.org ================= From greg_stein@eshop.com Thu Jan 25 22:27:09 1996 From: greg_stein@eshop.com (Greg Stein) Date: Thu, 25 Jan 1996 14:27:09 -0800 Subject: [PYTHON DB-SIG] Strawman API Message-ID: At 7:32 AM 1/13/96, Skip Montanaro wrote: >Greg Stein writes: > > Some questions I pose are: > - do we need a close() method or can we just assume the user can clear out > all references? > >One of the problems I run into using simpler modules like bsddb is that of >shared access. I could care less if I have to close() the object, but how >long can I leave it open? Obviously, the more I have to open() and close() >it, the more performance is reduced. I'm not sure if this is a factor for >true database modules, since they presumably implement locking to support >concurrent read/write operations. Getting back to this... :-) Good point. I'd say that you can leave it open as long as you'd like, with the caveats of whether leaving it open consumes memory or a limited resource (e.g. if the number of connections/cursors is limited) and whether those restrictions are relevant. For most usage, I'd say that _not_ closing it isn't a problem at all. Greg Stein, eShop Inc. greg_stein@eshop.com ================= DB-SIG - SIG on Tabular Databases in Python send messages to: db-sig@python.org administrivia to: db-sig-request@python.org ================= From greg_stein@eshop.com Thu Jan 25 22:41:00 1996 From: greg_stein@eshop.com (Greg Stein) Date: Thu, 25 Jan 1996 14:41:00 -0800 Subject: [PYTHON DB-SIG] Strawman API Message-ID: At 7:39 AM 1/13/96, Dr Peter J. Braam wrote: >... >What is suggested to be implemented here is a set of classes, which we >presumably would build _using_ the API libraries of the DB Server. Correct. This would be the API for the most basic, fundamental level of database access for tabular (e.g. SQL) databases from Python. >... >[discussion of local vs. network database access] I believe the API is independent of whether the database is local or remote. I forget the exact convention used for Oracle's SQL*net, but your connection string would be something like: db = oracle.oracle("scott/tiger@dbserver") Presumably, ODBC is similar (does anybody have some specific information on connection strings to ODBC?). So what it boils down to is that your connection string implies the user, password, and [location of] the server you wish to access. Peter: it sounds like you're very familiar with ODBC. Can you comment on whether this API is sufficient to connect to ODBC databases and perform all needed operations? Alternatively, can you provide some URLs so that we can investigate at leisure? >4) extend the suggested DB API with http-useful features. I am thinking > about some python classes that: > a) would be invoked through a cgi script > b) would spit out HTML forms enhanced with Python or Java control > code to make the forms useful for DB access through Grail or a > Java aware netbrowser. I think this may be simply layers on top of the database API rather than incorporated directly into it. >With regards to 3) I am particularly concerned about things like cursors. >It is a long way to go from an SQL cursor to a nicely scrollable list of >rows in a windows environment. Postgres introduced so called "portals" >which make this a lot easier, and we could perhaps consider implementing a >portal object (or using it through Postgres directly) and making that >available over the net. I believe these also would build on top of the basic database API. Would the requirements of implementating a "portal" have an impact on the requirements of the API? i.e. is there some missing functionality that needs to be provided by the API to create a portal? >... >[more dicussion of HTML/Java/etc related to DBs] >[pictures of component interactions] >... These additional layers on top of the database should be discussed as a separate topic, but are certainly valid within this forum. I simply think we need to keep the distinctions in mind between what layers we are talking about. I would be interested to see additional discussion. Cheers, Greg Stein, eShop Inc. greg_stein@eshop.com ================= DB-SIG - SIG on Tabular Databases in Python send messages to: db-sig@python.org administrivia to: db-sig-request@python.org ================= From peterw@stc.com Thu Jan 25 23:19:46 1996 From: peterw@stc.com (Peter Wolk) Date: Thu, 25 Jan 1996 15:19:46 -0800 Subject: [PYTHON DB-SIG] Catching up Message-ID: <199601252319.PAA24482@maya.kamro.com> Just got on board this SIG. Is there an archive somewhere I can browse so I can catch up on what is going on (specifically the API discussion that I just got from Greg Stein)? Thanks- Peter- ------------------------------------------------------------------------------ Peter M. Wolk Director, Managed Care Research and Development peterw@stc.com SOFTWARE TECHNOLOGIES CORP 818-445-9000 x506 P.O. Box 661090 818-445-5510 (fax) Arcadia, CA 91066 http://www.stc.com ------------------------------------------------------------------------------ ================= DB-SIG - SIG on Tabular Databases in Python send messages to: db-sig@python.org administrivia to: db-sig-request@python.org ================= From greg_stein@eshop.com Thu Jan 25 23:23:23 1996 From: greg_stein@eshop.com (Greg Stein) Date: Thu, 25 Jan 1996 15:23:23 -0800 Subject: [PYTHON DB-SIG] Catching up Message-ID: At 3:19 PM 1/25/96, Peter Wolk wrote: >Just got on board this SIG. Is there an archive somewhere I can browse so I >can catch up on what is going on (specifically the API discussion that I >just got from Greg Stein)? > >Thanks- > >Peter- Sure. Send a message to: db-sig-request@python.org. In the body of the message, put the following line: get ================= DB-SIG - SIG on Tabular Databases in Python send messages to: db-sig@python.org administrivia to: db-sig-request@python.org ================= From greg_stein@eshop.com Thu Jan 25 23:28:56 1996 From: greg_stein@eshop.com (Greg Stein) Date: Thu, 25 Jan 1996 15:28:56 -0800 Subject: [PYTHON DB-SIG] Catching up Message-ID: [ ack. ignore my previous note... trigger finger on the "Send" button... ] At 3:19 PM 1/25/96, Peter Wolk wrote: >Just got on board this SIG. Is there an archive somewhere I can browse so I >can catch up on what is going on (specifically the API discussion that I >just got from Greg Stein)? > >Thanks- > >Peter- Sure. Send a message to: db-sig-request@python.org. In the body of the message, put the following line: index help This will return a mail message to you listing the files currently in the DB Sig mail archive, along with help on how to use Majordomo. You'll see there is one file in the archive (db-sig.archive). Send another message to db-sig-request with the following body: get db-sig.archive That should do it for you! Greg Stein, eShop Inc. greg_stein@eshop.com ================= DB-SIG - SIG on Tabular Databases in Python send messages to: db-sig@python.org administrivia to: db-sig-request@python.org ================= From MHammond@skippinet.com.au Fri Jan 26 00:54:02 1996 From: MHammond@skippinet.com.au (Mark Hammond) Date: Fri, 26 Jan 1996 10:54:02 +1000 Subject: [PYTHON DB-SIG] Strawman API Message-ID: <199601252357.KAA28794@minotaur.labyrinth.net.au> I havent been folowwing closely, but... > I believe the API is independent of whether the database is local or > remote. I forget the exact convention used for Oracle's SQL*net, but your > connection string would be something like: > > db = oracle.oracle("scott/tiger@dbserver") > > Presumably, ODBC is similar (does anybody have some specific information on > connection strings to ODBC?). So what it boils down to is that your > connection string implies the user, password, and [location of] the server > you wish to access. ODBC has a "named param" type concept. The string is like "DATASOURCE=dbserver;USER=scott;PASS=tiger", etc. > Peter: it sounds like you're very familiar with ODBC. Can you comment on > whether this API is sufficient to connect to ODBC databases and perform all > needed operations? Alternatively, can you provide some URLs so that we can > investigate at leisure? Im not, and I cant. But I think it worth mentioning a point or 2. ODBC's concept of "portability" is crap in my POV. Almost no large (say "Oracle") systems are going to chage to Ingres, and assume all ODBC style code will work. The real world means that your ODBC will end up Oracle specific in at least a few areas. One benefit that _does_ exist is the learning curve between projects or for casual users. It is the API that people end up knowing and liking. ODBC's API looks something like: rs = db.OpenRecordSet("table_name") rs.MoveFirst rs.FieldName = "Field Value". My point (yes, I do have one) is that an ODBC API would be most valuable with an ODBC API. Almost by definition, the multiple database problem goes away - an ODBC effort would not be much more than a Python wrapping to the C api. I dont really think you should bother with ODBC at all - you would only be wrapping one DB independant wrapper around another. Mark. ---------------------------------------- Mark Hammond - MHammond@skippinet.com.au ================= DB-SIG - SIG on Tabular Databases in Python send messages to: db-sig@python.org administrivia to: db-sig-request@python.org ================= From Brian.Lloyd@digicool.com Fri Jan 26 15:20:20 1996 From: Brian.Lloyd@digicool.com (Brian Lloyd/Digital Creations) Date: 26 Jan 96 15:20:20 Subject: [PYTHON DB-SIG] PREVIEW OLE-Access database module..... Message-ID: <9601262020.AA0191@phoenix.cminds.com> Hi all. I have been playing with PythonWin's OLE functions here lately, and with a little help from an OLE object browser managed to build an interface to MS' Data Access Objects. You can essentially create and manipulate Access dbs on about the same level as VB 4 can, but do it from python using native python structures as records, etc. It's still a very early hack, but I thought someone might be interested (if you're interested enough to contribute, that'd be even better! ;) The first quick stab at an interface and a short demo are available via anonymous ftp at: ftp.digicool.com/pub/olepython/dao-py.zip Let me know if you find it useful (or hateful...) And now a quick aside related to OLE: This DAO module would have been impossible except that I happened to find some very cool software that lets you browse all of the OLE objects installed on your system. I had the DAO type library, but when I ran makeole.py on it, all of the class definitions were: class _: Needless to say, twenty-some classes named *underscore* are pretty useless. But using this object browser, I could sleuth out what the classes were supposed to be, as well as know much more about what datatypes the methods were expecting. (Which was *very* cool, as you generally know just about zilch as far as what an OLE method is expecting as an argument from python...) Anyway, the software is called VBA Companion, and any of you working on using OLE from python should definitely check it out. (http://www.apexsc.com/vbacomp) B. Lloyd, Digital Creations, L.C. (brian@digicool.com) ================= DB-SIG - SIG on Tabular Databases in Python send messages to: db-sig@python.org administrivia to: db-sig-request@python.org ================= From peterw@stc.com Wed Jan 31 01:38:04 1996 From: peterw@stc.com (Peter Wolk) Date: Tue, 30 Jan 1996 17:38:04 -0800 Subject: [PYTHON DB-SIG] Message-ID: <199601310138.RAA26431@maya.kamro.com> ==================== This is a mail to Greg from me. His response is coming next. ==================== Greg- Looking for guidance here. Am doing a project here using Python to build a process server interfacing with a (PowerBuilder based) RdB (Informix) system. So your postings were VERY timely. (I learned much about Python from TupleDescriptor/DatabaseTuple, thank you.) However, I won't be able to wait until an "official" API is out. Yet I don't want to modify "provided" code arbitrarily, which would mean that as the "latest and greatest" gets released, I would have to back implement all my customizations. I know the point of the SIG is to create a standard. Any advice on handling the interim until Ironman? Steelman? is released. Brief example: your code comment that "the term database tuple is rather specific; in actuality the tuple may have come from non-database sources ..." is right on. I am using the tuples all over the place. Considering also Peter Coad's separation of the domains would lead to NOT having __set*__ actually do a database update (as implied in your code). Set a flag, maybe? Anyway, I am moving toward such a separation. Also, there is a performance consideration. I found that a scan of 2000 tuples, extracting 1500 and adding two columns took 23 seconds using tuple.attr references and 0.5 seconds by using tuple._data_[index] references. So I will be implementing "containers" that will hide the internals, but will be able to (totals, extractions, etc.) fairly efficiently. Is this kind of thing on the agenda for the group as well? Thanks again for your efforts. Peter- ------------------------------------------------------------------------------ Peter M. Wolk Director, Managed Care Research and Development peterw@stc.com SOFTWARE TECHNOLOGIES CORP 818-445-9000 x506 P.O. Box 661090 818-445-5510 (fax) Arcadia, CA 91066 http://www.stc.com ------------------------------------------------------------------------------ ================= DB-SIG - SIG on Tabular Databases in Python send messages to: db-sig@python.org administrivia to: db-sig-request@python.org ================= From peterw@stc.com Wed Jan 31 01:39:18 1996 From: peterw@stc.com (Peter Wolk) Date: Tue, 30 Jan 1996 17:39:18 -0800 Subject: [PYTHON DB-SIG] Message-ID: <199601310139.RAA26505@maya.kamro.com> ================= Here is Greg's response ================= Peter - Thanks for mailing... I'll try to answer you below: >Looking for guidance here. Am doing a project here using Python to build a >process server interfacing with a (PowerBuilder based) RdB (Informix) >system. So your postings were VERY timely. (I learned much about Python >from TupleDescriptor/DatabaseTuple, thank you.) Great! And you're welcome :-) btw, I believe we'll begin work on the Informix module next week (our arrival of Informix has been delayed :-( ). We will begin moving the Oracle module to the new API tomorrow, though. >However, I won't be able to wait until an "official" API is out. Yet I >don't want to modify "provided" code arbitrarily, which would mean that as >the "latest and greatest" gets released, I would have to back implement all >my customizations. I know the point of the SIG is to create a standard. >Any advice on handling the interim until Ironman? Steelman? is released. Zero changes have been suggested to it, so I have to assume that it is darn close to final (Ironman :-). I want to find new names (and commentary) for the classes considering they have wider application (as you noted below) and post them to the main list and dump them onto the FTP site (guess I'll have to edit that file's header :-). If you have requests that seem to fall within the scope of those two classes, then I'd be glad to add them in and repost. >Brief example: your code comment that "the term database tuple is rather >specific; in actuality the tuple may have come from non-database sources >..." is right on. I am using the tuples all over the place. Considering Cool. We're using them in our database interface, but they haven't seen a lot of movement out of that area just yet. After using them for a bit, I think I realized the same thing you do: they are quite useful :-). >also Peter Coad's separation of the domains would lead to NOT having >__set*__ actually do a database update (as implied in your code). Set a >flag, maybe? Anyway, I am moving toward such a separation. I think it would be prudent to say that somebody would have to derive from DatabaseTuple to override how __set*__ is handled. >Also, there is a performance consideration. I found that a scan of 2000 >tuples, extracting 1500 and adding two columns took 23 seconds using >tuple.attr references and 0.5 seconds by using tuple._data_[index] ack. I could imagine. Looking at the code, there is a method invocation, a list search, and a couple dictionary lookups :-). You should be able to do tuple[index] almost as fast as tuple._data_[index]. This timing data is great, though. We haven't had an opportunity to do this kind of work yet. >references. So I will be implementing "containers" that will hide the >internals, but will be able to (totals, extractions, etc.) fairly >efficiently. Is this kind of thing on the agenda for the group as well? Not specifically, although there is nothing stopping us. Mainly, I think so far the list has been to create a way to deal effectively with database access from Python, rather than what to do with the stuff once you get it :-). In your particular case, you may want to look at what the matrix SIG is doing. It might be a reasonable thing to pull data from the database and place that into a matrix object. With that, you could then do column-wise slices and summations and other weird stuff (I'm not familiar with the matrix stuff, though, so I don't know how well this applies). In any case, I'd ask you to feel free to write to the DB list asking about the problem. Specifically, how can data be pulled from a database into a form that can be efficiently manipulated in the ways you mention? Also, if you find the matrix object does/does not turn out to be handy, I think it would be great if you could send your results to the list. Thanx for writing. The list is too silent for my tastes, so its always good to hear from somebody that is motivated in this area :-). I didn't copy my reply to the list (w/o permission), but I'd ask you to feel free to post this message to the list. Other people may be interested in your findings and may have comments themselves (and comments on my comments :-). Regards, ------------------------------------------------------------------------------ Peter M. Wolk Director, Managed Care Research and Development peterw@stc.com SOFTWARE TECHNOLOGIES CORP 818-445-9000 x506 P.O. Box 661090 818-445-5510 (fax) Arcadia, CA 91066 http://www.stc.com ------------------------------------------------------------------------------ ================= DB-SIG - SIG on Tabular Databases in Python send messages to: db-sig@python.org administrivia to: db-sig-request@python.org ================= From peterw@stc.com Wed Jan 31 03:00:06 1996 From: peterw@stc.com (Peter Wolk) Date: Tue, 30 Jan 1996 19:00:06 -0800 Subject: [PYTHON DB-SIG] Response to Greg Stein, and Mark Hammond's response to PeterBraam Message-ID: <199601310300.TAA27964@maya.kamro.com> At 05:22 PM 1/30/96 -0800, Greg Stein wrote: >> >Zero changes have been suggested to it, so I have to assume that it is darn >close to final (Ironman :-). I want to find new names (and commentary) for >the classes considering they have wider application (as you noted below) >and post them to the main list and dump them onto the FTP site (guess I'll >have to edit that file's header :-). > Well, there was the discussion about execagain. And was the cursor area updated? In any case, is there a current listing of the Copperman(?) API that I could use as a reference point? > btw, I believe we'll begin work on the Informix module next week (our > arrival of Informix has been delayed :-( ). We will begin moving the Oracle > module to the new API tomorrow, though. If your Informix module is together at all, I would be glad to try to get it going here. If not, can I get at the Oracle API, and start on that? What would be helpful would be an example listing covering SELECT, INSERT, UPDATE, and DELETE (and so on) with and without cursors. Until I see that kind of thing (or get it working myself) I find it hard to comment. (I get the feeling everybody else out there is a bloody genius! It's ntimidating!) For example, is the TupleDescriptor set by the API? When? How often? > ... You should be able to do tuple[index] almost as fast as tuple._data_[index]. Yes. It's a bit slower but OK. > [In regards to containers:] Not specifically, although there is nothing stopping us. Mainly, I > think so far the list has been to create a way to deal effectively with database > access from Python, rather than what to do with the stuff once you get it >:-). > In your particular case, you may want to look at what the matrix SIG is doing. .... >In any case, I'd ask you to feel free to write to the DB list asking about >the problem. Specifically, how can data be pulled from a database into a >form that can be efficiently manipulated in the ways you mention? Also, if >you find the matrix object does/does not turn out to be handy, I think it >would be great if you could send your results to the list. > Will look into this, but I don't want to mix protocols as it were. The TupleDescriptor IS the column labels, and your definition of the fetchall returns exactly what I'm using - a list of tuples. So what I want is a defined "Container" class returning this tuple list. Now, I would want to sub-class this object to add my own handling, and this would require me to specify the container that fetchall uses. (cursr.fetchall(Container=myContainer) This applies to fetchmany as well. (By the way, should it be cursr.fetchmany (size = 10) along with cursr.arraysize(10)?) If the Matrix definition does the job, then that is what fetchall/fetchmany should return. COMMENT REF MARK HAMMOND'S RESPONSE TO PETER BRAAM Peter made an argument for a 3-level DB model, separating the functionality from the connectivity. Mark's response basically said "Why bother? A site only uses one database anyway." My situation is that we are preparing a component of a product that is sold to many clients who potentially can have ANY commercial database. For this reason, an API connecting to ODBC allows us to move quickly(!) from one site to another WITH IDENTICAL CODE. So this is valuable for us. I also understand that this will simplify running on several platforms connecting to the same database, as well. So yes, Mark, it is wrapping one independent wrapper around another, but if the design reflects Peter's architecture of a single front-end talking to a set of (DB specific) backends, then ONE of the backends should be ODBC along with ORACLE, INFORMIX, etc. And I would support that architectural approach. Peter- ------------------------------------------------------------------------------ Peter M. Wolk Director, Managed Care Research and Development peterw@stc.com SOFTWARE TECHNOLOGIES CORP 818-445-9000 x506 P.O. Box 661090 818-445-5510 (fax) Arcadia, CA 91066 http://www.stc.com ------------------------------------------------------------------------------ ================= DB-SIG - SIG on Tabular Databases in Python send messages to: db-sig@python.org administrivia to: db-sig-request@python.org ================= From Paul.Everitt@digicool.com Wed Jan 31 07:00:33 1996 From: Paul.Everitt@digicool.com (Paul Everitt/Digital Creations) Date: 31 Jan 96 7:00:33 Subject: [PYTHON DB-SIG] Response to Greg Stein, and Mark Hammond's response to PeterBraam Message-ID: <9601311200.AA1365@phoenix.cminds.com> Gosh, I'd like to get in on all this chatting too :^) In a further effort to separate interface from implementation, Brian Lloyd and Chris Heaps here are writing an ILU interface for the Strawman API. Brian is working on an ILU module that will connect to a Microsoft Access database, and publish it as the Strawman ISL object. You can then connect from any platform that has ILU (being, it appears, Win32, many Unices, and even OS/2. Chris is working on connecting it to miniSQL...not directly (i.e. a C mapping), but via the Python module for miniSQL. I certainly don't expect to get everyone to use ILU. However, having an ISL is a very good way to define a spec. --Paul ================= DB-SIG - SIG on Tabular Databases in Python send messages to: db-sig@python.org administrivia to: db-sig-request@python.org ================= From greg_stein@eshop.com Wed Jan 31 23:41:48 1996 From: greg_stein@eshop.com (Greg Stein) Date: Wed, 31 Jan 1996 15:41:48 -0800 Subject: [PYTHON DB-SIG] "Copperman" API Message-ID: Using Peter Wolk's terminology... I've updated the Strawman API to produce the Copperman API :-) Some changes that have been made: - more comments as it seems this is getting close to correct (Ironman in Peter-speak :-) - dropped execagain() since it wouldn't have really worked quite right and was a bit redundant, provided that exec() was spec'd correctly. - converted arraysize() method to the 'arraysize' read/write attribute - converted describe() to the 'description' read-only attribute (*) - renamed setbufsize() to setoutputsize() - added setinputsizes() (**) - added size parameter to fetchmany() (*) should this be an attribute or a function (as it was before) ? what do people feel to be more Python-esque? A read-only attribute feels okay to me. (**) this will possibly be REMOVED. tomorrow, an engineer here is going to run a test to measure the overhead of rebinding input buffers to an Oracle cursor. This method was added to enable skipping the rebind step, presuming that it is expensive. We want to measure it, though. If we find that rebinding has little overhead, then setinputsizes() will be removed and we'll just rebind all the time. ISSUES: - does anybody know if binding input buffers is an "expensive" operation for some databases? - need some examples (I'll mail a note a bit later) - is callproc() sufficient? I have no experience with stored procedures to know That is all I have for now. I know there are more people out there with database experience than have mailed to the list. Can we get a few of you to take a bit of time to post some commentary? :-) Jim? Joel? You guys spoke up at the Workshop... I know you guys are out there... :-) Greg Stein, eShop Inc. greg_stein@eshop.com ---------------------------- class : def __init__(self, connection_string): "Open a connection to a database, returning a Connection instance" pass def close(self): "Close the connection now (rather than whenever __del__ is called)." pass def commit(self): "Commit any pending transaction to the database." pass def rollback(self): "Roll the database back to the start of any pending transaction." pass def cursor(self): """\ Return a new cursor. An exception may be thrown if the database does not support a cursor concept.""" pass def callproc(self, params=None): """\ Call a stored procedure with the given (optional) parameters. Return the result of the stored procedure.""" pass # Implement all Cursor methods (except close), by routing the methods # to an internal cursor. This cursor will be opened on the first # exec() on self. If the database has no concept of cursors, then # simply perform the operation. class Cursor: """\ This class represents a database cursor, which is used to manage the context of a fetch operation. Instances have a read/write attribute named 'arraysize' that specifies the number of rows to fetch at a time with fetchmany. This value is also used when inserting multiple rows at a time (passing a tuple/list of tuples/lists as the params value to exec()). This will default to a single row. Note that the arraysize is optional and is merely provided for higher performance database interaction. Implementations should observe it with respect to the fetchmany() method, but are free to interact with the database a single row at a time. Instances also have a read-only attribute named 'description' that is a tuple of 7-tuples. Each 7-tuple contains information describing each result column: (name, type_code, display_size, internal_size, precision, scale, null_ok). This attribute will be None for operations that do not return rows or if the cursor has not had an operation exec'd yet """ def close(self): "Close the cursor now (rather than whenever __del__ is called)." pass def exec(self, operation, params=None): """\ Execute (prepare) a database operation (query or command). Parameters may be provided (as a tuple/list) and will be bound to variables in the operation. Variables are specified in a database-specific notation that is based on the index in the parameter tuple (position-based rather than name-based). The parameters may also be specified as a tuple/list of tuples/lists to insert multiple rows in a single operation. A reference to the operation will be retained by the cursor. If the same operation object is passed in again, then the cursor can optimize its behavior. This is most effective for algorithms where the same operation is used, but different parameters are bound to it (many times). For maximum efficiency when reusing an operation, it is best to use the setinputsizes() method to specify the parameter types and sizes ahead of time. It is legal for a parameter to not match the predefined information; the implementation should compensate, possibly with a loss of efficiency. """ pass def fetchone(self): "Fetch the next row of a query result, returning a single tuple." pass def fetchmany(self, size=None): """\ Fetch the next set of rows of a query result, returning as a list of tuples. An empty list is returned when no more rows are available. The number of rows to fetch is specified by the parameter. If it is None, then the cursor's arraysize determines the number of rows to be fetched. Note there are performance considerations involved with the size parameter. For optimal performance, it is usually best to use the arraysize attribute. If the size parameter is used, then it is best for it to retain the same value from one call to the next. """ pass def fetchall(self): """\ Fetch all rows of a query result, returning as a list of tuples. Note that the cursor's arraysize attribute can affect the performance of this operation.""" pass def setinputsizes(self, sizes): """\ This can be used before a call to exec() to predefine memory areas for the operation's parameters. sizes is specified as a tuple -- one item for each input parameter. The item should be a Type object that corresponds to the input that will be used, or it should be an integer specifying the maximum length of a string parameter. If the item is None, then no predefined memory area will be reserved for that column (this is useful to avoid predefined areas for large inputs). Note that this is optional and is merely provided for higher performance database interaction. Implementations are free to do nothing and users are free to not use it. """ pass def setoutputsize(self, size, col=None): """\ Set a column buffer size for fetches of large columns (e.g. LONG). Column is specified as an index into the result tuple. Using a column of None will set the default size for all large columns in the cursor. Note that this is optional and is merely provided for higher performance database interaction. Implementations are free to do nothing and users are free to not use it. """ pass error = ".error" ================= DB-SIG - SIG on Tabular Databases in Python send messages to: db-sig@python.org administrivia to: db-sig-request@python.org =================