From 20afb079f0aa4bdbdc7f097c4271c6d16fdb900c Mon Sep 17 00:00:00 2001 From: Jonathan Stowe Date: Sat, 13 Apr 2024 16:03:06 +0100 Subject: [PATCH] Don't emit "FOR UPDATE .." On SQLite Rather than just ignore it like it does for other things it doesn't implement, SQLite throws an error with 'FOR UPDATE' on a select statement. This just over-rides with an implementation that doesn't emit anything if there is a '.for'. The test just checks it doesn't explode with .skip-update, I've confirmed the SQL generated for both Pg and SQLite visually. --- lib/Red/Driver/CommonSQL.rakumod | 24 +++++++++++++++--------- lib/Red/Driver/SQLite.rakumod | 4 ++++ t/81-resultset-skip-locked.rakutest | 25 +++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 9 deletions(-) create mode 100644 t/81-resultset-skip-locked.rakutest diff --git a/lib/Red/Driver/CommonSQL.rakumod b/lib/Red/Driver/CommonSQL.rakumod index b3741050..dd84e2ed 100644 --- a/lib/Red/Driver/CommonSQL.rakumod +++ b/lib/Red/Driver/CommonSQL.rakumod @@ -514,18 +514,24 @@ multi method translate(Red::AST::Select $ast, $context?, :$gambi) { "\nOFFSET $_" with $offset }{ do with $ast.for // $*red-subselect-for { - "\nFOR { - when UPDATE { - "UPDATE" - } - when SKIP_LOCKED { - "UPDATE SKIP LOCKED" - } - }" + self.translate($_); } }" => @bind } +multi method translate(Red::LockType $lock-type --> Str ) { + given $lock-type { + "\nFOR { + when UPDATE { + "UPDATE" + } + when SKIP_LOCKED { + "UPDATE SKIP LOCKED" + } + }" + } +} + multi method translate(Red::AST::StringFunction $_, $context?) { self.translate: .default-implementation, $context } @@ -805,7 +811,7 @@ multi method translate(Red::Column $_, "column-default") { my ($str, @bind); :(:key($str), :value(@bind)) := self.translate: do given .default.($_) { do if $_ !~~ Red::AST { - .&ast-value + .&ast-value } else { $_ } diff --git a/lib/Red/Driver/SQLite.rakumod b/lib/Red/Driver/SQLite.rakumod index b927d5b7..90172784 100644 --- a/lib/Red/Driver/SQLite.rakumod +++ b/lib/Red/Driver/SQLite.rakumod @@ -161,6 +161,10 @@ multi method translate(Red::AST::Value $_ where { .type ~~ Pair and .value.key ~ multi method translate(Red::AST::Minus $ast, "multi-select-op") { "EXCEPT" => [] } +multi method translate(Red::LockType $lock-type --> Str ) { + '' +} + method comment-on-same-statement { True } #multi method default-type-for(Red::Column $ where .attr.type ~~ Mu --> Str:D) {"varchar(255)"} diff --git a/t/81-resultset-skip-locked.rakutest b/t/81-resultset-skip-locked.rakutest new file mode 100644 index 00000000..4f2114e4 --- /dev/null +++ b/t/81-resultset-skip-locked.rakutest @@ -0,0 +1,25 @@ +use Test; +use Red; + +model Foo { + has Int $.bar is serial; + has Str $.foo is column; +} + +my $*RED-DEBUG = $_ with %*ENV; +my $*RED-DEBUG-RESPONSE = $_ with %*ENV; +my @conf = (%*ENV // "SQLite").split(" "); +my $driver = @conf.shift; +my $*RED-DB = database $driver, |%( @conf.map: { do given .split: "=" { .[0] => val .[1] } } ); + +schema(Foo).drop; +Foo.^create-table; + +Foo.^create: foo => "babaa"; + +my @rows; +lives-ok { @rows = Foo.^rs.skip-locked.all }, 'with skip-locked'; + +is @rows.elems, 1, "got the right number of rows"; + +done-testing