activerecord-jdbc-adapter 0.9.0.1 → 0.9.1

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 (49) hide show
  1. data/History.txt +31 -0
  2. data/Manifest.txt +7 -0
  3. data/README.txt +15 -2
  4. data/Rakefile +28 -30
  5. data/lib/active_record/connection_adapters/h2_adapter.rb +13 -1
  6. data/lib/active_record/connection_adapters/jdbc_adapter.rb +78 -96
  7. data/lib/jdbc_adapter/jdbc.rake +15 -5
  8. data/lib/jdbc_adapter/jdbc_adapter_internal.jar +0 -0
  9. data/lib/jdbc_adapter/jdbc_cachedb.rb +4 -4
  10. data/lib/jdbc_adapter/jdbc_db2.rb +5 -7
  11. data/lib/jdbc_adapter/jdbc_derby.rb +57 -30
  12. data/lib/jdbc_adapter/jdbc_firebird.rb +2 -2
  13. data/lib/jdbc_adapter/jdbc_hsqldb.rb +53 -46
  14. data/lib/jdbc_adapter/jdbc_informix.rb +4 -5
  15. data/lib/jdbc_adapter/jdbc_mimer.rb +2 -2
  16. data/lib/jdbc_adapter/jdbc_mssql.rb +25 -23
  17. data/lib/jdbc_adapter/jdbc_mysql.rb +20 -22
  18. data/lib/jdbc_adapter/jdbc_oracle.rb +115 -117
  19. data/lib/jdbc_adapter/jdbc_postgre.rb +129 -59
  20. data/lib/jdbc_adapter/jdbc_sqlite3.rb +149 -28
  21. data/lib/jdbc_adapter/jdbc_sybase.rb +13 -2
  22. data/lib/jdbc_adapter/missing_functionality_helper.rb +12 -3
  23. data/lib/jdbc_adapter/version.rb +1 -1
  24. data/src/java/jdbc_adapter/JdbcAdapterInternalService.java +6 -1101
  25. data/src/java/jdbc_adapter/JdbcDerbySpec.java +26 -23
  26. data/src/java/jdbc_adapter/JdbcMySQLSpec.java +79 -28
  27. data/src/java/jdbc_adapter/PostgresRubyJdbcConnection.java +35 -0
  28. data/src/java/jdbc_adapter/RubyJdbcConnection.java +1149 -0
  29. data/src/java/jdbc_adapter/SQLBlock.java +12 -3
  30. data/src/java/jdbc_adapter/Sqlite3RubyJdbcConnection.java +41 -0
  31. data/test/activerecord/connection_adapters/type_conversion_test.rb +1 -1
  32. data/test/db/derby.rb +0 -3
  33. data/test/db/h2.rb +0 -3
  34. data/test/db/hsqldb.rb +1 -4
  35. data/test/db/mysql.rb +1 -0
  36. data/test/db/oracle.rb +5 -0
  37. data/test/db/sqlite3.rb +7 -3
  38. data/test/derby_migration_test.rb +21 -0
  39. data/test/has_many_through.rb +11 -4
  40. data/test/jdbc_common.rb +13 -1
  41. data/test/models/data_types.rb +11 -1
  42. data/test/models/mixed_case.rb +20 -0
  43. data/test/mysql_multibyte_test.rb +4 -0
  44. data/test/oracle_simple_test.rb +1 -1
  45. data/test/postgres_mixed_case_test.rb +19 -0
  46. data/test/simple.rb +220 -41
  47. data/test/sqlite3_simple_test.rb +83 -0
  48. data/test/sybase_jtds_simple_test.rb +6 -0
  49. metadata +12 -10
@@ -13,12 +13,12 @@ module ::JdbcSpec
13
13
  module CacheDB
14
14
  include TSqlMethods
15
15
 
16
- def self.column_selector
17
- [ /cache/i, lambda { | cfg, col | col.extend( ::JdbcSpec::CacheDB::Column ) } ]
16
+ def self.adapter_matcher(name, *)
17
+ name =~ /cache/i ? self : false
18
18
  end
19
19
 
20
- def self.adapter_selector
21
- [ /cache/i, lambda { | cfg, adapt | adapt.extend( ::JdbcSpec::CacheDB ) } ]
20
+ def self.column_selector
21
+ [ /cache/i, lambda { | cfg, col | col.extend( ::JdbcSpec::CacheDB::Column ) } ]
22
22
  end
23
23
 
24
24
  module Column
@@ -1,12 +1,10 @@
1
1
  module JdbcSpec
2
2
  module DB2
3
- def self.column_selector
4
- [/db2/i, lambda {|cfg,col|
5
- if cfg[:url] =~ /^jdbc:derby:net:/
6
- col.extend(::JdbcSpec::Derby::Column)
7
- else
8
- col.extend(::JdbcSpec::DB2::Column)
9
- end }]
3
+ def self.adapter_matcher(name, config)
4
+ if name =~ /db2/i
5
+ return cfg[:url] =~ /^jdbc:derby:net:/ ? ::JdbcSpec::Derby : self
6
+ end
7
+ false
10
8
  end
11
9
 
12
10
  def self.adapter_selector
@@ -10,12 +10,12 @@ module ::JdbcSpec
10
10
  end
11
11
 
12
12
  module Derby
13
- def self.column_selector
14
- [/derby/i, lambda {|cfg,col| col.extend(::JdbcSpec::Derby::Column)}]
13
+ def self.adapter_matcher(name, *)
14
+ name =~ /derby/i ? self : false
15
15
  end
16
16
 
17
- def self.adapter_selector
18
- [/derby/i, lambda {|cfg,adapt| adapt.extend(::JdbcSpec::Derby)}]
17
+ def self.column_selector
18
+ [/derby/i, lambda {|cfg,col| col.extend(::JdbcSpec::Derby::Column)}]
19
19
  end
20
20
 
21
21
  def self.monkey_rails
@@ -48,37 +48,36 @@ module ::JdbcSpec
48
48
  end
49
49
 
50
50
  module Column
51
- def value_to_binary(value)
52
- value.scan(/[0-9A-Fa-f]{2}/).collect {|v| v.to_i(16)}.pack("C*")
53
- end
54
-
55
- def cast_to_date_or_time(value)
56
- return value if value.is_a? Date
57
- return nil if value.blank?
58
- guess_date_or_time((value.is_a? Time) ? value : cast_to_time(value))
59
- end
60
-
61
- def cast_to_time(value)
62
- return value if value.is_a? Time
63
- time_array = ParseDate.parsedate value
64
- time_array[0] ||= 2000; time_array[1] ||= 1; time_array[2] ||= 1;
65
- Time.send(ActiveRecord::Base.default_timezone, *time_array) rescue nil
66
- end
67
-
68
- def guess_date_or_time(value)
69
- (value.hour == 0 and value.min == 0 and value.sec == 0) ?
70
- Date.new(value.year, value.month, value.day) : value
71
- end
72
-
73
51
  def simplified_type(field_type)
74
52
  return :boolean if field_type =~ /smallint/i
75
53
  return :float if field_type =~ /real/i
76
54
  super
77
55
  end
56
+
57
+ # Post process default value from JDBC into a Rails-friendly format (columns{-internal})
58
+ def default_value(value)
59
+ # jdbc returns column default strings with actual single quotes around the value.
60
+ return $1 if value =~ /^'(.*)'$/
61
+
62
+ value
63
+ end
64
+ end
65
+
66
+ def adapter_name #:nodoc:
67
+ 'Derby'
78
68
  end
79
69
 
80
70
  include JdbcSpec::MissingFunctionalityHelper
81
71
 
72
+ # Convert the speficied column type to a SQL string. In Derby, :integers cannot specify
73
+ # a limit.
74
+ def type_to_sql(type, limit = nil, precision = nil, scale = nil) #:nodoc:
75
+ return super unless type == :integer
76
+
77
+ native = native_database_types[type.to_s.downcase.to_sym]
78
+ native.is_a?(Hash) ? native[:name] : native
79
+ end
80
+
82
81
  def modify_types(tp)
83
82
  tp[:primary_key] = "int generated by default as identity NOT NULL PRIMARY KEY"
84
83
  tp[:integer][:limit] = nil
@@ -100,8 +99,8 @@ module ::JdbcSpec
100
99
 
101
100
  # Set the sequence to the max value of the table's column.
102
101
  def reset_sequence!(table, column, sequence = nil)
103
- mpk = select_value("SELECT MAX(#{quote_column_name column}) FROM #{table}")
104
- execute("ALTER TABLE #{table} ALTER COLUMN #{quote_column_name column} RESTART WITH #{mpk.to_i + 1}")
102
+ mpk = select_value("SELECT MAX(#{quote_column_name(column)}) FROM #{quote_table_name(table)}")
103
+ execute("ALTER TABLE #{quote_table_name(table)} ALTER COLUMN #{quote_column_name(column)} RESTART WITH #{mpk.to_i + 1}")
105
104
  end
106
105
 
107
106
  def reset_pk_sequence!(table, pk = nil, sequence = nil)
@@ -216,6 +215,19 @@ module ::JdbcSpec
216
215
  end
217
216
  end
218
217
 
218
+ def execute(sql, name = nil)
219
+ if sql =~ /^\s*(UPDATE|INSERT)/i
220
+ i = sql =~ /\swhere\s/im
221
+ if i
222
+ sql[i..-1] = sql[i..-1].gsub(/!=\s*NULL/, 'IS NOT NULL').gsub(/=\sNULL/i, 'IS NULL')
223
+ end
224
+ else
225
+ sql.gsub!(/= NULL/i, 'IS NULL')
226
+ end
227
+ super
228
+ end
229
+
230
+
219
231
  # I don't think this method is ever called ??? (stepheneb)
220
232
  def create_column(name, refid, colno)
221
233
  stmt = COLUMN_TYPE_STMT % [refid, strip_quotes(name)]
@@ -350,6 +362,14 @@ module ::JdbcSpec
350
362
  @connection.primary_keys table_name.to_s.upcase
351
363
  end
352
364
 
365
+ def columns(table_name, name=nil)
366
+ @connection.columns_internal(table_name.to_s, name, derby_schema)
367
+ end
368
+
369
+ def tables
370
+ @connection.tables(nil, derby_schema)
371
+ end
372
+
353
373
  def recreate_database(db_name)
354
374
  tables.each do |t|
355
375
  drop_table t
@@ -359,11 +379,11 @@ module ::JdbcSpec
359
379
  # For DDL it appears you can quote "" column names, but in queries (like insert it errors out?)
360
380
  def quote_column_name(name) #:nodoc:
361
381
  name = name.to_s
362
- if /^references$/i =~ name
382
+ if /^(references|integer|key|group|year)$/i =~ name
363
383
  %Q{"#{name.upcase}"}
364
384
  elsif /[A-Z]/ =~ name && /[a-z]/ =~ name
365
385
  %Q{"#{name}"}
366
- elsif name =~ /\s/
386
+ elsif name =~ /[\s-]/
367
387
  %Q{"#{name.upcase}"}
368
388
  elsif name =~ /^[_\d]/
369
389
  %Q{"#{name.upcase}"}
@@ -379,6 +399,13 @@ module ::JdbcSpec
379
399
  def quoted_false
380
400
  '0'
381
401
  end
402
+
403
+ private
404
+ # Derby appears to define schemas using the username
405
+ def derby_schema
406
+ @config[:username].to_s
407
+ end
382
408
  end
383
409
  end
384
410
 
411
+
@@ -1,7 +1,7 @@
1
1
  module ::JdbcSpec
2
2
  module FireBird
3
- def self.adapter_selector
4
- [/firebird/i, lambda{|cfg,adapt| adapt.extend(::JdbcSpec::FireBird)}]
3
+ def self.adapter_matcher(name, *)
4
+ name =~ /firebird/i ? self : false
5
5
  end
6
6
 
7
7
  def modify_types(tp)
@@ -14,56 +14,22 @@ module ::JdbcSpec
14
14
  end
15
15
 
16
16
  module HSQLDB
17
- def self.column_selector
18
- [/hsqldb|\.h2\./i, lambda {|cfg,col| col.extend(::JdbcSpec::HSQLDB::Column)}]
17
+ def self.adapter_matcher(name, *)
18
+ name =~ /hsqldb/i ? self : false
19
19
  end
20
20
 
21
- def self.adapter_selector
22
- [/hsqldb|\.h2\./i, lambda do |cfg,adapt|
23
- adapt.extend(::JdbcSpec::HSQLDB)
24
- def adapt.h2_adapter; true; end if cfg[:driver] =~ /\.h2\./
25
- end]
21
+ def self.column_selector
22
+ [/hsqldb|\.h2\./i, lambda {|cfg,col| col.extend(::JdbcSpec::HSQLDB::Column)}]
26
23
  end
27
24
 
28
25
  module Column
29
- def type_cast(value)
30
- return nil if value.nil? || value =~ /^\s*null\s*$/i
31
- case type
32
- when :string then value
33
- when :integer then defined?(value.to_i) ? value.to_i : (value ? 1 : 0)
34
- when :primary_key then defined?(value.to_i) ? value.to_i : (value ? 1 : 0)
35
- when :float then value.to_f
36
- when :datetime then cast_to_date_or_time(value)
37
- when :timestamp then cast_to_time(value)
38
- when :binary then value.scan(/[0-9A-Fa-f]{2}/).collect {|v| v.to_i(16)}.pack("C*")
39
- when :time then cast_to_time(value)
40
- else value
41
- end
42
- end
43
- def cast_to_date_or_time(value)
44
- return value if value.is_a? Date
45
- return nil if value.blank?
46
- guess_date_or_time((value.is_a? Time) ? value : cast_to_time(value))
47
- end
48
-
49
- def cast_to_time(value)
50
- return value if value.is_a? Time
51
- time_array = ParseDate.parsedate value
52
- time_array[0] ||= 2000; time_array[1] ||= 1; time_array[2] ||= 1;
53
- Time.send(ActiveRecord::Base.default_timezone, *time_array) rescue nil
54
- end
55
-
56
- def guess_date_or_time(value)
57
- (value.hour == 0 and value.min == 0 and value.sec == 0) ?
58
- Date.new(value.year, value.month, value.day) : value
59
- end
60
-
61
-
62
26
  private
63
27
  def simplified_type(field_type)
64
28
  case field_type
65
29
  when /longvarchar/i
66
30
  :text
31
+ when /tinyint/i
32
+ :boolean
67
33
  else
68
34
  super(field_type)
69
35
  end
@@ -76,6 +42,18 @@ module ::JdbcSpec
76
42
  return nil if sql_type =~ /\(0\)/
77
43
  super
78
44
  end
45
+
46
+ # Post process default value from JDBC into a Rails-friendly format (columns{-internal})
47
+ def default_value(value)
48
+ # jdbc returns column default strings with actual single quotes around the value.
49
+ return $1 if value =~ /^'(.*)'$/
50
+
51
+ value
52
+ end
53
+ end
54
+
55
+ def adapter_name #:nodoc:
56
+ defined?(::Jdbc::H2) ? 'h2' : 'hsqldb'
79
57
  end
80
58
 
81
59
  def modify_types(tp)
@@ -84,13 +62,14 @@ module ::JdbcSpec
84
62
  tp[:boolean][:limit] = nil
85
63
  # set text and float limits so we don't see odd scales tacked on
86
64
  # in migrations
65
+ tp[:boolean] = { :name => "tinyint" }
87
66
  tp[:text][:limit] = nil
88
- tp[:float][:limit] = 17
67
+ tp[:float][:limit] = 17 if defined?(::Jdbc::H2)
89
68
  tp[:string][:limit] = 255
90
69
  tp[:datetime] = { :name => "DATETIME" }
91
70
  tp[:timestamp] = { :name => "DATETIME" }
92
- tp[:time] = { :name => "DATETIME" }
93
- tp[:date] = { :name => "DATETIME" }
71
+ tp[:time] = { :name => "TIME" }
72
+ tp[:date] = { :name => "DATE" }
94
73
  tp
95
74
  end
96
75
 
@@ -100,9 +79,9 @@ module ::JdbcSpec
100
79
  case value
101
80
  when String
102
81
  if respond_to?(:h2_adapter) && value.empty?
103
- "NULL"
82
+ "''"
104
83
  elsif column && column.type == :binary
105
- "'#{quote_string(value).unpack("C*").collect {|v| v.to_s(16)}.join}'"
84
+ "'#{value.unpack("H*")}'"
106
85
  else
107
86
  "'#{quote_string(value)}'"
108
87
  end
@@ -110,6 +89,15 @@ module ::JdbcSpec
110
89
  end
111
90
  end
112
91
 
92
+ def quote_column_name(name) #:nodoc:
93
+ name = name.to_s
94
+ if name =~ /[-]/
95
+ %Q{"#{name.upcase}"}
96
+ else
97
+ name
98
+ end
99
+ end
100
+
113
101
  def quote_string(str)
114
102
  str.gsub(/'/, "''")
115
103
  end
@@ -146,6 +134,13 @@ module ::JdbcSpec
146
134
  execute "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} RENAME TO #{new_column_name}"
147
135
  end
148
136
 
137
+ # Maps logical Rails types to MySQL-specific data types.
138
+ def type_to_sql(type, limit = nil, precision = nil, scale = nil)
139
+ return super if defined?(::Jdbc::H2) || type.to_s != 'integer' || limit == nil
140
+
141
+ type
142
+ end
143
+
149
144
  def rename_table(name, new_name)
150
145
  execute "ALTER TABLE #{name} RENAME TO #{new_name}"
151
146
  end
@@ -159,7 +154,7 @@ module ::JdbcSpec
159
154
  end
160
155
 
161
156
  def last_insert_id(table, sequence_name)
162
- Integer(select_value("SELECT IDENTITY() FROM #{table}"))
157
+ Integer(select_value("CALL IDENTITY()"))
163
158
  end
164
159
 
165
160
  # Override normal #_execute: See Rubyforge #11567
@@ -197,4 +192,16 @@ module ::JdbcSpec
197
192
  execute "DROP INDEX #{quote_column_name(index_name(table_name, options))}"
198
193
  end
199
194
  end
195
+
196
+ module H2
197
+ include HSQLDB
198
+
199
+ def self.adapter_matcher(name, *)
200
+ name =~ /\.h2\./i ? self : false
201
+ end
202
+
203
+ def h2_adapter
204
+ true
205
+ end
206
+ end
200
207
  end
@@ -40,14 +40,13 @@ module ::JdbcSpec
40
40
  @@db_major_version = base.select_one("SELECT dbinfo('version', 'major') version FROM systables WHERE tabid = 1")['version'].to_i
41
41
  end
42
42
 
43
- def self.column_selector
44
- [ /informix/i,
45
- lambda { |cfg, column| column.extend(::JdbcSpec::Informix::Column) } ]
43
+ def self.adapter_matcher(name, *)
44
+ name =~ /informix/i ? self : false
46
45
  end
47
46
 
48
- def self.adapter_selector
47
+ def self.column_selector
49
48
  [ /informix/i,
50
- lambda { |cfg, adapter| adapter.extend(::JdbcSpec::Informix) } ]
49
+ lambda { |cfg, column| column.extend(::JdbcSpec::Informix::Column) } ]
51
50
  end
52
51
 
53
52
  module Column
@@ -1,7 +1,7 @@
1
1
  module JdbcSpec
2
2
  module Mimer
3
- def self.adapter_selector
4
- [/mimer/i, lambda {|cfg,adapt| adapt.extend(::JdbcSpec::Mimer)}]
3
+ def self.adapter_matcher(name, *)
4
+ name =~ /mimer/i ? self : false
5
5
  end
6
6
 
7
7
  def modify_types(tp)
@@ -1,36 +1,36 @@
1
1
  require 'jdbc_adapter/tsql_helper'
2
2
 
3
- module ::ActiveRecord
4
- class Base
5
- # After setting large objects to empty, write data back with a helper method
6
- after_save :write_lobs
7
- def write_lobs() #:nodoc:
8
- if connection.is_a?(JdbcSpec::MsSQL)
9
- self.class.columns.select { |c| c.sql_type =~ /image/i }.each { |c|
10
- value = self[c.name]
11
- value = value.to_yaml if unserializable_attribute?(c.name, c)
12
- next if value.nil? || (value == '')
13
-
14
- connection.write_large_object(c.type == :binary, c.name, self.class.table_name, self.class.primary_key, quote_value(id), value)
15
- }
3
+ module ::JdbcSpec
4
+ module MsSQL
5
+ include TSqlMethods
6
+
7
+ def self.extended(mod)
8
+ unless @lob_callback_added
9
+ ActiveRecord::Base.class_eval do
10
+ def after_save_with_mssql_lob
11
+ self.class.columns.select { |c| c.sql_type =~ /image/i }.each do |c|
12
+ value = self[c.name]
13
+ value = value.to_yaml if unserializable_attribute?(c.name, c)
14
+ next if value.nil? || (value == '')
15
+
16
+ connection.write_large_object(c.type == :binary, c.name, self.class.table_name, self.class.primary_key, quote_value(id), value)
17
+ end
18
+ end
19
+ end
20
+
21
+ ActiveRecord::Base.after_save :after_save_with_mssql_lob
22
+ @lob_callback_added = true
16
23
  end
17
24
  end
18
- private :write_lobs
19
- end
20
- end
21
25
 
22
- module JdbcSpec
23
- module MsSQL
24
- include TSqlMethods
26
+ def self.adapter_matcher(name, *)
27
+ name =~ /sqlserver|tds/i ? self : false
28
+ end
25
29
 
26
30
  def self.column_selector
27
31
  [/sqlserver|tds/i, lambda {|cfg,col| col.extend(::JdbcSpec::MsSQL::Column)}]
28
32
  end
29
33
 
30
- def self.adapter_selector
31
- [/sqlserver|tds/i, lambda {|cfg,adapt| adapt.extend(::JdbcSpec::MsSQL)}]
32
- end
33
-
34
34
  module Column
35
35
  attr_accessor :identity, :is_special
36
36
 
@@ -154,11 +154,13 @@ module JdbcSpec
154
154
  end
155
155
 
156
156
  def drop_database(name)
157
+ execute "USE master"
157
158
  execute "DROP DATABASE #{name}"
158
159
  end
159
160
 
160
161
  def create_database(name)
161
162
  execute "CREATE DATABASE #{name}"
163
+ execute "USE #{name}"
162
164
  end
163
165
 
164
166
  def rename_table(name, new_name)