clickhouse-activerecord 0.4.0 → 0.4.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +2 -1
- data/README.md +5 -1
- data/lib/active_record/connection_adapters/clickhouse/schema_creation.rb +23 -2
- data/lib/active_record/connection_adapters/clickhouse/schema_definitions.rb +1 -1
- data/lib/active_record/connection_adapters/clickhouse/schema_statements.rb +2 -0
- data/lib/active_record/connection_adapters/clickhouse_adapter.rb +29 -4
- data/lib/clickhouse-activerecord/migration.rb +2 -2
- data/lib/clickhouse-activerecord/schema_dumper.rb +51 -22
- data/lib/clickhouse-activerecord/version.rb +1 -1
- data/lib/tasks/clickhouse.rake +13 -7
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ed292c2d626374fcb26194a4ca28ca34d60f8c49105b7217bfc7654a5a1b08f0
|
4
|
+
data.tar.gz: 97d4074d47e641e8202fd8796ea049f78bf76d86906ce08877f3eb96b236b116
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 23c4d834c3c3bbaa000b8bd9202064cbabf2c366a8984d1b2fd92dc51e4c6d2811301e39cd8d2b80141581dcedb8cb6647dcbe59d90eb0b0987afdd5f1b22efa
|
7
|
+
data.tar.gz: 0ac017b6614a3072fdad422db66a36201d5e3a62269230f102fbe8b6c7085f48a139cd8c0753adab11ca53a25e91e6eb327298036ebc385eb26f302c4e5178f8
|
data/CHANGELOG.md
CHANGED
@@ -1,8 +1,9 @@
|
|
1
|
-
### Version 0.4.
|
1
|
+
### Version 0.4.4 (Sep 23, 2020)
|
2
2
|
|
3
3
|
* Full support migration and rollback database
|
4
4
|
* Support cluster and replica. Auto inject to SQL queries.
|
5
5
|
* Fix schema dump/load
|
6
|
+
* Can dump schema for using PostgreSQL
|
6
7
|
|
7
8
|
### Version 0.3.10 (Dec 20, 2019)
|
8
9
|
|
data/README.md
CHANGED
@@ -125,7 +125,11 @@ Schema load from `db/clickhouse_schema.rb` file:
|
|
125
125
|
|
126
126
|
$ rake clickhouse:schema:load
|
127
127
|
|
128
|
-
|
128
|
+
For export schema to PostgreSQL, you need use:
|
129
|
+
|
130
|
+
$ rake clickhouse:schema:dump -- --simple
|
131
|
+
|
132
|
+
Schema will be dump to `db/clickhouse_schema_simple.rb`. If default file exists, it will be auto update after migration.
|
129
133
|
|
130
134
|
Structure dump to `db/clickhouse_structure.sql` file:
|
131
135
|
|
@@ -6,15 +6,17 @@ module ActiveRecord
|
|
6
6
|
class SchemaCreation < AbstractAdapter::SchemaCreation# :nodoc:
|
7
7
|
|
8
8
|
def visit_AddColumnDefinition(o)
|
9
|
-
+"ADD COLUMN #{accept(o.column)}"
|
9
|
+
sql = +"ADD COLUMN #{accept(o.column)}"
|
10
|
+
sql << " AFTER " + quote_column_name(o.column.options[:after]) if o.column.options.key?(:after)
|
11
|
+
sql
|
10
12
|
end
|
11
13
|
|
12
14
|
def add_column_options!(sql, options)
|
13
|
-
sql << " DEFAULT #{quote_default_expression(options[:default], options[:column])}" if options_include_default?(options)
|
14
15
|
if options[:null] || options[:null].nil?
|
15
16
|
sql.gsub!(/\s+(.*)/, ' Nullable(\1)')
|
16
17
|
end
|
17
18
|
sql.gsub!(/(\sString)\(\d+\)/, '\1')
|
19
|
+
sql << " DEFAULT #{quote_default_expression(options[:default], options[:column])}" if options_include_default?(options)
|
18
20
|
sql
|
19
21
|
end
|
20
22
|
|
@@ -47,6 +49,25 @@ module ActiveRecord
|
|
47
49
|
" TEMPORARY" if o.temporary
|
48
50
|
" MATERIALIZED" if o.materialized
|
49
51
|
end
|
52
|
+
|
53
|
+
def visit_ChangeColumnDefinition(o)
|
54
|
+
column = o.column
|
55
|
+
column.sql_type = type_to_sql(column.type, column.options)
|
56
|
+
options = column_options(column)
|
57
|
+
|
58
|
+
quoted_column_name = quote_column_name(o.name)
|
59
|
+
type = column.sql_type
|
60
|
+
type = "Nullable(#{type})" if options[:null]
|
61
|
+
change_column_sql = +"MODIFY COLUMN #{quoted_column_name} #{type}"
|
62
|
+
|
63
|
+
if options.key?(:default)
|
64
|
+
quoted_default = quote_default_expression(options[:default], column)
|
65
|
+
change_column_sql << " DEFAULT #{quoted_default}"
|
66
|
+
end
|
67
|
+
|
68
|
+
change_column_sql
|
69
|
+
end
|
70
|
+
|
50
71
|
end
|
51
72
|
end
|
52
73
|
end
|
@@ -205,7 +205,7 @@ module ActiveRecord
|
|
205
205
|
def create_database(name)
|
206
206
|
sql = apply_cluster "CREATE DATABASE #{quote_table_name(name)}"
|
207
207
|
log_with_debug(sql, adapter_name) do
|
208
|
-
res = @connection.post("/?#{@config.except(:database).to_param}",
|
208
|
+
res = @connection.post("/?#{@config.except(:database).to_param}", sql)
|
209
209
|
process_response(res)
|
210
210
|
end
|
211
211
|
end
|
@@ -213,7 +213,7 @@ module ActiveRecord
|
|
213
213
|
def create_view(table_name, **options)
|
214
214
|
options.merge!(view: true)
|
215
215
|
options = apply_replica(table_name, options)
|
216
|
-
td = create_table_definition(table_name, options)
|
216
|
+
td = create_table_definition(apply_cluster(table_name), options)
|
217
217
|
yield td if block_given?
|
218
218
|
|
219
219
|
if options[:force]
|
@@ -225,7 +225,7 @@ module ActiveRecord
|
|
225
225
|
|
226
226
|
def create_table(table_name, **options)
|
227
227
|
options = apply_replica(table_name, options)
|
228
|
-
td = create_table_definition(table_name, options)
|
228
|
+
td = create_table_definition(apply_cluster(table_name), options)
|
229
229
|
yield td if block_given?
|
230
230
|
|
231
231
|
if options[:force]
|
@@ -244,10 +244,29 @@ module ActiveRecord
|
|
244
244
|
end
|
245
245
|
end
|
246
246
|
|
247
|
+
def rename_table(table_name, new_name)
|
248
|
+
do_execute apply_cluster "RENAME TABLE #{quote_table_name(table_name)} TO #{quote_table_name(new_name)}"
|
249
|
+
end
|
250
|
+
|
247
251
|
def drop_table(table_name, options = {}) # :nodoc:
|
248
252
|
do_execute apply_cluster "DROP TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}"
|
249
253
|
end
|
250
254
|
|
255
|
+
def change_column(table_name, column_name, type, options = {})
|
256
|
+
result = do_execute "ALTER TABLE #{quote_table_name(table_name)} #{change_column_for_alter(table_name, column_name, type, options)}"
|
257
|
+
raise "Error parse json response: #{result}" if result.presence && !result.is_a?(Hash)
|
258
|
+
end
|
259
|
+
|
260
|
+
def change_column_null(table_name, column_name, null, default = nil)
|
261
|
+
structure = table_structure(table_name).select{|v| v[0] == column_name.to_s}.first
|
262
|
+
raise "Column #{column_name} not found in table #{table_name}" if structure.nil?
|
263
|
+
change_column table_name, column_name, structure[1].gsub(/(Nullable\()?(.*?)\)?/, '\2'), {null: null, default: default}.compact
|
264
|
+
end
|
265
|
+
|
266
|
+
def change_column_default(table_name, column_name, default)
|
267
|
+
change_column table_name, column_name, nil, {default: default}.compact
|
268
|
+
end
|
269
|
+
|
251
270
|
def cluster
|
252
271
|
@full_config[:cluster_name]
|
253
272
|
end
|
@@ -270,6 +289,12 @@ module ActiveRecord
|
|
270
289
|
result
|
271
290
|
end
|
272
291
|
|
292
|
+
def change_column_for_alter(table_name, column_name, type, options = {})
|
293
|
+
td = create_table_definition(table_name)
|
294
|
+
cd = td.new_column_definition(column_name, type, options)
|
295
|
+
schema_creation.accept(ChangeColumnDefinition.new(cd, column_name))
|
296
|
+
end
|
297
|
+
|
273
298
|
private
|
274
299
|
|
275
300
|
def connect
|
@@ -277,7 +302,7 @@ module ActiveRecord
|
|
277
302
|
end
|
278
303
|
|
279
304
|
def apply_replica(table, options)
|
280
|
-
if replica && cluster
|
305
|
+
if replica && cluster && options[:options]
|
281
306
|
match = options[:options].match(/^(.*?MergeTree)\(([^\)]*)\)(.*?)$/)
|
282
307
|
if match
|
283
308
|
options[:options] = "Replicated#{match[1]}(#{([replica_path(table), replica].map{|v| "'#{v}'"} + [match[2].presence]).compact.join(', ')})#{match[3]}"
|
@@ -7,7 +7,7 @@ module ClickhouseActiverecord
|
|
7
7
|
unless table_exists?
|
8
8
|
version_options = connection.internal_string_options_for_primary_key
|
9
9
|
|
10
|
-
connection.create_table(table_name, id: false, options: 'ReplacingMergeTree(ver) PARTITION BY version ORDER BY (version)') do |t|
|
10
|
+
connection.create_table(table_name, id: false, options: 'ReplacingMergeTree(ver) PARTITION BY version ORDER BY (version)', if_not_exists: true) do |t|
|
11
11
|
t.string :version, version_options
|
12
12
|
t.column :active, 'Int8', null: false, default: '1'
|
13
13
|
t.datetime :ver, null: false, default: -> { 'now()' }
|
@@ -27,7 +27,7 @@ module ClickhouseActiverecord
|
|
27
27
|
unless table_exists?
|
28
28
|
key_options = connection.internal_string_options_for_primary_key
|
29
29
|
|
30
|
-
connection.create_table(table_name, id: false, options: 'MergeTree() PARTITION BY toDate(created_at) ORDER BY (created_at)') do |t|
|
30
|
+
connection.create_table(table_name, id: false, options: 'MergeTree() PARTITION BY toDate(created_at) ORDER BY (created_at)', if_not_exists: true) do |t|
|
31
31
|
t.string :key, key_options
|
32
32
|
t.string :value
|
33
33
|
t.timestamps
|
@@ -1,6 +1,19 @@
|
|
1
1
|
module ClickhouseActiverecord
|
2
2
|
class SchemaDumper < ::ActiveRecord::ConnectionAdapters::SchemaDumper
|
3
3
|
|
4
|
+
attr_accessor :simple
|
5
|
+
|
6
|
+
class << self
|
7
|
+
def dump(connection = ActiveRecord::Base.connection, stream = STDOUT, config = ActiveRecord::Base, default = false)
|
8
|
+
dumper = connection.create_schema_dumper(generate_options(config))
|
9
|
+
dumper.simple = default
|
10
|
+
dumper.dump(stream)
|
11
|
+
stream
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
4
17
|
def header(stream)
|
5
18
|
stream.puts <<HEADER
|
6
19
|
# This file is auto-generated from the current state of the database. Instead
|
@@ -8,27 +21,29 @@ module ClickhouseActiverecord
|
|
8
21
|
# incrementally modify your database, and then regenerate this schema definition.
|
9
22
|
#
|
10
23
|
# This file is the source Rails uses to define your schema when running `rails
|
11
|
-
# clickhouse:schema:load`. When creating a new database, `rails clickhouse:schema:load` tends to
|
24
|
+
# #{simple ? 'db' : 'clickhouse'}:schema:load`. When creating a new database, `rails #{simple ? 'db' : 'clickhouse'}:schema:load` tends to
|
12
25
|
# be faster and is potentially less error prone than running all of your
|
13
26
|
# migrations from scratch. Old migrations may fail to apply correctly if those
|
14
27
|
# migrations use external dependencies or application code.
|
15
28
|
#
|
16
29
|
# It's strongly recommended that you check this file into your version control system.
|
17
30
|
|
18
|
-
ClickhouseActiverecord::Schema.define(#{define_params}) do
|
31
|
+
#{simple ? 'ActiveRecord' : 'ClickhouseActiverecord'}::Schema.define(#{define_params}) do
|
19
32
|
|
20
33
|
HEADER
|
21
34
|
end
|
22
35
|
|
23
36
|
def table(table, stream)
|
24
37
|
if table.match(/^\.inner\./).nil?
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
38
|
+
unless simple
|
39
|
+
stream.puts " # TABLE: #{table}"
|
40
|
+
sql = @connection.do_system_execute("SHOW CREATE TABLE `#{table.gsub(/^\.inner\./, '')}`")['data'].try(:first).try(:first)
|
41
|
+
stream.puts " # SQL: #{sql.gsub(/ENGINE = Replicated(.*?)\('[^']+',\s*'[^']+',?\s?([^\)]*)?\)/, "ENGINE = \\1(\\2)")}" if sql
|
42
|
+
# super(table.gsub(/^\.inner\./, ''), stream)
|
43
|
+
|
44
|
+
# detect view table
|
45
|
+
match = sql.match(/^CREATE\s+(MATERIALIZED)\s+VIEW/)
|
46
|
+
end
|
32
47
|
|
33
48
|
# Copy from original dumper
|
34
49
|
columns = @connection.columns(table)
|
@@ -40,9 +55,11 @@ HEADER
|
|
40
55
|
|
41
56
|
tbl.print " create_table #{remove_prefix_and_suffix(table).inspect}"
|
42
57
|
|
43
|
-
|
44
|
-
|
45
|
-
|
58
|
+
unless simple
|
59
|
+
# Add materialize flag
|
60
|
+
tbl.print ', view: true' if match
|
61
|
+
tbl.print ', materialized: true' if match && match[1].presence
|
62
|
+
end
|
46
63
|
|
47
64
|
case pk
|
48
65
|
when String
|
@@ -58,21 +75,25 @@ HEADER
|
|
58
75
|
tbl.print ", id: false"
|
59
76
|
end
|
60
77
|
|
61
|
-
|
62
|
-
|
63
|
-
|
78
|
+
unless simple
|
79
|
+
table_options = @connection.table_options(table)
|
80
|
+
if table_options.present?
|
81
|
+
tbl.print ", #{format_options(table_options)}"
|
82
|
+
end
|
64
83
|
end
|
65
84
|
|
66
85
|
tbl.puts ", force: :cascade do |t|"
|
67
86
|
|
68
87
|
# then dump all non-primary key columns
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
88
|
+
if simple || !match
|
89
|
+
columns.each do |column|
|
90
|
+
raise StandardError, "Unknown type '#{column.sql_type}' for column '#{column.name}'" unless @connection.valid_type?(column.type)
|
91
|
+
next if column.name == pk
|
92
|
+
type, colspec = column_spec(column)
|
93
|
+
tbl.print " t.#{type} #{column.name.inspect}"
|
94
|
+
tbl.print ", #{format_colspec(colspec)}" if colspec.present?
|
95
|
+
tbl.puts
|
96
|
+
end
|
76
97
|
end
|
77
98
|
|
78
99
|
indexes_in_create(table, tbl)
|
@@ -96,5 +117,13 @@ HEADER
|
|
96
117
|
end
|
97
118
|
super
|
98
119
|
end
|
120
|
+
|
121
|
+
def format_colspec(colspec)
|
122
|
+
if simple
|
123
|
+
super.gsub(/CAST\('?([^,']*)'?,\s?'.*?'\)/, "\\1")
|
124
|
+
else
|
125
|
+
super
|
126
|
+
end
|
127
|
+
end
|
99
128
|
end
|
100
129
|
end
|
data/lib/tasks/clickhouse.rake
CHANGED
@@ -3,11 +3,11 @@
|
|
3
3
|
namespace :clickhouse do
|
4
4
|
|
5
5
|
task prepare_schema_migration_table: :environment do
|
6
|
-
ClickhouseActiverecord::SchemaMigration.create_table
|
6
|
+
ClickhouseActiverecord::SchemaMigration.create_table unless ENV['simple'] || ARGV.map{|a| a.include?('--simple') ? true : nil}.compact.any?
|
7
7
|
end
|
8
8
|
|
9
9
|
task prepare_internal_metadata_table: :environment do
|
10
|
-
ClickhouseActiverecord::InternalMetadata.create_table
|
10
|
+
ClickhouseActiverecord::InternalMetadata.create_table unless ENV['simple'] || ARGV.map{|a| a.include?('--simple') ? true : nil}.compact.any?
|
11
11
|
end
|
12
12
|
|
13
13
|
task load_config: :environment do
|
@@ -20,16 +20,19 @@ namespace :clickhouse do
|
|
20
20
|
|
21
21
|
# todo not testing
|
22
22
|
desc 'Load database schema'
|
23
|
-
task load: [:load_config, :
|
24
|
-
|
23
|
+
task load: [:load_config, :prepare_internal_metadata_table] do |t, args|
|
24
|
+
simple = ENV['simple'] || ARGV.map{|a| a.include?('--simple') ? true : nil}.compact.any? ? '_simple' : nil
|
25
|
+
ClickhouseActiverecord::SchemaMigration.drop_table
|
26
|
+
load("#{Rails.root}/db/clickhouse_schema#{simple}.rb")
|
25
27
|
end
|
26
28
|
|
27
29
|
desc 'Dump database schema'
|
28
|
-
task dump: :environment do
|
29
|
-
|
30
|
+
task dump: :environment do |t, args|
|
31
|
+
simple = ENV['simple'] || args[:simple] || ARGV.map{|a| a.include?('--simple') ? true : nil}.compact.any? ? '_simple' : nil
|
32
|
+
filename = "#{Rails.root}/db/clickhouse_schema#{simple}.rb"
|
30
33
|
File.open(filename, 'w:utf-8') do |file|
|
31
34
|
ActiveRecord::Base.establish_connection(:"#{Rails.env}_clickhouse")
|
32
|
-
ClickhouseActiverecord::SchemaDumper.dump(ActiveRecord::Base.connection, file)
|
35
|
+
ClickhouseActiverecord::SchemaDumper.dump(ActiveRecord::Base.connection, file, ActiveRecord::Base, !!simple)
|
33
36
|
end
|
34
37
|
end
|
35
38
|
|
@@ -71,6 +74,9 @@ namespace :clickhouse do
|
|
71
74
|
desc 'Migrate the clickhouse database'
|
72
75
|
task migrate: [:load_config, :prepare_schema_migration_table, :prepare_internal_metadata_table] do
|
73
76
|
Rake::Task['db:migrate'].execute
|
77
|
+
if File.exists? "#{Rails.root}/db/clickhouse_schema_simple.rb"
|
78
|
+
Rake::Task['clickhouse:schema:dump'].execute(simple: true)
|
79
|
+
end
|
74
80
|
end
|
75
81
|
|
76
82
|
desc 'Rollback the clickhouse database'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: clickhouse-activerecord
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sergey Odintsov
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-11-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -134,7 +134,7 @@ homepage: https://github.com/pnixx/clickhouse-activerecord
|
|
134
134
|
licenses:
|
135
135
|
- MIT
|
136
136
|
metadata: {}
|
137
|
-
post_install_message:
|
137
|
+
post_install_message:
|
138
138
|
rdoc_options: []
|
139
139
|
require_paths:
|
140
140
|
- lib
|
@@ -150,7 +150,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
150
150
|
version: '0'
|
151
151
|
requirements: []
|
152
152
|
rubygems_version: 3.0.1
|
153
|
-
signing_key:
|
153
|
+
signing_key:
|
154
154
|
specification_version: 4
|
155
155
|
summary: ClickHouse ActiveRecord
|
156
156
|
test_files: []
|