clickhouse-activerecord 0.4.0 → 0.4.7

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8020d332d2b7b707fa2776a37fbb8d2c217ec50703962d06938be53eaae22796
4
- data.tar.gz: 6979255cb79396c6574e4b58c0e82ae39a42612fe1068b1308d98a1222c58b64
3
+ metadata.gz: ed292c2d626374fcb26194a4ca28ca34d60f8c49105b7217bfc7654a5a1b08f0
4
+ data.tar.gz: 97d4074d47e641e8202fd8796ea049f78bf76d86906ce08877f3eb96b236b116
5
5
  SHA512:
6
- metadata.gz: 3aa6d799adf1fc66d49c3dff6d02c5ad0349e467fb1067a1453de8bfcb3316abb46681367b7dfd3ebd8f82f13460cb658c4f167ff23398bba8b9cc459c5b6df1
7
- data.tar.gz: a1c8e252279678efb2fe1f8c36a8a7e1c5bfda24896438245921deee5862e94c047dced14b85408b5e1343606ec7055100b813af399568f5ee405a255c268777
6
+ metadata.gz: 23c4d834c3c3bbaa000b8bd9202064cbabf2c366a8984d1b2fd92dc51e4c6d2811301e39cd8d2b80141581dcedb8cb6647dcbe59d90eb0b0987afdd5f1b22efa
7
+ data.tar.gz: 0ac017b6614a3072fdad422db66a36201d5e3a62269230f102fbe8b6c7085f48a139cd8c0753adab11ca53a25e91e6eb327298036ebc385eb26f302c4e5178f8
@@ -1,8 +1,9 @@
1
- ### Version 0.4.0 (Sep 18, 2020)
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
- We use schema for emulate development or tests environment on PostgreSQL adapter.
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
@@ -28,7 +28,7 @@ module ActiveRecord
28
28
  @if_not_exists = if_not_exists
29
29
  @options = options
30
30
  @as = as
31
- @name = @conn.apply_cluster(name)
31
+ @name = name
32
32
  @comment = comment
33
33
  @view = view || materialized
34
34
  @materialized = materialized
@@ -156,6 +156,8 @@ module ActiveRecord
156
156
  when "true".freeze, "false".freeze
157
157
  default
158
158
  # Object identifier types
159
+ when "''"
160
+ ''
159
161
  when /\A-?\d+\z/
160
162
  $1
161
163
  else
@@ -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}", "CREATE DATABASE #{quote_table_name(name)}")
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
- stream.puts " # TABLE: #{table}"
26
- sql = @connection.do_system_execute("SHOW CREATE TABLE `#{table.gsub(/^\.inner\./, '')}`")['data'].try(:first).try(:first)
27
- stream.puts " # SQL: #{sql.gsub(/ENGINE = Replicated(.*?)\('[^']+',\s*'[^']+',?\s?([^\)]*)?\)/, "ENGINE = \\1(\\2)")}" if sql
28
- # super(table.gsub(/^\.inner\./, ''), stream)
29
-
30
- # detect view table
31
- match = sql.match(/^CREATE\s+(MATERIALIZED)\s+VIEW/)
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
- # Add materialize flag
44
- tbl.print ', view: true' if match
45
- tbl.print ', materialized: true' if match && match[1].presence
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
- table_options = @connection.table_options(table)
62
- if table_options.present?
63
- tbl.print ", #{format_options(table_options)}"
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
- columns.each do |column|
70
- raise StandardError, "Unknown type '#{column.sql_type}' for column '#{column.name}'" unless @connection.valid_type?(column.type)
71
- next if column.name == pk
72
- type, colspec = column_spec(column)
73
- tbl.print " t.#{type} #{column.name.inspect}"
74
- tbl.print ", #{format_colspec(colspec)}" if colspec.present?
75
- tbl.puts
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
@@ -1,3 +1,3 @@
1
1
  module ClickhouseActiverecord
2
- VERSION = '0.4.0'
2
+ VERSION = '0.4.7'
3
3
  end
@@ -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, :prepare_schema_migration_table, :prepare_internal_metadata_table] do
24
- load("#{Rails.root}/db/clickhouse_schema.rb")
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
- filename = "#{Rails.root}/db/clickhouse_schema.rb"
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.0
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-09-18 00:00:00.000000000 Z
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: []