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.
- data/History.txt +31 -0
- data/Manifest.txt +7 -0
- data/README.txt +15 -2
- data/Rakefile +28 -30
- data/lib/active_record/connection_adapters/h2_adapter.rb +13 -1
- data/lib/active_record/connection_adapters/jdbc_adapter.rb +78 -96
- data/lib/jdbc_adapter/jdbc.rake +15 -5
- data/lib/jdbc_adapter/jdbc_adapter_internal.jar +0 -0
- data/lib/jdbc_adapter/jdbc_cachedb.rb +4 -4
- data/lib/jdbc_adapter/jdbc_db2.rb +5 -7
- data/lib/jdbc_adapter/jdbc_derby.rb +57 -30
- data/lib/jdbc_adapter/jdbc_firebird.rb +2 -2
- data/lib/jdbc_adapter/jdbc_hsqldb.rb +53 -46
- data/lib/jdbc_adapter/jdbc_informix.rb +4 -5
- data/lib/jdbc_adapter/jdbc_mimer.rb +2 -2
- data/lib/jdbc_adapter/jdbc_mssql.rb +25 -23
- data/lib/jdbc_adapter/jdbc_mysql.rb +20 -22
- data/lib/jdbc_adapter/jdbc_oracle.rb +115 -117
- data/lib/jdbc_adapter/jdbc_postgre.rb +129 -59
- data/lib/jdbc_adapter/jdbc_sqlite3.rb +149 -28
- data/lib/jdbc_adapter/jdbc_sybase.rb +13 -2
- data/lib/jdbc_adapter/missing_functionality_helper.rb +12 -3
- data/lib/jdbc_adapter/version.rb +1 -1
- data/src/java/jdbc_adapter/JdbcAdapterInternalService.java +6 -1101
- data/src/java/jdbc_adapter/JdbcDerbySpec.java +26 -23
- data/src/java/jdbc_adapter/JdbcMySQLSpec.java +79 -28
- data/src/java/jdbc_adapter/PostgresRubyJdbcConnection.java +35 -0
- data/src/java/jdbc_adapter/RubyJdbcConnection.java +1149 -0
- data/src/java/jdbc_adapter/SQLBlock.java +12 -3
- data/src/java/jdbc_adapter/Sqlite3RubyJdbcConnection.java +41 -0
- data/test/activerecord/connection_adapters/type_conversion_test.rb +1 -1
- data/test/db/derby.rb +0 -3
- data/test/db/h2.rb +0 -3
- data/test/db/hsqldb.rb +1 -4
- data/test/db/mysql.rb +1 -0
- data/test/db/oracle.rb +5 -0
- data/test/db/sqlite3.rb +7 -3
- data/test/derby_migration_test.rb +21 -0
- data/test/has_many_through.rb +11 -4
- data/test/jdbc_common.rb +13 -1
- data/test/models/data_types.rb +11 -1
- data/test/models/mixed_case.rb +20 -0
- data/test/mysql_multibyte_test.rb +4 -0
- data/test/oracle_simple_test.rb +1 -1
- data/test/postgres_mixed_case_test.rb +19 -0
- data/test/simple.rb +220 -41
- data/test/sqlite3_simple_test.rb +83 -0
- data/test/sybase_jtds_simple_test.rb +6 -0
- metadata +12 -10
Binary file
|
@@ -13,12 +13,12 @@ module ::JdbcSpec
|
|
13
13
|
module CacheDB
|
14
14
|
include TSqlMethods
|
15
15
|
|
16
|
-
def self.
|
17
|
-
|
16
|
+
def self.adapter_matcher(name, *)
|
17
|
+
name =~ /cache/i ? self : false
|
18
18
|
end
|
19
19
|
|
20
|
-
def self.
|
21
|
-
[ /cache/i, lambda { | cfg,
|
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.
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
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.
|
14
|
-
|
13
|
+
def self.adapter_matcher(name, *)
|
14
|
+
name =~ /derby/i ? self : false
|
15
15
|
end
|
16
16
|
|
17
|
-
def self.
|
18
|
-
[/derby/i, lambda {|cfg,
|
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
|
104
|
-
execute("ALTER TABLE #{table} ALTER COLUMN #{quote_column_name
|
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 =~
|
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
|
+
|
@@ -14,56 +14,22 @@ module ::JdbcSpec
|
|
14
14
|
end
|
15
15
|
|
16
16
|
module HSQLDB
|
17
|
-
def self.
|
18
|
-
|
17
|
+
def self.adapter_matcher(name, *)
|
18
|
+
name =~ /hsqldb/i ? self : false
|
19
19
|
end
|
20
20
|
|
21
|
-
def self.
|
22
|
-
[/hsqldb|\.h2\./i, lambda
|
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 => "
|
93
|
-
tp[:date] = { :name => "
|
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
|
-
"
|
82
|
+
"''"
|
104
83
|
elsif column && column.type == :binary
|
105
|
-
"'#{
|
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("
|
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.
|
44
|
-
|
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.
|
47
|
+
def self.column_selector
|
49
48
|
[ /informix/i,
|
50
|
-
lambda { |cfg,
|
49
|
+
lambda { |cfg, column| column.extend(::JdbcSpec::Informix::Column) } ]
|
51
50
|
end
|
52
51
|
|
53
52
|
module Column
|
@@ -1,36 +1,36 @@
|
|
1
1
|
require 'jdbc_adapter/tsql_helper'
|
2
2
|
|
3
|
-
module ::
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
def
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
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
|
-
|
23
|
-
|
24
|
-
|
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)
|