NAME

Rosetta - Rigorous database portability

DEPENDENCIES

Perl Version: 5.008001

Core Modules: none

Non-Core Modules:

Locale::KeyedText 1.02 (for error messages)
SQL::Routine 0.56

COPYRIGHT AND LICENSE

This file is part of the Rosetta database portability library.

Rosetta is Copyright (c) 1999-2005, Darren R. Duncan. All rights reserved. Address comments, suggestions, and bug reports to perl@DarrenDuncan.net, or visit "http://www.DarrenDuncan.net" for more information.

Rosetta is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License (GPL) version 2 as published by the Free Software Foundation (http://www.fsf.org/). You should have received a copy of the GPL as part of the Rosetta distribution, in the file named "LICENSE"; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.

Linking Rosetta statically or dynamically with other modules is making a combined work based on Rosetta. Thus, the terms and conditions of the GPL cover the whole combination. As a special exception, the copyright holders of Rosetta give you permission to link Rosetta with independent modules, regardless of the license terms of these independent modules, and to copy and distribute the resulting combined work under terms of your choice, provided that every copy of the combined work is accompanied by a complete copy of the source code of Rosetta (the version of Rosetta used to produce the combined work), being distributed under the terms of the GPL plus this exception. An independent module is a module which is not derived from or based on Rosetta, and which is fully useable when not linked to Rosetta in any form.

Any versions of Rosetta that you modify and distribute must carry prominent notices stating that you changed the files and the date of any changes, in addition to preserving this original copyright notice and other credits. Rosetta is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

While it is by no means required, the copyright holders of Rosetta would appreciate being informed any time you create a modified version of Rosetta that you are willing to distribute, because that is a practical way of suggesting improvements to the standard version.

SYNOPSIS

The previous SYNOPSIS was removed; a new one will be written later.

DESCRIPTION

The Rosetta Perl 5 module defines a complete and rigorous API for database access that provides hassle-free portability between many dozens of database products for database-using applications of any size and complexity, that leverage all sorts of advanced database product features. The Rosetta Native Interface (RNI) allows you to create specifications for any type of database task or activity (eg: queries, DML, DDL, connection management) that look like ordinary routines (procedures or functions) to your programs, and execute them as such; all routine arguments are named.

Rosetta is trivially easy to install, since it is written in pure Perl and its whole dependency chain consists of just 2 other pure Perl modules.

One of the main goals of Rosetta is similar to that of the Java platform, namely "write once, run anywhere". Code written against the RNI will run in an identical fashion with zero changes regardless of what underlying database product is in use. Rosetta is intended to help free users and developers from database vendor lock-in, such as that caused by the investment in large quantities of vendor-specific code. It also comes with a comprehensive validation suite that proves it is providing identical behaviour no matter what the underlying database vendor is.

This module has a multi-layered API that lets you choose between writing fairly verbose code that performs faster, or fairly terse code that performs slower.

The RNI is structured in a loosely similar fashion to the DBI module's API, and it should be possible to adapt applications written to use the DBI or one of its many wrapper modules without too much trouble, if not directly then by way of an emulation layer. One aspect of this similarity is the hierarchy of interface objects; you start with a root, which spawns objects that represent database connections, each of which spawns objects representing queries or statements run against a database through said connections. Another similarity, which is more specific to DBI itself, is that the API definition is uncoupled from any particular implementation, such that many specialized implementations can exist and be distributed separately. Also, a multiplicity of implementations can be used in parallel by the same application through a common interface. Where DBI gives the name 'driver' to each implementation, Rosetta gives the name 'Engine', which may be more descriptive as they sit "beneath" the interface; in some cases, an Engine can even be fully self-contained, rather than mediating with an external database. Another similarity is that the preparation and execution (with place-holder substitution) of database instructions are distinct activities, and you can reuse a prepared instruction for multiple executions to get performance gains.

The Rosetta module does not talk to or implement any databases by itself; it is up to separately distributed Engine modules to do this. You can see a reference implementation of one in the Rosetta::Engine::Generic module.

The main difference between Rosetta and the DBI is that Rosetta takes its input primarily as SQL::Routine (SRT) objects, where DBI takes SQL strings. See the documentation for SQL::Routine (distributed separately) for details on how to define those objects. Also, when Rosetta dumps a scanned database schema, it does so as SRT objects, while DBI dumps as either SQL strings or simple Perl arrays, depending on the schema object type. Each 'routine' that Rosetta takes as input is equivalent to one or more SQL statements, where later statements can use the results of earlier ones as their input. The named argument list of a 'routine' is analogous to the bind var list of DBI; each one defines what values can be given to the statements at "execute" time.

Unlike SQL strings, SRT objects have very little redundancy, and the parts are linked by references rather than by name; the spelling of each SQL identifier (such as a table or column name) is stored exactly once; if you change the single copy, then all code that refers to the entity updates at once. SRT objects can also store meta-data that SQL strings can't accomodate, and you define database actions with the objects in exactly the same way regardless of the database product in use; you do not write slightly different versions for each as you do with SQL strings. Developers don't have to restrict their conceptual processes into the limits or dialect of a single product, or spend time worrying about how to express the same idea against different products.

Rosetta is especially suited for data-driven applications, since the composite scalar values in their data dictionaries can often be copied directly to RNI structures, saving applications the tedious work of generating SQL themselves.

Rosetta is conceptually a DBI wrapper, whose strongest addition is SQL generation, but it also works without the DBI, and with non-SQL databases; it is up to each Engine to use or not use DBI, though most will use it because the DBI is a high quality and mature platform to build upon.

The choice between using DBI and using Rosetta seems to be analogous to the choice between the C and Java programming languages, respectively, where each database product is analogous to a hardware CPU architecture or wider hardware platform. The DBI is great for people who like working as close to the metal as possible, with direct access to each database product's native way of doing things, those who *want* to talk to their database in its native SQL dialect, and those who want the absolute highest performance. Rosetta is more high level, for those who want the write-once run-anywhere experience, less of a burden on their creativity, more development time saving features, and are willing to sacrifice a modicum of performance for the privilege.

There exist on CPAN many dozens of other modules or frameworks whose modus operandi is to wrap the DBI or be used together with it for various reasons, such as to provide automated object persistence functionality, or a cross-database portability solution, or to provide part of a wider scoped application tool kit, or to generate SQL, or to clone databases, or generate reports, or provide a web interface, or to provide a "simpler" or "easier to use" interface. So, outside the DBI question, a choice exists between using Rosetta and one of these other CPAN modules. Going into detail on that matter is outside the scope of this documentation, but a few salient points are offered. For one thing, Rosetta allows you to do a lot more than the alternatives in an elegant fashion; with other modules, you would often have to inject fragments of raw SQL into their objects (such as "select" query conditionals) to accomplish what you want; with Rosetta, you should never need to do any SQL injection. For another point, Rosetta has a strong emphasis on portability between many database products; only a handful of other modules support more than 2-3 database products, and many only claim to support one (usually MySQL). Also, more than half of the other modules look like they had only 5-20 hours of effort at most put into them, while Rosetta and its related modules have likely had over 1000 hours of full time effort put into them. For another point, there is a frequent lack of support for commonly desired database features in other modules, such as multiple column keys. Also, most modules have a common structural deficiency such that they are designed to support a very specific set of database concepts, and adding more is a lot of work; by contrast, Rosetta is internally designed in a heavily data-driven fashion, allowing the addition or alternation of many features with little cost in effort or complexity.

Perhaps a number of other CPAN modules' authors will see value in adding back-end support for Rosetta and/or SQL::Routine to their offerings, either as a supplement to their DBI-using native database SQL back-ends, or as a single replacement for the lot of them. Particularly in the latter case, the authors will be more freed up to focus on their added value, such as object persistence or web interfaces, rather than worrying about portability issues. As quid quo pro, perhaps some of the other CPAN modules (or parts of them) can be used by a Rosetta Engine to help it do its work.

Please see the Rosetta::Framework documentation file for more information on the Rosetta framework at large. It shows this current module in the context of actual or possible other components.

CLASSES IN THIS MODULE

This module is implemented by several object-oriented Perl 5 packages, each of which is referred to as a class. They are: Rosetta (the module's name-sake), Rosetta::Interface (aka Interface), Rosetta::Engine (aka Engine), and Rosetta::Dispatcher (aka Dispatcher).

While all 4 of the above classes are implemented in one module for convenience, you should consider all 4 names as being "in use"; do not create any modules or packages yourself that have the same names.

The Interface class does most of the work and is what you mainly use. The name-sake class mainly exists to guide CPAN in indexing the whole module, but it also provides a set of stateless utility methods and constants that the other two classes inherit, and it provides a wrapper function over the Interface class for your convenience; you never instantiate an object of Rosetta itself.

The Engine class is only invoked indirectly, via the Interface class; moreover, you need to choose an external class which subclasses Engine (and implements all of its methods) to use via the Interface class.

The Dispatcher class is used internally by Interface to implement an ease-of-use feature of Rosetta where multiple Rosetta Engines can be used as one. An example of this is that you can invoke a CATALOG_LIST built-in routine without specifying an Engine to run it against; Dispatcher will run that command against each individual Engine behind the scenes and combine their results; you then see a single list of databases that Rosetta can access without regard for which Engine mediates access. As a second example, you can invoke a CATALOG_OPEN built-in off of the root Application Interface rather than having to do it against the correct Environment Interface; Dispatcher will detect which Environment is required (based on info in your SQL::Routine) and load/dispatch to the appropriate Engine that mediates the database connection.

STRUCTURE

The Rosetta core module is structured like a simple virtual machine, which can be conceptually thought of as implementing an embedded SQL database; alternately, it is a command interpreter. This module is implemented with 2 main classes that work together, which are "Interface" and "Engine". To use Rosetta, you first create a root Interface object (or several; one is normal) using Rosetta->new_application(), which provides a context in which you can prepare and execute commands against a database or three. One of your first commands is likely to open a connection to a database, during which you associate a separately available Engine plug-in of your choice with said connection. This Engine plug-in does all the meat of implementing the Rosetta API that the Interface defines; the Engine class defined inside the Rosetta core module is a simple common super-class for all Engine plug-in modules.

Note that each distinct Rosetta Engine class is represented by a distinct SQL::Routine "data_link_product" Node that you create; you put the name of the Rosetta Engine Class, such as "Rosetta::Engine::foo", in that Node's "product_code" attribute. The SQL::Routine documentation refers to that attribute as being just for recognition by an external "mediation layer"; when you use Rosetta, then Rosetta *is* said "mediation layer".

During the normal course of using Rosetta, you will end up talking to a whole bunch of Interface objects, which are all related to each other in a tree-like fashion. Each time you prepare() or execute() a command against one, another is typically spawned which represents the results of your command, be it an error condition or a database connection handle or a transaction context handle or a select cursor handle or a miscellaneous returned data container. Each Interface object has a "type" property which says what kind of thing it represents and how it behaves. All Interface types have a "get_error_message()" method but only a cursor type, for example, has a "fetch_row()" method. For simplicity, all Interface objects are explicitly defined to have all possible Interface methods (no "autoload" et al is used); however, an inappropriately called method will throw an exception saying so, so it is as if Perl had a normal run-time error due to calling a non-existent method.

Each Interface object may also have its own Engine object associated with it behind the scenes, with all the Engine objects in a mirroring tree structure; but that may not always be true. One example is right when you start out, or if you try to open a database connection using a non-existent Engine module. Specifically, it is Error Interfaces and Success Interfaces and Application Interfaces that never have their own associated Engine; every other type of Interface must have one.

This diagram shows all of the Interface types and how they are allowed to relate to each other parent-child wise in an Interface tree:

1	Error
2	Success
3	Application
4	  Preparation
5	    Literal
6	    Environment
7	      Preparation
8	        Literal
9	        Connection
10	          Preparation
11	            Literal
12	            Cursor
13	              Preparation
14	                Literal

The "Application" (3) at the top is created using "Rosetta->new()", and you normally have just one of those in your program. A "Preparation" (4,7,10,13) is created when you invoke "prepare()" off of an Interface object of one of these types: "Application" (3), "Environment" (6), "Connection" (9), "Cursor" (12). Every type of Interface except "Application" and "Preparation" is created by invoking "execute()" of an appropriate "Preparation" method. The "prepare()" and "execute()" methods always create a new Interface having the result of the call, and this is usually a child of the one you invoked it from.

An "Error" (1) Interface can be returned potentially by any method and it is self-contained; it has no parent Interfaces or children. Note that any other kind of Interface can also store an error condition in addition to keeping its normal properties.

A "Success" (2) Interface is returned by execute() when the method being executed is a PROCEDURE and it succeeds. Since procedures by design don't return anything, but execute() must return something, this is what you get to indicate a successful procedure; any routine that should return something meaningful is a FUNCTION. (An unsuccessful routine of any kind throws an Error.) A Success Interface has no parent or child Interfaces.

For convenience, what sometimes happens is that the Rosetta Engine will create multiple Interface generations for you as appropriate when you say "prepare()". For example, if you give a "open this database" routine to an "Application" (3) Interface, you would be given back a great-grand-child "Preparation" (7) Interface. Be aware of this if you ever request that an Interface you hold give you its parent.

FEATURE SUPPORT VALIDATION

The Rosetta Native Interface (RNI) declares accessors for a large number of actual or possible database features, any of which your application can invoke, and all of which each Rosetta Engine would ideally implement or interface to.

In reality, however, all Engines or underlying databases probably don't support some features, and if your application tries to invoke any of the same features that an Engine you are using doesn't support, then you will have problems ranging from immediate crashes/exceptions to subtle data corruption over time.

As an official quality assurance (QA) measure, Rosetta provides a means for each Engine to programmatically declare which RNI features it does and does not support, so that code using that Engine will know so in advance of trying to use said features. Feature support declarations are typically coarse grained and lump closely similar things together, for simplicity; they will be just as fine grained as necessary and no finer (this can be changed over time). See the features() method, which is how you read the declarations.

The features() method is usually invoked off of either an Environment Interface or a Connection Interface. The Environment method invocation is used to declare features that the Environment's Engine supports under all circumstances of its use. The Connection method invocation is used to declare features that the Engine conditionally supports on a per-connection basis, because the same Engine may be able to link to multiple database products that have different capabilities; the results only apply to the Connection Interface it was invoked off of. Note that the declarations by the second are a full super-set of those by the first; if the Engine knowingly deals with exactly one database product, then the two declaration sets would be identical.

One benefit of this QA feature is that, after you have written your application and it is working with one Engine/database, and you want to move it to a different Engine/database, you can determine at a glance which alternatives also support the features you are using. Note that, generally speaking, you would have to be using very proprietary features to begin with in order for the majority of Rosetta Engines/databases to not support the application outright.

Another benefit of this QA feature is that there can be made a common comprehensive test suite to run against all Engines in order to tell that they are implementing the Rosetta interface properly or not; said test suite will be smart enough to only test each Engine's RNI compliance for those features that the Engine claims to support, and not fail it for non-working features that it explicitly says it doesn't support. This common test suite will save each Engine maker from having to write their own module tests. It would be used similarly to how Sun has an official validation suite for Java Virtual Machines to make sure they implement the official Java specification. Please see the Rosetta::Validator module(s), which implements this test suite.

See the Rosetta::Features documentation file for a complete list of what RNI features a Rosetta Engine can possibly implement, and that Rosetta::Validator can test for.

SYNTAX

This class does not export any functions or methods, so you need to call them using object notation. This means using Class->function() for functions and $object->method() for methods. If you are inheriting this class for your own modules, then that often means something like $self->method().

All class functions and methods will throw exceptions on error conditions; they will only return normally if there are no error conditions. The thrown exceptions will be Locale::KeyedText::Message objects when the error is bad user/caller input, and they will be Rosetta::Interface objects with set Error Message properties when the error is a failed prepare() or execute(). You should never get a raw Perl exception that is generated within Rosetta or one of its Engines.

CONSTRUCTOR WRAPPER FUNCTIONS

These functions are stateless and can be invoked off of either the module name, or any package name in this module, or any object created by this module; they are thin wrappers over other methods and exist strictly for convenience.

new_application( SRT_NODE )

my $app = Rosetta->new_application( $my_app_inst );
my $app2 = Rosetta::Interface->new_application( $my_app_inst );
my $app3 = $app->new_application( $my_app_inst );

This function wraps Rosetta::Interface->new( 'Application', undef, undef, undef, SRT_NODE ). It can only create 'Application' Interfaces, and its sole SRT_NODE argument must be an 'application_instance' SQL::Routine::Node.

INTERFACE CONSTRUCTOR FUNCTIONS AND METHODS

This function/method is stateless and can be invoked off of either the Interface class name or an existing Interface object, with the same result.

new( INTF_TYPE[, ERR_MSG][, PARENT_INTF][, ENGINE][, SRT_NODE][, ROUTINE] )

my $app = Rosetta::Interface->new( 'Application', undef, undef, undef, $my_app_inst );
my $conn_prep = $app->new( 'Preparation', undef, $app, undef, $conn_srt_node, $conn_routine );

This "getter" function/method will create and return a single Rosetta::Interface (or subclass) object whose Interface Type is given in the INTF_TYPE (enum) argument; all of the other properties will be set from the remaining arguments depending on what the Interface Type is. All of an Interface's properties must be set on instantiation and can not be changed afterwards, except when said Interface is destroyed (the Throw Errors property is the lone exception). The class works this way since each Interface object is typically the result of invoking a method off another Interface object; the new object contains the "result" of calling the method. The ERR_MSG argument holds a Locale::KeyedText::Message object; you set this when the new Interface represents an error condition. The PARENT_INTF argument holds another Interface object which is to be the parent of the new one; typically it is the Interface whose method was invoked to indirectly create the current one; every Interface must have a parent unless it is an 'Application', which must not have one. The ENGINE argument is an Engine object that will implement the new Interface. The SRT_NODE argument is a SQL::Routine::Node argument that provides a context or instruction for how the new Interface is created; eg, it contains the "command" which the new Interface mediates the result of. The ROUTINE argument is a Perl anonymous subroutine reference (or closure); this is created by an Engine in its prepare() method, and executed by execute(). This function will throw exceptions if any arguments are inappropriate in context. Typically this function is only invoked directly by the Engine object behind its parent-to-be Interface when said Interface is called with prepare/execute. The Throw Errors property defaults from the parent Interface, or to false.

INTERFACE OBJECT METHODS

These methods are stateful and may only be invoked off of Interface objects.

destroy()

$interface->destroy();

This "setter" method will destroy the Interface object that it is invoked from, and it will also destroy the Engine associated with the Interface, if any. This method will fail if the current Interface object has child Interfaces; you have to destroy each of them first.

get_interface_type()

my $type = $interface->get_interface_type();

This "getter" method returns the Interface Type scalar (enum) property of this Interface.

get_error_message()

my $message = $interface->get_error_message();

This "getter" method returns by reference the Error Message Locale::KeyedText::Message object property of this Interface, if it has one.

get_parent_interface()

my $parent = $interface->get_parent_interface();

This "getter" method returns by reference the parent Interface of this Interface, if it has one.

get_root_interface()

my $appl_intf = $interface->get_root_interface();

This "getter" method returns by reference the root 'Application' Interface of the tree that this Interface is in, if possible. If the current Interface is an 'Application', then this method returns a reference to itself. If the current Interface is either an 'Error' or 'Success', then this method returns undef. This is strictly a convenience method, similar to calling get_parent_interface() recursively, and it exists to help make code faster.

get_child_interfaces()

my $children = $interface->get_child_interfaces();

This "getter" method returns a new array ref having references to all of this Interface's child Interfaces, or an empty array ref if there are no children.

get_sibling_interfaces([ SKIP_SELF ])

my $siblings_with_self = $interface->get_sibling_interfaces();
my $siblings_not_self = $interface->get_sibling_interfaces( 1 );

This "getter" method returns a new array ref having references to all of this Interface's sibling Interfaces. This list includes by default the Interface upon which the method was called; however, if the optional boolean argument SKIP_SELF is true, then the list will exclude the called-on Interface. If this Interface has no parent Interface, then the returned list will either consist of just itself, or be an empty list, depending on SKIP_SELF.

get_engine()

my $engine = $interface->get_engine();

This "getter" method returns by reference the Engine that implements this Interface, if it has one. This method may be removed later.

get_srt_node()

my $node = $interface->get_srt_node();

This "getter" method returns by reference the SQL::Routine::Node object property of this Interface, if it has one.

get_srt_container()

my $container = $interface->get_srt_container();

This "getter" method returns by reference the SQL::Routine::Container object that is shared by this Interface tree, if there is one.

get_routine()

my $routine = $preparation->get_routine();

This "getter" method returns by reference the Perl anonymous routine property of this Interface, if it has one. This method may be removed later.

get_trace_fh()

my $fh = $interface->get_trace_fh();

This "getter" method returns by reference the writeable Perl trace file handle property of root 'Application' Interface of the tree that this Interface is in, if possible; it returns undef otherwise. This property is set after Intf creation and can be cleared or set at any time. When set, details of what Rosetta is doing will be written to the file handle; to turn off the tracing, just clear the property. This class does not open or close the file; your external code must do that.

clear_trace_fh()

$interface->clear_trace_fh();

This "setter" method clears the trace file handle property of this Interface tree root, if it was set, thereby turning off any tracing output.

set_trace_fh( NEW_FH )

$interface->set_trace_fh( \*STDOUT );

This "setter" method sets or replaces the trace file handle property of this Interface tree root to a new writeable Perl file handle, provided in NEW_FH, so any subsequent tracing output is sent there.

features([ FEATURE_NAME ])

This "getter" method may only be invoked off of Interfaces having one of these types: "Application", "Environment", "Connection"; it will throw an exception if you invoke it on anything else. When called with no arguments, it will return a Perl hash ref whose keys are the names of key feature groups that the corresponding Engine is declaring its support status for; values are always either '1' for 'yes' and '0' for 'no'. If a key is absent, then the Engine is saying that it doesn't know yet whether it will support the feature or not. If the optional argument FEATURE_NAME is defined, then this method will treat that like a key in the previous mentioned hash and return just the associated value of 1, 0, or undefined (don't know). When a particular Environment says 'yes' or 'no' for particular features, then grandchild Connections are guaranteed to say likewise; when an Environment says "don't know" for a feature, then the Connections can each change this to 'yes' or 'no' as it applies to them; however, if a Connection still says "don't know" then this can be read as 'no' if the Connection state is open; it still means "don't know" if the Connection state is closed; a closed state's "don't know" can be changed by its corresponding open state. Note that invoking Application.features() will cause all available Engines to load, each of their Environments consulted, and the results combined to give the final result; for each possible feature, the combined output is 'yes' iff all input Engines are 'yes', 'no' iff all 'no', and undefined/missing if any inputs differ or are undefined/missing; if there are no available Engines, the result is empty-list/undefined.

prepare( ROUTINE_DEFN )

This "getter"/"setter" method takes a SQL::Routine::Node object usually representing a "routine" in its ROUTINE_DEFN argument, then "compiles" it into a new "Preparation" Interface (returned) which is ready to execute the specified action. The ROUTINE_DEFN is always a "routine" SRT Node, but with one exception when it is a "data_link_product" Node. This method may only be invoked off of Interfaces having one of these types: "Application", "Environment", "Connection", "Cursor"; it will throw an exception if you invoke it on anything else. Most of the time, this method just passes the buck to the Engine module that actually does its work, after doing some basic input checking, or it will instantiate an Engine object. Any calls to Engine objects are wrapped in an eval block so that miscellaneous exceptions generated there don't kill the program. Note that invoking Application.prepare() where the ROUTINE_DEFN is a "data_link_product" Node will create a "Preparation" Interface that would simply load an Engine, but doesn't make a Connection or otherwise ask the Engine to do anything. Invoking Application.prepare() with a "routine" SRT Node will pass the buck to Rosetta::Dispatcher, which usually passes to a single normal Engine (loading it first if necessary); as an exception to this, if the routine invokes the 'CATALOG_LIST' built-in standard routine, then Dispatcher invokes a multitude of Engines (loading if needed) and combines their results.

execute([ ROUTINE_ARGS ])

This "getter"/"setter" method can only be invoked off of a "Preparation" Interface and will actually perform the action that the Interface is created for. The optional hash ref argument ROUTINE_ARGS provides run-time arguments for the previously "compiled" routine, if it takes any. Unlike prepare(), which usually calls an Engine module's prepare() to do the actual work, execute() will not call an Engine directly at all. Rather, execute() simply executes the Perl anonymous subroutine property of the "Preparation" Interface. Said routine is created by an Engine's prepare() method and is usually a closure, containing within it all the context necessary for work as if the Engine was doing it. Said routine returns new non-prep Interfaces.

do( ROUTINE_DEFN[, ROUTINE_ARGS] )

This is a simple convenience method that wraps a single prepare/execute operation; it cuts in half the number of method calls you have to make, if you are only going to execute the same prepared routine once. You can invoke do() in any situation that you can invoke a prepare( ROUTINE_DEFN ) with the same first argument; the Preparation object returned by the prepare() has execute([ ROUTINE_ARGS ]) invoked on it; the Interface object returned by the execute() is what do() returns. Any exceptions thrown by the wrapped methods will propagate unchanged to the code that invoked do(). If the execute() phase either throws an exception of any kind, usually an 'Error' Interface, or it returns a 'Success' Interface, the do() method will destroy() the Preparation object that it just made before returning or re-throwing the execute() result; if execute() returns any other kind of Interface, the Preparation will not get destroyed, as the caller will have a handle on it via the returned result.

payload()

This "getter" method can only be invoked off of a "Literal" Interface. It will return the actual payload that the "Literal" Interface represents. This can either be an ordinary string or number or boolean, or a SRT Node ref, or an array ref or hash ref containing other literal values.

routine_source_code( ROUTINE_NODE )

This "getter" method can only be invoked off of a "Environment" Interface. It will return, as a character string, the Perl source code of the user-defined routine that was successfully prepared out of the ROUTINE_NODE SRT Node by the Engine behind this Environment, either because the method was invoked directly by prepare(), or it was invoked by another method that was; this method returns the undefined value if the ROUTINE_NODE was never compiled by this Engine. This method is only intended for use when debugging an Engine.

ENGINE OBJECT FUNCTIONS AND METHODS

Rosetta::Engine defines shims for all of the required Engine methods, each of which will throw an exception if the sub-classing Engine module doesn't override them. These methods all have the same names and functions as Interface methods, which just turn around and call them. Every Engine method takes as its first argument a reference to the Interface object that it is implementing (the Interface shim provides it); otherwise, each method's argument list is the same as its same-named Interface method. These are the methods: destroy(), features(), prepare(), payload(), routine_source_code(). Every Engine must also implement a new() method which will be called in the form [class-name-or-object-ref]->new() and must instantiate the Engine object; typically this is called by the parent Engine, which also makes the Interface for the new Engine.

DISPATCHER OBJECT FUNCTIONS AND METHODS

Rosetta::Dispatcher should never be either invoked or sub-classed by you, so its method list will remain undocumented and private.

INTERFACE FUNCTIONS AND METHODS FOR RAPID DEVELOPMENT

The following 9 "setter" functions and methods should assist more rapid development of code that uses Rosetta, at the cost of some flexibility. They differ from the other Interface functions and methods in that they also create or alter the SQL::Routine model associated with a Rosetta Interface tree. These methods are implemented as wrappers over other Rosetta and SQL::Routine methods, and allow you to accomplish with one method call what otherwise requires at least 4-40 function or method calls, meaning your code base is significantly smaller (unless you implement your own simplifying wrapper functions, which is recommended in some situations).

Note that when a subroutine is referred to as a "function", it is stateless and can be invoked off of either a class name or class object; when a subroutine is called a "method", it can only be invoked off of Interface objects.

build_application()

my $app_intf = Rosetta->build_application();

This function is like new_application() in that it will create and return a new Application Interface object. This function differs in that it will also create a new SQL::Routine model by itself and associate the new Interface with it, rather than requiring you to separately make the SRT model. The created model is as close to empty as possible; it contains only 2 SRT Nodes, which are 1 'application' and 1 related 'application_instance'; the latter becomes the SRT_NODE argument for new_application(). The "id" and "si_name" of each new Node is given a default generated value. You can invoke get_srt_node() or get_srt_container() on the new Application Interface to access the SRT Nodes and model for further additions or changes.

build_application_with_node_trees( SRT_NODE_DEFN_LIST[, AUTO_ASSERT[, AUTO_IDS[, MATCH_SURR_IDS]]] )

my $app_intf = Rosetta->build_application_with_node_trees( [...] );

This function is like build_application() except that it lets you define the entire SRT Node hierarchy for the new model yourself; that definition is provided in the SRT_NODE_DEFN_LIST argument. This function expects you to define the 'application' and 'application_instance' Nodes yourself, in SRT_NODE_DEFN_LIST, and it will link the new Application Interface to the first 'application_instance' Node that it finds in the newly created SRT model. This method invokes SQL::Routine->build_container( SRT_NODE_DEFN_LIST, AUTO_ASSERT, AUTO_IDS, MATCH_SURR_IDS ) to do most of the work.

build_environment( ENGINE_NAME )

my $env_intf = Rosetta->build_environment( 'Rosetta::Engine::Generic' );

This function is like build_application() except that it will also create a new 'data_link_product' Node, using ENGINE_NAME as the 'product_code' attribute, and it will create a new associated Environment Interface object, that fronts a newly instantiated Engine object of the ENGINE_NAME class; the Environment Interface is returned.

build_child_environment( ENGINE_NAME )

my $env_intf = $app_intf->build_child_environment( 'Rosetta::Engine::Generic' );

This method may only be invoked off of an Application Interface. This method is like build_environment( ENGINE_NAME ) except that it will reuse the Application Interface that it is invoked off of, and associated Nodes, rather than making new ones. Moreover, if an Environment Interface with the same 'product_code' already exists under the current Application Interface, then build_child_environment() will not create or change anything, but simply return the existing Environment Interface object instead of a new one.

build_connection( SETUP_OPTIONS[, RT_SI_NAME[, RT_ID]] )

my $conn_intf_sqlite = Rosetta->build_connection( {
	'data_storage_product' => {
		'product_code' => 'SQLite',
		'is_file_based' => 1,
	},
	'data_link_product' => {
		'product_code' => 'Rosetta::Engine::Generic',
	},
	'catalog_instance' => {
		'file_path' => 'test',
	},
} );
my $conn_intf_mysql = Rosetta->build_connection( {
	'data_storage_product' => {
		'product_code' => 'MySQL',
		'is_network_svc' => 1,
	},
	'data_link_product' => {
		'product_code' => 'Rosetta::Engine::Generic',
	},
	'catalog_link_instance' => {
		'local_dsn' => 'test',
		'login_name' => 'jane',
		'login_pass' => 'pwd',
	},
}, 'declare_conn_to_mysql', 3 );

This function will create and return a new Connection Interface object plus the prerequisite SQL::Routine model and Interface and Engine objects. The SETUP_OPTIONS argument is a two-dimensional hash, where each outer hash element corresponds to a Node type and each inner hash element corresponds to an attribute name and value for that Node type. There are 6 allowed Node types: data_storage_product, data_link_product, catalog_instance, catalog_link_instance, catalog_instance_opt, catalog_link_instance_opt; the first 4 have a specific set of actual scalar or enumerated attributes that may be set; with the latter 2, you can set any number of virtual attributes that you choose. The "setup options" say what Rosetta Engine to test and how to configure it to work in your customized environment. The actual attributes of the first 4 Node types should be recognized by all Engines and have the same meaning to them; you can set any or all of them (see the SQL::Routine documentation for the list) except for "id" and "si_name", which are given default generated values. The build_connection() function requires that, at the very least, you provide a 'data_link_product'.'product_code' SETUP_OPTIONS value, since that specifies the class name of the Rosetta Engine that implements the Connection. The virtual attributes of the last 2 Node types are specific to each Engine (see the Engine's documentation for a list), though an Engine may not define any at all. This function will generate 1 Node each of the first 4 Node types, and zero or more each of the last 2 Node types, all of which are cross-associated, plus the same Nodes that build_application() does, plus 1 each of: 'catalog', catalog_link, 'routine', plus several child Nodes of the 'routine'. The new 'routine' that this function creates is what declares the new Connection Interface, and becomes its SRT Node. Since you are likely to declare many routines subsequently in the same SRT model (but unlikely to declare more of any of the other aforementioned Node types), this function lets you provide explicit "si_name" and "id" attributes for the new 'routine' Node only, in the optional arguments RT_SI_NAME and RT_ID respectively.

build_child_connection( SETUP_OPTIONS[, RT_SI_NAME[, RT_ID]] )

my $conn_intf_postgres = $env_intf->build_child_connection( {
	'data_storage_product' => {
		'product_code' => 'PostgreSQL',
		'is_network_svc' => 1,
	},
	'catalog_link_instance' => {
		'local_dsn' => 'test',
		'login_name' => 'jane',
		'login_pass' => 'pwd',
	},
} );

This method may only be invoked off of an Application or Environment Interface. This method is like build_connection( SETUP_OPTIONS, RT_SI_NAME, RT_ID ) except that it will reuse the Application and/or Environment Interface that it is invoked off of, and associated Nodes, rather than making new ones. If invoked off of an Environment Interface, then any 'data_link_product' info that might be provided in SETUP_OPTIONS is ignored. If invoked off of an Application Interface, this method will try to reuse an existing child Environment Interface, that matches the given 'data_link_product'.'product_code', before making a new one, just as build_child_environment() does. This method will not attempt to reuse any other types of Nodes, so if that's what you want, you can't use this method to do it.

validate_connection_setup_options( SETUP_OPTIONS )

This function is used internally by build_connection() and build_child_connection() to confirm that its SETUP_OPTIONS argument is valid, prior to it changing the state of anything. This function is public so that external code can use it to perform advance validation on an identical configuration structure without side-effects. Note that this function is not thorough; except for the 'data_link_product'.'product_code' (Rosetta Engine class name), it does not test that Node attribute entries in SETUP_OPTIONS have defined values, and even that single attribute isn't tested beyond that it is defined. Testing for defined and mandatory option values is left to the SQL::Routine methods, which are only invoked by build_[child_]_connection().

destroy_interface_tree()

my $container = $app_intf->destroy_interface_tree();

This method can be invoked on any Rosetta Interface object and will recursively destroy an entire Rosetta Interface tree, starting with the child-most Interface objects and working down to the root Interface. This method does not destroy the SQL::Routine model used by the Interfaces, and returns a reference to it upon completion, so that you have the option to re-use the model later, such as with a new Interface tree. Note that it is pointless to invoke this on an 'Error' or 'Success' Interface object because those types do not live in Interface trees, and lack the circular refs to prevent them being auto-destroyed when references to them are gone.

destroy_interface_tree_and_srt_container()

$app_intf->destroy_interface_tree_and_srt_container();

This function is like destroy_interface_tree() except that it will also destroy the SQL::Routine model that the invoked-on Interface tree is using.

CONNECTION INTERFACE METHODS FOR RAPID DEVELOPMENT

These methods provide a simplified interface to many commonly performed database activities, and should should assist more rapid development of code that uses Rosetta; they are all implemented as wrappers over other, more generic Rosetta and SQL::Routine methods. All of these methods can only be invoked off of a Connection Interface, because they only make sense within the context of a database connection. Each of these methods will generate a new SRT 'routine' whose 'routine_context' is the 'CONN' that the invoked-on Connection Interface represents.

WARNING: These methods are early prototypes and will be replaced within 1-2 subsequent Rosetta releases with other methods that behave differently. The current versions will prepare() the new SRT routines and return the Preparation Interface to you to execute(). For each method, optional RT_SI_NAME and RT_ID arguments are provided; you can give explicit "si_name" and "id" attributes to the new 'routine' Node, or such values will be generated.

sroutine_catalog_list([ RT_SI_NAME[, RT_ID] ])

This method will build and return a prepared wrapper function for the CATALOG_LIST built-in SRT standard routine, which when executed, will return a Literal Interface whose payload is an array ref having zero or more newly generated 'catalog_link' SRT Nodes, each of which represents an auto-detected database catalog instance.

sroutine_catalog_open([ RT_SI_NAME[, RT_ID] ])

This method will build and return a prepared wrapper procedure for the CATALOG_OPEN built-in SRT standard routine, which when executed, will change the invoked on Connection from a closed state to an opened state, and will return a Success Interface. The prepared procedure will take 2 optional arguments at execute() time, which are 'login_name' and 'login_pass'; these values will be used if and only if a 'login_name' and 'login_pass' were not provided by the 'catalog_link' SRT Node that was used to make the invoked from Connection Interface.

sroutine_catalog_close([ RT_SI_NAME[, RT_ID] ])

This method will build and return a prepared wrapper procedure for the CATALOG_CLOSE built-in SRT standard routine, which when executed, will change the invoked on Connection from an opened state to a closed state, and will return a Success Interface.

BUGS

This module is currently in pre-alpha development status, meaning that some parts of it will be changed in the near future, perhaps in incompatible ways; however, I believe that any further incompatible changes will be small. The current state is analogous to 'developer releases' of operating systems; it is reasonable to being writing code that uses this module now, but you should be prepared to maintain it later in keeping with API changes. All of this said, I plan to move this module into alpha development status within the next few releases, once I start using it in a production environment myself.

SEE ALSO

perl(1), Rosetta::L::en, Rosetta::Features, Rosetta::Framework, Locale::KeyedText, SQL::Routine, Rosetta::Engine::Generic, DBI, Alzabo, SPOPS, Class::DBI, Tangram, HDB, Genezzo, DBIx::RecordSet, DBIx::SearchBuilder, SQL::Schema, DBIx::Abstract, DBIx::AnyDBD, DBIx::Browse, DBIx::SQLEngine, MKDoc::SQL, Data::Transactional, DBIx::ModelUpdate, DBIx::ProcedureCall, and various other modules.