activerecord-jdbc-adapter 1.2.8 → 1.2.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. data/.travis.yml +1 -1
  2. data/Gemfile +2 -1
  3. data/Gemfile.lock +5 -6
  4. data/History.txt +11 -0
  5. data/gemfiles/rails23.gemfile +2 -1
  6. data/gemfiles/rails23.gemfile.lock +5 -2
  7. data/gemfiles/rails30.gemfile +2 -1
  8. data/gemfiles/rails30.gemfile.lock +5 -2
  9. data/gemfiles/rails31.gemfile +2 -1
  10. data/gemfiles/rails31.gemfile.lock +5 -2
  11. data/gemfiles/rails32.gemfile +2 -1
  12. data/gemfiles/rails32.gemfile.lock +5 -2
  13. data/lib/arel/engines/sql/compilers/mssql_compiler.rb +1 -1
  14. data/lib/arel/visitors/sql_server.rb +4 -4
  15. data/lib/arjdbc/db2/adapter.rb +14 -3
  16. data/lib/arjdbc/discover.rb +1 -1
  17. data/lib/arjdbc/jdbc/adapter.rb +1 -0
  18. data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
  19. data/lib/arjdbc/jdbc/connection.rb +73 -63
  20. data/lib/arjdbc/jdbc/extension.rb +1 -1
  21. data/lib/arjdbc/mssql.rb +3 -0
  22. data/lib/arjdbc/mssql/adapter.rb +132 -115
  23. data/lib/arjdbc/mssql/connection_methods.rb +1 -1
  24. data/lib/arjdbc/mssql/limit_helpers.rb +62 -66
  25. data/lib/arjdbc/mssql/lock_helpers.rb +2 -2
  26. data/lib/arjdbc/mssql/tsql_methods.rb +58 -0
  27. data/lib/arjdbc/mssql/utils.rb +53 -0
  28. data/lib/arjdbc/oracle/adapter.rb +61 -39
  29. data/lib/arjdbc/sqlite3/adapter.rb +3 -6
  30. data/lib/arjdbc/version.rb +1 -1
  31. data/src/java/arjdbc/jdbc/AdapterJavaService.java +4 -2
  32. data/src/java/arjdbc/mssql/MSSQLModule.java +70 -0
  33. data/src/java/arjdbc/mssql/MSSQLRubyJdbcConnection.java +128 -0
  34. data/src/java/arjdbc/mssql/MssqlRubyJdbcConnection.java +25 -112
  35. data/test/db/mssql.rb +8 -8
  36. data/test/db2_simple_test.rb +7 -0
  37. data/test/models/entry.rb +2 -1
  38. data/test/mssql_binary_test.rb +6 -0
  39. data/test/mssql_db_create_test.rb +5 -2
  40. data/test/mssql_identity_insert_test.rb +1 -2
  41. data/test/mssql_ignore_system_views_test.rb +5 -5
  42. data/test/mssql_limit_offset_test.rb +51 -55
  43. data/test/mssql_multibyte_test.rb +1 -2
  44. data/test/mssql_row_locking_test.rb +1 -1
  45. data/test/mssql_simple_test.rb +6 -10
  46. data/test/{mssql_row_locking_sql_test.rb → mssql_test.rb} +110 -18
  47. data/test/mysql_db_create_test.rb +13 -7
  48. data/test/oracle_simple_test.rb +18 -0
  49. data/test/postgres_db_create_test.rb +26 -13
  50. data/test/simple.rb +45 -15
  51. data/test/sqlite3_schema_dump_test.rb +6 -0
  52. data/test/sqlite3_type_conversion_test.rb +20 -17
  53. data/test/test_helper.rb +44 -2
  54. metadata +9 -4
  55. data/lib/arjdbc/mssql/tsql_helper.rb +0 -53
@@ -1,5 +1,5 @@
1
- module ::ArJdbc
2
- module MsSQL
1
+ module ArJdbc
2
+ module MSSQL
3
3
  module LockHelpers
4
4
  module SqlServerAddLock
5
5
  # Microsoft SQL Server uses its own syntax for SELECT .. FOR UPDATE:
@@ -0,0 +1,58 @@
1
+ # Common methods for handling TSQL databases.
2
+ module ArJdbc
3
+ module MSSQL
4
+ module TSqlMethods
5
+
6
+ def type_to_sql(type, limit = nil, precision = nil, scale = nil) # :nodoc:
7
+ limit = nil if %w(text binary).include? type.to_s
8
+ return 'uniqueidentifier' if (type.to_s == 'uniqueidentifier')
9
+ return super unless type.to_s == 'integer'
10
+
11
+ if limit.nil? || limit == 4
12
+ 'int'
13
+ elsif limit == 2
14
+ 'smallint'
15
+ elsif limit == 1
16
+ 'tinyint'
17
+ else
18
+ 'bigint'
19
+ end
20
+ end
21
+
22
+ def add_limit_offset!(sql, options)
23
+ if options[:limit] and options[:offset]
24
+ total_rows = select_all("SELECT count(*) as TotalRows from (#{sql.gsub(/\bSELECT(\s+DISTINCT)?\b/i, "SELECT\\1 TOP 1000000000")}) tally")[0]["TotalRows"].to_i
25
+ if (options[:limit] + options[:offset]) >= total_rows
26
+ options[:limit] = (total_rows - options[:offset] >= 0) ? (total_rows - options[:offset]) : 0
27
+ end
28
+ sql.sub!(/^\s*SELECT(\s+DISTINCT)?/i, "SELECT * FROM (SELECT TOP #{options[:limit]} * FROM (SELECT\\1 TOP #{options[:limit] + options[:offset]} ")
29
+ sql << ") AS tmp1"
30
+ if options[:order]
31
+ options[:order] = options[:order].split(',').map do |field|
32
+ parts = field.split(" ")
33
+ tc = parts[0]
34
+ if sql =~ /\.\[/ and tc =~ /\./ # if column quoting used in query
35
+ tc.gsub!(/\./, '\\.\\[')
36
+ tc << '\\]'
37
+ end
38
+ if sql =~ /#{tc} AS (t\d_r\d\d?)/
39
+ parts[0] = $1
40
+ elsif parts[0] =~ /\w+\.(\w+)/
41
+ parts[0] = $1
42
+ end
43
+ parts.join(' ')
44
+ end.join(', ')
45
+ sql << " ORDER BY #{change_order_direction(options[:order])}) AS tmp2 ORDER BY #{options[:order]}"
46
+ else
47
+ sql << " ) AS tmp2"
48
+ end
49
+ elsif sql !~ /^\s*SELECT (@@|COUNT\()/i
50
+ sql.sub!(/^\s*SELECT(\s+DISTINCT)?/i) do
51
+ "SELECT#{$1} TOP #{options[:limit]}"
52
+ end unless options[:limit].nil?
53
+ end
54
+ end
55
+
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,53 @@
1
+ # NOTE: lot of code kindly borrowed from __activerecord-sqlserver-adapter__
2
+ module ArJdbc
3
+ module MSSQL
4
+ module Utils
5
+
6
+ module_function
7
+
8
+ GET_TABLE_NAME_INSERT_UPDATE_RE =
9
+ /^\s*(INSERT|EXEC sp_executesql N'INSERT)\s+INTO\s+([^\(\s,]+)\s*|^\s*update\s+([^\(\s,]+)\s*/i
10
+
11
+ GET_TABLE_NAME_FROM_RE = /\bFROM\s+([^\(\)\s,]+)\s*/i
12
+
13
+ def get_table_name(sql, qualified = nil)
14
+ if sql =~ GET_TABLE_NAME_INSERT_UPDATE_RE
15
+ tn = $2 || $3
16
+ qualified ? tn : unqualify_table_name(tn)
17
+ elsif sql =~ GET_TABLE_NAME_FROM_RE
18
+ qualified ? $1 : unqualify_table_name($1)
19
+ else
20
+ nil
21
+ end
22
+ end
23
+
24
+ # protected
25
+
26
+ def unquote_table_name(table_name)
27
+ unquote_column_name(table_name)
28
+ end
29
+
30
+ def unquote_column_name(column_name)
31
+ column_name.to_s.tr('[]', '')
32
+ end
33
+
34
+ def unquote_string(string)
35
+ string.to_s.gsub("''", "'")
36
+ end
37
+
38
+ def unqualify_table_name(table_name)
39
+ table_name.to_s.split('.').last.tr('[]', '')
40
+ end
41
+
42
+ def unqualify_table_schema(table_name)
43
+ table_name.to_s.split('.')[-2].gsub(/[\[\]]/, '') rescue nil
44
+ end
45
+
46
+ def unqualify_db_name(table_name)
47
+ table_names = table_name.to_s.split('.')
48
+ table_names.length == 3 ? table_names.first.tr('[]', '') : nil
49
+ end
50
+
51
+ end
52
+ end
53
+ end
@@ -32,11 +32,6 @@ module ArJdbc
32
32
  require 'arjdbc/jdbc/quoted_primary_key'
33
33
  ActiveRecord::Base.extend ArJdbc::QuotedPrimaryKeyExtension
34
34
  end
35
-
36
- (class << mod; self; end).class_eval do
37
- alias_chained_method :insert, :query_dirty, :ora_insert
38
- alias_chained_method :columns, :query_cache, :ora_columns
39
- end
40
35
  end
41
36
 
42
37
  def self.column_selector
@@ -141,14 +136,44 @@ module ArJdbc
141
136
 
142
137
  end
143
138
 
144
- def adapter_name
145
- 'Oracle'
146
- end
147
-
148
139
  def self.arel2_visitors(config)
149
140
  { 'oracle' => Arel::Visitors::Oracle }
150
141
  end
151
142
 
143
+ ADAPTER_NAME = 'Oracle'
144
+
145
+ def adapter_name
146
+ ADAPTER_NAME
147
+ end
148
+
149
+ NATIVE_DATABASE_TYPES = {
150
+ :primary_key => "NUMBER(38) NOT NULL PRIMARY KEY",
151
+ :string => { :name => "VARCHAR2", :limit => 255 },
152
+ :text => { :name => "CLOB" },
153
+ :integer => { :name => "NUMBER", :limit => 38 },
154
+ :float => { :name => "NUMBER" },
155
+ :decimal => { :name => "DECIMAL" },
156
+ :datetime => { :name => "DATE" },
157
+ :timestamp => { :name => "TIMESTAMP" },
158
+ :time => { :name => "DATE" },
159
+ :date => { :name => "DATE" },
160
+ :binary => { :name => "BLOB" },
161
+ :boolean => { :name => "NUMBER", :limit => 1 },
162
+ :raw => { :name => "RAW", :limit => 2000 },
163
+ }
164
+
165
+ def native_database_types
166
+ super.merge(NATIVE_DATABASE_TYPES)
167
+ end
168
+
169
+ def modify_types(types)
170
+ super(types)
171
+ NATIVE_DATABASE_TYPES.each do |key, value|
172
+ types[key] = value.dup
173
+ end
174
+ types
175
+ end
176
+
152
177
  def prefetch_primary_key?(table_name = nil)
153
178
  columns(table_name).detect {|c| c.primary } if table_name
154
179
  end
@@ -159,14 +184,16 @@ module ArJdbc
159
184
  end
160
185
  alias_method :ids_in_list_limit, :in_clause_length
161
186
 
187
+ IDENTIFIER_LENGTH = 30 # :nodoc:
188
+
162
189
  # maximum length of Oracle identifiers is 30
163
- def table_alias_length; 30; end # :nodoc:
164
- def table_name_length; 30; end # :nodoc:
165
- def index_name_length; 30; end # :nodoc:
166
- def column_name_length; 30; end # :nodoc:
190
+ def table_alias_length; IDENTIFIER_LENGTH; end # :nodoc:
191
+ def table_name_length; IDENTIFIER_LENGTH; end # :nodoc:
192
+ def index_name_length; IDENTIFIER_LENGTH; end # :nodoc:
193
+ def column_name_length; IDENTIFIER_LENGTH; end # :nodoc:
167
194
 
168
- def default_sequence_name(table, column = nil) #:nodoc:
169
- "#{table}_seq"
195
+ def default_sequence_name(table_name, column = nil) # :nodoc:
196
+ "#{table_name.to_s[0, IDENTIFIER_LENGTH - 4]}_seq"
170
197
  end
171
198
 
172
199
  def create_table(name, options = {}) #:nodoc:
@@ -229,40 +256,30 @@ module ArJdbc
229
256
  defined?(::Arel::SqlLiteral) && ::Arel::SqlLiteral === value
230
257
  end
231
258
 
232
- def ora_insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = []) #:nodoc:
259
+ def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = []) # :nodoc:
233
260
  if (id_value && ! sql_literal?(id_value)) || pk.nil?
234
261
  # Pre-assigned id or table without a primary key
235
262
  # Presence of #to_sql means an Arel literal bind variable
236
263
  # that should use #execute_id_insert below
237
- execute sql, name, binds
264
+ value = exec_insert(to_sql(sql, binds), name, binds)
265
+ id_value || last_inserted_id(value) # super
238
266
  else
239
267
  # Assume the sql contains a bind-variable for the id
240
268
  # Extract the table from the insert sql. Yuck.
241
- table = sql.split(" ", 4)[2].gsub('"', '')
242
- sequence_name ||= default_sequence_name(table)
243
- id_value = next_sequence_value(sequence_name)
244
- log(sql, name) do
245
- @connection.execute_id_insert(sql, id_value)
269
+ sequence_name ||= begin
270
+ table = extract_table_ref_from_insert_sql(sql)
271
+ default_sequence_name(table)
246
272
  end
273
+ id_value = next_sequence_value(sequence_name)
274
+ log(sql, name) { @connection.execute_id_insert(sql, id_value) }
275
+ id_value
247
276
  end
248
- id_value
249
277
  end
250
-
278
+
251
279
  def indexes(table, name = nil)
252
280
  @connection.indexes(table, name, @connection.connection.meta_data.user_name)
253
281
  end
254
282
 
255
- def modify_types(types)
256
- super(types)
257
- types[:primary_key] = "NUMBER(38) NOT NULL PRIMARY KEY"
258
- types[:integer] = { :name => "NUMBER", :limit => 38 }
259
- types[:datetime] = { :name => "DATE" }
260
- types[:timestamp] = { :name => "TIMESTAMP" }
261
- types[:time] = { :name => "DATE" }
262
- types[:date] = { :name => "DATE" }
263
- types
264
- end
265
-
266
283
  def add_limit_offset!(sql, options) #:nodoc:
267
284
  offset = options[:offset] || 0
268
285
 
@@ -425,13 +442,13 @@ module ArJdbc
425
442
  def tables
426
443
  @connection.tables(nil, oracle_schema)
427
444
  end
428
-
445
+
429
446
  # NOTE: better to use current_schema instead of the configured one ?!
430
447
 
431
- def ora_columns(table_name, name = nil)
448
+ def columns(table_name, name = nil) # :nodoc:
432
449
  @connection.columns_internal(table_name.to_s, name, oracle_schema)
433
450
  end
434
-
451
+
435
452
  # QUOTING ==================================================
436
453
 
437
454
  # See ACTIVERECORD_JDBC-33 for details -- better to not quote
@@ -514,7 +531,12 @@ module ArJdbc
514
531
  @connection.execute_update(sql)
515
532
  end
516
533
  end
517
-
534
+
535
+ def extract_table_ref_from_insert_sql(sql) # :nodoc:
536
+ table = sql.split(" ", 4)[2].gsub('"', '')
537
+ ( idx = table.index('(') ) ? table[0...idx] : table # INTO table(col1, col2) ...
538
+ end
539
+
518
540
  # In Oracle, schemas are usually created under your username :
519
541
  # http://www.oracle.com/technology/obe/2day_dba/schema/schema.htm
520
542
  #
@@ -197,12 +197,9 @@ module ::ArJdbc
197
197
  end
198
198
 
199
199
  def tables(name = nil, table_name = nil) # :nodoc:
200
- sql = "SELECT name FROM sqlite_master WHERE type = 'table'"
201
- if table_name
202
- sql << " AND name = #{quote_table_name(table_name)}"
203
- else
204
- sql << " AND NOT name = 'sqlite_sequence'"
205
- end
200
+ sql = "SELECT name FROM sqlite_master " +
201
+ "WHERE type = 'table' AND NOT name = 'sqlite_sequence'"
202
+ sql << " AND name = #{quote_table_name(table_name)}" if table_name
206
203
 
207
204
  select_rows(sql, name).map { |row| row[0] }
208
205
  end
@@ -1,6 +1,6 @@
1
1
  module ArJdbc
2
2
  module Version
3
- VERSION = "1.2.8"
3
+ VERSION = "1.2.9"
4
4
  end
5
5
  end
6
6
  # Compatibility with older versions of ar-jdbc for other extensions out there
@@ -34,7 +34,8 @@ import arjdbc.derby.DerbyModule;
34
34
  import arjdbc.h2.H2RubyJdbcConnection;
35
35
  import arjdbc.hsqldb.HSQLDBModule;
36
36
  import arjdbc.informix.InformixRubyJdbcConnection;
37
- import arjdbc.mssql.MssqlRubyJdbcConnection;
37
+ import arjdbc.mssql.MSSQLModule;
38
+ import arjdbc.mssql.MSSQLRubyJdbcConnection;
38
39
  import arjdbc.mysql.MySQLModule;
39
40
  import arjdbc.mysql.MySQLRubyJdbcConnection;
40
41
  import arjdbc.oracle.OracleModule;
@@ -54,7 +55,7 @@ public class AdapterJavaService implements BasicLibraryService {
54
55
  // ActiveRecord::ConnectionAdapter-s :
55
56
  RubyClass jdbcConnection = RubyJdbcConnection.createJdbcConnectionClass(runtime);
56
57
  PostgresqlRubyJdbcConnection.createPostgresqlJdbcConnectionClass(runtime, jdbcConnection);
57
- MssqlRubyJdbcConnection.createMssqlJdbcConnectionClass(runtime, jdbcConnection);
58
+ MSSQLRubyJdbcConnection.createMSSQLJdbcConnectionClass(runtime, jdbcConnection);
58
59
  InformixRubyJdbcConnection.createInformixJdbcConnectionClass(runtime, jdbcConnection);
59
60
  OracleRubyJdbcConnection.createOracleJdbcConnectionClass(runtime, jdbcConnection);
60
61
  SQLite3RubyJdbcConnection.createSQLite3JdbcConnectionClass(runtime, jdbcConnection);
@@ -64,6 +65,7 @@ public class AdapterJavaService implements BasicLibraryService {
64
65
  // ArJdbc :
65
66
  RubyModule arJdbc = runtime.getOrCreateModule("ArJdbc");
66
67
  MySQLModule.load(arJdbc);
68
+ MSSQLModule.load(arJdbc);
67
69
  DerbyModule.load(arJdbc);
68
70
  HSQLDBModule.load(arJdbc);
69
71
  SQLite3Module.load(arJdbc);
@@ -0,0 +1,70 @@
1
+ /*
2
+ * The MIT License
3
+ *
4
+ * Copyright 2013 Karol Bucek.
5
+ *
6
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ * of this software and associated documentation files (the "Software"), to deal
8
+ * in the Software without restriction, including without limitation the rights
9
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ * copies of the Software, and to permit persons to whom the Software is
11
+ * furnished to do so, subject to the following conditions:
12
+ *
13
+ * The above copyright notice and this permission notice shall be included in
14
+ * all copies or substantial portions of the Software.
15
+ *
16
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
+ * THE SOFTWARE.
23
+ */
24
+ package arjdbc.mssql;
25
+
26
+ import static arjdbc.util.QuotingUtils.BYTES_0;
27
+ import static arjdbc.util.QuotingUtils.BYTES_1;
28
+ import static arjdbc.util.QuotingUtils.quoteSingleQuotesWithFallback;
29
+
30
+ import org.jruby.RubyModule;
31
+ import org.jruby.RubyString;
32
+ import org.jruby.anno.JRubyMethod;
33
+ import org.jruby.runtime.ThreadContext;
34
+ import org.jruby.runtime.builtin.IRubyObject;
35
+
36
+ /**
37
+ * ArJdbc::MSSQL
38
+ *
39
+ * @author kares
40
+ */
41
+ public class MSSQLModule {
42
+
43
+ public static void load(final RubyModule arJdbc) {
44
+ RubyModule mssql = arJdbc.defineModuleUnder("MSSQL");
45
+ mssql.defineAnnotatedMethods( MSSQLModule.class );
46
+ }
47
+
48
+ @JRubyMethod(name = "quote_string", required = 1, frame = false)
49
+ public static IRubyObject quote_string(
50
+ final ThreadContext context,
51
+ final IRubyObject self,
52
+ final IRubyObject string) {
53
+ return quoteSingleQuotesWithFallback(context, string);
54
+ }
55
+
56
+ @JRubyMethod(name = "quoted_true", required = 0, frame = false)
57
+ public static IRubyObject quoted_true(
58
+ final ThreadContext context,
59
+ final IRubyObject self) {
60
+ return RubyString.newString(context.getRuntime(), BYTES_1);
61
+ }
62
+
63
+ @JRubyMethod(name = "quoted_false", required = 0, frame = false)
64
+ public static IRubyObject quoted_false(
65
+ final ThreadContext context,
66
+ final IRubyObject self) {
67
+ return RubyString.newString(context.getRuntime(), BYTES_0);
68
+ }
69
+
70
+ }
@@ -0,0 +1,128 @@
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
+ package arjdbc.mssql;
27
+
28
+ import java.sql.ResultSet;
29
+ import java.sql.SQLException;
30
+ import java.sql.Types;
31
+ import java.util.List;
32
+
33
+ import arjdbc.jdbc.RubyJdbcConnection;
34
+ import static arjdbc.jdbc.RubyJdbcConnection.ColumnData;
35
+
36
+ import org.jruby.Ruby;
37
+ import org.jruby.RubyClass;
38
+ import org.jruby.RubyString;
39
+ import org.jruby.runtime.ObjectAllocator;
40
+ import org.jruby.runtime.ThreadContext;
41
+ import org.jruby.runtime.builtin.IRubyObject;
42
+
43
+ /**
44
+ *
45
+ * @author nicksieger
46
+ */
47
+ public class MSSQLRubyJdbcConnection extends RubyJdbcConnection {
48
+
49
+ private RubyString _row_num;
50
+
51
+ protected MSSQLRubyJdbcConnection(Ruby runtime, RubyClass metaClass) {
52
+ super(runtime, metaClass);
53
+ _row_num = runtime.newString("_row_num");
54
+ }
55
+
56
+ public static RubyClass createMSSQLJdbcConnectionClass(Ruby runtime, RubyClass jdbcConnection) {
57
+ final RubyClass clazz = getConnectionAdapters(runtime). // ActiveRecord::ConnectionAdapters
58
+ defineClassUnder("MSSQLJdbcConnection", jdbcConnection, MSSQL_JDBCCONNECTION_ALLOCATOR);
59
+ clazz.defineAnnotatedMethods(MSSQLRubyJdbcConnection.class);
60
+ getConnectionAdapters(runtime).setConstant("MssqlJdbcConnection", clazz); // backwards-compat
61
+ return clazz;
62
+ }
63
+
64
+ private static ObjectAllocator MSSQL_JDBCCONNECTION_ALLOCATOR = new ObjectAllocator() {
65
+ public IRubyObject allocate(Ruby runtime, RubyClass klass) {
66
+ return new MSSQLRubyJdbcConnection(runtime, klass);
67
+ }
68
+ };
69
+
70
+ protected static IRubyObject booleanToRuby(Ruby runtime, ResultSet resultSet, boolean booleanValue)
71
+ throws SQLException {
72
+ if (booleanValue == false && resultSet.wasNull()) return runtime.getNil();
73
+ return runtime.newBoolean(booleanValue);
74
+ }
75
+
76
+ /**
77
+ * Treat LONGVARCHAR as CLOB on Mssql for purposes of converting a JDBC value to Ruby.
78
+ * Treat BOOLEAN/BIT as Boolean, rather than the default behaviour of conversion to string
79
+ */
80
+ @Override
81
+ protected IRubyObject jdbcToRuby(Ruby runtime, int column, int type, ResultSet resultSet)
82
+ throws SQLException {
83
+ if ( Types.BOOLEAN == type || Types.BIT == type ) {
84
+ return booleanToRuby(runtime, resultSet, resultSet.getBoolean(column));
85
+ }
86
+ if (type == Types.LONGVARCHAR) {
87
+ type = Types.CLOB;
88
+ }
89
+ return super.jdbcToRuby(runtime, column, type, resultSet);
90
+ }
91
+
92
+ /**
93
+ * Microsoft SQL 2000+ support schemas
94
+ */
95
+ @Override
96
+ protected boolean databaseSupportsSchemas() {
97
+ return true;
98
+ }
99
+
100
+ @Override
101
+ protected void populateFromResultSet(ThreadContext context, Ruby runtime, List results,
102
+ ResultSet resultSet, ColumnData[] columns) throws SQLException {
103
+ super.populateFromResultSet(context, runtime, results, resultSet, filterRowNumFromColumns(columns));
104
+ }
105
+
106
+ /**
107
+ * Filter out the <tt>_row_num</tt> column from results.
108
+ */
109
+ private ColumnData[] filterRowNumFromColumns(ColumnData[] columns) {
110
+ for (int i = 0; i < columns.length; i++) {
111
+ if (columns[i].name.equals(_row_num)) {
112
+ ColumnData[] filtered = new ColumnData[columns.length - 1];
113
+ if (i > 0) {
114
+ System.arraycopy(columns, 0, filtered, 0, i);
115
+ }
116
+
117
+ if (i + 1 < columns.length) {
118
+ System.arraycopy(columns, i + 1, filtered, i, columns.length - (i + 1));
119
+ }
120
+
121
+ return filtered;
122
+ }
123
+ }
124
+
125
+ return columns;
126
+ }
127
+
128
+ }