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.
- 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
|
+
}
|