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.
@@ -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
- require 'active_record/connection_adapters/jdbc_adapter'
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
- return value.to_s
88
- end
130
+ return value.to_s if column && column.type == :primary_key
131
+
89
132
  case value
90
- when String
91
- if column && column.type == :binary
92
- "CAST(x'#{quote_string(value).unpack("C*").collect {|v| v.to_s(16)}.join}' AS BLOB)"
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
@@ -0,0 +1,9 @@
1
+ require 'models/entry'
2
+ require 'db/h2'
3
+ require 'simple'
4
+ require 'test/unit'
5
+ require 'db/logger'
6
+
7
+ class H2SimpleTest < Test::Unit::TestCase
8
+ include SimpleTestMethods
9
+ end
@@ -1,8 +1,8 @@
1
+ require 'models/auto_id'
1
2
  require 'models/entry'
2
3
  require 'db/hsqldb'
3
4
  require 'simple'
4
5
  require 'test/unit'
5
- require 'db/logger'
6
6
 
7
7
  class HsqldbSimpleTest < Test::Unit::TestCase
8
8
  include SimpleTestMethods
@@ -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)}