activerecord-jdbc-adapter-ficoh 1.3.21-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 +462 -0
- data/.yardopts +4 -0
- data/Appraisals +36 -0
- data/CONTRIBUTING.md +49 -0
- data/Gemfile +68 -0
- data/History.md +1191 -0
- data/LICENSE.txt +25 -0
- data/README.md +277 -0
- data/RUNNING_TESTS.md +88 -0
- data/Rakefile +298 -0
- data/Rakefile.jdbc +20 -0
- data/activerecord-jdbc-adapter.gemspec +63 -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/oracle_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 +64 -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 +293 -0
- data/lib/arjdbc.rb +22 -0
- data/lib/arjdbc/db2.rb +4 -0
- data/lib/arjdbc/db2/adapter.rb +802 -0
- data/lib/arjdbc/db2/as400.rb +137 -0
- data/lib/arjdbc/db2/column.rb +177 -0
- data/lib/arjdbc/db2/connection_methods.rb +45 -0
- data/lib/arjdbc/derby.rb +3 -0
- data/lib/arjdbc/derby/active_record_patch.rb +13 -0
- data/lib/arjdbc/derby/adapter.rb +567 -0
- data/lib/arjdbc/derby/connection_methods.rb +16 -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 +468 -0
- data/lib/arjdbc/firebird/connection_methods.rb +20 -0
- data/lib/arjdbc/h2.rb +3 -0
- data/lib/arjdbc/h2/adapter.rb +335 -0
- data/lib/arjdbc/h2/connection_methods.rb +22 -0
- data/lib/arjdbc/hsqldb.rb +3 -0
- data/lib/arjdbc/hsqldb/adapter.rb +304 -0
- data/lib/arjdbc/hsqldb/connection_methods.rb +23 -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 +160 -0
- data/lib/arjdbc/informix/connection_methods.rb +9 -0
- data/lib/arjdbc/jdbc.rb +62 -0
- data/lib/arjdbc/jdbc/adapter.rb +997 -0
- data/lib/arjdbc/jdbc/adapter_require.rb +46 -0
- data/lib/arjdbc/jdbc/arel_support.rb +149 -0
- data/lib/arjdbc/jdbc/base_ext.rb +34 -0
- data/lib/arjdbc/jdbc/callbacks.rb +52 -0
- data/lib/arjdbc/jdbc/column.rb +83 -0
- data/lib/arjdbc/jdbc/connection.rb +26 -0
- data/lib/arjdbc/jdbc/connection_methods.rb +59 -0
- data/lib/arjdbc/jdbc/driver.rb +44 -0
- data/lib/arjdbc/jdbc/error.rb +75 -0
- data/lib/arjdbc/jdbc/extension.rb +69 -0
- data/lib/arjdbc/jdbc/java.rb +13 -0
- data/lib/arjdbc/jdbc/type_cast.rb +154 -0
- data/lib/arjdbc/jdbc/type_converter.rb +142 -0
- data/lib/arjdbc/mssql.rb +7 -0
- data/lib/arjdbc/mssql/adapter.rb +822 -0
- data/lib/arjdbc/mssql/column.rb +207 -0
- data/lib/arjdbc/mssql/connection_methods.rb +72 -0
- data/lib/arjdbc/mssql/explain_support.rb +99 -0
- data/lib/arjdbc/mssql/limit_helpers.rb +231 -0
- data/lib/arjdbc/mssql/lock_methods.rb +77 -0
- data/lib/arjdbc/mssql/types.rb +343 -0
- data/lib/arjdbc/mssql/utils.rb +82 -0
- data/lib/arjdbc/mysql.rb +3 -0
- data/lib/arjdbc/mysql/adapter.rb +998 -0
- data/lib/arjdbc/mysql/bulk_change_table.rb +150 -0
- data/lib/arjdbc/mysql/column.rb +167 -0
- data/lib/arjdbc/mysql/connection_methods.rb +137 -0
- data/lib/arjdbc/mysql/explain_support.rb +82 -0
- data/lib/arjdbc/mysql/schema_creation.rb +58 -0
- data/lib/arjdbc/oracle.rb +4 -0
- data/lib/arjdbc/oracle/adapter.rb +968 -0
- data/lib/arjdbc/oracle/column.rb +136 -0
- data/lib/arjdbc/oracle/connection_methods.rb +21 -0
- data/lib/arjdbc/postgresql.rb +3 -0
- data/lib/arjdbc/postgresql/_bc_time_cast_patch.rb +21 -0
- data/lib/arjdbc/postgresql/adapter.rb +1498 -0
- data/lib/arjdbc/postgresql/base/array_parser.rb +95 -0
- data/lib/arjdbc/postgresql/base/oid.rb +412 -0
- data/lib/arjdbc/postgresql/base/pgconn.rb +8 -0
- data/lib/arjdbc/postgresql/base/schema_definitions.rb +132 -0
- data/lib/arjdbc/postgresql/column.rb +640 -0
- data/lib/arjdbc/postgresql/connection_methods.rb +44 -0
- data/lib/arjdbc/postgresql/explain_support.rb +53 -0
- data/lib/arjdbc/postgresql/oid/bytea.rb +3 -0
- data/lib/arjdbc/postgresql/oid_types.rb +265 -0
- data/lib/arjdbc/postgresql/schema_creation.rb +60 -0
- data/lib/arjdbc/railtie.rb +11 -0
- data/lib/arjdbc/sqlite3.rb +3 -0
- data/lib/arjdbc/sqlite3/adapter.rb +654 -0
- data/lib/arjdbc/sqlite3/connection_methods.rb +36 -0
- data/lib/arjdbc/sqlite3/explain_support.rb +29 -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 +66 -0
- data/lib/arjdbc/tasks/databases.rake +91 -0
- data/lib/arjdbc/tasks/databases3.rake +239 -0
- data/lib/arjdbc/tasks/databases4.rake +39 -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/tasks/oracle/enhanced_structure_dump.rb +297 -0
- data/lib/arjdbc/tasks/oracle_database_tasks.rb +65 -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 +108 -0
- data/lib/arjdbc/version.rb +8 -0
- data/lib/generators/jdbc/USAGE +9 -0
- data/lib/generators/jdbc/jdbc_generator.rb +17 -0
- data/pom.xml +285 -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 +151 -0
- data/rakelib/bundler_ext.rb +11 -0
- data/rakelib/db.rake +58 -0
- data/rakelib/rails.rake +77 -0
- data/src/java/arjdbc/ArJdbcModule.java +288 -0
- data/src/java/arjdbc/db2/DB2Module.java +77 -0
- data/src/java/arjdbc/db2/DB2RubyJdbcConnection.java +128 -0
- data/src/java/arjdbc/derby/DerbyModule.java +180 -0
- data/src/java/arjdbc/derby/DerbyRubyJdbcConnection.java +153 -0
- data/src/java/arjdbc/firebird/FirebirdRubyJdbcConnection.java +190 -0
- data/src/java/arjdbc/h2/H2Module.java +50 -0
- data/src/java/arjdbc/h2/H2RubyJdbcConnection.java +86 -0
- data/src/java/arjdbc/hsqldb/HSQLDBModule.java +74 -0
- data/src/java/arjdbc/informix/InformixRubyJdbcConnection.java +76 -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 +77 -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 +128 -0
- data/src/java/arjdbc/jdbc/JdbcConnectionFactory.java +32 -0
- data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +4541 -0
- data/src/java/arjdbc/jdbc/SQLBlock.java +54 -0
- data/src/java/arjdbc/jdbc/WithResultSet.java +37 -0
- data/src/java/arjdbc/mssql/MSSQLModule.java +91 -0
- data/src/java/arjdbc/mssql/MSSQLRubyJdbcConnection.java +193 -0
- data/src/java/arjdbc/mysql/MySQLModule.java +140 -0
- data/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +456 -0
- data/src/java/arjdbc/oracle/OracleModule.java +81 -0
- data/src/java/arjdbc/oracle/OracleRubyJdbcConnection.java +477 -0
- data/src/java/arjdbc/postgresql/ByteaUtils.java +171 -0
- data/src/java/arjdbc/postgresql/DriverImplementation.java +78 -0
- data/src/java/arjdbc/postgresql/PGDriverImplementation.java +535 -0
- data/src/java/arjdbc/postgresql/PostgreSQLModule.java +189 -0
- data/src/java/arjdbc/postgresql/PostgreSQLRubyJdbcConnection.java +489 -0
- data/src/java/arjdbc/sqlite3/SQLite3Module.java +93 -0
- data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +405 -0
- data/src/java/arjdbc/util/CallResultSet.java +826 -0
- data/src/java/arjdbc/util/DateTimeUtils.java +517 -0
- data/src/java/arjdbc/util/NumberUtils.java +50 -0
- data/src/java/arjdbc/util/ObjectSupport.java +65 -0
- data/src/java/arjdbc/util/QuotingUtils.java +139 -0
- data/src/java/arjdbc/util/StringCache.java +60 -0
- data/src/java/arjdbc/util/StringHelper.java +155 -0
- metadata +288 -0
@@ -0,0 +1,207 @@
|
|
1
|
+
module ArJdbc
|
2
|
+
module MSSQL
|
3
|
+
|
4
|
+
# @see ActiveRecord::ConnectionAdapters::JdbcColumn#column_types
|
5
|
+
def self.column_selector
|
6
|
+
[ /sqlserver|tds|Microsoft SQL/i, lambda { |config, column| column.extend(ColumnMethods) } ]
|
7
|
+
end
|
8
|
+
|
9
|
+
# @private
|
10
|
+
# @see ActiveRecord::ConnectionAdapters::JdbcColumn
|
11
|
+
module ColumnMethods
|
12
|
+
|
13
|
+
def self.included(base)
|
14
|
+
# NOTE: assumes a standalone MSSQLColumn class
|
15
|
+
class << base; include Cast; end
|
16
|
+
end
|
17
|
+
|
18
|
+
include LockMethods unless AR42
|
19
|
+
|
20
|
+
# @override
|
21
|
+
def simplified_type(field_type)
|
22
|
+
case field_type
|
23
|
+
when /int|bigint|smallint|tinyint/i then :integer
|
24
|
+
when /numeric/i then (@scale.nil? || @scale == 0) ? :integer : :decimal
|
25
|
+
when /float|double|money|real|smallmoney/i then :decimal
|
26
|
+
when /datetime|smalldatetime/i then :datetime
|
27
|
+
when /timestamp/i then :timestamp
|
28
|
+
when /time/i then :time
|
29
|
+
when /date/i then :date
|
30
|
+
when /text|ntext|xml/i then :text
|
31
|
+
when /binary|image|varbinary/i then :binary
|
32
|
+
when /char|nchar|nvarchar|string|varchar/i then (@limit == 1073741823 ? (@limit = nil; :text) : :string)
|
33
|
+
when /bit/i then :boolean
|
34
|
+
when /uniqueidentifier/i then :string
|
35
|
+
else
|
36
|
+
super
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# @override
|
41
|
+
def default_value(value)
|
42
|
+
return $1 if value =~ /^\(N?'(.*)'\)$/ || value =~ /^\(\(?(.*?)\)?\)$/
|
43
|
+
value
|
44
|
+
end
|
45
|
+
|
46
|
+
# @override
|
47
|
+
def type_cast(value)
|
48
|
+
return nil if value.nil?
|
49
|
+
case type
|
50
|
+
when :integer then ( value.is_a?(String) ? unquote(value) : (value || 0) ).to_i
|
51
|
+
when :primary_key then value.respond_to?(:to_i) ? value.to_i : ((value && 1) || 0)
|
52
|
+
when :decimal then self.class.value_to_decimal(unquote(value))
|
53
|
+
when :date then self.class.string_to_date(value)
|
54
|
+
when :datetime then self.class.string_to_time(value)
|
55
|
+
when :timestamp then self.class.string_to_time(value)
|
56
|
+
when :time then self.class.string_to_dummy_time(value)
|
57
|
+
when :boolean then value == true || (value =~ /^t(rue)?$/i) == 0 || unquote(value) == '1'
|
58
|
+
when :binary then unquote(value)
|
59
|
+
when :string then (value == 'null' ? '' : value)
|
60
|
+
when :text then (value == 'null' ? '' : value)
|
61
|
+
else value
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# @override
|
66
|
+
def extract_limit(sql_type)
|
67
|
+
case sql_type
|
68
|
+
when /^smallint/i
|
69
|
+
2
|
70
|
+
when /^int/i
|
71
|
+
4
|
72
|
+
when /^bigint/i
|
73
|
+
8
|
74
|
+
when /\(max\)/, /decimal/, /numeric/
|
75
|
+
nil
|
76
|
+
when /text|ntext|xml|binary|image|varbinary|bit/
|
77
|
+
nil
|
78
|
+
else
|
79
|
+
super
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# #primary replacement that works on 4.2 as well
|
84
|
+
# #columns will set @primary even when on AR 4.2
|
85
|
+
def primary?; @primary end
|
86
|
+
alias_method :is_primary, :primary?
|
87
|
+
|
88
|
+
def identity?
|
89
|
+
!! sql_type.downcase.index('identity')
|
90
|
+
end
|
91
|
+
# @deprecated
|
92
|
+
alias_method :identity, :identity?
|
93
|
+
alias_method :is_identity, :identity?
|
94
|
+
|
95
|
+
# NOTE: these do not handle = equality as expected
|
96
|
+
# see {#repair_special_columns}
|
97
|
+
# (TEXT, NTEXT, and IMAGE data types are deprecated)
|
98
|
+
# @private
|
99
|
+
def special?
|
100
|
+
unless defined? @special # /text|ntext|image|xml/i
|
101
|
+
sql_type = @sql_type.downcase
|
102
|
+
@special = !! ( sql_type.index('text') || sql_type.index('image') || sql_type.index('xml') )
|
103
|
+
end
|
104
|
+
@special
|
105
|
+
end
|
106
|
+
# @deprecated
|
107
|
+
alias_method :special, :special?
|
108
|
+
alias_method :is_special, :special?
|
109
|
+
|
110
|
+
def is_utf8?
|
111
|
+
!!( sql_type =~ /nvarchar|ntext|nchar/i )
|
112
|
+
end
|
113
|
+
|
114
|
+
private
|
115
|
+
|
116
|
+
def unquote(value)
|
117
|
+
value.to_s.sub(/\A\([\(\']?/, "").sub(/[\'\)]?\)\Z/, "")
|
118
|
+
end
|
119
|
+
|
120
|
+
module Cast
|
121
|
+
|
122
|
+
def string_to_date(value)
|
123
|
+
return value unless value.is_a?(String)
|
124
|
+
return nil if value.empty?
|
125
|
+
|
126
|
+
if value.include? "/"
|
127
|
+
date = Date.strptime(value, "%m/%d/%Y")
|
128
|
+
else
|
129
|
+
date = fast_string_to_date(value)
|
130
|
+
date ? date : Date.parse(value) rescue nil
|
131
|
+
end
|
132
|
+
|
133
|
+
end
|
134
|
+
|
135
|
+
def string_to_time(value)
|
136
|
+
return value unless value.is_a?(String)
|
137
|
+
return nil if value.empty?
|
138
|
+
|
139
|
+
if value.include? "/"
|
140
|
+
result = DateTime.strptime(value+' '+Time.now.zone, "%m/%d/%Y %Z")
|
141
|
+
elsif value =~ ActiveRecord::ConnectionAdapters::Column::Format::ISO_DATE
|
142
|
+
result = DateTime.parse(value+'T00:00:00'+Time.now.formatted_offset)
|
143
|
+
elsif value =~ ActiveRecord::ConnectionAdapters::Column::Format::ISO_DATETIME
|
144
|
+
result = fast_string_to_time(value)
|
145
|
+
else
|
146
|
+
#It looks like there is a bug in the datetime string logic that lives upstream of this method
|
147
|
+
#getting called. The fractional seconds are getting thrown out of values stored in the db such
|
148
|
+
#as:
|
149
|
+
#2016-12-24 11:09:43.097 (by examination in db visualizer)
|
150
|
+
#which end up as (by the time the value gets to this method):
|
151
|
+
#2016-12-24 11:09:43.
|
152
|
+
#without the zero the ISO_DATETIME format check fails and we are getting conversion done
|
153
|
+
#by the DateTime.parse which ignores timezone. This ends up returning dates erroneously.
|
154
|
+
#It is tempting to make the else condition below raise an exception because we really don't
|
155
|
+
#want timezone to be ignored in any situation. My level of confidence is not high enough to
|
156
|
+
#change now.
|
157
|
+
check_missing_zero = value + '0'
|
158
|
+
if check_missing_zero =~ ActiveRecord::ConnectionAdapters::Column::Format::ISO_DATETIME
|
159
|
+
result = fast_string_to_time(check_missing_zero)
|
160
|
+
else
|
161
|
+
result = DateTime.parse(value).to_time rescue nil
|
162
|
+
end
|
163
|
+
end
|
164
|
+
result
|
165
|
+
end
|
166
|
+
|
167
|
+
ISO_TIME = /\A(\d\d)\:(\d\d)\:(\d\d)(\.\d+)?\z/
|
168
|
+
|
169
|
+
def string_to_dummy_time(value)
|
170
|
+
return value unless value.is_a?(String)
|
171
|
+
return nil if value.empty?
|
172
|
+
|
173
|
+
if value =~ ISO_TIME # "12:34:56.1234560"
|
174
|
+
microsec = ($4.to_f * 1_000_000).round.to_i
|
175
|
+
new_time 2000, 1, 1, $1.to_i, $2.to_i, $3.to_i, microsec
|
176
|
+
else
|
177
|
+
super(value)
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
def string_to_binary(value)
|
182
|
+
# this will only allow the adapter to insert binary data with a length
|
183
|
+
# of 7K or less because of a SQL Server statement length policy ...
|
184
|
+
"0x#{value.unpack("H*")}" # "0x#{value.unpack("H*")[0]}"
|
185
|
+
end
|
186
|
+
|
187
|
+
def binary_to_string(value)
|
188
|
+
if value.respond_to?(:force_encoding) && value.encoding != Encoding::ASCII_8BIT
|
189
|
+
value = value.force_encoding(Encoding::ASCII_8BIT)
|
190
|
+
end
|
191
|
+
value =~ /[^[:xdigit:]]/ ? value : [value].pack('H*')
|
192
|
+
end
|
193
|
+
|
194
|
+
end
|
195
|
+
|
196
|
+
end
|
197
|
+
|
198
|
+
def self.const_missing(name)
|
199
|
+
if name.to_sym == :Column
|
200
|
+
ArJdbc.deprecate("#{self.name}::Column will change to refer to the actual column class, please use ColumnMethods instead", :once)
|
201
|
+
return ColumnMethods
|
202
|
+
end
|
203
|
+
super
|
204
|
+
end
|
205
|
+
|
206
|
+
end
|
207
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
ArJdbc::ConnectionMethods.module_eval do
|
2
|
+
|
3
|
+
# Default connection method for MS-SQL adapter (`adapter: mssql`),
|
4
|
+
# uses the (open-source) jTDS driver.
|
5
|
+
# If you'd like to use the "official" MS's SQL-JDBC driver, it's preferable
|
6
|
+
# to use the {#sqlserver_connection} method (set `adapter: sqlserver`).
|
7
|
+
def mssql_connection(config)
|
8
|
+
# NOTE: this detection ain't perfect and is only meant as a temporary hack
|
9
|
+
# users will get a deprecation eventually to use `adapter: sqlserver` ...
|
10
|
+
if config[:driver] =~ /SQLServerDriver$/ || config[:url] =~ /^jdbc:sqlserver:/
|
11
|
+
return sqlserver_connection(config)
|
12
|
+
end
|
13
|
+
|
14
|
+
config[:adapter_spec] ||= ::ArJdbc::MSSQL
|
15
|
+
config[:adapter_class] = ActiveRecord::ConnectionAdapters::MSSQLAdapter unless config.key?(:adapter_class)
|
16
|
+
|
17
|
+
return jndi_connection(config) if jndi_config?(config)
|
18
|
+
|
19
|
+
ArJdbc.load_driver(:JTDS) unless config[:load_driver] == false
|
20
|
+
|
21
|
+
config[:host] ||= 'localhost'
|
22
|
+
config[:port] ||= 1433
|
23
|
+
config[:driver] ||= 'net.sourceforge.jtds.jdbc.Driver'
|
24
|
+
config[:connection_alive_sql] ||= 'SELECT 1'
|
25
|
+
|
26
|
+
config[:url] ||= begin
|
27
|
+
url = "jdbc:jtds:sqlserver://#{config[:host]}:#{config[:port]}/#{config[:database]}"
|
28
|
+
# Instance is often a preferrable alternative to port when dynamic ports are used.
|
29
|
+
# If instance is specified then port is essentially ignored.
|
30
|
+
url << ";instance=#{config[:instance]}" if config[:instance]
|
31
|
+
# This will enable windows domain-based authentication and will require the JTDS native libraries be available.
|
32
|
+
url << ";domain=#{config[:domain]}" if config[:domain]
|
33
|
+
# AppName is shown in sql server as additional information against the connection.
|
34
|
+
url << ";appname=#{config[:appname]}" if config[:appname]
|
35
|
+
url
|
36
|
+
end
|
37
|
+
|
38
|
+
unless config[:domain]
|
39
|
+
config[:username] ||= 'sa'
|
40
|
+
config[:password] ||= ''
|
41
|
+
end
|
42
|
+
jdbc_connection(config)
|
43
|
+
end
|
44
|
+
alias_method :jdbcmssql_connection, :mssql_connection
|
45
|
+
|
46
|
+
# @note Assumes SQLServer SQL-JDBC driver on the class-path.
|
47
|
+
def sqlserver_connection(config)
|
48
|
+
config[:adapter_spec] ||= ::ArJdbc::MSSQL
|
49
|
+
config[:adapter_class] = ActiveRecord::ConnectionAdapters::MSSQLAdapter unless config.key?(:adapter_class)
|
50
|
+
|
51
|
+
return jndi_connection(config) if jndi_config?(config)
|
52
|
+
|
53
|
+
config[:host] ||= 'localhost'
|
54
|
+
config[:driver] ||= 'com.microsoft.sqlserver.jdbc.SQLServerDriver'
|
55
|
+
config[:connection_alive_sql] ||= 'SELECT 1'
|
56
|
+
|
57
|
+
config[:url] ||= begin
|
58
|
+
url = "jdbc:sqlserver://#{config[:host]}"
|
59
|
+
url << ( config[:port] ? ":#{config[:port]};" : ';' )
|
60
|
+
url << "databaseName=#{config[:database]};" if config[:database]
|
61
|
+
url << "instanceName=#{config[:instance]};" if config[:instance]
|
62
|
+
app = config[:appname] || config[:application]
|
63
|
+
url << "applicationName=#{app};" if app
|
64
|
+
isc = config[:integrated_security] # Win only - needs sqljdbc_auth.dll
|
65
|
+
url << "integratedSecurity=#{isc};" unless isc.nil?
|
66
|
+
url
|
67
|
+
end
|
68
|
+
jdbc_connection(config)
|
69
|
+
end
|
70
|
+
alias_method :jdbcsqlserver_connection, :sqlserver_connection
|
71
|
+
|
72
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require 'active_support/core_ext/string'
|
2
|
+
|
3
|
+
module ArJdbc
|
4
|
+
module MSSQL
|
5
|
+
module ExplainSupport
|
6
|
+
|
7
|
+
DISABLED = Java::JavaLang::Boolean.getBoolean('arjdbc.mssql.explain_support.disabled')
|
8
|
+
|
9
|
+
def supports_explain?; ! DISABLED; end
|
10
|
+
|
11
|
+
def explain(arel, binds = [])
|
12
|
+
return if DISABLED
|
13
|
+
sql = to_sql(arel, binds)
|
14
|
+
result = with_showplan_on { exec_query(sql, 'EXPLAIN', binds) }
|
15
|
+
PrinterTable.new(result).pp
|
16
|
+
end
|
17
|
+
|
18
|
+
protected
|
19
|
+
|
20
|
+
def with_showplan_on
|
21
|
+
set_showplan_option(true)
|
22
|
+
yield
|
23
|
+
ensure
|
24
|
+
set_showplan_option(false)
|
25
|
+
end
|
26
|
+
|
27
|
+
def set_showplan_option(enable = true)
|
28
|
+
option = 'SHOWPLAN_TEXT'
|
29
|
+
execute "SET #{option} #{enable ? 'ON' : 'OFF'}"
|
30
|
+
rescue Exception => e
|
31
|
+
raise ActiveRecord::ActiveRecordError, "#{option} could not be turned" +
|
32
|
+
" #{enable ? 'ON' : 'OFF'} (check SHOWPLAN permissions) due : #{e.inspect}"
|
33
|
+
end
|
34
|
+
|
35
|
+
# @private
|
36
|
+
class PrinterTable
|
37
|
+
|
38
|
+
cattr_accessor :max_column_width, :cell_padding
|
39
|
+
self.max_column_width = 50
|
40
|
+
self.cell_padding = 1
|
41
|
+
|
42
|
+
attr_reader :result
|
43
|
+
|
44
|
+
def initialize(result)
|
45
|
+
@result = result
|
46
|
+
end
|
47
|
+
|
48
|
+
def pp
|
49
|
+
@widths = compute_column_widths
|
50
|
+
@separator = build_separator
|
51
|
+
pp = []
|
52
|
+
pp << @separator
|
53
|
+
pp << build_cells(result.columns)
|
54
|
+
pp << @separator
|
55
|
+
result.rows.each do |row|
|
56
|
+
pp << build_cells(row)
|
57
|
+
end
|
58
|
+
pp << @separator
|
59
|
+
pp.join("\n") << "\n"
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def compute_column_widths
|
65
|
+
[].tap do |computed_widths|
|
66
|
+
result.columns.each_with_index do |column, i|
|
67
|
+
cells_in_column = [column] + result.rows.map { |r| cast_item(r[i]) }
|
68
|
+
computed_width = cells_in_column.map(&:length).max
|
69
|
+
final_width = computed_width > max_column_width ? max_column_width : computed_width
|
70
|
+
computed_widths << final_width
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def build_separator
|
76
|
+
'+' << @widths.map {|w| '-' * (w + (cell_padding * 2))}.join('+') << '+'
|
77
|
+
end
|
78
|
+
|
79
|
+
def build_cells(items)
|
80
|
+
cells = []
|
81
|
+
items.each_with_index do |item, i|
|
82
|
+
cells << cast_item(item).ljust(@widths[i])
|
83
|
+
end
|
84
|
+
"| #{cells.join(' | ')} |"
|
85
|
+
end
|
86
|
+
|
87
|
+
def cast_item(item)
|
88
|
+
case item
|
89
|
+
when NilClass then 'NULL'
|
90
|
+
when Float then item.to_s.to(9)
|
91
|
+
else item.to_s.truncate(max_column_width)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,231 @@
|
|
1
|
+
module ArJdbc
|
2
|
+
module MSSQL
|
3
|
+
module LimitHelpers
|
4
|
+
|
5
|
+
# @private
|
6
|
+
FIND_SELECT = /\b(SELECT(\s+DISTINCT)?)\b(.*)/mi
|
7
|
+
# @private
|
8
|
+
FIND_AGGREGATE_FUNCTION = /(AVG|COUNT|COUNT_BIG|MAX|MIN|SUM|STDDEV|STDEVP|VAR|VARP)\(/i
|
9
|
+
|
10
|
+
# @private
|
11
|
+
module SqlServerReplaceLimitOffset
|
12
|
+
|
13
|
+
GROUP_BY = 'GROUP BY'
|
14
|
+
ORDER_BY = 'ORDER BY'
|
15
|
+
|
16
|
+
module_function
|
17
|
+
|
18
|
+
def replace_limit_offset!(sql, limit, offset, order)
|
19
|
+
offset ||= 0
|
20
|
+
|
21
|
+
if match = FIND_SELECT.match(sql)
|
22
|
+
select, distinct, rest_of_query = match[1], match[2], match[3]
|
23
|
+
rest_of_query.strip!
|
24
|
+
end
|
25
|
+
rest_of_query[0] = '*' if rest_of_query[0...1] == '1' && rest_of_query !~ /1 AS/i
|
26
|
+
if rest_of_query[0...1] == '*'
|
27
|
+
from_table = Utils.get_table_name(rest_of_query, true)
|
28
|
+
rest_of_query = "#{from_table}.#{rest_of_query}"
|
29
|
+
end
|
30
|
+
|
31
|
+
# Ensure correct queries if the rest_of_query contains a 'GROUP BY'. Otherwise the following error occurs:
|
32
|
+
# ActiveRecord::StatementInvalid: ActiveRecord::JDBCError: Column 'users.id' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
|
33
|
+
# SELECT t.* FROM ( SELECT ROW_NUMBER() OVER(ORDER BY users.id) AS _row_num, [users].[lft], COUNT([users].[lft]) FROM [users] GROUP BY [users].[lft] HAVING COUNT([users].[lft]) > 1 ) AS t WHERE t._row_num BETWEEN 1 AND 1
|
34
|
+
if i = ( rest_of_query.rindex(GROUP_BY) || rest_of_query.rindex('group by') )
|
35
|
+
# Do not catch 'GROUP BY' statements from sub-selects, indicated
|
36
|
+
# by more closing than opening brackets after the last group by.
|
37
|
+
rest_after_last_group_by = rest_of_query[i..-1]
|
38
|
+
opening_brackets_count = rest_after_last_group_by.count('(')
|
39
|
+
closing_brackets_count = rest_after_last_group_by.count(')')
|
40
|
+
|
41
|
+
if opening_brackets_count == closing_brackets_count
|
42
|
+
order_start = order.strip[0, 8]; order_start.upcase!
|
43
|
+
if order_start == ORDER_BY && order.match(FIND_AGGREGATE_FUNCTION)
|
44
|
+
# do nothing
|
45
|
+
elsif order.count(',') == 0
|
46
|
+
order.gsub!(/ORDER +BY +([^\s]+)(\s+ASC|\s+DESC)?/i, 'ORDER BY MIN(\1)\2')
|
47
|
+
else
|
48
|
+
raise("can not handle multiple order conditions (#{order.inspect}) in #{sql.inspect}")
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
if distinct # select =~ /DISTINCT/i
|
54
|
+
order = order.gsub(/(\[[a-z0-9_]+\]|[a-z0-9_]+)\./, 't.')
|
55
|
+
new_sql = "SELECT t.* FROM "
|
56
|
+
new_sql << "( SELECT ROW_NUMBER() OVER(#{order}) AS _row_num, t.* FROM (#{select} #{rest_of_query}) AS t ) AS t"
|
57
|
+
append_limit_row_num_clause(new_sql, limit, offset)
|
58
|
+
else
|
59
|
+
select_columns_before_from = rest_of_query.gsub(/FROM.*/, '').strip
|
60
|
+
only_one_column = !select_columns_before_from.include?(',')
|
61
|
+
only_one_id_column = only_one_column && (select_columns_before_from.ends_with?('.id') || select_columns_before_from.ends_with?('.[id]'))
|
62
|
+
|
63
|
+
if only_one_id_column
|
64
|
+
# If there's only one id column a subquery will be created which only contains this column
|
65
|
+
new_sql = "#{select} t.id FROM "
|
66
|
+
else
|
67
|
+
# All selected columns are used
|
68
|
+
new_sql = "#{select} t.* FROM "
|
69
|
+
end
|
70
|
+
new_sql << "( SELECT ROW_NUMBER() OVER(#{order}) AS _row_num, #{rest_of_query} ) AS t"
|
71
|
+
append_limit_row_num_clause(new_sql, limit, offset)
|
72
|
+
end
|
73
|
+
|
74
|
+
sql.replace new_sql
|
75
|
+
end
|
76
|
+
|
77
|
+
def append_limit_row_num_clause(sql, limit, offset)
|
78
|
+
if limit
|
79
|
+
start_row = offset + 1; end_row = offset + limit.to_i
|
80
|
+
sql << " WHERE t._row_num BETWEEN #{start_row} AND #{end_row}"
|
81
|
+
else
|
82
|
+
sql << " WHERE t._row_num > #{offset}"
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
# @private
|
89
|
+
module SqlServer2000ReplaceLimitOffset
|
90
|
+
|
91
|
+
module_function
|
92
|
+
|
93
|
+
def replace_limit_offset!(sql, limit, offset, order)
|
94
|
+
if limit
|
95
|
+
offset ||= 0
|
96
|
+
start_row = offset + 1
|
97
|
+
end_row = offset + limit.to_i
|
98
|
+
|
99
|
+
if match = FIND_SELECT.match(sql)
|
100
|
+
select, distinct, rest_of_query = match[1], match[2], match[3]
|
101
|
+
end
|
102
|
+
#need the table name for avoiding amiguity
|
103
|
+
table_name = Utils.get_table_name(sql, true)
|
104
|
+
primary_key = get_primary_key(order, table_name)
|
105
|
+
|
106
|
+
#I am not sure this will cover all bases. but all the tests pass
|
107
|
+
if order[/ORDER/].nil?
|
108
|
+
new_order = "ORDER BY #{order}, [#{table_name}].[#{primary_key}]" if order.index("#{table_name}.#{primary_key}").nil?
|
109
|
+
else
|
110
|
+
new_order ||= order
|
111
|
+
end
|
112
|
+
|
113
|
+
if (start_row == 1) && (end_row ==1)
|
114
|
+
new_sql = "#{select} TOP 1 #{rest_of_query} #{new_order}"
|
115
|
+
sql.replace(new_sql)
|
116
|
+
else
|
117
|
+
# We are in deep trouble here. SQL Server does not have any kind of OFFSET build in.
|
118
|
+
# Only remaining solution is adding a where condition to be sure that the ID is not in SELECT TOP OFFSET FROM SAME_QUERY.
|
119
|
+
# To do so we need to extract each part of the query to insert our additional condition in the right place.
|
120
|
+
query_without_select = rest_of_query[/FROM/i=~ rest_of_query.. -1]
|
121
|
+
additional_condition = "#{table_name}.#{primary_key} NOT IN (#{select} TOP #{offset} #{table_name}.#{primary_key} #{query_without_select} #{new_order})"
|
122
|
+
|
123
|
+
# Extract the different parts of the query
|
124
|
+
having, group_by, where, from, selection = split_sql(rest_of_query, /having/i, /group by/i, /where/i, /from/i)
|
125
|
+
|
126
|
+
# Update the where part to add our additional condition
|
127
|
+
if where.blank?
|
128
|
+
where = "WHERE #{additional_condition}"
|
129
|
+
else
|
130
|
+
where = "#{where} AND #{additional_condition}"
|
131
|
+
end
|
132
|
+
|
133
|
+
# Replace the query to be our new customized query
|
134
|
+
sql.replace("#{select} TOP #{limit} #{selection} #{from} #{where} #{group_by} #{having} #{new_order}")
|
135
|
+
end
|
136
|
+
end
|
137
|
+
sql
|
138
|
+
end
|
139
|
+
|
140
|
+
# Split the rest_of_query into chunks based on regexs (applied from end of string to the beginning)
|
141
|
+
# The result is an array of regexs.size+1 elements (the last one being the remaining once everything was chopped away)
|
142
|
+
def split_sql(rest_of_query, *regexs)
|
143
|
+
results = Array.new
|
144
|
+
|
145
|
+
regexs.each do |regex|
|
146
|
+
if position = (regex =~ rest_of_query)
|
147
|
+
# Extract the matched string and chop the rest_of_query
|
148
|
+
matched = rest_of_query[position..-1]
|
149
|
+
rest_of_query = rest_of_query[0...position]
|
150
|
+
else
|
151
|
+
matched = nil
|
152
|
+
end
|
153
|
+
|
154
|
+
results << matched
|
155
|
+
end
|
156
|
+
results << rest_of_query
|
157
|
+
|
158
|
+
results
|
159
|
+
end
|
160
|
+
|
161
|
+
def get_primary_key(order, table_name) # table_name might be quoted
|
162
|
+
if order =~ /(\w*id\w*)/i
|
163
|
+
$1
|
164
|
+
else
|
165
|
+
unquoted_name = Utils.unquote_table_name(table_name)
|
166
|
+
model = descendants.find { |m| m.table_name == table_name || m.table_name == unquoted_name }
|
167
|
+
model ? model.primary_key : 'id'
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
private
|
172
|
+
|
173
|
+
if ActiveRecord::VERSION::MAJOR >= 3
|
174
|
+
def descendants; ::ActiveRecord::Base.descendants; end
|
175
|
+
else
|
176
|
+
def descendants; ::ActiveRecord::Base.send(:subclasses) end
|
177
|
+
end
|
178
|
+
|
179
|
+
end
|
180
|
+
|
181
|
+
private
|
182
|
+
|
183
|
+
if ::ActiveRecord::VERSION::MAJOR < 3
|
184
|
+
|
185
|
+
def setup_limit_offset!(version = nil)
|
186
|
+
if version.to_s == '2000' || sqlserver_2000?
|
187
|
+
extend SqlServer2000AddLimitOffset
|
188
|
+
else
|
189
|
+
extend SqlServerAddLimitOffset
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
else
|
194
|
+
|
195
|
+
def setup_limit_offset!(version = nil); end
|
196
|
+
|
197
|
+
end
|
198
|
+
|
199
|
+
# @private
|
200
|
+
module SqlServerAddLimitOffset
|
201
|
+
|
202
|
+
# @note Only needed with (non-AREL) ActiveRecord **2.3**.
|
203
|
+
# @see Arel::Visitors::SQLServer
|
204
|
+
def add_limit_offset!(sql, options)
|
205
|
+
if options[:limit]
|
206
|
+
order = "ORDER BY #{options[:order] || determine_order_clause(sql)}"
|
207
|
+
sql.sub!(/ ORDER BY.*$/i, '')
|
208
|
+
SqlServerReplaceLimitOffset.replace_limit_offset!(sql, options[:limit], options[:offset], order)
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
end if ::ActiveRecord::VERSION::MAJOR < 3
|
213
|
+
|
214
|
+
# @private
|
215
|
+
module SqlServer2000AddLimitOffset
|
216
|
+
|
217
|
+
# @note Only needed with (non-AREL) ActiveRecord **2.3**.
|
218
|
+
# @see Arel::Visitors::SQLServer
|
219
|
+
def add_limit_offset!(sql, options)
|
220
|
+
if options[:limit]
|
221
|
+
order = "ORDER BY #{options[:order] || determine_order_clause(sql)}"
|
222
|
+
sql.sub!(/ ORDER BY.*$/i, '')
|
223
|
+
SqlServer2000ReplaceLimitOffset.replace_limit_offset!(sql, options[:limit], options[:offset], order)
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
end if ::ActiveRecord::VERSION::MAJOR < 3
|
228
|
+
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|