-- File: MultipleDBView.mesa -- Contents: View Level interface changes for multiple databases -- Created: Rick Cattell, July 8, 1981 Transaction: TYPE = REF ANY; -- Actually a REF FileIO.Trans, but we don't want compilation dependency; client can NARROW. TransactionAbortProc: PROC; Segment: TYPE = ATOM; DefaultNCachePages: CARDINAL = 512; DefaultNBytesInitial: LONG CARDINAL = 32768; DefaultNBytesPerExtent: LONG CARDINAL = 32768; -- *** Part 2: Global top-level operations [DBViewGlobalImpl] OpenDatabase: PROC[ userName, password: ROPE←NIL, localTrans, remoteTrans: Transaction← DefaultTransaction, remoteTransAbort: TransactionAbortProc← NIL, nCachePages: CARDINAL ← DefaultNCachePages, nBytesInitial: LONG CARDINAL ← DefaultNBytesInitial, nBytesPerExtent: LONG CARDINAL ← DefaultNBytesPerExtent ] RETURNS[localTrans, remoteTrans: Transaction, welcomeMsg: ROPE]; -- Opens local and remote transactions for database use. All remote segments will be accessed -- under remoteTrans, all local ones under localTrans. If these arguments are defaulted, new -- transactions are opened. In either case, localTrans and remoteTrans are returned to the client. -- If the remote transaction is aborted due to lock conflicts or juniper failure, the client's -- remoteTransAbort proc is called if supplied. In any case, all subsequent database calls -- touching remote data items will generate Process.ABORTED. -- Errors: -- BadUserPassword for access problems OpenSegment: PROC[ fileName: ROPE← NIL, version: Version← OldOnly ] RETURNS [Segment]; -- We really want a transaction per segment, but that's harder so OpenDatabase handles this now. -- The server, directory, and name should be concatenated in the fileName. -- <CedarDB> or some other directory must be part of any non-nil value in fileName, unless -- using server "[Local]" in which case the segment is on the local Pilot volume. -- The defaults work such that no arguments at all work: [juniper]<CedarDB>DB is the default -- if no name is given. For example, the following allows simple database use: -- OpenDatabase[]; -- OpenSegment[]; -- ... database operations ... -- CloseDatabase[]; -- There is no CloseSegment call; all open segments are closed on CloseDatabase. -- If version=NewOnly, creates a new segment, replacing existing one if present. -- Server defaults to juniper. -- IllegalSegmentName if directory name was omitted. -- SegmentNotFound if version=OldOnly and database doesn't exist. CloseDatabase: PROC; -- Closes currently open database, commiting transactions on all local and remote segments. MarkTransaction: PROC; -- Makes all of the interactions of the current transactions permanent and starts new transactions. AbortTransaction: PROC; -- Causes system to abort all database changes made since the last MarkTransaction (or since -- database was opened if no explicit MarkTransaction has been given). -- *** Part 4: Primitive operations [DBViewBasicImpl] EntitySegment: PROC[e: Entity] RETURNS [Segment]; -- Returns the segment in which e is stored. RelshipSegment: PROC[r: Relship] RETURNS [Segment]; -- Returns the segment in which r is stored. -- We might want a StringToSegment that does the atom lookup. DBView must know where to put do reads and writes. I have toyed with several possible designs for this, I want a design for the M local database N remote database case that does NOT demand a segment be passed on every single data read or write operation. The view level will pass the segment on the CreateTupleset, CreateIndex, etc. call to DBStorage as before, although the definition of a segment is different. I want the view level design for multiple segments to have the following properties. (1) Segments are physically independent. I.e., the the view level does not create any tuple refs between segments, it uses only the keys (entity names), causing a B-Tree lookup in the other segment. Every segment will have root B-Trees for the domains and relations in that segment. Thus it will be possible for the owner of the segment to delete the file, reconstruct it, delete tuples, or whatever, with the other segments continuing in degraded mode. (Possibly degraded because the information present there, e.g. phone numbers and addresses in the Whitepages database, will not be present.) (2) Across segments, I want to do the division of data by TupleSet (Domain or Relation, actually), as we did in the old DBTuples. E.g. Walnut objects like Msgs and mSender tuples will go to one segment, whitepages objects like Persons and phone tuples will go to another. Each database application will be assigned database segment names, like $Walnut, by the database administrator to use for the application's relations and domains. (3) When a query operation like RelationSubset is issued, the View Level searches the specified relation, and ALSO any relation with the same name in another segment. Similarly for DomainSubset. Any Entity-valued attributes of the attribute-value list passed to RelationSubset will be "translated" to the other segment: i.e., the entities with the same names in that segment will be used. Obviously RelationSubset will inefficient if the same relation occurs in many segments. I propose that by convention or possibly by enforcement that each relation or domain occur in exactly two: one local segment and one remote segment. The view level will need to cache the tupleset/segment correspondence efficiently. Here's a quick off-the-cuff guess at what the operations should do. There may be major holes in this design. DeclareDomain: PROC [name: ROPE, segment: Segment← NIL, version: Version← NewOrOld, estRelships: INT← 5] RETURNS [d: Domain]; -- Creates a domain of this name or fetches it if it already exists. -- All entities created in the domain will go in the indicated segment. DeclareRelation: PROC [ name: ROPE, segment: Segment← NIL, version: Version← NewOrOld] RETURNS [r: Relation]; -- Creates a relation of this name or fetches it if it already exists. -- All relships created in this relation will go in the indicated segment. DomainSubset: PROC[ d: Domain, constraintList: PropertyValueList← NIL, searchSubDomains: BOOL← TRUE, searchSegments: BOOL← TRUE] RETURNS [EntitySet]; -- Now searches d AND any domains with the same name in other segments, -- unless searchSegments=FALSE. RelationSubset: PROC[r: Relation, constraint: AttributeValueList← NIL] RETURNS [RelshipSet]; -- Now searches r AND any domains with the same name in other segments, -- unless searchSegments=FALSE. GetAllRefAttributes[e] and GetDomainRefAttributes[d] will find the attributes d (or e's domain) in the local segment and also all attributes that can reference the domain of the same name in other segments. DeclareEntity and DeclareRelship (like old FetchTuple and CreateTuple combined) create the entity or relship in the appropriate segment. Or in fetch mode, they do DomainSubset/RelationSubset calls, therefore the search both the Domain/Relation indicated plus any domains of the same name in other segments. DeclareProperty will need the segment argument like DeclareRelation it calls, though the design of properties may change soon to make DeclareProperty a no-op anyway. GetP and SetP are unaffected. DeclareAttribute, DeclareSubType, DestroyDomain, DestroyRelation, DestroyAttribute, DestroySubType are unchanged and unambiguous. DestroyEntity, DestroyRelship, SetF, SetFS, GetF, GetFS, GetName, SetName, GetEntityByName, DomainOf, RelationOf, Eq, and Null are unchanged and unambiguous. NextRelship, ReleaseRelshipSet, NextEntity, and ReleaseEntitySet are unchanged and unambiguous. Obviously Squirrel and the database applications it supports will have to be changed as well, I won't include that design here. However most application will be unaffected except in the data schema declarations, and Squirrel will mainly be affected in that (1) it needs a more complex database opening convention, a list of segment names that can be read from a profile, opened explicitly by the user, or implicitly by an application, and (2) the default editor and displayer should probably show you which segment data is coming from in some manner. END.