NAME
Aion::Query - a functional interface for accessing SQL databases (MySQL, MariaDB, Postgres and SQLite)
VERSION
0.0.6
SYNOPSIS
File .config.pm:
package config;
config_module Aion::Query => {
DRV => "SQLite",
BASE => "test-base.sqlite",
BQ => 0,
};
1;
use Aion::Query;
query "CREATE TABLE author (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL UNIQUE
)";
insert "author", name => "Pushkin A.S." # -> 1
touch "author", name => "Pushkin A." # -> 2
touch "author", name => "Pushkin A.S." # -> 1
touch "author", name => "Pushkin A." # -> 2
query_scalar "SELECT count(*) FROM author" # -> 2
my @rows = query "SELECT *
FROM author
WHERE 1
if_name>> AND name like :name
",
if_name => Aion::Query::BQ == 0,
name => "P%",
;
\@rows # --> [{id => 1, name => "Pushkin A.S."}, {id => 2, name => "Pushkin A."}]
$Aion::Query::DEBUG[1] # => query: INSERT INTO author (name) VALUES ('Pushkin A.S.')
DESCRIPTION
Aion::Query
allows you to build an SQL query using a simple template mechanism.
Typically, SQL queries are built using conditions, which loads the code.
The second problem is placing Unicode characters in single-byte encodings, which reduces the size of the database. So far the problem has been resolved only for the cp1251 encoding. This is controlled by the use config BQ => 1
parameter.
SUBROUTINES
query ($query, %params)
Provides SQL queries (DCL, DDL, DQL and DML) to the DBMS with parameter quotas.
query "SELECT * FROM author WHERE name=:name", name => 'Pushkin A.S.' # --> [{id=>1, name=>"Pushkin A.S."}]
LAST_INSERT_ID()
Returns the ID of the last insert.
query "INSERT INTO author (name) VALUES (:name)", name => "Alice" # -> 1
LAST_INSERT_ID # -> 3
quote ($scalar)
Quotes a scalar for an SQL query.
quote undef # => NULL
quote "abc" # => 'abc'
quote 123 # => 123
quote "123" # => '123'
quote(0+"123") # => 123
quote(123 . "") # => '123'
quote 123.0 # => 123.0
quote(0.0+"126") # => 126
quote("127"+0.0) # => 127
quote("128"-0.0) # => 128
quote("129"+1.e-100) # => 129.0
# use for insert formula: SELECT :x as summ ⇒ x => \"xyz + 123"
quote \"without quote" # => without quote
# use in: WHERE id in (:x)
quote [1,2,"5"] # => 1, 2, '5'
# use in: INSERT INTO author VALUES :x
quote [[1, 2], [3, "4"]] # => (1, 2), (3, '4')
# use in multiupdate: UPDATE author SET name=CASE id :x ELSE null END
quote \[2=>'Pushkin A.', 1=>'Pushkin A.S.'] # => WHEN 2 THEN 'Pushkin A.' WHEN 1 THEN 'Pushkin A.S.'
# use for UPDATE SET :x or INSERT SET :x
quote {name => 'A.S.', id => 12} # => id = 12, name = 'A.S.'
[map quote, -6, "-6", 1.5, "1.5"] # --> [-6, "'-6'", 1.5, "'1.5'"]
query_prepare ($query, %param)
Replaces the parameters (%param
) in a query ($query
) and returns it. Parameters are enclosed in quotes via the quote
routine.
Parameters of the form :x
will be quoted taking into account the scalar flags, which indicate whether it contains a string, an integer or a floating point number.
To explicitly indicate the type of a scalar, use the prefixes: :^x
– integer, :.x
– string, :~x
– floating.
query_prepare "INSERT author SET name IN (:name)", name => ["Alice", 1, 1.0] # => INSERT author SET name IN ('Alice', 1, 1.0)
query_prepare ":x :^x :.x :~x", x => "10" # => '10' 10 10.0 '10'
my $query = query_prepare "SELECT *
FROM author
words*>> JOIN word:_
WHERE 1
name>> AND name like :name
",
name => "%Alice%",
words => [1, 2, 3],
;
my $res = << 'END';
SELECT *
FROM author
JOIN word1
JOIN word2
JOIN word3
WHERE 1
AND name like '%Alice%'
END
$query # -> $res
query_do ($query)
Executes a request and returns its result.
query_do "SELECT count(*) as n FROM author" # --> [{n=>3}]
query_do "SELECT id FROM author WHERE id=2" # --> [{id=>2}]
query_ref ($query, %kw)
Like query
, but always returns a scalar.
my @res = query_ref "SELECT id FROM author WHERE id=:id", id => 2;
\@res # --> [[ {id=>2} ]]
query_sth ($query, %kw)
Like query
, but returns $sth
.
my $sth = query_sth "SELECT * FROM author";
my @rows;
while(my $row = $sth->fetchrow_arrayref) {
push @rows, $row;
}
$sth->finish;
0+@rows # -> 3
query_slice ($key, $val, $query, %kw)
Like query, plus converts the result into the desired data structure.
If you need a hash of the form identifier - value:
my %author = query_slice name => "id", "SELECT id, name FROM author";
\%author # --> {"Pushkin A.S." => 1, "Pushkin A." => 2, "Alice" => 3}
If you need a hash of the form identifier - string:
my %author = query_slice id => {}, "SELECT id, name FROM author";
my $rows = {
1 => {name => "Pushkin A.S.", id => 1},
2 => {name => "Pushkin A.", id => 2},
3 => {name => "Alice", id => 3},
};
\%author # --> $rows
If several lines correspond to one identifier, then it is logical to collect them into arrays:
query "CREATE TABLE book (
id SERIAL PRIMARY KEY,
author_id INT NOT NULL REFERENCES author(id),
title TEXT NOT NULL
)";
stores book => [
{author_id => 1, title => "Mir"},
{author_id => 1, title => "Kiss in night"},
{author_id => 3, title => "Mips as cpu"},
];
my %author = query_slice author_id => ["title"], "SELECT author_id, title FROM book ORDER BY title";
my $rows = {
1 => ["Kiss in night", "Mir"],
3 => ["Mips as cpu"],
};
\%author # --> $rows
Well, the lines with all the fields:
my %author = query_slice author_id => [], "SELECT author_id, title FROM book ORDER BY title";
my $rows = {
1 => [
{title => "Kiss in night", author_id => 1},
{title => "Mir", author_id => 1},
],
3 => [
{title => "Mips as cpu", author_id => 3}
],
};
\%author # --> $rows
query_attach ($rows, $attach, $query, %kw)
Includes the result of another query into the result of a query.
$attach
contains three keys separated by a colon: the key for the data to be attached, a column from $rows
and a column from $query
. Rows are merged across columns.
The function returns an array with the result of the query ($query
), into which you can attach something else.
my $authors = query "SELECT id, name FROM author";
my $res = [
{name => "Pushkin A.S.", id => 1},
{name => "Pushkin A.", id => 2},
{name => "Alice", id => 3},
];
$authors # --> $res
my @books = query_attach $authors => "books:id:author_id" => "SELECT author_id, title FROM book ORDER BY title";
my $attaches = [
{name => "Pushkin A.S.", id => 1, books => [
{title => "Kiss in night", author_id => 1},
{title => "Mir", author_id => 1},
]},
{name => "Pushkin A.", id => 2, books => []},
{name => "Alice", id => 3, books => [
{title => "Mips as cpu", author_id => 3},
]},
];
$authors # --> $attaches
my $books = [
{title => "Kiss in night", author_id => 1},
{title => "Mips as cpu", author_id => 3},
{title => "Mir", author_id => 1},
];
\@books # --> $books
query_col ($query, %params)
Returns one column.
query_col "SELECT name FROM author ORDER BY name" # --> ["Alice", "Pushkin A.", "Pushkin A.S."]
eval {query_col "SELECT id, name FROM author"}; $@ # ~> Only one column is acceptable!
query_row ($query, %params)
Returns one row.
query_row "SELECT name FROM author WHERE id=2" # --> {name => "Pushkin A."}
my ($id, $name) = query_row "SELECT id, name FROM author WHERE id=2";
$id # -> 2
$name # => Pushkin A.
eval { query_row "SELECT id, name FROM author" }; $@ # ~> A few lines!
query_row_ref ($query, %params)
Like query_row
, but always returns a scalar.
my @x = query_row_ref "SELECT name FROM author WHERE id=2";
\@x # --> [{name => "Pushkin A."}]
eval {query_row_ref "SELECT name FROM author"}; $@ # ~> A few lines!
query_scalar ($query, %params)
Returns the first value. The query must return one row, otherwise it throws an exception.
query_scalar "SELECT name FROM author WHERE id=2" # => Pushkin A.
make_query_for_order ($order, $next)
Creates a page request condition not by offset, but by cursor pagination.
To do this, it receives $order
of the SQL query and $next
- a link to the next page.
my ($select, $where, $order_sel) = make_query_for_order "name DESC, id ASC", undef;
$select # => name || ',' || id
$where # -> 1
$order_sel # -> undef
my @rows = query "SELECT $select as next FROM author WHERE $where LIMIT 2";
my $last = pop @rows;
($select, $where, $order_sel) = make_query_for_order "name DESC, id ASC", $last->{next};
$select # => name || ',' || id
$where # => (name < 'Pushkin A.'\nOR name = 'Pushkin A.' AND id >= '2')
$order_sel # --> [qw/name id/]
See also:
settings ($id, $value)
Sets or returns a key from the settings
table.
query "CREATE TABLE settings(
id TEXT PRIMARY KEY,
value TEXT NOT NULL
)";
settings "x1" # -> undef
settings "x1", 10 # -> 1
settings "x1" # -> 10
load_by_id ($tab, $pk, $fields, @options)
Returns a record by its ID.
load_by_id author => 2 # --> {id=>2, name=>"Pushkin A."}
load_by_id author => 2, "name as n" # --> {n=>"Pushkin A."}
load_by_id author => 2, "id+:x as n", x => 10 # --> {n=>12}
insert ($tab, %x)
Adds an entry and returns its ID.
insert 'author', name => 'Masha' # -> 4
update ($tab, $id, %params)
Updates a record by its ID and returns that ID.
update author => 3, name => 'Sasha' # -> 3
eval { update author => 5, name => 'Sasha' }; $@ # ~> Row author.id=5 is not!
remove ($tab, $id)
Delete a row from a table by its identifier and return this identifier.
remove "author", 4 # -> 4
eval { remove author => 4 }; $@ # ~> Row author.id=4 does not exist!
query_id ($tab, %params)
Returns an identifier based on other fields.
query_id 'author', name => 'Pushkin A.' # -> 2
stores ($tab, $rows, %opt)
Saves data (updates or inserts). Returns a count of successful operations.
my @authors = (
{id => 1, name => 'Pushkin A.S.'},
{id => 2, name => 'Pushkin A.'},
{id => 3, name => 'Sasha'},
);
query "SELECT * FROM author ORDER BY id" # --> \@authors
my $rows = stores 'author', [
{name => 'Locatelli'},
{id => 3, name => 'Kianu R.'},
{id => 2, name => 'Pushkin A.'},
];
$rows # -> 3
my $sql = "query: INSERT INTO author (id, name) VALUES (NULL, 'Locatelli'),
(3, 'Kianu R.'),
(2, 'Pushkin A.') ON CONFLICT DO UPDATE SET id = excluded.id, name = excluded.name";
$Aion::Query::DEBUG[$#Aion::Query::DEBUG] # -> $sql
@authors = (
{id => 1, name => 'Pushkin A.S.'},
{id => 2, name => 'Pushkin A.'},
{id => 3, name => 'Kianu R.'},
{id => 5, name => 'Locatelli'},
);
query "SELECT * FROM author ORDER BY id" # --> \@authors
store ($tab, %params)
Saves data (updates or inserts) one row.
store 'author', name => 'Bishop M.' # -> 1
touch ($tab, %params)
Super powerful function: returns the row identifier, and if it doesn't exist, creates or updates the row and returns anyway.
touch 'author', name => 'Pushkin A.' # -> 2
touch 'author', name => 'Pushkin X.' # -> 7
START_TRANSACTION()
Returns the variable on which the commit must be performed, otherwise a rollback occurs.
my $transaction = START_TRANSACTION;
query "UPDATE author SET name='Pushkin N.' where id=7" # -> 1
$transaction->commit;
query_scalar "SELECT name FROM author where id=7" # => Pushkin N.
eval {
my $transaction = START_TRANSACTION;
query "UPDATE author SET name='Pushkin X.' where id=7" # -> 1
die "!"; # rollback
$transaction->commit;
};
query_scalar "SELECT name FROM author where id=7" # => Pushkin N.
default_dsn()
Default DSN for DBI->connect
.
default_dsn # => DBI:SQLite:dbname=test-base.sqlite
default_connect_options()
DSN, user, password and commands after connection.
[default_connect_options] # --> ['DBI:SQLite:dbname=test-base.sqlite', 'root', 123, []]
base_connect ($dsn, $user, $password, $conn)
We connect to the database and return the connection and identify it.
my ($dbh, $connect_id) = base_connect("DBI:SQLite:dbname=base-2.sqlite", "toor", "toorpasswd", []);
ref $dbh # => DBI::db
$connect_id # -> -1
connect_respavn ($base)
Checking the connection and reconnecting.
my $old_base = $Aion::Query::base;
$old_base->ping # -> 1
connect_respavn $Aion::Query::base, $Aion::Query::base_connection_id;
$old_base # -> $Aion::Query::base
connect_restart ($base)
Restarting the connection.
my $connection_id = $Aion::Query::base_connection_id;
my $base = $Aion::Query::base;
connect_restart $Aion::Query::base, $Aion::Query::base_connection_id;
$base->ping # -> 0
$Aion::Query::base->ping # -> 1
query_stop()
Creates an additional connection to the base and kills the main one.
To do this, use $Aion::Query::base_connection_id
.
SQLite runs in the same process, so $Aion::Query::base_connection_id
has -1
. That is, for SQLite this method does nothing.
my @x = query_stop;
\@x # --> []
sql_debug ($fn, $query)
Stores database queries in @Aion::Query::DEBUG
. Called from query_do
.
sql_debug label => "SELECT 123";
$Aion::Query::DEBUG[$#Aion::Query::DEBUG] # => label: SELECT 123
AUTHOR
Yaroslav O. Kosmina Lmailto:dart@cpan.org
LICENSE
⚖ GPLv3
COPYRIGHT
The Aion::Surf module is copyright © 2023 Yaroslav O. Kosmina. Rusland. All rights reserved.