Skip to content

Commit

Permalink
Support functions (#120)
Browse files Browse the repository at this point in the history
support functions #120
felix-dumit authored Apr 4, 2024
1 parent 0e43bd7 commit 6ad665c
Showing 8 changed files with 107 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -57,6 +57,16 @@ def tables(name = nil)
result['data'].flatten
end

def functions
result = do_system_execute("SELECT name FROM system.functions WHERE origin = 'SQLUserDefined'")
return [] if result.nil?
result['data'].flatten
end

def show_create_function(function)
do_execute("SELECT create_query FROM system.functions WHERE origin = 'SQLUserDefined' AND name = '#{function}'", format: nil)
end

def table_options(table)
sql = show_create_table(table)
{ options: sql.gsub(/^(?:.*?)(?:ENGINE = (.*?))?( AS SELECT .*?)?$/, '\\1').presence, as: sql.match(/^CREATE (?:.*?) AS (SELECT .*?)$/).try(:[], 1) }.compact
21 changes: 21 additions & 0 deletions lib/active_record/connection_adapters/clickhouse_adapter.rb
Original file line number Diff line number Diff line change
@@ -308,6 +308,11 @@ def create_table(table_name, **options, &block)
end
end

def create_function(name, body)
fd = "CREATE FUNCTION #{apply_cluster(quote_table_name(name))} AS #{body}"
do_execute(fd, format: nil)
end

# Drops a ClickHouse database.
def drop_database(name) #:nodoc:
sql = apply_cluster "DROP DATABASE IF EXISTS #{quote_table_name(name)}"
@@ -317,6 +322,12 @@ def drop_database(name) #:nodoc:
end
end

def drop_functions
functions.each do |function|
drop_function(function)
end
end

def rename_table(table_name, new_name)
do_execute apply_cluster "RENAME TABLE #{quote_table_name(table_name)} TO #{quote_table_name(new_name)}"
end
@@ -336,6 +347,16 @@ def drop_table(table_name, options = {}) # :nodoc:
end
end

def drop_function(name, options = {})
query = "DROP FUNCTION"
query = "#{query} IF EXISTS " if options[:if_exists]
query = "#{query} #{quote_table_name(name)}"
query = apply_cluster(query)
query = "#{query} SYNC" if options[:sync]

do_execute(query, format: nil)
end

def add_column(table_name, column_name, type, **options)
return if options[:if_not_exists] == true && column_exists?(table_name, column_name, type)

13 changes: 12 additions & 1 deletion lib/clickhouse-activerecord/schema_dumper.rb
Original file line number Diff line number Diff line change
@@ -34,8 +34,12 @@ def header(stream)
end

def tables(stream)
functions = @connection.functions
functions.each do |function|
function(function, stream)
end

sorted_tables = @connection.tables.sort {|a,b| @connection.show_create_table(a).match(/^CREATE\s+(MATERIALIZED\s+)?VIEW/) ? 1 : a <=> b }

sorted_tables.each do |table_name|
table(table_name, stream) unless ignored?(table_name)
end
@@ -119,6 +123,13 @@ def table(table, stream)
end
end

def function(function, stream)
stream.puts " # FUNCTION: #{function}"
sql = @connection.show_create_function(function)
stream.puts " # SQL: #{sql}" if sql
stream.puts " create_function \"#{function}\", \"#{sql.gsub(/^CREATE FUNCTION (.*?) AS/, '').strip}\"" if sql
end

def format_options(options)
if options && options[:options]
options[:options] = options[:options].gsub(/^Replicated(.*?)\('[^']+',\s*'[^']+',?\s?([^\)]*)?\)/, "\\1(\\2)")
7 changes: 7 additions & 0 deletions lib/clickhouse-activerecord/tasks.rb
Original file line number Diff line number Diff line change
@@ -42,8 +42,15 @@ def structure_dump(*args)
# sort view to last
tables.sort_by! {|table| table.match(/^CREATE\s+(MATERIALIZED\s+)?VIEW/) ? 1 : 0}

# get all functions
functions = connection.execute("SELECT create_query FROM system.functions WHERE origin = 'SQLUserDefined'")['data'].flatten

# put to file
File.open(args.first, 'w:utf-8') do |file|
functions.each do |function|
file.puts function + ";\n\n"
end

tables.each do |table|
file.puts table + ";\n\n"
end
16 changes: 16 additions & 0 deletions spec/cluster/migration_spec.rb
Original file line number Diff line number Diff line change
@@ -60,6 +60,22 @@
expect(ActiveRecord::Base.connection.tables).not_to include('some_distributed')
end
end

context "function" do
after do
ActiveRecord::Base.connection.drop_functions
end

context 'dsl' do
let(:directory) { 'dsl_create_function' }

it 'creates a function' do
subject

expect(ActiveRecord::Base.connection.functions).to match_array(['some_fun'])
end
end
end
end

context 'with alias in cluster_name' do
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# frozen_string_literal: true

class CreateSomeFunction < ActiveRecord::Migration[5.0]
def up
create_function :some_fun, "(x,y) -> x + y"
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# frozen_string_literal: true

class CreateSomeFunction < ActiveRecord::Migration[5.0]
def up
sql = <<~SQL
CREATE FUNCTION some_fun AS (x,y) -> x + y
SQL
do_execute(sql, format: nil)
end
end
24 changes: 24 additions & 0 deletions spec/single/migration_spec.rb
Original file line number Diff line number Diff line change
@@ -267,5 +267,29 @@
expect(current_schema['date'].sql_type).to eq('Date')
end
end

context 'function creation' do
after do
ActiveRecord::Base.connection.drop_functions
end

context 'plain' do
let(:directory) { 'plain_function_creation' }
it 'creates a function' do
subject

expect(ActiveRecord::Base.connection.functions).to match_array(['some_fun'])
end
end

context 'dsl' do
let(:directory) { 'dsl_create_function' }
it 'creates a function' do
subject

expect(ActiveRecord::Base.connection.functions).to match_array(['some_fun'])
end
end
end
end
end

0 comments on commit 6ad665c

Please sign in to comment.