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/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
- raise "ActiveRecord-JDBC is for use with JRuby only"
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
- @limit ||= -1
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
- @offset ||= 0
58
- select(sql, name)[@offset]
57
+ @limit ||= 1
58
+ execute(sql, name).first
59
59
  ensure
60
- @limit = @offset = nil
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
- # def remove_column(table_name, column_name)
92
- # execute "ALTER TABLE #{table_name} DROP COLUMN #{column_name} RESTRICT"
93
- # end
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
- execute "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} SET DATA TYPE #{type_to_sql(type, options[:limit])}"
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
- return value.to_s
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
- str.gsub(/([\0\n\r\032\'\"\\])/) do
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}'")["type"]
120
+ current_type = select_one("SHOW COLUMNS FROM #{table_name} LIKE '#{column_name}'")["Type"]
91
121
 
92
- type, limit = native_sql_to_type(current_type)
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[:default] ||= select_one("SHOW COLUMNS FROM #{table_name} LIKE '#{column_name}'")["default"]
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}'")["type"]
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
@@ -17,5 +17,5 @@ at_exit {
17
17
  # Clean up derby files
18
18
  require 'fileutils'
19
19
  Dir.glob('derby-testdb/**/*') {|f| File.delete(f)}
20
- FileUtils.rm_r('derby-testdb')
21
- }
20
+ FileUtils.rm_rf('derby-testdb')
21
+ }
@@ -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
+
@@ -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)
@@ -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
@@ -1,7 +1,5 @@
1
- require 'models/entry'
1
+ require 'jdbc_common'
2
2
  require 'db/h2'
3
- require 'simple'
4
- require 'test/unit'
5
3
  require 'db/logger'
6
4
 
7
5
  class H2SimpleTest < Test::Unit::TestCase
@@ -1,8 +1,5 @@
1
- require 'models/auto_id'
2
- require 'models/entry'
1
+ require 'jdbc_common'
3
2
  require 'db/hsqldb'
4
- require 'simple'
5
- require 'test/unit'
6
3
 
7
4
  class HsqldbSimpleTest < Test::Unit::TestCase
8
5
  include SimpleTestMethods
@@ -0,0 +1,6 @@
1
+ require 'jdbc_adapter'
2
+ require 'rubygems'
3
+ require 'models/auto_id'
4
+ require 'models/entry'
5
+ require 'simple'
6
+ require 'test/unit'
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
+ }