activerecord-jdbc-adapter 1.2.8 → 1.2.9
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.
- data/.travis.yml +1 -1
- data/Gemfile +2 -1
- data/Gemfile.lock +5 -6
- data/History.txt +11 -0
- data/gemfiles/rails23.gemfile +2 -1
- data/gemfiles/rails23.gemfile.lock +5 -2
- data/gemfiles/rails30.gemfile +2 -1
- data/gemfiles/rails30.gemfile.lock +5 -2
- data/gemfiles/rails31.gemfile +2 -1
- data/gemfiles/rails31.gemfile.lock +5 -2
- data/gemfiles/rails32.gemfile +2 -1
- data/gemfiles/rails32.gemfile.lock +5 -2
- data/lib/arel/engines/sql/compilers/mssql_compiler.rb +1 -1
- data/lib/arel/visitors/sql_server.rb +4 -4
- data/lib/arjdbc/db2/adapter.rb +14 -3
- data/lib/arjdbc/discover.rb +1 -1
- data/lib/arjdbc/jdbc/adapter.rb +1 -0
- data/lib/arjdbc/jdbc/adapter_java.jar +0 -0
- data/lib/arjdbc/jdbc/connection.rb +73 -63
- data/lib/arjdbc/jdbc/extension.rb +1 -1
- data/lib/arjdbc/mssql.rb +3 -0
- data/lib/arjdbc/mssql/adapter.rb +132 -115
- data/lib/arjdbc/mssql/connection_methods.rb +1 -1
- data/lib/arjdbc/mssql/limit_helpers.rb +62 -66
- data/lib/arjdbc/mssql/lock_helpers.rb +2 -2
- data/lib/arjdbc/mssql/tsql_methods.rb +58 -0
- data/lib/arjdbc/mssql/utils.rb +53 -0
- data/lib/arjdbc/oracle/adapter.rb +61 -39
- data/lib/arjdbc/sqlite3/adapter.rb +3 -6
- data/lib/arjdbc/version.rb +1 -1
- data/src/java/arjdbc/jdbc/AdapterJavaService.java +4 -2
- data/src/java/arjdbc/mssql/MSSQLModule.java +70 -0
- data/src/java/arjdbc/mssql/MSSQLRubyJdbcConnection.java +128 -0
- data/src/java/arjdbc/mssql/MssqlRubyJdbcConnection.java +25 -112
- data/test/db/mssql.rb +8 -8
- data/test/db2_simple_test.rb +7 -0
- data/test/models/entry.rb +2 -1
- data/test/mssql_binary_test.rb +6 -0
- data/test/mssql_db_create_test.rb +5 -2
- data/test/mssql_identity_insert_test.rb +1 -2
- data/test/mssql_ignore_system_views_test.rb +5 -5
- data/test/mssql_limit_offset_test.rb +51 -55
- data/test/mssql_multibyte_test.rb +1 -2
- data/test/mssql_row_locking_test.rb +1 -1
- data/test/mssql_simple_test.rb +6 -10
- data/test/{mssql_row_locking_sql_test.rb → mssql_test.rb} +110 -18
- data/test/mysql_db_create_test.rb +13 -7
- data/test/oracle_simple_test.rb +18 -0
- data/test/postgres_db_create_test.rb +26 -13
- data/test/simple.rb +45 -15
- data/test/sqlite3_schema_dump_test.rb +6 -0
- data/test/sqlite3_type_conversion_test.rb +20 -17
- data/test/test_helper.rb +44 -2
- metadata +9 -4
- data/lib/arjdbc/mssql/tsql_helper.rb +0 -53
@@ -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;
|
164
|
-
def table_name_length;
|
165
|
-
def index_name_length;
|
166
|
-
def column_name_length;
|
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(
|
169
|
-
"#{
|
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
|
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
|
-
|
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
|
-
|
242
|
-
|
243
|
-
|
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
|
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
|
201
|
-
|
202
|
-
|
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
|
data/lib/arjdbc/version.rb
CHANGED
@@ -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.
|
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
|
-
|
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
|
+
}
|