activerecord-jdbc-adapter 0.9.0.1 → 0.9.1

Sign up to get free protection for your applications and to get access to all the features.
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)