activerecord-jdbc-adapter 1.3.13 → 1.3.14
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +212 -33
- data/Appraisals +10 -12
- data/History.md +17 -0
- data/lib/arel/visitors/db2.rb +24 -1
- data/lib/arel/visitors/derby.rb +24 -0
- data/lib/arjdbc.rb +6 -0
- data/lib/arjdbc/db2/adapter.rb +61 -50
- data/lib/arjdbc/derby/adapter.rb +1 -1
- data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
- data/lib/arjdbc/jdbc/type_cast.rb +3 -2
- data/lib/arjdbc/mysql/adapter.rb +75 -4
- data/lib/arjdbc/mysql/bulk_change_table.rb +48 -8
- data/lib/arjdbc/oracle/adapter.rb +1 -0
- data/lib/arjdbc/oracle/column.rb +1 -1
- data/lib/arjdbc/postgresql/adapter.rb +3 -0
- data/lib/arjdbc/version.rb +1 -1
- data/rakelib/02-test.rake +13 -23
- data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +38 -19
- metadata +242 -240
- checksums.yaml +0 -7
data/lib/arjdbc/db2/adapter.rb
CHANGED
@@ -187,28 +187,30 @@ module ArJdbc
|
|
187
187
|
select_value("SELECT NEXT VALUE FOR #{sequence_name} FROM sysibm.sysdummy1")
|
188
188
|
end
|
189
189
|
|
190
|
-
def create_table(name, options = {})
|
190
|
+
def create_table(name, options = {}, &block)
|
191
191
|
if zos?
|
192
|
-
zos_create_table(name, options)
|
192
|
+
zos_create_table(name, options, &block)
|
193
193
|
else
|
194
|
-
super
|
194
|
+
super
|
195
195
|
end
|
196
196
|
end
|
197
197
|
|
198
198
|
def zos_create_table(name, options = {})
|
199
|
-
|
200
|
-
|
199
|
+
table_definition = new_table_definition TableDefinition, name, options[:temporary], options[:options], options[:as]
|
200
|
+
|
201
201
|
unless options[:id] == false
|
202
202
|
table_definition.primary_key(options[:primary_key] || primary_key(name))
|
203
203
|
end
|
204
204
|
|
205
|
-
yield table_definition
|
205
|
+
yield table_definition if block_given?
|
206
206
|
|
207
207
|
# Clobs in DB2 Host have to be created after the Table with an auxiliary Table.
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
208
|
+
clob_columns = []
|
209
|
+
table_definition.columns.delete_if do |column|
|
210
|
+
if column.type && column.type.to_sym == :text
|
211
|
+
clob_columns << column; true
|
212
|
+
end
|
213
|
+
end
|
212
214
|
|
213
215
|
drop_table(name, options) if options[:force] && table_exists?(name)
|
214
216
|
|
@@ -217,11 +219,8 @@ module ArJdbc
|
|
217
219
|
create_sql << table_definition.to_sql
|
218
220
|
create_sql << ") #{options[:options]}"
|
219
221
|
if @config[:database] && @config[:tablespace]
|
220
|
-
|
221
|
-
else
|
222
|
-
in_db_table_space = ''
|
222
|
+
create_sql << " IN #{@config[:database]}.#{@config[:tablespace]}"
|
223
223
|
end
|
224
|
-
create_sql << in_db_table_space
|
225
224
|
|
226
225
|
execute create_sql
|
227
226
|
|
@@ -232,14 +231,12 @@ module ArJdbc
|
|
232
231
|
#primary_column = options[:id] == true ? 'id' : options[:primary_key]
|
233
232
|
#add_index(name, (primary_column || 'id').to_s, :unique => true)
|
234
233
|
|
235
|
-
|
234
|
+
clob_columns.each do |clob_column|
|
236
235
|
column_name = clob_column.name.to_s
|
237
|
-
execute "ALTER TABLE #{name
|
238
|
-
clob_table_name = name
|
236
|
+
execute "ALTER TABLE #{name} ADD COLUMN #{column_name} clob"
|
237
|
+
clob_table_name = "#{name}_#{column_name}_CD_"
|
239
238
|
if @config[:database] && @config[:lob_tablespaces]
|
240
239
|
in_lob_table_space = " IN #{@config[:database]}.#{@config[:lob_tablespaces][name.split(".")[1]]}"
|
241
|
-
else
|
242
|
-
in_lob_table_space = ''
|
243
240
|
end
|
244
241
|
execute "CREATE AUXILIARY TABLE #{clob_table_name} #{in_lob_table_space} STORES #{name} COLUMN #{column_name}"
|
245
242
|
execute "CREATE UNIQUE INDEX #{clob_table_name} ON #{clob_table_name};"
|
@@ -350,6 +347,14 @@ module ArJdbc
|
|
350
347
|
super(type, limit, precision, scale)
|
351
348
|
end
|
352
349
|
|
350
|
+
# @private
|
351
|
+
VALUES_DEFAULT = 'VALUES ( DEFAULT )' # NOTE: Arel::Visitors::DB2 uses this
|
352
|
+
|
353
|
+
# @override
|
354
|
+
def empty_insert_statement_value
|
355
|
+
VALUES_DEFAULT # won't work as DB2 needs to know the column count
|
356
|
+
end
|
357
|
+
|
353
358
|
def add_column(table_name, column_name, type, options = {})
|
354
359
|
# The keyword COLUMN allows to use reserved names for columns (ex: date)
|
355
360
|
add_column_sql = "ALTER TABLE #{quote_table_name(table_name)} ADD COLUMN #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
|
@@ -393,7 +398,7 @@ module ArJdbc
|
|
393
398
|
|
394
399
|
limit = limit.to_i
|
395
400
|
if offset
|
396
|
-
replace_limit_offset_with_ordering(sql, limit, offset)
|
401
|
+
replace_limit_offset_with_ordering!(sql, limit, offset)
|
397
402
|
else
|
398
403
|
if limit == 1
|
399
404
|
sql << " FETCH FIRST ROW ONLY"
|
@@ -404,42 +409,48 @@ module ArJdbc
|
|
404
409
|
end
|
405
410
|
end
|
406
411
|
|
407
|
-
# @private
|
408
|
-
def
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
def replace_limit_offset_with_ordering( sql, limit, offset, orders=[] )
|
413
|
-
sql.sub!(/SELECT/i, "SELECT B.* FROM (SELECT A.*, row_number() over (#{build_ordering(orders)}) AS internal$rownum FROM (SELECT")
|
412
|
+
# @private used from {Arel::Visitors::DB2}
|
413
|
+
def replace_limit_offset_with_ordering!(sql, limit, offset, orders = nil)
|
414
|
+
over_order_by = nil # NOTE: orders matching got reverted as it was not complete and there were no case covering it ...
|
415
|
+
sql.sub!(/SELECT/i, "SELECT B.* FROM (SELECT A.*, row_number() OVER (#{over_order_by}) AS internal$rownum FROM (SELECT")
|
414
416
|
sql << ") A ) B WHERE B.internal$rownum > #{offset} AND B.internal$rownum <= #{limit + offset}"
|
415
417
|
sql
|
416
418
|
end
|
417
|
-
private :replace_limit_offset_with_ordering
|
418
|
-
|
419
|
-
def build_ordering( orders )
|
420
|
-
return '' unless orders.size > 0
|
421
|
-
# need to remove the library/table names from the orderings because we are not really ordering by them anymore
|
422
|
-
# we are actually ordering by the results of a query where the result set has the same column names
|
423
|
-
orders = orders.map do |o|
|
424
|
-
# need to keep in mind that the order clause could be wrapped in a function
|
425
|
-
matches = /(?:\w+\(|\s)*(\S+)(?:\)|\s)*/.match(o)
|
426
|
-
o = o.gsub( matches[1], matches[1].split('.').last ) if matches
|
427
|
-
o
|
428
|
-
end
|
429
|
-
"ORDER BY " + orders.join( ', ')
|
430
|
-
end
|
431
|
-
private :build_ordering
|
432
419
|
|
433
420
|
# @deprecated seems not sued nor tested ?!
|
434
421
|
def runstats_for_table(tablename, priority = 10)
|
435
422
|
@connection.execute_update "call sysproc.admin_cmd('RUNSTATS ON TABLE #{tablename} WITH DISTRIBUTION AND DETAILED INDEXES ALL UTIL_IMPACT_PRIORITY #{priority}')"
|
436
423
|
end
|
437
424
|
|
438
|
-
|
439
|
-
|
440
|
-
|
425
|
+
if ::ActiveRecord::VERSION::MAJOR >= 4
|
426
|
+
|
427
|
+
def select(sql, name = nil, binds = [])
|
428
|
+
exec_query(to_sql(suble_null_test(sql), binds), name, binds)
|
429
|
+
end
|
430
|
+
|
431
|
+
else
|
432
|
+
|
433
|
+
def select(sql, name = nil, binds = [])
|
434
|
+
exec_query_raw(to_sql(suble_null_test(sql), binds), name, binds)
|
435
|
+
end
|
436
|
+
|
441
437
|
end
|
442
438
|
|
439
|
+
# @private
|
440
|
+
IS_NOT_NULL = /(!=|<>)\s*NULL/i
|
441
|
+
# @private
|
442
|
+
IS_NULL = /=\s*NULL/i
|
443
|
+
|
444
|
+
def suble_null_test(sql)
|
445
|
+
return sql unless sql.is_a?(String)
|
446
|
+
# DB2 does not like "= NULL", "!= NULL", or "<> NULL" :
|
447
|
+
sql = sql.dup
|
448
|
+
sql.gsub! IS_NOT_NULL, 'IS NOT NULL'
|
449
|
+
sql.gsub! IS_NULL, 'IS NULL'
|
450
|
+
sql
|
451
|
+
end
|
452
|
+
private :suble_null_test
|
453
|
+
|
443
454
|
def add_index(table_name, column_name, options = {})
|
444
455
|
if ! zos? || ( table_name.to_s == ActiveRecord::Migrator.schema_migrations_table_name.to_s )
|
445
456
|
column_name = column_name.to_s if column_name.is_a?(Symbol)
|
@@ -654,14 +665,14 @@ module ArJdbc
|
|
654
665
|
def db2_schema
|
655
666
|
@db2_schema = false unless defined? @db2_schema
|
656
667
|
return @db2_schema if @db2_schema != false
|
668
|
+
schema = config[:schema]
|
657
669
|
@db2_schema =
|
658
|
-
if
|
659
|
-
|
660
|
-
elsif config[:jndi].present?
|
670
|
+
if schema then schema
|
671
|
+
elsif config[:jndi] || config[:data_source]
|
661
672
|
nil # let JNDI worry about schema
|
662
673
|
else
|
663
674
|
# LUW implementation uses schema name of username by default
|
664
|
-
config[:username]
|
675
|
+
config[:username] || ENV['USER']
|
665
676
|
end
|
666
677
|
end
|
667
678
|
|
@@ -681,4 +692,4 @@ module ActiveRecord::ConnectionAdapters
|
|
681
692
|
include ::ArJdbc::DB2::Column
|
682
693
|
end
|
683
694
|
|
684
|
-
end
|
695
|
+
end
|
data/lib/arjdbc/derby/adapter.rb
CHANGED
@@ -248,7 +248,7 @@ module ArJdbc
|
|
248
248
|
|
249
249
|
# @override
|
250
250
|
def empty_insert_statement_value
|
251
|
-
|
251
|
+
::Arel::Visitors::Derby::VALUES_DEFAULT # Derby needs to know the columns
|
252
252
|
end
|
253
253
|
|
254
254
|
# Set the sequence to the max value of the table's column.
|
Binary file
|
@@ -104,9 +104,10 @@ module ActiveRecord::ConnectionAdapters
|
|
104
104
|
return nil unless time
|
105
105
|
|
106
106
|
time -= offset
|
107
|
-
Base.default_timezone == :utc ? time : time.getlocal
|
107
|
+
ActiveRecord::Base.default_timezone == :utc ? time : time.getlocal
|
108
108
|
else
|
109
|
-
|
109
|
+
timezone = ActiveRecord::Base.default_timezone
|
110
|
+
Time.public_send(timezone, year, mon, mday, hour, min, sec, microsec) rescue nil
|
110
111
|
end
|
111
112
|
end
|
112
113
|
|
data/lib/arjdbc/mysql/adapter.rb
CHANGED
@@ -390,6 +390,76 @@ module ArJdbc
|
|
390
390
|
columns
|
391
391
|
end
|
392
392
|
|
393
|
+
if defined? ::ActiveRecord::ConnectionAdapters::AbstractAdapter::SchemaCreation
|
394
|
+
|
395
|
+
class SchemaCreation < ::ActiveRecord::ConnectionAdapters::AbstractAdapter::SchemaCreation
|
396
|
+
|
397
|
+
# @private
|
398
|
+
def visit_AddColumn(o)
|
399
|
+
add_column_position!(super, column_options(o))
|
400
|
+
end
|
401
|
+
|
402
|
+
# @private re-defined since AR 4.1
|
403
|
+
def visit_ChangeColumnDefinition(o)
|
404
|
+
column = o.column
|
405
|
+
options = o.options
|
406
|
+
sql_type = type_to_sql(o.type, options[:limit], options[:precision], options[:scale])
|
407
|
+
change_column_sql = "CHANGE #{quote_column_name(column.name)} #{quote_column_name(options[:name])} #{sql_type}"
|
408
|
+
add_column_options!(change_column_sql, options.merge(:column => column))
|
409
|
+
add_column_position!(change_column_sql, options)
|
410
|
+
end
|
411
|
+
|
412
|
+
# @private since AR 4.2
|
413
|
+
def visit_DropForeignKey(name)
|
414
|
+
"DROP FOREIGN KEY #{name}"
|
415
|
+
end
|
416
|
+
|
417
|
+
# @private since AR 4.2
|
418
|
+
def visit_TableDefinition(o)
|
419
|
+
name = o.name
|
420
|
+
create_sql = "CREATE#{' TEMPORARY' if o.temporary} TABLE #{quote_table_name(name)} "
|
421
|
+
|
422
|
+
statements = o.columns.map { |c| accept c }
|
423
|
+
statements.concat(o.indexes.map { |column_name, options| index_in_create(name, column_name, options) })
|
424
|
+
|
425
|
+
create_sql << "(#{statements.join(', ')}) " if statements.present?
|
426
|
+
create_sql << "#{o.options}"
|
427
|
+
create_sql << " AS #{@conn.to_sql(o.as)}" if o.as
|
428
|
+
create_sql
|
429
|
+
end if AR42
|
430
|
+
|
431
|
+
private
|
432
|
+
|
433
|
+
def add_column_position!(sql, options)
|
434
|
+
if options[:first]
|
435
|
+
sql << " FIRST"
|
436
|
+
elsif options[:after]
|
437
|
+
sql << " AFTER #{quote_column_name(options[:after])}"
|
438
|
+
end
|
439
|
+
sql
|
440
|
+
end
|
441
|
+
|
442
|
+
def column_options(o)
|
443
|
+
column_options = {}
|
444
|
+
column_options[:null] = o.null unless o.null.nil?
|
445
|
+
column_options[:default] = o.default unless o.default.nil?
|
446
|
+
column_options[:column] = o
|
447
|
+
column_options[:first] = o.first
|
448
|
+
column_options[:after] = o.after
|
449
|
+
column_options
|
450
|
+
end
|
451
|
+
|
452
|
+
def index_in_create(table_name, column_name, options)
|
453
|
+
index_name, index_type, index_columns, index_options, index_algorithm, index_using = @conn.add_index_options(table_name, column_name, options)
|
454
|
+
"#{index_type} INDEX #{quote_column_name(index_name)} #{index_using} (#{index_columns})#{index_options} #{index_algorithm}"
|
455
|
+
end
|
456
|
+
|
457
|
+
end
|
458
|
+
|
459
|
+
def schema_creation; SchemaCreation.new self end
|
460
|
+
|
461
|
+
end
|
462
|
+
|
393
463
|
# @private
|
394
464
|
def recreate_database(name, options = {})
|
395
465
|
drop_database(name)
|
@@ -417,7 +487,7 @@ module ArJdbc
|
|
417
487
|
|
418
488
|
# @override
|
419
489
|
def create_table(name, options = {})
|
420
|
-
super(name, {:options => "ENGINE=InnoDB
|
490
|
+
super(name, { :options => "ENGINE=InnoDB" }.merge(options))
|
421
491
|
end
|
422
492
|
|
423
493
|
def drop_table(table_name, options = {})
|
@@ -572,7 +642,7 @@ module ArJdbc
|
|
572
642
|
when 0..0xfff; "varbinary(#{limit})"
|
573
643
|
when nil; "blob"
|
574
644
|
when 0x1000..0xffffffff; "blob(#{limit})"
|
575
|
-
else raise
|
645
|
+
else raise ActiveRecord::ActiveRecordError, "No binary type has character length #{limit}"
|
576
646
|
end
|
577
647
|
when 'integer'
|
578
648
|
case limit
|
@@ -581,7 +651,7 @@ module ArJdbc
|
|
581
651
|
when 3; 'mediumint'
|
582
652
|
when nil, 4, 11; 'int(11)' # compatibility with MySQL default
|
583
653
|
when 5..8; 'bigint'
|
584
|
-
else raise
|
654
|
+
else raise ActiveRecord::ActiveRecordError, "No integer type has byte size #{limit}"
|
585
655
|
end
|
586
656
|
when 'text'
|
587
657
|
case limit
|
@@ -589,7 +659,7 @@ module ArJdbc
|
|
589
659
|
when nil, 0x100..0xffff; 'text'
|
590
660
|
when 0x10000..0xffffff; 'mediumtext'
|
591
661
|
when 0x1000000..0xffffffff; 'longtext'
|
592
|
-
else raise
|
662
|
+
else raise ActiveRecord::ActiveRecordError, "No text type has character length #{limit}"
|
593
663
|
end
|
594
664
|
else
|
595
665
|
super
|
@@ -602,6 +672,7 @@ module ArJdbc
|
|
602
672
|
end
|
603
673
|
|
604
674
|
protected
|
675
|
+
|
605
676
|
def quoted_columns_for_index(column_names, options = {})
|
606
677
|
length = options[:length] if options.is_a?(Hash)
|
607
678
|
|
@@ -2,10 +2,14 @@ module ArJdbc
|
|
2
2
|
module MySQL
|
3
3
|
module BulkChangeTable
|
4
4
|
|
5
|
+
# @private
|
6
|
+
AR41 = ActiveRecord::VERSION::STRING >= '4.1'
|
7
|
+
|
8
|
+
# @private
|
9
|
+
ChangeColumnDefinition = ActiveRecord::ConnectionAdapters::ChangeColumnDefinition if AR41
|
10
|
+
|
5
11
|
# @override
|
6
|
-
def supports_bulk_alter
|
7
|
-
true
|
8
|
-
end
|
12
|
+
def supports_bulk_alter?; true end
|
9
13
|
|
10
14
|
def bulk_change_table(table_name, operations)
|
11
15
|
sqls = operations.map do |command, args|
|
@@ -30,7 +34,13 @@ module ArJdbc
|
|
30
34
|
add_column_options!(add_column_sql, options)
|
31
35
|
add_column_position!(add_column_sql, options)
|
32
36
|
add_column_sql
|
33
|
-
end
|
37
|
+
end unless AR41
|
38
|
+
|
39
|
+
def add_column_sql(table_name, column_name, type, options = {})
|
40
|
+
td = create_table_definition table_name, options[:temporary], options[:options]
|
41
|
+
cd = td.new_column_definition(column_name, type, options)
|
42
|
+
schema_creation.visit_AddColumn cd
|
43
|
+
end if AR41
|
34
44
|
|
35
45
|
def change_column_sql(table_name, column_name, type, options = {})
|
36
46
|
column = column_for(table_name, column_name)
|
@@ -47,7 +57,22 @@ module ArJdbc
|
|
47
57
|
add_column_options!(change_column_sql, options)
|
48
58
|
add_column_position!(change_column_sql, options)
|
49
59
|
change_column_sql
|
50
|
-
end
|
60
|
+
end unless AR41
|
61
|
+
|
62
|
+
def change_column_sql(table_name, column_name, type, options = {})
|
63
|
+
column = column_for(table_name, column_name)
|
64
|
+
|
65
|
+
unless options_include_default?(options)
|
66
|
+
options[:default] = column.default
|
67
|
+
end
|
68
|
+
|
69
|
+
unless options.has_key?(:null)
|
70
|
+
options[:null] = column.null
|
71
|
+
end
|
72
|
+
|
73
|
+
options[:name] = column.name
|
74
|
+
schema_creation.accept ChangeColumnDefinition.new column, type, options
|
75
|
+
end if AR41
|
51
76
|
|
52
77
|
def rename_column_sql(table_name, column_name, new_column_name)
|
53
78
|
options = {}
|
@@ -64,7 +89,22 @@ module ArJdbc
|
|
64
89
|
rename_column_sql = "CHANGE #{quote_column_name(column_name)} #{quote_column_name(new_column_name)} #{current_type}"
|
65
90
|
add_column_options!(rename_column_sql, options)
|
66
91
|
rename_column_sql
|
67
|
-
end
|
92
|
+
end unless AR41
|
93
|
+
|
94
|
+
def rename_column_sql(table_name, column_name, new_column_name)
|
95
|
+
options = { :name => new_column_name }
|
96
|
+
|
97
|
+
if column = columns(table_name).find { |c| c.name == column_name.to_s }
|
98
|
+
options[:default] = column.default
|
99
|
+
options[:null] = column.null
|
100
|
+
options[:auto_increment] = (column.extra == "auto_increment")
|
101
|
+
else
|
102
|
+
raise ActiveRecordError, "No such column: #{table_name}.#{column_name}"
|
103
|
+
end
|
104
|
+
|
105
|
+
current_type = select_one("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE '#{column_name}'", 'SCHEMA')["Type"]
|
106
|
+
schema_creation.accept ChangeColumnDefinition.new column, current_type, options
|
107
|
+
end if AR41
|
68
108
|
|
69
109
|
def remove_column_sql(table_name, column_name, type = nil, options = {})
|
70
110
|
"DROP #{quote_column_name(column_name)}"
|
@@ -84,8 +124,8 @@ module ArJdbc
|
|
84
124
|
"DROP INDEX #{index_name}"
|
85
125
|
end
|
86
126
|
|
87
|
-
def add_timestamps_sql(table_name)
|
88
|
-
[add_column_sql(table_name, :created_at, :datetime), add_column_sql(table_name, :updated_at, :datetime)]
|
127
|
+
def add_timestamps_sql(table_name, options = {})
|
128
|
+
[add_column_sql(table_name, :created_at, :datetime, options), add_column_sql(table_name, :updated_at, :datetime, options)]
|
89
129
|
end
|
90
130
|
|
91
131
|
def remove_timestamps_sql(table_name)
|
@@ -140,6 +140,7 @@ module ArJdbc
|
|
140
140
|
def table_name_length; IDENTIFIER_LENGTH; end
|
141
141
|
def index_name_length; IDENTIFIER_LENGTH; end
|
142
142
|
def column_name_length; IDENTIFIER_LENGTH; end
|
143
|
+
def sequence_name_length; IDENTIFIER_LENGTH end
|
143
144
|
|
144
145
|
def default_sequence_name(table_name, column = nil)
|
145
146
|
# TODO: remove schema prefix if present (before truncating)
|
data/lib/arjdbc/oracle/column.rb
CHANGED
@@ -988,7 +988,10 @@ module ArJdbc
|
|
988
988
|
pk, seq = pk_and_sequence_for(new_name)
|
989
989
|
if seq == "#{table_name}_#{pk}_seq"
|
990
990
|
new_seq = "#{new_name}_#{pk}_seq"
|
991
|
+
idx = "#{table_name}_pkey"
|
992
|
+
new_idx = "#{new_name}_pkey"
|
991
993
|
execute "ALTER TABLE #{quote_table_name(seq)} RENAME TO #{quote_table_name(new_seq)}"
|
994
|
+
execute "ALTER INDEX #{quote_table_name(idx)} RENAME TO #{quote_table_name(new_idx)}"
|
992
995
|
end
|
993
996
|
rename_table_indexes(table_name, new_name) if respond_to?(:rename_table_indexes) # AR-4.0 SchemaStatements
|
994
997
|
end
|