NAME

Rosetta - Rigorous database portability

VERSION

This document describes Rosetta version 0.48.2.

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 it has few external dependencies.

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.

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. Rosetta itself mainly provides a skeleton to which Engines attach, and does basic input and output validation so that Engines and applications respectively don't have to; otherwise, an Engine has complete freedom to fulfill requests how it wishes.

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.

It should be noted that Rosetta itself has no aim to be an "object-relational mapper", since this isn't related to its primary function of making all database products look the same; its API is still defined solidly in relational database specific terms such as schemas, tables, columns, keys, joins, queries, views, DML, DDL, connections, cursors, triggers, etc. If you need an actual "object oriented persistence" solution, which makes your own custom application objects transparently persistent, or a persistence layer whose API is defined in terms like objects and attributes, then you will need an alternative to the Rosetta core to give that to you, such as modules like Class::DBI, Tangram, and Alzabo, or a custom solution (these can, of course, be layered on top of Rosetta).

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.

To cut down on the size of the SQL::Routine module itself, some of the POD documentation is in these other files: Rosetta::Features, Rosetta::Framework.

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::Interface::Application (aka Application), Rosetta::Interface::Environment (aka Environment), Rosetta::Interface::Connection (aka Connection), Rosetta::Interface::Cursor (aka Cursor), Rosetta::Interface::Literal (aka Literal), Rosetta::Interface::Success (aka Success), Rosetta::Interface::Preparation (aka Preparation), Rosetta::Interface::Error (aka Error), Rosetta::Engine (aka Engine), and Rosetta::Dispatcher (aka Dispatcher).

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

The Interface classes do most of the work and are 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 classes inherit, and it provides wrapper functions over the Interface classes for your convenience; you never instantiate an object of 'Rosetta' itself.

The Engine class is only invoked indirectly, via the Interface classes; 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 the Application class (as its "Engine") 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 groups of classes that work together, which are "Interface" (Rosetta::Interface::*) and "Engine" (Rosetta::Engine::*). To use Rosetta, you first create a root Application Interface object (or several; one is normal), that is associated with a SQL::Routine "application_instance" Node, using Rosetta->new_application_interface(), 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 class of Interface object represents something different and has a distinct set of properties and methods, though all Interface objects have some of both in common. 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_srt_container()" method but only a literal type, for example, has a "payload()" 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. (Technically, an Application has a pseudo-Engine named Rosetta::Dispatcher behind it, but that is just for coordinating the use of other actual Engines, which have Environment or other Interface types for them.)

There are two types of parent-child relationships for Interface objects, which are called [parent|child]-by-creation and [parent|child]-by-context. Each Interface type is involved in either both, one, or none of these relationships.

The by-context relationship is simpler to explain and involves just 4 Interface types, in this hierarchy:

X1  Application
X2    Environment
X3      Connection
X4        Cursor

In summary, each parent provides a context in which the child operates, and the child always has a state that is relative to the parent. For example, a Connection is always implemented by a specific Engine, whose root is held in an Environment. As another connection, a Cursor always operates within the context of an open database Connection. And all Engines/Environments exist to serve your user application, represented by Application.

The by-creation relationship involves all Interface types, usually mediated by the Preparation type, and most of which can have a child Error.

To start off, you invoke new_application_interface() on anything to get this:

N1   Application

With this set, new_environment_interface() on the parents to get the children:

N2   Application
N3     Error
N4     Environment

With this set, you invoke prepare() on the parents to get the children:

N5   Application
N6     Error
N7     Preparation
N8   Environment
N9     Error
N10    Preparation
N11  Connection
N12    Error
N13    Preparation
N14  Cursor
N15    Error
N16    Preparation

With this set, you invoke execute() on the parents to get the children:

N17  Preparation
N18    Error
N19    Success
N20    Connection
N21    Cursor
N22    Literal

In summary, each parent conceptually creates the child. For example, when you invoke prepare() on a Connection, that creates a Preparation object, which is a "compiled" routine; when you call execute() on that, it performs the routine and returns the results, for example as a Literal. As a more specific example, if the compiled routine represents a select-query, then the Connection represents the database it will be run against, and the Literal represents the fetched row-set; in DBI terms, this Connection is a $dbh, and the Preparation a $sth.

A Success Interface is returned implicitly when the routine being executed is a PROCEDURE (which has no explicit return type). An Error Interface is thrown as an exception any time something fails.

A few more notes about a conceptualization in progress; very rough draft:

stages of doingness:
1. define in a SQL::Routine model (can be done during app's init, prior to forking)
2. compile to a Perl closure (can be done during server app's init, prior to forking workers)
    2.1 by definition, no external resources (eg, databases) are contacted
    2.2 this may or may not (probably will be) done by an Engine
    2.3 by definition, these can be reused by multiple equivalent db connections etc (no db-side prepare)
    2.4 usually all generation of SQL strings happens here
3. execute said closure which actually opens db conn or execs statement or fetches data etc
    3.1 *all* DBI invocations (if used) are done here
    3.2 this does both the DBI prepare() and execute() the first time called; just the second subsequently
        3.2.1 any preparation that the database does itself happens here, and is cached for appropriate time
    3.3 the result of a DBI prepare() is another closure that's cached for use by DBI execute()
        3.3.1 the compile-closure includes all the actual invocations of the cache for the prepare-closure
4. like Perl itself, the above steps can be recursive; step 2 or 3 may trigger a step 1 or 2

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 caught by an Interface class; they will be Rosetta::Interface::Error objects when an Engine has either caught bad user input, or the database has a problem, or the Engine fails in some other way during execution. You should never get a raw Perl exception that is generated within Rosetta or one of its Engines.

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.

CONSTRUCTOR WRAPPER FUNCTIONS

These 8 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.

All 8 have a similar format, "new_<lowercased-class-name>_interface( <arg-list> )", and each one can be invoked like this example:

my $app = Rosetta->new_application_interface( $my_app_inst_node );
my $app2 = Rosetta::Interface->new_application_interface( $my_app_inst_node );
my $app3 = $app->new_application_interface( $my_app_inst_node );

new_application_interface( APP_INST_NODE )

This function wraps Rosetta::Interface::Application->new( * ).

This function wraps Rosetta::Interface::Environment->new( * ).

This function wraps Rosetta::Interface::Connection->new( * ).

new_cursor_interface( PARENT_BYCRE_INTF, PARENT_BYCXT_INTF, EXTERN_CURS_NODE )

This function wraps Rosetta::Interface::Cursor->new( * ).

new_literal_interface( PARENT_BYCRE_INTF )

This function wraps Rosetta::Interface::Literal->new( * ).

new_success_interface( PARENT_BYCRE_INTF )

This function wraps Rosetta::Interface::Success->new( * ).

new_preparation_interface( PARENT_BYCRE_INTF, ROUTINE_NODE )

This function wraps Rosetta::Interface::Preparation->new( * ).

new_error_interface( PARENT_BYCRE_INTF, ERROR_MSG )

This function wraps Rosetta::Interface::Error->new( * ).

INTERFACE OBJECT METHODS

These methods are stateful and may only be invoked off of Rosetta::Interface::* objects; they access properties defined by the Rosetta::Interface class, and inherited by said other classes; each object in the Interface tree has its own.

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. This is strictly a convenience method, similar to calling get_parent_by_creation_interface() recursively, and it exists to help make code faster.

get_parent_by_creation_interface()

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

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

get_child_by_creation_interfaces()

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

This "getter" method returns a new array ref having references to all of this Interface's child-by-creation Interfaces, or an empty array ref for none.

get_parent_by_context_interface()

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

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

get_child_by_context_interfaces()

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

This "getter" method returns a new array ref having references to all of this Interface's child-by-context Interfaces, or an empty array ref for none.

get_engine()

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

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

INDIRECT APPLICATION OBJECT METHODS

These methods are stateful and may only be invoked off of Rosetta::Interface::* objects; technically these methods are specific to Application objects, but for your convenience all Rosetta::Interface::* objects in a Rosetta Interface tree will act as proxies of the Application at the root of the tree; invoking these methods on any tree object is the same as doing so on the root Application.

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_app_inst_node()

my $app_inst_node = $interface->get_app_inst_node();

This "getter" method returns by reference the 'application_instance' SQL::Routine::Node object property of this tree's root Application.

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.

APPLICATION CONSTRUCTOR FUNCTIONS

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

new( APP_INST_NODE )

my $app_intf = Rosetta::Interface::Application->new( $app_inst_node );

This "getter" function will create and return a single Application (or subclass) object. The APP_INST_NODE argument is a 'application_instance' SQL::Routine Node that this new Application is to represent.

APPLICATION OBJECT METHODS

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

features([ FEATURE_NAME ])

This method is similar to Environment.features(); see the documentation for that method to help understand the rest of this one. 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 method is similar to Environment.prepare(); see the documentation for that method to help understand the rest of this one. Application.prepare() will mainly invoke Rosetta::Dispatcher.prepare(), which in turn 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.

do( ROUTINE_DEFN[, ROUTINE_ARGS] )

This wrapper simply returns prepare( ROUTINE_DEFN )->execute( ROUTINE_ARGS ).

ENVIRONMENT CONSTRUCTOR FUNCTIONS

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

my $env_intf = Rosetta::Interface::Environment->new( $app_intf, $link_prod_node );

This "getter" function will return a single Environment (or subclass) object that it either finds or creates. The PARENT_INTF argument is an Application object that is meant to be both of the parent-by-creation and parent-by-context Interfaces of the new Environment. The LINK_PROD_NODE argument is a 'data_link_product' SQL::Routine Node that this new Environment is to represent. Any particular 'data_link_product' Node can only be associated with a single Environment under the same Application; this method will only create a new Environment if no existing one uses the Node; otherwise it returns the existing one. If this method is creating a new Environment Interface, it will also discover and load the Rosetta Engine class specified in the link product's "product_code", and create the root object of that class, which is the new Environment object's implementor.

ENVIRONMENT OBJECT METHODS

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

my $link_prod_node = $env_intf->get_link_prod_node();

This "getter" method returns by reference the 'data_link_product' SQL::Routine::Node object property of this Environment.

features([ FEATURE_NAME ])

This "getter" method will, when called with no arguments, 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). See also the documentation for Connection.features() and Application.features().

prepare( ROUTINE_DEFN )

This "getter"/"setter" method takes a "routine" SQL::Routine Node in its ROUTINE_DEFN argument, then "compiles" it into a new "Preparation" Interface (returned) which is ready to execute the specified action. This method will mainly just invoke the same-name method on its Engine object, which actually does its work, after doing some basic input checking. Any calls to Engine objects are wrapped in an eval block so that miscellaneous exceptions generated there don't kill the program. Note that you may only prepare routines on an Environment if that routine expects to be invoked in a void context; if the routine expects a Connection/CONN or Cursor/CURSOR context, this method throws an exception. The prepare() method will throw an Error exception if the Engine invocation fails, rather than return its normal output. It is anticipated that, behind the scenes, the form of a "compiled" routine that a typical Engine's prepare() will make is a Perl anonymous subroutine reference (or closure), which its execute() simply invokes; however, the Engine can implement how it likes.

do( ROUTINE_DEFN[, ROUTINE_ARGS] )

This wrapper simply returns prepare( ROUTINE_DEFN )->execute( ROUTINE_ARGS ).

CONNECTION CONSTRUCTOR FUNCTIONS

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

my $conn_intf = Rosetta::Interface::Connection->new( $prep_intf, $env_intf, $cat_link_node );

This "getter" function will create and return a single Connection (or subclass) object. The PARENT_BYCRE_INTF and PARENT_BYCXT_INTF arguments are typically Preparation and Environment objects, respectively, that are meant to be the parent-by-creation and parent-by-context Interfaces, respectively, of the new Connection. The LINK_PROD_NODE argument is a 'catalog_link' SQL::Routine Node that this new Connection is to represent. This method is not typically invoked by a user application, but rather by the Engine of the parent-by-creation.

CONNECTION OBJECT METHODS

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

my $cat_link_node = $conn_intf->get_cat_link_node();

This "getter" method returns by reference the 'catalog_link' SQL::Routine::Node object property of this Connection.

features([ FEATURE_NAME ])

This method is similar to Environment.features(); see the documentation for that method to help understand the rest of this one. When a particular Environment says 'yes' or 'no' for particular features, then child-by-context 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.

prepare( ROUTINE_DEFN )

This method is similar to Environment.prepare(); see the documentation for that method to help understand the rest of this one. Note that a routine which expects to be invoked in a Connection context (because it has a 'routine_context' child Node whose cont_type is 'CONN') can only be prepared using Connection.prepare() and not some other prepare().

do( ROUTINE_DEFN[, ROUTINE_ARGS] )

This wrapper simply returns prepare( ROUTINE_DEFN )->execute( ROUTINE_ARGS ).

CURSOR CONSTRUCTOR FUNCTIONS

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

new( PARENT_BYCRE_INTF, PARENT_BYCXT_INTF, EXTERN_CURS_NODE )

my $curs_intf = Rosetta::Interface::Cursor->new( $prep_intf, $conn_intf, $ext_curs_node );

This "getter" function will create and return a single Cursor (or subclass) object. The PARENT_BYCRE_INTF and PARENT_BYCXT_INTF arguments are typically Preparation and Connection objects, respectively, that are meant to be the parent-by-creation and parent-by-context Interfaces, respectively, of the new Cursor. The EXTERN_CURS_NODE argument is a 'external_cursor' SQL::Routine Node that this new Cursor is to represent. This method is not typically invoked by a user application, but rather by the Engine of the parent-by-creation.

CURSOR OBJECT METHODS

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

get_extern_curs_node()

my $extern_curs_node = $curs_intf->get_extern_curs_node();

This "getter" method returns by reference the 'external_cursor' SQL::Routine::Node object property of this Cursor.

prepare( ROUTINE_DEFN )

This method is similar to Environment.prepare(); see the documentation for that method to help understand the rest of this one. Note that a routine which expects to be invoked in a Cursor context (because it has a 'routine_context' child Node whose cont_type is 'CURSOR') can only be prepared using Cursor.prepare() and not some other prepare().

do( ROUTINE_DEFN[, ROUTINE_ARGS] )

This wrapper simply returns prepare( ROUTINE_DEFN )->execute( ROUTINE_ARGS ).

LITERAL CONSTRUCTOR FUNCTIONS

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

new( PARENT_BYCRE_INTF )

my $lit_intf = Rosetta::Interface::Literal->new( $prep_intf );

This "getter" function will create and return a single Literal (or subclass) object. The PARENT_BYCRE_INTF argument is typically a Preparation object that is meant to be the parent-by-creation Interface of the new Literal. This method is not typically invoked by a user application, but rather by the Engine of the parent-by-creation.

LITERAL OBJECT METHODS

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

payload()

This "getter" method 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. This method calls back to the Engine to produce the literal value rather than storing that in itself; this way, the value could be fetched or produced right on demand rather than earlier. Therefore, it is also possible for this method to throw an Error exception.

SUCCESS CONSTRUCTOR FUNCTIONS

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

new( PARENT_BYCRE_INTF )

my $succ_intf = Rosetta::Interface::Success->new( $prep_intf );

This "getter" function will create and return a single Success (or subclass) object. The PARENT_BYCRE_INTF argument is typically a Preparation object that is meant to be the parent-by-creation Interface of the new Success. This method is not typically invoked by a user application, but rather by the Engine of the parent-by-creation.

SUCCESS OBJECT METHODS

Rosetta::Interface::Success objects have no methods.

PREPARATION CONSTRUCTOR FUNCTIONS

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

new( PARENT_BYCRE_INTF, ROUTINE_NODE )

my $prep_intf = Rosetta::Interface::Preparation->new( $conn_intf, $routine_node );

This "getter" function will create and return a single Preparation (or subclass) object. The PARENT_BYCRE_INTF argument is an object whose type is one of [Application, Environment, Connection, Cursor], that is meant to be the parent-by-creation Interface of the new Preparation. The ROUTINE_NODE argument is a 'routine' SQL::Routine Node that this new Preparation is to represent. This method is not typically invoked by a user application, but rather by the Engine of the parent-by-creation.

PREPARATION OBJECT METHODS

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

get_routine_node()

my $routine_node = $prep_intf->get_routine_node();

This "getter" method returns by reference the 'routine' SQL::Routine::Node object property of this Preparation.

execute([ ROUTINE_ARGS ])

This "getter"/"setter" method will actually perform the action that the Preparation is created for. The optional hash ref argument ROUTINE_ARGS provides run-time arguments for the previously "compiled" routine, if it takes any. This method will mainly just invoke the same-name method on its Engine object, which actually does its work, after doing some basic input checking. Any calls to Engine objects are wrapped in an eval block so that miscellaneous exceptions generated there don't kill the program. The "compiled" routine returns new non-prep Interfaces. The execute() method will throw an Error exception if the prepared routine fails, rather than return its normal output. The return object type of execute() corresponds to the 'routine' Node's declared return type, if it is a function; execute() returns a Success object if the routine is a procedure.

ERROR CONSTRUCTOR FUNCTIONS

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

new( PARENT_BYCRE_INTF, ERROR_MSG )

my $err_intf = Rosetta::Interface::Error->new( $prep_intf, $message );

This "getter" function will create and return a single Error (or subclass) object. The PARENT_BYCRE_INTF argument may be any Interface type except Success or Error, that is meant to be the parent-by-creation Interface of the new Error. The ERROR_MSG argument is a Locale::KeyedText::Message object that this Error object is to represent. This method is not typically invoked by a user application, but rather by the Engine of the parent-by-creation.

ERROR OBJECT METHODS

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

get_error_message()

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

This "getter" method returns by reference the Error Message Locale::KeyedText::Message object property of this Error Interface.

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: features(), prepare(), payload(), execute(). Every Engine must also implement the stateless new_[environment|connection|cursor|literal|preparation]_engine() functions, each taking zero arguments, that must instantiate a 'default' Engine object; these are always called indirectly by the corresponding new_*_interface() functions, which are in turn called typically by the parent-to-be Engine, or an Application Interface. An Engine can only be configured to a non-default state by a different Engine method after new_*_interface()->get_engine() has returned it; the state of one Engine object could potentially be changed by any Engine object attached to its Rosetta Interface tree, such as any Engine.prepare() method.

DISPATCHER OBJECT FUNCTIONS AND METHODS

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

DEPENDENCIES

This module requires any version of Perl 5.x.y that is at least 5.8.1.

It also requires the Perl modules version and only, which would conceptually be built-in to Perl, but aren't, so they are on CPAN instead.

It also requires the Perl module Scalar::Util, which would conceptually be built-in to Perl, but is bundled with it instead.

It also requires these modules that are on CPAN:

Locale::KeyedText 1.6.0 (for error messages)
SQL::Routine 0.70.0

INCOMPATIBILITIES

None reported.

SEE ALSO

perl(1), Rosetta::L::en, Rosetta::Features, Rosetta::Framework, Locale::KeyedText, SQL::Routine, Rosetta::Validator, Rosetta::Engine::Generic, Rosetta::Emulator::DBI, 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.

BUGS AND LIMITATIONS

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.

AUTHOR

Darren R. Duncan (perl@DarrenDuncan.net)

LICENCE AND COPYRIGHT

This file is part of the Rosetta database portability library.

Rosetta is Copyright (c) 2002-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) as published by the Free Software Foundation (http://www.fsf.org/); either version 2 of the License, or (at your option) any later version. You should have received a copy of the GPL as part of the Rosetta distribution, in the file named "GPL"; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, 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.