ActiveRecord-JDBC 0.2.2 → 0.2.3
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +13 -0
- data/Manifest.txt +41 -0
- data/README.txt +94 -0
- data/Rakefile +55 -0
- data/lib/active_record/connection_adapters/jdbc_adapter.rb +251 -84
- data/lib/active_record/connection_adapters/jndi_adapter.rb +51 -0
- data/lib/jdbc_adapter.rb +9 -1
- data/lib/jdbc_adapter/jdbc_derby.rb +66 -6
- data/lib/jdbc_adapter/jdbc_hsqldb.rb +50 -2
- data/lib/jdbc_adapter/jdbc_mysql.rb +3 -12
- data/test/activerecord/connection_adapters/type_conversion_test.rb +30 -0
- data/test/db/derby.rb +21 -0
- data/test/db/h2.rb +14 -0
- data/test/db/hsqldb.rb +5 -0
- data/test/derby_simple_test.rb +16 -0
- data/test/h2_simple_test.rb +9 -0
- data/test/hsqldb_simple_test.rb +1 -1
- data/test/minirunit/testH2.rb +73 -0
- data/test/models/auto_id.rb +19 -0
- data/test/models/data_types.rb +19 -0
- data/test/models/entry.rb +3 -4
- data/test/mysql_simple_test.rb +1 -0
- data/test/simple.rb +75 -24
- metadata +39 -39
- data/init.rb +0 -8
- data/install.rb +0 -25
- data/test/activerecord/jall.sh +0 -7
- data/test/activerecord/jtest.sh +0 -3
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'active_record/connection_adapters/jdbc_adapter'
|
2
|
+
|
3
|
+
module ActiveRecord #:nodoc:
|
4
|
+
class Base #:nodoc:
|
5
|
+
def self.jdbc_connection(config)
|
6
|
+
config.symbolize_keys
|
7
|
+
if config[:jndi]
|
8
|
+
connection = ConnectionAdapters::JndiConnection.new(config)
|
9
|
+
else
|
10
|
+
connection = ConnectionAdapters::JdbcConnection.new(config)
|
11
|
+
end
|
12
|
+
ConnectionAdapters::JdbcAdapter.new(connection, logger, config)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
module ConnectionAdapters #:nodoc:
|
17
|
+
|
18
|
+
# This adapter allows ActiveRecord to use JNDI to retrieve
|
19
|
+
# a JDBC connection from a previously configured DataSource.
|
20
|
+
# The ActiveRecord configuration looks like this:
|
21
|
+
#
|
22
|
+
# ActiveRecord::Base.establish_connection(
|
23
|
+
# :adapter => 'jdbc',
|
24
|
+
# :jndi => 'java:comp/env/jdbc/test',
|
25
|
+
# :driver => 'sqlserver'
|
26
|
+
# )
|
27
|
+
#
|
28
|
+
# Right now, enough driver information needs to be supplied so that AR-JDBC
|
29
|
+
# can genrate the right flavor of SQL. However, it's not necessary to know
|
30
|
+
# exactly which driver is being used, just enough so the right SQL is generated.
|
31
|
+
#
|
32
|
+
class JndiConnection < JdbcConnection
|
33
|
+
|
34
|
+
def initialize(config)
|
35
|
+
@config = config
|
36
|
+
jndi = @config[:jndi].to_s
|
37
|
+
|
38
|
+
ctx = javax.naming.InitialContext.new
|
39
|
+
ds = ctx.lookup(jndi)
|
40
|
+
@connection = ds.connection
|
41
|
+
set_native_database_types
|
42
|
+
|
43
|
+
@stmts = {}
|
44
|
+
rescue Exception => e
|
45
|
+
raise "The driver encountered an error: #{e}"
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
data/lib/jdbc_adapter.rb
CHANGED
@@ -1 +1,9 @@
|
|
1
|
-
|
1
|
+
if RUBY_PLATFORM =~ /java/
|
2
|
+
if defined?(RAILS_CONNECTION_ADAPTERS)
|
3
|
+
RAILS_CONNECTION_ADAPTERS << %q(jdbc)
|
4
|
+
else
|
5
|
+
RAILS_CONNECTION_ADAPTERS = %w(jdbc)
|
6
|
+
end
|
7
|
+
else
|
8
|
+
raise "ActiveRecord-JDBC is for use with JRuby only"
|
9
|
+
end
|
@@ -82,14 +82,69 @@ module JdbcSpec
|
|
82
82
|
execute "RENAME TABLE #{name} TO #{new_name}"
|
83
83
|
end
|
84
84
|
|
85
|
+
# Support for removing columns added via derby bug issue:
|
86
|
+
# https://issues.apache.org/jira/browse/DERBY-1489
|
87
|
+
#
|
88
|
+
# This feature has not made it into a formal release and is not in Java 6. We will
|
89
|
+
# need to conditionally support this somehow (supposed to arrive for 10.3.0.0)
|
90
|
+
#
|
91
|
+
# def remove_column(table_name, column_name)
|
92
|
+
# execute "ALTER TABLE #{table_name} DROP COLUMN #{column_name} RESTRICT"
|
93
|
+
# end
|
94
|
+
|
95
|
+
# Notes about changing in Derby:
|
96
|
+
# http://db.apache.org/derby/docs/10.2/ref/rrefsqlj81859.html#rrefsqlj81859__rrefsqlj37860)
|
97
|
+
# Derby cannot: Change the column type or decrease the precision of an existing type, but
|
98
|
+
# can increase the types precision only if it is a VARCHAR.
|
99
|
+
#
|
100
|
+
def change_column(table_name, column_name, type, options = {}) #:nodoc:
|
101
|
+
execute "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} SET DATA TYPE #{type_to_sql(type, options[:limit])}"
|
102
|
+
end
|
103
|
+
|
104
|
+
def change_column_default(table_name, column_name, default) #:nodoc:
|
105
|
+
execute "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} SET DEFAULT #{quote(default)}"
|
106
|
+
end
|
107
|
+
|
108
|
+
# Support for renaming columns:
|
109
|
+
# https://issues.apache.org/jira/browse/DERBY-1490
|
110
|
+
#
|
111
|
+
# This feature is expect to arrive in version 10.3.0.0:
|
112
|
+
# http://wiki.apache.org/db-derby/DerbyTenThreeRelease)
|
113
|
+
#
|
114
|
+
#def rename_column(table_name, column_name, new_column_name) #:nodoc:
|
115
|
+
# execute "ALTER TABLE #{table_name} ALTER RENAME COLUMN #{column_name} TO #{new_column_name}"
|
116
|
+
#end
|
117
|
+
|
118
|
+
def primary_keys(table_name)
|
119
|
+
@connection.primary_keys table_name.to_s.upcase
|
120
|
+
end
|
121
|
+
|
122
|
+
# For migrations, exclude the primary key index as recommended
|
123
|
+
# by the HSQLDB docs. This is not a great test for primary key
|
124
|
+
# index.
|
125
|
+
def indexes(table_name)
|
126
|
+
@connection.indexes(table_name)
|
127
|
+
end
|
128
|
+
|
85
129
|
def quote(value, column = nil) # :nodoc:
|
86
|
-
if column && column.type == :primary_key
|
87
|
-
|
88
|
-
end
|
130
|
+
return value.to_s if column && column.type == :primary_key
|
131
|
+
|
89
132
|
case value
|
90
|
-
when String
|
91
|
-
if column
|
92
|
-
|
133
|
+
when String
|
134
|
+
if column
|
135
|
+
case column.type
|
136
|
+
when :binary
|
137
|
+
"CAST(x'#{quote_string(value).unpack("C*").collect {|v| v.to_s(16)}.join}' AS BLOB)"
|
138
|
+
when :string
|
139
|
+
"'#{quote_string(value)}'"
|
140
|
+
else
|
141
|
+
vi = value.to_i
|
142
|
+
if vi.to_s == value
|
143
|
+
value
|
144
|
+
else
|
145
|
+
"'#{quote_string(value)}'"
|
146
|
+
end
|
147
|
+
end
|
93
148
|
else
|
94
149
|
vi = value.to_i
|
95
150
|
if vi.to_s == value
|
@@ -101,6 +156,11 @@ module JdbcSpec
|
|
101
156
|
else super
|
102
157
|
end
|
103
158
|
end
|
159
|
+
|
160
|
+
# For DDL it appears you can quote "" column names, but in queries (like insert it errors out?)
|
161
|
+
# def quote_column_name(name) #:nodoc:
|
162
|
+
# %Q{"#{name}"}
|
163
|
+
# end
|
104
164
|
|
105
165
|
def quoted_true
|
106
166
|
'1'
|
@@ -6,7 +6,7 @@ module JdbcSpec
|
|
6
6
|
case type
|
7
7
|
when :string then value
|
8
8
|
when :integer then defined?(value.to_i) ? value.to_i : (value ? 1 : 0)
|
9
|
-
when :primary_key then defined?(value.to_i) ? value.to_i : (value ? 1 : 0)
|
9
|
+
when :primary_key then defined?(value.to_i) ? value.to_i : (value ? 1 : 0)
|
10
10
|
when :float then value.to_f
|
11
11
|
when :datetime then cast_to_date_or_time(value)
|
12
12
|
when :timestamp then cast_to_time(value)
|
@@ -32,12 +32,36 @@ module JdbcSpec
|
|
32
32
|
(value.hour == 0 and value.min == 0 and value.sec == 0) ?
|
33
33
|
Date.new(value.year, value.month, value.day) : value
|
34
34
|
end
|
35
|
+
|
36
|
+
|
37
|
+
private
|
38
|
+
def simplified_type(field_type)
|
39
|
+
case field_type
|
40
|
+
when /longvarchar/i
|
41
|
+
:text
|
42
|
+
else
|
43
|
+
super(field_type)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Override of ActiveRecord::ConnectionAdapters::Column
|
48
|
+
def extract_limit(sql_type)
|
49
|
+
# HSQLDB appears to return "LONGVARCHAR(0)" for :text columns, which
|
50
|
+
# for AR purposes should be interpreted as "no limit"
|
51
|
+
return nil if sql_type =~ /\(0\)/
|
52
|
+
super
|
53
|
+
end
|
35
54
|
end
|
36
55
|
|
37
56
|
def modify_types(tp)
|
38
57
|
tp[:primary_key] = "INTEGER GENERATED BY DEFAULT AS IDENTITY(START WITH 0) PRIMARY KEY"
|
39
58
|
tp[:integer][:limit] = nil
|
40
59
|
tp[:boolean][:limit] = nil
|
60
|
+
# set text and float limits so we don't see odd scales tacked on
|
61
|
+
# in migrations
|
62
|
+
tp[:text][:limit] = nil
|
63
|
+
tp[:float][:limit] = 17
|
64
|
+
tp[:string][:limit] = 255
|
41
65
|
tp[:datetime] = { :name => "DATETIME" }
|
42
66
|
tp[:timestamp] = { :name => "DATETIME" }
|
43
67
|
tp[:time] = { :name => "DATETIME" }
|
@@ -47,7 +71,7 @@ module JdbcSpec
|
|
47
71
|
|
48
72
|
def quote(value, column = nil) # :nodoc:
|
49
73
|
case value
|
50
|
-
when String
|
74
|
+
when String
|
51
75
|
if column && column.type == :binary
|
52
76
|
"'#{quote_string(value).unpack("C*").collect {|v| v.to_s(16)}.join}'"
|
53
77
|
else
|
@@ -57,6 +81,10 @@ module JdbcSpec
|
|
57
81
|
end
|
58
82
|
end
|
59
83
|
|
84
|
+
def quote_string(str)
|
85
|
+
str.gsub(/'/, "''")
|
86
|
+
end
|
87
|
+
|
60
88
|
def quoted_true
|
61
89
|
'1'
|
62
90
|
end
|
@@ -100,5 +128,25 @@ module JdbcSpec
|
|
100
128
|
sql.replace "select limit #{offset} 0 #{bef}"
|
101
129
|
end
|
102
130
|
end
|
131
|
+
|
132
|
+
# override to filter out system tables that otherwise end
|
133
|
+
# up in db/schema.rb during migrations. JdbcConnection#tables
|
134
|
+
# now takes an optional block filter so we can screen out
|
135
|
+
# rows corresponding to system tables. HSQLDB names its
|
136
|
+
# system tables SYSTEM.*, but H2 seems to name them without
|
137
|
+
# any kind of convention
|
138
|
+
def tables
|
139
|
+
@connection.tables do |result_row|
|
140
|
+
result_row.get_string(ActiveRecord::ConnectionAdapters::Jdbc::TableMetaData::TABLE_TYPE) !~ /^SYSTEM TABLE$/i
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
# For migrations, exclude the primary key index as recommended
|
145
|
+
# by the HSQLDB docs. This is not a great test for primary key
|
146
|
+
# index.
|
147
|
+
def indexes(table_name, name = nil)
|
148
|
+
@connection.indexes(table_name.to_s)
|
149
|
+
end
|
150
|
+
|
103
151
|
end
|
104
152
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'active_record/connection_adapters/abstract/schema_definitions'
|
2
|
+
|
1
3
|
module JdbcSpec
|
2
4
|
module MySQL
|
3
5
|
def modify_types(tp)
|
@@ -73,18 +75,7 @@ module JdbcSpec
|
|
73
75
|
end
|
74
76
|
|
75
77
|
def indexes(table_name, name = nil)#:nodoc:
|
76
|
-
indexes
|
77
|
-
current_index = nil
|
78
|
-
execute("SHOW KEYS FROM #{table_name}", name).each do |row|
|
79
|
-
if current_index != row[2]
|
80
|
-
next if row[2] == "PRIMARY" # skip the primary key
|
81
|
-
current_index = row[2]
|
82
|
-
indexes << IndexDefinition.new(row[0], row[2], row[1] == "0", [])
|
83
|
-
end
|
84
|
-
|
85
|
-
indexes.last.columns << row[4]
|
86
|
-
end
|
87
|
-
indexes
|
78
|
+
@connection.indexes(table_name)
|
88
79
|
end
|
89
80
|
|
90
81
|
def create_table(name, options = {}) #:nodoc:
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'java'
|
2
|
+
require 'models/data_types'
|
3
|
+
require 'db/derby'
|
4
|
+
require 'test/unit'
|
5
|
+
|
6
|
+
JInteger = java.lang.Integer
|
7
|
+
|
8
|
+
class TypeConversionTest < Test::Unit::TestCase
|
9
|
+
|
10
|
+
def setup
|
11
|
+
DbTypeMigration.up
|
12
|
+
DbType.create(
|
13
|
+
:sample_timestamp => Time.at(1169964202),
|
14
|
+
:sample_decimal => JInteger::MAX_VALUE + 1)
|
15
|
+
end
|
16
|
+
|
17
|
+
def teardown
|
18
|
+
DbTypeMigration.down
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_timestamp
|
22
|
+
types = DbType.find(:first)
|
23
|
+
assert_equal 'Sun Jan 28 06:03:22 UTC 2007', types.sample_timestamp.getutc.to_s
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_decimal
|
27
|
+
types = DbType.find(:first)
|
28
|
+
assert_equal((JInteger::MAX_VALUE + 1), types.sample_decimal)
|
29
|
+
end
|
30
|
+
end
|
data/test/db/derby.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
config = {
|
4
|
+
:adapter => 'jdbc',
|
5
|
+
:username => 'sa',
|
6
|
+
:password => '',
|
7
|
+
:driver => 'org.apache.derby.jdbc.EmbeddedDriver',
|
8
|
+
:url => 'jdbc:derby:derby-testdb;create=true'
|
9
|
+
}
|
10
|
+
|
11
|
+
ActiveRecord::Base.establish_connection(config)
|
12
|
+
logger = Logger.new 'derby-testdb.log'
|
13
|
+
logger.level = Logger::DEBUG
|
14
|
+
ActiveRecord::Base.logger = logger
|
15
|
+
|
16
|
+
at_exit {
|
17
|
+
# Clean up derby files
|
18
|
+
require 'fileutils'
|
19
|
+
Dir.glob('derby-testdb/**/*') {|f| File.delete(f)}
|
20
|
+
FileUtils.rm_r('derby-testdb')
|
21
|
+
}
|
data/test/db/h2.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
config = {
|
2
|
+
:adapter => 'jdbc',
|
3
|
+
:username => 'sa',
|
4
|
+
:password => '',
|
5
|
+
:driver => 'org.h2.Driver',
|
6
|
+
:url => 'jdbc:h2:test.db'
|
7
|
+
}
|
8
|
+
|
9
|
+
ActiveRecord::Base.establish_connection(config)
|
10
|
+
|
11
|
+
at_exit {
|
12
|
+
# Clean up hsqldb when done
|
13
|
+
Dir['test.db*'].each {|f| File.delete(f)}
|
14
|
+
}
|
data/test/db/hsqldb.rb
CHANGED
@@ -4,11 +4,16 @@ config = {
|
|
4
4
|
:password => '',
|
5
5
|
:driver => 'org.hsqldb.jdbcDriver',
|
6
6
|
:url => 'jdbc:hsqldb:test.db'
|
7
|
+
#:url => 'jdbc:hsqldb:mem:test'
|
7
8
|
}
|
8
9
|
|
9
10
|
ActiveRecord::Base.establish_connection(config)
|
11
|
+
logger = Logger.new 'hsqldb-testdb.log'
|
12
|
+
logger.level = Logger::DEBUG
|
13
|
+
ActiveRecord::Base.logger = logger
|
10
14
|
|
11
15
|
at_exit {
|
12
16
|
# Clean up hsqldb when done
|
13
17
|
Dir['test.db*'].each {|f| File.delete(f)}
|
18
|
+
File.delete('hsqldb-testdb.log')
|
14
19
|
}
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# To run this script, run the following in a mysql instance:
|
2
|
+
#
|
3
|
+
# drop database if exists weblog_development;
|
4
|
+
# create database weblog_development;
|
5
|
+
# grant all on weblog_development.* to blog@localhost;
|
6
|
+
|
7
|
+
|
8
|
+
require 'models/auto_id'
|
9
|
+
require 'models/entry'
|
10
|
+
require 'db/derby'
|
11
|
+
require 'simple'
|
12
|
+
require 'test/unit'
|
13
|
+
|
14
|
+
class DerbySimpleTest < Test::Unit::TestCase
|
15
|
+
include SimpleTestMethods
|
16
|
+
end
|
data/test/hsqldb_simple_test.rb
CHANGED
@@ -0,0 +1,73 @@
|
|
1
|
+
|
2
|
+
require 'minirunit'
|
3
|
+
|
4
|
+
config = {
|
5
|
+
:adapter => 'jdbc',
|
6
|
+
:username => 'sa',
|
7
|
+
:password => '',
|
8
|
+
:driver => 'org.h2.Driver',
|
9
|
+
:url => 'jdbc:h2:test.db'
|
10
|
+
}
|
11
|
+
RAILS_CONNECTION_ADAPTERS = ['abstract', 'jdbc']
|
12
|
+
|
13
|
+
require 'active_record'
|
14
|
+
|
15
|
+
ActiveRecord::Base.establish_connection(config)
|
16
|
+
require 'logger'
|
17
|
+
ActiveRecord::Base.logger = Logger.new($stdout)
|
18
|
+
ActiveRecord::Base.logger.level = Logger::DEBUG
|
19
|
+
|
20
|
+
class CreateEntries < ActiveRecord::Migration
|
21
|
+
def self.up
|
22
|
+
create_table "entries", :force => true do |t|
|
23
|
+
t.column :title, :string, :limit => 100
|
24
|
+
t.column :updated_on, :datetime
|
25
|
+
t.column :content, :text
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.down
|
30
|
+
drop_table "entries"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
CreateEntries.up
|
35
|
+
|
36
|
+
test_ok ActiveRecord::Base.connection.tables.include?('entries')
|
37
|
+
|
38
|
+
class Entry < ActiveRecord::Base
|
39
|
+
end
|
40
|
+
|
41
|
+
Entry.delete_all
|
42
|
+
|
43
|
+
test_equal 0, Entry.count
|
44
|
+
|
45
|
+
TITLE = "First post!"
|
46
|
+
CONTENT = "Hello from JRuby on Rails!"
|
47
|
+
NEW_TITLE = "First post updated title"
|
48
|
+
|
49
|
+
post = Entry.new
|
50
|
+
post.title = TITLE
|
51
|
+
post.content = CONTENT
|
52
|
+
post.save
|
53
|
+
|
54
|
+
test_equal 1, Entry.count
|
55
|
+
|
56
|
+
post = Entry.find(:first)
|
57
|
+
test_equal TITLE, post.title
|
58
|
+
test_equal CONTENT, post.content
|
59
|
+
|
60
|
+
post.title = NEW_TITLE
|
61
|
+
post.save
|
62
|
+
|
63
|
+
post = Entry.find(:first)
|
64
|
+
test_equal NEW_TITLE, post.title
|
65
|
+
|
66
|
+
post.destroy
|
67
|
+
|
68
|
+
test_equal 0, Entry.count
|
69
|
+
|
70
|
+
CreateEntries.down
|
71
|
+
|
72
|
+
# Clean up hsqldb when done
|
73
|
+
Dir['test.db*'].each {|f| File.delete(f)}
|