activerecord-jdbc-alt-adapter 50.3.0-java
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.
- 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
|