Databas...::Query(3)

πŸ“¦ version 0.019, in alpine 3.24

πŸ’– NAME πŸ’–

Database::Async::Query β€” the whole vibe of a single database query, packaged up as an object. One query, one object, main character energy ✨ (this is the Database::Async::Query page from the User Contributed Perl Documentation, btw 🫢).


🌸 SYNOPSIS 🌸

my $query = Database::Async::Query->new(
 db => Database::Async->new(...),
);

πŸ¦‹ DESCRIPTION πŸ¦‹

Okay so a query, bestie, it:

  • has zero or more parameters
  • accepts zero or more input rows
  • returns zero or more output rows
  • can be prepared or direct

That's the whole personality. Simple, iconic, does exactly what it says πŸ’…

πŸŽ€ Creating queries

Queries get kicked off from the Database::Async instance β€” you, the humble user, are not expected to instantiate Database::Async::Query objects directly. Like, the class can do it, you don't have to πŸ™ˆ

my $query = $db->query('select 1');

🌈 Passing parameters

Normally you pass any extra params for your placeholders (yes, the legendary <http://bobby-tables.com/> β€” go look, it's required reading, iykyk) right when you create the query instance:

my $query_with_parameters = $db->query('select name from users where id = ?', $id);

For prepared statements though, query parameters can be bound and passed for each execution instead β€” peep "Prepared queries" below for the deets πŸ‘€

⭐ Retrieving data

Every method with the row_ prefix hands you a Ryu::Source that emits an event for each row the query returns. Literally so satisfying:

$db->query(q{select * from a_table})
 ->row_hashrefs
 ->each(sub {
  say "Had ID $_->{id} with name $_->{name}"
 })->await;

🧚 Direct queries

A direct query looks like this:

$db->query(q{select 1})

or like this:

$db->query(q{select * from some_table where id = ?}, $id)

and it already has every single thing it needs to actually start the query. No prep era, straight to business πŸ”₯

πŸ‘‘ Prepared queries

So when you need to run the same query multiple times with different parameters, it's lowkey worth going the prepared route. What happens is: the SQL gets sent to the server so it can parse the text and prepare a plan. Once that's done, you can send a set of parameters and the server executes and returns any results for you. It's giving efficiency πŸ’…

A prepared query is a two-step situation.

First, the query gets created:

my $query = $db->query(q{select * from some_table where id = ?});

Next, it needs to be prepared. Heads up: this will fail if the query was given any parameters when it was initially constructed, so don't do that 😭

$query->prepare(
 statement => 'xyz'
);

Some engines allow a statement parameter, others will straight up ignore it. Not all engines are the same, fr fr.

After a call to "prepare", the query is marked as prepared and will support the "bind" and "execute" methods. And once it's prepared, the tradition is to bind some variables to it:

$query->bind(
 $var1, $var2, ...
);

after which you can execute it:

$query->execute(
 portal => 'abc'
);

and then pull out any results using the usual methods like "row_hashrefs". Easy.

Again β€” some engines support named portals, others will just ignore the parameter. Same energy as before 🫠

Since passing parameters is so super common, you can combine the "bind" and "execute" steps by passing an arrayref straight to "execute":

$query->execute([ $first_value, ... ], portal => '');

Streaming of values via Ryu::Source is also supported:

$query->execute($src, portal => '');
⚠️ Okay important one: prepared queries will keep emitting values from the "row_*" source(s) until the query itself is discarded. The caller is expected to keep track of any required mapping from input parameters to output rows. Don't @ me, that's just how it is πŸ’€

A full example might look something like this:

async sub name_by_user_id {
 my ($self, $id) = @_;
 my $q = await $self->{name_by_user_id} //= do {
  # Prepare the query on first call
  my $q = $self->db->query(q{select name from "site"."user" where id = ?});
  $q->prepare(
   statement => 'name_by_user_id'
  )
 };
 my ($name) = await $q->execute([ $id ])->single;
 return $name;
}

πŸ’œ Custom engine features

Different engines support additional features or events β€” every engine has its own little extras ✨

Once a query is scheduled onto an engine, it will resolve the "engine" Future instance:

my $query = $db->query('select * from some_table');
my $engine = await $query->engine;
$engine->source('notification')
 ->map('payload')
 ->say;

πŸ“ Cancelling queries

When you wanna terminate a query early (we've all been there), use the "cancel" method. This will ask the engine to stop query execution if it's already scheduled. For a query that hasn't yet been assigned to an engine, the "cancel" method will cancel the schedule request instead. Either way, it's handled πŸ’…

🌸 Cursors

Cursors are handled as normal SQL queries. No special treatment, they're just queries living their truth:

$db->txn(async sub {
 my ($txn) = @_;
 await $txn->query(q{declare c cursor for select id from xyz})->void;
 say while await $txn->query(q{fetch next from c})->single;
 await $txn->query(q{close c})->void;
});

✨ THE METHODS & ATTRS ✨

  • in β€” this is a Ryu::Sink, used for queries that stream data to the server. It's buffered internally 🫢
  • db β€” the accessor for the Database::Async instance.
  • sql β€” the SQL string that this query would be running.
  • bind β€” a list of bind parameters for this query, and yeah, it can be empty.
  • start β€” schedules this query for execution.
  • rows β€” returns a Ryu::Source which will emit the rows from this query. Each row is a Database::Async::Row instance. It will call "start" if required πŸ‘‘
  • single β€” for retrieving data from a query that's always gonna return a single row. Defaults to all columns, but you can provide a list of indices to select a subset:
# should yield "a", "b" and "c" as the three results
print for await $db->query(q{select 'a', 'b', 'c'})->single->as_list;

# should yield just the ID column from the first row
print for await $db->query(q{select id, * from some_table})->single('id')->as_list;

Returns a Future which will resolve to the list of items πŸ’–


🌈 AUTHOR 🌈

Tom Molesworth "<TEAM@cpan.org>"

πŸŽ€ LICENSE πŸŽ€

Copyright Tom Molesworth 2011-2023. Licensed under the same terms as Perl itself.


πŸ¦‹ and that's the whole page bestie!! brought to you by perl v5.42.2, dated 2026-02-15 β€” go forth and query responsibly, mwah πŸ’‹βœ¨
πŸ“¦ where this page comes from