ActiveRecord-JDBC 0.2.3 → 0.2.4
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 +10 -0
- data/Manifest.txt +15 -9
- data/Rakefile +59 -15
- data/lib/active_record/connection_adapters/jdbc_adapter.rb +94 -281
- data/lib/jdbc_adapter.rb +5 -2
- data/lib/jdbc_adapter/jdbc_derby.rb +51 -13
- data/lib/jdbc_adapter/jdbc_mysql.rb +63 -18
- data/lib/jdbc_adapter/missing_functionality_helper.rb +72 -0
- data/lib/jdbc_adapter_internal.jar +0 -0
- data/test/db/derby.rb +2 -2
- data/test/db/jndi_config.rb +30 -0
- data/test/db/postgres.rb +10 -0
- data/test/derby_simple_test.rb +1 -5
- data/test/h2_simple_test.rb +1 -3
- data/test/hsqldb_simple_test.rb +1 -4
- data/test/jdbc_common.rb +6 -0
- data/test/jndi_test.rb +37 -0
- data/test/models/auto_id.rb +0 -1
- data/test/models/data_types.rb +18 -19
- data/test/models/entry.rb +0 -1
- data/test/mysql_simple_test.rb +1 -5
- data/test/postgres_simple_test.rb +12 -0
- data/test/simple.rb +2 -2
- metadata +17 -11
- data/lib/active_record/connection_adapters/jndi_adapter.rb +0 -51
data/lib/jdbc_adapter.rb
CHANGED
@@ -4,6 +4,9 @@ if RUBY_PLATFORM =~ /java/
|
|
4
4
|
else
|
5
5
|
RAILS_CONNECTION_ADAPTERS = %w(jdbc)
|
6
6
|
end
|
7
|
+
if defined?(RAILS_GEM_VERSION) && RAILS_GEM_VERSION =~ /1\.1\.\d+/
|
8
|
+
require 'active_record/connection_adapters/jdbc_adapter'
|
9
|
+
end
|
7
10
|
else
|
8
|
-
|
9
|
-
end
|
11
|
+
$stderr.print "warning: ActiveRecord-JDBC is for use with JRuby only\n"
|
12
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'jdbc_adapter/missing_functionality_helper'
|
2
|
+
|
1
3
|
module JdbcSpec
|
2
4
|
module Derby
|
3
5
|
module Column
|
@@ -34,6 +36,8 @@ module JdbcSpec
|
|
34
36
|
end
|
35
37
|
end
|
36
38
|
|
39
|
+
include JdbcSpec::MissingFunctionalityHelper
|
40
|
+
|
37
41
|
def modify_types(tp)
|
38
42
|
tp[:primary_key] = "int generated by default as identity NOT NULL PRIMARY KEY"
|
39
43
|
tp[:integer][:limit] = nil
|
@@ -46,20 +50,35 @@ module JdbcSpec
|
|
46
50
|
end
|
47
51
|
|
48
52
|
def select_all(sql, name = nil)
|
49
|
-
|
50
|
-
@offset ||= 0
|
51
|
-
select(sql, name)[@offset..(@offset+@limit)]
|
52
|
-
ensure
|
53
|
-
@limit = @offset = nil
|
53
|
+
execute(sql, name)
|
54
54
|
end
|
55
55
|
|
56
56
|
def select_one(sql, name = nil)
|
57
|
-
@
|
58
|
-
|
57
|
+
@limit ||= 1
|
58
|
+
execute(sql, name).first
|
59
59
|
ensure
|
60
|
-
@limit =
|
60
|
+
@limit = nil
|
61
|
+
end
|
62
|
+
|
63
|
+
def classes_for_table_name(table)
|
64
|
+
ActiveRecord::Base.send(:subclasses).select {|klass| klass.table_name == table}
|
61
65
|
end
|
62
66
|
|
67
|
+
# Set the sequence to the max value of the table's column.
|
68
|
+
def reset_sequence!(table, column, sequence = nil)
|
69
|
+
mpk = select_value("SELECT MAX(#{column}) FROM #{table}")
|
70
|
+
execute("ALTER TABLE #{table} ALTER COLUMN #{column} RESTART WITH #{mpk.to_i + 1}")
|
71
|
+
end
|
72
|
+
|
73
|
+
def reset_pk_sequence!(table, pk = nil, sequence = nil)
|
74
|
+
klasses = classes_for_table_name(table)
|
75
|
+
klass = klasses.nil? ? nil : klasses.first
|
76
|
+
pk = klass.primary_key unless klass.nil?
|
77
|
+
if pk && klass.columns_hash[pk].type == :integer
|
78
|
+
reset_sequence!(klass.table_name, pk)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
63
82
|
def execute(sql, name = nil)
|
64
83
|
log_no_bench(sql, name) do
|
65
84
|
if sql =~ /^select/i
|
@@ -74,6 +93,10 @@ module JdbcSpec
|
|
74
93
|
@limit = @offset = nil
|
75
94
|
end
|
76
95
|
|
96
|
+
def primary_key(table_name) #:nodoc:
|
97
|
+
primary_keys(table_name).first
|
98
|
+
end
|
99
|
+
|
77
100
|
def remove_index(table_name, options) #:nodoc:
|
78
101
|
execute "DROP INDEX #{index_name(table_name, options)}"
|
79
102
|
end
|
@@ -87,10 +110,16 @@ module JdbcSpec
|
|
87
110
|
#
|
88
111
|
# This feature has not made it into a formal release and is not in Java 6. We will
|
89
112
|
# need to conditionally support this somehow (supposed to arrive for 10.3.0.0)
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
113
|
+
def remove_column(table_name, column_name)
|
114
|
+
begin
|
115
|
+
execute "ALTER TABLE #{table_name} DROP COLUMN #{column_name} RESTRICT"
|
116
|
+
rescue
|
117
|
+
alter_table(table_name) do |definition|
|
118
|
+
definition.columns.delete(definition[column_name])
|
119
|
+
end
|
120
|
+
# raise NotImplementedError, "remove_column is not support on this Derby version"
|
121
|
+
end
|
122
|
+
end
|
94
123
|
|
95
124
|
# Notes about changing in Derby:
|
96
125
|
# http://db.apache.org/derby/docs/10.2/ref/rrefsqlj81859.html#rrefsqlj81859__rrefsqlj37860)
|
@@ -98,7 +127,16 @@ module JdbcSpec
|
|
98
127
|
# can increase the types precision only if it is a VARCHAR.
|
99
128
|
#
|
100
129
|
def change_column(table_name, column_name, type, options = {}) #:nodoc:
|
101
|
-
|
130
|
+
# Derby can't change the datatype or size unless the type is varchar
|
131
|
+
execute "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} SET DATA TYPE #{type_to_sql(type, options[:limit])}" if type == :string
|
132
|
+
if options.include? :null
|
133
|
+
# This seems to only work with 10.2 of Derby
|
134
|
+
if options[:null] == false
|
135
|
+
execute "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} NOT NULL"
|
136
|
+
else
|
137
|
+
execute "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} NULL"
|
138
|
+
end
|
139
|
+
end
|
102
140
|
end
|
103
141
|
|
104
142
|
def change_column_default(table_name, column_name, default) #:nodoc:
|
@@ -2,9 +2,41 @@ require 'active_record/connection_adapters/abstract/schema_definitions'
|
|
2
2
|
|
3
3
|
module JdbcSpec
|
4
4
|
module MySQL
|
5
|
+
def self.extended(adapter)
|
6
|
+
adapter.execute("SET SQL_AUTO_IS_NULL=0")
|
7
|
+
end
|
8
|
+
|
9
|
+
module Column
|
10
|
+
TYPES_ALLOWING_EMPTY_STRING_DEFAULT = Set.new([:binary, :string, :text])
|
11
|
+
|
12
|
+
def simplified_type(field_type)
|
13
|
+
return :boolean if field_type =~ /tinyint\(1\)|bit\(1\)/i
|
14
|
+
return :string if field_type =~ /enum/i
|
15
|
+
super
|
16
|
+
end
|
17
|
+
|
18
|
+
def init_column(name, default, *args)
|
19
|
+
@original_default = default
|
20
|
+
@default = nil if missing_default_forged_as_empty_string?
|
21
|
+
end
|
22
|
+
|
23
|
+
# MySQL misreports NOT NULL column default when none is given.
|
24
|
+
# We can't detect this for columns which may have a legitimate ''
|
25
|
+
# default (string, text, binary) but we can for others (integer,
|
26
|
+
# datetime, boolean, and the rest).
|
27
|
+
#
|
28
|
+
# Test whether the column has default '', is not null, and is not
|
29
|
+
# a type allowing default ''.
|
30
|
+
def missing_default_forged_as_empty_string?
|
31
|
+
!null && @original_default == '' && !TYPES_ALLOWING_EMPTY_STRING_DEFAULT.include?(type)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
5
35
|
def modify_types(tp)
|
6
36
|
tp[:primary_key] = "int(11) DEFAULT NULL auto_increment PRIMARY KEY"
|
7
37
|
tp[:decimal] = { :name => "decimal" }
|
38
|
+
tp[:timestamp] = { :name => "datetime" }
|
39
|
+
tp[:datetime][:limit] = nil
|
8
40
|
tp
|
9
41
|
end
|
10
42
|
|
@@ -12,9 +44,15 @@ module JdbcSpec
|
|
12
44
|
|
13
45
|
def quote(value, column = nil)
|
14
46
|
if column && column.type == :primary_key
|
15
|
-
|
47
|
+
value.to_s
|
48
|
+
elsif column && String === value && column.type == :binary && column.class.respond_to?(:string_to_binary)
|
49
|
+
s = column.class.string_to_binary(value).unpack("H*")[0]
|
50
|
+
"x'#{s}'"
|
51
|
+
elsif BigDecimal === value
|
52
|
+
"'#{value.to_s("F")}'"
|
53
|
+
else
|
54
|
+
super
|
16
55
|
end
|
17
|
-
super
|
18
56
|
end
|
19
57
|
|
20
58
|
def quote_column_name(name) #:nodoc:
|
@@ -23,15 +61,7 @@ module JdbcSpec
|
|
23
61
|
|
24
62
|
# from active_record/vendor/mysql.rb
|
25
63
|
def quote_string(str) #:nodoc:
|
26
|
-
|
27
|
-
case $1
|
28
|
-
when "\0" then "\\0"
|
29
|
-
when "\n" then "\\n"
|
30
|
-
when "\r" then "\\r"
|
31
|
-
when "\032" then "\\Z"
|
32
|
-
else "\\"+$1
|
33
|
-
end
|
34
|
-
end
|
64
|
+
@connection.mysql_quote_string(str)
|
35
65
|
end
|
36
66
|
|
37
67
|
def quoted_true
|
@@ -87,24 +117,39 @@ module JdbcSpec
|
|
87
117
|
end
|
88
118
|
|
89
119
|
def change_column_default(table_name, column_name, default) #:nodoc:
|
90
|
-
current_type = select_one("SHOW COLUMNS FROM #{table_name} LIKE '#{column_name}'")["
|
120
|
+
current_type = select_one("SHOW COLUMNS FROM #{table_name} LIKE '#{column_name}'")["Type"]
|
91
121
|
|
92
|
-
|
93
|
-
|
94
|
-
change_column(table_name, column_name, type, { :default => default, :limit => limit })
|
122
|
+
execute("ALTER TABLE #{table_name} CHANGE #{column_name} #{column_name} #{current_type} DEFAULT #{quote(default)}")
|
95
123
|
end
|
96
124
|
|
97
125
|
def change_column(table_name, column_name, type, options = {}) #:nodoc:
|
98
|
-
options
|
126
|
+
unless options.include?(:default) && !(options[:null] == false && options[:default].nil?)
|
127
|
+
options[:default] = select_one("SHOW COLUMNS FROM #{table_name} LIKE '#{column_name}'")["Default"]
|
128
|
+
end
|
99
129
|
|
100
|
-
change_column_sql = "ALTER TABLE #{table_name} CHANGE #{column_name} #{column_name} #{type_to_sql(type, options[:limit])}"
|
130
|
+
change_column_sql = "ALTER TABLE #{table_name} CHANGE #{column_name} #{column_name} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
|
101
131
|
add_column_options!(change_column_sql, options)
|
102
132
|
execute(change_column_sql)
|
103
133
|
end
|
104
134
|
|
105
135
|
def rename_column(table_name, column_name, new_column_name) #:nodoc:
|
106
|
-
current_type = select_one("SHOW COLUMNS FROM #{table_name} LIKE '#{column_name}'")["
|
136
|
+
current_type = select_one("SHOW COLUMNS FROM #{table_name} LIKE '#{column_name}'")["Type"]
|
107
137
|
execute "ALTER TABLE #{table_name} CHANGE #{column_name} #{new_column_name} #{current_type}"
|
108
138
|
end
|
139
|
+
|
140
|
+
def add_limit_offset!(sql, options) #:nodoc:
|
141
|
+
if limit = options[:limit]
|
142
|
+
unless offset = options[:offset]
|
143
|
+
sql << " LIMIT #{limit}"
|
144
|
+
else
|
145
|
+
sql << " LIMIT #{offset}, #{limit}"
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
private
|
151
|
+
def supports_views?
|
152
|
+
false
|
153
|
+
end
|
109
154
|
end
|
110
155
|
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module JdbcSpec
|
2
|
+
module MissingFunctionalityHelper
|
3
|
+
#Taken from SQLite adapter
|
4
|
+
|
5
|
+
def alter_table(table_name, options = {}) #:nodoc:
|
6
|
+
table_name = table_name.downcase
|
7
|
+
altered_table_name = "altered_#{table_name}"
|
8
|
+
caller = lambda {|definition| yield definition if block_given?}
|
9
|
+
|
10
|
+
transaction do
|
11
|
+
move_table(table_name, altered_table_name)
|
12
|
+
move_table(altered_table_name, table_name, &caller)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def move_table(from, to, options = {}, &block) #:nodoc:
|
17
|
+
copy_table(from, to, options, &block)
|
18
|
+
drop_table(from)
|
19
|
+
end
|
20
|
+
|
21
|
+
def copy_table(from, to, options = {}) #:nodoc:
|
22
|
+
create_table(to, options) do |@definition|
|
23
|
+
columns(from).each do |column|
|
24
|
+
column_name = options[:rename] ?
|
25
|
+
(options[:rename][column.name] ||
|
26
|
+
options[:rename][column.name.to_sym] ||
|
27
|
+
column.name) : column.name
|
28
|
+
|
29
|
+
@definition.column(column_name, column.type,
|
30
|
+
:limit => column.limit, :default => column.default,
|
31
|
+
:null => column.null)
|
32
|
+
end
|
33
|
+
@definition.primary_key(primary_key(from))
|
34
|
+
yield @definition if block_given?
|
35
|
+
end
|
36
|
+
|
37
|
+
copy_table_indexes(from, to)
|
38
|
+
copy_table_contents(from, to,
|
39
|
+
@definition.columns.map {|column| column.name},
|
40
|
+
options[:rename] || {})
|
41
|
+
end
|
42
|
+
|
43
|
+
def copy_table_indexes(from, to) #:nodoc:
|
44
|
+
indexes(from).each do |index|
|
45
|
+
name = index.name.downcase
|
46
|
+
if to == "altered_#{from}"
|
47
|
+
name = "temp_#{name}"
|
48
|
+
elsif from == "altered_#{to}"
|
49
|
+
name = name[5..-1]
|
50
|
+
end
|
51
|
+
|
52
|
+
# index name can't be the same
|
53
|
+
opts = { :name => name.gsub(/_(#{from})_/, "_#{to}_") }
|
54
|
+
opts[:unique] = true if index.unique
|
55
|
+
add_index(to, index.columns, opts)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def copy_table_contents(from, to, columns, rename = {}) #:nodoc:
|
60
|
+
column_mappings = Hash[*columns.map {|name| [name, name]}.flatten]
|
61
|
+
rename.inject(column_mappings) {|map, a| map[a.last] = a.first; map}
|
62
|
+
from_columns = columns(from).collect {|col| col.name}
|
63
|
+
columns = columns.find_all{|col| from_columns.include?(column_mappings[col])}
|
64
|
+
execute("SELECT * FROM #{from}").each do |row|
|
65
|
+
sql = "INSERT INTO #{to} ("+columns*','+") VALUES ("
|
66
|
+
sql << columns.map {|col| quote row[column_mappings[col]]} * ', '
|
67
|
+
sql << ')'
|
68
|
+
execute sql
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
Binary file
|
data/test/db/derby.rb
CHANGED
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'active_record/connection_adapters/jdbc_adapter'
|
3
|
+
|
4
|
+
System = java.lang.System
|
5
|
+
Context = javax.naming.Context
|
6
|
+
InitialContext = javax.naming.InitialContext
|
7
|
+
Reference = javax.naming.Reference
|
8
|
+
StringRefAddr = javax.naming.StringRefAddr
|
9
|
+
|
10
|
+
System.set_property(Context::INITIAL_CONTEXT_FACTORY,
|
11
|
+
'com.sun.jndi.fscontext.RefFSContextFactory')
|
12
|
+
project_path = File.expand_path(File.dirname(__FILE__) + '/../..')
|
13
|
+
jndi_dir = project_path + '/jndi_test'
|
14
|
+
jdbc_dir = jndi_dir + '/jdbc'
|
15
|
+
FileUtils.mkdir_p jdbc_dir unless File.exist?(jdbc_dir)
|
16
|
+
|
17
|
+
System.set_property(Context::PROVIDER_URL, "file://#{jndi_dir}")
|
18
|
+
derby_ref = Reference.new('javax.sql.DataSource',
|
19
|
+
'org.apache.commons.dbcp.BasicDataSourceFactory',
|
20
|
+
nil)
|
21
|
+
derby_ref.add StringRefAddr.new('driverClassName',
|
22
|
+
'org.apache.derby.jdbc.EmbeddedDriver')
|
23
|
+
derby_ref.add StringRefAddr.new('url',
|
24
|
+
'jdbc:derby:derby-testdb;create=true')
|
25
|
+
derby_ref.add StringRefAddr.new('username', 'sa')
|
26
|
+
derby_ref.add StringRefAddr.new('password', '')
|
27
|
+
|
28
|
+
ic = InitialContext.new
|
29
|
+
ic.rebind("jdbc/derbydb", derby_ref)
|
30
|
+
|
data/test/db/postgres.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
config = {
|
2
|
+
:adapter => 'jdbc',
|
3
|
+
:database => 'weblog_development',
|
4
|
+
:url => 'jdbc:postgresql://localhost/weblog_development',
|
5
|
+
:driver => 'org.postgresql.Driver',
|
6
|
+
:username => 'blog',
|
7
|
+
:password => ''
|
8
|
+
}
|
9
|
+
|
10
|
+
ActiveRecord::Base.establish_connection(config)
|
data/test/derby_simple_test.rb
CHANGED
@@ -4,12 +4,8 @@
|
|
4
4
|
# create database weblog_development;
|
5
5
|
# grant all on weblog_development.* to blog@localhost;
|
6
6
|
|
7
|
-
|
8
|
-
require 'models/auto_id'
|
9
|
-
require 'models/entry'
|
7
|
+
require 'jdbc_common'
|
10
8
|
require 'db/derby'
|
11
|
-
require 'simple'
|
12
|
-
require 'test/unit'
|
13
9
|
|
14
10
|
class DerbySimpleTest < Test::Unit::TestCase
|
15
11
|
include SimpleTestMethods
|
data/test/h2_simple_test.rb
CHANGED
data/test/hsqldb_simple_test.rb
CHANGED
data/test/jdbc_common.rb
ADDED
data/test/jndi_test.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
# In order to run these tests, you need to have a few things on your
|
2
|
+
# classpath. First, you're going to need the Sun File system
|
3
|
+
# context. You can get that here:
|
4
|
+
#
|
5
|
+
# http://java.sun.com/products/jndi/serviceproviders.html.
|
6
|
+
#
|
7
|
+
# Make sure that you put both the fscontext.jar and the
|
8
|
+
# providerutil.jar on your classpath.
|
9
|
+
#
|
10
|
+
# To support the connection pooling in the test, you'll need
|
11
|
+
# commons-dbcp, commons-pool, and commons-collections.
|
12
|
+
#
|
13
|
+
# Finally, you'll need the jdbc driver, which is derby, for this test.
|
14
|
+
|
15
|
+
require 'models/auto_id'
|
16
|
+
require 'models/entry'
|
17
|
+
require 'db/jndi_config'
|
18
|
+
require 'simple'
|
19
|
+
require 'test/unit'
|
20
|
+
require 'logger'
|
21
|
+
|
22
|
+
class DerbyJndiTest < Test::Unit::TestCase
|
23
|
+
include SimpleTestMethods
|
24
|
+
alias_method :setup_simple, :setup
|
25
|
+
def setup
|
26
|
+
ActiveRecord::Base.establish_connection({ :jndi => 'jdbc/derbydb', :adapter => 'jdbc'})
|
27
|
+
logger = Logger.new('jndi_test.log')
|
28
|
+
logger.level = Logger::DEBUG
|
29
|
+
ActiveRecord::Base.logger = logger
|
30
|
+
setup_simple
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
at_exit {
|
35
|
+
require 'fileutils'
|
36
|
+
FileUtils.rm_rf 'derby-testdb'
|
37
|
+
}
|