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

Sign up to get free protection for your applications and to get access to all the features.
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