activerecord-jdbc-adapter 1.3.0.beta1 → 1.3.0.beta2

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.
Files changed (161) hide show
  1. data/.gitignore +12 -11
  2. data/.travis.yml +36 -7
  3. data/Appraisals +3 -3
  4. data/Gemfile +1 -1
  5. data/Gemfile.lock +13 -6
  6. data/History.txt +64 -0
  7. data/README.md +8 -1
  8. data/Rakefile +3 -1
  9. data/gemfiles/rails23.gemfile +1 -1
  10. data/gemfiles/rails23.gemfile.lock +6 -5
  11. data/gemfiles/rails30.gemfile +1 -1
  12. data/gemfiles/rails30.gemfile.lock +7 -6
  13. data/gemfiles/rails31.gemfile +1 -1
  14. data/gemfiles/rails31.gemfile.lock +6 -5
  15. data/gemfiles/rails32.gemfile +1 -1
  16. data/gemfiles/rails32.gemfile.lock +6 -5
  17. data/gemfiles/rails40.gemfile +2 -4
  18. data/gemfiles/rails40.gemfile.lock +37 -51
  19. data/lib/active_record/connection_adapters/as400_adapter.rb +2 -0
  20. data/lib/active_record/connection_adapters/db2_adapter.rb +1 -1
  21. data/lib/arel/visitors/db2.rb +5 -1
  22. data/lib/arel/visitors/hsqldb.rb +1 -0
  23. data/lib/arel/visitors/sql_server.rb +55 -13
  24. data/lib/arjdbc/db2/adapter.rb +197 -227
  25. data/lib/arjdbc/db2/as400.rb +124 -0
  26. data/lib/arjdbc/db2/connection_methods.rb +20 -1
  27. data/lib/arjdbc/derby/adapter.rb +17 -85
  28. data/lib/arjdbc/derby/connection_methods.rb +2 -1
  29. data/lib/arjdbc/discover.rb +55 -47
  30. data/lib/arjdbc/h2/adapter.rb +52 -18
  31. data/lib/arjdbc/h2/connection_methods.rb +10 -2
  32. data/lib/arjdbc/hsqldb/adapter.rb +33 -9
  33. data/lib/arjdbc/hsqldb/connection_methods.rb +10 -2
  34. data/lib/arjdbc/informix.rb +2 -1
  35. data/lib/arjdbc/jdbc.rb +5 -1
  36. data/lib/arjdbc/jdbc/adapter.rb +167 -89
  37. data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
  38. data/lib/arjdbc/jdbc/adapter_require.rb +46 -0
  39. data/lib/arjdbc/jdbc/base_ext.rb +25 -3
  40. data/lib/arjdbc/jdbc/callbacks.rb +9 -8
  41. data/lib/arjdbc/jdbc/column.rb +8 -20
  42. data/lib/arjdbc/jdbc/connection.rb +69 -80
  43. data/lib/arjdbc/jdbc/extension.rb +6 -8
  44. data/lib/arjdbc/jdbc/jdbc.rake +3 -141
  45. data/lib/arjdbc/jdbc/rake_tasks.rb +3 -10
  46. data/lib/arjdbc/mssql/adapter.rb +108 -34
  47. data/lib/arjdbc/mssql/connection_methods.rb +3 -1
  48. data/lib/arjdbc/mssql/limit_helpers.rb +3 -2
  49. data/lib/arjdbc/mssql/lock_helpers.rb +5 -1
  50. data/lib/arjdbc/mysql/adapter.rb +127 -70
  51. data/lib/arjdbc/mysql/connection_methods.rb +5 -2
  52. data/lib/arjdbc/oracle/adapter.rb +124 -94
  53. data/lib/arjdbc/oracle/connection_methods.rb +2 -1
  54. data/lib/arjdbc/postgresql/adapter.rb +99 -67
  55. data/lib/arjdbc/postgresql/column_cast.rb +3 -5
  56. data/lib/arjdbc/postgresql/connection_methods.rb +6 -6
  57. data/lib/arjdbc/railtie.rb +3 -1
  58. data/lib/arjdbc/sqlite3/adapter.rb +60 -43
  59. data/lib/arjdbc/sqlite3/connection_methods.rb +9 -9
  60. data/lib/arjdbc/sybase.rb +1 -1
  61. data/lib/arjdbc/tasks.rb +13 -0
  62. data/lib/arjdbc/tasks/database_tasks.rb +50 -0
  63. data/lib/arjdbc/tasks/databases.rake +89 -0
  64. data/lib/arjdbc/tasks/databases3.rake +203 -0
  65. data/lib/arjdbc/tasks/databases4.rake +39 -0
  66. data/lib/arjdbc/tasks/db2_database_tasks.rb +104 -0
  67. data/lib/arjdbc/tasks/derby_database_tasks.rb +95 -0
  68. data/lib/arjdbc/tasks/h2_database_tasks.rb +29 -0
  69. data/lib/arjdbc/tasks/hsqldb_database_tasks.rb +70 -0
  70. data/lib/arjdbc/tasks/jdbc_database_tasks.rb +122 -0
  71. data/lib/arjdbc/tasks/mssql_database_tasks.rb +36 -0
  72. data/lib/arjdbc/tasks/oracle/enhanced_structure_dump.rb +297 -0
  73. data/lib/arjdbc/tasks/oracle_database_tasks.rb +62 -0
  74. data/lib/arjdbc/version.rb +1 -1
  75. data/pom.xml +11 -12
  76. data/rails_generators/jdbc_generator.rb +1 -1
  77. data/rails_generators/templates/config/initializers/jdbc.rb +8 -5
  78. data/rails_generators/templates/lib/tasks/jdbc.rake +7 -4
  79. data/rakelib/02-test.rake +42 -15
  80. data/rakelib/compile.rake +29 -2
  81. data/rakelib/db.rake +2 -1
  82. data/rakelib/rails.rake +23 -6
  83. data/src/java/arjdbc/ArJdbcModule.java +175 -0
  84. data/src/java/arjdbc/db2/DB2Module.java +2 -1
  85. data/src/java/arjdbc/derby/DerbyModule.java +5 -24
  86. data/src/java/arjdbc/hsqldb/HSQLDBModule.java +3 -2
  87. data/src/java/arjdbc/jdbc/AdapterJavaService.java +3 -46
  88. data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +1001 -259
  89. data/src/java/arjdbc/mssql/MSSQLModule.java +2 -1
  90. data/src/java/arjdbc/mysql/MySQLModule.java +4 -3
  91. data/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +12 -7
  92. data/src/java/arjdbc/oracle/OracleModule.java +2 -1
  93. data/src/java/arjdbc/sqlite3/SQLite3Module.java +2 -1
  94. data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +12 -0
  95. data/test/db/db2.rb +14 -7
  96. data/test/db/db2/rake_test.rb +82 -0
  97. data/test/db/db2/rake_test_data.sql +35 -0
  98. data/test/db/db2/simple_test.rb +20 -0
  99. data/test/db/db2/unit_test.rb +3 -1
  100. data/test/db/derby.rb +7 -5
  101. data/test/db/derby/rake_test.rb +96 -0
  102. data/test/db/derby/simple_test.rb +10 -2
  103. data/test/db/h2.rb +6 -8
  104. data/test/db/h2/identity_column_test.rb +35 -0
  105. data/test/db/h2/offset_test.rb +49 -0
  106. data/test/db/h2/rake_test.rb +98 -0
  107. data/test/db/h2/schema_dump_test.rb +5 -1
  108. data/test/db/hsqldb.rb +6 -10
  109. data/test/db/hsqldb/rake_test.rb +101 -0
  110. data/test/db/hsqldb/schema_dump_test.rb +5 -1
  111. data/test/db/hsqldb/simple_test.rb +8 -0
  112. data/test/db/jndi_config.rb +1 -3
  113. data/test/db/jndi_pooled_config.rb +1 -3
  114. data/test/db/mssql/limit_offset_test.rb +23 -14
  115. data/test/db/mssql/rake_test.rb +143 -0
  116. data/test/db/mysql/_rails_test_mysql.32.out +1069 -1252
  117. data/test/db/mysql/nonstandard_primary_key_test.rb +21 -24
  118. data/test/db/mysql/rake_test.rb +97 -0
  119. data/test/db/mysql/schema_dump_test.rb +11 -11
  120. data/test/db/mysql/simple_test.rb +52 -3
  121. data/test/db/mysql/statement_escaping_test.rb +46 -0
  122. data/test/db/oracle/rake_test.rb +100 -0
  123. data/test/db/oracle/simple_test.rb +48 -0
  124. data/test/db/postgres/_rails_test_postgres.32.out +998 -1370
  125. data/test/db/postgres/active_schema_unit_test.rb +68 -0
  126. data/test/db/postgres/connection_test.rb +10 -2
  127. data/test/db/postgres/data_types_test.rb +2 -2
  128. data/test/db/postgres/ltree_test.rb +6 -5
  129. data/test/db/postgres/native_types_test.rb +1 -5
  130. data/test/db/postgres/rake_test.rb +117 -0
  131. data/test/db/postgres/schema_dump_test.rb +9 -2
  132. data/test/db/postgres/schema_test.rb +4 -2
  133. data/test/db/postgres/simple_test.rb +57 -16
  134. data/test/db/sqlite3.rb +3 -10
  135. data/test/db/sqlite3/_rails_test_sqlite3.32.out +1070 -1298
  136. data/test/db/sqlite3/rake_test.rb +71 -0
  137. data/test/db/sqlite3/simple_test.rb +9 -9
  138. data/test/has_many_through.rb +4 -1
  139. data/test/jdbc/db2.rb +14 -1
  140. data/test/jdbc_column_test.rb +23 -0
  141. data/test/{generic_jdbc_connection_test.rb → jdbc_connection_test.rb} +22 -17
  142. data/test/jndi_callbacks_test.rb +26 -28
  143. data/test/jndi_test.rb +7 -16
  144. data/test/models/data_types.rb +2 -1
  145. data/test/models/thing.rb +1 -0
  146. data/test/rails/mysql.rb +13 -0
  147. data/test/rails/sqlite3/version.rb +6 -0
  148. data/test/rails_stub.rb +31 -0
  149. data/test/rake_test_support.rb +298 -0
  150. data/test/serialize.rb +2 -4
  151. data/test/{helper.rb → shared_helper.rb} +0 -0
  152. data/test/simple.rb +167 -93
  153. data/test/test_helper.rb +52 -16
  154. metadata +388 -354
  155. data/lib/pg.rb +0 -26
  156. data/test/abstract_db_create.rb +0 -139
  157. data/test/activerecord/connection_adapters/type_conversion_test.rb +0 -36
  158. data/test/db/mssql/db_create_test.rb +0 -29
  159. data/test/db/mysql/db_create_test.rb +0 -33
  160. data/test/db/postgres/db_create_test.rb +0 -44
  161. data/test/db/postgres/db_drop_test.rb +0 -17
@@ -9,13 +9,16 @@ ArJdbc::ConnectionMethods.module_eval do
9
9
  config[:port] ||= 3306
10
10
  config[:url] ||= "jdbc:mysql://#{config[:host]}:#{config[:port]}/#{config[:database]}"
11
11
  config[:driver] ||= defined?(::Jdbc::MySQL.driver_name) ? ::Jdbc::MySQL.driver_name : 'com.mysql.jdbc.Driver'
12
- config[:adapter_class] = ActiveRecord::ConnectionAdapters::MysqlAdapter
13
- config[:adapter_spec] = ::ArJdbc::MySQL
12
+ config[:adapter_spec] ||= ::ArJdbc::MySQL
13
+ config[:adapter_class] = ActiveRecord::ConnectionAdapters::MysqlAdapter unless config.key?(:adapter_class)
14
+ # config[:connection_alive_sql] ||= 'SELECT 1'
15
+
14
16
  options = (config[:options] ||= {})
15
17
  options['zeroDateTimeBehavior'] ||= 'convertToNull'
16
18
  options['jdbcCompliantTruncation'] ||= 'false'
17
19
  options['useUnicode'] ||= 'true'
18
20
  options['characterEncoding'] = config[:encoding] || 'utf8'
21
+
19
22
  connection = jdbc_connection(config)
20
23
  ::ArJdbc::MySQL.kill_cancel_timer(connection.raw_connection)
21
24
  connection
@@ -1,31 +1,31 @@
1
- require 'arjdbc/jdbc/serialized_attributes_helper'
1
+ ArJdbc.load_java_part :Oracle
2
2
 
3
3
  module ArJdbc
4
4
  module Oracle
5
5
 
6
- @@_lob_callback_added = nil
7
-
8
- def self.extended(base)
9
- unless @@_lob_callback_added
10
- ActiveRecord::Base.class_eval do
11
- def after_save_with_oracle_lob
12
- self.class.columns.select { |c| c.sql_type =~ /LOB\(|LOB$/i }.each do |column|
13
- value = ::ArJdbc::SerializedAttributesHelper.dump_column_value(self, column)
14
- next if value.nil? || (value == '')
15
-
16
- connection.write_large_object(
17
- column.type == :binary, column.name,
18
- self.class.table_name, self.class.primary_key,
19
- quote_value(id), value
20
- )
21
- end
6
+ def self.extended(adapter); initialize!; end
7
+
8
+ @@_initialized = nil
9
+
10
+ def self.initialize!
11
+ return if @@_initialized; @@_initialized = true
12
+
13
+ require 'arjdbc/jdbc/serialized_attributes_helper'
14
+ ActiveRecord::Base.class_eval do
15
+ def after_save_with_oracle_lob
16
+ self.class.columns.select { |c| c.sql_type =~ /LOB\(|LOB$/i }.each do |column|
17
+ value = ::ArJdbc::SerializedAttributesHelper.dump_column_value(self, column)
18
+ next if value.nil? || (value == '')
19
+
20
+ self.class.connection.write_large_object(
21
+ column.type == :binary, column.name,
22
+ self.class.table_name, self.class.primary_key,
23
+ self.class.connection.quote(id), value
24
+ )
22
25
  end
23
26
  end
24
-
25
- ActiveRecord::Base.after_save :after_save_with_oracle_lob
26
-
27
- @@_lob_callback_added = true
28
27
  end
28
+ ActiveRecord::Base.after_save :after_save_with_oracle_lob
29
29
 
30
30
  unless ActiveRecord::ConnectionAdapters::AbstractAdapter.
31
31
  instance_methods(false).detect { |m| m.to_s == "prefetch_primary_key?" }
@@ -33,7 +33,7 @@ module ArJdbc
33
33
  ActiveRecord::Base.extend ArJdbc::QuotedPrimaryKeyExtension
34
34
  end
35
35
  end
36
-
36
+
37
37
  def self.column_selector
38
38
  [ /oracle/i, lambda { |cfg, column| column.extend(::ArJdbc::Oracle::Column) } ]
39
39
  end
@@ -46,6 +46,16 @@ module ArJdbc
46
46
  ::ActiveRecord::ConnectionAdapters::OracleColumn
47
47
  end
48
48
 
49
+ @@emulate_booleans = true
50
+
51
+ # Boolean emulation can be disabled using :
52
+ #
53
+ # ArJdbc::Oracle.emulate_booleans = false
54
+ #
55
+ # @see ActiveRecord::ConnectionAdapters::OracleAdapter#emulate_booleans
56
+ def self.emulate_booleans; @@emulate_booleans; end
57
+ def self.emulate_booleans=(emulate); @@emulate_booleans = emulate; end
58
+
49
59
  module Column
50
60
 
51
61
  def primary=(value)
@@ -56,9 +66,9 @@ module ArJdbc
56
66
  def type_cast(value)
57
67
  return nil if value.nil?
58
68
  case type
59
- when :datetime then ArJdbc::Oracle::Column.string_to_time(value)
60
- when :timestamp then ArJdbc::Oracle::Column.string_to_time(value)
61
- when :boolean then ArJdbc::Oracle::Column.value_to_boolean(value)
69
+ when :datetime then Column.string_to_time(value)
70
+ when :timestamp then Column.string_to_time(value)
71
+ when :boolean then Column.value_to_boolean(value)
62
72
  else
63
73
  super
64
74
  end
@@ -117,11 +127,11 @@ module ArJdbc
117
127
 
118
128
  def simplified_type(field_type)
119
129
  case field_type
120
- when /^number\(1\)$/i then :boolean
121
130
  when /char/i then :string
122
131
  when /float|double/i then :float
123
132
  when /int/i then :integer
124
- when /num|dec|real/i then extract_scale(field_type) == 0 ? :integer : :decimal
133
+ when /^number\(1\)$/i then Oracle.emulate_booleans ? :boolean : :integer
134
+ when /^num|dec|real/i then extract_scale(field_type) == 0 ? :integer : :decimal
125
135
  # Oracle TIMESTAMP stores the date and time to up to 9 digits of sub-second precision
126
136
  when /TIMESTAMP/i then :timestamp
127
137
  # Oracle DATE stores the date and time to the second
@@ -246,14 +256,6 @@ module ArJdbc
246
256
  seq_name = options[:sequence_name] || default_sequence_name(name)
247
257
  execute "DROP SEQUENCE #{seq_name}" rescue nil
248
258
  end
249
-
250
- def recreate_database(name, options = {})
251
- tables.each{ |table| drop_table(table) }
252
- end
253
-
254
- def drop_database(name)
255
- recreate_database(name)
256
- end
257
259
 
258
260
  def type_to_sql(type, limit = nil, precision = nil, scale = nil) #:nodoc:
259
261
  case type.to_sym
@@ -376,7 +378,7 @@ module ArJdbc
376
378
 
377
379
  def rename_column(table_name, column_name, new_column_name) #:nodoc:
378
380
  execute "ALTER TABLE #{quote_table_name(table_name)} " <<
379
- "RENAME COLUMN #{quote_column_name(column_name)} to #{quote_column_name(new_column_name)}"
381
+ "RENAME COLUMN #{quote_column_name(column_name)} TO #{quote_column_name(new_column_name)}"
380
382
  end
381
383
 
382
384
  def remove_column(table_name, *column_names) #:nodoc:
@@ -384,49 +386,7 @@ module ArJdbc
384
386
  execute "ALTER TABLE #{quote_table_name(table_name)} DROP COLUMN #{quote_column_name(column_name)}"
385
387
  end
386
388
  end
387
-
388
- def structure_dump #:nodoc:
389
- s = select_all("select sequence_name from user_sequences").inject("") do |structure, seq|
390
- structure << "create sequence #{seq.to_a.first.last};\n\n"
391
- end
392
-
393
- select_all("select table_name from user_tables").inject(s) do |structure, table|
394
- ddl = "create table #{table.to_a.first.last} (\n "
395
- cols = select_all(%Q{
396
- select column_name, data_type, data_length, data_precision, data_scale, data_default, nullable
397
- from user_tab_columns
398
- where table_name = '#{table.to_a.first.last}'
399
- order by column_id
400
- }).map do |row|
401
- row = row.inject({}) { |h, args| h[ args[0].downcase ] = args[1]; h }
402
- col = "#{row['column_name'].downcase} #{row['data_type'].downcase}"
403
- if row['data_type'] == 'NUMBER' and ! row['data_precision'].nil?
404
- col << "(#{row['data_precision'].to_i}"
405
- col << ",#{row['data_scale'].to_i}" if ! row['data_scale'].nil?
406
- col << ')'
407
- elsif row['data_type'].include?('CHAR')
408
- col << "(#{row['data_length'].to_i})"
409
- end
410
- col << " default #{row['data_default']}" if !row['data_default'].nil?
411
- col << ' not null' if row['nullable'] == 'N'
412
- col
413
- end
414
- ddl << cols.join(",\n ")
415
- ddl << ");\n\n"
416
- structure << ddl
417
- end
418
- end
419
-
420
- def structure_drop #:nodoc:
421
- s = select_all("select sequence_name from user_sequences").inject("") do |drop, seq|
422
- drop << "drop sequence #{seq.to_a.first.last};\n\n"
423
- end
424
-
425
- select_all("select table_name from user_tables").inject(s) do |drop, table|
426
- drop << "drop table #{table.to_a.first.last} cascade constraints;\n\n"
427
- end
428
- end
429
-
389
+
430
390
  # SELECT DISTINCT clause for a given set of columns and a given ORDER BY clause.
431
391
  #
432
392
  # Oracle requires the ORDER BY columns to be in the SELECT list for DISTINCT
@@ -471,6 +431,10 @@ module ArJdbc
471
431
  end
472
432
  private :extract_order_columns
473
433
 
434
+ def temporary_table?(table_name) # :nodoc:
435
+ select_value("SELECT temporary FROM user_tables WHERE table_name = '#{table_name.upcase}'") == 'Y'
436
+ end
437
+
474
438
  def tables # :nodoc:
475
439
  @connection.tables(nil, oracle_schema)
476
440
  end
@@ -484,27 +448,44 @@ module ArJdbc
484
448
  select_value "SELECT tablespace_name FROM user_tables WHERE table_name='#{table_name.to_s.upcase}'"
485
449
  end
486
450
 
487
- # QUOTING ==================================================
451
+ def charset
452
+ database_parameters['NLS_CHARACTERSET']
453
+ end
488
454
 
489
- # See ACTIVERECORD_JDBC-33 for details -- better to not quote
490
- # table names, esp. if they have schemas.
455
+ def collation
456
+ database_parameters['NLS_COMP']
457
+ end
458
+
459
+ def database_parameters
460
+ return @database_parameters unless ( @database_parameters ||= {} ).empty?
461
+ @connection.execute_query_raw("SELECT * FROM NLS_DATABASE_PARAMETERS") do
462
+ |name, value| @database_parameters[name] = value
463
+ end
464
+ @database_parameters
465
+ end
466
+
467
+ # QUOTING ==================================================
468
+
491
469
  def quote_table_name(name) # :nodoc:
492
- name.to_s
470
+ name.to_s.split('.').map{ |n| n.split('@').map{ |m| quote_column_name(m) }.join('@') }.join('.')
493
471
  end
494
-
495
- # Camelcase column names need to be quoted.
496
- # Nonquoted identifiers can contain only alphanumeric characters from your
497
- # database character set and the underscore (_), dollar sign ($), and pound sign (#).
498
- # Database links can also contain periods (.) and "at" signs (@).
499
- # Oracle strongly discourages you from using $ and # in nonquoted identifiers.
500
- # Source: http://download.oracle.com/docs/cd/B28359_01/server.111/b28286/sql_elements008.htm
472
+
501
473
  def quote_column_name(name) #:nodoc:
502
- name.to_s =~ /^[a-z0-9_$#]+$/ ? name.to_s : "\"#{name}\""
474
+ # if only valid lowercase column characters in name
475
+ if ( name = name.to_s ) =~ /\A[a-z][a-z_0-9\$#]*\Z/
476
+ # putting double-quotes around an identifier causes Oracle to treat the
477
+ # identifier as case sensitive (otherwise assumes case-insensitivity) !
478
+ # all upper case is an exception, where double-quotes are meaningless
479
+ "\"#{name.upcase}\"" # name.upcase
480
+ else
481
+ # remove double quotes which cannot be used inside quoted identifier
482
+ "\"#{name.gsub('"', '')}\""
483
+ end
503
484
  end
504
485
 
505
486
  def quote(value, column = nil) # :nodoc:
506
487
  return value if sql_literal?(value) # Arel 2 passes SqlLiterals through
507
-
488
+
508
489
  column_type = column && column.type
509
490
  if column_type == :text || column_type == :binary
510
491
  if /(.*?)\([0-9]+\)/ =~ column.sql_type
@@ -514,6 +495,8 @@ module ArJdbc
514
495
  end
515
496
  elsif column_type == :xml
516
497
  "XMLTYPE('#{quote_string(value)}')" # XMLTYPE ?
498
+ elsif column_type == :raw
499
+ quote_raw(value)
517
500
  else
518
501
  if column.respond_to?(:primary) && column.primary && column.klass != String
519
502
  return value.to_i.to_s
@@ -528,6 +511,11 @@ module ArJdbc
528
511
  end
529
512
  end
530
513
 
514
+ def quote_raw(value) # :nodoc:
515
+ value = value.unpack('C*') if value.is_a?(String)
516
+ "'#{value.map { |x| "%02X" % x }.join}'"
517
+ end
518
+
531
519
  def supports_migrations? # :nodoc:
532
520
  true
533
521
  end
@@ -599,9 +587,51 @@ module ArJdbc
599
587
  end
600
588
 
601
589
  module ActiveRecord::ConnectionAdapters
602
- OracleAdapter = Class.new(AbstractAdapter) unless const_defined?(:OracleAdapter)
590
+
591
+ remove_const(:OracleAdapter) if const_defined?(:OracleAdapter)
592
+
593
+ class OracleAdapter < JdbcAdapter
594
+ include ::ArJdbc::Oracle
595
+
596
+ # By default, the MysqlAdapter will consider all columns of type
597
+ # <tt>tinyint(1)</tt> as boolean. If you wish to disable this :
598
+ #
599
+ # ActiveRecord::ConnectionAdapters::OracleAdapter.emulate_booleans = false
600
+ #
601
+ def self.emulate_booleans; ::ArJdbc::Oracle.emulate_booleans; end
602
+ def self.emulate_booleans=(emulate); ::ArJdbc::Oracle.emulate_booleans = emulate; end
603
+
604
+ def initialize(*args)
605
+ ::ArJdbc::Oracle.initialize!
606
+ super # configure_connection happens in super
607
+ end
608
+
609
+ # some QUOTING caching :
610
+
611
+ @@quoted_table_names = {}
612
+
613
+ def quote_table_name(name)
614
+ unless quoted = @@quoted_table_names[name]
615
+ quoted = super
616
+ @@quoted_table_names[name] = quoted.freeze
617
+ end
618
+ quoted
619
+ end
620
+
621
+ @@quoted_column_names = {}
622
+
623
+ def quote_column_name(name)
624
+ unless quoted = @@quoted_column_names[name]
625
+ quoted = super
626
+ @@quoted_column_names[name] = quoted.freeze
627
+ end
628
+ quoted
629
+ end
630
+
631
+ end
603
632
 
604
633
  class OracleColumn < JdbcColumn
605
- include ArJdbc::Oracle::Column
634
+ include ::ArJdbc::Oracle::Column
606
635
  end
636
+
607
637
  end
@@ -3,7 +3,8 @@ ArJdbc::ConnectionMethods.module_eval do
3
3
  config[:port] ||= 1521
4
4
  config[:url] ||= "jdbc:oracle:thin:@#{config[:host]}:#{config[:port]}:#{config[:database]}"
5
5
  config[:driver] ||= "oracle.jdbc.driver.OracleDriver"
6
- config[:adapter_spec] = ::ArJdbc::Oracle
6
+ config[:adapter_spec] ||= ::ArJdbc::Oracle
7
+ config[:adapter_class] = ActiveRecord::ConnectionAdapters::OracleAdapter unless config.key?(:adapter_class)
7
8
  config[:connection_alive_sql] ||= 'SELECT 1 FROM DUAL'
8
9
  jdbc_connection(config)
9
10
  end
@@ -1,3 +1,5 @@
1
+ ArJdbc.load_java_part :PostgreSQL
2
+
1
3
  require 'ipaddr'
2
4
  require 'arjdbc/postgresql/column_cast'
3
5
  require 'arjdbc/postgresql/explain_support'
@@ -6,10 +8,6 @@ module ArJdbc
6
8
  module PostgreSQL
7
9
 
8
10
  AR4_COMPAT = ::ActiveRecord::VERSION::MAJOR > 3 unless const_defined?(:AR4_COMPAT) # :nodoc:
9
-
10
- def self.extended(base)
11
- base.configure_connection
12
- end
13
11
 
14
12
  def self.column_selector
15
13
  [ /postgre/i, lambda { |cfg, column| column.extend(::ArJdbc::PostgreSQL::Column) } ]
@@ -18,13 +16,21 @@ module ArJdbc
18
16
  def self.jdbc_connection_class
19
17
  ::ActiveRecord::ConnectionAdapters::PostgreSQLJdbcConnection
20
18
  end
21
-
19
+
20
+ def set_client_encoding(encoding)
21
+ ActiveRecord::Base.logger.warn "client_encoding is set by the driver and should not be altered, ('#{encoding}' ignored)"
22
+ ActiveRecord::Base.logger.debug "Set the 'allowEncodingChanges' driver property (e.g. using config[:properties]) if you need to override the client encoding when doing a copy."
23
+ end
24
+
22
25
  # Configures the encoding, verbosity, schema search path, and time zone of the connection.
23
26
  # This is called by #connect and should not be called manually.
24
27
  def configure_connection
25
- if encoding = config[:encoding]
26
- self.set_client_encoding(encoding)
27
- end
28
+ #if encoding = config[:encoding]
29
+ # The client_encoding setting is set by the driver and should not be altered.
30
+ # If the driver detects a change it will abort the connection.
31
+ # see http://jdbc.postgresql.org/documentation/91/connect.html
32
+ # self.set_client_encoding(encoding)
33
+ #end
28
34
  self.client_min_messages = config[:min_messages] || 'warning'
29
35
  self.schema_search_path = config[:schema_search_path] || config[:schema_order]
30
36
 
@@ -53,9 +59,9 @@ module ArJdbc
53
59
  end
54
60
 
55
61
  # constants taken from postgresql_adapter in rails project
56
- ADAPTER_NAME = 'PostgreSQL'
62
+ ADAPTER_NAME = 'PostgreSQL'.freeze
57
63
 
58
- def adapter_name #:nodoc:
64
+ def adapter_name # :nodoc:
59
65
  ADAPTER_NAME
60
66
  end
61
67
 
@@ -66,6 +72,16 @@ module ArJdbc
66
72
  'pg' => ::Arel::Visitors::PostgreSQL
67
73
  }
68
74
  end
75
+
76
+ def new_visitor(config = nil)
77
+ visitor = ::Arel::Visitors::PostgreSQL
78
+ ( prepared_statements? ? visitor : bind_substitution(visitor) ).new(self)
79
+ end if defined? ::Arel::Visitors::PostgreSQL
80
+
81
+ # @see #bind_substitution
82
+ class BindSubstitution < Arel::Visitors::PostgreSQL # :nodoc:
83
+ include Arel::Visitors::BindVisitor
84
+ end if defined? Arel::Visitors::BindVisitor
69
85
 
70
86
  def postgresql_version
71
87
  @postgresql_version ||=
@@ -182,6 +198,7 @@ module ArJdbc
182
198
  when :cidr, :inet then self.class.string_to_cidr value
183
199
  when :macaddr then value
184
200
  when :tsvector then value
201
+ when :datetime, :timestamp then self.class.string_to_time value
185
202
  else
186
203
  case sql_type
187
204
  when 'money'
@@ -415,13 +432,13 @@ module ArJdbc
415
432
  return super(value, column) unless 'bytea' == column.sql_type
416
433
  value # { :value => value, :format => 1 }
417
434
  when Array
418
- return super(value, column) unless column.array
435
+ return super(value, column) unless column.array?
419
436
  column_class = ::ActiveRecord::ConnectionAdapters::PostgreSQLColumn
420
437
  column_class.array_to_string(value, column, self)
421
438
  when NilClass
422
- if column.array && array_member
439
+ if column.array? && array_member
423
440
  'NULL'
424
- elsif column.array
441
+ elsif column.array?
425
442
  value
426
443
  else
427
444
  super(value, column)
@@ -579,7 +596,11 @@ module ArJdbc
579
596
  def supports_index_sort_order? # :nodoc:
580
597
  true
581
598
  end
582
-
599
+
600
+ def supports_partial_index?
601
+ true
602
+ end if AR4_COMPAT
603
+
583
604
  # Range datatypes weren't introduced until PostgreSQL 9.2
584
605
  def supports_ranges? # :nodoc:
585
606
  postgresql_version >= 90200
@@ -592,7 +613,11 @@ module ArJdbc
592
613
  def supports_transaction_isolation?(level = nil)
593
614
  true
594
615
  end
595
-
616
+
617
+ def index_algorithms
618
+ { :concurrently => 'CONCURRENTLY' }
619
+ end
620
+
596
621
  def create_savepoint
597
622
  execute("SAVEPOINT #{current_savepoint_name}")
598
623
  end
@@ -991,36 +1016,27 @@ module ArJdbc
991
1016
  sql.replace "SELECT * FROM (#{sql}) AS id_list ORDER BY #{order}"
992
1017
  end
993
1018
 
994
- def quote(value, column = nil) #:nodoc:
1019
+ def quote(value, column = nil) # :nodoc:
995
1020
  return super unless column
996
-
997
- # TODO recent 4.0 (master) seems to be passing a ColumnDefinition here :
998
- # NoMethodError: undefined method `sql_type' for #<ActiveRecord::ConnectionAdapters::PostgreSQLAdapter::ColumnDefinition:0x634f6b>
999
- # .../activerecord-jdbc-adapter/lib/arjdbc/postgresql/adapter.rb:1014:in `quote'
1000
- # .../gems/rails-817e8fad5a84/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb:698:in `add_column_options!'
1001
- # .../activerecord-jdbc-adapter/lib/arjdbc/postgresql/adapter.rb:507:in `add_column_options!'
1002
- # .../gems/rails-817e8fad5a84/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:168:in `add_column_options!'
1003
- # .../gems/rails-817e8fad5a84/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:135:in `visit_ColumnDefinition'
1004
- sql_type = column.sql_type rescue nil
1005
1021
 
1006
1022
  case value
1007
1023
  when Float
1008
- if value.infinite? && column.type == :datetime
1024
+ if value.infinite? && ( column.type == :datetime || column.type == :timestamp )
1009
1025
  "'#{value.to_s.downcase}'"
1010
1026
  elsif value.infinite? || value.nan?
1011
1027
  "'#{value.to_s}'"
1012
- else
1013
- super
1028
+ else super
1014
1029
  end
1015
1030
  when Numeric
1016
- return super unless sql_type == 'money'
1017
- # Not truly string input, so doesn't require (or allow) escape string syntax.
1018
- ( column.type == :string || column.type == :text ) ? "'#{value}'" : super
1031
+ if column.respond_to?(:sql_type) && column.sql_type == 'money'
1032
+ # not truly string input, so doesn't require (or allow) escape syntax :
1033
+ ( column.type == :string || column.type == :text ) ? "'#{value}'" : super
1034
+ else super
1035
+ end
1019
1036
  when String
1020
- case sql_type
1021
- when 'bytea' then "E'#{escape_bytea(value)}'::bytea" # "'#{escape_bytea(value)}'"
1022
- when 'xml' then "xml '#{quote_string(value)}'"
1023
- when /^bit/
1037
+ return "E'#{escape_bytea(value)}'::bytea" if column.type == :binary
1038
+ return "xml '#{quote_string(value)}'" if column.type == :xml
1039
+ if column.respond_to?(:sql_type) && column.sql_type[0, 3] == 'bit'
1024
1040
  case value
1025
1041
  # NOTE: as reported with #60 this is not quite "right" :
1026
1042
  # "0103" will be treated as hexadecimal string
@@ -1031,34 +1047,31 @@ module ArJdbc
1031
1047
  when /^[01]*$/ then "B'#{value}'" # Bit-string notation
1032
1048
  when /^[0-9A-F]*$/i then "X'#{value}'" # Hexadecimal notation
1033
1049
  end
1034
- else
1035
- super
1050
+ else super
1036
1051
  end
1037
1052
  when Array
1038
- if column.array && AR4_COMPAT
1053
+ if AR4_COMPAT && column.array? # will be always falsy in AR < 4.0
1039
1054
  column_class = ::ActiveRecord::ConnectionAdapters::PostgreSQLColumn
1040
1055
  "'#{column_class.array_to_string(value, column, self)}'"
1041
- else
1042
- super
1056
+ else super
1043
1057
  end
1044
1058
  when Hash
1045
- if sql_type == 'hstore' && AR4_COMPAT
1059
+ if column.type == :hstore # only in AR-4.0
1046
1060
  column_class = ::ActiveRecord::ConnectionAdapters::PostgreSQLColumn
1047
1061
  super(column_class.hstore_to_string(value), column)
1048
- elsif sql_type == 'json' && AR4_COMPAT
1062
+ elsif column.type == :json # only in AR-4.0
1049
1063
  column_class = ::ActiveRecord::ConnectionAdapters::PostgreSQLColumn
1050
1064
  super(column_class.json_to_string(value), column)
1051
1065
  else super
1052
1066
  end
1053
1067
  when Range
1054
- if /range$/ =~ sql_type && AR4_COMPAT
1068
+ if column.type.to_s[-5..-1] == 'range' # :'xxxrange' only in AR-4.0
1055
1069
  column_class = ::ActiveRecord::ConnectionAdapters::PostgreSQLColumn
1056
- "'#{column_class.range_to_string(value)}'::#{sql_type}"
1057
- else
1058
- super
1070
+ "'#{column_class.range_to_string(value)}'::#{column.sql_type}"
1071
+ else super
1059
1072
  end
1060
1073
  when IPAddr
1061
- if (sql_type == 'inet' || sql_type == 'cidr') && AR4_COMPAT
1074
+ if column.type == :inet || column.type == :cidr # only in AR-4.0
1062
1075
  column_class = ::ActiveRecord::ConnectionAdapters::PostgreSQLColumn
1063
1076
  super(column_class.cidr_to_string(value), column)
1064
1077
  else super
@@ -1100,7 +1113,11 @@ module ArJdbc
1100
1113
  "#{quote_column_name(schema)}.#{quote_column_name(table_name)}"
1101
1114
  end
1102
1115
  end
1103
-
1116
+
1117
+ def quote_table_name_for_assignment(table, attr)
1118
+ quote_column_name(attr)
1119
+ end if ::ActiveRecord::VERSION::MAJOR > 3
1120
+
1104
1121
  def quote_column_name(name)
1105
1122
  %("#{name.to_s.gsub("\"", "\"\"")}")
1106
1123
  end
@@ -1205,7 +1222,12 @@ module ArJdbc
1205
1222
  rename_column_indexes(table_name, column_name, new_column_name) if respond_to?(:rename_column_indexes) # AR-4.0 SchemaStatements
1206
1223
  end
1207
1224
 
1208
- def remove_index!(table_name, index_name) #:nodoc:
1225
+ def add_index(table_name, column_name, options = {}) # :nodoc:
1226
+ index_name, index_type, index_columns, index_options, index_algorithm, index_using = add_index_options(table_name, column_name, options)
1227
+ execute "CREATE #{index_type} INDEX #{index_algorithm} #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} #{index_using} (#{index_columns})#{index_options}"
1228
+ end if AR4_COMPAT
1229
+
1230
+ def remove_index!(table_name, index_name) # :nodoc:
1209
1231
  execute "DROP INDEX #{quote_table_name(index_name)}"
1210
1232
  end
1211
1233
 
@@ -1224,7 +1246,11 @@ module ArJdbc
1224
1246
  notnull = notnull == 't' if notnull.is_a?(String) # JDBC gets true/false
1225
1247
  # for ID columns we get a bit of non-sense default :
1226
1248
  # e.g. "nextval('mixed_cases_id_seq'::regclass"
1227
- default = nil if default =~ /^nextval\(.*?\:\:regclass\)$/
1249
+ if default =~ /^nextval\(.*?\:\:regclass\)$/
1250
+ default = nil
1251
+ elsif default =~ /^\(([-+]?[\d\.]+)\)$/ # e.g. "(-1)" for a negative default
1252
+ default = $1
1253
+ end
1228
1254
  klass.new(name, default, oid, type, ! notnull)
1229
1255
  end
1230
1256
  end
@@ -1314,15 +1340,24 @@ module ArJdbc
1314
1340
  WHERE a.attrelid = #{oid}
1315
1341
  AND a.attnum IN (#{indkey.join(",")})
1316
1342
  SQL
1317
-
1343
+
1318
1344
  columns = Hash[ columns.each { |column| column[0] = column[0].to_s } ]
1319
1345
  column_names = columns.values_at(*indkey).compact
1320
1346
 
1321
- # add info on sort order for columns (only desc order is explicitly specified, asc is the default)
1322
- desc_order_columns = inddef.scan(/(\w+) DESC/).flatten
1323
- orders = desc_order_columns.any? ? Hash[ desc_order_columns.map { |column| [column, :desc] } ] : {}
1324
-
1325
- column_names.empty? ? nil : IndexDefinition.new(table_name, index_name, unique, column_names, [], orders)
1347
+ unless column_names.empty?
1348
+ # add info on sort order for columns (only desc order is explicitly specified, asc is the default)
1349
+ desc_order_columns = inddef.scan(/(\w+) DESC/).flatten
1350
+ orders = desc_order_columns.any? ? Hash[ desc_order_columns.map { |column| [column, :desc] } ] : {}
1351
+
1352
+ if ActiveRecord::VERSION::MAJOR > 3 # AR4 supports `where` and `using` index options
1353
+ where = inddef.scan(/WHERE (.+)$/).flatten[0]
1354
+ using = inddef.scan(/USING (.+?) /).flatten[0].to_sym
1355
+
1356
+ IndexDefinition.new(table_name, index_name, unique, column_names, [], orders, where, nil, using)
1357
+ else
1358
+ IndexDefinition.new(table_name, index_name, unique, column_names, [], orders)
1359
+ end
1360
+ end
1326
1361
  end
1327
1362
  result.compact!
1328
1363
  result
@@ -1400,13 +1435,15 @@ module ActiveRecord::ConnectionAdapters
1400
1435
  class PostgreSQLColumn < JdbcColumn
1401
1436
  include ArJdbc::PostgreSQL::Column
1402
1437
 
1403
- def initialize(name, default, oid_type, sql_type = nil, null = true)
1438
+ def initialize(name, default, oid_type = nil, sql_type = nil, null = true)
1439
+ # NOTE: we support AR <= 3.2 : (name, default, sql_type = nil, null = true)
1440
+ null, sql_type, oid_type = !! sql_type, oid_type, nil unless oid_type.is_a?(Integer)
1404
1441
  @oid_type = oid_type
1405
1442
  if sql_type =~ /\[\]$/
1406
- @array = true
1443
+ @array = true if respond_to?(:array)
1407
1444
  super(name, default, sql_type[0..sql_type.length - 3], null)
1408
1445
  else
1409
- @array = false
1446
+ @array = false if respond_to?(:array)
1410
1447
  super(name, default, sql_type, null)
1411
1448
  end
1412
1449
  end
@@ -1418,7 +1455,7 @@ module ActiveRecord::ConnectionAdapters
1418
1455
  class PostgreSQLAdapter < JdbcAdapter
1419
1456
  include ArJdbc::PostgreSQL
1420
1457
  include ArJdbc::PostgreSQL::ExplainSupport
1421
-
1458
+
1422
1459
  def initialize(*args)
1423
1460
  super
1424
1461
 
@@ -1426,7 +1463,7 @@ module ActiveRecord::ConnectionAdapters
1426
1463
  @local_tz = nil
1427
1464
  @table_alias_length = nil
1428
1465
 
1429
- configure_connection
1466
+ # configure_connection happens in super
1430
1467
 
1431
1468
  @local_tz = execute('SHOW TIME ZONE', 'SCHEMA').first["TimeZone"]
1432
1469
  @use_insert_returning = config.key?(:insert_returning) ?
@@ -1517,9 +1554,7 @@ module ActiveRecord::ConnectionAdapters
1517
1554
  # NOTE: <= 3.1 no #new_column_definition hard-coded ColumnDef.new :
1518
1555
  # column = self[name] || ColumnDefinition.new(@base, name, type)
1519
1556
  # thus we simply do not support array column definitions on <= 3.1
1520
- if column.is_a?(ColumnDefinition)
1521
- column.array = options[:array]
1522
- end
1557
+ column.array = options[:array] if column.is_a?(ColumnDefinition)
1523
1558
  self
1524
1559
  end
1525
1560
 
@@ -1588,6 +1623,3 @@ module ActiveRecord::ConnectionAdapters
1588
1623
 
1589
1624
  end
1590
1625
  end
1591
-
1592
- # Don't need to load native postgres adapter
1593
- $LOADED_FEATURES << 'active_record/connection_adapters/postgresql_adapter.rb'