activerecord-jdbc-adapter 1.0.0.beta1-java → 1.0.0.beta2-java

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.
Files changed (57) hide show
  1. data/History.txt +37 -0
  2. data/Manifest.txt +8 -0
  3. data/README.txt +41 -88
  4. data/lib/active_record/connection_adapters/mysql2_adapter.rb +1 -0
  5. data/lib/arel/engines/sql/compilers/hsqldb_compiler.rb +9 -0
  6. data/lib/arel/engines/sql/compilers/mssql_compiler.rb +34 -0
  7. data/lib/arjdbc/db2/adapter.rb +232 -52
  8. data/lib/arjdbc/derby/adapter.rb +28 -1
  9. data/lib/arjdbc/derby/connection_methods.rb +1 -1
  10. data/lib/arjdbc/discover.rb +1 -1
  11. data/lib/arjdbc/firebird/adapter.rb +26 -0
  12. data/lib/arjdbc/h2/adapter.rb +13 -0
  13. data/lib/arjdbc/hsqldb/adapter.rb +8 -6
  14. data/lib/arjdbc/informix/adapter.rb +4 -0
  15. data/lib/arjdbc/jdbc/adapter.rb +27 -5
  16. data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
  17. data/lib/arjdbc/jdbc/connection.rb +76 -45
  18. data/lib/arjdbc/jdbc/jdbc.rake +22 -20
  19. data/lib/arjdbc/jdbc/type_converter.rb +9 -2
  20. data/lib/arjdbc/mssql/adapter.rb +102 -24
  21. data/lib/arjdbc/mssql/connection_methods.rb +19 -2
  22. data/lib/arjdbc/mssql/tsql_helper.rb +1 -0
  23. data/lib/arjdbc/mysql/adapter.rb +6 -0
  24. data/lib/arjdbc/mysql/connection_methods.rb +8 -7
  25. data/lib/arjdbc/oracle/adapter.rb +8 -6
  26. data/lib/arjdbc/postgresql/adapter.rb +51 -19
  27. data/lib/arjdbc/version.rb +1 -1
  28. data/lib/jdbc_adapter/rake_tasks.rb +3 -0
  29. data/rails_generators/templates/lib/tasks/jdbc.rake +2 -2
  30. data/rakelib/package.rake +2 -0
  31. data/rakelib/test.rake +6 -3
  32. data/src/java/arjdbc/derby/DerbyModule.java +30 -1
  33. data/src/java/arjdbc/informix/InformixRubyJdbcConnection.java +74 -0
  34. data/src/java/arjdbc/jdbc/AdapterJavaService.java +7 -3
  35. data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +45 -30
  36. data/src/java/arjdbc/mssql/MssqlRubyJdbcConnection.java +54 -1
  37. data/src/java/arjdbc/oracle/OracleRubyJdbcConnection.java +85 -0
  38. data/test/abstract_db_create.rb +6 -1
  39. data/test/db/jndi_config.rb +20 -10
  40. data/test/db2_simple_test.rb +34 -1
  41. data/test/derby_simple_test.rb +78 -0
  42. data/test/generic_jdbc_connection_test.rb +21 -1
  43. data/test/jndi_callbacks_test.rb +2 -1
  44. data/test/jndi_test.rb +1 -11
  45. data/test/models/entry.rb +20 -0
  46. data/test/mssql_limit_offset_test.rb +28 -0
  47. data/test/mssql_simple_test.rb +7 -1
  48. data/test/mysql_info_test.rb +49 -6
  49. data/test/mysql_simple_test.rb +4 -0
  50. data/test/oracle_simple_test.rb +3 -47
  51. data/test/oracle_specific_test.rb +83 -0
  52. data/test/postgres_db_create_test.rb +6 -0
  53. data/test/postgres_drop_db_test.rb +16 -0
  54. data/test/postgres_simple_test.rb +17 -0
  55. data/test/postgres_table_alias_length_test.rb +15 -0
  56. data/test/simple.rb +17 -4
  57. metadata +33 -7
@@ -215,6 +215,29 @@ module ::ArJdbc
215
215
  super
216
216
  end
217
217
 
218
+ # SELECT DISTINCT clause for a given set of columns and a given ORDER BY clause.
219
+ #
220
+ # Derby requires the ORDER BY columns in the select list for distinct queries, and
221
+ # requires that the ORDER BY include the distinct column.
222
+ #
223
+ # distinct("posts.id", "posts.created_at desc")
224
+ #
225
+ # Based on distinct method for PostgreSQL Adapter
226
+ def distinct(columns, order_by)
227
+ return "DISTINCT #{columns}" if order_by.blank?
228
+
229
+ # construct a clean list of column names from the ORDER BY clause, removing
230
+ # any asc/desc modifiers
231
+ order_columns = order_by.split(',').collect { |s| s.split.first }
232
+ order_columns.delete_if(&:blank?)
233
+ order_columns = order_columns.zip((0...order_columns.size).to_a).map { |s,i| "#{s} AS alias_#{i}" }
234
+
235
+ # return a DISTINCT clause that's distinct on the columns we want but includes
236
+ # all the required columns for the ORDER BY to work properly
237
+ sql = "DISTINCT #{columns}, #{order_columns * ', '}"
238
+ sql
239
+ end
240
+
218
241
  # I don't think this method is ever called ??? (stepheneb)
219
242
  def create_column(name, refid, colno)
220
243
  stmt = COLUMN_TYPE_STMT % [refid, strip_quotes(name)]
@@ -400,7 +423,11 @@ module ::ArJdbc
400
423
  private
401
424
  # Derby appears to define schemas using the username
402
425
  def derby_schema
403
- @config[:username].to_s
426
+ if @config.has_key?(:schema)
427
+ config[:schema]
428
+ else
429
+ (@config[:username] && @config[:username].to_s) || ''
430
+ end
404
431
  end
405
432
  end
406
433
  end
@@ -5,7 +5,7 @@ module ActiveRecord
5
5
  config[:url] ||= "jdbc:derby:#{config[:database]};create=true"
6
6
  config[:driver] ||= "org.apache.derby.jdbc.EmbeddedDriver"
7
7
  conn = embedded_driver(config)
8
- md = conn.raw_connection.connection.meta_data
8
+ md = conn.jdbc_connection.meta_data
9
9
  if md.database_major_version < 10 || md.database_minor_version < 5
10
10
  raise ::ActiveRecord::ConnectionFailed, "Derby adapter requires Derby 10.5 or later"
11
11
  end
@@ -28,7 +28,7 @@ module ::ArJdbc
28
28
  end
29
29
 
30
30
  extension :DB2 do |name, config|
31
- if name =~ /db2/i && config[:url] !~ /^jdbc:derby:net:/
31
+ if name =~ /(db2|as400)/i && config[:url] !~ /^jdbc:derby:net:/
32
32
  require 'arjdbc/db2'
33
33
  true
34
34
  end
@@ -1,5 +1,28 @@
1
1
  module ::ArJdbc
2
2
  module FireBird
3
+
4
+ def self.extended(mod)
5
+ unless @lob_callback_added
6
+ ActiveRecord::Base.class_eval do
7
+ def after_save_with_firebird_blob
8
+ self.class.columns.select { |c| c.sql_type =~ /blob/i }.each do |c|
9
+ value = self[c.name]
10
+ value = value.to_yaml if unserializable_attribute?(c.name, c)
11
+ next if value.nil?
12
+ connection.write_large_object(c.type == :binary, c.name, self.class.table_name, self.class.primary_key, quote_value(id), value)
13
+ end
14
+ end
15
+ end
16
+
17
+ ActiveRecord::Base.after_save :after_save_with_firebird_blob
18
+ @lob_callback_added = true
19
+ end
20
+ end
21
+
22
+ def adapter_name
23
+ 'Firebird'
24
+ end
25
+
3
26
  def modify_types(tp)
4
27
  tp[:primary_key] = 'INTEGER NOT NULL PRIMARY KEY'
5
28
  tp[:string][:limit] = 252
@@ -62,6 +85,9 @@ module ::ArJdbc
62
85
  def quote(value, column = nil) # :nodoc:
63
86
  return value.quoted_id if value.respond_to?(:quoted_id)
64
87
 
88
+ # BLOBs are updated separately by an after_save trigger.
89
+ return value.nil? ? "NULL" : "'#{quote_string(value[0..1])}'" if column && [:binary, :text].include?(column.type)
90
+
65
91
  if [Time, DateTime].include?(value.class)
66
92
  "CAST('#{value.strftime("%Y-%m-%d %H:%M:%S")}' AS TIMESTAMP)"
67
93
  else
@@ -11,5 +11,18 @@ module ArJdbc
11
11
  def h2_adapter
12
12
  true
13
13
  end
14
+
15
+ def tables
16
+ @connection.tables(nil, h2_schema)
17
+ end
18
+
19
+ def columns(table_name, name=nil)
20
+ @connection.columns_internal(table_name.to_s, name, h2_schema)
21
+ end
22
+
23
+ private
24
+ def h2_schema
25
+ @config[:schema] || ''
26
+ end
14
27
  end
15
28
  end
@@ -143,12 +143,14 @@ module ::ArJdbc
143
143
  end
144
144
 
145
145
  def add_limit_offset!(sql, options) #:nodoc:
146
- offset = options[:offset] || 0
147
- bef = sql[7..-1]
148
- if limit = options[:limit]
149
- sql.replace "select limit #{offset} #{limit} #{bef}"
150
- elsif offset > 0
151
- sql.replace "select limit #{offset} 0 #{bef}"
146
+ if sql =~ /^select/i
147
+ offset = options[:offset] || 0
148
+ bef = sql[7..-1]
149
+ if limit = options[:limit]
150
+ sql.replace "SELECT LIMIT #{offset} #{limit} #{bef}"
151
+ elsif offset > 0
152
+ sql.replace "SELECT LIMIT #{offset} 0 #{bef}"
153
+ end
152
154
  end
153
155
  end
154
156
 
@@ -36,6 +36,10 @@ module ::ArJdbc
36
36
  lambda { |cfg, column| column.extend(::ArJdbc::Informix::Column) } ]
37
37
  end
38
38
 
39
+ def self.jdbc_connection_class
40
+ ::ActiveRecord::ConnectionAdapters::InformixJdbcConnection
41
+ end
42
+
39
43
  module Column
40
44
  private
41
45
  # TODO: Test all Informix column types.
@@ -46,13 +46,35 @@ module ActiveRecord
46
46
  ActiveRecord::ConnectionAdapters::JdbcColumn
47
47
  end
48
48
 
49
+ # Retrieve the raw java.sql.Connection object.
50
+ def jdbc_connection
51
+ raw_connection.connection
52
+ end
53
+
49
54
  # Locate specialized adapter specification if one exists based on config data
50
55
  def adapter_spec(config)
51
- dialect = (config[:dialect] || config[:driver]).to_s
52
- ::ArJdbc.constants.map { |name| ::ArJdbc.const_get name }.each do |constant|
53
- if constant.respond_to? :adapter_matcher
54
- spec = constant.adapter_matcher(dialect, config)
55
- return spec if spec
56
+ 2.times do
57
+ dialect = (config[:dialect] || config[:driver]).to_s
58
+ ::ArJdbc.constants.map { |name| ::ArJdbc.const_get name }.each do |constant|
59
+ if constant.respond_to? :adapter_matcher
60
+ spec = constant.adapter_matcher(dialect, config)
61
+ return spec if spec
62
+ end
63
+ end
64
+
65
+ # If nothing matches and we're using jndi, try to automatically detect the database.
66
+ break unless config[:jndi] and !config[:dialect]
67
+ begin
68
+ conn = Java::javax.naming.InitialContext.new.lookup(config[:jndi]).getConnection
69
+ config[:dialect] = conn.getMetaData.getClass.getName.downcase
70
+
71
+ # Derby-specific hack
72
+ if ::ArJdbc::Derby.adapter_matcher(config[:dialect], config)
73
+ # Needed to set the correct database schema name
74
+ config[:username] ||= conn.getMetaData.getUserName
75
+ end
76
+ rescue
77
+ conn.close
56
78
  end
57
79
  end
58
80
  nil
Binary file
@@ -1,6 +1,76 @@
1
1
  module ActiveRecord
2
2
  module ConnectionAdapters
3
3
  class JdbcConnection
4
+ module ConfigHelper
5
+ attr_reader :config
6
+
7
+ def config=(config)
8
+ @config = config.symbolize_keys!
9
+ end
10
+
11
+ def configure_connection
12
+ config[:retry_count] ||= 5
13
+ config[:connection_alive_sql] ||= "select 1"
14
+ @jndi_connection = false
15
+ @connection = nil
16
+ if config[:jndi]
17
+ begin
18
+ configure_jndi
19
+ rescue => e
20
+ warn "JNDI data source unavailable: #{e.message}; trying straight JDBC"
21
+ configure_jdbc
22
+ end
23
+ else
24
+ configure_jdbc
25
+ end
26
+ end
27
+
28
+ def configure_jndi
29
+ jndi = config[:jndi].to_s
30
+ ctx = javax.naming.InitialContext.new
31
+ ds = ctx.lookup(jndi)
32
+ @connection_factory = JdbcConnectionFactory.impl do
33
+ ds.connection
34
+ end
35
+ unless config[:driver]
36
+ config[:driver] = connection.meta_data.connection.java_class.name
37
+ end
38
+ @jndi_connection = true
39
+ end
40
+
41
+ def configure_url
42
+ url = config[:url].to_s
43
+ if Hash === config[:options]
44
+ options = ''
45
+ config[:options].each do |k,v|
46
+ options << '&' unless options.empty?
47
+ options << "#{k}=#{v}"
48
+ end
49
+ url = url['?'] ? "#{url}&#{options}" : "#{url}?#{options}" unless options.empty?
50
+ config[:url] = url
51
+ config[:options] = nil
52
+ end
53
+ url
54
+ end
55
+
56
+ def configure_jdbc
57
+ unless config[:driver] && config[:url]
58
+ raise ::ActiveRecord::ConnectionNotEstablished, "jdbc adapter requires driver class and url"
59
+ end
60
+
61
+ driver = config[:driver].to_s
62
+ user = config[:username].to_s
63
+ pass = config[:password].to_s
64
+ url = configure_url
65
+
66
+ jdbc_driver = JdbcDriver.new(driver)
67
+ jdbc_driver.load
68
+ @connection_factory = JdbcConnectionFactory.impl do
69
+ jdbc_driver.connection(url, user, pass)
70
+ end
71
+ end
72
+ end
73
+
4
74
  attr_reader :adapter, :connection_factory
5
75
 
6
76
  # @native_database_types - setup properly by adapter= versus set_native_database_types.
@@ -12,26 +82,15 @@ module ActiveRecord
12
82
  # then this is used as a base to be tweaked by each adapter to create @native_database_types
13
83
 
14
84
  def initialize(config)
15
- @config = config.symbolize_keys!
16
- @config[:retry_count] ||= 5
17
- @config[:connection_alive_sql] ||= "select 1"
18
- @jndi_connection = false
19
- @connection = nil
20
- if @config[:jndi]
21
- begin
22
- configure_jndi
23
- rescue => e
24
- warn "JNDI data source unavailable: #{e.message}; trying straight JDBC"
25
- configure_jdbc
26
- end
27
- else
28
- configure_jdbc
29
- end
85
+ self.config = config
86
+ configure_connection
30
87
  connection # force the connection to load
31
88
  set_native_database_types
32
89
  @stmts = {}
90
+ rescue ::ActiveRecord::ActiveRecordError
91
+ raise
33
92
  rescue Exception => e
34
- raise "The driver encountered an error: #{e}"
93
+ raise "The driver encountered an unknown error: #{e}"
35
94
  end
36
95
 
37
96
  def adapter=(adapter)
@@ -63,35 +122,7 @@ module ActiveRecord
63
122
  end
64
123
 
65
124
  private
66
- def configure_jndi
67
- jndi = @config[:jndi].to_s
68
- ctx = javax.naming.InitialContext.new
69
- ds = ctx.lookup(jndi)
70
- @connection_factory = JdbcConnectionFactory.impl do
71
- ds.connection
72
- end
73
- unless @config[:driver]
74
- @config[:driver] = connection.meta_data.connection.java_class.name
75
- end
76
- @jndi_connection = true
77
- end
78
-
79
- def configure_jdbc
80
- driver = @config[:driver].to_s
81
- user = @config[:username].to_s
82
- pass = @config[:password].to_s
83
- url = @config[:url].to_s
84
-
85
- unless driver && url
86
- raise ::ActiveRecord::ConnectionFailed, "jdbc adapter requires driver class and url"
87
- end
88
-
89
- jdbc_driver = JdbcDriver.new(driver)
90
- jdbc_driver.load
91
- @connection_factory = JdbcConnectionFactory.impl do
92
- jdbc_driver.connection(url, user, pass)
93
- end
94
- end
125
+ include ConfigHelper
95
126
  end
96
127
  end
97
128
  end
@@ -20,7 +20,7 @@ def rails_env
20
20
  end
21
21
 
22
22
  namespace :db do
23
- redefine_task :create => :environment do
23
+ redefine_task :create => :rails_env do
24
24
  create_database(ActiveRecord::Base.configurations[rails_env])
25
25
  end
26
26
  task :create => :load_config if Rake.application.lookup(:load_config)
@@ -28,9 +28,7 @@ namespace :db do
28
28
  redefine_task :drop => :environment do
29
29
  config = ActiveRecord::Base.configurations[rails_env]
30
30
  begin
31
- ActiveRecord::Base.establish_connection(config)
32
- db = ActiveRecord::Base.connection.database_name
33
- ActiveRecord::Base.connection.drop_database(db)
31
+ ActiveRecord::Base.connection.drop_database(find_database_name(config))
34
32
  rescue
35
33
  drop_database(config.merge('adapter' => config['adapter'].sub(/^jdbc/, '')))
36
34
  end
@@ -38,7 +36,7 @@ namespace :db do
38
36
  task :drop => :load_config if Rake.application.lookup(:load_config)
39
37
 
40
38
  namespace :create do
41
- task :all => :environment
39
+ task :all => :rails_env
42
40
  end
43
41
 
44
42
  namespace :drop do
@@ -50,6 +48,24 @@ namespace :db do
50
48
  alias_method :previous_drop_database, :drop_database
51
49
  end
52
50
 
51
+ def find_database_name(config)
52
+ if config['adapter'] =~ /postgresql/i
53
+ config = config.dup
54
+ if config['url']
55
+ db = config['url'][/\/([^\/]*)$/, 1]
56
+ config['url'][/\/([^\/]*)$/, 1] = 'postgres' if db
57
+ else
58
+ db = config['database']
59
+ config['database'] = 'postgres'
60
+ end
61
+ ActiveRecord::Base.establish_connection(config)
62
+ else
63
+ ActiveRecord::Base.establish_connection(config)
64
+ db = ActiveRecord::Base.connection.database_name
65
+ end
66
+ db
67
+ end
68
+
53
69
  def create_database(config)
54
70
  begin
55
71
  ActiveRecord::Base.establish_connection(config)
@@ -102,21 +118,7 @@ namespace :db do
102
118
 
103
119
  redefine_task :purge => :environment do
104
120
  abcs = ActiveRecord::Base.configurations
105
- config = abcs['test'].dup
106
- if config['adapter'] =~ /postgresql/i
107
- if config['url']
108
- db = config['url'][/\/([^\/]*)$/, 1]
109
- config['url'][/\/([^\/]*)$/, 1] = 'postgres' if db
110
- else
111
- db = config['database']
112
- config['database'] = 'postgres'
113
- end
114
- ActiveRecord::Base.establish_connection(config)
115
- else
116
- ActiveRecord::Base.establish_connection(config)
117
- db = ActiveRecord::Base.connection.database_name
118
- end
119
- ActiveRecord::Base.connection.recreate_database(db)
121
+ ActiveRecord::Base.connection.recreate_database(find_database_name(abcs['test']))
120
122
  end
121
123
  end
122
124
  end
@@ -20,6 +20,7 @@ module ActiveRecord
20
20
  lambda {|r| r['type_name'] =~ /varying/i}],
21
21
  :text => [ lambda {|r| [Jdbc::Types::LONGVARCHAR, Jdbc::Types::CLOB].include?(r['data_type'].to_i)},
22
22
  lambda {|r| r['type_name'] =~ /^text$/i}, # For Informix
23
+ lambda {|r| r['type_name'] =~ /sub_type 1$/i}, # For FireBird
23
24
  lambda {|r| r['type_name'] =~ /^(text|clob)$/i},
24
25
  lambda {|r| r['type_name'] =~ /^character large object$/i},
25
26
  lambda {|r| r['sql_data_type'] == 2005}],
@@ -83,7 +84,7 @@ module ActiveRecord
83
84
  name = row['type_name'].downcase
84
85
  k = name.to_sym
85
86
  type_map[k] = { :name => name }
86
- type_map[k][:limit] = row['precision'].to_i if row['precision']
87
+ set_limit_to_nonzero_precision(type_map[k], row)
87
88
  end
88
89
 
89
90
  AR_TO_JDBC_TYPES.keys.each do |k|
@@ -91,7 +92,7 @@ module ActiveRecord
91
92
  type_map[k] = { :name => typerow['type_name'].downcase }
92
93
  case k
93
94
  when :integer, :string, :decimal
94
- type_map[k][:limit] = typerow['precision'] && typerow['precision'].to_i
95
+ set_limit_to_nonzero_precision(type_map[k], typerow)
95
96
  when :boolean
96
97
  type_map[k][:limit] = 1
97
98
  end
@@ -114,6 +115,12 @@ module ActiveRecord
114
115
  end
115
116
  raise "unable to choose type for #{ar_type} from:\n#{types.collect{|t| t['type_name']}.inspect}"
116
117
  end
118
+
119
+ def set_limit_to_nonzero_precision(map, row)
120
+ if row['precision'] && row['precision'].to_i > 0
121
+ map[:limit] = row['precision'].to_i
122
+ end
123
+ end
117
124
  end
118
125
  end
119
126
  end