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.
@@ -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(name, options)
194
+ super
195
195
  end
196
196
  end
197
197
 
198
198
  def zos_create_table(name, options = {})
199
- # NOTE: this won't work for 4.0 - need to pass different initialize args :
200
- table_definition = TableDefinition.new(self)
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
- # First: Save them for later in Array "clobs"
209
- clobs = table_definition.columns.select { |x| x.type.to_s == "text" }
210
- # Second: and delete them from the original Colums-Array
211
- table_definition.columns.delete_if { |x| x.type.to_s == "text" }
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
- in_db_table_space = " IN #{@config[:database]}.#{@config[:tablespace]}"
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
- clobs.each do |clob_column|
234
+ clob_columns.each do |clob_column|
236
235
  column_name = clob_column.name.to_s
237
- execute "ALTER TABLE #{name + ' ADD COLUMN ' + column_name + ' clob'}"
238
- clob_table_name = name + '_' + column_name + '_CD_'
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 only used from {Arel::Visitors::DB2}
408
- def replace_limit_offset_for_arel!( query, sql )
409
- replace_limit_offset_with_ordering sql, query.limit.value, query.offset && query.offset.value, query.orders
410
- end
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
- def select(sql, name, binds)
439
- # DB2 does not like "= NULL", "!= NULL", or "<> NULL".
440
- exec_query(to_sql(sql.gsub(/(!=|<>)\s*null/i, "IS NOT NULL").gsub(/=\s*null/i, "IS NULL"), binds), name, binds)
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 config[:schema].present?
659
- config[:schema]
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].presence || ENV['USER']
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
@@ -248,7 +248,7 @@ module ArJdbc
248
248
 
249
249
  # @override
250
250
  def empty_insert_statement_value
251
- 'VALUES ( DEFAULT )' # won't work as Derby does need to know the columns count
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.
@@ -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
- Time.public_send(Base.default_timezone, year, mon, mday, hour, min, sec, microsec) rescue nil
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
 
@@ -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 DEFAULT CHARSET=utf8"}.merge(options))
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(ActiveRecordError, "No binary type has character length #{limit}")
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(ActiveRecordError, "No integer type has byte size #{limit}")
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(ActiveRecordError, "No text type has character length #{limit}")
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)
@@ -17,7 +17,7 @@ module ArJdbc
17
17
  def primary=(value)
18
18
  super
19
19
  @type = :integer if value && @sql_type =~ /^NUMBER$/i
20
- end
20
+ end if ::ActiveRecord::VERSION::STRING < '4.2'
21
21
 
22
22
  def type_cast(value)
23
23
  return nil if value.nil?
@@ -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