activerecord-jdbc-adapter 1.0.2-java → 1.0.3-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.
@@ -1,3 +1,12 @@
1
+ == 1.0.3 (11/29/10)
2
+
3
+ - ACTIVERECORD_JDBC-143: Implement table_exists? fixing association
4
+ table names with schema prefixes
5
+ - Cleanup of column code for hsqldb (Denis Odorcic)
6
+ - Rails 3.0.3 support - add Arel 2 visitors for all adapters
7
+ - Fix MySQL date types to not have limits (Chris Lowder)
8
+ - ACTIVERECORD_JDBC-141: Better schema support in H2
9
+
1
10
  == 1.0.2
2
11
 
3
12
  - ACTIVERECORD_JDBC-134: Fix conflicting adapter/column superclasses
@@ -25,6 +25,10 @@ lib/arel/engines/sql/compilers/h2_compiler.rb
25
25
  lib/arel/engines/sql/compilers/hsqldb_compiler.rb
26
26
  lib/arel/engines/sql/compilers/jdbc_compiler.rb
27
27
  lib/arel/engines/sql/compilers/mssql_compiler.rb
28
+ lib/arel/visitors/db2.rb
29
+ lib/arel/visitors/derby.rb
30
+ lib/arel/visitors/hsqldb.rb
31
+ lib/arel/visitors/mssql.rb
28
32
  lib/arjdbc/db2.rb
29
33
  lib/arjdbc/derby.rb
30
34
  lib/arjdbc/discover.rb
@@ -71,6 +75,7 @@ lib/arjdbc/jdbc/type_converter.rb
71
75
  lib/arjdbc/mimer/adapter.rb
72
76
  lib/arjdbc/mssql/adapter.rb
73
77
  lib/arjdbc/mssql/connection_methods.rb
78
+ lib/arjdbc/mssql/limit_helpers.rb
74
79
  lib/arjdbc/mssql/tsql_helper.rb
75
80
  lib/arjdbc/mysql/adapter.rb
76
81
  lib/arjdbc/mysql/connection_methods.rb
@@ -150,6 +155,7 @@ test/models/string_id.rb
150
155
  test/models/validates_uniqueness_of_string.rb
151
156
  lib/arjdbc/jdbc/jdbc.rake
152
157
  src/java/arjdbc/derby/DerbyModule.java
158
+ src/java/arjdbc/h2/H2RubyJdbcConnection.java
153
159
  src/java/arjdbc/informix/InformixRubyJdbcConnection.java
154
160
  src/java/arjdbc/jdbc/AdapterJavaService.java
155
161
  src/java/arjdbc/jdbc/JdbcConnectionFactory.java
@@ -0,0 +1,15 @@
1
+ module Arel
2
+ module Visitors
3
+ class DB2 < Arel::Visitors::ToSql
4
+ def visit_Arel_Nodes_SelectStatement o
5
+ add_limit_offset([o.cores.map { |x| visit_Arel_Nodes_SelectCore x }.join,
6
+ ("ORDER BY #{o.orders.map { |x| visit x }.join(', ')}" unless o.orders.empty?),
7
+ ].compact.join(' '), o)
8
+ end
9
+
10
+ def add_limit_offset(sql, o)
11
+ @connection.replace_limit_offset! sql, o.limit, o.offset
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,19 @@
1
+ module Arel
2
+ module Visitors
3
+ class Derby < Arel::Visitors::ToSql
4
+ def visit_Arel_Nodes_SelectStatement o
5
+ [
6
+ o.cores.map { |x| visit_Arel_Nodes_SelectCore x }.join,
7
+ ("ORDER BY #{o.orders.map { |x| visit x }.join(', ')}" unless o.orders.empty?),
8
+ ("FETCH FIRST #{o.limit} ROWS ONLY" if o.limit),
9
+ (visit(o.offset) if o.offset),
10
+ (visit(o.lock) if o.lock),
11
+ ].compact.join ' '
12
+ end
13
+
14
+ def visit_Arel_Nodes_Offset o
15
+ "OFFSET #{visit o.value} ROWS"
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,24 @@
1
+ module Arel
2
+ module Visitors
3
+ class HSQLDB < Arel::Visitors::ToSql
4
+ def visit_Arel_Nodes_SelectStatement o
5
+ [
6
+ limit_offset(o.cores.map { |x| visit_Arel_Nodes_SelectCore x }.join, o),
7
+ ("ORDER BY #{o.orders.map { |x| visit x }.join(', ')}" unless o.orders.empty?),
8
+ ].compact.join ' '
9
+ end
10
+
11
+ def limit_offset sql, o
12
+ offset = o.offset || 0
13
+ bef = sql[7..-1]
14
+ if limit = o.limit
15
+ "SELECT LIMIT #{offset} #{limit} #{bef}"
16
+ elsif offset > 0
17
+ "SELECT LIMIT #{offset} 0 #{bef}"
18
+ else
19
+ sql
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,44 @@
1
+ module Arel
2
+ module Visitors
3
+ class SQLServer < Arel::Visitors::ToSql
4
+ include ArJdbc::MsSQL::LimitHelpers::SqlServerReplaceLimitOffset
5
+
6
+ def select_count? o
7
+ sel = o.cores.length == 1 && o.cores.first
8
+ projections = sel.projections.length == 1 && sel.projections
9
+ Arel::Nodes::Count === projections.first
10
+ end
11
+
12
+ # Need to mimic the subquery logic in ARel 1.x for select count with limit
13
+ # See arel/engines/sql/compilers/mssql_compiler.rb for details
14
+ def visit_Arel_Nodes_SelectStatement o
15
+ order = "ORDER BY #{o.orders.map { |x| visit x }.join(', ')}" unless o.orders.empty?
16
+ if o.limit
17
+ if select_count?(o)
18
+ subquery = true
19
+ sql = o.cores.map do |x|
20
+ x = x.dup
21
+ x.projections = [Arel::Nodes::SqlLiteral.new("*")]
22
+ visit_Arel_Nodes_SelectCore x
23
+ end.join
24
+ else
25
+ sql = o.cores.map { |x| visit_Arel_Nodes_SelectCore x }.join
26
+ end
27
+
28
+ order ||= "ORDER BY #{@connection.determine_order_clause(sql)}"
29
+ replace_limit_offset!(sql, o.limit.to_i, o.offset && o.offset.value.to_i, order)
30
+ sql = "SELECT COUNT(*) AS count_id FROM (#{sql}) AS subquery" if subquery
31
+ elsif order
32
+ sql << " #{order}"
33
+ else
34
+ sql = super
35
+ end
36
+ sql
37
+ end
38
+ end
39
+
40
+ class SQLServer2000 < SQLServer
41
+ include ArJdbc::MsSQL::LimitHelpers::SqlServer2000ReplaceLimitOffset
42
+ end
43
+ end
44
+ end
@@ -143,8 +143,16 @@ module ArJdbc
143
143
  'DB2'
144
144
  end
145
145
 
146
+ def arel2_visitors
147
+ require 'arel/visitors/db2'
148
+ {'db2' => ::Arel::Visitors::DB2, 'as400' => ::Arel::Visitors::DB2}
149
+ end
150
+
146
151
  def add_limit_offset!(sql, options)
147
- limit, offset = options[:limit], options[:offset]
152
+ replace_limit_offset!(sql, options[:limit], options[:offset])
153
+ end
154
+
155
+ def replace_limit_offset!(sql, limit, offset)
148
156
  if limit && !offset
149
157
  if limit == 1
150
158
  sql << " FETCH FIRST ROW ONLY"
@@ -153,9 +161,9 @@ module ArJdbc
153
161
  end
154
162
  elsif limit && offset
155
163
  sql.gsub!(/SELECT/i, 'SELECT B.* FROM (SELECT A.*, row_number() over () AS internal$rownum FROM (SELECT')
156
- sql << ") A ) B WHERE B.internal$rownum > #{offset} AND B.internal$rown
157
- um <= #{sanitize_limit(limit) + offset}"
164
+ sql << ") A ) B WHERE B.internal$rownum > #{offset} AND B.internal$rownum <= #{sanitize_limit(limit) + offset}"
158
165
  end
166
+ sql
159
167
  end
160
168
 
161
169
  def pk_and_sequence_for(table)
@@ -55,6 +55,11 @@ module ::ArJdbc
55
55
  'Derby'
56
56
  end
57
57
 
58
+ def arel2_visitors
59
+ require 'arel/visitors/derby'
60
+ {'derby' => ::Arel::Visitors::Derby, 'jdbcderby' => ::Arel::Visitors::Derby}
61
+ end
62
+
58
63
  include ArJdbc::MissingFunctionalityHelper
59
64
 
60
65
  def index_name_length
@@ -87,7 +92,6 @@ module ::ArJdbc
87
92
  # Override default -- fix case where ActiveRecord passes :default => nil, :null => true
88
93
  def add_column_options!(sql, options)
89
94
  options.delete(:default) if options.has_key?(:default) && options[:default].nil?
90
- options.delete(:null) if options.has_key?(:null) && (options[:null].nil? || options[:null] == true)
91
95
  sql << " DEFAULT #{quote(options.delete(:default))}" if options.has_key?(:default)
92
96
  super
93
97
  end
@@ -158,16 +162,9 @@ module ::ArJdbc
158
162
 
159
163
 
160
164
  def add_column(table_name, column_name, type, options = {})
161
- if option_not_null = (options[:null] == false)
162
- options.delete(:null)
163
- end
164
165
  add_column_sql = "ALTER TABLE #{quote_table_name(table_name)} ADD #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
165
166
  add_column_options!(add_column_sql, options)
166
167
  execute(add_column_sql)
167
- if option_not_null
168
- alter_column_sql = "ALTER TABLE #{quote_table_name(table_name)} ALTER #{quote_column_name(column_name)} NOT NULL"
169
- execute(alter_column_sql)
170
- end
171
168
  end
172
169
 
173
170
  def execute(sql, name = nil)
@@ -6,7 +6,7 @@ module ActiveRecord
6
6
  config[:driver] ||= "org.apache.derby.jdbc.EmbeddedDriver"
7
7
  conn = embedded_driver(config)
8
8
  md = conn.jdbc_connection.meta_data
9
- if md.database_major_version < 10 || md.database_minor_version < 5
9
+ if md.database_major_version < 10 || (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
12
12
  conn
@@ -4,10 +4,18 @@ module ArJdbc
4
4
  module H2
5
5
  include HSQLDB
6
6
 
7
+ def self.jdbc_connection_class
8
+ ::ActiveRecord::ConnectionAdapters::H2JdbcConnection
9
+ end
10
+
7
11
  def adapter_name #:nodoc:
8
12
  'H2'
9
13
  end
10
14
 
15
+ def arel2_visitors
16
+ super.merge 'h2' => ::Arel::Visitors::HSQLDB, 'jdbch2' => ::Arel::Visitors::HSQLDB
17
+ end
18
+
11
19
  def h2_adapter
12
20
  true
13
21
  end
@@ -40,6 +40,11 @@ module ::ArJdbc
40
40
  'Hsqldb'
41
41
  end
42
42
 
43
+ def arel2_visitors
44
+ require 'arel/visitors/hsqldb'
45
+ {'hsqldb' => ::Arel::Visitors::HSQLDB, 'jdbchsqldb' => ::Arel::Visitors::HSQLDB}
46
+ end
47
+
43
48
  def modify_types(tp)
44
49
  tp[:primary_key] = "INTEGER GENERATED BY DEFAULT AS IDENTITY(START WITH 0) PRIMARY KEY"
45
50
  tp[:integer][:limit] = nil
@@ -99,15 +104,9 @@ module ::ArJdbc
99
104
  end
100
105
 
101
106
  def add_column(table_name, column_name, type, options = {})
102
- if option_not_null = options[:null] == false
103
- option_not_null = options.delete(:null)
104
- end
105
107
  add_column_sql = "ALTER TABLE #{quote_table_name(table_name)} ADD #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
106
108
  add_column_options!(add_column_sql, options)
107
109
  execute(add_column_sql)
108
- if option_not_null
109
- alter_column_sql = "ALTER TABLE #{quote_table_name(table_name)} ALTER #{quote_column_name(column_name)} NOT NULL"
110
- end
111
110
  end
112
111
 
113
112
  def change_column(table_name, column_name, type, options = {}) #:nodoc:
@@ -32,7 +32,7 @@ module ActiveRecord
32
32
  end
33
33
  super(connection, logger)
34
34
  extend spec if spec
35
- configure_arel2_visitors
35
+ configure_arel2_visitors(config)
36
36
  connection.adapter = self
37
37
  JndiConnectionPoolCallbacks.prepare(self, connection)
38
38
  end
@@ -93,12 +93,17 @@ module ActiveRecord
93
93
  {}
94
94
  end
95
95
 
96
- def configure_arel2_visitors
96
+ def configure_arel2_visitors(config)
97
97
  if defined?(::Arel::Visitors::VISITORS)
98
98
  visitors = ::Arel::Visitors::VISITORS
99
+ visitor = nil
99
100
  arel2_visitors.each do |k,v|
101
+ visitor = v
100
102
  visitors[k] = v unless visitors.has_key?(k)
101
103
  end
104
+ if visitor && config[:dialect] && config[:adapter] =~ /^(jdbc|jndi)$/
105
+ visitors[config[:adapter]] = visitor
106
+ end
102
107
  end
103
108
  end
104
109
 
@@ -220,7 +225,6 @@ module ActiveRecord
220
225
  id_value || id
221
226
  end
222
227
 
223
-
224
228
  def jdbc_columns(table_name, name = nil)
225
229
  @connection.columns(table_name.to_s)
226
230
  end
@@ -230,6 +234,10 @@ module ActiveRecord
230
234
  @connection.tables
231
235
  end
232
236
 
237
+ def table_exists?(name)
238
+ jdbc_columns(name) rescue nil
239
+ end
240
+
233
241
  def indexes(table_name, name = nil, schema_name = nil)
234
242
  @connection.indexes(table_name, name, schema_name)
235
243
  end
@@ -266,6 +274,12 @@ module ActiveRecord
266
274
  def select(*args)
267
275
  execute(*args)
268
276
  end
277
+
278
+ def translate_exception(e, message)
279
+ puts e.backtrace if $DEBUG || ENV['DEBUG']
280
+ super
281
+ end
282
+ protected :translate_exception
269
283
  end
270
284
  end
271
285
  end
@@ -1,8 +1,10 @@
1
1
  require 'arjdbc/mssql/tsql_helper'
2
+ require 'arjdbc/mssql/limit_helpers'
2
3
 
3
4
  module ::ArJdbc
4
5
  module MsSQL
5
6
  include TSqlMethods
7
+ include LimitHelpers
6
8
 
7
9
  def self.extended(mod)
8
10
  unless @lob_callback_added
@@ -32,15 +34,21 @@ module ::ArJdbc
32
34
  ::ActiveRecord::ConnectionAdapters::MssqlJdbcConnection
33
35
  end
34
36
 
37
+ def arel2_visitors
38
+ require 'arel/visitors/mssql'
39
+ visitor_class = sqlserver_version == "2000" ? ::Arel::Visitors::SQLServer2000 : ::Arel::Visitors::SQLServer
40
+ { 'mssql' => visitor_class, 'jdbcmssql' => visitor_class}
41
+ end
42
+
35
43
  def sqlserver_version
36
44
  @sqlserver_version ||= select_value("select @@version")[/Microsoft SQL Server\s+(\d{4})/, 1]
37
45
  end
38
46
 
39
47
  def add_version_specific_add_limit_offset
40
48
  if sqlserver_version == "2000"
41
- extend SqlServer2000LimitOffset
49
+ extend LimitHelpers::SqlServer2000AddLimitOffset
42
50
  else
43
- extend SqlServerLimitOffset
51
+ extend LimitHelpers::SqlServerAddLimitOffset
44
52
  end
45
53
  end
46
54
 
@@ -228,64 +236,6 @@ module ::ArJdbc
228
236
  'MsSQL'
229
237
  end
230
238
 
231
- module SqlServer2000LimitOffset
232
- def add_limit_offset!(sql, options)
233
- limit = options[:limit]
234
- if limit
235
- offset = (options[:offset] || 0).to_i
236
- start_row = offset + 1
237
- end_row = offset + limit.to_i
238
- order = (options[:order] || determine_order_clause(sql))
239
- sql.sub!(/ ORDER BY.*$/i, '')
240
- find_select = /\b(SELECT(?:\s+DISTINCT)?)\b(.*)/im
241
- whole, select, rest_of_query = find_select.match(sql).to_a
242
- if (start_row == 1) && (end_row ==1)
243
- new_sql = "#{select} TOP 1 #{rest_of_query}"
244
- sql.replace(new_sql)
245
- else
246
- #UGLY
247
- #KLUDGY?
248
- #removing out stuff before the FROM...
249
- rest = rest_of_query[/FROM/i=~ rest_of_query.. -1]
250
- #need the table name for avoiding amiguity
251
- table_name = get_table_name(sql)
252
- #I am not sure this will cover all bases. but all the tests pass
253
- new_order = "#{order}, #{table_name}.id" if order.index("#{table_name}.id").nil?
254
- new_order ||= order
255
-
256
- if (rest_of_query.match(/WHERE/).nil?)
257
- new_sql = "#{select} TOP #{limit} #{rest_of_query} WHERE #{table_name}.id NOT IN (#{select} TOP #{offset} #{table_name}.id #{rest} ORDER BY #{new_order}) ORDER BY #{order} "
258
- else
259
- new_sql = "#{select} TOP #{limit} #{rest_of_query} AND #{table_name}.id NOT IN (#{select} TOP #{offset} #{table_name}.id #{rest} ORDER BY #{new_order}) ORDER BY #{order} "
260
- end
261
-
262
- sql.replace(new_sql)
263
- end
264
- end
265
- end
266
- end
267
-
268
- module SqlServerLimitOffset
269
- def add_limit_offset!(sql, options)
270
- limit = options[:limit]
271
- if limit
272
- offset = (options[:offset] || 0).to_i
273
- start_row = offset + 1
274
- end_row = offset + limit.to_i
275
- order = (options[:order] || determine_order_clause(sql))
276
- sql.sub!(/ ORDER BY.*$/i, '')
277
- find_select = /\b(SELECT(?:\s+DISTINCT)?)\b(.*)/im
278
- whole, select, rest_of_query = find_select.match(sql).to_a
279
- if rest_of_query.strip!.first == '*'
280
- from_table = /.*FROM\s*\b(\w*)\b/i.match(rest_of_query).to_a[1]
281
- end
282
- new_sql = "#{select} t.* FROM (SELECT ROW_NUMBER() OVER(ORDER BY #{order}) AS _row_num, #{from_table + '.' if from_table}#{rest_of_query}"
283
- new_sql << ") AS t WHERE t._row_num BETWEEN #{start_row.to_s} AND #{end_row.to_s}"
284
- sql.replace(new_sql)
285
- end
286
- end
287
- end
288
-
289
239
  def change_order_direction(order)
290
240
  order.split(",").collect do |fragment|
291
241
  case fragment
@@ -442,8 +392,6 @@ module ::ArJdbc
442
392
  sql
443
393
  end
444
394
 
445
- private
446
-
447
395
  # Turns IDENTITY_INSERT ON for table during execution of the block
448
396
  # N.B. This sets the state of IDENTITY_INSERT to OFF after the
449
397
  # block has been executed without regard to its previous state
@@ -460,16 +408,6 @@ module ::ArJdbc
460
408
  raise ActiveRecord::ActiveRecordError, "IDENTITY_INSERT could not be turned #{enable ? 'ON' : 'OFF'} for table #{table_name}"
461
409
  end
462
410
 
463
- def get_table_name(sql)
464
- if sql =~ /^\s*insert\s+into\s+([^\(\s,]+)\s*|^\s*update\s+([^\(\s,]+)\s*/i
465
- $1
466
- elsif sql =~ /from\s+([^\(\s,]+)\s*/i
467
- $1
468
- else
469
- nil
470
- end
471
- end
472
-
473
411
  def identity_column(table_name)
474
412
  columns(table_name).each do |col|
475
413
  return col.name if col.identity
@@ -527,7 +465,7 @@ module ::ArJdbc
527
465
  end
528
466
 
529
467
  def clear_cached_table(name)
530
- (@table_columns ||= {}).delete(name)
468
+ (@table_columns ||= {}).delete(name.to_s)
531
469
  end
532
470
  end
533
471
  end
@@ -0,0 +1,92 @@
1
+ module ::ArJdbc
2
+ module MsSQL
3
+ module LimitHelpers
4
+ module_function
5
+ def get_table_name(sql)
6
+ if sql =~ /^\s*insert\s+into\s+([^\(\s,]+)\s*|^\s*update\s+([^\(\s,]+)\s*/i
7
+ $1
8
+ elsif sql =~ /from\s+([^\(\s,]+)\s*/i
9
+ $1
10
+ else
11
+ nil
12
+ end
13
+ end
14
+
15
+ module SqlServer2000ReplaceLimitOffset
16
+ module_function
17
+ def replace_limit_offset!(sql, limit, offset, order)
18
+ if limit
19
+ offset ||= 0
20
+ start_row = offset + 1
21
+ end_row = offset + limit.to_i
22
+ find_select = /\b(SELECT(?:\s+DISTINCT)?)\b(.*)/im
23
+ whole, select, rest_of_query = find_select.match(sql).to_a
24
+ if (start_row == 1) && (end_row ==1)
25
+ new_sql = "#{select} TOP 1 #{rest_of_query}"
26
+ sql.replace(new_sql)
27
+ else
28
+ #UGLY
29
+ #KLUDGY?
30
+ #removing out stuff before the FROM...
31
+ rest = rest_of_query[/FROM/i=~ rest_of_query.. -1]
32
+ #need the table name for avoiding amiguity
33
+ table_name = LimitHelpers.get_table_name(sql)
34
+ #I am not sure this will cover all bases. but all the tests pass
35
+ new_order = "ORDER BY #{order}, #{table_name}.id" if order.index("#{table_name}.id").nil?
36
+ new_order ||= order
37
+
38
+ if (rest_of_query.match(/WHERE/).nil?)
39
+ new_sql = "#{select} TOP #{limit} #{rest_of_query} WHERE #{table_name}.id NOT IN (#{select} TOP #{offset} #{table_name}.id #{rest} #{new_order}) #{order} "
40
+ else
41
+ new_sql = "#{select} TOP #{limit} #{rest_of_query} AND #{table_name}.id NOT IN (#{select} TOP #{offset} #{table_name}.id #{rest} #{new_order}) #{order} "
42
+ end
43
+
44
+ sql.replace(new_sql)
45
+ end
46
+ end
47
+ sql
48
+ end
49
+ end
50
+
51
+ module SqlServer2000AddLimitOffset
52
+ def add_limit_offset!(sql, options)
53
+ if options[:limit]
54
+ order = "ORDER BY #{options[:order] || determine_order_clause(sql)}"
55
+ sql.sub!(/ ORDER BY.*$/i, '')
56
+ SqlServerReplaceLimitOffset.replace_limit_offset!(sql, options[:limit], options[:offset], order)
57
+ end
58
+ end
59
+ end
60
+
61
+ module SqlServerReplaceLimitOffset
62
+ module_function
63
+ def replace_limit_offset!(sql, limit, offset, order)
64
+ if limit
65
+ offset ||= 0
66
+ start_row = offset + 1
67
+ end_row = offset + limit.to_i
68
+ find_select = /\b(SELECT(?:\s+DISTINCT)?)\b(.*)/im
69
+ whole, select, rest_of_query = find_select.match(sql).to_a
70
+ if rest_of_query.strip!.first == '*'
71
+ from_table = /.*FROM\s*\b(\w*)\b/i.match(rest_of_query).to_a[1]
72
+ end
73
+ new_sql = "#{select} t.* FROM (SELECT ROW_NUMBER() OVER(#{order}) AS _row_num, #{from_table + '.' if from_table}#{rest_of_query}"
74
+ new_sql << ") AS t WHERE t._row_num BETWEEN #{start_row.to_s} AND #{end_row.to_s}"
75
+ sql.replace(new_sql)
76
+ end
77
+ sql
78
+ end
79
+ end
80
+
81
+ module SqlServerAddLimitOffset
82
+ def add_limit_offset!(sql, options)
83
+ if options[:limit]
84
+ order = "ORDER BY #{options[:order] || determine_order_clause(sql)}"
85
+ sql.sub!(/ ORDER BY.*$/i, '')
86
+ SqlServerReplaceLimitOffset.replace_limit_offset!(sql, options[:limit], options[:offset], order)
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
@@ -58,7 +58,7 @@ module ::ArJdbc
58
58
  when /^mediumint/i; 3
59
59
  when /^smallint/i; 2
60
60
  when /^tinyint/i; 1
61
- when /^(datetime|timestamp)/
61
+ when /^(datetime|date|timestamp)/
62
62
  nil
63
63
  else
64
64
  super
@@ -109,6 +109,15 @@ module ::ArJdbc
109
109
  'Oracle'
110
110
  end
111
111
 
112
+ def arel2_visitors
113
+ { 'oracle' => Arel::Visitors::Oracle }
114
+ end
115
+
116
+ # TODO: use this instead of the QuotedPrimaryKey logic and execute_id_insert?
117
+ # def prefetch_primary_key?(table_name = nil)
118
+ # columns(table_name).detect {|c| c.primary } if table_name
119
+ # end
120
+
112
121
  def table_alias_length
113
122
  30
114
123
  end
@@ -144,8 +153,17 @@ module ::ArJdbc
144
153
  recreate_database(name)
145
154
  end
146
155
 
156
+ def next_sequence_value(sequence_name)
157
+ # avoid #select or #select_one so that the sequence values aren't cached
158
+ execute("select #{sequence_name}.nextval id from dual").first['id'].to_i
159
+ end
160
+
161
+ def sql_literal?(value)
162
+ defined?(::Arel::SqlLiteral) && ::Arel::SqlLiteral === value
163
+ end
164
+
147
165
  def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
148
- if (id_value && !id_value.respond_to?(:to_sql)) || pk.nil?
166
+ if (id_value && !sql_literal?(id_value)) || pk.nil?
149
167
  # Pre-assigned id or table without a primary key
150
168
  # Presence of #to_sql means an Arel literal bind variable
151
169
  # that should use #execute_id_insert below
@@ -155,7 +173,7 @@ module ::ArJdbc
155
173
  # Extract the table from the insert sql. Yuck.
156
174
  table = sql.split(" ", 4)[2].gsub('"', '')
157
175
  sequence_name ||= default_sequence_name(table)
158
- id_value = select_one("select #{sequence_name}.nextval id from dual")['id'].to_i
176
+ id_value = next_sequence_value(sequence_name)
159
177
  log(sql, name) do
160
178
  @connection.execute_id_insert(sql,id_value)
161
179
  end
@@ -344,6 +362,9 @@ module ::ArJdbc
344
362
  end
345
363
 
346
364
  def quote(value, column = nil) #:nodoc:
365
+ # Arel 2 passes SqlLiterals through
366
+ return value if sql_literal?(value)
367
+
347
368
  if column && [:text, :binary].include?(column.type)
348
369
  if /(.*?)\([0-9]+\)/ =~ column.sql_type
349
370
  %Q{empty_#{ $1.downcase }()}
@@ -90,6 +90,10 @@ module ::ArJdbc
90
90
  'PostgreSQL'
91
91
  end
92
92
 
93
+ def arel2_visitors
94
+ {'jdbcpostgresql' => ::Arel::Visitors::PostgreSQL}
95
+ end
96
+
93
97
  def postgresql_version
94
98
  @postgresql_version ||=
95
99
  begin
@@ -1,6 +1,6 @@
1
1
  module ArJdbc
2
2
  module Version
3
- VERSION = "1.0.2"
3
+ VERSION = "1.0.3"
4
4
  end
5
5
  end
6
6
  # Compatibility with older versions of ar-jdbc for other extensions out there
@@ -0,0 +1,70 @@
1
+ /*
2
+ **** BEGIN LICENSE BLOCK *****
3
+ * Copyright (c) 2006-2010 Nick Sieger <nick@nicksieger.com>
4
+ * Copyright (c) 2006-2007 Ola Bini <ola.bini@gmail.com>
5
+ * Copyright (c) 2008-2009 Thomas E Enebo <enebo@acm.org>
6
+ *
7
+ * Permission is hereby granted, free of charge, to any person obtaining
8
+ * a copy of this software and associated documentation files (the
9
+ * "Software"), to deal in the Software without restriction, including
10
+ * without limitation the rights to use, copy, modify, merge, publish,
11
+ * distribute, sublicense, and/or sell copies of the Software, and to
12
+ * permit persons to whom the Software is furnished to do so, subject to
13
+ * the following conditions:
14
+ *
15
+ * The above copyright notice and this permission notice shall be
16
+ * included in all copies or substantial portions of the Software.
17
+ *
18
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25
+ ***** END LICENSE BLOCK *****/
26
+
27
+ package arjdbc.h2;
28
+
29
+ import java.sql.ResultSet;
30
+ import java.sql.SQLException;
31
+ import java.sql.Types;
32
+
33
+ import arjdbc.jdbc.RubyJdbcConnection;
34
+
35
+ import org.jruby.Ruby;
36
+ import org.jruby.RubyClass;
37
+ import org.jruby.runtime.ObjectAllocator;
38
+ import org.jruby.runtime.builtin.IRubyObject;
39
+
40
+ /**
41
+ *
42
+ * @author nicksieger
43
+ */
44
+ public class H2RubyJdbcConnection extends RubyJdbcConnection {
45
+ protected H2RubyJdbcConnection(Ruby runtime, RubyClass metaClass) {
46
+ super(runtime, metaClass);
47
+ }
48
+
49
+ public static RubyClass createH2JdbcConnectionClass(Ruby runtime, RubyClass jdbcConnection) {
50
+ RubyClass clazz = RubyJdbcConnection.getConnectionAdapters(runtime).defineClassUnder("H2JdbcConnection",
51
+ jdbcConnection, H2_JDBCCONNECTION_ALLOCATOR);
52
+ clazz.defineAnnotatedMethods(H2RubyJdbcConnection.class);
53
+
54
+ return clazz;
55
+ }
56
+
57
+ private static ObjectAllocator H2_JDBCCONNECTION_ALLOCATOR = new ObjectAllocator() {
58
+ public IRubyObject allocate(Ruby runtime, RubyClass klass) {
59
+ return new H2RubyJdbcConnection(runtime, klass);
60
+ }
61
+ };
62
+
63
+ /**
64
+ * H2 supports schemas.
65
+ */
66
+ @Override
67
+ protected boolean databaseSupportsSchemas() {
68
+ return true;
69
+ }
70
+ }
@@ -29,6 +29,7 @@ package arjdbc.jdbc;
29
29
  import java.io.IOException;
30
30
 
31
31
  import arjdbc.derby.DerbyModule;
32
+ import arjdbc.h2.H2RubyJdbcConnection;
32
33
  import arjdbc.informix.InformixRubyJdbcConnection;
33
34
  import arjdbc.mssql.MssqlRubyJdbcConnection;
34
35
  import arjdbc.mysql.MySQLModule;
@@ -53,6 +54,7 @@ public class AdapterJavaService implements BasicLibraryService {
53
54
  InformixRubyJdbcConnection.createInformixJdbcConnectionClass(runtime, jdbcConnection);
54
55
  OracleRubyJdbcConnection.createOracleJdbcConnectionClass(runtime, jdbcConnection);
55
56
  Sqlite3RubyJdbcConnection.createSqlite3JdbcConnectionClass(runtime, jdbcConnection);
57
+ H2RubyJdbcConnection.createH2JdbcConnectionClass(runtime, jdbcConnection);
56
58
  RubyModule arJdbc = runtime.getOrCreateModule("ArJdbc");
57
59
 
58
60
  rubyApi = JavaEmbedUtils.newObjectAdapter();
@@ -4,3 +4,38 @@ require 'db/h2'
4
4
  class H2SimpleTest < Test::Unit::TestCase
5
5
  include SimpleTestMethods
6
6
  end
7
+
8
+ class H2SchemaTest < Test::Unit::TestCase
9
+ def setup
10
+ @connection = ActiveRecord::Base.connection
11
+ @connection.execute("create schema s1");
12
+ @connection.execute("set schema s1");
13
+ CreateEntries.up
14
+ @connection.execute("create schema s2");
15
+ @connection.execute("set schema s2");
16
+ CreateUsers.up
17
+ @connection.execute("set schema public");
18
+ Entry.set_table_name 's1.entries'
19
+ User.set_table_name 's2.users'
20
+ user = User.create! :login => "something"
21
+ Entry.create! :title => "title", :content => "content", :rating => 123.45, :user => user
22
+ end
23
+
24
+ def teardown
25
+ @connection.execute("set schema s1");
26
+ CreateEntries.down
27
+ @connection.execute("set schema s2");
28
+ CreateUsers.down
29
+ @connection.execute("drop schema s1");
30
+ @connection.execute("drop schema s2");
31
+ @connection.execute("set schema public");
32
+ Entry.reset_table_name
33
+ Entry.reset_column_information
34
+ User.reset_table_name
35
+ User.reset_column_information
36
+ end
37
+
38
+ def test_find_in_other_schema
39
+ assert !Entry.all(:include => :user).empty?
40
+ end
41
+ end
@@ -20,6 +20,6 @@ require 'helper'
20
20
  require 'test/unit'
21
21
 
22
22
  # Comment/uncomment to enable logging to be loaded for any of the database adapters
23
- # require 'db/logger'
23
+ require 'db/logger' if $DEBUG || ENV['DEBUG']
24
24
 
25
25
 
@@ -8,9 +8,9 @@ class MysqlDbCreateTest < Test::Unit::TestCase
8
8
  MYSQL_CONFIG
9
9
  end
10
10
 
11
- if find_executable?("mysql")
12
- def test_rake_db_create
13
- Rake::Task["db:create"].invoke
11
+ def test_rake_db_create
12
+ Rake::Task["db:create"].invoke
13
+ if find_executable?("mysql")
14
14
  output = nil
15
15
  IO.popen("mysql -u #{MYSQL_CONFIG[:username]} --password=#{MYSQL_CONFIG[:password]}", "r+") do |mysql|
16
16
  mysql << "show databases where `Database` = '#{@db_name}';"
@@ -18,13 +18,10 @@ class MysqlDbCreateTest < Test::Unit::TestCase
18
18
  assert mysql.read =~ /#{@db_name}/m
19
19
  end
20
20
  end
21
+ end
21
22
 
22
- def test_rake_db_test_purge
23
- Rake::Task["db:create"].invoke
24
- Rake::Task["db:test:purge"].invoke
25
- end
26
- else
27
- def test_skipped
28
- end
23
+ def test_rake_db_test_purge
24
+ Rake::Task["db:create"].invoke
25
+ Rake::Task["db:test:purge"].invoke
29
26
  end
30
27
  end
@@ -12,6 +12,7 @@ class DBSetup < ActiveRecord::Migration
12
12
 
13
13
  create_table :cars, :primary_key => 'legacy_id' do |t|
14
14
  t.string :name
15
+ t.date :production_started_on
15
16
  end
16
17
 
17
18
  create_table :cats, :id => false do |t|
@@ -80,6 +81,13 @@ class MysqlInfoTest < Test::Unit::TestCase
80
81
  dump = strio.string
81
82
  dump.grep(/datetime/).each {|line| assert line !~ /limit/ }
82
83
  end
84
+
85
+ def test_schema_dump_should_not_have_limits_on_date
86
+ strio = StringIO.new
87
+ ActiveRecord::SchemaDumper::dump(@connection, strio)
88
+ dump = strio.string
89
+ dump.grep(/date/).each {|line| assert line !~ /limit/ }
90
+ end
83
91
 
84
92
  def test_should_include_limit
85
93
  text_column = @connection.columns('memos').find { |c| c.name == 'text' }
@@ -29,6 +29,19 @@ class MysqlSimpleTest < Test::Unit::TestCase
29
29
  def test_update_all_with_limit
30
30
  assert_nothing_raised { Entry.update_all({:title => "test"}, {}, {:limit => 1}) }
31
31
  end
32
+
33
+ def test_find_in_other_schema_with_include
34
+ old_entries_table_name = Entry.table_name
35
+ old_users_table_name = User.table_name
36
+ begin
37
+ User.set_table_name 'weblog_development.users'
38
+ Entry.set_table_name 'weblog_development.entries'
39
+ assert !Entry.all(:include => :user).empty?
40
+ ensure
41
+ Entry.set_table_name old_entries_table_name
42
+ User.set_table_name old_users_table_name
43
+ end
44
+ end
32
45
  end
33
46
 
34
47
  class MysqlHasManyThroughTest < Test::Unit::TestCase
@@ -7,4 +7,12 @@ class OracleSimpleTest < Test::Unit::TestCase
7
7
  def test_default_id_type_is_integer
8
8
  assert Integer === Entry.first.id
9
9
  end
10
+
11
+ def test_sequences_are_not_cached
12
+ ActiveRecord::Base.transaction do
13
+ e1 = Entry.create :title => "hello1"
14
+ e2 = Entry.create :title => "hello2"
15
+ assert e1.id != e2.id
16
+ end
17
+ end
10
18
  end
@@ -348,6 +348,36 @@ module SimpleTestMethods
348
348
  AddNotNullColumnToTable.down
349
349
  end
350
350
 
351
+ def test_add_null_column_with_default
352
+ Entry.connection.add_column :entries, :color, :string, :null => false, :default => "blue"
353
+ created_columns = Entry.connection.columns('entries')
354
+
355
+ color = created_columns.detect { |c| c.name == 'color' }
356
+ assert !color.null
357
+ end
358
+
359
+ def test_add_null_column_with_no_default
360
+ # You must specify a default value with most databases
361
+ if ActiveRecord::Base.connection.adapter_name =~ /mysql/i
362
+ Entry.connection.add_column :entries, :color, :string, :null => false
363
+ created_columns = Entry.connection.columns('entries')
364
+
365
+ color = created_columns.detect { |c| c.name == 'color' }
366
+ assert !color.null
367
+ end
368
+ end
369
+
370
+ def test_add_null_column_with_nil_default
371
+ # You must specify a default value with most databases
372
+ if ActiveRecord::Base.connection.adapter_name =~ /mysql/i
373
+ Entry.connection.add_column :entries, :color, :string, :null => false, :default => nil
374
+ created_columns = Entry.connection.columns('entries')
375
+
376
+ color = created_columns.detect { |c| c.name == 'color' }
377
+ assert !color.null
378
+ end
379
+ end
380
+
351
381
  def test_validates_uniqueness_of_strings_case_sensitive
352
382
  name_lower = ValidatesUniquenessOfString.new(:cs_string => "name", :ci_string => '1')
353
383
  name_lower.save!
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 1
7
7
  - 0
8
- - 2
9
- version: 1.0.2
8
+ - 3
9
+ version: 1.0.3
10
10
  platform: java
11
11
  authors:
12
12
  - Nick Sieger, Ola Bini and JRuby contributors
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-10-20 00:00:00 -05:00
17
+ date: 2010-11-29 00:00:00 -06:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -89,6 +89,10 @@ files:
89
89
  - lib/arel/engines/sql/compilers/hsqldb_compiler.rb
90
90
  - lib/arel/engines/sql/compilers/jdbc_compiler.rb
91
91
  - lib/arel/engines/sql/compilers/mssql_compiler.rb
92
+ - lib/arel/visitors/db2.rb
93
+ - lib/arel/visitors/derby.rb
94
+ - lib/arel/visitors/hsqldb.rb
95
+ - lib/arel/visitors/mssql.rb
92
96
  - lib/arjdbc/db2.rb
93
97
  - lib/arjdbc/derby.rb
94
98
  - lib/arjdbc/discover.rb
@@ -135,6 +139,7 @@ files:
135
139
  - lib/arjdbc/mimer/adapter.rb
136
140
  - lib/arjdbc/mssql/adapter.rb
137
141
  - lib/arjdbc/mssql/connection_methods.rb
142
+ - lib/arjdbc/mssql/limit_helpers.rb
138
143
  - lib/arjdbc/mssql/tsql_helper.rb
139
144
  - lib/arjdbc/mysql/adapter.rb
140
145
  - lib/arjdbc/mysql/connection_methods.rb
@@ -214,6 +219,7 @@ files:
214
219
  - test/models/validates_uniqueness_of_string.rb
215
220
  - lib/arjdbc/jdbc/jdbc.rake
216
221
  - src/java/arjdbc/derby/DerbyModule.java
222
+ - src/java/arjdbc/h2/H2RubyJdbcConnection.java
217
223
  - src/java/arjdbc/informix/InformixRubyJdbcConnection.java
218
224
  - src/java/arjdbc/jdbc/AdapterJavaService.java
219
225
  - src/java/arjdbc/jdbc/JdbcConnectionFactory.java