Rose::DB::Object::Metadata(3pm) | User Contributed Perl Documentation | Rose::DB::Object::Metadata(3pm) |
Rose::DB::Object::Metadata - Database object metadata.
use Rose::DB::Object::Metadata; $meta = Rose::DB::Object::Metadata->new(class => 'Product'); # ...or... $meta = Rose::DB::Object::Metadata->for_class('Product'); # # Auto-initialization # $meta->table('products'); # optional if class name ends with "::Product" $meta->auto_initialize; # # ...or manual setup # $meta->setup ( table => 'products', columns => [ id => { type => 'int', primary_key => 1 }, name => { type => 'varchar', length => 255 }, description => { type => 'text' }, category_id => { type => 'int' }, status => { type => 'varchar', check_in => [ 'active', 'inactive' ], default => 'inactive', }, start_date => { type => 'datetime' }, end_date => { type => 'datetime' }, date_created => { type => 'timestamp', default => 'now' }, last_modified => { type => 'timestamp', default => 'now' }, ], unique_key => 'name', foreign_keys => [ category => { class => 'Category', key_columns => { category_id => 'id', } }, ], relationships => [ prices => { type => 'one to many', class => 'Price', column_map => { id => 'id_product' }, }, ], ); # # ...or even more verbose manual setup (old-style, not recommended) # $meta->table('products'); $meta->columns ( id => { type => 'int', primary_key => 1 }, name => { type => 'varchar', length => 255 }, description => { type => 'text' }, category_id => { type => 'int' }, status => { type => 'varchar', check_in => [ 'active', 'inactive' ], default => 'inactive', }, start_date => { type => 'datetime' }, end_date => { type => 'datetime' }, date_created => { type => 'timestamp', default => 'now' }, last_modified => { type => 'timestamp', default => 'now' }, ); $meta->unique_key('name'); $meta->foreign_keys ( category => { class => 'Category', key_columns => { category_id => 'id', } }, ); $meta->relationships ( prices => { type => 'one to many', class => 'Price', column_map => { id => 'id_product' }, }, ); ...
Rose::DB::Object::Metadata objects store information about a single table in a database: the name of the table, the names and types of columns, any foreign or unique keys, etc. These metadata objects are also responsible for supplying information to, and creating object methods for, the Rose::DB::Object-derived objects to which they belong.
Rose::DB::Object::Metadata objects also store information about the Rose::DB::Objects that front the database tables they describe. What might normally be thought of as "class data" for the Rose::DB::Object is stored in the metadata object instead, in order to keep the method namespace of the Rose::DB::Object-derived class uncluttered.
Rose::DB::Object::Metadata objects are per-class singletons; there is one Rose::DB::Object::Metadata object for each Rose::DB::Object-derived class. Metadata objects are almost never explicitly instantiated. Rather, there are automatically created and accessed through Rose::DB::Object-derived objects' meta method.
Once created, metadata objects can be populated manually or automatically. Both techniques are shown in the synopsis above. The automatic mode works by asking the database itself for the information. There are some caveats to this approach. See the auto-initialization section for more information.
Rose::DB::Object::Metadata objects contain three categories of objects that are responsible for creating object methods in Rose::DB::Object-derived classes: columns, foreign keys, and relationships.
Column objects are subclasses of Rose::DB::Object::Metadata::Column. They are intended to store as much information as possible about each column. The particular class of the column object created for a database column is determined by a mapping table. The column class, in turn, is responsible for creating the accessor/mutator method(s) for the column. When it creates these methods, the column class can use (or ignore) any information stored in the column object.
Foreign key objects are of the class Rose::DB::Object::Metadata::ForeignKey. They store information about columns that refer to columns in other tables that are fronted by their own Rose::DB::Object-derived classes. A foreign key object is responsible for creating accessor method(s) to fetch the foreign object from the foreign table.
Relationship objects are subclasses of Rose::DB::Object::Metadata::Relationship. They store information about a table's relationship to other tables that are fronted by their own Rose::DB::Object-derived classes. The particular class of the relationship object created for each relationship is determined by a mapping table. A relationship object is responsible for creating accessor method(s) to fetch the foreign objects from the foreign table.
Manual population of metadata objects can be tedious and repetitive. Nearly all of the information stored in a Rose::DB::Object::Metadata object exists in the database in some form. It's reasonable to consider simply extracting this information from the database itself, rather than entering it all manually. This automatic metadata extraction and subsequent Rose::DB::Object::Metadata object population is called "auto-initialization."
The example of auto-initialization in the synopsis above is the most succinct variant:
$meta->auto_initialize;
As you can read in the documentation for the auto_initialize method, that's shorthand for individually auto-initializing each part of the metadata object: columns, the primary key, unique keys, and foreign keys. But this brevity comes at a price. There are many caveats to auto-initialization.
Start-Up Cost
In order to retrieve the information required for auto-initialization, a database connection must be opened and queries must be run. Sometimes these queries include complex joins. All of these queries must be successfully completed before the Rose::DB::Object-derived objects that the Rose::DB::Object::Metadata is associated with can be used.
In an environment like mod_perl, server start-up time is precisely when you want to do any expensive operations. But in a command-line script or other short-lived process, the overhead of auto-initializing many metadata objects may become prohibitive.
Also, don't forget that auto-initialization requires a database connection. Rose::DB::Object-derived objects can sometimes be useful even without a database connection (e.g., to temporarily store information that will never go into the database, or to synthesize data using object methods that have no corresponding database column). When using auto-initialization, this is not possible because the Rose::DB::Object-derived class won't even load if auto-initialization fails because it could not connect to the database.
Detail
First, auto-initialization cannot generate information that exists only in the mind of the programmer. The most common example is a relationship between two database tables that is either ambiguous or totally unexpressed by the database itself.
For example, if a foreign key constraint does not exist, the relationship between rows in two different tables cannot be extracted from the database, and therefore cannot be auto-initialized.
Even within the realm of information that, by all rights, should be available in the database, there are limitations. Although there is a handy DBI API for extracting metadata from databases, unfortunately, very few DBI drivers support it fully. Some don't support it at all. In almost all cases, some manual work is required to (often painfully) extract information from the database's "system tables" or "catalog."
More troublingly, databases do not always provide all the metadata that a human could extract from the series of SQL statement that created the table in the first place. Sometimes, the information just isn't in the database to be extracted, having been lost in the process of table creation. Here's just one example. Consider this MySQL table definition:
CREATE TABLE mytable ( id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, code CHAR(6), flag BOOLEAN NOT NULL DEFAULT 1, bits BIT(5) NOT NULL DEFAULT '00101', name VARCHAR(64) );
Now look at the metadata that MySQL 4 stores internally for this table:
mysql> describe mytable; +-------+------------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------+------------------+------+-----+---------+----------------+ | id | int(10) unsigned | | PRI | NULL | auto_increment | | code | varchar(6) | YES | | NULL | | | flag | tinyint(1) | | | 1 | | | bits | tinyint(1) | | | 101 | | | name | varchar(64) | YES | | NULL | | +-------+------------------+------+-----+---------+----------------+
Note the following divergences from the "CREATE TABLE" statement.
Remember that the auto-initialization process can only consider the metadata actually stored in the database. It has no access to the original "create table" statement. Thus, the semantics implied by the original table definition are effectively lost.
Again, this is just one example of the kind of detail that can be lost in the process of converting your table definition into metadata that is stored in the database. Admittedly, MySQL is perhaps the worst case-scenario, having a well-deserved reputation for disregarding the wishes of table definitions. (The use of implicit default values for "NOT NULL" columns is yet another example.)
Thankfully, there is a solution to this dilemma. Remember that auto-initialization is actually a multi-step process hiding behind that single call to the auto_initialize method. To correct the sins of the database, simply break the auto-initialization process into its components. For example, here's how to correctly auto-initialize the "mytable" example above:
# Make a first pass at column setup $meta->auto_init_columns; # Account for inaccuracies in DBD::mysql's column info by # replacing incorrect column definitions with new ones. # Fix CHAR(6) column that shows up as VARCHAR(6) $meta->column(code => { type => 'char', length => 6 }); # Fix BIT(5) column that shows up as TINYINT(1) $meta->column(bits => { type => 'bits', bits => 5, default => 101 }); # Fix BOOLEAN column that shows up as TINYINT(1) $meta->column(flag => { type => 'boolean', default => 1 }); # Do everything else $meta->auto_initialize;
Note that auto_initialize was called at the end. Without the "replace_existing" parameter, this call will preserve any existing metadata, rather than overwriting it, so our "corrections" are safe.
Maintenance
The price of auto-initialization is eternal vigilance. "What does that mean? Isn't auto-initialization supposed to save time and effort?" Well, yes, but at a cost. In addition to the caveats described above, consider what happens when a table definition changes.
"Ah ha!" you say, "My existing class will automatically pick up the changes the next time it's loaded! Auto-initialization at its finest!" But is it? What if you added a "NOT NULL" column with no default value? Yes, your existing auto-initialized class will pick up the change, but your existing code will no longer be able to save one these objects. Or what if you're using MySQL and your newly added column is one of the types described above that requires manual tweaking in order to get the desired semantics. Will you always remember to make this change?
Auto-initialization is not a panacea. Every time you make a change to your database schema, you must also revisit each affected Rose::DB::Object-derived class to at least consider whether or not the metadata needs to be corrected or updated.
The trade-off may be well worth it, but it's still something to think about. There is, however, a hybrid solution that might be even better. Continue on to the next section to learn more.
As described in the section above, auto-initializing metadata at runtime by querying the database has many caveats. An alternate approach is to query the database for metadata just once, and then generate the equivalent Perl code which can be pasted directly into the class definition in place of the call to auto_initialize.
Like the auto-initialization process itself, perl code generation has a convenient wrapper method as well as separate methods for the individual parts. All of the perl code generation methods begin with "perl_", and they support some rudimentary code formatting options to help the code conform to you preferred style. Examples can be found with the documentation for each perl_* method.
This hybrid approach to metadata population strikes a good balance between upfront effort and ongoing maintenance. Auto-generating the Perl code for the initial class definition saves a lot of tedious typing. From that point on, manually correcting and maintaining the definition is a small price to pay for the decreased start-up cost, the ability to use the class in the absence of a database connection, and the piece of mind that comes from knowing that your class is stable, and won't change behind your back in response to an "action at a distance" (i.e., a database schema update).
This hash is class data. If you want to modify it, I suggest making your own subclass of Rose::DB::Object::Metadata and then setting that as the meta_class of your Rose::DB::Object subclass.
If passed MAP (a list of type/class pairs or a reference to a hash of the same) then MAP replaces the current column type mapping. Returns a list of type/class pairs (in list context) or a reference to the hash of type/class mappings (in scalar context).
The default mapping of type names to class names is:
scalar => Rose::DB::Object::Metadata::Column::Scalar char => Rose::DB::Object::Metadata::Column::Character character => Rose::DB::Object::Metadata::Column::Character varchar => Rose::DB::Object::Metadata::Column::Varchar varchar2 => Rose::DB::Object::Metadata::Column::Varchar nvarchar => Rose::DB::Object::Metadata::Column::Varchar nvarchar2 => Rose::DB::Object::Metadata::Column::Varchar string => Rose::DB::Object::Metadata::Column::Varchar text => Rose::DB::Object::Metadata::Column::Text blob => Rose::DB::Object::Metadata::Column::Blob bytea => Rose::DB::Object::Metadata::Column::Pg::Bytea bits => Rose::DB::Object::Metadata::Column::Bitfield bitfield => Rose::DB::Object::Metadata::Column::Bitfield bool => Rose::DB::Object::Metadata::Column::Boolean boolean => Rose::DB::Object::Metadata::Column::Boolean int => Rose::DB::Object::Metadata::Column::Integer integer => Rose::DB::Object::Metadata::Column::Integer tinyint => Rose::DB::Object::Metadata::Column::Integer smallint => Rose::DB::Object::Metadata::Column::Integer mediumint => Rose::DB::Object::Metadata::Column::Integer bigint => Rose::DB::Object::Metadata::Column::BigInt serial => Rose::DB::Object::Metadata::Column::Serial bigserial => Rose::DB::Object::Metadata::Column::BigSerial enum => Rose::DB::Object::Metadata::Column::Enum num => Rose::DB::Object::Metadata::Column::Numeric numeric => Rose::DB::Object::Metadata::Column::Numeric decimal => Rose::DB::Object::Metadata::Column::Numeric float => Rose::DB::Object::Metadata::Column::Float float8 => Rose::DB::Object::Metadata::Column::DoublePrecision 'double precision' => Rose::DB::Object::Metadata::Column::DoublePrecision time => Rose::DB::Object::Metadata::Column::Time interval => Rose::DB::Object::Metadata::Column::Interval date => Rose::DB::Object::Metadata::Column::Date datetime => Rose::DB::Object::Metadata::Column::Datetime timestamp => Rose::DB::Object::Metadata::Column::Timestamp timestamptz => Rose::DB::Object::Metadata::Column::TimestampWithTimeZone 'timestamp with time zone' => Rose::DB::Object::Metadata::Column::TimestampWithTimeZone 'datetime year to fraction' => Rose::DB::Object::Metadata::Column::DatetimeYearToFraction 'datetime year to fraction(1)' => Rose::DB::Object::Metadata::Column::DatetimeYearToFraction1 'datetime year to fraction(2)' => Rose::DB::Object::Metadata::Column::DatetimeYearToFraction2 'datetime year to fraction(3)' => Rose::DB::Object::Metadata::Column::DatetimeYearToFraction3 'datetime year to fraction(4)' => Rose::DB::Object::Metadata::Column::DatetimeYearToFraction4 'datetime year to fraction(5)' => Rose::DB::Object::Metadata::Column::DatetimeYearToFraction5 'timestamp with time zone' => Rose::DB::Object::Metadata::Column::Timestamp 'timestamp without time zone' => Rose::DB::Object::Metadata::Column::Timestamp 'datetime year to second' => Rose::DB::Object::Metadata::Column::DatetimeYearToSecond 'datetime year to minute' => Rose::DB::Object::Metadata::Column::DatetimeYearToMinute 'datetime year to month' => Rose::DB::Object::Metadata::Column::DatetimeYearToMonth 'epoch' => Rose::DB::Object::Metadata::Column::Epoch 'epoch hires' => Rose::DB::Object::Metadata::Column::Epoch::HiRes array => Rose::DB::Object::Metadata::Column::Array set => Rose::DB::Object::Metadata::Column::Set chkpass => Rose::DB::Object::Metadata::Column::Pg::Chkpass
If a CLASS is passed, then NAME is mapped to CLASS.
This hash is class data. If you want to modify it, I suggest making your own subclass of Rose::DB::Object::Metadata and then setting that as the meta_class of your Rose::DB::Object subclass.
If passed MAP (a list of name/class pairs or a reference to a hash of the same) then MAP replaces the current mapping. Returns a list of name/class pairs (in list context) or a reference to the hash of name/class mappings (in scalar context).
The default mapping of names to classes is:
default => Rose::DB::Object::ConventionManager null => Rose::DB::Object::ConventionManager::Null
If defined, the subroutine should take four arguments: the metadata object, the column name, the column method type, and the method name that would be used if the mapper subroutine did not exist. It should return a method name.
This hash is class data. If you want to modify it, I suggest making your own subclass of Rose::DB::Object::Metadata and then setting that as the meta_class of your Rose::DB::Object subclass.
If passed MAP (a list of type/class pairs or a reference to a hash of the same) then MAP replaces the current relationship type mapping. Returns a list of type/class pairs (in list context) or a reference to the hash of type/class mappings (in scalar context).
The default mapping of type names to class names is:
'one to one' => Rose::DB::Object::Metadata::Relationship::OneToOne 'one to many' => Rose::DB::Object::Metadata::Relationship::OneToMany 'many to one' => Rose::DB::Object::Metadata::Relationship::ManyToOne 'many to many' => Rose::DB::Object::Metadata::Relationship::ManyToMany
Any object method is a valid parameter name, but PARAMS must include a value for the "class" parameter, since that's how Rose::DB::Object::Metadata objects are mapped to their corresponding Rose::DB::Object-derived class.
If an argument is a subclass of Rose::DB::Object::Metadata::Column, it is added as-is.
If an argument is a plain scalar, it is taken as the name of a scalar column. A column object of the class returned by the method call "$obj->column_type_class('scalar')" is constructed and then added.
Otherwise, only name/value pairs are considered, where the name is taken as the column name and the value must be a reference to a hash.
If the hash contains the key "primary_key" with a true value, then the column is marked as a primary_key_member and the column name is added to the list of primary key columns by calling the add_primary_key_column method with the column name as its argument.
If the hash contains the key "alias", then the value of that key is used as the alias for the column. This is a shorthand equivalent to explicitly calling the alias_column column method.
If the hash contains the key "temp" and its value is true, then the column is actually added to the list of non-persistent columns.
If the hash contains a key with the same name as a column trigger event type (e.g., "on_set", "on_load", "inflate") then the value of that key must be a code reference or a reference to an array of code references, which will be added to the list of the column's triggers for the specified event type.
If the hash contains the key "methods", then its value must be a reference to an array or a reference to a hash. The auto_method_types of the column are then set to the values of the referenced array, or the keys of the referenced hash. The values of the referenced hash are used to set the method_name for their corresponding method types.
If the hash contains the key "add_methods", then its value must be a reference to an array or a reference to a hash. The values of the referenced array or the keys of the referenced hash are added to the column's auto_method_types. The values of the referenced hash are used to set the method_name for their corresponding method types.
If the "methods" and "add_methods" keys are both set, a fatal error will occur.
Then the column_type_class method is called with the value of the "type" hash key as its argument (or "scalar" if that key is missing), returning the name of a column class. Finally, a new column object of that class is constructed and is passed all the remaining pairs in the hash reference, along with the name and type of the column. That column object is then added to the list of columns.
This is done until there are no more arguments to be processed, or until an argument does not conform to one of the required formats, in which case a fatal error occurs.
Example:
$meta->add_columns ( # Add a scalar column 'name', # which is roughly equivalent to: # # $class = $meta->column_type_class('scalar'); # $col = $class->new(name => 'name'); # (then add $col to the list of columns) # Add by name/hashref pair with explicit method types age => { type => 'int', default => 5, methods => [ 'get', 'set' ] }, # which is roughly equivalent to: # # $class = $meta->column_type_class('int'); # $col = $class->new(name => 'age', # type => 'int', # default => 5); # $col->auto_method_types('get', 'set'); # (then add $col to the list of columns) # Add by name/hashref pair with additional method type and name size => { type => 'int', add_methods => { 'set' => 'set_my_size' } }, # which is roughly equivalent to: # # $class = $meta->column_type_class('int'); # $col = $class->new(name => 'size', # type => 'int',); # $col->add_auto_method_types('set'); # $col->method_name(set => 'set_my_size'); # (then add $col to the list of columns) # Add a column object directly Rose::DB::Object::Metadata::Column::Date->new( name => 'start_date'), );
Foreign keys can be specified in ARGS in several ways.
If an argument is a Rose::DB::Object::Metadata::ForeignKey object (or subclass thereof), it is added as-is.
Otherwise, only name/value pairs are considered, where the name is taken as the foreign key name and the value must be a reference to a hash.
If the hash contains the key "methods", then its value must be a reference to an array or a reference to a hash. The auto_method_types of the foreign key are then set to the values of the referenced array, or the keys of the referenced hash. The values of the referenced hash are used to set the method_name for their corresponding method types.
If the hash contains the key "add_methods", then its value must be a reference to an array or a reference to a hash. The values of the referenced array or the keys of the referenced hash are added to the foreign key's auto_method_types. The values of the referenced hash are used to set the method_name for their corresponding method types.
If the "methods" and "add_methods" keys are both set, a fatal error will occur.
A new Rose::DB::Object::Metadata::ForeignKey object is constructed and is passed all the remaining pairs in the hash reference, along with the name of the foreign key as the value of the "name" parameter. That foreign key object is then added to the list of foreign keys.
This is done until there are no more arguments to be processed, or until an argument does not conform to one of the required formats, in which case a fatal error occurs.
Example:
$meta->add_foreign_keys ( # Add by name/hashref pair with explicit method type category => { class => 'Category', key_columns => { category_id => 'id' }, methods => [ 'get' ], }, # which is roughly equivalent to: # # $fk = Rose::DB::Object::Metadata::ForeignKey->new( # class => 'Category', # key_columns => { category_id => 'id' }, # name => 'category'); # $fk->auto_method_types('get'); # (then add $fk to the list of foreign keys) # Add by name/hashref pair with additional method type and name color => { class => 'Color', key_columns => { color_id => 'id' }, add_methods => { set => 'set_my_color' }, }, # which is roughly equivalent to: # # $fk = Rose::DB::Object::Metadata::ForeignKey->new( # class => 'Color', # key_columns => { color_id => 'id' }, # name => 'color'); # $fk->add_auto_method_types('set'); # $fk->method_name(set => 'set_my_color'); # (then add $fk to the list of foreign keys) # Add a foreign key object directly Rose::DB::Object::Metadata::ForeignKey->new(...), );
For each foreign key added, a corresponding relationship with the same name is added if it does not already exist. The relationship type is determined by the value of the foreign key object's relationship attribute. The default is "many to one". The class of the relationship is chosen by calling relationship_type_class with the relationship type as an argument.
Relationships can be specified in ARGS in several ways.
If an argument is a subclass of Rose::DB::Object::Metadata::Relationship, it is added as-is.
Otherwise, only name/value pairs are considered, where the name is taken as the relationship name and the value must be a reference to a hash.
If the hash contains the key "methods", then its value must be a reference to an array or a reference to a hash. The auto_method_types of the relationship are then set to the values of the referenced array, or the keys of the referenced hash. The values of the referenced hash are used to set the method_name for their corresponding method types.
If the hash contains the key "add_methods", then its value must be a reference to an array or a reference to a hash. The values of the referenced array or the keys of the referenced hash are added to the relationship's auto_method_types. The values of the referenced hash are used to set the method_name for their corresponding method types.
If the "methods" and "add_methods" keys are both set, a fatal error will occur.
Then the relationship_type_class method is called with the value of the "type" hash key as its argument, returning the name of a relationship class.
Finally, a new relationship object of that class is constructed and is passed all the remaining pairs in the hash reference, along with the name and type of the relationship. That relationship object is then added to the list of relationships.
This is done until there are no more arguments to be processed, or until an argument does not conform to one of the required formats, in which case a fatal error occurs.
Example:
$meta->add_relationships ( # Add by name/hashref pair with explicit method type category => { type => 'many to one', class => 'Category', column_map => { category_id => 'id' }, methods => [ 'get' ], }, # which is roughly equivalent to: # # $class = $meta->relationship_type_class('many to one'); # $rel = $class->new(class => 'Category', # column_map => { category_id => 'id' }, # name => 'category'); # $rel->auto_method_types('get'); # (then add $rel to the list of relationships) # Add by name/hashref pair with additional method type and name color => { type => 'many to one', class => 'Color', column_map => { color_id => 'id' }, add_methods => { set => 'set_my_color' }, }, # which is roughly equivalent to: # # $class = $meta->relationship_type_class('many to one'); # $rel = $class->new(class => 'Color', # column_map => { color_id => 'id' }, # name => 'color'); # $rel->add_auto_method_types('set'); # $fk->method_name(set => 'set_my_color'); # (rel add $fk to the list of foreign keys) # Add a relationship object directly Rose::DB::Object::Metadata::Relationship::OneToOne->new(...), );
If an argument is a Rose::DB::Object::Metadata::UniqueKey object (or subclass thereof), then its parent is set to the metadata object itself, and it is added.
Otherwise, an argument must be a single column name or a reference to an array of column names that make up a unique key. A new Rose::DB::Object::Metadata::UniqueKey is created, with its parent set to the metadata object itself, and then the unique key object is added to this list of unique keys for this class.
For example, imagine a column named "save". The Rose::DB::Object API already defines a method named save, so obviously that name can't be used for the accessor method for the "save" column. To solve this, make an alias:
$meta->alias_column(save => 'save_flag');
See the Rose::DB::Object documentation or call the method_name_is_reserved method to determine if a method name is reserved.
Enabling this flag reduces the performance of the update and insert operations on the Rose::DB::Object-derived object. But it is sometimes necessary to enable the flag because some DBI drivers do not (or cannot) always do the right thing when binding values to placeholders in SQL statements. For example, consider the following SQL for the Informix database:
CREATE TABLE test (d DATETIME YEAR TO SECOND); INSERT INTO test (d) VALUES (CURRENT);
This is valid Informix SQL and will insert a row with the current date and time into the "test" table.
Now consider the following attempt to do the same thing using DBI placeholders (assume the table was already created as per the CREATE TABLE statement above):
$sth = $dbh->prepare('INSERT INTO test (d) VALUES (?)'); $sth->execute('CURRENT'); # Error!
What you'll end up with is an error like this:
DBD::Informix::st execute failed: SQL: -1262: Non-numeric character in datetime or interval.
In other words, DBD::Informix has tried to quote the string "CURRENT", which has special meaning to Informix only when it is not quoted.
In order to make this work, the value "CURRENT" must be "inlined" rather than bound to a placeholder when it is the value of a "DATETIME YEAR TO SECOND" column in an Informix database.
Note: This method may also be called as a class method, but may require explicit "catalog" and/or "schema" arguments when dealing with databases that support these concepts and have default implicit values for them.
If both NAME and COLUMN are passed, then COLUMN must be a Rose::DB::Object::Metadata::Column-derived object. COLUMN has its name set to NAME, and is then stored as the column metadata object for NAME, replacing any existing column.
If both NAME and HASHREF are passed, then the combination of NAME and HASHREF must form a name/value pair suitable for passing to the add_columns method. The new column specified by NAME and HASHREF replaces any existing column.
Returns a list of column objects in list context, or a reference to an array of column objects in scalar context.
Note that modifying this map has no effect if initialize, make_methods, or make_column_methods has already been called for the current class.
If defined, the subroutine should take four arguments: the metadata object, the column name, the column method type, and the method name that would be used if the mapper subroutine did not exist. It should return a method name.
Defaults to the value returned by the default_column_undef_overrides_default class method.
If undef is passed, then a Rose::DB::Object::ConventionManager::Null object is stored instead.
If a Rose::DB::Object::ConventionManager-derived object is passed, its meta attribute set to this metadata object and then it is used as the convention manager for this class.
If a Rose::DB::Object::ConventionManager-derived class name is passed, a new object of that class is created with its meta attribute set to this metadata object. Then it is used as the convention manager for this class.
If a convention manager name is passed, then the corresponding class is looked up in the convention manager class map, a new object of that class is constructed, its meta attribute set to this metadata object, and it is used as the convention manager for this class. If there is no class mapped to NAME, a fatal error will occur.
See the Rose::DB::Object::ConventionManager documentation for more information on convention managers.
The error mode determines what happens when a Rose::DB::Object method encounters an error. The "return" error mode causes the methods to behave as described in the Rose::DB::Object documentation. All other error modes cause an action to be performed before (possibly) returning as per the documentation (depending on whether or not the "action" is some variation on "throw an exception.")
Valid values of MODE are:
In all cases, the object's error attribute will also contain the error message.
If both NAME and FOREIGNKEY are passed, then FOREIGNKEY must be a Rose::DB::Object::Metadata::ForeignKey-derived object. FOREIGNKEY has its name set to NAME, and is then stored, replacing any existing foreign key with the same name.
If both NAME and HASHREF are passed, then the combination of NAME and HASHREF must form a name/value pair suitable for passing to the add_foreign_keys method. The new foreign key specified by NAME and HASHREF replaces any existing foreign key with the same name.
Returns a list of foreign key objects in list context, or a reference to an array of foreign key objects in scalar context.
If a primary_key_generator is defined, it will be called (passed this metadata object and the DB) and its value returned.
If no primary_key_generator is defined, new primary key values will be generated, if possible, using the native facilities of the current database. Note that this may not be possible for databases that auto-generate such values only after an insertion. In that case, undef will be returned.
Here's an example of a unique index that has a predicate:
CREATE UNIQUE INDEX my_idx ON mytable (mycolumn) WHERE mycolumn > 123;
The predicate in this case is "WHERE mycolumn > 123".
Predicated unique indexes differ semantically from unpredicated unique indexes in that predicates generally cause the index to only apply to part of a table. Rose::DB::Object expects unique indexes to uniquely identify a row within a table. Predicated indexes that fail to do so due to their predicates should therefore not have Rose::DB::Object::Metadata::UniqueKey objects created for them, thus the false default for this attribute.
Override this method in your Rose::DB::Object::Metadata subclass, or re-map the "default" convention manager class, in order to use a different convention manager class. See the tips and tricks section of the Rose::DB::Object::ConventionManager documentation for an example of the subclassing approach.
If any column name in the primary key or any of the unique keys does not exist in the list of columns, then that primary or unique key is deleted. (As per the above, this will trigger a fatal error if any column in the primary key is not in the column list.)
ARGS, if any, are passed to the call to make_methods that actually creates the methods.
If auto_prime_caches is true, then the prime_caches method will be called at the end of the initialization process.
ARGS are name/value pairs which are passed on to the other "make_*_methods" calls. They are all optional. Valid ARGS are:
If set to a true value, a method will not be created if there is already an existing method with the same named.
If set to a true value, override any existing method with the same name.
In the absence of one of these parameters, any method name that conflicts with an existing method name will cause a fatal error.
If set to a true value, a method will not be created if there is already an existing method with the same named.
If set to a true value, override any existing method with the same name.
For each auto_method_type in each column, the method name is determined by passing the column name and the method type to method_name_from_column_name. If the resulting method name is reserved (according to method_name_is_reserved, a fatal error will occur. The object methods for each column are created by calling the column object's make_methods method.
If set to a true value, a method will not be created if there is already an existing method with the same named.
If set to a true value, override any existing method with the same name.
For each auto_method_type in each foreign key, the method name is determined by passing the method type to the method_name method of the foreign key object, or the build_method_name_for_type method if the method_name call returns a false value. If the method name is reserved (according to method_name_is_reserved), a fatal error will occur. The object methods for each foreign key are created by calling the foreign key object's make_methods method.
Foreign keys and relationships with the type "one to one" or "many to one" both encapsulate essentially the same information. They are kept in sync when this method is called by setting the foreign_key attribute of each "one to one" or "many to one" relationship object to be the corresponding foreign key object.
If set to a true value, a method will not be created if there is already an existing method with the same named.
If set to a true value, override any existing method with the same name.
For each auto_method_type in each relationship, the method name is determined by passing the method type to the method_name method of the relationship object, or the build_method_name_for_type method if the method_name call returns a false value. If the method name is reserved (according to method_name_is_reserved), a fatal error will occur. The object methods for each relationship are created by calling the relationship object's make_methods method.
Foreign keys and relationships with the type "one to one" or "many to one" both encapsulate essentially the same information. They are kept in sync when this method is called by setting the foreign_key attribute of each "one to one" or "many to one" relationship object to be the corresponding foreign key object.
If a relationship corresponds exactly to a foreign key, and that foreign key already made an object method, then the relationship is not asked to make its own method.
Returns a list of non-persistent column objects in list context, or a reference to an array of non-persistent column objects in scalar context.
Non-persistent columns allow the creation of object attributes and associated accessor/mutator methods exactly like those associated with columns, but without ever sending any of these attributes to (or pulling any these attributes from) the database.
Non-persistent columns are tracked entirely separately from columns. Adding, deleting, and listing non-persistent columns has no affect on the list of normal (i.e., "persistent") columns.
You cannot query the database (e.g., using Rose::DB::Object::Manager) and filter on a non-persistent column; non-persistent columns do not exist in the database. This feature exists solely to leverage the method creation abilities of the various column classes.
Returns all of the columns that make up the primary key. Each column is a Rose::DB::Object::Metadata::Column-derived column object if a column object with the same name exists, or just the column name otherwise. In scalar context, a reference to an array of columns is returned. In list context, a list is returned.
This method is just a shortcut for the code:
$meta->primary_key->columns(...);
See the primary_key method and the Rose::DB::Object::Metadata::PrimaryKey class for more information.
Returns the list of column names (in list context) or a reference to the array of column names (in scalar context).
This method is just a shortcut for the code:
$meta->primary_key->column_names(...);
See the primary_key method and the Rose::DB::Object::Metadata::PrimaryKey class for more information.
The subroutine is expected to return a list of values, one for each primary key column. The values must be in the same order as the corresponding columns returned by primary_key_columns. (i.e., the first value belongs to the first column returned by primary_key_columns, the second value belongs to the second column, and so on.)
If you do not set this value, it will be derived for you based on the name of the primary key columns. In the common case, you do not need to be concerned about this method. If you are using the built-in SERIAL or AUTO_INCREMENT types in your database for your primary key columns, everything should just work.
PARAMS are name/value pairs. Valid parameters are:
If both NAME and RELATIONSHIP are passed, then RELATIONSHIP must be a Rose::DB::Object::Metadata::Relationship-derived object. RELATIONSHIP has its name set to NAME, and is then stored as the relationship metadata object for NAME, replacing any existing relationship.
If both NAME and HASHREF are passed, then the combination of NAME and HASHREF must form a name/value pair suitable for passing to the add_relationships method. The new relationship specified by NAME and HASHREF replaces any existing relationship.
Returns a list of relationship objects in list context, or a reference to an array of relationship objects in scalar context.
$meta->replace_column($name => $value);
is equivalent to this:
$meta->delete_column($name); $meta->add_column($name => $value);
The value of the new column may be a Rose::DB::Object::Metadata::Column-derived object or a reference to a hash suitable for passing to the add_columns method.
The setup() method does nothing if the metadata object is already initialized (according to the is_initialized method).
PARAMS are method/arguments pairs. In general, the following transformations apply.
Given a method/arrayref pair:
METHOD => [ ARG1, ARG2 ]
The arguments will be removed from their array reference and passed to METHOD like this:
$meta->METHOD(ARG1, ARG2);
Given a method/value pair:
METHOD => ARG
The argument will be passed to METHOD as-is:
$meta->METHOD(ARG);
There are two exceptions to these transformation rules.
If METHOD is "unique_key" or "add_unique_key" and the argument is a reference to an array containing only non-reference values, then the array reference itself is passed to the method. For example, this pair:
unique_key => [ 'name', 'status' ]
will result in this method call:
$meta->unique_key([ 'name', 'status' ]);
(Note that these method names are singular. This exception does not apply to the plural variants, "unique_keys" and "add_unique_keys".)
If METHOD is "helpers", then the argument is dereferenced (if it's an array reference) and passed on to Rose::DB::Object::Helpers. That is, this:
helpers => [ 'load_or_save', { load_or_insert => 'find_or_create' } ],
Is equivalent to having this in your class:
use Rose::DB::Object::Helpers 'load_or_save', { load_or_insert => 'find_or_create' };
Method names may appear more than once in PARAMS. The methods are called in the order that they appear in PARAMS, with the exception of the initialize (or auto_initialize) method, which is always called last.
If "initialize" is not one of the method names, then it will be called automatically (with no arguments) at the end. If you do not want to pass any arguments to the initialize method, standard practice is to omit it.
If "auto_initialize" is one of the method names, then the auto_initialize method will be called instead of the initialize method. This is useful if you want to manually set up a few pieces of metadata, but want the auto-initialization system to set up the rest.
The name "auto" is considered equivalent to "auto_initialize", but any arguments are ignored unless they are encapsulated in a reference to an array. For example, these are equivalent:
$meta->setup( table => 'mytable', # Call auto_initialize() with no arguments auto_initialize => [], ); # This is another way of writing the same thing as the above $meta->setup( table => 'mytable', # The value "1" is ignored because it's not an arrayref, # so auto_initialize() will be called with no arguments. auto => 1, );
Finally, here's a full example of a setup() method call followed by the equivalent "long-hand" implementation.
$meta->setup ( table => 'colors', columns => [ code => { type => 'character', length => 3, not_null => 1 }, name => { type => 'varchar', length => 255 }, ], primary_key_columns => [ 'code' ], unique_key => [ 'name' ], );
The setup() method call above is equivalent to the following code:
unless($meta->is_initialized) { $meta->table('colors'); $meta->columns( [ code => { type => 'character', length => 3, not_null => 1 }, name => { type => 'varchar', length => 255 }, ]); $meta->primary_key_columns('code'); $meta->unique_key([ 'name' ]), $meta->initialize; }
For example, here is some SQL that might be used to load an object, as generated with sql_qualify_column_names_on_load set to false:
SELECT id, name FROM dogs WHERE id = 5;
Now here's how it would look with sql_qualify_column_names_on_load set to true:
SELECT dogs.id, dogs.name FROM dogs WHERE dogs.id = 5;
Returns the list (in list context) or reference to an array (in scalar context) of Rose::DB::Object::Metadata::UniqueKey objects.
# Example of a scalar context return value [ [ 'id', 'name' ], [ 'code' ] ] # Example of a list context return value ([ 'id', 'name' ], [ 'code' ])
These methods are associated with the auto-initialization process. Calling any of them will cause the auto-initialization code to be loaded, which costs memory. This should be considered an implementation detail for now.
Regardless of the implementation details, you should still avoid calling any of these methods unless you plan to do some auto-initialization. No matter how generic they may seem (e.g., default_perl_indent), rest assured that none of these methods are remotely useful unless you are doing auto-initialization.
Returns a list of column objects (in list context) or a reference to a hash of column objects, keyed by column name (in scalar context). The hash reference return value is intended to allow easy modification of the auto-generated column objects. Example:
$columns = $meta->auto_generate_columns; # hash ref return value # Make some changes $columns->{'name'}->length(10); # set different length $columns->{'age'}->default(5); # set different default ... # Finally, set the column list $meta->columns(values %$columns);
If you do not want to modify the auto-generated columns, you should use the auto_init_columns method instead.
A fatal error will occur unless at least one column was auto-generated.
PARAMS are optional name/value pairs. If a "no_warnings" parameter is passed with a true value, then the warning described above will not be issued.
Returns a list of foreign key objects (in list context) or a reference to an array of foreign key objects (in scalar context).
If you do not want to inspect or modify the auto-generated foreign keys, but just want them to populate the metadata object's foreign_keys list, you should use the auto_init_foreign_keys method instead.
Note: This method works with MySQL only when using the InnoDB storage type.
Returns a list of unique key objects (in list context) or a reference to an array of unique key objects (in scalar context).
If you do not want to inspect or modify the auto-generated unique keys, but just want them to populate the metadata object's unique_keys list, you should use the auto_init_unique_keys method instead.
This method is rarely called explicitly. Usually, you will use the auto_init_primary_key_columns method instead.
A fatal error will occur unless at least one column name can be retrieved.
(This method uses the word "retrieve" instead of "generate" like its sibling methods above because it does not generate objects; it simply returns column names.)
$meta->auto_init_columns(...); $meta->auto_init_primary_key_columns; $meta->auto_init_unique_keys(...); $meta->auto_init_foreign_keys(...); $meta->auto_init_relationships(...); $meta->initialize;
PARAMS are optional name/value pairs. When applicable, these parameters are passed on to each of the "auto_init_*" methods. Valid parameters are:
Note: If some classes that are not actually map classes are being skipped, you should not use this parameter to force them to be included. It's more appropriate to make your own custom convention manager subclass and then override the is_map_class method to make the correct determination.
During initialization, if one of the columns has a method name that clashes with a reserved method name, then the column_alias_generator will be called to remedy the situation by aliasing the column. If the name still conflicts, then a fatal error will occur.
A fatal error will occur if auto-initialization fails.
Note: This method works with MySQL only when using the InnoDB storage type.
Note: If some classes that are not actually map classes are being skipped, you should not use this parameter to force them to be included. It's more appropriate to make your own custom convention manager subclass and then override the is_map_class method to make the correct determination.
Assume that this class is called "Local" and any hypothetical foreign class is called "Remote". Relationships are auto-generated according to the following rules.
In all cases, if there is an existing, semantically identical relationship, then a new relationship is not auto-generated. Similarly, any existing methods with the same names are not overridden by methods associated with auto-generated relationships.
The subroutine should take two arguments: the metadata object and the column name. The $_ variable will also be set to the column name at the time of the call. The subroutine should return an alias for the column.
The default column alias generator simply appends the string "_col" to the end of the column name and returns that as the alias.
The subroutine should take two arguments: a metadata object and a Rose::DB::Object::Metadata::ForeignKey object. It should return a name for the foreign key.
Each foreign key must have a name that is unique within the class. By default, this name will also be the name of the method generated to access the object referred to by the foreign key, so it must be unique among method names in the class as well.
The default foreign key name generator uses the following algorithm:
If the foreign key has only one column, and if the name of that column ends with an underscore and the name of the referenced column, then that part of the column name is removed and the remaining string is used as the foreign key name. For example, given the following tables:
CREATE TABLE categories ( id SERIAL PRIMARY KEY, ... ); CREATE TABLE products ( category_id INT REFERENCES categories (id), ... );
The foreign key name would be "category", which is the name of the referring column ("category_id") with an underscore and the name of the referenced column ("_id") removed from the end of it.
If the foreign key has only one column, but it does not meet the criteria described above, then "_object" is appended to the name of the referring column and the resulting string is used as the foreign key name.
If the foreign key has more than one column, then the foreign key name is generated by replacing double colons and case-transitions in the referenced class name with underscores, and then converting to lowercase. For example, if the referenced table is fronted by the class My::TableOfStuff, then the generated foreign key name would be "my_table_of_stuff".
In all of the scenarios above, if the generated foreign key name is still not unique within the class, then a number is appended to the end of the name. That number is incremented until the name is unique.
In practice, rather than setting a custom foreign key name generator, it's usually easier to simply set the foreign key name(s) manually after auto-initializing the foreign keys (but before calling initialize or auto_initialize, of course).
This method is simply a wrapper (with some glue) for the following methods: perl_columns_definition, perl_primary_key_columns_definition, perl_unique_keys_definition, perl_foreign_keys_definition, and perl_relationships_definition. The "braces" and "indent" parameters are passed on to these other methods.
Here's a complete example, which also serves as an example of the individual "perl_*" methods that this method wraps. First, the table definitions.
CREATE TABLE topics ( id SERIAL PRIMARY KEY, name VARCHAR(32) ); CREATE TABLE codes ( k1 INT NOT NULL, k2 INT NOT NULL, k3 INT NOT NULL, name VARCHAR(32), PRIMARY KEY(k1, k2, k3) ); CREATE TABLE products ( id SERIAL PRIMARY KEY, name VARCHAR(32) NOT NULL, flag BOOLEAN NOT NULL DEFAULT 't', status VARCHAR(32) DEFAULT 'active', topic_id INT REFERENCES topics (id), fk1 INT, fk2 INT, fk3 INT, last_modified TIMESTAMP, date_created TIMESTAMP, FOREIGN KEY (fk1, fk2, fk3) REFERENCES codes (k1, k2, k3) ); CREATE TABLE prices ( id SERIAL PRIMARY KEY, product_id INT REFERENCES products (id), price DECIMAL(10,2) NOT NULL DEFAULT 0.00, region CHAR(2) NOT NULL DEFAULT 'US' );
First we'll auto-initialize the classes.
package Code; use base qw(Rose::DB::Object); __PACKAGE__->meta->auto_initialize; package Category; use base qw(Rose::DB::Object); # Explicit table name required because the class name # does not match up with the table name in this case. __PACKAGE__->meta->table('topics'); __PACKAGE__->meta->auto_initialize; package Product; use base qw(Rose::DB::Object); __PACKAGE__->meta->auto_initialize; package Price; use base qw(Rose::DB::Object); __PACKAGE__->meta->auto_initialize;
Now we'll print the "Product" class definition;
print Product->meta->perl_class_definition(braces => 'bsd', indent => 2);
The output looks like this:
package Product; use strict; use base qw(Rose::DB::Object); __PACKAGE__->meta->setup ( table => 'products', columns => [ id => { type => 'integer', not_null => 1 }, name => { type => 'varchar', length => 32, not_null => 1 }, flag => { type => 'boolean', default => 'true', not_null => 1 }, status => { type => 'varchar', default => 'active', length => 32 }, topic_id => { type => 'integer' }, fk1 => { type => 'integer' }, fk2 => { type => 'integer' }, fk3 => { type => 'integer' }, last_modified => { type => 'timestamp' }, date_created => { type => 'timestamp' }, ], primary_key_columns => [ 'id' ], foreign_keys => [ code => { class => 'Code', key_columns => { fk1 => 'k1', fk2 => 'k2', fk3 => 'k3', }, }, topic => { class => 'Category', key_columns => { topic_id => 'id', }, }, ], relationships => [ prices => { class => 'Price', key_columns => { id => 'product_id' }, type => 'one to many', }, ], ); 1;
Here's the output when the "use_setup" parameter is explicitly set to false.
print Product->meta->perl_class_definition(braces => 'bsd', indent => 2, use_setup => 0);
Note that this approach is not recommended, but exists for historical reasons.
package Product; use strict; use base qw(Rose::DB::Object); __PACKAGE__->meta->table('products'); __PACKAGE__->meta->columns ( id => { type => 'integer', not_null => 1 }, name => { type => 'varchar', length => 32, not_null => 1 }, flag => { type => 'boolean', default => 'true', not_null => 1 }, status => { type => 'varchar', default => 'active', length => 32 }, topic_id => { type => 'integer' }, fk1 => { type => 'integer' }, fk2 => { type => 'integer' }, fk3 => { type => 'integer' }, last_modified => { type => 'timestamp' }, date_created => { type => 'timestamp' }, ); __PACKAGE__->meta->primary_key_columns([ 'id' ]); __PACKAGE__->meta->foreign_keys ( code => { class => 'Code', key_columns => { fk1 => 'k1', fk2 => 'k2', fk3 => 'k3', }, }, topic => { class => 'Category', key_columns => { topic_id => 'id', }, }, ); __PACKAGE__->meta->relationships ( prices => { class => 'Price', key_columns => { id => 'product_id' }, type => 'one to many', }, ); __PACKAGE__->meta->initialize; 1;
See the auto-initialization section for more discussion of Perl code generation.
To see examples of the generated code, look in the documentation for the perl_class_definition method.
To see examples of the generated code, look in the documentation for the perl_class_definition method.
For example, given this class:
package Product; use Rose::DB::Object; our @ISA = qw(Rose::DB::Object); ... print Product->meta->perl_manager_class( class => 'Prod::Mgr', base_name => 'prod');
The following would be printed:
package Prod::Mgr; use Rose::DB::Object::Manager; our @ISA = qw(Rose::DB::Object::Manager); sub object_class { 'Product' } __PACKAGE__->make_manager_methods('prod'); 1;
See the larger example in the documentation for the perl_class_definition method to see what the generated Perl code looks like.
To see examples of the generated code, look in the documentation for the perl_class_definition method.
To see examples of the generated code, look in the documentation for the perl_class_definition method.
The "array" style passes references to arrays of column names:
__PACKAGE__->meta->unique_keys ( [ 'id', 'name' ], [ 'flag', 'status' ], );
The "object" style sets unique keys using calls to the Rose::DB::Object::Metadata::UniqueKey constructor:
__PACKAGE__->meta->unique_keys ( Rose::DB::Object::Metadata::UniqueKey->new( name => 'products_id_key', columns => [ 'id', 'name' ]), Rose::DB::Object::Metadata::UniqueKey->new( name => 'products_flag_key', columns => [ 'flag', 'status' ]), );
John C. Siracusa (siracusa@gmail.com)
Copyright (c) 2010 by John C. Siracusa. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
2022-10-14 | perl v5.34.0 |