activerecord-jdbc-alt-adapter 50.3.0-java
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +35 -0
- data/.travis.yml +100 -0
- data/.yardopts +4 -0
- data/CONTRIBUTING.md +50 -0
- data/Gemfile +92 -0
- data/History.md +1191 -0
- data/LICENSE.txt +26 -0
- data/README.md +240 -0
- data/RUNNING_TESTS.md +127 -0
- data/Rakefile +336 -0
- data/Rakefile.jdbc +20 -0
- data/activerecord-jdbc-adapter.gemspec +55 -0
- data/activerecord-jdbc-alt-adapter.gemspec +56 -0
- data/lib/active_record/connection_adapters/as400_adapter.rb +2 -0
- data/lib/active_record/connection_adapters/db2_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/derby_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/firebird_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/h2_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/hsqldb_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/informix_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/jdbc_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/jndi_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/mariadb_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/mssql_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/mysql_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +1 -0
- data/lib/activerecord-jdbc-adapter.rb +1 -0
- data/lib/arel/visitors/compat.rb +60 -0
- data/lib/arel/visitors/db2.rb +137 -0
- data/lib/arel/visitors/derby.rb +112 -0
- data/lib/arel/visitors/firebird.rb +79 -0
- data/lib/arel/visitors/h2.rb +25 -0
- data/lib/arel/visitors/hsqldb.rb +32 -0
- data/lib/arel/visitors/postgresql_jdbc.rb +6 -0
- data/lib/arel/visitors/sql_server.rb +225 -0
- data/lib/arel/visitors/sql_server/ng42.rb +294 -0
- data/lib/arel/visitors/sqlserver.rb +214 -0
- data/lib/arjdbc.rb +19 -0
- data/lib/arjdbc/abstract/connection_management.rb +35 -0
- data/lib/arjdbc/abstract/core.rb +74 -0
- data/lib/arjdbc/abstract/database_statements.rb +64 -0
- data/lib/arjdbc/abstract/statement_cache.rb +58 -0
- data/lib/arjdbc/abstract/transaction_support.rb +86 -0
- data/lib/arjdbc/db2.rb +4 -0
- data/lib/arjdbc/db2/adapter.rb +789 -0
- data/lib/arjdbc/db2/as400.rb +130 -0
- data/lib/arjdbc/db2/column.rb +167 -0
- data/lib/arjdbc/db2/connection_methods.rb +44 -0
- data/lib/arjdbc/derby.rb +3 -0
- data/lib/arjdbc/derby/active_record_patch.rb +13 -0
- data/lib/arjdbc/derby/adapter.rb +540 -0
- data/lib/arjdbc/derby/connection_methods.rb +20 -0
- data/lib/arjdbc/derby/schema_creation.rb +15 -0
- data/lib/arjdbc/discover.rb +104 -0
- data/lib/arjdbc/firebird.rb +4 -0
- data/lib/arjdbc/firebird/adapter.rb +434 -0
- data/lib/arjdbc/firebird/connection_methods.rb +23 -0
- data/lib/arjdbc/h2.rb +3 -0
- data/lib/arjdbc/h2/adapter.rb +303 -0
- data/lib/arjdbc/h2/connection_methods.rb +27 -0
- data/lib/arjdbc/hsqldb.rb +3 -0
- data/lib/arjdbc/hsqldb/adapter.rb +297 -0
- data/lib/arjdbc/hsqldb/connection_methods.rb +28 -0
- data/lib/arjdbc/hsqldb/explain_support.rb +35 -0
- data/lib/arjdbc/hsqldb/schema_creation.rb +11 -0
- data/lib/arjdbc/informix.rb +5 -0
- data/lib/arjdbc/informix/adapter.rb +162 -0
- data/lib/arjdbc/informix/connection_methods.rb +9 -0
- data/lib/arjdbc/jdbc.rb +59 -0
- data/lib/arjdbc/jdbc/adapter.rb +475 -0
- data/lib/arjdbc/jdbc/adapter_require.rb +46 -0
- data/lib/arjdbc/jdbc/base_ext.rb +15 -0
- data/lib/arjdbc/jdbc/callbacks.rb +53 -0
- data/lib/arjdbc/jdbc/column.rb +97 -0
- data/lib/arjdbc/jdbc/connection.rb +14 -0
- data/lib/arjdbc/jdbc/connection_methods.rb +37 -0
- data/lib/arjdbc/jdbc/error.rb +65 -0
- data/lib/arjdbc/jdbc/extension.rb +59 -0
- data/lib/arjdbc/jdbc/java.rb +13 -0
- data/lib/arjdbc/jdbc/railtie.rb +2 -0
- data/lib/arjdbc/jdbc/rake_tasks.rb +3 -0
- data/lib/arjdbc/jdbc/serialized_attributes_helper.rb +3 -0
- data/lib/arjdbc/jdbc/type_cast.rb +166 -0
- data/lib/arjdbc/jdbc/type_converter.rb +142 -0
- data/lib/arjdbc/mssql.rb +7 -0
- data/lib/arjdbc/mssql/adapter.rb +384 -0
- data/lib/arjdbc/mssql/column.rb +29 -0
- data/lib/arjdbc/mssql/connection_methods.rb +79 -0
- data/lib/arjdbc/mssql/database_statements.rb +134 -0
- data/lib/arjdbc/mssql/errors.rb +6 -0
- data/lib/arjdbc/mssql/explain_support.rb +129 -0
- data/lib/arjdbc/mssql/extensions.rb +36 -0
- data/lib/arjdbc/mssql/limit_helpers.rb +231 -0
- data/lib/arjdbc/mssql/lock_methods.rb +77 -0
- data/lib/arjdbc/mssql/old_adapter.rb +804 -0
- data/lib/arjdbc/mssql/old_column.rb +200 -0
- data/lib/arjdbc/mssql/quoting.rb +101 -0
- data/lib/arjdbc/mssql/schema_creation.rb +31 -0
- data/lib/arjdbc/mssql/schema_definitions.rb +74 -0
- data/lib/arjdbc/mssql/schema_statements.rb +329 -0
- data/lib/arjdbc/mssql/transaction.rb +69 -0
- data/lib/arjdbc/mssql/types.rb +52 -0
- data/lib/arjdbc/mssql/types/binary_types.rb +33 -0
- data/lib/arjdbc/mssql/types/date_and_time_types.rb +134 -0
- data/lib/arjdbc/mssql/types/deprecated_types.rb +40 -0
- data/lib/arjdbc/mssql/types/numeric_types.rb +71 -0
- data/lib/arjdbc/mssql/types/string_types.rb +56 -0
- data/lib/arjdbc/mssql/utils.rb +66 -0
- data/lib/arjdbc/mysql.rb +3 -0
- data/lib/arjdbc/mysql/adapter.rb +140 -0
- data/lib/arjdbc/mysql/connection_methods.rb +166 -0
- data/lib/arjdbc/oracle/adapter.rb +863 -0
- data/lib/arjdbc/postgresql.rb +3 -0
- data/lib/arjdbc/postgresql/adapter.rb +687 -0
- data/lib/arjdbc/postgresql/base/array_decoder.rb +26 -0
- data/lib/arjdbc/postgresql/base/array_encoder.rb +25 -0
- data/lib/arjdbc/postgresql/base/array_parser.rb +95 -0
- data/lib/arjdbc/postgresql/base/pgconn.rb +11 -0
- data/lib/arjdbc/postgresql/column.rb +51 -0
- data/lib/arjdbc/postgresql/connection_methods.rb +67 -0
- data/lib/arjdbc/postgresql/name.rb +24 -0
- data/lib/arjdbc/postgresql/oid_types.rb +266 -0
- data/lib/arjdbc/railtie.rb +11 -0
- data/lib/arjdbc/sqlite3.rb +3 -0
- data/lib/arjdbc/sqlite3/adapter.rb +678 -0
- data/lib/arjdbc/sqlite3/connection_methods.rb +59 -0
- data/lib/arjdbc/sybase.rb +2 -0
- data/lib/arjdbc/sybase/adapter.rb +47 -0
- data/lib/arjdbc/tasks.rb +13 -0
- data/lib/arjdbc/tasks/database_tasks.rb +31 -0
- data/lib/arjdbc/tasks/databases.rake +48 -0
- data/lib/arjdbc/tasks/db2_database_tasks.rb +104 -0
- data/lib/arjdbc/tasks/derby_database_tasks.rb +95 -0
- data/lib/arjdbc/tasks/h2_database_tasks.rb +31 -0
- data/lib/arjdbc/tasks/hsqldb_database_tasks.rb +70 -0
- data/lib/arjdbc/tasks/jdbc_database_tasks.rb +169 -0
- data/lib/arjdbc/tasks/mssql_database_tasks.rb +46 -0
- data/lib/arjdbc/util/quoted_cache.rb +60 -0
- data/lib/arjdbc/util/serialized_attributes.rb +98 -0
- data/lib/arjdbc/util/table_copier.rb +110 -0
- data/lib/arjdbc/version.rb +3 -0
- data/lib/generators/jdbc/USAGE +9 -0
- data/lib/generators/jdbc/jdbc_generator.rb +17 -0
- data/lib/jdbc_adapter.rb +2 -0
- data/lib/jdbc_adapter/rake_tasks.rb +4 -0
- data/lib/jdbc_adapter/version.rb +4 -0
- data/pom.xml +114 -0
- data/rails_generators/jdbc_generator.rb +15 -0
- data/rails_generators/templates/config/initializers/jdbc.rb +10 -0
- data/rails_generators/templates/lib/tasks/jdbc.rake +11 -0
- data/rakelib/01-tomcat.rake +51 -0
- data/rakelib/02-test.rake +132 -0
- data/rakelib/bundler_ext.rb +11 -0
- data/rakelib/db.rake +75 -0
- data/rakelib/rails.rake +223 -0
- data/src/java/arjdbc/ArJdbcModule.java +276 -0
- data/src/java/arjdbc/db2/DB2Module.java +76 -0
- data/src/java/arjdbc/db2/DB2RubyJdbcConnection.java +126 -0
- data/src/java/arjdbc/derby/DerbyModule.java +178 -0
- data/src/java/arjdbc/derby/DerbyRubyJdbcConnection.java +152 -0
- data/src/java/arjdbc/firebird/FirebirdRubyJdbcConnection.java +174 -0
- data/src/java/arjdbc/h2/H2Module.java +50 -0
- data/src/java/arjdbc/h2/H2RubyJdbcConnection.java +85 -0
- data/src/java/arjdbc/hsqldb/HSQLDBModule.java +73 -0
- data/src/java/arjdbc/informix/InformixRubyJdbcConnection.java +75 -0
- data/src/java/arjdbc/jdbc/AdapterJavaService.java +43 -0
- data/src/java/arjdbc/jdbc/Callable.java +44 -0
- data/src/java/arjdbc/jdbc/ConnectionFactory.java +45 -0
- data/src/java/arjdbc/jdbc/DataSourceConnectionFactory.java +156 -0
- data/src/java/arjdbc/jdbc/DriverConnectionFactory.java +63 -0
- data/src/java/arjdbc/jdbc/DriverWrapper.java +119 -0
- data/src/java/arjdbc/jdbc/JdbcResult.java +130 -0
- data/src/java/arjdbc/jdbc/RubyConnectionFactory.java +61 -0
- data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +3979 -0
- data/src/java/arjdbc/mssql/MSSQLModule.java +90 -0
- data/src/java/arjdbc/mssql/MSSQLRubyJdbcConnection.java +508 -0
- data/src/java/arjdbc/mysql/MySQLModule.java +152 -0
- data/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +294 -0
- data/src/java/arjdbc/oracle/OracleModule.java +80 -0
- data/src/java/arjdbc/oracle/OracleRubyJdbcConnection.java +455 -0
- data/src/java/arjdbc/postgresql/ByteaUtils.java +157 -0
- data/src/java/arjdbc/postgresql/PgDateTimeUtils.java +52 -0
- data/src/java/arjdbc/postgresql/PostgreSQLModule.java +77 -0
- data/src/java/arjdbc/postgresql/PostgreSQLResult.java +192 -0
- data/src/java/arjdbc/postgresql/PostgreSQLRubyJdbcConnection.java +948 -0
- data/src/java/arjdbc/sqlite3/SQLite3Module.java +73 -0
- data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +525 -0
- data/src/java/arjdbc/util/CallResultSet.java +826 -0
- data/src/java/arjdbc/util/DateTimeUtils.java +699 -0
- data/src/java/arjdbc/util/ObjectSupport.java +65 -0
- data/src/java/arjdbc/util/QuotingUtils.java +137 -0
- data/src/java/arjdbc/util/StringCache.java +63 -0
- data/src/java/arjdbc/util/StringHelper.java +145 -0
- metadata +269 -0
@@ -0,0 +1,142 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module ConnectionAdapters
|
3
|
+
# I want to use JDBC's DatabaseMetaData#getTypeInfo to choose the best native types to
|
4
|
+
# use for ActiveRecord's Adapter#native_database_types in a database-independent way,
|
5
|
+
# but apparently a database driver can return multiple types for a given
|
6
|
+
# java.sql.Types constant. So this type converter uses some heuristics to try to pick
|
7
|
+
# the best (most common) type to use. It's not great, it would be better to just
|
8
|
+
# delegate to each database's existing AR adapter's native_database_types method, but I
|
9
|
+
# wanted to try to do this in a way that didn't pull in all the other adapters as
|
10
|
+
# dependencies. Improvements appreciated.
|
11
|
+
class JdbcTypeConverter
|
12
|
+
|
13
|
+
# @private
|
14
|
+
TEXT_TYPES = [ Jdbc::Types::LONGVARCHAR, Jdbc::Types::CLOB ]
|
15
|
+
private_constant :TEXT_TYPES if respond_to? :private_constant
|
16
|
+
|
17
|
+
# @private
|
18
|
+
FLOAT_TYPES = [ Jdbc::Types::FLOAT, Jdbc::Types::DOUBLE, Jdbc::Types::REAL ]
|
19
|
+
private_constant :FLOAT_TYPES if respond_to? :private_constant
|
20
|
+
|
21
|
+
# @private
|
22
|
+
BINARY_TYPES = [ Jdbc::Types::LONGVARBINARY,Jdbc::Types::BINARY,Jdbc::Types::BLOB ]
|
23
|
+
private_constant :BINARY_TYPES if respond_to? :private_constant
|
24
|
+
|
25
|
+
# The basic ActiveRecord types, mapped to an array of procs that are used to #select
|
26
|
+
# the best type. The procs are used as selectors in order until there is only one
|
27
|
+
# type left. If all the selectors are applied and there is still more than one
|
28
|
+
# type, an exception will be raised.
|
29
|
+
AR_TO_JDBC_TYPES = {
|
30
|
+
:string => [ lambda {|r| Jdbc::Types::VARCHAR == r['data_type'].to_i},
|
31
|
+
lambda {|r| r['type_name'] =~ /^varchar/i},
|
32
|
+
lambda {|r| r['type_name'] =~ /^varchar$/i},
|
33
|
+
lambda {|r| r['type_name'] =~ /varying/i}],
|
34
|
+
:text => [ lambda {|r| TEXT_TYPES.include?(r['data_type'].to_i)},
|
35
|
+
lambda {|r| r['type_name'] =~ /^text$/i}, # For Informix
|
36
|
+
lambda {|r| r['type_name'] =~ /sub_type 1$/i}, # For FireBird
|
37
|
+
lambda {|r| r['type_name'] =~ /^(text|clob)$/i},
|
38
|
+
lambda {|r| r['type_name'] =~ /^character large object$/i},
|
39
|
+
lambda {|r| r['sql_data_type'] == 2005}],
|
40
|
+
:integer => [ lambda {|r| Jdbc::Types::INTEGER == r['data_type'].to_i},
|
41
|
+
lambda {|r| r['type_name'] =~ /^integer$/i},
|
42
|
+
lambda {|r| r['type_name'] =~ /^int4$/i},
|
43
|
+
lambda {|r| r['type_name'] =~ /^int$/i}],
|
44
|
+
:decimal => [ lambda {|r| Jdbc::Types::DECIMAL == r['data_type'].to_i},
|
45
|
+
lambda {|r| r['type_name'] =~ /^decimal$/i},
|
46
|
+
lambda {|r| r['type_name'] =~ /^numeric$/i},
|
47
|
+
lambda {|r| r['type_name'] =~ /^number$/i},
|
48
|
+
lambda {|r| r['type_name'] =~ /^real$/i},
|
49
|
+
lambda {|r| r['precision'] == '38'},
|
50
|
+
lambda {|r| r['data_type'].to_i == Jdbc::Types::DECIMAL}],
|
51
|
+
:float => [ lambda {|r| FLOAT_TYPES.include?(r['data_type'].to_i)},
|
52
|
+
lambda {|r| r['data_type'].to_i == Jdbc::Types::REAL}, #Prefer REAL to DOUBLE for Postgresql
|
53
|
+
lambda {|r| r['type_name'] =~ /^float/i},
|
54
|
+
lambda {|r| r['type_name'] =~ /^double$/i},
|
55
|
+
lambda {|r| r['type_name'] =~ /^real$/i},
|
56
|
+
lambda {|r| r['precision'] == '15'}],
|
57
|
+
:datetime => [ lambda {|r| Jdbc::Types::TIMESTAMP == r['data_type'].to_i},
|
58
|
+
lambda {|r| r['type_name'] =~ /^datetime$/i},
|
59
|
+
lambda {|r| r['type_name'] =~ /^timestamp$/i},
|
60
|
+
lambda {|r| r['type_name'] =~ /^datetime.+/i},
|
61
|
+
lambda {|r| r['type_name'] =~ /^date/i},
|
62
|
+
lambda {|r| r['type_name'] =~ /^integer/i}], #Num of milliseconds for SQLite3 JDBC Driver
|
63
|
+
:timestamp => [ lambda {|r| Jdbc::Types::TIMESTAMP == r['data_type'].to_i},
|
64
|
+
lambda {|r| r['type_name'] =~ /^timestamp$/i},
|
65
|
+
lambda {|r| r['type_name'] =~ /^datetime$/i},
|
66
|
+
lambda {|r| r['type_name'] =~ /^datetime.+/i},
|
67
|
+
lambda {|r| r['type_name'] =~ /^date/i},
|
68
|
+
lambda {|r| r['type_name'] =~ /^integer/i}], #Num of milliseconds for SQLite3 JDBC Driver
|
69
|
+
:time => [ lambda {|r| Jdbc::Types::TIME == r['data_type'].to_i},
|
70
|
+
lambda {|r| r['type_name'] =~ /^time$/i},
|
71
|
+
lambda {|r| r['type_name'] =~ /^datetime$/i},
|
72
|
+
lambda {|r| r['type_name'] =~ /^datetime.+/i}, # For Informix
|
73
|
+
lambda {|r| r['type_name'] =~ /^date/i},
|
74
|
+
lambda {|r| r['type_name'] =~ /^integer/i}], #Num of milliseconds for SQLite3 JDBC Driver
|
75
|
+
:date => [ lambda {|r| Jdbc::Types::DATE == r['data_type'].to_i},
|
76
|
+
lambda {|r| r['type_name'] =~ /^date$/i},
|
77
|
+
lambda {|r| r['type_name'] =~ /^date/i},
|
78
|
+
lambda {|r| r['type_name'] =~ /^integer/i}], #Num of milliseconds for SQLite3 JDBC Driver3
|
79
|
+
:binary => [ lambda {|r| BINARY_TYPES.include?(r['data_type'].to_i)},
|
80
|
+
lambda {|r| r['type_name'] =~ /^blob/i},
|
81
|
+
lambda {|r| r['type_name'] =~ /sub_type 0$/i}, # For FireBird
|
82
|
+
lambda {|r| r['type_name'] =~ /^varbinary$/i}, # We want this sucker for Mimer
|
83
|
+
lambda {|r| r['type_name'] =~ /^binary$/i}, ],
|
84
|
+
:boolean => [ lambda {|r| Jdbc::Types::BIT == r['data_type'].to_i && r['precision'].to_i == 1},
|
85
|
+
lambda {|r| Jdbc::Types::TINYINT == r['data_type'].to_i},
|
86
|
+
lambda {|r| r['type_name'] =~ /^bool/i},
|
87
|
+
lambda {|r| r['data_type'].to_i == Jdbc::Types::BIT},
|
88
|
+
lambda {|r| r['type_name'] =~ /^tinyint$/i},
|
89
|
+
lambda {|r| r['type_name'] =~ /^decimal$/i},
|
90
|
+
lambda {|r| r['type_name'] =~ /^integer$/i}]
|
91
|
+
}
|
92
|
+
|
93
|
+
def initialize(types)
|
94
|
+
@types = types
|
95
|
+
@types.each {|t| t['type_name'] ||= t['local_type_name']} # Sybase driver seems to want 'local_type_name'
|
96
|
+
end
|
97
|
+
|
98
|
+
def choose_best_types
|
99
|
+
type_map = {}
|
100
|
+
@types.each do |row|
|
101
|
+
name = row['type_name'].downcase
|
102
|
+
k = name.to_sym
|
103
|
+
type_map[k] = { :name => name }
|
104
|
+
set_limit_to_nonzero_precision(type_map[k], row)
|
105
|
+
end
|
106
|
+
|
107
|
+
AR_TO_JDBC_TYPES.keys.each do |ar_type|
|
108
|
+
typerow = choose_type(ar_type)
|
109
|
+
type_map[ar_type] = { :name => typerow['type_name'].downcase }
|
110
|
+
case ar_type
|
111
|
+
when :integer, :string, :decimal
|
112
|
+
set_limit_to_nonzero_precision(type_map[ar_type], typerow)
|
113
|
+
when :boolean
|
114
|
+
type_map[ar_type][:limit] = 1
|
115
|
+
end
|
116
|
+
end
|
117
|
+
type_map
|
118
|
+
end
|
119
|
+
|
120
|
+
def choose_type(ar_type)
|
121
|
+
types = @types
|
122
|
+
AR_TO_JDBC_TYPES[ar_type].each do |proc|
|
123
|
+
new_types = types.reject {|r| r["data_type"].to_i == Jdbc::Types::OTHER}
|
124
|
+
new_types = new_types.select(&proc)
|
125
|
+
new_types = new_types.inject([]) do |typs,t|
|
126
|
+
typs << t unless typs.detect {|el| el['type_name'] == t['type_name']}
|
127
|
+
typs
|
128
|
+
end
|
129
|
+
return new_types.first if new_types.length == 1
|
130
|
+
types = new_types if new_types.length > 0
|
131
|
+
end
|
132
|
+
raise "unable to choose type for #{ar_type} from:\n#{types.collect{|t| t['type_name']}.inspect}"
|
133
|
+
end
|
134
|
+
|
135
|
+
def set_limit_to_nonzero_precision(map, row)
|
136
|
+
if row['precision'] && row['precision'].to_i > 0
|
137
|
+
map[:limit] = row['precision'].to_i
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
data/lib/arjdbc/mssql.rb
ADDED
@@ -0,0 +1,384 @@
|
|
1
|
+
# frozen_string_literal: false
|
2
|
+
|
3
|
+
ArJdbc.load_java_part :MSSQL
|
4
|
+
|
5
|
+
require 'arel'
|
6
|
+
require 'arel/visitors/bind_visitor'
|
7
|
+
require 'arel/visitors/sqlserver'
|
8
|
+
require 'active_record/connection_adapters/abstract_adapter'
|
9
|
+
|
10
|
+
require 'arjdbc/abstract/core'
|
11
|
+
require 'arjdbc/abstract/connection_management'
|
12
|
+
require 'arjdbc/abstract/database_statements'
|
13
|
+
require 'arjdbc/abstract/statement_cache'
|
14
|
+
require 'arjdbc/abstract/transaction_support'
|
15
|
+
|
16
|
+
require 'arjdbc/mssql/column'
|
17
|
+
require 'arjdbc/mssql/types'
|
18
|
+
require 'arjdbc/mssql/quoting'
|
19
|
+
require 'arjdbc/mssql/schema_definitions'
|
20
|
+
require 'arjdbc/mssql/schema_statements'
|
21
|
+
require 'arjdbc/mssql/database_statements'
|
22
|
+
require 'arjdbc/mssql/explain_support'
|
23
|
+
require 'arjdbc/mssql/extensions'
|
24
|
+
require 'arjdbc/mssql/transaction'
|
25
|
+
require 'arjdbc/mssql/errors'
|
26
|
+
require 'arjdbc/mssql/schema_creation'
|
27
|
+
|
28
|
+
module ActiveRecord
|
29
|
+
module ConnectionAdapters
|
30
|
+
# MSSQL (SQLServer) adapter class definition
|
31
|
+
class MSSQLAdapter < AbstractAdapter
|
32
|
+
ADAPTER_NAME = 'MSSQL'.freeze
|
33
|
+
|
34
|
+
MSSQL_VERSION_YEAR = {
|
35
|
+
8 => '2000',
|
36
|
+
9 => '2005',
|
37
|
+
10 => '2008',
|
38
|
+
11 => '2012',
|
39
|
+
12 => '2014',
|
40
|
+
13 => '2016',
|
41
|
+
14 => '2017',
|
42
|
+
15 => '2019'
|
43
|
+
}.freeze
|
44
|
+
|
45
|
+
include Jdbc::ConnectionPoolCallbacks
|
46
|
+
include ArJdbc::Abstract::Core
|
47
|
+
include ArJdbc::Abstract::ConnectionManagement
|
48
|
+
include ArJdbc::Abstract::DatabaseStatements
|
49
|
+
include ArJdbc::Abstract::StatementCache
|
50
|
+
include ArJdbc::Abstract::TransactionSupport
|
51
|
+
|
52
|
+
include MSSQL::Quoting
|
53
|
+
include MSSQL::SchemaStatements
|
54
|
+
include MSSQL::DatabaseStatements
|
55
|
+
include MSSQL::ExplainSupport
|
56
|
+
|
57
|
+
@cs_equality_operator = 'COLLATE Latin1_General_CS_AS_WS'
|
58
|
+
|
59
|
+
class << self
|
60
|
+
attr_accessor :cs_equality_operator
|
61
|
+
end
|
62
|
+
|
63
|
+
def initialize(connection, logger, _connection_parameters, config = {})
|
64
|
+
# configure_connection happens in super
|
65
|
+
super(connection, logger, config)
|
66
|
+
|
67
|
+
unless mssql_major_version >= 11
|
68
|
+
raise "Your MSSQL #{mssql_version_year} is too old. This adapter supports MSSQL >= 2012."
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# Returns the (JDBC) connection class to be used for this adapter.
|
73
|
+
# The class is defined in the java part
|
74
|
+
def jdbc_connection_class(_spec)
|
75
|
+
::ActiveRecord::ConnectionAdapters::MSSQLJdbcConnection
|
76
|
+
end
|
77
|
+
|
78
|
+
# Returns the (JDBC) `ActiveRecord` column class for this adapter.
|
79
|
+
# Used in the java part.
|
80
|
+
def jdbc_column_class
|
81
|
+
::ActiveRecord::ConnectionAdapters::MSSQLColumn
|
82
|
+
end
|
83
|
+
|
84
|
+
# Can this adapter determine the primary key for tables not attached
|
85
|
+
# to an Active Record class, such as join tables?
|
86
|
+
def supports_primary_key?
|
87
|
+
true
|
88
|
+
end
|
89
|
+
|
90
|
+
# Does this adapter support creating foreign key constraints?
|
91
|
+
def supports_foreign_keys?
|
92
|
+
true
|
93
|
+
end
|
94
|
+
|
95
|
+
# Does this adapter support migrations?
|
96
|
+
def supports_migrations?
|
97
|
+
true
|
98
|
+
end
|
99
|
+
|
100
|
+
# Does this adapter support setting the isolation level for a transaction?
|
101
|
+
def supports_transaction_isolation?
|
102
|
+
true
|
103
|
+
end
|
104
|
+
|
105
|
+
# The MSSQL datetime type doe have precision.
|
106
|
+
def supports_datetime_with_precision?
|
107
|
+
true
|
108
|
+
end
|
109
|
+
|
110
|
+
# Overrides abstract method which always returns false
|
111
|
+
def valid_type?(type)
|
112
|
+
!native_database_types[type].nil?
|
113
|
+
end
|
114
|
+
|
115
|
+
# FIXME: to be reviewed.
|
116
|
+
def clear_cache!
|
117
|
+
reload_type_map
|
118
|
+
super
|
119
|
+
end
|
120
|
+
|
121
|
+
def disable_referential_integrity
|
122
|
+
tables = tables_with_referential_integrity
|
123
|
+
|
124
|
+
tables.each do |table_name|
|
125
|
+
execute "ALTER TABLE #{table_name} NOCHECK CONSTRAINT ALL"
|
126
|
+
end
|
127
|
+
yield
|
128
|
+
ensure
|
129
|
+
tables.each do |table_name|
|
130
|
+
execute "ALTER TABLE #{table_name} CHECK CONSTRAINT ALL"
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
# Overrides the method in abstract adapter to set the limit and offset
|
135
|
+
# in the right order. (SQLServer specific)
|
136
|
+
# Called by bound_attributes
|
137
|
+
def combine_bind_parameters(
|
138
|
+
from_clause: [],
|
139
|
+
join_clause: [],
|
140
|
+
where_clause: [],
|
141
|
+
having_clause: [],
|
142
|
+
limit: nil,
|
143
|
+
offset: nil
|
144
|
+
)
|
145
|
+
|
146
|
+
result = from_clause + join_clause + where_clause + having_clause
|
147
|
+
result << offset if offset
|
148
|
+
result << limit if limit
|
149
|
+
result
|
150
|
+
end
|
151
|
+
|
152
|
+
def arel_visitor # :nodoc:
|
153
|
+
::Arel::Visitors::SQLServer.new(self)
|
154
|
+
end
|
155
|
+
|
156
|
+
def schema_creation # :nodoc:
|
157
|
+
MSSQL::SchemaCreation.new(self)
|
158
|
+
end
|
159
|
+
|
160
|
+
def create_table_definition(*args) # :nodoc:
|
161
|
+
MSSQL::TableDefinition.new(*args)
|
162
|
+
end
|
163
|
+
|
164
|
+
def update_table_definition(table_name, base) #:nodoc:
|
165
|
+
MSSQL::Table.new(table_name, base)
|
166
|
+
end
|
167
|
+
|
168
|
+
# Returns the name of the current security context
|
169
|
+
def current_user
|
170
|
+
@current_user ||= select_value('SELECT CURRENT_USER')
|
171
|
+
end
|
172
|
+
|
173
|
+
# Returns the default schema (to be used for table resolution)
|
174
|
+
# used for the {#current_user}.
|
175
|
+
def default_schema
|
176
|
+
@default_schema ||= select_value('SELECT default_schema_name FROM sys.database_principals WHERE name = CURRENT_USER')
|
177
|
+
end
|
178
|
+
|
179
|
+
alias_method :current_schema, :default_schema
|
180
|
+
|
181
|
+
# Allows for changing of the default schema.
|
182
|
+
# (to be used during unqualified table name resolution).
|
183
|
+
def default_schema=(default_schema)
|
184
|
+
execute("ALTER #{current_user} WITH DEFAULT_SCHEMA=#{default_schema}")
|
185
|
+
@default_schema = nil if defined?(@default_schema)
|
186
|
+
end
|
187
|
+
|
188
|
+
alias_method :current_schema=, :default_schema=
|
189
|
+
|
190
|
+
# Overrides method in abstract adapter
|
191
|
+
def case_sensitive_comparison(table, attribute, column, value)
|
192
|
+
if value.nil?
|
193
|
+
table[attribute].eq(value)
|
194
|
+
elsif value.acts_like?(:string)
|
195
|
+
table[attribute].eq(Arel::Nodes::Bin.new(Arel::Nodes::BindParam.new))
|
196
|
+
else
|
197
|
+
table[attribute].eq(Arel::Nodes::BindParam.new)
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
def configure_connection
|
202
|
+
execute("SET LOCK_TIMEOUT #{lock_timeout}")
|
203
|
+
|
204
|
+
set_session_transaction_isolation
|
205
|
+
end
|
206
|
+
|
207
|
+
def lock_timeout
|
208
|
+
timeout = config[:lock_timeout].to_i
|
209
|
+
|
210
|
+
if timeout.positive?
|
211
|
+
timeout
|
212
|
+
else
|
213
|
+
5_000
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
def set_session_transaction_isolation
|
218
|
+
isolation_level = config[:transaction_isolation]
|
219
|
+
|
220
|
+
self.transaction_isolation = isolation_level if isolation_level
|
221
|
+
end
|
222
|
+
|
223
|
+
def mssql?
|
224
|
+
true
|
225
|
+
end
|
226
|
+
|
227
|
+
def mssql_major_version
|
228
|
+
return @mssql_major_version if defined? @mssql_major_version
|
229
|
+
|
230
|
+
@mssql_major_version = @connection.database_major_version
|
231
|
+
end
|
232
|
+
|
233
|
+
def mssql_version_year
|
234
|
+
MSSQL_VERSION_YEAR[mssql_major_version.to_i]
|
235
|
+
end
|
236
|
+
|
237
|
+
def tables_with_referential_integrity
|
238
|
+
schema_and_tables_sql = %(
|
239
|
+
SELECT s.name, o.name
|
240
|
+
FROM sys.foreign_keys i
|
241
|
+
INNER JOIN sys.objects o ON i.parent_object_id = o.OBJECT_ID
|
242
|
+
INNER JOIN sys.schemas s ON o.schema_id = s.schema_id
|
243
|
+
).squish
|
244
|
+
|
245
|
+
schemas_and_tables = select_rows(schema_and_tables_sql)
|
246
|
+
|
247
|
+
schemas_and_tables.map do |schema_table|
|
248
|
+
schema, table = schema_table
|
249
|
+
"#{quote_name_part(schema)}.#{quote_name_part(table)}"
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
protected
|
254
|
+
|
255
|
+
def translate_exception(e, message)
|
256
|
+
case message
|
257
|
+
when /(cannot insert duplicate key .* with unique index) | (violation of unique key constraint)/i
|
258
|
+
RecordNotUnique.new(message)
|
259
|
+
when /Lock request time out period exceeded/i
|
260
|
+
LockTimeout.new(message)
|
261
|
+
when /The .* statement conflicted with the FOREIGN KEY constraint/
|
262
|
+
ActiveRecord::InvalidForeignKey.new(message)
|
263
|
+
when /(String or binary data would be truncated)/i
|
264
|
+
ActiveRecord::ValueTooLong.new(message)
|
265
|
+
else
|
266
|
+
super
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
# This method is called indirectly by the abstract method
|
271
|
+
# 'fetch_type_metadata' which then it is called by the java part when
|
272
|
+
# calculating a table's columns.
|
273
|
+
def initialize_type_map(map)
|
274
|
+
# Build the type mapping from SQL Server to ActiveRecord
|
275
|
+
|
276
|
+
# Integer types.
|
277
|
+
map.register_type 'int', MSSQL::Type::Integer.new(limit: 4)
|
278
|
+
map.register_type 'tinyint', MSSQL::Type::TinyInteger.new(limit: 1)
|
279
|
+
map.register_type 'smallint', MSSQL::Type::SmallInteger.new(limit: 2)
|
280
|
+
map.register_type 'bigint', MSSQL::Type::BigInteger.new(limit: 8)
|
281
|
+
|
282
|
+
# Exact Numeric types.
|
283
|
+
map.register_type %r{\Adecimal}i do |sql_type|
|
284
|
+
scale = extract_scale(sql_type)
|
285
|
+
precision = extract_precision(sql_type)
|
286
|
+
if scale == 0
|
287
|
+
MSSQL::Type::DecimalWithoutScale.new(precision: precision)
|
288
|
+
else
|
289
|
+
MSSQL::Type::Decimal.new(precision: precision, scale: scale)
|
290
|
+
end
|
291
|
+
end
|
292
|
+
map.register_type %r{\Amoney\z}i, MSSQL::Type::Money.new
|
293
|
+
map.register_type %r{\Asmallmoney\z}i, MSSQL::Type::SmallMoney.new
|
294
|
+
|
295
|
+
# Approximate Numeric types.
|
296
|
+
map.register_type %r{\Afloat\z}i, MSSQL::Type::Float.new
|
297
|
+
map.register_type %r{\Areal\z}i, MSSQL::Type::Real.new
|
298
|
+
|
299
|
+
# Character strings CHAR and VARCHAR (it can become Unicode UTF-8)
|
300
|
+
map.register_type 'varchar(max)', MSSQL::Type::VarcharMax.new
|
301
|
+
map.register_type %r{\Avarchar\(\d+\)} do |sql_type|
|
302
|
+
limit = extract_limit(sql_type)
|
303
|
+
MSSQL::Type::Varchar.new(limit: limit)
|
304
|
+
end
|
305
|
+
map.register_type %r{\Achar\(\d+\)} do |sql_type|
|
306
|
+
limit = extract_limit(sql_type)
|
307
|
+
MSSQL::Type::Char.new(limit: limit)
|
308
|
+
end
|
309
|
+
|
310
|
+
# Character strings NCHAR and NVARCHAR (by default Unicode UTF-16)
|
311
|
+
map.register_type %r{\Anvarchar\(\d+\)} do |sql_type|
|
312
|
+
limit = extract_limit(sql_type)
|
313
|
+
MSSQL::Type::Nvarchar.new(limit: limit)
|
314
|
+
end
|
315
|
+
map.register_type %r{\Anchar\(\d+\)} do |sql_type|
|
316
|
+
limit = extract_limit(sql_type)
|
317
|
+
MSSQL::Type::Nchar.new(limit: limit)
|
318
|
+
end
|
319
|
+
map.register_type 'nvarchar(max)', MSSQL::Type::NvarcharMax.new
|
320
|
+
map.register_type 'nvarchar(4000)', MSSQL::Type::Nvarchar.new
|
321
|
+
|
322
|
+
# Binary data types.
|
323
|
+
map.register_type 'varbinary(max)', MSSQL::Type::VarbinaryMax.new
|
324
|
+
register_class_with_limit map, %r{\Abinary\(\d+\)}, MSSQL::Type::BinaryBasic
|
325
|
+
register_class_with_limit map, %r{\Avarbinary\(\d+\)}, MSSQL::Type::Varbinary
|
326
|
+
|
327
|
+
# Miscellaneous types, Boolean, XML, UUID
|
328
|
+
# FIXME The xml data needs to be reviewed and fixed
|
329
|
+
map.register_type 'bit', MSSQL::Type::Boolean.new
|
330
|
+
map.register_type %r{\Auniqueidentifier\z}i, MSSQL::Type::UUID.new
|
331
|
+
map.register_type %r{\Axml\z}i, MSSQL::Type::XML.new
|
332
|
+
|
333
|
+
# Date and time types
|
334
|
+
map.register_type 'date', MSSQL::Type::Date.new
|
335
|
+
map.register_type 'datetime', MSSQL::Type::DateTime.new
|
336
|
+
map.register_type 'smalldatetime', MSSQL::Type::SmallDateTime.new
|
337
|
+
register_class_with_precision map, %r{\Atime\(\d+\)}i, MSSQL::Type::Time
|
338
|
+
map.register_type 'time(7)', MSSQL::Type::Time.new
|
339
|
+
register_class_with_precision map, %r{\Adatetime2\(\d+\)}i, MSSQL::Type::DateTime2
|
340
|
+
map.register_type 'datetime2(7)', MSSQL::Type::DateTime2.new
|
341
|
+
|
342
|
+
# aliases
|
343
|
+
map.alias_type 'int identity', 'int'
|
344
|
+
map.alias_type 'bigint identity', 'bigint'
|
345
|
+
map.alias_type 'integer', 'int'
|
346
|
+
map.alias_type 'integer', 'int'
|
347
|
+
map.alias_type 'INTEGER', 'int'
|
348
|
+
map.alias_type 'TINYINT', 'tinyint'
|
349
|
+
map.alias_type 'SMALLINT', 'smallint'
|
350
|
+
map.alias_type 'BIGINT', 'bigint'
|
351
|
+
map.alias_type %r{\Anumeric}i, 'decimal'
|
352
|
+
map.alias_type %r{\Anumber}i, 'decimal'
|
353
|
+
map.alias_type %r{\Adouble\z}i, 'float'
|
354
|
+
map.alias_type 'string', 'nvarchar(4000)'
|
355
|
+
map.alias_type %r{\Aboolean\z}i, 'bit'
|
356
|
+
map.alias_type 'DATE', 'date'
|
357
|
+
map.alias_type 'DATETIME', 'datetime'
|
358
|
+
map.alias_type 'SMALLDATETIME', 'smalldatetime'
|
359
|
+
map.alias_type %r{\Atime\z}i, 'time(7)'
|
360
|
+
map.alias_type %r{\Abinary\z}i, 'varbinary(max)'
|
361
|
+
map.alias_type %r{\Ablob\z}i, 'varbinary(max)'
|
362
|
+
map.alias_type %r{\Adatetime2\z}i, 'datetime2(7)'
|
363
|
+
|
364
|
+
# Deprecated SQL Server types.
|
365
|
+
map.register_type 'text', MSSQL::Type::Text.new
|
366
|
+
map.register_type 'ntext', MSSQL::Type::Ntext.new
|
367
|
+
map.register_type 'image', MSSQL::Type::Image.new
|
368
|
+
end
|
369
|
+
end
|
370
|
+
end
|
371
|
+
end
|
372
|
+
|
373
|
+
# FIXME: this is not used by the adapter anymore, it is here because
|
374
|
+
# it is a dependency of old tests that needs to be reviewed
|
375
|
+
module ArJdbc
|
376
|
+
module MSSQL
|
377
|
+
require 'arjdbc/mssql/utils'
|
378
|
+
require 'arjdbc/mssql/limit_helpers'
|
379
|
+
require 'arjdbc/mssql/lock_methods'
|
380
|
+
|
381
|
+
include LimitHelpers
|
382
|
+
include Utils
|
383
|
+
end
|
384
|
+
end
|