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 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: []