This page uses CSS, and renders properly in the latest versions of Microsoft Internet Explorer and Mozilla.
This is Frontier: The Definitive Guide by Matt Neuburg, unabridged and unaltered from the January 1998 printing.
It deals with the freeware Frontier 4.2.3; for commercial Frontier 8, look here, and for the inexpensive Radio UserLand, look here. See my Web site for information.
Those wishing an offline or printed copy may purchase the book, which is still in print.
Copyright 1998 O'Reilly & Associates, Inc. ALL RIGHTS RESERVED. Reprinted with permission.
TOP UP: Interface NEXT: Border Crossings

30

Multiple Databases

The Frontier database is a remarkable thing: a hierarchical name-value storage system, with hash-indexed access that stays rapid as tables get large, and with efficient memory management both in RAM and on disk. (In fact, the database is such cool technology that UserLand distributes it separately as a cross-platform library for C and Java applications.1)

As with other things, so with the database: if it's good to have one of them, you might, not unreasonably, want two. Frontier.root is the "working" database, constantly being accessed for verbs and other pieces of Frontier's "system." The storage for a sizeable storage task might preferably be separated from the working database, out of considerations of convenience, speed, safety, and size.

For this reason, it is possible to open a second database simultaneously with the first, and communicate with it manually or in UserTalk. This chapter explains the process.

Another option for storing data outside the database is to drive a serious, dedicated database application such as FileMaker or FoxPro, or even a small flat-file database application such as uBASE which is included with the Frontier distribution. This involves inter-application communication, which might mean a speed hit, but such an application might have capacities that a Frontier database does not provide. For example, uBASE provides a flatfile structure of records with identically named fields. A serious database application might provide indexing and rapid search of fields, ability to work with very large numbers of records, relational structure, and so forth. On such matters, see Chapter 32, Driving Other Applications.

Manual Interface

You can open a second database manually. There is little point to doing this except to examine or alter data by typing, copying and pasting, and so forth. That's because the second database is very minimal: it has a restricted set of verbs (just the kernel, in system.compiler) and no custom menus (just the built-in File, Edit, and Window menus).

You could add scripts and an interface to the second database, but this would be rather against the spirit of the thing. The second database isn't intended as a place to work. It contains sufficient functionality to enable the manipulation of data in an edit window, and no more.2

To create and open a second database manually, choose New from the File menu; to open an existing second database manually, choose Open from the File menu. You can perform these actions programmatically with filemenu.new() and filemenu.open().

When a second database is opened in this way, there is a second Main Window representing it. This Main Window's popup menu gives access to an agent which shows the pathname of the database; this is useful so that you know which Main Window goes with which database. For instance, you might wish to close the second database by clicking in the go-away box of its Main Window; you'll want to be sure you're closing the right database.

Frontier keeps track of what database owns any window you bring to the front; this determines which database you are "in," and the menus at the top of the screen change accordingly, as does the repertory of verbs available to you.

Programmatic Interface

To work with a second database programmatically, the db verbs are provided. These verbs do not cause any windows to open on the second database, and they communicate with the second database through a completely different mechanism than if the second database were opened manually. (For example, a database that is opened with Open from the File menu is not necessarily open in the db.open() sense.)

References to entries in the second database must be strings, because if they were addresses their validity would be checked against the main database, which might not work. These strings must be full paths: they cannot be partial paths, and they cannot be abbreviated by means of with constructions. Hence it makes sense to create tables at or close to root level.

To create a database on disk:

db.new (pathname)

To open an existing database:

db.open (pathname, readOnly?)

The advantage of opening a database read-only is that it remains possible for another thread, or another copy of Frontier, also to open it read-only.

To make a new table:

db.newTable (pathname, tablePathString)

To set a database entry value, possibly creating the entry:

db.setValue (pathname, entryPathString, value)

To learn a database entry value:

db.getValue (pathname, entryPathString)

To learn whether a database entry exists:

db.defined (pathname, entryPathString)

To learn whether an existing database entry is a table:

db.isTable (pathname, entryPathString)

To learn the number of entries in a table:

db.countItems (pathname, entryPathString)

To obtain the value of the n th item of a table:

db.getNthItem (pathname, entryPathString, n)

db.getNthItem() makes up for the fact that, in the absence of direct references, array notation is impossible.

To remove a database entry:

db.delete (pathname, entryPathString)

To save a database:

db.save (pathname)

db.save() returns true or false, as the database was saved or not. It is well to check this value, just to be certain that Frontier realized the database was dirty. If you call db.close() without saving first, changes will be lost.

To close a database without saving:

db.close (pathname)

In this example, we create a second database, make a table at root level called myTable, and copy into it all the entries in the main database's workspace table.

Example 30-1 Copying a non-scalar between databases
local (f = "HD:testing")
try
    db.new(f)
    db.open(f, false)
    db.setValue (f, "root.myTable", workspace)
    db.save(f)
    db.close(f)
else
    db.close(f)
    scripterror(tryerror)

The whole operation is embedded in a try so that, if something goes wrong, the second database is not left open; this approach is useful when working with second databases, as there is no way to learn the pathnames of open databases.

As mentioned in Chapter 29, Import/Export, one can take advantage of the ability to open multiple databases as a way of migrating to a clean root. Imagine that as you customize the database you note the address of each customized entry in an outline at, for example, workspace.myMigrateList.3 So workspace.myMigrateList might look like this:

people.man.customcommands
system.verbs.apps.MORE
system.verbs.apps.Eudora.examples.man
people.man.notepad
user.hooks.closewindow

And so forth. Then, having renamed your old root and migrated to a clean Frontier.root, you could run a script like the following.

Example 30-2 migrate( )
local (x, o, f = "HD:Frontier folder:Frontier.root.old")
on grab (s)
    x = db.getValue (f, s)
    s^ = x
db.open (f, true)
o = workspace.myMigrateList
target.set(@o); op.firstSummit()
grab (op.getLineText())
while op.go(flatdown, 1)
    grab (op.getLineText())
db.close (f)

1. For more information, and to obtain the database SDK, see http://www.scripting.com/Gimme5/odb/.

2. If for some reason you need a reduced working database, make a copy of your Frontier.root and selectively delete entries from it.

3. Granted, this assumes a degree of self-discipline and organization beyond most people's imagination. However, I owe this example to Doug Baron, who really is this organized and really does use this technique.


TOP UP: Interface NEXT: Border Crossings

This is Frontier: The Definitive Guide by Matt Neuburg, unabridged and unaltered from the January 1998 printing.
It deals with the freeware Frontier 4.2.3; for commercial Frontier 8, look here, and for the inexpensive Radio UserLand, look here. See my Web site for information.
Those wishing an offline or printed copy may purchase the book, which is still in print.
Copyright 1998 O'Reilly & Associates, Inc. ALL RIGHTS RESERVED. Reprinted with permission.