kudu_adapter 0.1.0
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 +7 -0
- data/.gitignore +4 -0
- data/.rubocop.yml +8 -0
- data/Gemfile +9 -0
- data/LICENSE.txt +20 -0
- data/README.md +178 -0
- data/kudu_adapter.gemspec +33 -0
- data/lib/active_record/connection_adapters/kudu/column.rb +17 -0
- data/lib/active_record/connection_adapters/kudu/database_statements.rb +41 -0
- data/lib/active_record/connection_adapters/kudu/quoting.rb +51 -0
- data/lib/active_record/connection_adapters/kudu/schema_creation.rb +89 -0
- data/lib/active_record/connection_adapters/kudu/schema_statements.rb +507 -0
- data/lib/active_record/connection_adapters/kudu/sql_type_metadata.rb +16 -0
- data/lib/active_record/connection_adapters/kudu/table_definition.rb +32 -0
- data/lib/active_record/connection_adapters/kudu/type/big_int.rb +22 -0
- data/lib/active_record/connection_adapters/kudu/type/boolean.rb +23 -0
- data/lib/active_record/connection_adapters/kudu/type/char.rb +17 -0
- data/lib/active_record/connection_adapters/kudu/type/date_time.rb +21 -0
- data/lib/active_record/connection_adapters/kudu/type/double.rb +17 -0
- data/lib/active_record/connection_adapters/kudu/type/float.rb +18 -0
- data/lib/active_record/connection_adapters/kudu/type/integer.rb +22 -0
- data/lib/active_record/connection_adapters/kudu/type/small_int.rb +22 -0
- data/lib/active_record/connection_adapters/kudu/type/string.rb +17 -0
- data/lib/active_record/connection_adapters/kudu/type/time.rb +30 -0
- data/lib/active_record/connection_adapters/kudu/type/tiny_int.rb +22 -0
- data/lib/active_record/connection_adapters/kudu_adapter.rb +173 -0
- data/lib/active_record/tasks/kudu_database_tasks.rb +29 -0
- data/lib/arel/visitors/kudu.rb +7 -0
- data/lib/kudu_adapter/bind_substitution.rb +15 -0
- data/lib/kudu_adapter/table_definition_extensions.rb +28 -0
- data/lib/kudu_adapter/version.rb +5 -0
- data/lib/kudu_adapter.rb +5 -0
- data/spec/spec_config.yaml.template +8 -0
- data/spec/spec_helper.rb +124 -0
- metadata +205 -0
@@ -0,0 +1,507 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_record/connection_adapters/kudu/column'
|
4
|
+
require 'active_record/connection_adapters/kudu/schema_creation'
|
5
|
+
require 'active_record/connection_adapters/kudu/sql_type_metadata'
|
6
|
+
require 'active_record/connection_adapters/kudu/table_definition'
|
7
|
+
require 'active_record/migration/join_table'
|
8
|
+
|
9
|
+
# :nodoc:
|
10
|
+
module ActiveRecord
|
11
|
+
module ConnectionAdapters
|
12
|
+
module Kudu
|
13
|
+
include ::ActiveRecord::Migration::JoinTable
|
14
|
+
|
15
|
+
# TODO methods delegate :quote_column_name, :quote_default_expression, :type_to_sql,
|
16
|
+
# :options_include_default?, :supports_indexes_in_create?, :supports_foreign_keys_in_create?,
|
17
|
+
# :foreign_key_options, to: :@conn
|
18
|
+
# ^^^ THOSE ARE FROM SCHEMACREATION ^^^
|
19
|
+
|
20
|
+
# :nodoc:
|
21
|
+
module SchemaStatements
|
22
|
+
|
23
|
+
def table_options(table_name)
|
24
|
+
nil
|
25
|
+
end
|
26
|
+
|
27
|
+
def table_comment(table_name)
|
28
|
+
raise NotImplementedError, '#table_comment Comments not implemented'
|
29
|
+
end
|
30
|
+
|
31
|
+
def table_alias_for(table_name)
|
32
|
+
table_name.tr('.', '_')
|
33
|
+
end
|
34
|
+
|
35
|
+
def data_sources
|
36
|
+
tables | views
|
37
|
+
end
|
38
|
+
|
39
|
+
def data_source_exists?(name)
|
40
|
+
data_sources.include? name.to_s
|
41
|
+
end
|
42
|
+
|
43
|
+
def tables
|
44
|
+
connection.query('SHOW TABLES').map { |table| table[:name] }
|
45
|
+
end
|
46
|
+
|
47
|
+
def table_exists?(table_name)
|
48
|
+
tables.include? table_name.to_s
|
49
|
+
end
|
50
|
+
|
51
|
+
# Return list of existing views. As we are not supporting them,
|
52
|
+
# this list will be always empty
|
53
|
+
def views
|
54
|
+
[]
|
55
|
+
end
|
56
|
+
|
57
|
+
# Check if given view exists. As we are not supporting views,
|
58
|
+
# it'll be always false
|
59
|
+
def view_exists?(_)
|
60
|
+
false
|
61
|
+
end
|
62
|
+
|
63
|
+
def indexes(table_name, name = nil)
|
64
|
+
[]
|
65
|
+
end
|
66
|
+
|
67
|
+
def index_exists?(table_name, column_name, options = {})
|
68
|
+
raise NotImplementedError, '#index_exists? Indexing not implemented'
|
69
|
+
end
|
70
|
+
|
71
|
+
def columns(table_name)
|
72
|
+
table_structure(table_name).map do |col_def|
|
73
|
+
type = if col_def[:type] == 'int'
|
74
|
+
:integer
|
75
|
+
elsif col_def[:type] == 'bigint' && /_at$/ =~ col_def[:name]
|
76
|
+
:datetime
|
77
|
+
else
|
78
|
+
col_def[:type].to_sym
|
79
|
+
end
|
80
|
+
stm = ::ActiveRecord::ConnectionAdapters::Kudu::SqlTypeMetadata.new(sql_type: col_def[:type], type: type)
|
81
|
+
::ActiveRecord::ConnectionAdapters::Kudu::Column.new(
|
82
|
+
col_def[:name],
|
83
|
+
col_def[:default_value]&.empty? ? nil : col_def[:default_value],
|
84
|
+
stm,
|
85
|
+
col_def[:null] == 'true',
|
86
|
+
table_name,
|
87
|
+
col_def[:comment]
|
88
|
+
)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def column_exists?(table_name, column_name, type = nil, options = {})
|
93
|
+
column_name = column_name.to_s
|
94
|
+
checks = []
|
95
|
+
checks << lambda { |c| c.name == column_name }
|
96
|
+
checks << lambda { |c| c.type == type } if type
|
97
|
+
column_options_keys.each do |attr|
|
98
|
+
checks << lambda { |c| c.send(attr) == options[attr] } if options.key?(attr)
|
99
|
+
end
|
100
|
+
columns(table_name).any? { |c| checks.all? { |check| check[c] } }
|
101
|
+
end
|
102
|
+
|
103
|
+
def primary_key(table_name)
|
104
|
+
pks = table_structure(table_name).select { |col| col[:primary_key] == 'true' }
|
105
|
+
pks.map! { |pk| pk[:name] }
|
106
|
+
pks.size == 1 ? pks.first : pks
|
107
|
+
end
|
108
|
+
|
109
|
+
def create_table(table_name, comment: nil, **options)
|
110
|
+
td = create_table_definition table_name, options[:temporary], options[:options], options[:as], comment: comment
|
111
|
+
if options[:id] != false && !options[:as]
|
112
|
+
pk = options.fetch(:primary_key) do
|
113
|
+
Base.get_primary_key table_name.to_s.singularize
|
114
|
+
end
|
115
|
+
|
116
|
+
if pk.is_a?(Array)
|
117
|
+
td.primary_keys pk
|
118
|
+
else
|
119
|
+
td.primary_key pk, options.fetch(:id, :primary_key), options
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
yield td if block_given?
|
124
|
+
|
125
|
+
options[:force] && drop_table(table_name, **options, if_exists: true)
|
126
|
+
|
127
|
+
result = execute schema_creation.accept td
|
128
|
+
|
129
|
+
unless supports_indexes_in_create?
|
130
|
+
td.indexes.each do |column_name, index_options|
|
131
|
+
add_index(table_name, column_name, index_options)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
if supports_comments? && !supports_comments_in_create?
|
136
|
+
change_table_comment(table_name, comment) if comment.present?
|
137
|
+
td.columns.each do |column|
|
138
|
+
change_column_comment(table_name, column.name, column.comment) if column.comment.present?
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
result
|
143
|
+
end
|
144
|
+
|
145
|
+
def drop_table(table_name, **options)
|
146
|
+
execute "DROP TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}"
|
147
|
+
end
|
148
|
+
|
149
|
+
def drop_temp_tables
|
150
|
+
tbl = tables
|
151
|
+
to_delete = tbl.select {|tbl| tbl.include? '_temp' }
|
152
|
+
to_delete.each do |dt|
|
153
|
+
drop_table(dt)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def create_join_table(table_1, table_2, colum_options: {}, **options)
|
158
|
+
join_table_name = find_join_table_name(table_1, table_2, options)
|
159
|
+
|
160
|
+
column_options = options.delete(:column_options) || {}
|
161
|
+
column_options.reverse_merge!(null: false)
|
162
|
+
column_options.merge!(primary_key: true)
|
163
|
+
|
164
|
+
t1_column, t2_column = [table_1, table_2].map{ |t| t.to_s.singularize.foreign_key }
|
165
|
+
|
166
|
+
create_table(join_table_name, options.merge!(id: false)) do |td|
|
167
|
+
td.string t1_column, column_options
|
168
|
+
td.string t2_column, column_options
|
169
|
+
yield td if block_given?
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
def drop_join_table(table_1, table_2, options = {})
|
174
|
+
join_table_name = find_join_table_name(table_1, table_2, options)
|
175
|
+
drop_table join_table_name
|
176
|
+
end
|
177
|
+
|
178
|
+
def change_table(table_name, options = {})
|
179
|
+
if supports_bulk_alter? && options[:bulk]
|
180
|
+
recorder = ActiveRecord::Migration::CommandRecorder.new(self)
|
181
|
+
yield update_table_definition(table_name, recorder)
|
182
|
+
bulk_change_table(table_name, recorder.commands)
|
183
|
+
else
|
184
|
+
yield update_table_definition(table_name, self)
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
def rename_table(table_name, new_name)
|
189
|
+
db_name = Rails.configuration.database_configuration[Rails.env]['database']
|
190
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} RENAME TO #{quote_table_name(new_name)}"
|
191
|
+
execute "ALTER TABLE #{quote_table_name(new_name)} SET TblProperties('kudu.table_name' = 'impala::#{db_name}.#{new_name}')"
|
192
|
+
end
|
193
|
+
|
194
|
+
def add_column(table_name, column_name, type, options = {})
|
195
|
+
if options_has_primary_key(options)
|
196
|
+
# be aware of primary key columns
|
197
|
+
redefine_table_add_primary_key(table_name, column_name, type, options)
|
198
|
+
else
|
199
|
+
at = create_alter_table table_name
|
200
|
+
at.add_column(column_name, type, options)
|
201
|
+
execute schema_creation.accept at
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
def remove_columns(table_name, column_names)
|
206
|
+
raise ArgumentError.new("You must specify at least one column name. Example: remove_columns(:people, :first_name)") if column_names.empty?
|
207
|
+
column_names.each do |column_name|
|
208
|
+
remove_column(table_name, column_name)
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
def remove_column(table_name, column_name, type = nil, options = {})
|
213
|
+
if primary_keys_contain_column_name(table_name, column_name)
|
214
|
+
# be aware of primary key columns
|
215
|
+
#raise ArgumentError.new("You cannot drop primary key fields")
|
216
|
+
redefine_table_drop_primary_key(table_name, column_name, type, options)
|
217
|
+
else
|
218
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} DROP COLUMN #{quote_column_name(column_name)}"
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
def change_column(table_name, column_name, type, options)
|
223
|
+
raise NotImplementedError, '#change_column Altering columns not implemented'
|
224
|
+
end
|
225
|
+
|
226
|
+
def change_column_default(table_name, column_name, default_or_changes)
|
227
|
+
raise NotImplementedError, '#change_column_default Altering column defaults not implemented'
|
228
|
+
end
|
229
|
+
|
230
|
+
def change_column_null(table_name, column_name, null, default = nil)
|
231
|
+
raise NotImplementedError, '#change_column_null Altering column null not implemented'
|
232
|
+
end
|
233
|
+
|
234
|
+
def rename_column(table_name, column_name, new_column_name)
|
235
|
+
raise ArgumentError.new('You cannot rename primary key fields') if primary_keys_contain_column_name(table_name, column_name)
|
236
|
+
column = columns(table_name).find { |c| c.name.to_s == column_name.to_s }
|
237
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} CHANGE #{quote_column_name(column_name)} #{quote_column_name(new_column_name)} #{column.sql_type}"
|
238
|
+
end
|
239
|
+
|
240
|
+
# It will reload all data from temp table name into new one.
|
241
|
+
# We're seeking for table_name_temp while we inserting data with additional new column and it's value.
|
242
|
+
# At the end table_name_temp is dropped indeed.
|
243
|
+
def reload_table_data(table_name, column_name, options = {})
|
244
|
+
temp_table_name = table_name + '_temp'
|
245
|
+
|
246
|
+
# get table structure and remove our column name
|
247
|
+
columns = table_structure table_name
|
248
|
+
columns.reject! { |c| c[:name] == column_name.to_s }
|
249
|
+
|
250
|
+
select_qry = columns.map {|col| col[:name].to_s }.join(',')
|
251
|
+
|
252
|
+
# values with additional column name
|
253
|
+
values = select_qry + ',' + column_name.to_s
|
254
|
+
# fetch values with our column name and value to insert
|
255
|
+
fetch_values = select_qry + ',' + quote(options[:default]) + ' AS ' + column_name.to_s
|
256
|
+
|
257
|
+
insert_qry = "INSERT INTO #{quote_table_name(table_name)} (#{values}) SELECT #{fetch_values} FROM #{quote_table_name(temp_table_name)}"
|
258
|
+
execute insert_qry
|
259
|
+
|
260
|
+
drop_table(temp_table_name)
|
261
|
+
end
|
262
|
+
|
263
|
+
def add_index(table_name, column_name, options = {})
|
264
|
+
p '(add_index) Indexing not supported by Apache KUDU'
|
265
|
+
end
|
266
|
+
|
267
|
+
def remove_index(table_name, options = {})
|
268
|
+
p '(remove_index) Indexing not supported by Apache KUDU'
|
269
|
+
end
|
270
|
+
|
271
|
+
def rename_index(table_name, old_name, new_name)
|
272
|
+
p '(rename_index) Indexing not supported by Apache KUDU'
|
273
|
+
end
|
274
|
+
|
275
|
+
def index_name(table_name, options)
|
276
|
+
p '(index_name) Indexing not supported by Apache KUDU'
|
277
|
+
end
|
278
|
+
|
279
|
+
def index_name_exists?(table_name, index_name, default = nil)
|
280
|
+
p '(index_name_exists?) Indexing not supported by Apache KUDU'
|
281
|
+
end
|
282
|
+
|
283
|
+
def add_reference(table_name, ref_name, **options)
|
284
|
+
p '(add_reference) Traditional referencing not supported by Apache KUDU'
|
285
|
+
end
|
286
|
+
alias add_belongs_to add_reference
|
287
|
+
|
288
|
+
def remove_reference(table_name, ref_name, foreign_key: false, polymorphic: false, **options)
|
289
|
+
p '(remove_reference) Traditional referencing not supported by Apache KUDU'
|
290
|
+
end
|
291
|
+
|
292
|
+
def foreign_keys(table_name)
|
293
|
+
p '(foreign_keys) Foreign keys not supported by Apache KUDU'
|
294
|
+
end
|
295
|
+
|
296
|
+
def add_foreign_key(from_table, to_table, options = {})
|
297
|
+
p '(add_foreign_key) Foreign keys not supported by Apache KUDU'
|
298
|
+
end
|
299
|
+
|
300
|
+
def remove_foreign_key(from_table, options_or_to_table = {})
|
301
|
+
p '(remove_foreign_key) Foreign keys not supported by Apache KUDU'
|
302
|
+
end
|
303
|
+
|
304
|
+
def foreign_key_exists?(from_table, options_or_to_table = {})
|
305
|
+
p '(foreign_key_exists?) Foreign keys not supported by Apache KUDU'
|
306
|
+
end
|
307
|
+
|
308
|
+
def foreign_key_for(from_table, options_or_to_table = {})
|
309
|
+
p '(foreign_key_for?) Foreign keys not supported by Apache KUDU'
|
310
|
+
end
|
311
|
+
|
312
|
+
def foreign_key_for!(from_table, options_or_to_table = {})
|
313
|
+
p '(foreign_key_for!) Foreign keys not supported by Apache KUDU'
|
314
|
+
end
|
315
|
+
|
316
|
+
def foreign_key_for_column_for(table_name)
|
317
|
+
p '(foreign_key_for_column_for) Foreign keys not supported by Apache KUDU'
|
318
|
+
end
|
319
|
+
|
320
|
+
def foreign_key_options(from_table, to_table, options)
|
321
|
+
p '(foreign_key_options) Foreign keys not supported by Apache KUDU'
|
322
|
+
end
|
323
|
+
|
324
|
+
def assume_migrated_upto_version(version, migrations_paths)
|
325
|
+
migrations_paths = Array(migrations_paths)
|
326
|
+
version = version.to_i
|
327
|
+
sm_table = quote_table_name(ActiveRecord::SchemaMigration.table_name)
|
328
|
+
|
329
|
+
migrated = ActiveRecord::SchemaMigration.all_versions.map(&:to_i)
|
330
|
+
versions = ActiveRecord::Migrator.migration_files(migrations_paths).map do |file|
|
331
|
+
ActiveRecord::Migrator.parse_migration_filename(file).first.to_i
|
332
|
+
end
|
333
|
+
|
334
|
+
unless migrated.include?(version)
|
335
|
+
execute "INSERT INTO #{sm_table} (version) VALUES (#{quote(version.to_s)})"
|
336
|
+
end
|
337
|
+
|
338
|
+
inserting = (versions - migrated).select { |v| v < version }
|
339
|
+
if inserting.any?
|
340
|
+
if (duplicate = inserting.detect { |v| inserting.count(v) > 1 })
|
341
|
+
raise "Duplicate migration #{duplicate}. Please renumber your migrations to resolve the conflict."
|
342
|
+
end
|
343
|
+
if supports_multi_insert?
|
344
|
+
execute insert_versions_sql(inserting)
|
345
|
+
else
|
346
|
+
inserting.each do |v|
|
347
|
+
execute insert_versions_sql(v.to_s)
|
348
|
+
end
|
349
|
+
end
|
350
|
+
end
|
351
|
+
end
|
352
|
+
|
353
|
+
def type_to_sql(type, limit: nil, precision: nil, scale: nil, **)
|
354
|
+
case type
|
355
|
+
when 'integer'
|
356
|
+
case limit
|
357
|
+
when 1 then 'TINYINT'
|
358
|
+
when 2 then 'SMALLINT'
|
359
|
+
when 3..4, nil then 'INT'
|
360
|
+
when 5..8 then 'BIGINT'
|
361
|
+
else
|
362
|
+
raise(ActiveRecordError, 'Invalid integer precision')
|
363
|
+
end
|
364
|
+
else
|
365
|
+
super
|
366
|
+
end
|
367
|
+
end
|
368
|
+
|
369
|
+
def columns_for_distinct(columns, orders)
|
370
|
+
columns
|
371
|
+
end
|
372
|
+
|
373
|
+
def add_timestamps(table_name, options = {})
|
374
|
+
options[:null] = false if options[:null].nil?
|
375
|
+
add_column table_name, :created_at, :bigint, options
|
376
|
+
add_column table_name, :updated_at, :bigint, options
|
377
|
+
end
|
378
|
+
|
379
|
+
def remove_timestamps(table_name, options = {})
|
380
|
+
remove_column table_name, :updated_at
|
381
|
+
remove_column table_name, :created_at
|
382
|
+
end
|
383
|
+
|
384
|
+
def update_table_definition(table_name, base)
|
385
|
+
Table.new(table_name, base)
|
386
|
+
end
|
387
|
+
|
388
|
+
def add_index_options(table_name, column_name, comment: nil, **options)
|
389
|
+
p '(add_index_options) Indexing not supported by Apache KUDU'
|
390
|
+
end
|
391
|
+
|
392
|
+
def options_include_default?(options)
|
393
|
+
options.include?(:default) && !(options[:null] == false && options[:default].nil?)
|
394
|
+
end
|
395
|
+
|
396
|
+
def change_table_comment(table_name, comment)
|
397
|
+
p '(change_table_comment) Altering table comments not supported by Apache KUDU'
|
398
|
+
end
|
399
|
+
|
400
|
+
def change_column_comment(table_name, column_name, comment)
|
401
|
+
p '(change_column_comment) Altering column comments not supported by Apache KUDU'
|
402
|
+
end
|
403
|
+
|
404
|
+
private
|
405
|
+
|
406
|
+
def schema_creation
|
407
|
+
::ActiveRecord::ConnectionAdapters::Kudu::SchemaCreation.new(self)
|
408
|
+
end
|
409
|
+
|
410
|
+
def create_table_definition(*args)
|
411
|
+
::ActiveRecord::ConnectionAdapters::Kudu::TableDefinition.new(*args)
|
412
|
+
end
|
413
|
+
|
414
|
+
def table_structure(table_name)
|
415
|
+
quoted = quote_table_name table_name
|
416
|
+
connection.query('DESCRIBE ' + quoted)
|
417
|
+
end
|
418
|
+
|
419
|
+
# check if options contains primary_key
|
420
|
+
def options_has_primary_key(options)
|
421
|
+
options[:primary_key] = false if options[:primary_key].nil?
|
422
|
+
options[:primary_key]
|
423
|
+
end
|
424
|
+
|
425
|
+
def primary_keys_contain_column_name(table_name, column_name)
|
426
|
+
pks = primary_key(table_name)
|
427
|
+
pks.include? column_name.to_s
|
428
|
+
end
|
429
|
+
|
430
|
+
def set_options_from_column_definition(column)
|
431
|
+
opt = {}
|
432
|
+
opt[:primary_key] = ActiveModel::Type::Boolean.new.cast(column[:primary_key]) if column[:primary_key].present?
|
433
|
+
opt[:null] = ActiveModel::Type::Boolean.new.cast(column[:nullable]) if column[:nullable].present?
|
434
|
+
opt[:default] = lookup_cast_type(column[:type]).serialize(column[:default_value]) if column[:default_value].present?
|
435
|
+
# TODO: Do we have more options ?
|
436
|
+
opt
|
437
|
+
end
|
438
|
+
|
439
|
+
# This method will copy existing structure of table with added new field.
|
440
|
+
# It works only if we're adding new primary key on existing table.
|
441
|
+
def redefine_table_add_primary_key(table_name, column_name, type, options = {})
|
442
|
+
|
443
|
+
redefined_table_name = table_name + '_redefined'
|
444
|
+
temp_table_name = table_name + '_temp'
|
445
|
+
|
446
|
+
columns = table_structure table_name
|
447
|
+
pk_columns = columns.select {|c| c[:primary_key] == 'true'}
|
448
|
+
non_pk_columns = columns.select {|c| c[:primary_key] == 'false'}
|
449
|
+
|
450
|
+
create_table(redefined_table_name, { id: false }) do |td|
|
451
|
+
|
452
|
+
# existing pk columns
|
453
|
+
pk_columns.each do |col|
|
454
|
+
td.send col[:type].to_sym, col[:name], set_options_from_column_definition(col)
|
455
|
+
end
|
456
|
+
|
457
|
+
# add new column
|
458
|
+
td.send type, column_name, options
|
459
|
+
|
460
|
+
non_pk_columns.each do |col|
|
461
|
+
td.send col[:type].to_sym, col[:name], set_options_from_column_definition(col)
|
462
|
+
end
|
463
|
+
|
464
|
+
end
|
465
|
+
|
466
|
+
# rename existing table to temp
|
467
|
+
rename_table(table_name, temp_table_name)
|
468
|
+
# rename newly created to active one
|
469
|
+
rename_table(redefined_table_name, table_name)
|
470
|
+
|
471
|
+
end
|
472
|
+
|
473
|
+
# This method will copy existing structure of table with primary key field removed.
|
474
|
+
# It works only if we're removing primary key on existing table.
|
475
|
+
def redefine_table_drop_primary_key(table_name, column_name, type, options = {})
|
476
|
+
|
477
|
+
redefined_table_name = table_name + '_redefined'
|
478
|
+
temp_table_name = table_name + '_temp'
|
479
|
+
|
480
|
+
columns = table_structure table_name
|
481
|
+
columns.reject! { |c| c[:name] == column_name.to_s }
|
482
|
+
|
483
|
+
create_table(redefined_table_name, { id: false }) do |td|
|
484
|
+
columns.each do |col|
|
485
|
+
td.send col[:type].to_sym, col[:name], set_options_from_column_definition(col)
|
486
|
+
end
|
487
|
+
end
|
488
|
+
|
489
|
+
# rename existing table to temp
|
490
|
+
rename_table(table_name, temp_table_name)
|
491
|
+
# rename newly created to active one
|
492
|
+
rename_table(redefined_table_name, table_name)
|
493
|
+
|
494
|
+
# copy reduced existing data into new table
|
495
|
+
select_qry = columns.map {|col| col[:name].to_s }.join(',')
|
496
|
+
copy_qry = "INSERT INTO #{quote_table_name(table_name)} (#{select_qry}) SELECT #{select_qry} FROM #{quote_table_name(temp_table_name)}"
|
497
|
+
execute copy_qry
|
498
|
+
|
499
|
+
# finally, drop temp table
|
500
|
+
drop_table(temp_table_name)
|
501
|
+
|
502
|
+
end
|
503
|
+
|
504
|
+
end
|
505
|
+
end
|
506
|
+
end
|
507
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_record/connection_adapters/sql_type_metadata'
|
4
|
+
|
5
|
+
module ActiveRecord
|
6
|
+
module ConnectionAdapters
|
7
|
+
module Kudu
|
8
|
+
# :nodoc:
|
9
|
+
class SqlTypeMetadata < ::ActiveRecord::ConnectionAdapters::SqlTypeMetadata
|
10
|
+
def initialize(sql_type: nil, type: nil)
|
11
|
+
super(sql_type: sql_type, type: type, limit: nil, precision: nil, scale: nil)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_record/connection_adapters/abstract/schema_definitions'
|
4
|
+
|
5
|
+
module ActiveRecord
|
6
|
+
module ConnectionAdapters
|
7
|
+
module Kudu
|
8
|
+
# :nodoc:
|
9
|
+
class TableDefinition < ::ActiveRecord::ConnectionAdapters::TableDefinition
|
10
|
+
attr_reader :external
|
11
|
+
attr_reader :partitions_count
|
12
|
+
attr_reader :partition_columns
|
13
|
+
|
14
|
+
def initialize(name, temporary = false, options = nil, as = nil, comment: nil)
|
15
|
+
super
|
16
|
+
@external = options&.[](:external)
|
17
|
+
@partitions_count = options&.[](:partitions_count)
|
18
|
+
@partition_columns = options&.[](:partition_columns)
|
19
|
+
end
|
20
|
+
|
21
|
+
def double(*args, **options)
|
22
|
+
args.each { |name| column(name, :double, options) }
|
23
|
+
end
|
24
|
+
|
25
|
+
def float(*args, **options)
|
26
|
+
args.each { |name| column(name, :float, options) }
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_model/type/integer'
|
4
|
+
|
5
|
+
module ActiveRecord
|
6
|
+
module ConnectionAdapters
|
7
|
+
module Kudu
|
8
|
+
module Type
|
9
|
+
# :nodoc:
|
10
|
+
class BigInt < ::ActiveModel::Type::Integer
|
11
|
+
def type
|
12
|
+
:bigint
|
13
|
+
end
|
14
|
+
|
15
|
+
def limit
|
16
|
+
8
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_model/type/boolean'
|
4
|
+
|
5
|
+
module ActiveRecord
|
6
|
+
module ConnectionAdapters
|
7
|
+
module Kudu
|
8
|
+
module Type
|
9
|
+
class Boolean < ::ActiveModel::Type::Boolean
|
10
|
+
|
11
|
+
def type
|
12
|
+
:boolean
|
13
|
+
end
|
14
|
+
|
15
|
+
def serialize(value)
|
16
|
+
ActiveModel::Type::Boolean.new.cast(value)
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_model/type/string'
|
4
|
+
|
5
|
+
module ActiveRecord
|
6
|
+
module ConnectionAdapters
|
7
|
+
module Kudu
|
8
|
+
module Type
|
9
|
+
class Char < ::ActiveModel::Type::String
|
10
|
+
def type
|
11
|
+
:char
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_model/type/helpers/time_value'
|
4
|
+
require 'active_record/connection_adapters/kudu/type/time'
|
5
|
+
|
6
|
+
module ActiveRecord
|
7
|
+
module ConnectionAdapters
|
8
|
+
module Kudu
|
9
|
+
module Type
|
10
|
+
include ::ActiveModel::Type::Helpers::TimeValue
|
11
|
+
|
12
|
+
# :nodoc:
|
13
|
+
class DateTime < ::ActiveRecord::ConnectionAdapters::Kudu::Type::Time
|
14
|
+
def type
|
15
|
+
:datetime
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_model/type/float'
|
4
|
+
|
5
|
+
module ActiveRecord
|
6
|
+
module ConnectionAdapters
|
7
|
+
module Kudu
|
8
|
+
module Type
|
9
|
+
class Double < ::ActiveModel::Type::Float
|
10
|
+
def type
|
11
|
+
:double
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_model/type/float'
|
4
|
+
|
5
|
+
module ActiveRecord
|
6
|
+
module ConnectionAdapters
|
7
|
+
module Kudu
|
8
|
+
module Type
|
9
|
+
# :nodoc:
|
10
|
+
class Float < ::ActiveModel::Type::Float
|
11
|
+
def type
|
12
|
+
:float
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|