Closed frioux closed 5 years ago
cc: @oalders
[13:02:19] <@frew> our best bet at this point is to add a limit => 1 or do research on if there is a standard dummy empty table
[13:02:26] <@frew> or something like that
[13:02:39] <@frew> I think the end result needs to be like
[13:02:50] <@frew> SELECT 1 FROM DUMMY where EXISTENCE CHECK
[13:02:53] <@frew> more or less
[13:03:07] <@frew> and actually dummy should have 1 row
Seemingly unrelated conversation that could resolve our issue:
14:56:44 xinming_ | mst: Is it possible to make DBIC to do soemthing like SELECT 1 FROM table WHERE ...?
14:57:10 @mst | step back. explain what you're trying to achieve.
14:57:18 xinming_ | What I mean is something like SELECT EXISTS (SELECT ... FROM ...);
14:57:39 xinming_ | I want to test wether we have results in for a ResultSet
14:58:25 xinming_ | so we don't need to use count to access all rows. Since in postgres, It'll try to check all rows matched.
14:59:10 xinming_ | so something like 'SELECT 1 FROM table WHERE rs filtering clauses'
15:01:06 @mst | columns => [ { foo => \1 } ] ?
15:02:06 xinming_ | $self->search($query, { rows => 1, columns => [\1] })->single; <--- Something like this?
15:02:18 xinming_ | Let me ry.
15:02:19 xinming_ | try
15:06:55 tharkun | How can I do an INSERT ... SELECT ... with DBIx::Class ? man page will suffice
15:07:54 @castaway | tharkun: ->as_query
15:08:10 tharkun | castaway: Thanks
15:09:39 xinming_ | Thanks
15:09:41 xinming_ | got it
15:10:12 xinming_ | $rs->search(undef, { columns => { exists => \1 }, rows => 1 })->single->get_column('exists'); worked
Got started on this and sadly the answer @shadowcat-mst gave to @xinming fails tests and always returns true.
Oh the branch I started on was here if anyone wants to try out themselves.
The simplest thing would be $self->search(undef, { rows => 1 })->count
, but this bit in DBIx::Class::ResultSet::Count
defeats that:
# this is a little optimization - it is faster to do the limit
# adjustments in software, instead of a subquery
my ($rows, $offset) = delete @{$attrs}{qw/rows offset/};
It might be the case for some databases without built-in LIMIT
(or equivalent) and have to do horrible ROW_COUNT
subqueries, but PostgreSQL definitely benefits from leaving the LIMIT
in.
Has anyone looked at this in a while? it would be great to not have a dangling cursor from the ->first
in the current implementation on cpan.
Nope; help welcome :)
@ribasushi's comments here (https://github.com/frioux/DBIx-Class-Helpers/commit/85702318c13da517d0c9d6b997ff346fdc47abba) seemed sane, but this is way too far out of my depth to be able to add on to that.
FWIW, here is a postgres-specific implementation:
# generates: select exists (SELECT 1 FROM $table WHERE ... )
sub exists {
my $self = shift;
my $inner = $self->search(undef, { select => [ \1 ] })->as_query;
my $statement = 'select exists ' . $inner->$*->[0];
my @binds = map { $_->[1] } $inner->$*->@[1 .. $inner->$*->$#*];
my ($exists) = $self->result_source->schema->storage->dbh_do(sub {
my ($storage, $dbh) = @_;
# cribbed from DBIx::Class::Storage::DBI::_format_for_trace
$storage->debugobj->query_start($statement, map { defined($_) ? qq{'$_'} : q{NULL} } @binds)
if $storage->debug;
return $dbh->selectrow_array($statement, undef, @binds);
});
return $exists;
}
Fixed by #98
Given the changes to the query that I see in https://gist.github.com/karenetheridge/750f774d1e4dea2692d80d9babecb568, I think I will stick with the original implementation I had. The addition of all columns in the original source, twice, can become quite inefficient.
@karenetheridge something locally in your codebase ( some other extension? ) is interfering with the generation of the column list. You need to debug this. The shipped version does not do what you are describing:
rabbit@Ahasver:~$ cpanm --look DBIx::Class::Helpers
--> Working on DBIx::Class::Helpers
Fetching http://www.cpan.org/authors/id/F/FR/FREW/DBIx-Class-Helpers-2.034001.tar.gz ... OK
Entering /home/rabbit/.cpanm/work/1575914802.6172/DBIx-Class-Helpers-2.034001 with /bin/bash
rabbit@Ahasver:~/.cpanm/work/1575914802.6172/DBIx-Class-Helpers-2.034001$ DBIC_TRACE=1 prove -l t/ResultSet/Shortcut/ResultsExist.t
t/ResultSet/Shortcut/ResultsExist.t ..
...
SELECT * FROM ( SELECT EXISTS (SELECT 42 FROM "Foo" "me" WHERE ( "id" > ? )) ): '0'
t/ResultSet/Shortcut/ResultsExist.t .. 1/? SELECT * FROM ( SELECT EXISTS (SELECT 42 FROM "Foo" "me" WHERE ( "id" < ? )) ): '0'
SELECT ( SELECT EXISTS (SELECT 42 FROM "Foo" "correlation" WHERE ( ( "correlation"."id" > "me"."id" AND "id" > ? ) )) ), ( SELECT EXISTS (SELECT 42 FROM "Foo" "correlation" WHERE ( ( "correlation"."id" < "me"."id" AND "id" > ? ) )) ), "me"."id" FROM "Foo" "me" WHERE ( "id" > ? ) ORDER BY "id": '0', '0', '0'
t/ResultSet/Shortcut/ResultsExist.t .. ok
All tests successful.
Files=1, Tests=3, 0 wallclock secs ( 0.02 usr 0.00 sys + 0.56 cusr 0.01 csys = 0.59 CPU)
Result: PASS
@ribasushi much to my embarrassment I discovered that I was testing on a machine where I hadn't updated ::Helpers to 2.034001, which contain the reworded exists
. :/
However, this leads to a worse problem.. the reworked query doesn't work at all. :(
(This is with PostgreSQL 10.10 on x86_64-apple-darwin17.7.0, compiled by Apple LLVM version 9.1.0 (clang-902.0.39.2), 64-bit
)
my code:
my $exists = $schema->resultset('build')->search({ name => 'my first build' })->results_exist;
output, with DBIC_TRACE enabled:
SELECT * FROM ( SELECT EXISTS (SELECT 42 FROM build me WHERE ( name = ? )) ): 'my first build'
DBI Exception: DBD::Pg::st execute failed: ERROR: subquery in FROM must have an alias
LINE 1: SELECT * FROM ( SELECT EXISTS (SELECT 42 FROM build me WHERE...
^
HINT: For example, FROM (SELECT ...) [AS] foo. [for Statement "SELECT * FROM ( SELECT EXISTS (SELECT 42 FROM build me WHERE ( name = ? )) )" with ParamValues: 1='my first build'] at /Users/ether/.perlbrew/libs/26.3@std/lib/perl5/DBIx/Class/Schema.pm line 1118.
DBIx::Class::Schema::throw_exception(Conch::DB=HASH(0x7fc41b349a28), "DBI Exception: DBD::Pg::st execute failed: ERROR: subquery i"...) called at /Users/ether/.perlbrew/libs/26.3@std/lib/perl5/DBIx/Class/Storage.pm line 113
DBIx::Class::Storage::throw_exception(DBIx::Class::Storage::DBI::Pg=HASH(0x7fc41b348e40), "DBI Exception: DBD::Pg::st execute failed: ERROR: subquery i"...) called at /Users/ether/.perlbrew/libs/26.3@std/lib/perl5/DBIx/Class/Storage/DBI.pm line 1501
DBIx::Class::Storage::DBI::__ANON__("DBD::Pg::st execute failed: ERROR: subquery in FROM must hav"..., DBI::st=HASH(0x7fc41bc53e50), undef) called at /Users/ether/.perlbrew/libs/26.3@std/lib/perl5/DBIx/Class/Storage/DBI.pm line 1836
DBIx::Class::Storage::DBI::_dbh_execute(DBIx::Class::Storage::DBI::Pg=HASH(0x7fc41b348e40), DBI::db=HASH(0x7fc41e4af7b0), "SELECT * FROM ( SELECT EXISTS (SELECT 42 FROM build me WHERE "..., ARRAY(0x7fc41b349ab8), ARRAY(0x7fc41bd15d70)) called at /Users/ether/.perlbrew/libs/26.3@std/lib/perl5/DBIx/Class/Storage/DBI.pm line 856
DBIx::Class::Storage::DBI::__ANON__() called at /Users/ether/.perlbrew/libs/26.3@std/lib/perl5/DBIx/Class/Storage/BlockRunner.pm line 130
DBIx::Class::Storage::BlockRunner::try {...} () called at /Users/ether/.perlbrew/libs/26.3@std/lib/perl5/Try/Tiny.pm line 98
eval {...} called at /Users/ether/.perlbrew/libs/26.3@std/lib/perl5/Try/Tiny.pm line 93
Try::Tiny::try(CODE(0x7fc41bc54150), Try::Tiny::Catch=REF(0x7fc41bd2f610)) called at /Users/ether/.perlbrew/libs/26.3@std/lib/perl5/DBIx/Class/Storage/BlockRunner.pm line 134
DBIx::Class::Storage::BlockRunner::__ANON__() called at /Users/ether/.perlbrew/libs/26.3@std/lib/perl5/Context/Preserve.pm line 33
Context::Preserve::preserve_context(CODE(0x7fc41d617918), "replace", CODE(0x7fc41f1d0038)) called at /Users/ether/.perlbrew/libs/26.3@std/lib/perl5/DBIx/Class/Storage/BlockRunner.pm line 213
DBIx::Class::Storage::BlockRunner::_run(DBIx::Class::Storage::BlockRunner=HASH(0x7fc41e423ed8), CODE(0x7fc41e4aed60)) called at /Users/ether/.perlbrew/libs/26.3@std/lib/perl5/DBIx/Class/Storage/BlockRunner.pm line 105
DBIx::Class::Storage::BlockRunner::run(DBIx::Class::Storage::BlockRunner=HASH(0x7fc41e423ed8), CODE(0x7fc41e4aed60)) called at /Users/ether/.perlbrew/libs/26.3@std/lib/perl5/DBIx/Class/Storage/DBI.pm line 857
DBIx::Class::Storage::DBI::dbh_do(undef, undef, "SELECT * FROM ( SELECT EXISTS (SELECT 42 FROM build me WHERE "..., ARRAY(0x7fc41b349ab8), ARRAY(0x7fc41bd15d70)) called at /Users/ether/.perlbrew/libs/26.3@std/lib/perl5/DBIx/Class/Storage/DBI.pm line 1817
DBIx::Class::Storage::DBI::_execute(DBIx::Class::Storage::DBI::Pg=HASH(0x7fc41b348e40), "select", REF(0x7fc41bd0e320), SCALAR(0x7fc41e356be0), HASH(0x7fc41e4b0d88), HASH(0x7fc41f19e9b0)) called at /Users/ether/.perlbrew/libs/26.3@std/lib/perl5/DBIx/Class/Storage/DBI.pm line 2409
DBIx::Class::Storage::DBI::_select(DBIx::Class::Storage::DBI::Pg=HASH(0x7fc41b348e40), REF(0x7fc41bd0e320), SCALAR(0x7fc41e356be0), HASH(0x7fc41e4b0d88), HASH(0x7fc41b349110)) called at /Users/ether/.perlbrew/libs/26.3@std/lib/perl5/DBIx/Class/Helper/ResultSet/Shortcut/ResultsExist.pm line 27
DBIx::Class::Helper::ResultSet::Shortcut::ResultsExist::results_exist(Conch::DB::ResultSet::Build=HASH(0x7fc41f190a50)) called at lib/Conch/DB/Helper/ResultSet/ResultsExist.pm line 39
Conch::DB::Helper::ResultSet::ResultsExist::exists(Conch::DB::ResultSet::Build=HASH(0x7fc41f190a50)) called at t/resultsexist.t line 14
@karenetheridge indeed, I screwed up an aliasing step when writing up the patch for @frioux. Something like this should do it, but I will not have time to properly test/PR this:
--- lib/DBIx/Class/Helper/ResultSet/Shortcut/ResultsExist.pm.orig 2019-12-09 19:44:10.666520779 +0100
+++ lib/DBIx/Class/Helper/ResultSet/Shortcut/ResultsExist.pm 2019-12-09 19:47:21.834153731 +0100
@@ -24,11 +24,14 @@
sub results_exist {
my $self = shift;
+ my $inner_q = $self->results_exist_as_query;
+ $$inner_q->[0] .= " AS _existence_subq";
+
my( undef, $sth ) = $self->result_source
->schema
->storage
->_select(
- $self->results_exist_as_query,
+ $inner_q,
\'*',
{},
{},
That appears to work -- the new query is SELECT * FROM ( SELECT EXISTS (SELECT 42 FROM build me WHERE ( name = ? )) ) AS _existence_subq: 'my first build'
which successfully evaluates.
I can PR this up for @frioux, but I am also unsure of the ramifications to other db drivers.
for my internal use, I've boiled this down to:
sub exists ($self) {
my $inner_q = $self->search(undef, { select => [ \1 ] })->as_query;
$$inner_q->[0] = '( select exists '.$$inner_q->[0].' ) as _existence_subq';
my (undef, $sth) = $self->result_source
->schema
->storage
->_select(
$inner_q,
\'*',
{},
{},
);
return $sth->fetchall_arrayref->[0][0];
}
much to my embarrassment I discovered that I was testing on a machine where I hadn't updated
@karenetheridge apparently you also have not looked at the open bugreports :unamused: https://github.com/frioux/DBIx-Class-Helpers/pull/99
much to my embarrassment I discovered that I was testing on a machine where I hadn't updated
@karenetheridge apparently you also have not looked at the open bugreports 😒
99
You didn't seem to know about the bug report before yesterday either :)
This actually broke results_exist on Oracle:
ORA-00936: missing expression (DBD ERROR: error possibly near <*> indicator at char 23 in 'SELECT * FROM ( SELECT <*>EXISTS (SELECT 42 FROM (
Looks like when I "fixed" ResultsExist I broke it :(
Here's some info: