activerecord-jdbc-adapter 1.0.2-java → 1.0.3-java

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