NAME

DBIO::ResultSet - Lazy query object for fetching and manipulating DBIO rows

VERSION

version 0.900004

SYNOPSIS

my $users_rs = $schema->resultset('User');
while( $user = $users_rs->next) {
  print $user->username;
}

my $registered_users_rs = $schema->resultset('User')->search({ registered => 1 });
my @cds_in_2005 = $schema->resultset('CD')->search({ year => 2005 })->all();

# Async: same query, non-blocking on a connection with an async mode chosen
# (see L</ASYNC> below)
my $schema = MyApp::Schema->connect($dsn, $user, $pass, { async => 'future_io' });
$schema->resultset('CD')->search({ year => 2005 })->all_async->then(sub {
  my @cds_in_2005 = @_;
  ...
});

See t/resultset/as_query.t for a runnable example.

DESCRIPTION

A ResultSet stores the conditions and attributes that make up a query. It is the central query object in DBIO and the main way rows are searched, updated, deleted, joined, paged, and prefetched.

Creating a ResultSet does not execute SQL. It only stores the information needed to build the eventual query.

A basic ResultSet representing the data of an entire table is returned by calling resultset on a DBIO::Schema and passing in a Source name.

my $users_rs = $schema->resultset('User');

A new ResultSet is returned from calling "search" on an existing ResultSet. The new one will contain all the conditions of the original, plus any new conditions added in the search call.

ResultSets also act as iterators. "next" and "reset" let you walk through the DBIO::Row objects represented by the query.

The query is only executed when you call methods such as: "find", "next", "all", "first", "single", "count".

If a resultset is used in a numeric context it returns the "count". However, if it is used in a boolean context it is always true. So if you want to check if a resultset has any results, you must use if $rs != 0.

METHODS

new

Arguments: $source, \%attrs?
Return Value: $resultset

The resultset constructor. Takes a source object (usually a DBIO::ResultSourceProxy) and an attribute hash (see "ATTRIBUTES" below). Does not perform any queries -- these are executed as needed by the other methods.

Generally you never construct a resultset manually. Instead you get one from e.g. a $schema->resultset('$source_name') or $another_resultset->search(...):

my $rs = $schema->resultset('CD')->search({ title => '100th Window' });
WARNING

If called on an object, proxies to "new_result" instead, so

my $cd = $schema->resultset('CD')->new({ title => 'Spoon' });

will return a CD object, not a ResultSet, and is equivalent to:

my $cd = $schema->resultset('CD')->new_result({ title => 'Spoon' });

Please also keep in mind that many internals call "new_result" directly, so overloading this method with the idea of intercepting new result object creation will not work. See also warning pertaining to "create".

Arguments: $cond | undef, \%attrs?
Return Value: $resultset
my $cds    = $cd_rs->search({ year => 2001 }); # "... WHERE year = 2001"
my $new_rs = $cd_rs->search({ year => 2005 });

my $new_rs = $cd_rs->search([ { year => 2005 }, { year => 2004 } ]);
               # year = 2005 OR year = 2004

Search returns a DBIO::ResultSet and throws in list context (ADR 0042); called in void context from outside DBIO it throws as well. To get the matching rows, call ->all() on the result:

my @cds = $cd_rs->search({ year => 2001 })->all;

Where the call may land in list context -- as a function argument, inside a list constructor, as the last statement of a method -- use "search_rs", which returns the resultset in every context.

If you need to pass in additional attributes but no additional condition, call it as search(undef, \%attrs).

# "SELECT name, artistid FROM $artist_table"
my @all_artists = $schema->resultset('Artist')->search(undef, {
  columns => [qw/name artistid/],
})->all;

For a list of attributes that can be passed to search, see "ATTRIBUTES". For more examples of using this function, see Searching. For a complete documentation for the first argument, see "WHERE CLAUSES" in SQL::Abstract and its extension DBIO::SQLMaker.

For more help on using joins with search, see DBIO::Manual::Joining.

Truth value of a condition

Every node of a condition is true, false or SQL, the same for SELECT, UPDATE and DELETE:

true

{}, undef, -and => [], -and => {}, -not_in => [], and an -and group of nothing but true nodes. A true condition is no restriction and renders no WHERE.

false

[] as a whole condition, -or => [], -in => [], and an -or group of nothing but false nodes. A false condition renders as 0=1.

In an -and group a false member makes the group false and true members drop out; in an -or group a true member makes the group true and false members drop out.

$rs->search({ name => 'a', -or => [] });   # WHERE ( 0=1 )
$rs->search({ name => 'a', -and => [] });  # WHERE ( name = 'a' )

"update" and "delete" refuse a resultset whose condition is true.

Untrusted input

The structure of a condition is code: its keys, its operators and its hashes decide what the statement does. Only values come from a request.

# keys and operator are written in the program, the value comes in
$cd_rs->search({ year => { '>=' => $params->{year} } });

The same holds for every attribute (order_by, columns, having, result_class, ...): it is written in the program. A condition whose structure is not written in the program goes through "search_untrusted", which accepts columns of the resultset as keys, scalars as values and a fixed list of operators.

CAVEAT

Note that "search" does not process/deflate any of the values passed in the SQL::Abstract-compatible search condition structure. This is unlike other condition-bound methods "new_result", "create" and "find". The user must ensure manually that any value passed to this method will stringify to something the RDBMS knows how to deal with. A notable example is the handling of DateTime objects, for more info see: "Formatting DateTime objects in queries" in DBIO::Manual::Cookbook.

search_rs

Arguments: $cond, \%attrs?
Return Value: $resultset

This method does the same exact thing as search() except it will always return a resultset, even in list context.

search_untrusted

Arguments: \%cond | \@cond
Return Value: $resultset
my $rs = $cd_rs->search_untrusted($decoded_json_body);

Does what "search" does for a condition whose structure does not come from the program (see "Untrusted input"). The condition is checked first and handed to "search" unchanged; the first form outside the following grammar throws, naming the path of its key. The method takes exactly one argument, an unblessed hash or array reference.

A condition node is a hash reference (key to operand) or an array reference of condition nodes (OR). -and and -or take an array or hash reference of condition nodes. A scalar as an element of a condition, a blessed container and a cycle throw.

A key is a column of the source of the resultset, or alias.column where alias is an alias of the resolved attributes (the resultset itself or one of its joins) and the column belongs to the source of that alias.

An operand is

  • a scalar (undef included)

  • an array reference of scalars (a list of values, an OR of equalities)

  • a hash reference with exactly one operator of =, !=, <>, <, <=, >, >=, -like, -not_like, -in, -not_in, -between, -not_between, -is and -is_not

-in and -not_in take an array reference of scalars, -between and -not_between an array reference of exactly two scalars, -is and -is_not undef, every other operator a scalar.

Everything else throws: -literal, -func, -op, -ident, -value, -bool, -not, and every reference as a value.

The attributes are not part of the method; they are written in the program (see "Untrusted input").

with_context

Arguments: $async_context
Return Value: $resultset
$schema->storage->txn_do_async(sub {
  my ($ctx) = @_;
  return $rs->with_context($ctx)->all_async->then(sub { ... });
});

Returns a clone of this resultset bound to a pinned async context -- the object handed to the coderef of "txn_do_async" in DBIO::Storage::Async or "pipeline" in DBIO::Storage::Async. Once bound, every *_async method on the clone (and on rows and DBIO::ResultSetColumns derived from it) routes its storage read/write onto the context's pinned connection instead of taking a fresh connection out of the pool. Inside a transaction that means the async read actually runs inside the transaction rather than on a sibling connection that sees pre-BEGIN state; inside a pipeline it means the statement is batched onto the bracketed connection.

The binding is carried, not resolved: the resultset already knows its source and schema, so with_context only threads the context through -- it does not reach back into storage for an ambient "current context" (there is none; an async program may have two transactions in flight at once). The context propagates across "search" / "search_rs" clones and is inherited by rows ("all_async", "first_async", "create_async", ...) and column resultsets ("get_column") produced from the bound resultset. The same pin follows these derivations too (ADR 0045): "page", "as_subselect_rs", "related_resultset" / "search_related" (and the generated relationship accessors), every chaining shortcut built on "search_rs", prefetched children, cache-served rows, and the row refetch/copy helpers. So do the derivations that build a fresh resultset: "bare", "correlate", "count_rs" (the DBIO::ResultSetColumn it returns) and the set operations "union", "union_all", "intersect", "intersect_all", "except" and "except_all". A set operation needs its operands bound alike -- see there.

The rows are bound whichever inflate_result built them, a result class's own included; what DBIO::ResultClass::HashRefInflator returns is plain data and carries nothing. What a bound row or column resultset hands out in turn stays bound as well -- see "with_context" in DBIO::Row and "with_context" in DBIO::ResultSetColumn. The one exception: the related rows a multi-create builds from nested data ("new_result", "create") are not bound, only the row they are nested in is.

A context is only valid while its owning scope remains active. A pipeline context stops accepting work the moment its synchronous batch coderef returns. A transaction context remains active while its callback Future is pending and ends as its COMMIT or ROLLBACK goes out. A bound resultset used after either boundary throws rather than silently falling back to the pool (ADR 0045). See DBIO::Manual::AsyncStorage for the full picture.

A transaction context keeps track of every statement issued on it, the ones a bound resultset or row sends included. If one of them fails, the transaction is rolled back and fails with that statement's error -- also when your code catches the failure and carries on ("txn_do_async" in DBIO::Storage::Async). A caught failure does not rescue the transaction.

In the immediate mode txn_do_async hands its coderef the storage itself (ADR 0055), and with_context takes it, so the pattern above is the same in every async mode. A resultset bound that way runs its *_async in-process, inside the transaction; a statement of it that fails rolls the transaction back, awaited or not; what a bound resultset refuses on a real backend it refuses there; and held past the transaction it is refused. A storage is taken only as the context of its own resultsets and only in the immediate mode.

Two things are particular to that mode. There is one connection and no pool, so an unbound resultset runs inside the transaction too -- but its failed statements are not the transaction's, only a bound one's are. And every immediate transaction on a storage has that one storage for its context: a resultset bound in one transaction and used inside a later one runs in the later one, where a real backend refuses it.

search_literal

CAVEAT: search_literal is a legacy convenience method. It is equivalent to calling $rs->search(\[]), but if you want to ensure columns are bound correctly, use "search".

See "SEARCHING" in DBIO::Manual::Cookbook and "Searching" in DBIO::Manual::FAQ for searching techniques that do not require search_literal.

Arguments: $sql_fragment, @standalone_bind_values
Return Value: $resultset
my $cds   = $cd_rs->search_literal('year = ? AND title = ?', qw/2001 Reload/);
my $newrs = $artist_rs->search_literal('name = ?', 'Metallica');

Pass a literal chunk of SQL to be added to the conditional part of the resultset query. Returns a DBIO::ResultSet and throws in list context (ADR 0042).

Example of how to use search instead of search_literal

my @cds = $cd_rs->search_literal('cdid = ? AND (artist = ? OR artist = ?)', (2, 1, 2))->all;
my @cds = $cd_rs->search(\[ 'cdid = ? AND (artist = ? OR artist = ?)', [ 'cdid', 2 ], [ 'artist', 1 ], [ 'artist', 2 ] ])->all;

find

Arguments: \%columns_values | @pk_values, { key => $unique_constraint, %attrs }?
Return Value: $result | undef

Finds and returns a single row based on supplied criteria. Takes either a hashref with the same format as "create" (including inference of foreign keys from related objects), or a list of primary key values in the same order as the primary columns declaration on the "result_source".

The condition of the resultset and the condition passed to this method are combined: find looks up exactly the row the condition describes. Every key of the passed condition goes into the query as one AND, and a key given as undef stays in it as IS NULL.

The condition has to cover a unique constraint (the constraint corresponding to the primary columns is always named primary): every column of the constraint has a concrete equality in the condition, that is a defined plain value or { '=' => $value }, on the top AND level, after the condition of the resultset and the values of relationships are merged in. Operators, value lists, undef and a constraint that only appears inside an OR branch do not cover it. A condition that covers no constraint throws an exception that names the given columns and the constraints of the source. To search by any other condition use $rs->search(\%cond)->single.

A relationship value (a related row object or a condition hashref for a belongs_to) resolves to the foreign key columns of this source. A foreign key column passed explicitly keeps its value; a relationship resolving it to a different one throws an exception.

The key attribute names the unique constraint that has to be covered; it is the name of a constraint. If a column of the named unique constraint is missing from the condition, an exception is thrown, and so it is for a column supplied as undef: only defined values cover a constraint. Without key, a column given as undef beside a covered constraint stays in the query as IS NULL.

In addition to key, "find" recognizes and applies standard resultset attributes in the same way as "search" does.

Pass the condition with the columns named, as in find({ id => $id }), whenever a value comes from outside the program; a bare find($input) is for values the program itself controls.

For example, to find a row by its primary key:

my $cd = $schema->resultset('CD')->find(5);

A condition of a unique constraint plus further columns finds the row only if it matches all of them, which is how to look up a row of an owner:

my $cd = $schema->resultset('CD')->find({ cdid => $id, owner_id => $me });

You can also find a row by a specific unique constraint:

my $cd = $schema->resultset('CD')->find(
  {
    artist => 'Massive Attack',
    title  => 'Mezzanine',
  },
  { key => 'cd_artist_title' }
);

See also "find_or_create", "update_or_create" and "find_async".

Arguments: $rel_name, $cond?, \%attrs?
Return Value: $resultset
$new_rs = $cd_rs->search_related('artist', {
  name => 'Emo-R-Us',
});

Searches the specified relationship, optionally specifying a condition and attributes for matching records. See "ATTRIBUTES" for more information.

Returns a DBIO::ResultSet and throws in list context (ADR 0042) -- call ->all on the result to fetch rows, or use "search_related_rs", which returns the resultset in every context.

See also "search_related_rs".

This method works exactly the same as search_related, except that it guarantees a resultset, even in list context.

cursor

Arguments: none
Return Value: $cursor

Returns a storage-driven cursor to the given resultset. See DBIO::Cursor for more information.

single

Arguments: $cond?
Return Value: $result | undef
my $cd = $schema->resultset('CD')->single({ year => 2001 });

Inflates the first result without creating a cursor if the resultset has any records in it; if not returns undef. Used by "find" as a lean version of "search".

While this method can take an optional search condition (just like "search") being a fast-code-path it does not recognize search attributes. If you need to add extra joins or similar, call "search" and then chain-call "single" on the DBIO::ResultSet returned.

Note

This method enforces the assumption that the preceding query returns only one row. If more than one row is returned, you will receive a warning:

Query returned more than one row

In this case, you should be using "next" or "find" instead, or if you really know what you are doing, use the "rows" attribute to explicitly limit the size of the resultset.

This method will also throw an exception if it is called on a resultset prefetching has_many, as such a prefetch implies fetching multiple rows from the database in order to assemble the resulting object.

get_column

Arguments: $column_name
Return Value: $resultsetcolumn
my $max_length = $rs->get_column('length')->max;

Returns a DBIO::ResultSetColumn instance for a column of the ResultSet.

search_like

Arguments: $cond, \%attrs?
Return Value: $resultset
# WHERE title LIKE '%blue%'
$cd_rs = $rs->search_like({ title => '%blue%'});

Performs a search, but uses LIKE instead of = as the condition. Note that this is simply a legacy convenience method. You most likely want to use "search" with specific operators.

For more information, see DBIO::Manual::Cookbook.

This method is deprecated. Use search() instead. An example conversion is:

->search_like({ foo => 'bar' });

# Becomes

->search({ foo => { like => 'bar' } });

slice

Arguments: $first, $last
Return Value: $resultset

Returns a resultset representing a subset of elements from the resultset slice is called on. It is a DBIO::ResultSet; in list context slice throws (ADR 0042). Indexes are from 0, i.e., to get the first three records, call:

my ($one, $two, $three) = $rs->slice(0, 2)->all;

next

Arguments: none
Return Value: $result | undef

Returns the next element in the resultset (undef is there is none).

Can be used to efficiently iterate over records in the resultset:

my $rs = $schema->resultset('CD')->search;
while (my $cd = $rs->next) {
  print $cd->title;
}

Note that you need to store the resultset object, and call next on it. Calling resultset('Table')->next repeatedly will always return the first record from the resultset.

result_source

Arguments: $result_source?
Return Value: $result_source

An accessor for the primary ResultSource object from which this ResultSet is derived.

result_class

Arguments: $result_class?
Return Value: $result_class

An accessor for the class to use when creating result objects. Defaults to result_source->result_class - which in most cases is the name of the "table" class.

Note that changing the result_class will also remove any components that were originally loaded in the source class via load_components. Any overloaded methods in the original source class will not run.

The '::HRI'-style shorthand of DBIx::Class::Helper::ResultSet::ResultClassDWIM is deliberately not supported and throws: pass the fully qualified class name, or use "hri" for DBIO::ResultClass::HashRefInflator.

The value is a class name -- Perl identifiers joined by :: -- or an object; anything else throws '...' is not a class name ("ensure_class_loaded" in DBIO::Componentised).

count

Arguments: $cond, \%attrs?
Return Value: $count

Performs an SQL COUNT with the same query as the resultset was built with to find the number of elements. Passing arguments is equivalent to $rs->search ($cond, \%attrs)->count, also with an undefined $cond. "count_async" and "count_rs" take the same arguments.

count_rs

Arguments: $cond, \%attrs?
Return Value: $count_rs

Same as "count" but returns a DBIO::ResultSetColumn object. This can be very handy for subqueries:

->search( { amount => $some_rs->count_rs->as_query } )

As with regular resultsets the SQL query will be executed only after the resultset is accessed via "next" or "all". That would return the same single value obtainable via "count".

count_literal

CAVEAT: count_literal is a legacy convenience method. See "search_literal" for further info.

Arguments: $sql_fragment, @standalone_bind_values
Return Value: $count

Counts the results in a literal query. Equivalent to calling "search_literal" with the passed arguments, then "count".

count_literal_async

my $future = $rs->count_literal_async($sql_fragment, @standalone_bind_values);
$future->then(sub { my $count = shift; ... });

Async variant of "count_literal". Equivalent to calling "search_literal" with the passed arguments, then "count_async"; carries count_async's own backend routing, immediate degrade, and croak-on-sync-instance contract with no further wrapping needed here.

all

Arguments: none
Return Value: @result_objs

Returns all elements in the resultset.

reset

Arguments: none
Return Value: $self

Resets the resultset's cursor, so you can iterate through the elements again. Implicitly resets the storage cursor, so a subsequent "next" will trigger another query.

first

Arguments: none
Return Value: $result | undef

Resets the resultset (causing a fresh query to storage) and returns an object for the first result (or undef if the resultset is empty).

all_async

my $future = $rs->all_async;
$future->then(sub { my @rows = @_; ... });

Async variant of "all". Returns a DBIO::Future that resolves with all result objects, fully inflated with the same prefetch/collapse handling as "all" (the raw rows fetched through the backend's select_async are run through the same _construct_results path).

Requires an async connection (connect(..., { async => ... })); on a sync instance it croaks. In the immediate mode it runs synchronously and resolves immediately. See ADR 0030/0031.

first_async

my $future = $rs->first_async;

Async variant of "first". Returns a DBIO::Future that resolves with the first result object (or undef). The backend path fetches and collapses the full result set (the only collapse-correct way to obtain the first object under a prefetch) and resolves with its first element.

single_async

my $future = $rs->single_async;

Async variant of "single". Routes the single-row fetch through the backend's select_single_async and inflates the row with the same single-row collapse assertion as "single".

find_async

my $future = $rs->find_async(\%cond, \%attrs?);
$future->then(sub { my $row = shift; ... });   # $row may be undef

Async variant of "find". Shares find()'s own condition resolution -- the same covered-constraint check (key => ... or any covered constraint) and the same relationship-valued-key handling (a belongs_to given a related row object or condition hashref resolves to a concrete foreign-key value exactly as for sync "find"; a relation that does not resolve to a concrete condition is a safe miss, resolving with undef and issuing no query at all -- ADR 0034). Routes the backend fetch through select_single_async (the same path "single_async" uses), or, when the resultset collapses a has_many, through the collapse-aware fetch-all path "all_async" uses, carping if more than one row was returned (matching find()'s own "Query returned more than one row" duplicate check).

Note this is not the same thing as calling "single_async" directly: single_async, like sync "single", takes its condition raw and does no relationship-key resolution at all -- handing it a relationship-valued key is the caller's own responsibility, exactly as with plain single. Reach for find_async when you want find()'s lookup semantics async; reach for single_async only where you would also reach for plain single.

count_async

my $future = $rs->count_async;
$future->then(sub { my $count = shift; ... });

Async variant of "count". Routes the count query through the backend's select_single_async and resolves with the integer count (applying the same software-side rows/offset adjustment as "count").

create_async

my $future = $rs->create_async(\%vals);
$future->then(sub { my $row = shift; ... });

Async variant of "create". For a plain single-row create the row's own insert is routed through the backend's insert_async and the resulting result object is built in the Future's then (reusing the same column store-back as the synchronous "insert" in DBIO::Row). Data that carries an object for a column with an inflator, or a row already in storage for a belongs_to, is still a plain single-row create: the object is deflated and the row lends its key, as in "create".

A create that involves related-object multi-create -- a belongs_to given as a hashref or as a row not yet in storage, a has_many given as an arrayref -- does not go out as a single insert_async (delegated to "insert_async" in DBIO::Row, which has the full rule). On an unbound resultset it is run synchronously and the Future is returned settled: resolved with the row, or failed with what the insert threw. On a resultset bound with "with_context" it throws insert_async cannot run on a bound async context instead -- an exception out of create_async, not a failed Future, naming the relationship that makes it a cascade -- since the synchronous insert would run outside the pinned transaction or pipeline (ADR 0045). A belongs_to hashref has by then been looked up with a synchronous statement, outside the transaction.

update

Arguments: \%values, \%options
Return Value: $underlying_storage_rv

Sets the specified columns in the resultset to the supplied values in a single query. Note that this will not run any accessor/set_column/update triggers, nor will it update any result object instances derived from this resultset (this includes the contents of the resultset cache if any). See "update_all" if you need to execute any on-update triggers or cascades defined either by you or a result component.

The return value is a pass through of what the underlying storage backend returned, and may vary. See "execute" in DBI for the most common case.

A resultset whose condition is true (see "search") is unrestricted, and update refuses it: the call must say so with { all => 1 }, the one key \%options takes; any other key throws. A resultset with rows or offset counts as restricted.

$rs->update({ rank => 1 }, { all => 1 });   # the whole table

A column value is a value: a plain scalar, undef, an object, an arrayref (an array data type, always bound) or explicit literal SQL (\'NOW()', \[ 'UPPER(?)', $v ]). A hash, code or glob reference, or a reference to a reference, is no value and throws, naming the column and the type.

CAVEAT

Note that "update" does not process/deflate any of the values passed in. This is unlike the corresponding "update" in DBIO::Row. The user must ensure manually that any value passed to this method will stringify to something the RDBMS knows how to deal with. A notable example is the handling of DateTime objects, for more info see: "Formatting DateTime objects in queries" in DBIO::Manual::Cookbook.

update_all

Arguments: \%values, \%options
Return Value: 1

Fetches all objects and updates them one at a time via "update" in DBIO::Row. Note that update_all will run DBIO defined triggers, while "update" will not. An unrestricted resultset needs { all => 1 } as for "update".

delete

Arguments: \%options
Return Value: $underlying_storage_rv

Deletes the rows matching this resultset in a single query. Note that this will not run any delete triggers, nor will it alter the in_storage status of any result object instances derived from this resultset (this includes the contents of the resultset cache if any). See "delete_all" if you need to execute any on-delete triggers or cascades defined either by you or a result component.

The return value is a pass through of what the underlying storage backend returned, and may vary. See "execute" in DBI for the most common case.

A resultset whose condition is true (see "search") is unrestricted, and delete refuses it: the call must say so with { all => 1 }, the one key \%options takes; any other key throws. A resultset with rows or offset counts as restricted.

$rs->delete({ all => 1 });   # the whole table

delete_all

Arguments: \%options
Return Value: 1

Fetches all objects and deletes them one at a time via "delete" in DBIO::Row. Note that delete_all will run DBIO defined triggers, while "delete" will not. An unrestricted resultset needs { all => 1 } as for "delete".

populate

Arguments: [ \@column_list, \@row_values+ ] | [ \%col_data+ ]
Return Value: \@result_objects (scalar context) | @result_objects (list context)

Accepts either an arrayref of hashrefs or alternatively an arrayref of arrayrefs.

Every column value is a value, in void context as well: a plain scalar, undef, an object, an arrayref (an array data type, always bound) or explicit literal SQL. A hash, code or glob reference (or a reference to a reference) under a column name throws, naming the column and the type; a hash or array under a relationship name is related data.

NOTE

The context of this method call has an important effect on what is submitted to storage. In void context data is fed directly to fastpath insertion routines provided by the underlying storage (most often "execute_for_fetch" in DBI), bypassing the new and insert calls on the Result class, including any augmentation of these methods provided by components. For example if you are using something like DBIO::UUIDColumns to create primary keys for you, you will find that your PKs are empty. In this case you will have to explicitly force scalar or list context in order to create those values.

In non-void (scalar or list) context, this method is simply a wrapper for "create". Depending on list or scalar context either a list of Result objects or an arrayref containing these objects is returned. populate is in this an exception to ADR 0042, under which a DBIO method does not change its return type by call context.

When supplying data in "arrayref of arrayrefs" invocation style, the first element should be a list of column names and each subsequent element should be a data value in the earlier specified column order. For example:

$schema->resultset("Artist")->populate([
  [ qw( artistid name ) ],
  [ 100, 'A Formally Unknown Singer' ],
  [ 101, 'A singer that jumped the shark two albums ago' ],
  [ 102, 'An actually cool singer' ],
]);

For the arrayref of hashrefs style each hashref should be a structure suitable for passing to "create". Multi-create is also permitted with this syntax.

$schema->resultset("Artist")->populate([
   { artistid => 4, name => 'Manufactured Crap', cds => [
      { title => 'My First CD', year => 2006 },
      { title => 'Yet More Tweeny-Pop crap', year => 2007 },
    ],
   },
   { artistid => 5, name => 'Angsty-Whiny Girl', cds => [
      { title => 'My parents sold me to a record company', year => 2005 },
      { title => 'Why Am I So Ugly?', year => 2006 },
      { title => 'I Got Surgery and am now Popular', year => 2007 }
    ],
   },
]);

If you attempt a void-context multi-create as in the example above (each Artist also has the related list of CDs), and do not supply the necessary autoinc foreign key information, this method will proxy to the less efficient "create", and then throw the Result objects away. In this case there are obviously no benefits to using this method over "create".

pager

Arguments: none
Return Value: $pager

Returns a DBIO::ResultSet::Pager object tied to the current resultset. Requires the page attribute to have been previously set on the resultset object, usually via a call to "page".

To get the full count of entries for a paged resultset, call total_entries on the pager object.

A resultset bound with "with_context" hands its context on to the count resultset the pager builds, so the count resultset belongs to the same transaction or pipeline, as the hand-built count of the async recipe in "ASYNC" in DBIO::ResultSet::Pager. An unbound resultset counts as before.

page

Arguments: $page_number
Return Value: $resultset

Returns a resultset for the $page_number page of the resultset on which page is called, where each page contains a number of rows equal to the 'rows' attribute set on the resultset (10 by default).

new_result

Arguments: \%col_data
Return Value: $result

Creates a new result object in the resultset's result class and returns it. The row is not inserted into the database at this point, call "insert" in DBIO::Row to do that. Calling "in_storage" in DBIO::Row will tell you whether the result object has been inserted or not.

Passes the hashref of input on to "new" in DBIO::Row.

as_query

Arguments: none
Return Value: \[ $sql, @bind_values ]

Returns the SQL query and bind vars associated with the invocant.

This is generally used as the RHS for a subquery.

find_or_new

Arguments: \%col_data, { key => $unique_constraint, %attrs }?
Return Value: $result
my $artist = $schema->resultset('Artist')->find_or_new(
  { artist => 'fred' }, { key => 'artists' });

$cd->cd_to_producer->find_or_new({ producer => $producer },
                                 { key => 'primary' });

Find an existing record from this resultset using "find". if none exists, instantiate a new result object and return it. The object will not be saved into your storage until you call "insert" in DBIO::Row on it.

The lookup identifies the row: it uses the columns of the one unique constraint the data covers (see "find"), or of the constraint named by key; the other values are the data of the new object. When the data covers several unique constraints, key names the one to look up; when it covers none, the method throws.

You most likely want this method when looking for existing rows using a unique constraint that is not the primary key, or looking for related rows.

If you want objects to be saved immediately, use "find_or_create" instead.

Note: "find" describes what a condition has to cover and what the key attribute names.

Note: Take care when using find_or_new with a table having columns with default values that you intend to be automatically supplied by the database (e.g. an auto_increment primary key column). In normal usage, the value of such columns should NOT be included at all in the call to find_or_new, even when set to undef.

find_or_new_async

my $future = $rs->find_or_new_async(\%col_data, { key => $unique_constraint, L<%attrs|/ATTRIBUTES> }?);
$future->then(sub { my $row = shift; ... });

Async variant of "find_or_new". Composed of "find_async" (shared condition-resolution and lookup semantics, including the ADR 0034 relationship-valued-key handling) and a plain, synchronous "new_result" for the not-found branch. Carries find_async's own backend routing, immediate degrade, and croak-on-sync-instance contract; a race between the async lookup and any concurrent write is the same one documented for the sync method.

create

Arguments: \%col_data
Return Value: $result

Attempt to create a single new row or a row with multiple related rows in the table represented by the resultset (and related tables). This will not check for duplicate rows before inserting, use "find_or_create" to do that.

To create one row for this resultset, pass a hashref of key/value pairs representing the columns of the table and the values you wish to store. If the appropriate relationships are set up, foreign key fields can also be passed an object representing the foreign row, and the value will be set to its primary key.

To create related objects, pass a hashref of related-object column values keyed on the relationship name. If the relationship is of type multi ("has_many" in DBIO::Relationship) - pass an arrayref of hashrefs. The process will correctly identify columns holding foreign keys, and will transparently populate them from the keys of the corresponding relation. This can be applied recursively, and will work correctly for a structure with an arbitrary depth and width, as long as the relationships actually exists and the correct column data has been supplied.

Instead of hashrefs of plain related data (key/value pairs), you may also pass new or inserted objects. New objects (not inserted yet, see "new_result"), will be inserted into their appropriate tables.

A column value is a value: a plain scalar, undef, an object, an arrayref (an array data type, always bound), explicit literal SQL (\'NOW()', \[ 'UPPER(?)', $v ]), or what an inflated column type deflates. A hash under a relationship name is a relationship; a hash, code or glob reference (or a reference to a reference) under a column name is no value and throws, naming the column and the type.

Effectively a shortcut for ->new_result(\%col_data)->insert.

Passing a request hash to create is mass assignment: what ends up in the columns is what is checked.

  • A value of the hash that contradicts an equality of the resultset's own condition throws (create on a scoped resultset: 'artist' is fixed to a value by the condition, got another value, naming the column and never a value); the same value is allowed. undef is the same value as a condition IS NULL and contradicts a defined value. This holds for a relationship value as well: what it resolves to is the value that is checked.

  • A column marked is_guarded takes no value from the hash, also when a relationship value of the hash sets it as foreign key; a relationship marked is_guarded takes no hash of related data. The values the resultset's condition holds, and the foreign keys the rows created along inherit from their parent, are no assignment of the hash's.

  • A hash of related data is looked up with "find" only for a belongs_to relationship, and only when it gives every column of a unique constraint of the related source (see "find"); any other related data, and every has_many, might_have and has_one row, is created. Being unique is no permission to link a row: mark the relationship or its foreign key is_guarded where a caller may not link existing rows.

Example of creating a new row.

$person_rs->create({
  name=>"Some Person",
  email=>"somebody@someplace.com"
});

Example of creating a new row and also creating rows in a related has_many or has_one resultset. Note Arrayref.

$artist_rs->create(
   { artistid => 4, name => 'Manufactured Crap', cds => [
      { title => 'My First CD', year => 2006 },
      { title => 'Yet More Tweeny-Pop crap', year => 2007 },
    ],
   },
);

Example of creating a new row and also creating a row in a related belongs_to resultset. Note Hashref.

$cd_rs->create({
  title=>"Music for Silly Walks",
  year=>2000,
  artist => {
    name=>"Silly Musician",
  }
});
WARNING

When subclassing ResultSet never attempt to override this method. Since it is a simple shortcut for $self->new_result($attrs)->insert, a lot of the internals simply never call it, so your override will be bypassed more often than not. Override either "new" in DBIO::Row or "insert" in DBIO::Row depending on how early in the "create" process you need to intervene. See also warning pertaining to "new".

find_or_create

Arguments: \%col_data, { key => $unique_constraint, %attrs }?
Return Value: $result
$cd->cd_to_producer->find_or_create({ producer => $producer },
                                    { key => 'primary' });

Tries to find a record based on a unique constraint; if none is found, creates one and returns that instead.

The lookup identifies the row: it uses the columns of the one unique constraint the data covers (see "find"), the other values are the data of the row to create.

my $cd = $schema->resultset('CD')->find_or_create({
  artist => 'Massive Attack',
  title  => 'Mezzanine',
  year   => 2005,
});

When the data covers several unique constraints, the key attribute names the one to look up; when it covers none, the method throws. For example:

my $cd = $schema->resultset('CD')->find_or_create(
  {
    artist => 'Massive Attack',
    title  => 'Mezzanine',
  },
  { key => 'cd_artist_title' }
);

Note: "find" describes what a condition has to cover and what the key attribute names.

Note: Because find_or_create() reads from the database and then possibly inserts based on the result, this method is subject to a race condition. Another process could create a record in the table after the find has completed and before the create has started. To avoid this problem, use find_or_create() inside a transaction.

Note: Take care when using find_or_create with a table having columns with default values that you intend to be automatically supplied by the database (e.g. an auto_increment primary key column). In normal usage, the value of such columns should NOT be included at all in the call to find_or_create, even when set to undef.

See also "find" and "update_or_create". For information on how to declare unique constraints, see "add_unique_constraint" in DBIO::ResultSource.

If you need to know if an existing row was found or a new one created use "find_or_new" and "in_storage" in DBIO::Row instead. Don't forget to call "insert" in DBIO::Row to save the newly created row to the database!

my $cd = $schema->resultset('CD')->find_or_new({
  artist => 'Massive Attack',
  title  => 'Mezzanine',
  year   => 2005,
});

if( !$cd->in_storage ) {
    # do some stuff
    $cd->insert;
}

find_or_create_async

my $future = $rs->find_or_create_async(\%col_data, { key => $unique_constraint, L<%attrs|/ATTRIBUTES> }?);
$future->then(sub { my $row = shift; ... });

Async variant of "find_or_create". Composed of "find_async" for the lookup and "create_async" for the not-found branch; carries whichever of the two methods' own backend routing, immediate degrade, and croak-on-sync-instance contract applies. Subject to the same race as sync "find_or_create" -- another process may insert between the lookup and the create -- so use this inside a transaction if that matters: on a resultset bound to the transaction's context with "with_context". A create that fails there fails the whole transaction, also when the failure is caught -- catching it and looking the row up again does not save the transaction.

update_or_create

Arguments: \%col_data, { key => $unique_constraint, %attrs }?
Return Value: $result
$resultset->update_or_create({ col => $val, ... });

Like "find_or_create", but if a row is found it is immediately updated via $found_row->update (\%col_data).

The lookup identifies the row: it uses the columns of the one unique constraint the data covers (see "find"), or of the constraint named by key; the other values are the data the found row is updated with or the new row is created with. When the data covers several unique constraints, key names the one to look up; when it covers none, the method throws.

Takes an optional key attribute to search on a specific unique constraint. For example:

# In your application
my $cd = $schema->resultset('CD')->update_or_create(
  {
    artist => 'Massive Attack',
    title  => 'Mezzanine',
    year   => 1998,
  },
  { key => 'cd_artist_title' }
);

$cd->cd_to_producer->update_or_create({
  producer => $producer,
  name => 'harry',
}, {
  key => 'primary',
});

If %col_data mixes plain columns of this table with a value keyed on a relationship name ({ some_col => $x, some_relation => \%data }), the row is looked up using the plain columns plus any belongs_to that reduces to a concrete foreign-key condition on this table. A relation that does not so reduce (a has_many / has_one / might_have sub-structure, or a belongs_to whose value is not a resolvable foreign key) is omitted from the lookup, so that find() is never handed a condition that would clobber a key column with an undef foreign key. The FULL %col_data, relation keys included, is then passed to $found_row->update or $new_row->insert. How those relation keys are treated from there is the row layer's responsibility: a resolvable belongs_to is folded into this row's own foreign-key columns, whereas a nested has_many / might_have sub-structure is rejected unless the result class composes in a component that owns nested-relation semantics. The key attribute constrains only the lookup.

Note: For such a nested has_many / might_have sub-structure the create and update branches behave asymmetrically in stock DBIO. When no row is found the row is created and the nested rows are multi-created alongside it (the row layer's insert-time nested-write path), so the call succeeds; when a row is found it is updated via $found_row->update, whose set_from_related guard refuses the same sub-structure, so the call throws. This is the honest pre-0.900001 contract -- there is an insert-time nested-write path but as yet no update-time one -- not a designed feature; a row-layer seam for nested update is expected to follow (see the Changes entry for this release).

Note: "find" describes what a condition has to cover and what the key attribute names.

Note: Take care when using update_or_create with a table having columns with default values that you intend to be automatically supplied by the database (e.g. an auto_increment primary key column). In normal usage, the value of such columns should NOT be included at all in the call to update_or_create, even when set to undef.

See also "find" and "find_or_create". For information on how to declare unique constraints, see "add_unique_constraint" in DBIO::ResultSource.

If you need to know if an existing row was updated or a new one created use "update_or_new" and "in_storage" in DBIO::Row instead. Don't forget to call "insert" in DBIO::Row to save the newly created row to the database!

update_or_create_async

my $future = $rs->update_or_create_async(\%col_data, { key => $unique_constraint, L<%attrs|/ATTRIBUTES> }?);
$future->then(sub { my $row = shift; ... });

Async variant of "update_or_create". Reuses _find_lookup_cond as-is for the lookup (the same relation-valued-key dropping ADR 0034 gives sync update_or_create), then composes "find_async" with update_async (found) or "create_async" (not found) -- the FULL, unsplit condition reaches the row layer via update_async/create_async either way, exactly like sync. No transaction is opened, matching current sync behaviour. Carries whichever of the composed methods' own backend routing, immediate degrade, and croak-on-sync-instance contract applies, and is subject to the same find/create race documented for sync "update_or_create".

upsert

Arguments: \%col_data, \%opts?
Return Value: $result | undef | $count | ()
my $row = $schema->resultset('Artist')->upsert(
  { artistid => 5, name => 'Aphex Twin', rank => 1 },
);

A single-statement, server-side atomic upsert: one INSERT that carries a conflict clause, so the engine -- not the client -- resolves the collision. Unlike "find_or_create", "update_or_create" and "insert_or_update" in DBIO::Row, which read and then write and are therefore subject to a time-of-check/time-of-use race, upsert has no window between the two.

upsert is only available on engines whose driver declares the supports_upsert capability (PostgreSQL / SQLite ON CONFLICT, MySQL ON DUPLICATE KEY UPDATE). On any other engine it throws; it never degrades silently to a client-side two-step. See "Unsupported engines" below.

\%col_data may also carry nested relationship data, in which case upsert upserts the parts too -- see "Cascading upsert". The batch name upsert_all is reserved and not implemented.

Options

key / conflict_target

Which unique constraint the statement conflicts on. key names a unique constraint declared with "add_unique_constraint" in DBIO::ResultSource, exactly as "find" uses it. conflict_target names the columns directly: an arrayref, a single column name, or the string 'any'.

The two are mutually exclusive -- passing both throws.

key works on every supporting engine, including those whose conflict-clause grammar knows a column list only: the constraint name and its resolved columns are both handed to the driver, which picks whichever its grammar takes.

conflict_target => 'any' is the engine-native "whichever unique constraint collides" of MySQL's ON DUPLICATE KEY UPDATE. It is a legitimate want but not a portable one: PostgreSQL and SQLite require a target for DO UPDATE and will reject it (on_conflict => 'nothing' works everywhere, see below).

When neither option is given the primary key is the target, but only if \%col_data supplies every primary key column. If it does not -- or the source has no primary key at all -- upsert throws. With an unsupplied auto-increment key the statement could never collide, so the upsert would silently degenerate into a plain INSERT: a call that does nothing, with no error to show for it. upsert deliberately does not fall back to a lone unique constraint; with no primary key, name the target.

on_conflict

'update' (the default) or 'nothing'.

update

Which columns the conflict branch writes. By default every supplied column that is not part of the conflict target and not a primary key column is set to the value that was attempted to be inserted (the engine's EXCLUDED.col / VALUES(col) -- core stays abstract, the driver renders the token).

update => [qw/ score seen_at /]                      # restrict to these
update => { count => \'count + 1', seen_at => \'NOW()' }   # expressions

The arrayref form restricts the default set; every listed column must be among the inserted values. The hashref form replaces the set: values are used verbatim, so a literal \'count + 1' passes straight through to the engine.

update_where

A condition on the conflict branch: the engine performs the update only where it holds and leaves the stored row untouched otherwise. This is what makes the last-write-wins guard -- "insert, or update only if the incoming row is newer" -- expressible in a single statement, instead of choosing between overwriting newer data and giving up atomicity:

$rs->upsert(
  { id => 5, payload => $blob, updated_at => $ts },
  { update_where => { updated_at => { '<' => { -attempted => 'updated_at' } } } },
);

It is the WHERE of the DO UPDATE branch, not a search condition on the resultset -- the resultset's own condition is folded into the inserted values the way "create" folds it in, and a second plain where at the call site would be indistinguishable from it. Hence the name.

The interesting conditions compare the two rows the statement knows about, and both are sayable without engine tokens:

  • an unqualified column is the stored row -- the row the statement collided with.

  • { -attempted => $column } is the value the statement tried to insert. That is the same engine-neutral marker the update set uses; the driver renders it as its excluded-row reference (EXCLUDED.col on PostgreSQL and SQLite), and it may appear anywhere in the condition.

A literal is the escape hatch for what the abstract form cannot say: \'excluded.updated_at > artist.updated_at' or \[ $sql, @bind ] passes through verbatim -- and is engine-specific by construction, which is the honest trade for writing engine SQL by hand.

Core validates the shape and nothing else, exactly as "search" does not look inside its \%cond: a column that does not exist is an error from the engine, not from upsert.

Needs the supports_upsert_where capability on top of supports_upsert, and throws without it. A driver that does not render the condition would emit a valid unconditional UPDATE -- the very overwrite the option exists to prevent -- so the driver has to declare that it renders one before core sends one. MySQL cannot: ON DUPLICATE KEY UPDATE has no WHERE, and a per-column IF() does not suppress the write.

Refused up front together with on_conflict => 'nothing' (no update branch to condition), result => 'row_or_die' (a condition that does not hold writes no row, so no row can be guaranteed) and fallback ("update_or_create" writes unconditionally).

result

What the call returns. See "Return shapes".

fallback

fallback => 'update_or_create' -- explicitly accept the client-side race for this one call on an engine that has no atomic upsert. Per call only; there is deliberately no schema- or storage-level default, because a global switch is exactly the lever that would re-enable the race application-wide, including inside libraries that never asked for it.

relations

The per-relation conflict targets for a cascade. See "Cascading upsert".

Return shapes

result => ...   returns
'row_or_undef' (default)

A stored Result when the written row can be named, undef when nothing was written or the engine could not name what it wrote. The first time the unnameable case is hit, a warning naming the source is emitted; pass result => 'row_or_undef' explicitly to silence it.

'row_or_die'

Guarantees a Result -- throws rather than hand back nothing. Refused up front in combination with on_conflict => 'nothing' and with update_where, both of which exist in order to possibly write nothing.

'count'

The statement's affected-row count, as a plain number -- notably 0, not DBI's zero-but-true '0E0', so if (my $n = $rs->upsert(...)) reads false when nothing was written. (upsert throws on failure, so there is no success-versus-failure information for a zero-but-true value to carry.) The count itself is engine-specific. MySQL and MariaDB connect with CLIENT_FOUND_ROWS by default (both DBD::mysql and DBD::MariaDB), which counts matched rows: an insert reports 1, an update that changes the row 2, and an update that leaves it unchanged still 1 -- as does on_conflict => 'nothing', whether it inserted or skipped. Only a connection with that flag switched off reports 0 for the unchanged cases.

'void'

Returns nothing and spends nothing obtaining it: no RETURNING tail, no last_insert_id, no follow-up SELECT -- one round-trip.

Literally nothing: the empty list, not undef. In list context it therefore contributes no element at all, which silently eats an argument -- is $rs->upsert(\%data, { result => 'void' }), undef, 'name' becomes is(undef, 'name'). Call it in void or scalar context.

There is no plain 'row': not every engine can name the row it just wrote, and every value in this set says what happens in that case.

Portable code passes result explicitly. The default is uniform across engines in type, but what an engine can prove differs; stating the shape you need turns an engine difference into either a Result or a clear exception at the call site instead of a silent undef.

The returned Result carries the columns you supplied plus whatever the engine handed back (primary key, retrieve_on_insert columns) -- like "create", it is not a re-read of the stored row, so columns the statement did not touch are not populated from the database.

on_conflict => 'nothing' with row_or_undef returns undef when the row was already there: DO NOTHING makes RETURNING yield zero rows, which is the legitimate outcome rather than a failure -- and, unlike the unnameable case above, it is not warned about: nothing was written, so no identity is missing. On an engine without RETURNING the identity comes from last_insert_id, which cannot distinguish "skipped" from "inserted". The affected-row count does not reliably either: it tells them apart only where the engine counts changed rows (on MySQL and MariaDB, only with CLIENT_FOUND_ROWS switched off -- see 'count' above).

Cascading upsert

my $cd = $schema->resultset('CD')->upsert(
  {
    title  => 'Bad',
    year   => 1987,
    artist => { name => 'Michael Jackson' },        # belongs_to
    tracks => [ { position => 1, title => 'Bad' },  # has_many
                { position => 2, title => 'Speed Demon' } ],
  },
  {
    conflict_target => [qw/ artist title /],
    relations => {
      artist => { key => 'artist_name' },
      tracks => { conflict_target => [qw/ cd position /] },
    },
  },
);

When \%col_data nests relationship data, upsert writes the parts as well. What that buys you, in exactly these words and not a word more:

N atomic upserts, in dependency order, inside one transaction -- all-or-nothing, and every part individually free of the read-then-write race.

No engine has a multi-table upsert, so a cascade is inherently more than one statement; this is not "an atomic upsert". It is strictly stronger than the "update_or_create" cascade, which has to guess at every part, and strictly weaker than a single statement.

Order

belongs_to parts are written before the row -- their key becomes its foreign key. has_many and might_have parts are written after it -- its key becomes theirs. This is the same dependency split "insert" in DBIO::Row performs for multi-create, and the same metadata decides it, so a relationship that multi-creates in one order cascades in that order too. Parts of the same kind run in relationship-name order.

Targets, per relation, never inferred

relations mirrors the nested data: one entry per nested relationship, holding key or conflict_target (mutually exclusive) and, for data nested deeper, its own relations. Naming a relationship that has no nested data throws rather than doing nothing.

on_conflict, update, update_where and result are decisions about the whole call and have no per-relation form.

The three-way default rule from "Options" applies per part: a part whose data does not supply the related source's primary key, and which has no target of its own, throws. A foreign key the part will be handed -- by its parent, or by a part written before it -- counts as supplied.

Identity is a precondition, not a nicety

Setting a child's foreign key needs the key of the parent part just written, so every part something depends on must be nameable in the sense of "Return shapes": it is upserted as row_or_die whatever you asked to get back. A shape in which the written row cannot be named -- a source with no primary key, conflict_target => 'any', on_conflict => 'nothing' or update_where on a row that children hang off -- makes the cascade impossible and throws. A conditional update on a row nothing depends on is fine: it may write nothing and hand back undef, exactly as it does flat.

All of that follows from schema metadata and the options, so it is checked before the first statement runs: an impossible cascade fails on the first call in development, not data-dependently in production. What core cannot see statically is whether a given engine can name the row for a given table shape (MySQL's is the one that cannot always) -- that surfaces as the part's own row_or_die exception, inside the rolled-back transaction.

result is restricted

Only row_or_undef and row_or_die; count and void throw at option-validation time. A count across a cascade has no defined meaning, and void would withhold the identity the cascade structurally needs. The return value is the top-level Result only -- related rows are not collected, just as "create" with nested data does not hand them back. With has_many or might_have parts the top-level row is written as row_or_die regardless, because the children need its key.

Leaf parts nobody depends on are written with result => 'void': one round-trip, no identity work spent on a row that is not handed back.

One transaction

The cascade wraps itself in a txn_scope_guard. If the caller is already inside a transaction it joins it, so a failing cascade rolls the caller's outer work back too. There is deliberately no savepoint magic here: a caller who wants the cascade isolated from surrounding work opens their own savepoint.

has_many upserts the listed children and never deletes the others

Children not in the list are left untouched. "Replace the set" is set_$rel semantics and something else entirely; implicit deletion would be the nastiest surprise this API could offer.

many_to_many throws

It would mean writing bridge rows, which reopens the deletion question above for the link table. Nest the bridge relationship itself, or use set_$rel.

Result objects in the data throw

A nested part is a hashref of columns. An existing Result object does not say whether you want it written or only linked, and guessing is the DWIM this API exists to avoid -- pass { $row->get_columns }, or set the foreign key yourself.

fallback cannot express a cascade

"update_or_create" takes one named constraint for the top-level row and nothing at all for the parts, so a fallback cascade would look every part up by "find"'s heuristics -- a different row than the atomic path writes. The combination throws.

Unsupported engines

$rs->result_source->storage->_use_supports_upsert
  ? $rs->upsert(\%data, \%opts)
  : $rs->update_or_create(\%data);   # racy, and visible at the call site

is the capability-based branch for portable library code -- no driver-name string match anywhere. Alternatively, fallback => 'update_or_create' folds the same branch into the call. On the fallback path the return mapping is kept identical so the same call never changes type per engine: row_or_undef / row_or_die hand back the Result update_or_create produced, void discards it, and 'count' returns a synthetic 1 -- not a real affected-rows figure, since no single statement ran. No warning is emitted when the fallback engages: it was explicitly requested.

The fallback looks the row up by the same constraint the atomic statement would have conflicted on -- key if given, otherwise the primary key. conflict_target cannot be expressed that way ("update_or_create" takes a constraint name, never a column list), so combining the two throws and the message points at key => $constraint.

Because the fallback always writes every supplied column, it likewise cannot honour on_conflict => 'nothing' or a restricted update set. All three combinations throw at option-validation time, on every engine, rather than drifting semantically on the one engine where the fallback engages.

See ADR 0036 for the full decision record.

upsert_async

my $future = $rs->upsert_async(\%col_data, \%opts?);
$future->then(sub { my $row_or_count_or_undef = shift; ... });

Async variant of "upsert". Shares every validation rule, conflict-target resolution and capability gate with the synchronous method verbatim (both route through the private _upsert_prepare, so the two cannot drift) -- an invalid call is invalid on both, identically.

The single-statement, row_or_undef/row_or_die case is genuinely non-blocking: the write is routed through the backend's insert_async and the result is inflated in the Future's then, exactly like "create_async". Two shapes cannot be: nested relationship data is a multi-statement transactional cascade (_upsert_cascade), and fallback => 'update_or_create' is itself a client-side two-step -- neither is a single insert_async. result => 'count' also cannot: the affected-row count sync reads off a storage-specific side channel that the generic async insert path does not populate (see "insert_async" in DBIO::Storage::Async -- it resolves the returned-columns hashref, never a count). On an unbound resultset all three run the synchronous "upsert" -- blocking, on the owning storage's own connection, not on a pooled one -- and return a settled Future -- resolved with its result, or failed with what it threw -- the same precedent "create_async" sets for related-object multi-create (ADR 0030/0031). On a resultset bound with "with_context" all three throw upsert_async cannot run on a bound async context instead -- an exception out of upsert_async, not a failed Future -- since the synchronous fallback would run outside the pinned transaction or pipeline (ADR 0045).

Requires an async connection; on a sync instance it croaks, and in the immediate mode it degrades to a synchronous "upsert" wrapped in an immediately-resolved Future. See ADR 0036 for the option contract.

update_or_new

Arguments: \%col_data, { key => $unique_constraint, %attrs }?
Return Value: $result
$resultset->update_or_new({ col => $val, ... });

Like "find_or_new" but if a row is found it is immediately updated via $found_row->update (\%col_data).

The lookup identifies the row, as for "update_or_create": the columns of the one unique constraint the data covers, or of the constraint named by key.

For example:

# In your application
my $cd = $schema->resultset('CD')->update_or_new(
  {
    artist => 'Massive Attack',
    title  => 'Mezzanine',
    year   => 1998,
  },
  { key => 'cd_artist_title' }
);

if ($cd->in_storage) {
    # the cd was updated
}
else {
    # the cd is not yet in the database, let's insert it
    $cd->insert;
}

Note: "find" describes what a condition has to cover and what the key attribute names.

Note: Take care when using update_or_new with a table having columns with default values that you intend to be automatically supplied by the database (e.g. an auto_increment primary key column). In normal usage, the value of such columns should NOT be included at all in the call to update_or_new, even when set to undef.

See also "find", "find_or_create" and "find_or_new".

update_or_new_async

my $future = $rs->update_or_new_async(\%col_data, { key => $unique_constraint, L<%attrs|/ATTRIBUTES> }?);
$future->then(sub { my $row = shift; ... });

Async variant of "update_or_new". Composed of "find_async" (looking up by the columns of the covered unique constraint, exactly like sync) and update_async for the found branch, or a plain, synchronous "new_result" for the not-found branch. Carries the composed methods' own backend routing, immediate degrade, and croak-on-sync-instance contract.

get_cache

Arguments: none
Return Value: \@result_objs | undef

Gets the contents of the cache for the resultset, if the cache is set.

The cache is populated either by using the "prefetch" attribute to "search" or by calling "set_cache".

set_cache

Arguments: \@result_objs
Return Value: \@result_objs

Sets the contents of the cache for the resultset. Expects an arrayref of objects of the same class as those produced by the resultset. Note that if the cache is set, the resultset will return the cached objects rather than re-querying the database even if the cache attr is not set.

The contents of the cache can also be populated by using the "prefetch" attribute to "search".

clear_cache

Arguments: none
Return Value: undef

Clears the cache for the resultset.

is_paged

Arguments: none
Return Value: true, if the resultset has been paginated

is_ordered

Arguments: none
Return Value: true, if the resultset has been ordered with order_by.
Arguments: $rel_name
Return Value: $resultset

Returns a related resultset for the supplied relationship name.

$artist_rs = $schema->resultset('CD')->related_resultset('Artist');

current_source_alias

Arguments: none
Return Value: $source_alias

Returns the current table alias for the result source this resultset is built on, that will be used in the SQL query. Usually it is me.

Currently the source alias that refers to the result set returned by a "search"/"find" family method depends on how you got to the resultset: it's me by default, but eg. "search_related" aliases it to the related result source name (and keeps me referring to the original result set). The long term goal is to make DBIO always alias the current resultset as me (and make this method unnecessary).

Thus it's currently necessary to use this method in predefined queries (see "Predefined searches" in DBIO::Manual::Cookbook) when referring to the source alias of the current result set:

# in a result set class
sub modified_by {
  my ($self, $user) = @_;

  my $me = $self->current_source_alias;

  return $self->search({
    "$me.modified" => $user->id,
  });
}

The alias of newly created resultsets can be altered by the alias attribute.

as_subselect_rs

Arguments: none
Return Value: $resultset

Act as a barrier to SQL symbols. The resultset provided will be made into a "virtual view" by including it as a subquery within the from clause. From this point on, any joined tables are inaccessible to ->search on the resultset (as if it were simply where-filtered without joins). For example:

my $rs = $schema->resultset('Bar')->search({'x.name' => 'abc'},{ join => 'x' });

# 'x' now pollutes the query namespace

# So the following works as expected
my $ok_rs = $rs->search({'x.other' => 1});

# But this doesn't: instead of finding a 'Bar' related to two x rows (abc and
# def) we look for one row with contradictory terms
my $broken_rs = $rs->search({'x.name' => 'def'});

my $rs2 = $rs->as_subselect_rs;

# doesn't work - 'x' is no longer accessible in $rs2, having been sealed away
my $not_joined_rs = $rs2->search({'x.other' => 1});

# works as expected: finds a 'Bar' row related to two x rows (abc and def).
# The join has to be named: one is not inferred from the condition once the
# FROM is a subquery
my $correctly_joined_rs = $rs2->search({'x.name' => 'def'}, { join => 'x' });

Another example of when one might use this would be to select a subset of columns in a group by clause:

my $rs = $schema->resultset('Bar')->search(undef, {
  group_by => [qw{ id foo_id baz_id }],
})->as_subselect_rs->search(undef, {
  columns => [qw{ id foo_id }]
});

In the above example normally columns would have to be equal to the group by, but because we isolated the group by into a subselect the above works.

STORABLE_thaw

The Storable hook that restores a frozen resultset. Thaw only data of your own, from a source you trust -- Storable creates objects of whatever class the data names. The result source inside is resolved and checked by "STORABLE_thaw" in DBIO::ResultSource.

EXAMPLES

Chaining resultsets

Let's say you've got a query that needs to be run to return some data to the user. But, you have an authorization system in place that prevents certain users from seeing certain information. So, you want to construct the basic query in one method, but add constraints to it in another.

sub get_data {
  my $self = shift;
  my $request = $self->get_request; # Get a request object somehow.
  my $schema = $self->result_source->schema;

  my $cd_rs = $schema->resultset('CD')->search({
    title => $request->param('title'),
    year => $request->param('year'),
  });

  $cd_rs = $self->apply_security_policy( $cd_rs );

  return $cd_rs->all();
}

sub apply_security_policy {
  my $self = shift;
  my ($rs) = @_;

  return $rs->search({
    subversive => 0,
  });
}

Resolving conditions and attributes

When a resultset is chained from another resultset (e.g.: my $new_rs = $old_rs->search(\%extra_cond, \%attrs)), conditions and attributes with the same keys need resolving.

If any of "columns", "select", "as" are present, they reset the original selection, and start the selection "clean".

The "join", "prefetch", "+columns", "+select", "+as" attributes are merged into the existing ones from the original resultset.

The "where" and "having" attributes, and any search conditions, are merged with an SQL AND to the existing condition from the original resultset.

All other attributes are overridden by any new ones supplied in the search attributes.

Multiple queries

Since a resultset just defines a query, you can do all sorts of things with it with the same object.

# Don't hit the DB yet.
my $cd_rs = $schema->resultset('CD')->search({
  title => 'something',
  year => 2009,
});

# Each of these hits the DB individually.
my $count = $cd_rs->count;
my $most_recent = $cd_rs->get_column('date_released')->max();
my @records = $cd_rs->all;

And it's not just limited to SELECT statements.

$cd_rs->delete();   # "... WHERE title = 'something' AND year = 2009"

This is even cooler:

$cd_rs->create({ artist => 'Fred' });

Which is the same as:

$schema->resultset('CD')->create({
  title => 'something',
  year => 2009,
  artist => 'Fred'
});

See: "search", "count", "get_column", "all", "create".

Custom ResultSet classes

To add methods to your resultsets, you can subclass DBIO::ResultSet, similar to:

package MyApp::Schema::ResultSet::User;

use strict;
use warnings;

use base 'DBIO::ResultSet';

sub active {
  my $self = shift;
  $self->search({ $self->current_source_alias . '.active' => 1 });
}

sub unverified {
  my $self = shift;
  $self->search({ $self->current_source_alias . '.verified' => 0 });
}

sub created_n_days_ago {
  my ($self, $days_ago) = @_;
  $self->search({
    $self->current_source_alias . '.create_date' => {
      '<=',
    $self->result_source->schema->storage->datetime_parser->format_datetime(
      DateTime->now( time_zone => 'UTC' )->subtract( days => $days_ago )
    )}
  });
}

sub users_to_warn { shift->active->unverified->created_n_days_ago(7) }

1;

A method that ends in "search" hands its caller's context on to it, and "search" throws in list context (ADR 0042). End the method in "search_rs" if it is to be called there, as in [ $rs->active, $rs->unverified ].

See "load_namespaces" in DBIO::Schema on how DBIO can discover and automatically attach Result-specific ResulSet classes.

ResultSet subclassing with custom constructors

DBIO does not require Moose, Moo, or any other constructor layer for custom ResultSet classes. A plain subclass of DBIO::ResultSet is usually enough.

If you do use an external constructor provider, you must adapt its constructor to DBIO's ->new($source, \%args) calling convention.

In order to write custom ResultSet classes with Moo you need to use the following template. The BUILDARGS is necessary due to the unusual signature of the constructor provided by DBIO ->new($source, \%args).

use Moo;
extends 'DBIO::ResultSet';
sub BUILDARGS { $_[2] || {} } # ::RS::new() expects my ($class, $rsrc, $args) = @_

...your code...

1;

If you want to build your custom ResultSet classes with Moose, you need a similar, though a little more elaborate template in order to interface the inlining of the Moose-provided object constructor, with the DBIO one.

package MyApp::Schema::ResultSet::User;

use Moose;
use MooseX::NonMoose;
extends 'DBIO::ResultSet';

sub BUILDARGS { $_[2] || {} } # ::RS::new() expects my ($class, $rsrc, $args) = @_

...your code...

__PACKAGE__->meta->make_immutable;

1;

The MooseX::NonMoose is necessary so that the Moose constructor does not entirely overwrite the DBIO one (in contrast Moo does this automatically). Alternatively, you can skip MooseX::NonMoose and get by with just Moose instead by doing:

__PACKAGE__->meta->make_immutable(inline_constructor => 0);

ASYNC

An async mode is chosen per connection (ADR 0030), not by naming a storage class -- MyApp::Schema->connect($dsn, $user, $pass, { async => 'future_io' }) (or 'ev', 'immediate', 'forked' -- see DBIO::Storage::Async). With a mode that has a backend behind it, the *_async methods below are genuinely non-blocking on that connection, not a synchronous call wrapped in a resolved Future (ADR 0031). The 'immediate' mode has no backend and is that wrapper (see the dispatch contract below), and a few operations that do not go out as one async statement run synchronously in every mode -- "create_async" and "upsert_async" name them.

A runtime failure arrives as a failed Future: an error while the pooled backend is resolved or the statement is built (the connection cannot be made, say) fails the Future the call returns, with the message as thrown. Misuse is an exception at the call: a connection without an async mode, a resultset bound to a context that is expired or not its own ("with_context"), and arguments the call refuses (an unknown option, no data, a capability the storage does not declare, as with "upsert_async").

The full *_async surface on a ResultSet is:

find_async is worth calling out on its own: it is not simply single_async wrapped in a Future. It shares find's own unique-constraint-narrowed, relationship-key-aware condition resolution, so a relationship-valued key resolves to a concrete foreign-key value exactly as sync find does, and a relation that cannot resolve to one is a safe miss -- undef, no query issued at all (ADR 0034). single_async, like sync single, takes its condition raw with no such resolution; reach for find_async when you want find's lookup semantics, single_async only where you would also reach for plain single.

ResultSet's other write/helper methods (update, update_all, delete, delete_all, populate) have no async twin. Row by row, use "update_async" in DBIO::Row and "delete_async" in DBIO::Row.

Every *_async method shares one three-way dispatch contract:

  • a live embedded async backend

    The read/write is routed through the storage's own *_async operation and the raw result is inflated (with full prefetch/collapse parity to the synchronous method) in the returned DBIO::Future's then. Genuinely non-blocking.

  • the immediate mode (an async mode is chosen, but no event-loop backend)

    The ordinary synchronous method runs in-process and its result is wrapped in an already-resolved Future via "future_class" in DBIO::Storage.

  • a plain synchronous connection (no async mode chosen at connect)

    Croaks: "not an async connection -- connect with { async => ... } to use ${name}_async". There is no silent degrade.

See t/resultset/async_backend.t and t/resultset/find_async.t for runnable examples of all of the above against a mock async backend (no event loop, no real database).

throw_exception

See "throw_exception" in DBIO::Schema for details.

me

$rs->search({ $rs->me.'title' => 'foo' })
$rs->search({ $rs->me('title') => 'foo' })

Returns the current source alias with a dot appended (e.g. me.), suitable for use in search conditions. If an argument is passed, it is appended to the alias.

order_by

$rs->order_by('col1')                    # col1 ASC
$rs->order_by('!col1')                   # col1 DESC
$rs->order_by('col1,!col2')              # col1 ASC, col2 DESC
$rs->order_by(qw(col1 !col2))            # same
$rs->order_by(-desc => 'col1')           # col1 DESC
$rs->order_by(-asc => 'col1,col2')       # col1 ASC, col2 ASC
$rs->order_by(-desc => [qw(col1 col2)])  # col1 DESC, col2 DESC
$rs->order_by({ -desc => 'col' })
$rs->order_by({ -desc => 'col', -nulls => 'last' })  # col DESC NULLS LAST
$rs->order_by(\'name DESC')
$rs->order_by('col1', -desc => 'col2', { -asc => 'col3' })

Shortcut for ->search(undef, { order_by => ... }).

Takes a flat argument list whose elements may appear in any order and in any mixture; the resulting order of the ORDER BY clauses always follows the order of the arguments. Accepted forms:

  • Magic strings

    A bare string is a "magic string": a ! prefix means DESC (otherwise ASC), and commas separate multiple columns, so 'a,!b' means a ASC, b DESC.

  • Flat direction pairs

    -asc => $value and -desc => $value set the direction for $value explicitly. $value may be a single column, a comma separated list of columns (each of them gets the given direction), or an arrayref of either (one clause per element). The direction token is recognised strictly as -asc / -desc with a leading dash, so a real column named desc is still treated as a column. A direction without a following value (or with an undefined one) throws an exception. A $value that is a ref other than an arrayref is given the direction but is otherwise left untouched, so -desc => \'LENGTH(name)' orders by the literal descending.

  • Refs, at any position

    A ref means "the caller speaks native SQL::Abstract" and is passed on untouched: hashrefs are taken over as-is, literals (\'name DESC' and \['name = ? DESC', $bind]) are handed through verbatim. An arrayref is flattened one level, so that arguments following it append after its contents, but its elements are not parsed either - no magic string, no qualification, no direction detection inside a ref.

  • Position of NULLs

    A hashref may carry -nulls beside the direction, 'first' or 'last', and the clause renders as col DESC NULLS LAST. It is kept through every limit dialect, so it holds with rows/offset and with prefetch as well:

    $rs->order_by({ -desc => 'me.col', -nulls => 'last' })

Qualification rule: only what this method parses itself gets qualified with the current source alias, and that is exactly two things - magic strings at the top level, and the column names of top level -asc/-desc pairs (in both cases only when they do not already contain a dot, so 'rel.col' is left alone). Every ref at the top level belongs to SQL::Abstract and is never touched, its elements included. Note the deliberate asymmetry: an arrayref as an argument is native syntax and stays unqualified, while an arrayref as the value of a direction pair is this method's own sugar and is qualified:

$rs->order_by([qw(a b)])       # ORDER BY a, b
$rs->order_by(-asc => [qw(a b)])  # ORDER BY me.a ASC, me.b ASC

The pass-through is the escape hatch for ordering by an output alias introduced via +select/-as: pass it inside a ref and no me. prefix is added.

A single argument that is a ref is handed to the order_by attribute verbatim rather than inside a list; the resulting SQL is the same either way.

columns

$rs->columns([qw/ col1 col2 /])

Shortcut for ->search(undef, { columns => ... }).

add_columns

$rs->add_columns([qw/ extra_col1 extra_col2 /])

Shortcut for ->search(undef, { '+columns' => ... }).

remove_columns

$rs->remove_columns([qw/ unwanted_col /])

Shortcut for ->search(undef, { remove_columns => ... }).

distinct

$rs->distinct
$rs->distinct(1)

Shortcut for ->search(undef, { distinct => 1 }).

group_by

$rs->group_by([qw/ col1 col2 /])

Shortcut for ->search(undef, { group_by => ... }).

where

$rs->where(col1 => 'value')          # flat list -> { col1 => 'value' }
$rs->where({ -or => [ ... ] })       # single ref passed through verbatim
$rs->where([ { a => 1 }, { b => 2 } ])
$rs->where(\'raw sql')
$rs->where()                         # no-op clone
$rs->where('a')                      # throws: odd number of elements

Condition-only chaining shortcut for "search_rs": always returns a new DBIO::ResultSet, in every context. Takes either a flat key/value list (which is collapsed into a condition hashref), or a single condition ref (hashref/arrayref/literal, handed to the where attribute untouched). A flat list with an odd number of elements throws, as does an empty one-element call like where('a') -- both are unambiguous mistakes. No attributes are accepted here: they belong to "order_by", "group_by", "columns", "rows" and "offset". Repeated where() calls stack (AND semantics), exactly like repeated "search" conditions.

See ADR 0042 for the chaining contract.

having

$rs->having(cnt => { '>' => 3 })     # flat list -> { cnt => { '>' => 3 } }
$rs->having({ cnt => { '>' => 3 } }) # single ref passed through verbatim

Condition-only chaining shortcut for the having search attribute, with the same flat-list-or-single-ref signature as "where" (odd flat lists throw). Always returns a new DBIO::ResultSet; repeated having() calls stack like "where".

offset

$rs->offset(20)

Shortcut for ->search(undef, { offset => ... }). Always returns a new DBIO::ResultSet.

join

$rs->join('artist')

Shortcut for ->search(undef, { join => ... }). Always returns a new DBIO::ResultSet. The join type comes from the relationship's join_type attribute, exactly as it does for a plain search(undef, { join => ... }): has_many-style relationships default to LEFT JOIN, belongs_to defaults to INNER JOIN. There is deliberately no left_join helper: the relationship declares the join type, and a belongs_to that should join left does so via its own join_type attribute.

select

$rs->select(\@cols, [\@as])
$rs->select(\@cols)->as(\@aliases)
$rs->select('me.cdid')
$rs->select()                         # no-op clone

Shortcut for ->search(undef, { select => ..., (as => ...) }). Always returns a new DBIO::ResultSet. The select list may be an arrayref, a single scalar, or any other select structure SQL::Abstract understands. An optional second argument supplies the alias list together with the select list (the select/as pair must always be given together in a single search call -- see "as"). Calling select() with no arguments returns a clone without changing the projection -- consistent with "where" and "having" (ADR 0042).

as

$rs->select(\@cols)->as(\@aliases)

Sets the alias list for a "select" already applied to the resultset. Always returns a new DBIO::ResultSet. Because the select/as pair is normalized together (search_rs throws when as is supplied without a matching select), as() re-applies the current select alongside the new aliases rather than setting as alone; calling it without a preceding "select" throws.

hri

$rs->hri

Shortcut to set result_class to DBIO::ResultClass::HashRefInflator.

rows

$rs->rows(10)

Shortcut for ->search(undef, { rows => ... }).

limit

$rs->limit(10)

Alias for "rows".

has_rows

if ($rs->has_rows) { ... }

Lightweight check if the ResultSet contains any data, without counting all rows.

limited_page

$rs->limited_page(2, 10)
$rs->limited_page({ page => 2, rows => 10 })

Combined "page" + "rows" shortcut.

prefetch

$rs->prefetch('rel')
$rs->prefetch('rel1', 'rel2')

Shortcut for ->search(undef, { prefetch => ... }).

null

$rs->null('col')
$rs->null([qw/ col1 col2 /])
$rs->null('.col')

Search for rows where the given column(s) are NULL.

A column name starting with . is qualified with the ResultSet's "current_source_alias", so '.col' becomes 'me.col'. Use it to stay unambiguous when a join brings in a second column of the same name.

not_null

$rs->not_null('col')
$rs->not_null([qw/ col1 col2 /])

Search for rows where the given column(s) are NOT NULL. The . prefix described under "null" works here too.

like

$rs->like('col', '%pattern%')
$rs->like([qw/ col1 col2 /], '%pattern%')

Search for rows where the given column(s) match the LIKE pattern. The . prefix described under "null" works here too. Anything other than a plain column name or an ARRAY reference of them throws.

not_like

$rs->not_like('col', '%pattern%')
$rs->not_like([qw/ col1 col2 /], '%pattern%')

Search for rows where the given column(s) do NOT match the LIKE pattern. Same . prefix and same argument check as "like".

results_exist

my $bool = $rs->results_exist
my $bool = $rs->results_exist(\%cond)

Uses SQL EXISTS to efficiently check if the query would return any rows. Much lighter than $rs->count.

The statement executed is the one the storage's "_exists_value_sql" in DBIO::Storage::DBI hook renders -- a complete statement yielding 1 or 0, in the base rendering SELECT CASE WHEN EXISTS <inner> THEN 1 ELSE 0 END -- and it is only issued when the storage declares the supports_exists_query capability (ADR 0037). On an engine whose driver has not declared it, this throws instead of sending SQL the engine may not parse; it never degrades silently to a different query shape. The portable alternative is "has_rows", which works everywhere:

$storage->_use_supports_exists_query ? $rs->results_exist : $rs->has_rows

results_exist_async

my $future = $rs->results_exist_async
my $future = $rs->results_exist_async(\%cond)
$future->then(sub { my $bool = shift; ... });

Async variant of "results_exist", resolving the identical statement: the one "_exists_value_sql" in DBIO::Storage::DBI renders. That statement is complete (its base form has no FROM at all), so it carries no ident / select / where to hand to the CRUD entry points; it goes to the transport's raw-SQL seam _query_async, the same seam "_execute_ddl_async" in DBIO::Storage::Async uses for statements core has rendered itself.

The capability check is a static precondition, decidable without I/O, so it throws synchronously here rather than failing the Future -- exactly as _rs_run_async already does for "not an async connection" (ADR 0037 section 4).

results_exist_as_query

my $query = $rs->results_exist_as_query
my $query = $rs->results_exist_as_query(\%cond)

Returns a subquery reference that evaluates to a boolean via SQL EXISTS. Useful for correlated subqueries.

The fragment is the storage's own rendering (see "results_exist") in a pair of parentheses -- a parenthesised scalar subquery is universal, and that is the entire decoration core adds. It is gated by the same supports_exists_query capability, and throws for the same reason: the fragment would otherwise be embedded in a larger query and fail far from its cause.

correlate

$rs->correlate('relationship')

Returns a ResultSet for the given relationship that is correlated to the current ResultSet, suitable for use in subqueries.

# count books per author
$author_rs->search(undef, {
  '+columns' => {
    book_count => $author_rs->correlate('books')->count_rs->as_query
  }
});

search_or

$rs->search_or([ $rs->method1, $rs->method2 ])

Combine multiple ResultSet searches with OR logic. All ResultSets must share the same result source.

The methods in the arrayref are called in list context. One that ends in "search" throws there (ADR 0042); it has to return its resultset through "search_rs".

union

$rs1->union($rs2)
$rs1->union([$rs2, $rs3])

union_all

intersect

intersect_all

except

except_all

SQL set operations on ResultSets. All operands must select the same columns and use the same result class.

All six take their keyword from the storage's "_set_operation_keyword" in DBIO::Storage::DBI hook, which is asked once per call with the canonical operation name -- the method's own name (ADR 0039). The base implementation answers with the ANSI spelling of each, so nothing changes for an engine that has them all; DBIO::Oracle::Storage answers MINUS for "except". Building the query therefore needs no database connection.

The same hook is where an engine refuses an operation it does not have (Oracle has no MINUS ALL before 21c; SQLite and SQL Server have no EXCEPT ALL / INTERSECT ALL). The keyword is resolved before any operand SQL is assembled, so a refusing engine throws at query-build time and never hands back a fragment that could be embedded in a larger query -- the same argument ADR 0037 section 4 made for "results_exist_as_query".

The operands must be bound alike (ADR 0045): all of them unbound, or all of them bound with "with_context" to the same context -- the same object, not an equal one. The result carries that binding. A mixture is refused when the operation is built, before any operand is rendered: an unbound resultset with a bound operand, a bound one with an unbound operand, and operands bound to two different contexts (two transactions) alike. The exception names the operation and the operands that differ from the one the operation is called on, which is operand 0:

union: the operands of a set operation must be bound alike, all unbound or
all to the same async context -- operand 0 (the caller) is unbound,
operand 2 is bound to DBIO::Storage::Async::...=HASH(0x...)

The rule is about the binding alone and holds for the result's synchronous use as well. A statement cannot run partly inside a transaction and partly outside it; bind every operand to the context, or none.

one_row

my $result = $rs->one_row;
my $result = $rs->one_row(\%cond);

Returns a single result object, or undef if there are none. Applies a rows => 1 limit and returns "next", so only one row is requested from the database rather than the whole set being pulled into a cursor first. An optional search condition is applied before fetching.

no_columns

my $rs = $rs->no_columns;

Returns a new resultset with the column selection cleared (columns => []), as a starting point for a custom projection built up with +columns or +select.

bare

my $rs = $rs->bare;

Returns a fresh, unsearched resultset for the same result source, discarding any search conditions and attributes accumulated on the current resultset. A binding made with "with_context" is neither and stays.

rand

my $rs = $rs->rand;       # one random row
my $rs = $rs->rand($n);   # $n random rows

Returns a resultset ordered by the storage's random function ("_random_function" in DBIO::Storage::DBI) and limited to $amount rows (default 1). $amount must be a positive integer, otherwise an exception is thrown.

explain

my $plan = $rs->explain;

Returns the database query plan for this resultset's query. The whole operation is delegated to the storage's "_explain_query" in DBIO::Storage::DBI hook; the return value is the plan as an arrayref of row arrayrefs, whose columns are engine-specific.

ATTRIBUTES

Attributes are used to refine a ResultSet in various ways when searching for data. They can be passed to any method which takes an \%attrs argument. See "search", "search_rs", "find", "count".

Default attributes can be set on the result class using "resultset_attributes" in DBIO::ResultSource. (Please read the CAVEATS on that feature before using it!)

These are in no particular order:

order_by

Value: ( $order_by | \@order_by | \%order_by )

Which column(s) to order the results by.

[The full list of suitable values is documented in "ORDER BY CLAUSES" in SQL::Abstract; the following is a summary of common options.]

If a single column name, or an arrayref of names is supplied, the argument is passed through directly to SQL. The hashref syntax allows for connection-agnostic specification of ordering direction:

For descending order:

 order_by => { -desc => [qw/col1 col2 col3/] }

For explicit ascending order:

 order_by => { -asc => 'col' }

For a chosen position of the NULLs ('first' or 'last'):

 order_by => { -desc => 'col', -nulls => 'last' }  # col DESC NULLS LAST

The old scalarref syntax (i.e. order_by => \'year DESC') is still supported, although you are strongly encouraged to use the hashref syntax as outlined above.

columns

Value: \@columns | \%columns | $column

Shortcut to request a particular set of columns to be retrieved. Each column spec may be a string (a table column name), or a hash (in which case the key is the as value, and the value is used as the select expression). Adds the "current_source_alias" onto the start of any column without a . in it and sets select from that, then auto-populates as from select as normal. (You may also use the cols attribute, as in earlier versions of DBIx::Class, but this is deprecated)

Essentially columns does the same as "select" and "as".

columns => [ 'some_column', { dbic_slot => 'another_column' } ]

is the same as

select => [qw(some_column another_column)],
as     => [qw(some_column dbic_slot)]

If you want to individually retrieve related columns (in essence perform manual "prefetch") you have to make sure to specify the correct inflation slot chain such that it matches existing relationships:

my $rs = $schema->resultset('Artist')->search({}, {
    # required to tell DBIO to collapse has_many relationships
    collapse => 1,
    join     => { cds => 'tracks' },
    '+columns'  => {
      'cds.cdid'         => 'cds.cdid',
      'cds.tracks.title' => 'tracks.title',
    },
});

Like elsewhere, literal SQL or literal values can be included by using a scalar reference or a literal bind value, and these values will be available in the result with get_column (see also "Literal SQL and value type operators" in SQL::Abstract):

# equivalent SQL: SELECT 1, 'a string', IF(my_column,?,?) ...
# bind values: $true_value, $false_value
columns => [
    {
        foo => \1,
        bar => \q{'a string'},
        baz => \[ 'IF(my_column,?,?)', $true_value, $false_value ],
    }
]

+columns

NOTE: You MUST explicitly quote '+columns' when using this attribute. Not doing so causes Perl to incorrectly interpret +columns as a bareword with a unary plus operator before it, which is the same as simply columns.

Value: \@extra_columns

Indicates additional columns to be selected from storage. Works the same as "columns" but adds columns to the current selection. (You may also use the include_columns attribute, as in earlier versions of DBIx::Class, but this is deprecated)

$schema->resultset('CD')->search(undef, {
  '+columns' => ['artist.name'],
  join => ['artist']
});

would return all CDs and include a 'name' column to the information passed to object inflation. Note that the 'artist' is the name of the column (or relationship) accessor, and 'name' is the name of the column accessor in the related table.

remove_columns

Value: \@columns

Selects every column of the result source except the listed ones. The list is resolved when the query is built, so a "columns" or "select" anywhere in the chain wins and the attribute is then ignored; "+columns" and "+select" add to what is left. A later remove_columns replaces an earlier one, the two lists are not combined.

# every column of 'artist' but 'rank'
my $rs = $schema->resultset('Artist')->search(undef, { remove_columns => ['rank'] });

select

Value: \@select_columns

Indicates which columns should be selected from the storage. You can use column names, or in the case of RDBMS back ends, function or stored procedure names:

$rs = $schema->resultset('Employee')->search(undef, {
  select => [
    'name',
    { count => 'employeeid' },
    { max => { length => 'name' }, -as => 'longest_name' }
  ]
});

# Equivalent SQL
SELECT name, COUNT( employeeid ), MAX( LENGTH( name ) ) AS longest_name FROM employee

NOTE: You will almost always need a corresponding "as" attribute when you use "select", to instruct DBIO how to store the result of the column.

Also note that the "as" attribute has nothing to do with the SQL-side AS identifier aliasing. You can alias a function (so you can use it e.g. in an ORDER BY clause), however this is done via the -as select function attribute supplied as shown in the example above.

+select

NOTE: You MUST explicitly quote '+select' when using this attribute. Not doing so causes Perl to incorrectly interpret +select as a bareword with a unary plus operator before it, which is the same as simply select.

Value: \@extra_select_columns

Indicates additional columns to be selected from storage. Works the same as "select" but adds columns to the current selection, instead of specifying a new explicit list.

as

Value: \@inflation_names

Indicates DBIO-side names for object inflation. That is "as" indicates the slot name in which the column value will be stored within the Row object. The value will then be accessible via this identifier by the get_column method (or via the object accessor if one with the same name already exists) as shown below.

The "as" attribute has nothing to do with the SQL-side identifier aliasing AS. See "select" for details.

$rs = $schema->resultset('Employee')->search(undef, {
  select => [
    'name',
    { count => 'employeeid' },
    { max => { length => 'name' }, -as => 'longest_name' }
  ],
  as => [qw/
    name
    employee_count
    max_name_length
  /],
});

If the object against which the search is performed already has an accessor matching a column name specified in as, the value can be retrieved using the accessor as normal:

my $name = $employee->name();

If on the other hand an accessor does not exist in the object, you need to use get_column instead:

my $employee_count = $employee->get_column('employee_count');

You can create your own accessors if required - see DBIO::Manual::Cookbook for details.

+as

NOTE: You MUST explicitly quote '+as' when using this attribute. Not doing so causes Perl to incorrectly interpret +as as a bareword with a unary plus operator before it, which is the same as simply as.

Value: \@extra_inflation_names

Indicates additional inflation names for selectors added via "+select". See "as".

join

Value: ($rel_name | \@rel_names | \%rel_names)

Contains a list of relationships that should be joined for this query.

Note: In most cases you do not need to specify join explicitly. DBIO automatically discovers required joins from dotted column names in your search conditions. For example, 'artist.name' => 'Nine Inch Nails' will automatically add the artist join. You only need explicit join when referencing relationships in select, order_by, or other attributes that are not part of the WHERE/HAVING conditions.

# Automatic -- join is discovered from the condition:
my $rs = $schema->resultset('CD')->search(
  { 'artist.name' => 'Nine Inch Nails' }
);

# Equivalent explicit form (still supported):
my $rs = $schema->resultset('CD')->search(
  { 'artist.name' => 'Nine Inch Nails' },
  { join => 'artist' }
);

Can also contain a hash reference to refer to the other relation's relations. For example:

package MyApp::Schema::Track;
use base qw/DBIO/;
__PACKAGE__->table('track');
__PACKAGE__->add_columns(qw/trackid cd position title/);
__PACKAGE__->set_primary_key('trackid');
__PACKAGE__->belongs_to(cd => 'MyApp::Schema::CD');
1;

# In your application
my $rs = $schema->resultset('Artist')->search(
  { 'track.title' => 'Teardrop' },
  {
    join     => { cd => 'track' },
    order_by => 'artist.name',
  }
);

You need to use the relationship (not the table) name in conditions, because they are aliased as such. The current table is aliased as "me", so you need to use me.column_name in order to avoid ambiguity. For example:

# Get CDs from 1984 with a 'Foo' track
my $rs = $schema->resultset('CD')->search(
  {
    'me.year' => 1984,
    'tracks.name' => 'Foo'
  },
  { join => 'tracks' }
);

If the same join is supplied twice, it will be aliased to <rel>_2 (and similarly for a third time). For e.g.

my $rs = $schema->resultset('Artist')->search({
  'cds.title'   => 'Down to Earth',
  'cds_2.title' => 'Popular',
}, {
  join => [ qw/cds cds/ ],
});

will return a set of all artists that have both a cd with title 'Down to Earth' and a cd with title 'Popular'.

If you want to fetch related objects from other tables as well, see "prefetch" below.

NOTE: An internal join-chain pruner will discard certain joins while
constructing the actual SQL query, as long as the joins in question do not
affect the retrieved result. This for example includes 1:1 left joins
that are not part of the restriction specification (WHERE/HAVING) nor are
a part of the query selection.

For more help on using joins with search, see DBIO::Manual::Joining.

collapse

Value: (0 | 1)

When set to a true value, indicates that any rows fetched from joined has_many relationships are to be aggregated into the corresponding "parent" object. For example, the resultset:

my $rs = $schema->resultset('CD')->search({}, {
  '+columns' => [ qw/ tracks.title tracks.position / ],
  join => 'tracks',
  collapse => 1,
});

While executing the following query:

SELECT me.*, tracks.title, tracks.position
  FROM cd me
  LEFT JOIN track tracks
    ON tracks.cdid = me.cdid

Will return only as many objects as there are rows in the CD source, even though the result of the query may span many rows. Each of these CD objects will in turn have multiple "Track" objects hidden behind the has_many generated accessor tracks. Without collapse => 1, the return values of this resultset would be as many CD objects as there are tracks (a "Cartesian product"), with each CD object containing exactly one of all fetched Track data.

When a collapse is requested on a non-ordered resultset, an order by some unique part of the main source (the left-most table) is inserted automatically. This is done so that the resultset is allowed to be "lazy" - calling $rs->next will fetch only as many rows as it needs to build the next object with all of its related data.

If an "order_by" is already declared, and orders the resultset in a way that makes collapsing as described above impossible (e.g. ORDER BY has_many_rel.column or ORDER BY RANDOM()), DBIO will automatically switch to "eager" mode and slurp the entire resultset before constructing the first object returned by "next".

Setting this attribute on a resultset that does not join any has_many relations is a no-op.

The *_async read methods (see "ASYNC") honour collapse with full parity to their synchronous counterparts.

For a more in-depth discussion, see "PREFETCHING".

prefetch

Value: ($rel_name | \@rel_names | \%rel_names)

This attribute is a shorthand for specifying a "join" spec, adding all columns from the joined related sources as "+columns" and setting "collapse" to a true value. It can be thought of as a rough superset of the "join" attribute.

For example, the following two queries are equivalent:

my $rs = $schema->resultset('Artist')->search({}, {
  prefetch => { cds => ['genre', 'tracks' ] },
});

and

my $rs = $schema->resultset('Artist')->search({}, {
  join => { cds => ['genre', 'tracks' ] },
  collapse => 1,
  '+columns' => [
    (map
      { +{ "cds.$_" => "cds.$_" } }
      $schema->source('Artist')->related_source('cds')->columns
    ),
    (map
      { +{ "cds.genre.$_" => "genre.$_" } }
      $schema->source('Artist')->related_source('cds')->related_source('genre')->columns
    ),
    (map
      { +{ "cds.tracks.$_" => "tracks.$_" } }
      $schema->source('Artist')->related_source('cds')->related_source('tracks')->columns
    ),
  ],
});

Both producing the following SQL:

SELECT  me.artistid, me.name, me.rank, me.charfield,
        cds.cdid, cds.artist, cds.title, cds.year, cds.genreid, cds.single_track,
        genre.genreid, genre.name,
        tracks.trackid, tracks.cd, tracks.position, tracks.title, tracks.last_updated_on, tracks.last_updated_at
  FROM artist me
  LEFT JOIN cd cds
    ON cds.artist = me.artistid
  LEFT JOIN genre genre
    ON genre.genreid = cds.genreid
  LEFT JOIN track tracks
    ON tracks.cd = cds.cdid
ORDER BY me.artistid

While "prefetch" implies a "join", it is ok to mix the two together, as the arguments are properly merged and generally do the right thing. For example, you may want to do the following:

my $artists_and_cds_without_genre = $schema->resultset('Artist')->search(
  { 'genre.genreid' => undef },
  {
    join => { cds => 'genre' },
    prefetch => 'cds',
  }
);

Which generates the following SQL:

SELECT  me.artistid, me.name, me.rank, me.charfield,
        cds.cdid, cds.artist, cds.title, cds.year, cds.genreid, cds.single_track
  FROM artist me
  LEFT JOIN cd cds
    ON cds.artist = me.artistid
  LEFT JOIN genre genre
    ON genre.genreid = cds.genreid
WHERE genre.genreid IS NULL
ORDER BY me.artistid

The *_async read methods (see "ASYNC") inflate prefetched relations with full parity to their synchronous counterparts.

For a more in-depth discussion, see "PREFETCHING".

alias

Value: $source_alias

Sets the source alias for the query. Normally, this defaults to me, but nested search queries (sub-SELECTs) might need specific aliases set to reference inner queries. For example:

my $q = $rs
   ->related_resultset('CDs')
   ->related_resultset('Tracks')
   ->search({
      'track.id' => { -ident => 'none_search.id' },
   })
   ->as_query;

my $ids = $self->search({
   -not_exists => $q,
}, {
   alias    => 'none_search',
   group_by => 'none_search.id',
})->get_column('id')->as_query;

$self->search({ id => { -in => $ids } })

This attribute is directly tied to "current_source_alias".

page

Value: $page

Makes the resultset paged and specifies the page to retrieve. Effectively identical to creating a non-pages resultset and then calling ->page($page) on it.

If "rows" attribute is not specified it defaults to 10 rows per page.

When you have a paged resultset, "count" will only return the number of rows in the page. To get the total, use the "pager" and call total_entries on it.

rows

Value: $rows

Specifies the maximum number of rows for direct retrieval or the number of rows per page if the "page" attribute or method is used.

See also "offset", "page" and "software_limit".

offset

Value: $offset

Specifies the (zero-based) row number of the first row to be returned, or of the first row of the first page if paging is used.

See also "rows", "page" and "software_limit".

software_limit

Value: (0 | 1)

When combined with "rows" and/or "offset" the generated SQL will not include any limit dialect stanzas. Instead the entire result will be selected as if no limits were specified, and DBIO will perform the limit locally, by artificially advancing and finishing the resulting "cursor".

This is the recommended way of performing resultset limiting when no sane RDBMS implementation is available (e.g. Sybase ASE using the Generic Sub Query hack)

group_by

Value: \@columns

An arrayref of columns to group by. Can include columns of joined tables.

group_by => [qw/ column1 column2 ... /]

See also "having" for post-grouping constraints, and "distinct", which generates a group_by automatically from the selection.

having

Value: $condition

The HAVING operator specifies a secondary condition applied to the set after the grouping calculations have been done. In other words it is a constraint just like "where" (and accepting the same SQL::Abstract syntax) applied to the data as it exists after GROUP BY has taken place. Specifying "having" without "group_by" is a logical mistake, and a fatal error on most RDBMS engines.

E.g.

having => { 'count_employee' => { '>=', 100 } }

or with an in-place function in which case literal SQL is required:

having => \[ 'count(employee) >= ?', 100 ]

distinct

Value: (0 | 1)

Set to 1 to automatically generate a "group_by" clause based on the selection (including intelligent handling of "order_by" contents). Note that the group criteria calculation takes place over the final selection. This includes any "+columns", "+select" or "order_by" additions in subsequent "search" calls, and standalone columns selected via DBIO::ResultSetColumn ("get_column"). A notable exception are the extra selections specified via "prefetch" - such selections are explicitly excluded from group criteria calculations.

If the final ResultSet also explicitly defines a "group_by" attribute, this setting is ignored and an appropriate warning is issued.

where

Value: $condition

Adds to the WHERE clause, accepting the same SQL::Abstract syntax as the condition passed to "search". Most useful as a default set on the result class, so that every search from that source carries the restriction:

# only return rows WHERE deleted IS NULL for all searches
__PACKAGE__->resultset_attributes({ where => { deleted => undef } });

A where set this way cannot be taken off again by a later search: every further condition, a where attribute included, is ANDed onto it, and { where => undef } leaves it in place. See also "having" for a constraint applied after "group_by".

cache

Value: (0 | 1)

Set to 1 to cache search results. This prevents extra SQL queries if you revisit rows in your ResultSet:

my $resultset = $schema->resultset('Artist')->search( undef, { cache => 1 } );

while( my $artist = $resultset->next ) {
  ... do stuff ...
}

$rs->first; # without cache, this would issue a query

By default, searches are not cached.

For more examples of using these attributes, see DBIO::Manual::Cookbook.

for

Value: ( 'update' | 'shared' | \$scalar )

Set to 'update' for a SELECT ... FOR UPDATE or 'shared' for a SELECT ... FOR SHARE. If \$scalar is passed, its content is appended after the keyword FOR: for => \'UPDATE SKIP LOCKED' renders FOR UPDATE SKIP LOCKED.

PREFETCHING

DBIO supports arbitrary related data prefetching from multiple related sources. Any combination of relationship types and column sets are supported. If collapsing is requested, there is an additional requirement of selecting enough data to make every individual object uniquely identifiable.

Here are some more involved examples, based on the following relationship map:

# Assuming:
My::Schema::CD->belongs_to( artist      => 'My::Schema::Artist'     );
My::Schema::CD->might_have( liner_note  => 'My::Schema::LinerNotes' );
My::Schema::CD->has_many(   tracks      => 'My::Schema::Track'      );

My::Schema::Artist->belongs_to( record_label => 'My::Schema::RecordLabel' );

My::Schema::Track->has_many( guests => 'My::Schema::Guest' );



my $rs = $schema->resultset('Tag')->search(
  undef,
  {
    prefetch => {
      cd => 'artist'
    }
  }
);

The initial search results in SQL like the following:

SELECT tag.*, cd.*, artist.* FROM tag
JOIN cd ON tag.cd = cd.cdid
JOIN artist ON cd.artist = artist.artistid

DBIO has no need to go back to the database when we access the cd or artist relationships, which saves us two SQL statements in this case.

Simple prefetches will be joined automatically, so there is no need for a join attribute in the above search.

The "prefetch" attribute can be used with any of the relationship types and multiple prefetches can be specified together. Below is a more complex example that prefetches a CD's artist, its liner notes (if present), the cover image, the tracks on that CD, and the guests on those tracks.

my $rs = $schema->resultset('CD')->search(
  undef,
  {
    prefetch => [
      { artist => 'record_label'},  # belongs_to => belongs_to
      'liner_note',                 # might_have
      'cover_image',                # has_one
      { tracks => 'guests' },       # has_many => has_many
    ]
  }
);

This will produce SQL like the following:

SELECT cd.*, artist.*, record_label.*, liner_note.*, cover_image.*,
       tracks.*, guests.*
  FROM cd me
  JOIN artist artist
    ON artist.artistid = me.artistid
  JOIN record_label record_label
    ON record_label.labelid = artist.labelid
  LEFT JOIN track tracks
    ON tracks.cdid = me.cdid
  LEFT JOIN guest guests
    ON guests.trackid = track.trackid
  LEFT JOIN liner_notes liner_note
    ON liner_note.cdid = me.cdid
  JOIN cd_artwork cover_image
    ON cover_image.cdid = me.cdid
ORDER BY tracks.cd

Now the artist, record_label, liner_note, cover_image, tracks, and guests of the CD will all be available through the relationship accessors without the need for additional queries to the database.

CAVEATS

Prefetch does a lot of deep magic. As such, it may not behave exactly as you might expect.

  • Prefetch uses the "cache" to populate the prefetched relationships. This may or may not be what you want.

  • If you specify a condition on a prefetched relationship, ONLY those rows that match the prefetched condition will be fetched into that relationship. This means that adding prefetch to a search() may alter what is returned by traversing a relationship. So, if you have Artist->has_many(CDs) and you do

    my $artist_rs = $schema->resultset('Artist')->search({
        'cds.year' => 2008,
    }, {
        join => 'cds',
    });
    
    my $count = $artist_rs->first->cds->count;
    
    my $artist_rs_prefetch = $artist_rs->search( {}, { prefetch => 'cds' } );
    
    my $prefetch_count = $artist_rs_prefetch->first->cds->count;
    
    cmp_ok( $count, '==', $prefetch_count, "Counts should be the same" );

    That cmp_ok() may or may not pass depending on the datasets involved. In other words the WHERE condition would apply to the entire dataset, just like it would in regular SQL. If you want to add a condition only to the "right side" of a LEFT JOIN - consider declaring and using a relationship with a custom condition

DBIO BIND VALUES

Because DBIO may need more information to bind values than just the column name and value itself, it uses a special format for both passing and receiving bind values. Each bind value should be composed of an arrayref of [ \%args => $val ]. The format of \%args is currently:

dbd_attrs

If present (in any form), this is what is being passed directly to bind_param. Note that different DBD's expect different bind args. (e.g. DBD::SQLite takes a single numerical type, while DBD::Pg takes a hashref if bind options.)

If this is specified, all other bind options described below are ignored.

sqlt_datatype

If present, this is used to infer the actual bind attribute by passing to $resolved_storage->bind_attribute_by_data_type(). Defaults to the "data_type" from the add_columns column info.

Note that the data type is somewhat freeform (hence the sqlt_ prefix); currently drivers are expected to "Do the Right Thing" when given a common datatype name. (Not ideal, but that's what we got at this point.)

sqlt_size

Currently used to correctly allocate buffers for bind_param_inout(). Defaults to "size" from the add_columns column info, or to a sensible value based on the "data_type".

dbic_colname

Used to fill in missing sqlt_datatype and sqlt_size attributes (if they are explicitly specified they are never overridden). Also used by some weird DBDs, where the column name should be available at bind_param time (e.g. Oracle).

For backwards compatibility and convenience, the following shortcuts are supported:

[ $name => $val ] === [ { dbic_colname => $name }, $val ]
[ \$dt  => $val ] === [ { sqlt_datatype => $dt }, $val ]
[ undef,   $val ] === [ {}, $val ]
$val              === [ {}, $val ]

AUTHOR

DBIO & DBIx::Class Authors

COPYRIGHT AND LICENSE

Copyright (C) 2026 DBIO Authors Portions Copyright (C) 2005-2025 DBIx::Class Authors Based on DBIx::Class, heavily modified.

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.