Skip to content

Commit

Permalink
feat(limit_by): Add Limit By clause support
Browse files Browse the repository at this point in the history
  • Loading branch information
jdenquin committed Oct 8, 2024
1 parent 0be4d04 commit eb112d9
Show file tree
Hide file tree
Showing 8 changed files with 54 additions and 5 deletions.
3 changes: 2 additions & 1 deletion lib/active_record/connection_adapters/clickhouse_adapter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
require 'arel/nodes/final'
require 'arel/nodes/settings'
require 'arel/nodes/using'
require 'arel/nodes/limit_by'
require 'active_record/connection_adapters/clickhouse/oid/array'
require 'active_record/connection_adapters/clickhouse/oid/date'
require 'active_record/connection_adapters/clickhouse/oid/date_time'
Expand Down Expand Up @@ -48,7 +49,7 @@ def is_view

module ModelSchema
module ClassMethods
delegate :final, :final!, :settings, :settings!, :window, :window!, to: :all
delegate :final, :final!, :settings, :settings!, :window, :window!, :limit_by, :limit_by!, to: :all

def is_view
@is_view || false
Expand Down
12 changes: 12 additions & 0 deletions lib/arel/nodes/limit_by.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
module Arel
module Nodes
class LimitBy < Arel::Nodes::Unary
attr_reader :column

def initialize(limit, column)
@column = column
super(limit)
end
end
end
end
6 changes: 6 additions & 0 deletions lib/arel/visitors/clickhouse.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ def visit_Arel_Attributes_Attribute(o, collector)
end

def visit_Arel_Nodes_SelectOptions(o, collector)
maybe_visit o.limit_by, collector
maybe_visit o.settings, super
end

Expand Down Expand Up @@ -64,6 +65,11 @@ def visit_Arel_Nodes_Using o, collector
collector
end

def visit_Arel_Nodes_LimitBy(o, collector)
collector << "LIMIT #{o.expr} BY #{o.column}"
collector
end

def visit_Arel_Nodes_Matches(o, collector)
op = o.case_sensitive ? " LIKE " : " ILIKE "
infix_value o, collector, op
Expand Down
10 changes: 10 additions & 0 deletions lib/core_extensions/active_record/relation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,15 @@ def window!(name, **opts)
self
end

def limit_by(*opts)
spawn.limit_by!(*opts)
end

def limit_by!(*opts)
@values[:limit_by] = *opts
self
end

private

def check_command(cmd)
Expand All @@ -95,6 +104,7 @@ def build_arel(connection_or_aliases = nil, aliases = nil)
end

arel.final! if @values[:final].present?
arel.limit_by(*@values[:limit_by]) if @values[:limit_by].present?
arel.settings(@values[:settings]) if @values[:settings].present?
arel.using(@values[:using]) if @values[:using].present?
arel.windows(@values[:windows]) if @values[:windows].present?
Expand Down
7 changes: 5 additions & 2 deletions lib/core_extensions/arel/nodes/select_statement.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,18 @@ module CoreExtensions
module Arel # :nodoc: all
module Nodes
module SelectStatement
attr_accessor :settings
attr_accessor :limit_by, :settings

def initialize(relation = nil)
super
@limit_by = nil
@settings = nil
end

def eql?(other)
super && settings == other.settings
super &&
limit_by == other.limit_by &&
settings == other.settings
end
end
end
Expand Down
5 changes: 5 additions & 0 deletions lib/core_extensions/arel/select_manager.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ def using(*exprs)
@ctx.source.right.last.right = ::Arel::Nodes::Using.new(::Arel.sql(exprs.join(',')))
self
end

def limit_by(*exprs)
@ast.limit_by = ::Arel::Nodes::LimitBy.new(*exprs)
self
end
end
end
end
12 changes: 12 additions & 0 deletions spec/single/model_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,18 @@ class ModelPk < ActiveRecord::Base
expect(Model.final.where(date: '2023-07-21').to_sql).to eq('SELECT sample.* FROM sample FINAL WHERE sample.date = \'2023-07-21\'')
end
end

describe '#limit_by' do
it 'works' do
sql = Model.limit_by(1, :event_name).to_sql
expect(sql).to eq('SELECT sample.* FROM sample LIMIT 1 BY event_name')
end

it 'works with limit' do
sql = Model.limit(1).limit_by(1, :event_name).to_sql
expect(sql).to eq('SELECT sample.* FROM sample LIMIT 1 BY event_name LIMIT 1')
end
end
end

context 'sample with id column' do
Expand Down
4 changes: 2 additions & 2 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@
host: 'localhost',
port: ENV['CLICKHOUSE_PORT'] || 8123,
database: ENV['CLICKHOUSE_DATABASE'] || 'test',
username: nil,
password: nil,
username: ENV['CLICKHOUSE_USER'],
password: ENV['CLICKHOUSE_PASSWORD'],
cluster_name: ENV['CLICKHOUSE_CLUSTER'],
}
)
Expand Down

0 comments on commit eb112d9

Please sign in to comment.