activerecord-jdbc-adapter 50.4-java → 50.5-java

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,200 +0,0 @@
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(Column) } ]
7
- end
8
-
9
- # @see ActiveRecord::ConnectionAdapters::JdbcColumn
10
- module Column
11
-
12
- def self.included(base)
13
- # NOTE: assumes a standalone MSSQLColumn class
14
- class << base; include Cast; end
15
- end
16
-
17
- include LockMethods unless AR42
18
-
19
- # @override
20
- def simplified_type(field_type)
21
- case field_type
22
- when /int|bigint|smallint|tinyint/i then :integer
23
- when /numeric/i then (@scale.nil? || @scale == 0) ? :integer : :decimal
24
- when /float|double|money|real|smallmoney/i then :decimal
25
- when /datetime|smalldatetime/i then :datetime
26
- when /timestamp/i then :timestamp
27
- when /time/i then :time
28
- when /date/i then :date
29
- when /text|ntext|xml/i then :text
30
- when /binary|image|varbinary/i then :binary
31
- when /char|nchar|nvarchar|string|varchar/i then (@limit == 1073741823 ? (@limit = nil; :text) : :string)
32
- when /bit/i then :boolean
33
- when /uniqueidentifier/i then :string
34
- else
35
- super
36
- end
37
- end
38
-
39
- # @override
40
- def default_value(value)
41
- return $1 if value =~ /^\(N?'(.*)'\)$/ || value =~ /^\(\(?(.*?)\)?\)$/
42
- value
43
- end
44
-
45
- # @override
46
- def type_cast(value)
47
- return nil if value.nil?
48
- case type
49
- when :integer then ( value.is_a?(String) ? unquote(value) : (value || 0) ).to_i
50
- when :primary_key then value.respond_to?(:to_i) ? value.to_i : ((value && 1) || 0)
51
- when :decimal then self.class.value_to_decimal(unquote(value))
52
- when :date then self.class.string_to_date(value)
53
- when :datetime then self.class.string_to_time(value)
54
- when :timestamp then self.class.string_to_time(value)
55
- when :time then self.class.string_to_dummy_time(value)
56
- when :boolean then value == true || (value =~ /^t(rue)?$/i) == 0 || unquote(value) == '1'
57
- when :binary then unquote(value)
58
- else value
59
- end
60
- end
61
-
62
- # @override
63
- def extract_limit(sql_type)
64
- case sql_type
65
- when /^smallint/i
66
- 2
67
- when /^int/i
68
- 4
69
- when /^bigint/i
70
- 8
71
- when /\(max\)/, /decimal/, /numeric/
72
- nil
73
- when /text|ntext|xml|binary|image|varbinary|bit/
74
- nil
75
- else
76
- super
77
- end
78
- end
79
-
80
- # #primary replacement that works on 4.2 as well
81
- # #columns will set @primary even when on AR 4.2
82
- def primary?; @primary end
83
- alias_method :is_primary, :primary?
84
-
85
- def identity?
86
- !! sql_type.downcase.index('identity')
87
- end
88
- # @deprecated
89
- alias_method :identity, :identity?
90
- alias_method :is_identity, :identity?
91
-
92
- # NOTE: these do not handle = equality as expected
93
- # see {#repair_special_columns}
94
- # (TEXT, NTEXT, and IMAGE data types are deprecated)
95
- # @private
96
- def special?
97
- unless defined? @special # /text|ntext|image|xml/i
98
- sql_type = @sql_type.downcase
99
- @special = !! ( sql_type.index('text') || sql_type.index('image') || sql_type.index('xml') )
100
- end
101
- @special
102
- end
103
- # @deprecated
104
- alias_method :special, :special?
105
- alias_method :is_special, :special?
106
-
107
- def is_utf8?
108
- !!( sql_type =~ /nvarchar|ntext|nchar/i )
109
- end
110
-
111
- private
112
-
113
- def unquote(value)
114
- value.to_s.sub(/\A\([\(\']?/, "").sub(/[\'\)]?\)\Z/, "")
115
- end
116
-
117
- # @deprecated no longer used
118
- def cast_to_time(value)
119
- return value if value.is_a?(Time)
120
- DateTime.parse(value).to_time rescue nil
121
- end
122
-
123
- # @deprecated no longer used
124
- def cast_to_date(value)
125
- return value if value.is_a?(Date)
126
- return Date.parse(value) rescue nil
127
- end
128
-
129
- # @deprecated no longer used
130
- def cast_to_datetime(value)
131
- if value.is_a?(Time)
132
- if value.year != 0 and value.month != 0 and value.day != 0
133
- return value
134
- else
135
- return Time.mktime(2000, 1, 1, value.hour, value.min, value.sec) rescue nil
136
- end
137
- end
138
- if value.is_a?(DateTime)
139
- begin
140
- # Attempt to convert back to a Time, but it could fail for dates significantly in the past/future.
141
- return Time.mktime(value.year, value.mon, value.day, value.hour, value.min, value.sec)
142
- rescue ArgumentError
143
- return value
144
- end
145
- end
146
-
147
- return cast_to_time(value) if value.is_a?(Date) or value.is_a?(String) rescue nil
148
-
149
- return value.is_a?(Date) ? value : nil
150
- end
151
-
152
- module Cast
153
-
154
- def string_to_date(value)
155
- return value unless value.is_a?(String)
156
- return nil if value.empty?
157
-
158
- date = fast_string_to_date(value)
159
- date ? date : Date.parse(value) rescue nil
160
- end
161
-
162
- def string_to_time(value)
163
- return value unless value.is_a?(String)
164
- return nil if value.empty?
165
-
166
- fast_string_to_time(value) || DateTime.parse(value).to_time rescue nil
167
- end
168
-
169
- ISO_TIME = /\A(\d\d)\:(\d\d)\:(\d\d)(\.\d+)?\z/
170
-
171
- def string_to_dummy_time(value)
172
- return value unless value.is_a?(String)
173
- return nil if value.empty?
174
-
175
- if value =~ ISO_TIME # "12:34:56.1234560"
176
- microsec = ($4.to_f * 1_000_000).round.to_i
177
- new_time 2000, 1, 1, $1.to_i, $2.to_i, $3.to_i, microsec
178
- else
179
- super(value)
180
- end
181
- end
182
-
183
- def string_to_binary(value)
184
- # this will only allow the adapter to insert binary data with a length
185
- # of 7K or less because of a SQL Server statement length policy ...
186
- "0x#{value.unpack("H*")}" # "0x#{value.unpack("H*")[0]}"
187
- end
188
-
189
- def binary_to_string(value)
190
- if value.respond_to?(:force_encoding) && value.encoding != Encoding::ASCII_8BIT
191
- value = value.force_encoding(Encoding::ASCII_8BIT)
192
- end
193
- value =~ /[^[:xdigit:]]/ ? value : [value].pack('H*')
194
- end
195
-
196
- end
197
-
198
- end
199
- end
200
- end
@@ -1,79 +0,0 @@
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
- begin
20
- require 'jdbc/jtds'
21
- # NOTE: the adapter has only support for working with the
22
- # open-source jTDS driver (won't work with MS's driver) !
23
- ::Jdbc::JTDS.load_driver(:require) if defined?(::Jdbc::JTDS.load_driver)
24
- rescue LoadError => e # assuming driver.jar is on the class-path
25
- raise e unless e.message.to_s.index('no such file to load')
26
- end
27
-
28
- config[:host] ||= 'localhost'
29
- config[:port] ||= 1433
30
- config[:driver] ||= defined?(::Jdbc::JTDS.driver_name) ? ::Jdbc::JTDS.driver_name : 'net.sourceforge.jtds.jdbc.Driver'
31
- config[:connection_alive_sql] ||= 'SELECT 1'
32
-
33
- config[:url] ||= begin
34
- url = "jdbc:jtds:sqlserver://#{config[:host]}:#{config[:port]}/#{config[:database]}"
35
- # Instance is often a preferrable alternative to port when dynamic ports are used.
36
- # If instance is specified then port is essentially ignored.
37
- url << ";instance=#{config[:instance]}" if config[:instance]
38
- # This will enable windows domain-based authentication and will require the JTDS native libraries be available.
39
- url << ";domain=#{config[:domain]}" if config[:domain]
40
- # AppName is shown in sql server as additional information against the connection.
41
- url << ";appname=#{config[:appname]}" if config[:appname]
42
- url
43
- end
44
-
45
- unless config[:domain]
46
- config[:username] ||= 'sa'
47
- config[:password] ||= ''
48
- end
49
- jdbc_connection(config)
50
- end
51
- alias_method :jdbcmssql_connection, :mssql_connection
52
-
53
- # @note Assumes SQLServer SQL-JDBC driver on the class-path.
54
- def sqlserver_connection(config)
55
- config[:adapter_spec] ||= ::ArJdbc::MSSQL
56
- config[:adapter_class] = ActiveRecord::ConnectionAdapters::MSSQLAdapter unless config.key?(:adapter_class)
57
-
58
- return jndi_connection(config) if jndi_config?(config)
59
-
60
- config[:host] ||= 'localhost'
61
- config[:driver] ||= 'com.microsoft.sqlserver.jdbc.SQLServerDriver'
62
- config[:connection_alive_sql] ||= 'SELECT 1'
63
-
64
- config[:url] ||= begin
65
- url = "jdbc:sqlserver://#{config[:host]}"
66
- url << ( config[:port] ? ":#{config[:port]};" : ';' )
67
- url << "databaseName=#{config[:database]};" if config[:database]
68
- url << "instanceName=#{config[:instance]};" if config[:instance]
69
- app = config[:appname] || config[:application]
70
- url << "applicationName=#{app};" if app
71
- isc = config[:integrated_security] # Win only - needs sqljdbc_auth.dll
72
- url << "integratedSecurity=#{isc};" unless isc.nil?
73
- url
74
- end
75
- jdbc_connection(config)
76
- end
77
- alias_method :jdbcsqlserver_connection, :sqlserver_connection
78
-
79
- end
@@ -1,99 +0,0 @@
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
@@ -1,231 +0,0 @@
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