Aion-Query
view release on metacpan or search on metacpan
lib/Aion/Query.pm view on Meta::CPAN
USER => 'root',
PASS => 123,
CONN => undef,
DEBUG => 0,
MAX_QUERY_ERROR => 1000,
BQ => 1,
};
# ФоÑмиÑÑÐµÑ DSN на оÑнове конÑига
our $DEFAULT_DSN;
sub default_dsn() {
$DEFAULT_DSN //= do {
if(defined DSN) {DSN}
elsif(DRV =~ /mysql|mariadb/i) {
my $sock = SOCK;
$sock //= "/var/run/mysqld/mysqld.sock" if !defined HOST;
"DBI:${\ DRV}:database=${\ BASE};${\
(defined(HOST)?
'host=' . HOST
. (defined(PORT)? ':' . PORT: ())
. ';': ()
)
}${\ (defined($sock)? 'mysql_socket=' . $sock: ()) }"
}
elsif(DRV =~ /sqlite/i) { "DBI:${\ DRV}:dbname=${\ BASE}" }
else { die "Using DSN! DRV: ${\ DRV} is'nt supported." }
}
}
my $CONN;
sub default_connect_options() {
return default_dsn, USER, PASS, $CONN //= CONN // do {
if(DRV =~ /mysql|mariadb/i) {[
"SET NAMES utf8",
"SET sql_mode='NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION'",
]}
else {[]}
};
}
# ÐÐ¾Ð½Ð½ÐµÐºÑ Ðº базе и id коннекÑа
lib/Aion/Query.pm view on Meta::CPAN
# вÑпомогаÑелÑное подклÑÑение
my $signal = base_connect(default_connect_options());
$signal->do("KILL HARD " . ($base_connection_id + 0));
$signal->disconnect;
return;
}
# ÐапÑоÑÑ Ðº базе
our @DEBUG;
sub sql_debug(@) {
my ($fn, $query) = @_;
my $msg = "$fn: " . (ref $query? np($query): $query);
push @DEBUG, $msg;
print STDERR $msg, "\n" if DEBUG;
}
# sub debug_html {
# join "", map { ("<p class='debug'>", to_html($_), "</p>\n") } @DEBUG;
# }
lib/Aion/Query.pm view on Meta::CPAN
# join "", map { "$_\n\n" } @DEBUG, "";
# }
# sub debug_array {
# return if !@DEBUG;
# $_[0]->{SQL_DEBUG} = \@DEBUG;
# return;
# }
sub LAST_INSERT_ID() {
$base->last_insert_id
}
# ÐÑеобÑазÑÐµÑ Ð² бинаÑнÑÑ ÑÑÑÐ¾ÐºÑ Ð¿ÑинÑÑÑÑ Ð² MYSQL
sub _to_hex_str($) {
my ($s) = @_;
no utf8;
use bytes;
$s =~ s/./sprintf "%02X", ord $&/gaes;
"X'$s'"
}
# ÐÐ´ÐµÑ Ð¿ÐµÑекодиÑÐ¾Ð²Ð°Ð½Ð¸Ñ Ñимволов:
# Рбазе иÑполÑзÑеÑÑÑ cp1251, поÑÑÐ¾Ð¼Ñ ÑимволÑ, коÑоÑÑе в Ð½ÐµÑ Ð½Ðµ вÑ
одÑÑ, нÑжно пеÑевеÑÑи в поÑледоваÑелÑноÑÑи.
# Ðид поÑледоваÑелÑноÑÑи: °ЧÐСÐÐ_Ð_254-ÑиÑной ÑиÑÑеме; \x7F
# Ðнак ° вÑбÑан поÑомÑ, ÑÑо он вÑÑе 127, ÑооÑвеÑÑÑвенно ÑÑÑока из Ð±Ð°Ð·Ñ Ð´Ð°Ð½Ð½ÑÑ
, ÑодеÑжаÑÐ°Ñ ÑакÑÑ Ð¿Ð¾ÑледоваÑелÑноÑÑÑ,
# бÑÐ´ÐµÑ Ñ Ñлагом utf8, ÑÑо необÑ
одимо Ð´Ð»Ñ Ð¾Ð±ÑаÑного пеÑекодиÑованиÑ.
sub _recode_cp1251 {
my ($s) = @_;
return $s unless BQ;
$s =~ s/°|[^\Q$Aion::Format::CIF\E]/"°${\ to_radix(ord $&, 254) }\x7F"/ge;
$s
}
sub quote(;$);
sub quote(;$) {
my $k = @_ == 0? $_: $_[0];
my $ref;
!defined($k)? "NULL":
ref $k eq "ARRAY" && ref $k->[0] eq "ARRAY"?
join(", ", map { join "", "(", join(", ", map quote, @$_), ")" } @$k):
ref $k eq "ARRAY"? join("", join(", ", map quote, @$k)):
ref $k eq "HASH"?
join(", ", map { join "", $_, " = ", quote $k->{$_} } sort keys %$k):
ref $k eq "REF" && ref $$k eq "ARRAY"?
lib/Aion/Query.pm view on Meta::CPAN
exists $+{for}? do {
my ($sep, $param, $code) = @+{qw/sep for code/};
join "\n", map { local $param{'_'} = $_; _set_params("$sep$code", \%param) } @{$param{$param}}
}:
_set_params($+{param}, \%param)
!imgex;
$query
}
# ÐÑполнÑÐµÑ sql-запÑоÑ
sub query_do($;$) {
my ($query, $columns) = @_;
sql_debug query => $query;
connect_respavn($base, $base_connection_id);
my $res = eval {
if($query =~ /^\s*(select|show|desc(ribe)?)\b/in) {
my $r = @_>1? do {
my $sth = $base->prepare($query);
$sth->execute;
lib/Aion/Query.pm view on Meta::CPAN
$r
} else {
0 + $base->do($query)
}
};
die +(length($query)>MAX_QUERY_ERROR? substr($query, 0, MAX_QUERY_ERROR) . " ...": $query) . "\n\n$@" if $@;
$res
}
sub query_ref(@) {
my ($query, %kw) = @_;
my $map = delete $kw{MAP};
$query = query_prepare($query, %kw) if @_>1;
my $res = query_do($query);
if($map && ref $res eq "ARRAY") {
eval "require $map" or die unless UNIVERSAL::can($map, "new");
[map { $map->new(%$_) } @$res]
} else {
$res
}
}
sub query(@) {
my $ref = query_ref(@_);
wantarray && ref $ref? @$ref: $ref;
}
# ÐозвÑаÑÐ°ÐµÑ sth
sub query_sth(@) {
my ($query, %kw) = @_;
$query = query_prepare($query, %kw) if @_>1;
my $sth = $base->prepare($query);
$sth->execute;
$sth
}
# ÐÐ»Ñ ÑлайÑа
#
# query_slice word => "id", "SELECT word, id FROM word WHERE word in (1,2,3)" -> { 1 => 10, 2 => 20 }
lib/Aion/Query.pm view on Meta::CPAN
# query_slice word => ["id"], "SELECT word, id FROM word WHERE word in (1,2,3)" -> { 1 => [10, 20], 2 => [30] }
#
# query_slice word => [], "SELECT word, id FROM word WHERE word in (1,2,3)" -> { 1 => [{id => 10, word => 1}, {id => 20, word => 2}] }
#
# query_slice word => [[]], "SELECT word, id FROM word WHERE word in (1,2,3)" -> [ [{id => 10, word => 1}, {id => 20, word => 2}], ... ]
#
# TODO: query_slice [] => word, "SELECT word, id FROM word WHERE word in (1,2,3)" -> [{id => 10, word => 1}, {id => 20, word => 2}]
#
# TODO: [ "id", "name", "jinni" ] -> [{ id=>1, items => [{ name => "hi!", items => [{ jinni=>2, items => [{...}] }] }] }]
#
sub query_slice(@);
sub query_slice(@) {
my ($key, $val, @args) = @_;
my $is_array = ref $val eq "ARRAY" && @$val && ref $val->[0] eq "ARRAY";
return $is_array? [ query_slice @_ ]: +{ query_slice @_ } if !wantarray;
my $rows = query_ref(@args);
if($is_array) {
my %x; my @x;
lib/Aion/Query.pm view on Meta::CPAN
push @{$row1->{$attach}}, $row2;
}
wantarray? @$rows2: $rows2
}
# ÐÑбÑаÑÑ Ð¾Ð´Ð¸Ð½ колÑмн
#
# query_col "SELECT id FROM word WHERE word in (1,2,3)" -> [1,2,3]
#
sub query_col(@);
sub query_col(@) {
return [query_col @_] if !wantarray;
my $rows = query_ref(@_);
die "Only one column is acceptable!" if @$rows and 1 != keys %{$rows->[0]};
map { my ($k, $v) = %$_; $v } @$rows
}
# ÐÑбÑаÑÑ ÑÑÑокÑ
#
# query_row_ref "SELECT id, word FROM word WHERE word = 1" -> {id=>1, word=>"ÑеÑебÑо"}
#
sub query_row_ref(@) {
my $rows = query_ref(@_);
die "A few lines!" if @$rows>1;
$rows->[0]
}
# ÐÑбÑаÑÑ ÑÑÑокÑ
#
# ($id, $word) = query_row_ref "SELECT id, word FROM word WHERE word = 1"
#
sub query_row(@) {
return query_row_ref(@_) unless wantarray;
my $sql = query_prepare(@_);
my $rows = query_do($sql, my $columns);
die "A few lines!" if @$rows > 1;
my $row = $rows->[0];
map $row->{$_}, @$columns
}
# ÐÑбÑаÑÑ Ð·Ð½Ð°Ñение
#
# query_scalar "SELECT word FROM word WHERE id = 1" -> "золоÑо"
#
sub query_scalar(@) {
my $rows = query_ref(@_);
die "A few lines!" if @$rows>1;
die "Only one column is acceptable! " . keys %{$rows->[0]} if @$rows and 1 != keys %{$rows->[0]};
my ($k, $v) = %{$rows->[0]};
$v
}
# СоздаÑÑ ÑаÑÑи sql-запÑоÑа Ð´Ð»Ñ ÑоÑÑиÑовки по ÑÑловиÑ, а не лимиÑÑ
#
# ("concat(size,',',likes)", "(size < 10 OR size = 10 AND likes >= 12)", ["size", "likes"]) = make_query_for_order "size desc, likes", "10,12"
#
# ("concat(size,',',likes)", 1) = make_query_for_order "size desc, likes", ""
#
sub make_query_for_order(@) {
my ($order, $next) = @_;
my @orders = split /\s*,\s*/, $order;
my @order_direct;
my @order_sel = map { my $x=$_; push @order_direct, $x=~s/\s+(asc|desc)\s*$//ie ? lc $1: "asc"; $x } @orders;
my $select = @order_sel==1? $order_sel[0]:
_check_drv($base, "mysql|mariadb")?
join("", "concat(", join(",',',", @order_sel), ")"):
join " || ',' || ", @order_sel
lib/Aion/Query.pm view on Meta::CPAN
}
}
push @whr, join " AND ", @opr;
}
my $where = join "\nOR ", map "$_", @whr;
return $select, "($where)", \@order_sel;
}
# УÑÑÐ°Ð½Ð°Ð²Ð»Ð¸Ð²Ð°ÐµÑ Ð¸Ð»Ð¸ возвÑаÑÐ°ÐµÑ ÐºÐ»ÑÑ Ð¸Ð· ÑаблиÑÑ settings
sub settings($;$) {
my ($id, $value) = @_;
if(@_ == 1) {
my $v = query_scalar("SELECT value FROM settings WHERE id=:id", id => $id);
return defined($v)? Aion::Format::Json::from_json($v): $v;
}
return remove("settings" => $id) if !defined $value;
store('settings',
id => $id,
value => Aion::Format::Json::to_json($value),
);
}
# возвÑаÑÐ°ÐµÑ Ð·Ð°Ð¿Ð¸ÑÑ Ð¿Ð¾ ÐµÑ pk
sub load_by_id(@) {
my ($tab, $pk, $fields, @options) = @_;
$fields //= "*";
query_row("SELECT $fields FROM $tab WHERE id=:id LIMIT 2", @options, id=>$pk)
}
# ÐÑовеÑÑÐµÑ Ð´ÑÐ°Ð¹Ð²ÐµÑ ÐРна имена
sub _check_drv {
my ($dbh, $drv) = @_;
$dbh->{Driver}{Name} =~ /^($drv)/ain
}
# ÐобавлÑÐµÑ Ð·Ð°Ð¿Ð¸ÑÑ Ð¸ возвÑаÑÐ°ÐµÑ ÐµÑ id
sub insert(@) {
my ($tab, %x) = @_;
if(_check_drv($base, "mysql|mariadb")) {
query "INSERT INTO $tab SET :set", set => \%x;
} else {
stores($tab, [\%x], insert => 1);
}
LAST_INSERT_ID()
}
# ÐбновлÑÐµÑ Ð·Ð°Ð¿Ð¸ÑÑ Ð¿Ð¾ ÐµÑ id
#
# update "tab" => 123, word => 123 -> 6
#
sub update(@) {
my ($tab, $id, %x) = @_;
die "Row $tab.id=$id is not!" if !query "UPDATE $tab SET :set WHERE id=:id", id=>$id, set => \%x;
$id
}
# УдалÑÐµÑ Ð·Ð°Ð¿Ð¸ÑÑ Ð¿Ð¾ ÐµÑ id
#
# remove "tab" => 123 -> 123
#
sub remove(@) {
my ($tab, $id) = @_;
die "Row $tab.id=$id does not exist!" if !query "DELETE FROM $tab WHERE id=:id", id=>$id;
$id
}
# ÐозвÑаÑÐ°ÐµÑ ÐºÐ»ÑÑ Ð¿Ð¾ дÑÑгим полÑм
#
# query_id "tab", word => 123 -> 6
#
sub query_id(@) {
my $tab = shift; my %row = @_;
my $pk = delete($row{'-pk'}) // "id";
my $fields = ref $pk? join(", ", @$pk): $pk;
my $where = join " AND ", map { my $v = $row{$_}; defined($v)? "$_ = ${\ quote($v) }": "$_ is NULL" } sort keys %row;
my $query = "SELECT $fields FROM $tab WHERE $where LIMIT 2";
my $v = query_row($query);
ref $pk? $v: $v->{$pk}
}
# UPSERT: ÑоÑ
ÑанÑÐµÑ Ð´Ð°Ð½Ð½Ñе (update или insert)
#
# stores "tab", [{word=>1}, {word=>2}];
#
sub stores(@);
sub stores(@) {
my ($tab, $rows, %opt) = @_;
my ($ignore, $insert) = delete @opt{qw/ignore insert/};
die "Keys ${\ join('', )}" if keys %opt;
my @keys = sort keys %{+{map %$_, @$rows}};
die "No fields in bean $tab!" if !@keys;
lib/Aion/Query.pm view on Meta::CPAN
# ÑоÑ
ÑанÑÐµÑ Ð´Ð°Ð½Ð½Ñе (update или insert)
#
# store "tab", word=>123;
#
sub store (@) {
my $tab = shift;
stores $tab, [+{@_}];
}
# СвеÑÑ
моÑÐ½Ð°Ñ ÑÑнкÑиÑ: возвÑаÑÐ°ÐµÑ pk, а еÑли его Ð½ÐµÑ - ÑоздаÑÑ Ð¸Ð»Ð¸ обновлÑÐµÑ Ð·Ð°Ð¿Ð¸ÑÑ Ð¸ вÑÑ Ñавно возвÑаÑаеÑ
sub touch(@) {
my $sub;
$sub = pop @_ if ref $_[$#_] eq "CODE";
my $pk = query_id @_;
return $pk if defined $pk;
store @_, $sub? $sub->(): ();
query_id @_
}
( run in 0.245 second using v1.01-cache-2.11-cpan-cba739cd03b )