Skip to content

Commit

Permalink
Make Dataset#get and #first without argument not create intermediate …
Browse files Browse the repository at this point in the history
…datasets if receiver uses raw SQL

There's no reason for a dataset clone in this case, as the
limit and select settings will be ignored.

This also fixes use of Dataset#get without argument when using the
implicit_subquery extension. Previously, it would try to use a
subquery in an unhelpful way:

  DB['SELECT a FROM b WHERE c = 1'].extension(:implicit_subquery).get
  #  SELECT NULL AS v FROM (SELECT a FROM b WHERE c = 1) AS t1 LIMIT 1

Now, it runs the query directly and returns the first value.
  • Loading branch information
jeremyevans committed Sep 6, 2024
1 parent 26a2243 commit 45b7f37
Show file tree
Hide file tree
Showing 3 changed files with 20 additions and 2 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
=== master

* Make Dataset#get and #first without argument not create intermediate datasets if receiver uses raw SQL (jeremyevans)

* Add dataset_run extension, for building SQL using datasets, and running with Database#run (jeremyevans)

* Switch default connection pool to timed_queue on Ruby 3.2+ (jeremyevans)
Expand Down
10 changes: 9 additions & 1 deletion lib/sequel/dataset/actions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ def first(*args, &block)
case args.length
when 0
unless block
return single_record
return(@opts[:sql] ? single_record! : single_record)
end
when 1
arg = args[0]
Expand Down Expand Up @@ -282,13 +282,21 @@ def first!(*args, &block)
#
# DB[:table].get{[sum(id).as(sum), name]} # SELECT sum(id) AS sum, name FROM table LIMIT 1
# # => [6, 'foo']
#
# If called on a dataset with raw SQL, returns the
# first value in the dataset without changing the selection or setting a limit:
#
# DB["SELECT id FROM table"].get # SELECT id FROM table
# # => 3
def get(column=(no_arg=true; nil), &block)
ds = naked
if block
raise(Error, 'Must call Dataset#get with an argument or a block, not both') unless no_arg
ds = ds.select(&block)
column = ds.opts[:select]
column = nil if column.is_a?(Array) && column.length < 2
elsif no_arg && opts[:sql]
return ds.single_value!
else
case column
when Array
Expand Down
10 changes: 9 additions & 1 deletion spec/core/dataset_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3035,6 +3035,12 @@ def supports_cte_in_subselect?; false end
Sequel.mock[:t].first.must_be_nil
end

it "should return first record in query when using raw SQL" do
db = Sequel.mock(:fetch=>{:v=>1})
db['SELECT 1'].first.must_equal(:v=>1)
db.sqls.must_equal ['SELECT 1']
end

it "#last should raise if no order is given" do
proc {@d.last}.must_raise(Sequel::Error)
proc {@d.last(2)}.must_raise(Sequel::Error)
Expand Down Expand Up @@ -3375,7 +3381,9 @@ def supports_cte_in_subselect?; false end
@d.with_sql('SELECT foo').get(:name).must_equal "SELECT foo"
@d = @d.with_fetch(:name=>1, :abc=>2)
@d.with_sql('SELECT foo').get{[name, n[abc]]}.must_equal [1, 2]
@d.db.sqls.must_equal ['SELECT foo'] * 2
@d = @d.with_fetch(:name=>1)
@d.with_sql('SELECT foo').get.must_equal 1
@d.db.sqls.must_equal ['SELECT foo'] * 3
end

it "should handle cases where no rows are returned" do
Expand Down

0 comments on commit 45b7f37

Please sign in to comment.